【问题标题】:How Can I Make My Jump Animation Work All At Once?我怎样才能让我的跳跃动画一次全部工作?
【发布时间】:2021-03-20 00:38:25
【问题描述】:

我在让我的游戏角色跳跃方面遇到了一些问题。我已经将我的跳跃功能设置为空格键,但跳跃动画不会一下子发生。相反,按空格会导致角色每次向上移动一点,直到达到最大跳跃高度限制。然后,一旦它达到那个高度,按下空间会导致玩家每次向下移动一点,直到达到地面极限。按住空格键时看起来很正常,但是一旦松开,角色就会卡在松开空格键的空中。我希望通过单击空格键一次完成所有序列。我已经查看了我的代码很长一段时间,并尝试更改从 JPanel 调用我的 Player 类的“act”方法的位置。这是我的 Player 类,其中的跳跃功能应该被赋予其值:

import java.awt.*;
import javax.swing.*;
import java.awt.event.*;


public class Player {
    ImageIcon movements[];
    ImageIcon leftMove[];
    ImageIcon move[];
    ImageIcon attack;
    boolean ableAttack, canJump;
    int count, x, y, width, height;
    int fallingSpeed = 0;
    int gravity = 1;
    int jumpPower = -20;

    public Player(int count, int x, int y) {
        this.x = x;
        this.y = y;
        this.count = count;
        movements = new ImageIcon[8];
        leftMove = new ImageIcon[8];
        attack = new ImageIcon("attackRight.gif");
        this.width = 80;
        this.height = 130;
        for (int i = 0; i < 8; i++) {
            movements[i] = new ImageIcon("right_"+(i + 1) + ".png");
            leftMove[i] = new ImageIcon("go_"+(i+1)+".png");
        }
        move = movements.clone();
    }

    public void act() {
        if (isOnGround()) {
            canJump = true;
            jump();

        } else {
            canJump = false;
            fall();
        }
    }
    public void jump() {
        fallingSpeed = jumpPower;
        fall();
    }
    public void fall() {
        y += fallingSpeed;
        fallingSpeed = fallingSpeed + gravity;
    }

    public void setX(int x) {
        this.x = x;
    }

    public void setY(int y) {
        this.y = y;
    }

    public boolean isOnGround() {
        if (y > 410) 
            return true;
        return false;
    }

    public void setAttack(boolean attack) {
        ableAttack = attack;
    }

    public void setImageArrLeft() {
        move = leftMove.clone();
        attack = new ImageIcon("attack.gif");
    }
    public void setImageArrRight() {
        move = movements.clone();
        attack = new ImageIcon("attackRight.gif");
    }

    public void increaseCount() {
        count++;
    }

    public void myDraw(Graphics g) {
        if (ableAttack) {
            g.drawImage(attack.getImage(), x, y, width + 30, height + 10, null);
        } 
        else {
            g.drawImage(move[count % 8].getImage(), x, y, width, height, null);
        }   
    }
}

然后,在我的 JPanel 类中,我在我的 keyPressed 方法下调用 Player 类中的 act() 方法,从 KeyListener 接口检查 VK_ENTER:

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.*;

public class MyPanel extends JPanel implements ActionListener, MouseListener, MouseMotionListener, KeyListener{
    int mouseX, mouseY, x = 100, y = 420;
    Timer timer;
    ImageIcon background = null;
    Player p;
    
    public MyPanel() {
        timer = new Timer(60, this);
        p = new Player((int)(Math.random() * 8), x, y);
        background = new ImageIcon("battlefield1.png");
        addKeyListener(this);
        setFocusable(true);
    }
    
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(background.getImage(), 0, 0, 1375, 750, null);
        p.myDraw(g);
        repaint();
    }
    public void actionPerformed(ActionEvent e) {
        if(e.getSource()==timer) {
        }
    }

    public void mouseClicked(MouseEvent me) {}
    public void mouseEntered(MouseEvent me) {}
    public void mouseExited(MouseEvent me) {}
    public void mousePressed(MouseEvent me) {}
    public void mouseReleased(MouseEvent me) {}
    public void mouseDragged(MouseEvent me) {}
    public void mouseMoved(MouseEvent e) {}

    @Override
    public void keyTyped(KeyEvent e) {}

    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
            p.increaseCount();
            if (x < 1300)
                x += 10;
            p.setX(x);
            p.setImageArrRight();
        }
        if (e.getKeyCode() == KeyEvent.VK_LEFT) {
            p.increaseCount();
            if (x > 0) 
                x -= 5;
            p.setX(x);
            p.setImageArrLeft();
        }
        if (e.getKeyCode() == KeyEvent.VK_ENTER) {
            p.setAttack(true);
        }
        if (e.getKeyCode() == KeyEvent.VK_SPACE) {
            p.act();
        } 
    }
    @Override
    public void keyReleased(KeyEvent e) {
        // TODO Auto-generated method stub
        p.setAttack(false);

    }
}

【问题讨论】:

  • 跳跃是一种“动画”状态 - 您需要确定是否正在进行“跳跃”以及当前的“跳跃”值是什么,因此您可以相应地修改该值(继续上升,开始下降)
  • 对于exampleexampleexample
  • @MadProgrammer 我的程序似乎知道什么时候应该继续推动角色,什么时候应该开始下降。问题是按下空格键后它不是一个快速动画,每次按下就像序列的片段。不过,按住空格键会使角色的跳跃看起来很正常。我只是不确定为什么跳转不会立即执行。
  • 您还没有包含任何演示您如何处理动画的代码...似乎您不断重新启动每个键事件的跳转动作,但实际上,您应该检查是否跳跃正在进行中。如果没有,开始遍历跳跃动画的帧。如果您已经在进行跳跃,那么什么也不做,让现有的跳跃序列播放。
  • @MarsAtomic 这正是我的问题。我想知道如何遍历这个跳跃动画的所有帧,而不是每次点击空间时都将其分割成碎片。你对如何做到这一点有什么建议吗?

标签: java swing animation graphics jframe


【解决方案1】:

您需要更好地了解动画(通常),尤其是游戏循环。

Swing 使用被动渲染方法,因此您需要设置一个“游戏循环”来更新游戏状态,同时考虑到用户给出的任何输入,然后安排重绘。

这意味着,您不能在KeyListener 中更新玩家的状态,相反,您需要设置一个允许“游戏循环”更新整体状态的状态。

这是一个非常基本的例子......

import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Test {

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

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new MyPanel());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class MyPanel extends JPanel implements ActionListener, MouseListener, MouseMotionListener, KeyListener {

        int mouseX, mouseY, x = 100, y = 420;
        Timer timer;
        Player p;

        public MyPanel() {
            timer = new Timer(60, this);
            p = new Player((int) (Math.random() * 8), x, y);
            setBackground(Color.BLUE);
            addKeyListener(this);
            setFocusable(true);
        }

        @Override
        public void addNotify() {
            super.addNotify();
            timer.start();
        }

        @Override
        public void removeNotify() {
            super.removeNotify();
            timer.stop();
        }

        @Override
        public Dimension getPreferredSize() {
            return new Dimension(800, 800);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            p.myDraw(g);
        }

        public void actionPerformed(ActionEvent e) {
            if (e.getSource() == timer) {
                p.update();
                repaint();
            }
        }

        public void mouseClicked(MouseEvent me) {
        }

        public void mouseEntered(MouseEvent me) {
        }

        public void mouseExited(MouseEvent me) {
        }

        public void mousePressed(MouseEvent me) {
        }

        public void mouseReleased(MouseEvent me) {
        }

        public void mouseDragged(MouseEvent me) {
        }

        public void mouseMoved(MouseEvent e) {
        }

        @Override
        public void keyTyped(KeyEvent e) {
        }

        @Override
        public void keyPressed(KeyEvent e) {
            if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
                p.increaseCount();
                if (x < 1300) {
                    x += 10;
                }
                p.setX(x);
                p.setImageArrRight();
            }
            if (e.getKeyCode() == KeyEvent.VK_LEFT) {
                p.increaseCount();
                if (x > 0) {
                    x -= 5;
                }
                p.setX(x);
                p.setImageArrLeft();
            }
            if (e.getKeyCode() == KeyEvent.VK_ENTER) {
                p.setAttack(true);
            }
            if (e.getKeyCode() == KeyEvent.VK_SPACE) {
                p.act();
            }
        }

        @Override
        public void keyReleased(KeyEvent e) {
            // TODO Auto-generated method stub
            p.setAttack(false);

        }
    }

    public class Player {

        boolean ableAttack, canJump;
        int count, x, y, width, height;
        int fallingSpeed = 0;
        int gravity = 1;
        int jumpPower = -20;

        private boolean isJumping = false;

        public Player(int count, int x, int y) {
            this.x = x;
            this.y = y;
            this.count = count;
            width = 10;
            height = 10;
        }

        public void act() {
            if (isOnGround()) {
                jump();
//                canJump = true;
//                jump();
//
//            } else {
//                canJump = false;
//                fall();
            }
        }

        public void jump() {
            isJumping = true;
            fallingSpeed = jumpPower;
            fall();
        }

        public void fall() {
            y += fallingSpeed;
            fallingSpeed = fallingSpeed + gravity;
        }

        public void setX(int x) {
            this.x = x;
        }

        public void setY(int y) {
            this.y = y;
        }

        public boolean isOnGround() {
            if (y > 410) {
                return true;
            }
            return false;
        }

        public void setAttack(boolean attack) {
            ableAttack = attack;
        }

        public void setImageArrLeft() {
        }

        public void setImageArrRight() {
        }

        public void increaseCount() {
            count++;
        }

        public void update() {
            if (isJumping) {
                if (!isOnGround()) {
                    fall();
                } else {
                    isJumping = false;
                }
            }
        }

        public void myDraw(Graphics g) {
            g.setColor(Color.RED);
            g.fillRect(x, y, width, height);
        }
    }
}

就我个人而言,我会使用Key Bindings 而不是KeyListener,因为它将解决键盘焦点的直接问题并提供更可配置的方法。

这意味着“输入状态”将独立于玩家,游戏循环将负责根据游戏状态控制玩家状态的变化

【讨论】:

  • 感谢您在我找出代码时一直支持我。幸运的是,我找到了实现跳转功能的更好逻辑 - 部分使用了您关于通过动作侦听器而不是按键侦听器更新图形的建议。
猜你喜欢
  • 1970-01-01
  • 2017-08-30
  • 1970-01-01
  • 2021-01-22
  • 2011-04-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多