【问题标题】:Player object moving one direction but turned a different direction玩家对象向一个方向移动但转向不同的方向
【发布时间】:2014-07-04 01:23:53
【问题描述】:

当游戏第一次开始时,玩家会生成,朝 0 方向(右)看。按下右键 (vk_right) 将玩家精灵向左转,但方向被设置为将玩家发送到(看起来像)正确的方向。左右键改变角色的方向变量,上/下键加速/减速角色。

我不太擅长三角学,所以我可能到处都有一些错误(这就是我发布此内容的原因)。我似乎无法理解如何让角色朝着他“看”的方向移动(方向变量)。

这是 Player.java 类:

package rpg2d;

import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

import javax.swing.ImageIcon;

public class Player implements KeyListener{

    private double x,y,direction;
    private double speed;
    private final int max_speed;
    private int hp,lives;
    private Image img;
    //other variables

    public Player(int x, int y, int hp, int lives, String imgpath) {
        this.x = x;
        this.y = y;
        this.hp = hp;
        this.lives = lives;
        img = new ImageIcon(this.getClass().getResource(imgpath)).getImage();
        //loads the player image from the string path
        max_speed = 6;
        speed = 0;
        direction = 0;
    }

    //returns the direction the player is 'facing' as an int
    public int getDirection() {
        return (int)direction;
    }

    //updates the player's location
    public void move() {
        x += speed * Math.cos(-1*direction);
        y += speed * Math.sin(-1*direction);
    }

    @Override
    public void keyPressed(KeyEvent e) {
        int key = e.getKeyCode();
        if(key == KeyEvent.VK_LEFT){
            turn(.6);
            //this is meant to turn the player int a clockwise direction by 0.6
        } else if (key == KeyEvent.VK_RIGHT){
            turn(-.6);
            //counterclockwise by 0.6
        }
        if (key == KeyEvent.VK_DOWN){
            speed -= 0.3;
            if(speed < 0) speed = 0;
            //decelerate until stopped
        } else if (key == KeyEvent.VK_UP){
            speed += 0.3;
            if(speed > max_speed) speed = max_speed;
            //accelerates until it hits maximum speed
        }
    }

    private void turn(double degrees) {
        direction += degrees;
        if(direction > 180) direction -= 180;
        else if(direction < -180) direction += 180;
        /* I honestly don't know whether 180 and -180 are the max and min
         * for Polar Coordinates, so this could be a problem.
         */
    }

    @Override
    public void keyReleased(KeyEvent e) {
    }

    @Override
    public void keyTyped(KeyEvent arg0) {
    }

    public int getX() {
        return (int)x;
    }

    public int getY() {
        return (int)y;
    }

    public double getWidth() {
        return img.getWidth(null);
    }

    public double getHeight() {
        return img.getHeight(null);
    }

    public Image getImg() {
        return img;
    }

}

这是主要的游戏类:

package rpg2d;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class Main {

    boolean gameRunning = true;
    Graphics2D g;
    JFrame frame;
    JPanel screen;
    Player player;

    public static void main(String[] args) {
        new Main();
    }

    @SuppressWarnings("serial")
    public Main() {
        frame = new JFrame("2D RPG Test");
        frame.setSize(800,600);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        player = new Player(10,10,100,3,"/img/player.png");
        screen = new JPanel() {
            @Override
            public void paintComponent(Graphics g) {
                super.paintComponent(g);
                Graphics2D g2d = (Graphics2D)g;
                AffineTransform trans = new AffineTransform();
                AffineTransform old = new AffineTransform();
                trans.setToIdentity();
                trans.rotate(Math.toRadians(player.getDirection()),player.getWidth()/2,player.getHeight()/2);
                g2d.setTransform(trans);
                g2d.drawImage(player.getImg(), player.getX(), player.getY(), (int)player.getWidth(), (int)player.getHeight(), null);
                g2d.setTransform(old);
                g2d.drawString("X: " + player.getX() + " Y: " + player.getY(), 5,10);
                trans.setToIdentity();
            }
        };

        frame.add(screen);
        frame.setVisible(true);
        screen.addKeyListener(player);
        screen.requestFocus();
        Thread t = new Thread(new Runnable() {
            public void run() {
                gameLoop();
            }
        });
        t.setDaemon(false);
        t.start();
    }

    public void update() {
        player.move();
    }

    public void gameLoop() {
        final int FPS = 60;
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new TimerTask() {
            public void run() {
                update();
                screen.repaint();
            }
        }, 0, FPS);
    }
}

【问题讨论】:

  • 如果这是我的问题,我会摆脱底部的咆哮,因为它会分散注意力并且毫无用处。如果您花时间更详细地描述您的代码以便我们能够理解它,这个问题会更好地解决。如果我能一眼看懂你的代码,我现在可能已经给你答案了。顺便说一句,将 java.util.Timer 与 Swing 应用程序一起使用通常是不安全的。最好使用 javax.swing.Timer。
  • 对咆哮感到抱歉。只是每次我发布这样的东西,我最终都会得到这样的回应。我会用代码解释更新帖子。
  • 我会说问题出在您的图像上,而不是您的代码上。当我在运行时创建一个简单的箭头形状时,指向右边,它工作得很好。考虑发布您的图片副本
  • 请理解,当您在此处发布信息寻求帮助时,您将获得志愿者的免费帮助。我们将尝试回答您的问题,但我们也会尝试帮助解决我们可能会看到的您的代码的任何其他问题。我自己,我会感谢您免费获得的额外努力。例如,我还要告诉你避免使用 KeyListeners 而是支持 Key Bindings,并避免给你的类一个 Graphics 或 Graphics2D 字段,因为这可能是一件危险的事情,并且有可能引发 NPE。
  • 您应该避免使用Threadjava.util.Timer,因为Swing 不是线程安全的,当您调用update 时,Swing 可能正在绘制,这可能会导致绘制/更新不干净。考虑使用javax.swing.Timer 或创建一个屏幕外缓冲区,您可以在其上进行绘画,然后通过同步机制推送到 UI

标签: java swing graphics trigonometry radians


【解决方案1】:

我做了一些改进:

  1. 在计算 Cos 和 Sin 时,必须将度数转换为弧度,因为这些函数采用弧度(弧度是度数的不同单位)。
  2. 我更改了左右键的 - 和 +。按照惯例,逆时针方向增加而不是相反;)
  3. 我修改了转弯方法,因为它有一些小错误(-180 是 0 触发)

如果有更多错误请发表评论,我会再看一下:) 祝你好运!

public void move() {
    x += speed * Math.cos(Math.toRadians(direction));
    y += speed * Math.sin(Math.toRadians(direction));
}

@Override
public void keyPressed(KeyEvent e) {
    int key = e.getKeyCode();
    if(key == KeyEvent.VK_LEFT){
        turn(-.6);
        //this is meant to turn the player int a clockwise direction by 0.6
    } else if (key == KeyEvent.VK_RIGHT){
        turn(.6);
        //counterclockwise by 0.6
    }
   .....//rest of code}

private void turn(double degrees) {
    direction += degrees;
    if(direction > 180) direction = 180;
    else if(direction < 180) direction = 0;
    }

【讨论】:

  • 你知道,如果你将Graphics 上下文翻译成播放器的本地上下文,你可以去掉Math.cosMath.sin...
  • 我现在添加这个来测试它;但我注意到:如果方向小于 180,它将被设置为 0,这意味着除非它是 180,否则它将始终为 0。另外,极坐标系(180 或 360)的最大值是多少?
  • 极坐标:360 度,如果我没记错的话。 A - 符号加 180
  • 好的,我不确定出了什么问题。没有抛出异常,没有崩溃,但是精灵围绕面板的左上角 (0,0) 旋转。我不明白这个..
  • 啊。忽略最后一条评论。显然我忘记了旋转方法中的最后两个参数。
【解决方案2】:

增加 Dean 的努力...

在没有图像的情况下,我创建了一个简单的箭头形状,开始指向右...

public Player(int x, int y, int hp, int lives, String imgpath) {
    this.x = x;
    this.y = y;
    this.hp = hp;
    this.lives = lives;
    //img = new ImageIcon(this.getClass().getResource(imgpath)).getImage();

    BufferedImage arrow = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);
    Graphics2D g2d = arrow.createGraphics();
    g2d.setColor(Color.RED);
    g2d.drawLine(0, 0, 16, 8);
    g2d.drawLine(16, 8, 0, 16);
    g2d.drawLine(0, 16, 0, 0);
    g2d.dispose();
    img = arrow;

    max_speed = 6;
    speed = 0;
    direction = 0;
}

现在,这可以很好地开始。

当我开始移动播放器时遇到了一些问题,所以我稍微更新了绘制代码。

@Override
public void paintComponent(Graphics g) {
    super.paintComponent(g);
    Graphics2D g2d = (Graphics2D) g.create();
    AffineTransform trans = new AffineTransform();
    trans.setToIdentity();
    trans.translate(player.x, player.y);
    trans.rotate(Math.toRadians(player.getDirection()), player.getWidth() / 2, player.getHeight() / 2);
    g2d.setTransform(trans);
    g2d.drawImage(player.getImg(), 0, 0, (int) player.getWidth(), (int) player.getHeight(), null);
    g2d.dispose();

    g2d = (Graphics2D) g.create();
    g2d.drawString("X: " + player.getX() + " Y: " + player.getY(), 5, 10);
    g2d.dispose();
    trans.setToIdentity();
}

基本上,我复制了Graphics 上下文...

Graphics2D g2d = (Graphics2D) g.create();

使用AffineTransform,我将上下文转换为玩家的当前位置,这简化了旋转代码,因为我不需要偏移锚点...

trans.translate(player.x, player.y);
trans.rotate(Math.toRadians(player.getDirection()), player.getWidth() / 2, player.getHeight() / 2);

这也意味着画图更容易……

g2d.drawImage(player.getImg(), 0, 0, (int) player.getWidth(), (int) player.getHeight(), null);

所有这一切的美妙之处在于,所有更改都与Graphics 上下文的副本相关,您无需全部“撤消”...只是不要忘记dispose完成后的副本。

我也不鼓励你使用KeyListener,而是支持Key bindings API

我还用javax.swing.Timer 替换了您的Threadjava.util.Timer...

public void gameLoop() {
    Timer timer = new Timer(16, new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
            update();
            screen.repaint();
        }
    });
    timer.start();
}

另外,请注意,scheduleAtFixedRate 的最后一个参数是调用之间的毫秒数,因此您当前的代码生成大约 16fps。 60fps 大约是 16 毫秒...

【讨论】:

  • 从问题上的 cmets 以及使用院长的信息,我能够对其进行调整,以获得几乎与您现在所拥有的完全一样的信息。 (唯一的区别是你平移和旋转的时候;我用af.translate(-player.getWidth()/2, -player.getHeight()/2); af.rotate(Math.toRadians(player.getDirection()),player.getX() - player.getWidth()/2,player.getY() - player.getHeight()/2);
  • @Levi 是的,我同意,我只是想在顶部添加计时器和键绑定的油漆更新和建议,因为如果你不小心它们会困扰你;)
  • 我确实有一个刚刚提出的问题。 Key Bindings 似乎一次只允许捕获一个键;按多个键取消第一个键。这是有原因的,还是有更好的方法来处理键绑定?
  • KeyListener 会遇到类似的问题。基本上发生的事情是当您第一次按下键时触发一个键事件,然后根据操作系统,该键将重复,首先有一个小的延迟,然后是更短的延迟。人们通常做的是有一个“按下”和“释放”标志,然后根据标志的值,相应地更新状态。这通常更干净、更健壮,您将获得跨平台更一致的结果
  • 键绑定不会遇到困扰KeyListener的焦点相关问题;)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-29
  • 1970-01-01
  • 2021-04-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多