【问题标题】:Problem with animation in Java swing - PacmanJava swing 中的动画问题 - Pacman
【发布时间】:2022-01-05 10:30:00
【问题描述】:

我试图复制吃豆人游戏,我想出了玩家动画,然后根据需要转身并循环打开和关闭嘴巴。我编写了代码,它可以根据需要正常工作。只是当我按住按键(W、A、S、D)时它会张开和合上嘴,但并非总是如此。我不明白错误在哪里..这是我现在的代码:

package playerAnimation;

import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import javax.swing.Timer;

public class Animation extends JPanel implements ActionListener, KeyListener  {
    
    Timer tm = new Timer(1,this);

    private String playerDirection="Right";
    private int playerX;
    private int playerY;
    private int playerSpeedX;
    private int playerSpeedY;
    
    private int spriteCounter = 0;
    private int spriteNum = 1;
    
    public BufferedImage playerUp, playerDown, playerLeft, playerRight, playerClosedMouth;
    public BufferedImage imagePlayer = null;
    
    public void getPlayerImage() {
        try {
            playerUp = ImageIO.read(getClass().getResourceAsStream("/player/imageUp.png"));
            playerDown = ImageIO.read(getClass().getResourceAsStream("/player/imageDown.png"));
            playerLeft = ImageIO.read(getClass().getResourceAsStream("/player/imageLeft.png"));
            playerRight = ImageIO.read(getClass().getResourceAsStream("/player/imageRight.png"));
            playerClosedMouth = ImageIO.read(getClass().getResourceAsStream("/player/closed.png"));
        }catch(IOException e ) {
            e.printStackTrace();
        }
    }

    Timer timer = new Timer(1,this);
    
    
    public Animation() {
        getPlayerImage();
        tm.start();
        addKeyListener(this);
        setFocusable(true);
    }
    
    
    
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(imagePlayer,playerX,playerY,null);
    }
    
    public void actionPerformed(ActionEvent e) {
        playerX=playerX+playerSpeedX;
        playerY=playerY+playerSpeedY;
        repaint();
    }
    

    
        public void keyPressed(KeyEvent e) {
            
            if(e.getKeyCode() == KeyEvent.VK_W) {
                
                if(spriteNum==1) {
                    imagePlayer=playerUp;
                }
                if(spriteNum==2) {
                    imagePlayer=playerClosedMouth;
                }
                
                playerSpeedX=0;
                playerSpeedY=-1;
            }
            
            if(e.getKeyCode() == KeyEvent.VK_D) {
                
                if(spriteNum==1) {
                    imagePlayer=playerRight;
                }
                if(spriteNum==2) {
                    imagePlayer=playerClosedMouth;
                }
                
                playerSpeedX=1;
                playerSpeedY=0;
            }
            
            if(e.getKeyCode() == KeyEvent.VK_A) {
                
                if(spriteNum==1) {
                    imagePlayer=playerLeft;
                }
                if(spriteNum==2) {
                    imagePlayer=playerClosedMouth;
                }
                
                playerSpeedX=-1;
                playerSpeedY=0;
            }
            
            if(e.getKeyCode() == KeyEvent.VK_S) {
                
                if(spriteNum==1) {
                    imagePlayer=playerDown;
                }
                if(spriteNum==2) {
                    imagePlayer=playerClosedMouth;
                }
                
                playerSpeedX=0;
                playerSpeedY=1;
            }
            
            spriteCounter++;
            if(spriteCounter>10) {
                if(spriteNum==1) {
                    spriteNum=2;
                }
                else if(spriteNum==2) {
                    spriteNum=1;
                }
                spriteCounter=0;
            }
            
        }

    
    public void keyTyped(KeyEvent e) {
        

    }



    public void keyReleased(KeyEvent e) {
        
    }

}

还有主要的:

package playerAnimation;

import javax.swing.JFrame;

public class Main {

    public static void main(String[] args) {
        
        Animation player = new Animation();
        JFrame Window = new JFrame();
        
        Window.setTitle("playerAnimation");
        Window.setSize(600,400);
        Window.setVisible(true);
        Window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Window.add(player);
    
    }

}

这是照片:

playerClosedMouth

playerDown

playerLeft

playerRight

playerUp

【问题讨论】:

  • 1) 绘制方法应该只绘制类的当前状态,而不是改变状态。您当前的逻辑是更改要绘制的图像。该逻辑应移至侦听器类。所以基本上不需要“playerDirection”变量。相反,您只需在处理 KeyEvent 时设置“imagePlayer”变量。绘画方法然后只是绘画图像。 2) 你的定时器启动太快了。 1ms 不实用。也许它触发得太快并且重绘结果合并为一个,因此您看不到图像发生变化。尝试使用 1000 看看是否有效。
  • 我照你说的做了。在paintComponent 方法中,我只留下了 g.drawImage (imagePlayer, playerX, playerY, null);相反,在 keyPressed 方法中,我在按下键的每个条件中放置了两个 if,例如(如果 spriteNum == 1)playerImage 变为 playerUp,并且(如果 spriteNum == 2)playerImage 变为 playerClosedMouth。这与密钥检测的所有条件有关。不幸的是,没有任何改变。只有按住按键时才会出现嘴部动画。
  • 请包含图像或从 URL 热链接它们,这样我们也可以测试代码。并根据 camickr 提出的建议发布编辑后的代码
  • 完成。我添加了照片并使用 camickr 所做的更正编辑了代码
  • Timer timer 从未使用过

标签: java swing animation 2d


【解决方案1】:

欢迎来到 SO。请阅读代码中的 cmets 以了解为显示基本 packman 播放器动画所做的更改。
有关更多增强建议,请参阅 todo cmets:

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;

public class Animation extends JPanel implements ActionListener, KeyListener  {

    private int playerX , playerY, playerSpeedX, playerSpeedY;

    private boolean isMouthOpen; //flag to toggle mouth open / closed

    private static final Dimension P_SIZE = new Dimension(600,400);

    public BufferedImage playerUp, playerDown, playerLeft, playerRight, playerClosedMouth, imagePlayer;

    //publicly available images for the MRE
    private static final String IMAGE_UP ="https://i.stack.imgur.com/wD64A.png",
                                IMAGE_DOWN ="https://i.stack.imgur.com/am6PQ.png",
                                IMAGE_LEFT ="https://i.stack.imgur.com/5Pjqm.png",
                                IMAGE_RIGHT = "https://i.stack.imgur.com/sVD6m.png",
                                IMAGE_CLOSED = "https://i.stack.imgur.com/Kkw83.png"
                                ;

    public void getPlayerImage() {
        try {
            playerUp = ImageIO.read(new URL(IMAGE_UP));
            playerDown = ImageIO.read(new URL(IMAGE_DOWN));
            playerLeft = ImageIO.read(new URL(IMAGE_LEFT));
            playerRight = ImageIO.read(new URL(IMAGE_RIGHT));
            playerClosedMouth = ImageIO.read(new URL(IMAGE_CLOSED));
        }catch(IOException e ) {
            e.printStackTrace();
        }
    }

    public Animation() {
        getPlayerImage();

        //initialize player image, location and speed
        imagePlayer = playerRight;
        playerX = 100; playerY = 100; playerSpeedX = 1; playerSpeedY = 0;
        isMouthOpen = true;
        addKeyListener(this);
        setFocusable(true);
        Timer tm = new Timer(100,this);
        tm.start();
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        //if mouth is open - draw mouth closed and vice versa
        BufferedImage  image = isMouthOpen ? playerClosedMouth : imagePlayer;
        g.drawImage(image, playerX, playerY, this);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        checkBounds();
        playerX=playerX+playerSpeedX;
        playerY=playerY+playerSpeedY;
        isMouthOpen = ! isMouthOpen; //toggle mouth flag 
        //todo add control parameters to allow toggling mouth at a different rate 
        repaint();
    }

    //use key presses to change direction and player image 
    //todo: use key binding instead of KeyListener
    @Override
    public void keyPressed(KeyEvent e) {

        if(e.getKeyCode() == KeyEvent.VK_W) {
            playerSpeedX=0;
            playerSpeedY=-1;
            imagePlayer=playerUp;
        }

        if(e.getKeyCode() == KeyEvent.VK_D) {
            playerSpeedX=1;
            playerSpeedY=0;
             imagePlayer=playerRight;
        }

        if(e.getKeyCode() == KeyEvent.VK_A) {
            playerSpeedX=-1;
            playerSpeedY=0;
            imagePlayer=playerLeft;
        }

        if(e.getKeyCode() == KeyEvent.VK_S) {
            playerSpeedX=0;
            playerSpeedY=1;
            imagePlayer=playerDown;
        }
    }

    private void checkBounds(){
        //todo change direction when player hits bounds 
    }
    
    @Override
    public void keyTyped(KeyEvent e) { }
    @Override
    public void keyReleased(KeyEvent e) { }

    @Override
    public Dimension getPreferredSize() {
        return P_SIZE;
    }

    public static void main(String[] args) {

        Animation player = new Animation();
        JFrame window = new JFrame();
        window.setTitle("Player Animation");
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        window.add(player);
        window.pack();
        window.setVisible(true);
    }
}

【讨论】:

    【解决方案2】:

    只有按住按键时才会出现嘴巴动画

    如果您希望动画在不按住键的情况下发生,则切换图像的逻辑需要移动到 Timer ActionListener,以便它在您更改图像位置的同时发生变化。

    (未经测试的)逻辑可能类似于:

    if(e.getKeyCode() == KeyEvent.VK_W) {
        playerOpenMouth = playerUp;
        playerSpeedX=0;
        playerSpeedY=-1;
    }
    
    ...
    

    在 ActionListener 中切换开/闭嘴图像的逻辑可能类似于:

    if (openMouth)
        imagePlayer = playerOpenMouth;
    else
        imagePlayer = playerClosedMouth;
    
    openMouth = !openMouth;
    
    playerX = playerX + playerSpeedX; // use white space to improve readability
    playerY = playerY + playerSpeedY;
    repaint();
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-04-20
      • 2015-03-31
      • 2014-09-14
      • 2015-11-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-24
      相关资源
      最近更新 更多