1

I have been trying to code a half built simple java game i was given to by a friend (I wanted to try learning coding so he gave me this old assignment from his 1st year of programming class). He told me to try and build a game using the 2 classes he gave me, a main class and a sub class.

So far I have got everything to work as intended except for finding a way to get my enemy class to move towards the goal regardless of its position. I can get it to always move in a fixed direction towards the goal if I set it up perfectly but I want to be able to find the position of the goal regardless and make its way towards it.

The code need to be written in the peformAction method under Enemy.

If anyone would be kind enough to explain how I can go about this, it would be much appropriated, or even if you can just link me to another post that explains how to do what I'm trying to do.

Thank you in advance.

GameManager (main) Class:

import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.swing.JFrame;

public class GameManager extends JFrame implements KeyListener {
    private int canvasWidth;
    private int canvasHeight;
    private int borderLeft;
    private int borderTop;
    private BufferedImage canvas;
    private Stage stage;
    private Enemy[] enemies;
    private Player player;
    private Goal goal;
    private Graphics gameGraphics;
    private Graphics canvasGraphics;
    private int numEnemies;
    private boolean continueGame;

    public static void main(String[] args) {
        // During development, you can adjust the values provided in the brackets below
        // as needed. However, your code must work with different/valid combinations
        // of values.
        GameManager managerObj = new GameManager(1920, 1280, 30);
    }

    public GameManager(int preferredWidth, int preferredHeight, int maxEnemies) {
        this.borderLeft = getInsets().left;
        this.borderTop = getInsets().top;
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        if (screenSize.width < preferredWidth)
            this.canvasWidth = screenSize.width - getInsets().left - getInsets().right;
        else
            this.canvasWidth = preferredWidth - getInsets().left - getInsets().right;
        if (screenSize.height < preferredHeight)
            this.canvasHeight = screenSize.height - getInsets().top - getInsets().bottom;
        else
            this.canvasHeight = preferredHeight - getInsets().top - getInsets().bottom;
        setSize(this.canvasWidth, this.canvasHeight);
        setResizable(false);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setVisible(true);
        addKeyListener(this);
        Random rng = new Random(2);
        this.canvas = new BufferedImage(this.canvasWidth, this.canvasHeight, BufferedImage.TYPE_INT_RGB);
        // Create a Stage object to hold the background images
        this.stage = new Stage();
        // Create a Goal object with its initial x and y coordinates
        this.goal = new Goal(this.canvasWidth / 2, Math.abs(rng.nextInt()) % this.canvasHeight);
        // Create a Player object with its initial x and y coordinates
        this.player = new Player(this.canvasWidth - (Math.abs(rng.nextInt()) %  (this.canvasWidth / 2)),
            (Math.abs(rng.nextInt()) % this.canvasHeight));
        // Create the Enemy objects, each with a reference to this (GameManager) object
        // and their initial x and y coordinates.
        this.numEnemies = maxEnemies;
        this.enemies = new Enemy[this.numEnemies];
        for (int i = 0; i < this.numEnemies; i++) {
            this.enemies[i] = new Enemy(this, Math.abs(rng.nextInt()) % (this.canvasWidth / 4),
            Math.abs(rng.nextInt()) % this.canvasHeight);
        }
        this.gameGraphics = getGraphics();
        this.canvasGraphics = this.canvas.getGraphics();
        this.continueGame = true;
        while (this.continueGame) {
            updateCanvas();
        }
        this.stage.setGameOverBackground();
        updateCanvas();
    }

    public void updateCanvas() {
        long start = System.nanoTime();
        // If the player is alive, this should move the player in the direction of the
        // key that has been pressed
        // Note: See keyPressed and keyReleased methods in the GameManager class.
        this.player.performAction();
        // If the enemy is alive, the enemy must move towards the goal. The goal object
        // is obtained
        // via the GameManager object that is given at the time of creating an Enemy
        // object.
        // Note: The amount that the enemy moves by must be much smaller than that of
        // the player above
        // or else the game becomes too hard to play.
        for (int i = 0; i < this.numEnemies; i++) {
            this.enemies[i].performAction();
        }
        if ((Math.abs(this.goal.getX() - this.player.getX()) < (this.goal.getCurrentImage().getWidth() / 2))
            && (Math.abs(this.goal.getY() - this.player.getY()) < (this.goal.getCurrentImage().getWidth() / 2))) {
            for (int i = 0; i < this.numEnemies; i++) {
                // Sets the image of the enemy to the "dead" image and sets its status to
                // indicate dead
                this.enemies[i].die();
            }
            // Sets the image of the enemy to the "dead" image and sets its status to
            // indicate dead
            this.goal.die();
            // Sets the background of the stage to the finished game background.
            this.stage.setGameOverBackground();
            this.continueGame=false;
        }
        // If an enemy is close to the goal, the player and goal die
        int j=0;
        while(j<this.numEnemies) {
            if ((Math.abs(this.goal.getX() - this.enemies[j].getX()) < (this.goal.getCurrentImage().getWidth() / 2))
                && (Math.abs(this.goal.getY() - this.enemies[j].getY()) < (this.goal.getCurrentImage().getWidth()/ 2))) {
                this.player.die();
                this.goal.die();
                this.stage.setGameOverBackground();
                j=this.numEnemies;
                this.continueGame=false;
            }
            j++;
        }
        try {
            // Draw stage
            this.canvasGraphics.drawImage(stage.getCurrentImage(), 0, 0, null);
            // Draw player
            this.canvasGraphics.drawImage(player.getCurrentImage(),
            this.player.getX() - (this.player.getCurrentImage().getWidth() / 2),
            this.player.getY() - (this.player.getCurrentImage().getHeight() / 2), null);
            // Draw enemies
            for (int i = 0; i < this.numEnemies; i++) {
                this.canvasGraphics.drawImage(this.enemies[i].getCurrentImage(),
                this.enemies[i].getX() - (this.enemies[i].getCurrentImage().getWidth() / 2),
                this.enemies[i].getY() - (this.enemies[i].getCurrentImage().getHeight() / 2), null);
            }
            // Draw goal
            this.canvasGraphics.drawImage(this.goal.getCurrentImage(),
                this.goal.getX() - (this.goal.getCurrentImage().getWidth() / 2),
                this.goal.getY() - (this.goal.getCurrentImage().getHeight() / 2), null);
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
        // Draw everything.
        this.gameGraphics.drawImage(this.canvas, this.borderLeft, this.borderTop, this);
        long end = System.nanoTime();
        this.gameGraphics.drawString("FPS: " + String.format("%2d", (int) (1000000000.0 / (end - start))),
            this.borderLeft + 50, this.borderTop + 50);
    }

    public Goal getGoal() {
        return this.goal;
    }

    public void keyPressed(KeyEvent ke) {
        // Below, the setKey method is used to tell the Player object which key is
        // currently pressed.
        // The Player object must keep track of the pressed key and use it for
        // determining the direction
        // to move.
        if (ke.getKeyCode() == KeyEvent.VK_LEFT)
            this.player.setKey('L', true);
        if (ke.getKeyCode() == KeyEvent.VK_RIGHT)
            this.player.setKey('R', true);
        if (ke.getKeyCode() == KeyEvent.VK_UP)
            this.player.setKey('U', true);
        if (ke.getKeyCode() == KeyEvent.VK_DOWN)
            this.player.setKey('D', true);
        if (ke.getKeyCode() == KeyEvent.VK_ESCAPE)
            this.continueGame = false;
    }

    @Override
    public void keyReleased(KeyEvent ke) {
        // Below, the setKey method is used to tell the Player object which key is
        // currently released.
        // The Player object must keep track of the pressed key and use it for
        // determining the direction
        // to move.
        if (ke.getKeyCode() == KeyEvent.VK_LEFT)
            this.player.setKey('L', false);
        if (ke.getKeyCode() == KeyEvent.VK_RIGHT)
            this.player.setKey('R', false);
        if (ke.getKeyCode() == KeyEvent.VK_UP)
            this.player.setKey('U', false);
        if (ke.getKeyCode() == KeyEvent.VK_DOWN)
            this.player.setKey('D', false);
    }

    @Override
    public void keyTyped(KeyEvent ke) {
    }
}

My Enemy (sub) Class:

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

public class Enemy {
    //variables 
    private BufferedImage imageRunning;
    private BufferedImage imageOver;
    private BufferedImage imageCurrent;
    int valX = 200;
    int valY = 200;

    public Enemy (GameManager gameManager, int x, int y) {
        try {
            this.imageRunning = ImageIO.read(new File("C:/Users/HUS/Desktop/Assigment222/images/enemy-alive.png"));
            this.imageOver = ImageIO.read(new File("C:/Users/HUS/Desktop/Assigment222/images/enemy-dead.png"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        this.imageCurrent = this.imageRunning;
    }

    public BufferedImage getCurrentImage() {
        return this.imageCurrent;
    }


    public void performAction() {
        valX += 1;
        valY += 1;
        return;

    }

    public void die() {
        // TODO Auto-generated method stub
        this.imageCurrent = this.imageOver;
    }

    public int getX() {
        // TODO Auto-generated method stub
        return this.valX;
    }

    public int getY() {
        // TODO Auto-generated method stub
        return this.valY;
}


}

My Goal (sub) class:

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;

public class Goal {
    private BufferedImage imageRunning;
    private BufferedImage imageOver;
    private BufferedImage imageCurrent;
    int posX = 500;
    int poxY = 500;

    public Goal(int x, int y) {
        try {
            this.imageRunning = ImageIO.read(new File("C:/Users/HUS/Desktop/Assigment222/images/goal-alive.png"));
            this.imageOver = ImageIO.read(new File("C:/Users/HUS/Desktop/Assigment222/images/goal-dead.png"));
        } catch (IOException e) {
            e.printStackTrace();
        }
        this.imageCurrent = this.imageRunning;
    }

    public BufferedImage getCurrentImage() {
        return this.imageCurrent;
    }


    public int getX() {

        return this.posX;
    }

    public int getY() {

        return this.poxY;
    }

    public void die() {
        this.imageCurrent = this.imageOver;
    }


}
jhamon
  • 3,603
  • 4
  • 26
  • 37
  • You have coordinate in both class. So you need to check the direction to go based on those. `goal.getX() - player.getX()` will give you the `X` direction based on negative, zero or positive value. Same for `Y`. If you can give us more information on what you can do with a [mcve], it would be simpler to answer ;) – AxelH May 16 '18 at 07:32
  • i believe this: http://prntscr.com/jihld5 part has something to do with letting me link the goal's x and y cords to the main enemy class, i just want to to go towards what ever the goals position is (even though it stays still if i was to move the enemy should still go towards the goal). i also belive that it need to be written under the peformAction method, but not 100% sure tbh – Huseyin Yesiler May 16 '18 at 07:56
  • What you just linked there is the calculation whether your enemy actually reached the goal so you can call the `die` method on it and remove it from the game. The logic for moving your character there is quite similar though to be honest. – Ben May 16 '18 at 07:57

3 Answers3

1

It's impossible with the current code if you want to restrict the modifications only to the performAction method.

1) The Enemy has no way of accessing (seeing) the coordinates of the goal. This means the code can't figure out the way to the goal because it doesn't have the X and Y of where the destination is. 2) To fix that you would need Enemy to be able to see the X and Y of the Goal. The quickest way would be to use getters and setters in the GameManager class to return the stored Goal object. Then you can modify your Enemy constructor to store the reference to the GameManager that created it.

GameManager:

public Goal getGoal() {
    return goal;
}

Enemy:

 private GameManager gameManager;

 public Enemy (GameManager gameManager, int x, int y) {
        ...
        this.gameManager = gameManager;
 }

The last step would be getting the Goal object from the stored gameManager, and using its X and Y to do some logic (this would be performed inside the performAction method):

int goalX = gameManager.getGoal().getX();
int goalY = gameManager.getGoal().getY();

...some logic to calculate the direction here...

I'm sure you'll know what to do with the numbers and figure out how to move towards the goal from there.

@Edit If not - one way would be comparing Xs and Ys of both the Enemy and the Goal and then incrementing/decrementing valX and/or valY accordingly.

Camile
  • 124
  • 9
  • Hey thanks for taking the time to reply. I was told that to do this properly i should not change any of the code in my GameManger class, although it doesnt have to be written in pefromAction, thats just what i thought. Also about making the enemy class able to read the goal class, i thought about it a lot and couldnt come up with any idea how to do it, although the info u gave for enemy class and goal class should work in theory if i can manage it without changing the GameManager class – Huseyin Yesiler May 16 '18 at 07:47
  • If you can modify Goal class, there's a way to go around it and make the X and Y of the Goal class static: `static int posX;` and then do `Goal.posX` in the Enemy class to get goal's X. This will only work if you have no more than one Goal and is discouraged. See: [Static variable](https://beginnersbook.com/2013/05/static-variable/) – Camile May 16 '18 at 07:50
  • ah yes i can change any other class besides GameManger how ever i see fit, i believe i understand what ur saying and i will give it a shot. Thanks alot – Huseyin Yesiler May 16 '18 at 07:58
0

In your method performAction you are currently changing the x and y position respectively by incrementing them by 1 each time the method gets called.

This will move your enemy exactly 1 unit in direction x and 1 unit in direction y.

You instead want to move your character in the direction of the Goal. That's more of a math problem than a programming issue but let's give it a go:

You have 2 positions:

Your Enemy has a position of form and the same goes for your goal with position

You now want to move your Enemy in the direction of the goal. So you calculate a vector connecting these two points with

You then change the length of the vector to the speed your enemy can move with (maybe define this with a field?) by normalizing the vector and doing a scalar multiplication with your speed.

So with a given speed S this gives you the full formula:

Now let us transform this into code. In the constructor of your Enemy class save the GameManager as a field so you can call the getGoal method on it.

So we add a field private GameManager gameManager and in the Constructor we do this.gameManager = gameManager to store the reference.

Also we adda field private double speed = 2 for our calculation.

And then we change the performAction method to implement our formula. Something like this:

public void performAction()
{
    // Calculate our P_g - P_e
    double dX = gameManager.getGoal().getX() - valX;
    double dY = gameManager.getGoal().getX() - valX;

    // Calculate its length to normalize it
    double divider = Math.sqrt(dX * dX + dY * dY);

    // Normalize it
    dX /= divider;
    dY /= divider;

    // Do a scalar multiplication with our speed
    dX *= speed;
    dY *= speed;

    valX += dX;
    valY += dY;
}

The only problem we are running into here is that we are adding double values to our integer values valX and valY so probably change those to double values generally or else you might run into problems where your enemy will never move at all.

Ben
  • 1,665
  • 1
  • 11
  • 22
  • Oh thank you this one seemed to work. If im being honest you lost me towards the end with the maths stuff but i understood the gist of it :P thanks so much the code works <3 – Huseyin Yesiler May 16 '18 at 09:04
  • You're welcome. The math is quite simple but I did not want to go into too much detail, as it's Stackoverflow after all and not math.stackexchange ;) – Ben May 16 '18 at 09:16
0

A very simple implementation:

public void performAction()
{
    valX += Math.signum(gameManager.getGoal().getX() - valX);
    valY += Math.signum(gameManager.getGoal().getX() - valX);
}
Usagi Miyamoto
  • 6,196
  • 1
  • 19
  • 33
  • I just gave this one a shot and everything but "gameManager.getGoal()." works. for some reason thats giving me a error :/ Here is the error message: http://prntscr.com/jiibqz – Huseyin Yesiler May 16 '18 at 08:47
  • @yasbilc See **Camile**'s answer for details on how to access the goal's co-ordinates... – Usagi Miyamoto May 17 '18 at 07:38