我一直在做一个2D游戏,我觉得我过度复杂化了如何让动画只循环一次。例如,如果用户按下' a'关键,角色的攻击'变量设置为true,然后游戏将不断地将动画精灵图像渲染到屏幕上,直到释放该键。问题是,我希望攻击动画只发生一次,无论是' a'按键保持0.001秒或1分钟。
我觉得解决方案就像在渲染动画的最后一个精灵帧时将变量设置为false一样简单。并且只有在该变量为true且控制被按下时才会呈现动画。这是一个非常广泛的问题(可能有很多解决方案),所以如果我遗漏任何细节,请告诉我。下面是一个代码示例,删除了所有内容,除了与1个玩家操作有关的内容(此示例中为攻击):
游戏类(w / main()):
public class Game extends Canvas implements Runnable{
private static final long serialVersionUID = 1L;
public static final int WIDTH = 1200, HEIGHT = 600, SCALE = 1;
public static boolean running = false;
public Thread gameThread;
private BufferedImage p1AttackSpriteSheet;
private ImageManager im;
private static Player1 player1;
public void init(){
ImageLoader loader = new ImageLoader();
//Prepare sprite sheets (the loading takes some time...)
p1AttackSpriteSheet = loader.load("/player1/attackSpriteSheet.png");
SpriteSheet SS1 = new SpriteSheet(p1AttackSpriteSheet);
im = new ImageManager(SS1);
player1 = new Player1(-50, 200, im);
}
public synchronized void start() {
if(running)return;
running = true;
gameThread = new Thread(this);
gameThread.start();
}
public synchronized void stop() {
if(!running)return;
running = false;
try {
gameThread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void run() {
// Credit for this run loop goes to MadProgrammer @ StackOverflow
init();
final long amountOfTicks = 60;
long ns = Math.round(1_000_000_000 / (double)amountOfTicks);
int frames = 0;
long frameStart = System.currentTimeMillis();
while (running) {
long startedAt = System.nanoTime();
tick();
render();
long completedAt = System.nanoTime();
long duration = completedAt - startedAt;
long frameEnd = System.currentTimeMillis();
if (frameEnd - frameStart >= 1000) {
System.out.println(frames); //Take this out for final version
frames = 0;
frameStart = System.currentTimeMillis();
} else {
frames++;
}
long rest = ns - duration;
if (rest > 0) {
rest = TimeUnit.MILLISECONDS.convert(rest, TimeUnit.NANOSECONDS);
try {
Thread.sleep(rest);
} catch (InterruptedException ex) {
}
}
}
stop();
}
public void tick() {
player1.tick();
}
public void render() {
BufferStrategy bs = this.getBufferStrategy();
if(bs == null)
{
createBufferStrategy(3); //Use 5 at most
return;
}
Graphics g = bs.getDrawGraphics();
//RENDER HERE
player1.render(g);
//END RENDER
g.dispose();
bs.show();
}
public static void main(String[] args)
{
JLabel controlKeyPanel;
Game game = new Game();
game.setPreferredSize(new Dimension(WIDTH * SCALE, HEIGHT * SCALE));
JFrame frame = new JFrame("-");
frame.setResizable(false);
frame.setSize(WIDTH * SCALE, HEIGHT * SCALE);
frame.setLayout(new BorderLayout());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(game);
frame.setVisible(true);
game.start();
}
public static Player1 getPlayer1() {
return player1;
}
public static Player2 getPlayer2() {
return player2;
}
KeyManager类:
public class KeyManager implements KeyListener{
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if(keyCode == KeyEvent.VK_A) //P1 Attack
{
Game.getPlayer1().attack1 = true;
}
}
public void keyReleased(KeyEvent e) {
int keyCode = e.getKeyCode();
if(keyCode == KeyEvent.VK_C) //P1 Attack
{
Game.getPlayer1().attack1 = false;
}
}
}
Player1类:
public class Player1 {
private ImageManager im;
public boolean attack1 = false;
private int x1, y1;
private Animation P1AttackLeftAnimation;
public Player1(int x, int y, ImageManager im) {
this.x1 = x;
this.y1 = y;
this.im = im;
P1AttackLeftAnimation = new Animation(im.player1AttackLeft, 20);
}
public void tick() {
P1AttackLeftAnimation.tick();
}
public void render(Graphics g) {
if(attack1)
{
P1AttackLeftAnimation.render(g, x1, y1, 400 * Game.SCALE, 400 * Game.SCALE);
}
}
}
动画类:
public class Animation {
private BufferedImage[] images;
private int interval, index;
private long timer, now, lastTime;
public Animation(BufferedImage[] images, int interval) {
this.images = images;
this.interval = interval;
index = 0;
timer = 0;
now = 0;
lastTime = System.currentTimeMillis();
}
public void tick() {
now = System.currentTimeMillis();
timer += now - lastTime;
lastTime = now;
if(timer >= interval)
{
index++;
timer = 0;
if(index >= images.length)
{
index = 0;
}
}
}
public void render(Graphics g, int x, int y, int width, int height) {
g.drawImage(images[index], x, y, width, height, null);
}
}
ImageManager类:
public class ImageManager {
public BufferedImage [] player1AttackLeft;
public ImageManager(SpriteSheet ss1) {
//Player 1 Attack images
player1AttackLeft = new BufferedImage[12];
player1AttackLeft[0] = ss1.crop(0, 0, 400, 400);
player1AttackLeft[1] = ss1.crop(1, 0, 400, 400);
player1AttackLeft[2] = ss1.crop(2, 0, 400, 400);
player1AttackLeft[3] = ss1.crop(3, 0, 400, 400);
player1AttackLeft[4] = ss1.crop(4, 0, 400, 400);
player1AttackLeft[5] = ss1.crop(5, 0, 400, 400);
player1AttackLeft[6] = ss1.crop(6, 0, 400, 400);
player1AttackLeft[7] = ss1.crop(7, 0, 400, 400);
player1AttackLeft[8] = ss1.crop(8, 0, 400, 400);
player1AttackLeft[9] = ss1.crop(9, 0, 400, 400);
player1AttackLeft[10] = ss1.crop(10, 0, 400, 400);
player1AttackLeft[11] = ss1.crop(11, 0, 400, 400);
}
}
SpriteSheet类:
public class SpriteSheet {
private BufferedImage sheet;
public SpriteSheet(BufferedImage sheet) {
this.sheet = sheet;
}
public BufferedImage crop(int col, int row, int w, int h) {
return sheet.getSubimage(col * 400, row * 400, w, h);
}
}