我怎样才能让我的 "Enemy" 追逐 "goal" - Java

How can i get my "Enemy" to chase the "goal" - Java

我一直在尝试编写一个朋友给我的半构建的简单 java 游戏(我想尝试学习编码,所以他在第一年的编程中给了我这个旧作业 class). 他告诉我尝试使用他给我的 2 个 classes 构建一个游戏,一个主要 class 和一个子 class。

到目前为止,除了找到让我的敌人 class 无论其位置如何都朝着目标移动的方法之外,我的一切都按预期工作。如果我完美地设置它,我可以让它始终朝着目标的固定方向移动,但我希望能够无论如何找到目标的位置并朝着它前进。

代码需要写在Enemy下的peformAction方法中。

如果有人愿意解释我如何解决这个问题,那将是非常合适的,或者即使你可以 link 我向另一个 post 解释如何做我正在尝试做什么。

提前致谢。

游戏管理器(主要)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) {
    }
}

我的敌人(子)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;
}


}

我的目标(子)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;
    }


}

如果您只想将修改限制在 performAction 方法中,则当前代码是不可能的。

1) Enemy 无法访问(查看)目标的坐标。这意味着代码无法找出到达目标的路径,因为它没有目的地所在位置的 X 和 Y。 2) 要解决这个问题,您需要 Enemy 才能看到目标的 X 和 Y。最快的方法是使用 getters and setters in the GameManager class to return the stored Goal object. Then you can modify your Enemy constructor 存储对创建它的 GameManager 的引用。

游戏经理:

public Goal getGoal() {
    return goal;
}

敌人:

 private GameManager gameManager;

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

最后一步是从存储的 gameManager 中获取目标对象,并使用其 X 和 Y 执行一些逻辑(这将在 performAction 方法中执行):

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

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

我相信你会知道如何处理这些数字,并弄清楚如何从那里朝着目标前进。

@编辑 如果不是 - one way would be comparing Xs and Ys of both the Enemy and the Goal and then incrementing/decrementing valX and/or valY 相应地。

在您的方法 performAction 中,您当前正在分别更改 x 和 y 位置,方法是每次调用该方法时将它们递增 1。

这将使你的敌人在 x 方向移动 1 个单位,在 y 方向移动 1 个单位。

相反,您想要将角色朝目标方向移动。这与其说是编程问题,不如说是数学问题,但让我们试一试:

您有 2 个职位:

你的敌人的位置是 ,你的目标也是如此,位置是

你现在想要将你的敌人移动到目标的方向。所以你计算出一个用

连接这两个点的向量

然后您将向量的长度更改为敌人可以移动的速度(也许用场定义它?),方法是对向量​​进行归一化并用您的速度进行标量乘法。

所以在给定的速度下 S 这给了你完整的公式:

现在让我们将其转换为代码。在 Enemy class 的构造函数中,将 GameManager 保存为一个字段,以便您可以对其调用 getGoal 方法。

所以我们添加一个字段 private GameManager gameManager 并且在构造函数中我们 this.gameManager = gameManager 来存储引用。

我们还添加了一个字段 private double speed = 2 用于我们的计算。

然后我们更改 performAction 方法来实现我们的公式。像这样:

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;
}

我们 运行 进入这里的唯一问题是我们将双精度值添加到我们的整数值 valXvalY 所以可能通常将它们更改为双精度值,否则您可能 运行 遇到敌人根本无法移动的问题。

一个非常简单的实现:

public void performAction()
{
    valX += Math.signum(gameManager.getGoal().getX() - valX);
    valY += Math.signum(gameManager.getGoal().getX() - valX);
}