下面是使用背景作为简单游戏循环的简单示例。它更新游戏对象的状态并计算所需的延迟以保持所需的 fps。
游戏对象 ( Ship
) 具有在短时间内加速/减速的能力
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.geom.Path2D;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class AnimationTest {
public static void main(String[] args) {
new AnimationTest();
}
public AnimationTest() {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new GamePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class GamePane extends JPanel {
private Ship ship;
public GamePane() {
ship = new Ship();
Thread thread = new Thread(new MainLoop(this));
thread.setDaemon(true);
thread.start();
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
// Key controls...
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "upPressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "downPressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), "upReleased");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "downReleased");
am.put("upPressed", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
// Change the direction...
ship.setDirection(-1);
// Accelerate by 1 per frame
ship.setVelocity(1);
}
});
am.put("downPressed", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
// Change direction
ship.setDirection(1);
// Accelerate by 1 per frame
ship.setVelocity(1);
}
});
am.put("upReleased", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
// Deccelerate by 1 per frame
ship.setVelocity(-1);
}
});
am.put("downReleased", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
// Deccelerate by 1 per frame
ship.setVelocity(-1);
}
});
}
@Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
public void updateState() {
// Update the state of the game objects.
// This would typically be better done in
// some kind of model
ship.update(getWidth(), getHeight());
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// Paint the game state...
Graphics2D g2d = (Graphics2D) g.create();
ship.paint(g2d);
g2d.dispose();
}
}
public class MainLoop implements Runnable {
private GamePane pane;
private int fps = 25;
public MainLoop(GamePane pane) {
this.pane = pane;
}
@Override
public void run() {
// Wait until the screen is ready
while (pane.getHeight() <= 0) {
try {
Thread.sleep(125);
} catch (InterruptedException ex) {
}
}
// Main loop
while (true) {
// Start time loop
long startTime = System.currentTimeMillis();
// Update the game state
pane.updateState();
// Calculate the amount of time it took to update
long elasped = System.currentTimeMillis() - startTime;
// Calculate the number of milliseconds we need to sleep
long sleep = Math.round((1000f / fps) - elasped);
pane.repaint();
if (sleep > 0) {
try {
Thread.sleep(sleep);
} catch (InterruptedException ex) {
}
}
}
}
}
public static class Ship {
public static int MAX_SPEED = 8;
private int direction = 0;
private int velocity = 0;
private int x;
private int y;
private int speed = 0;
private Path2D shape;
private boolean initState;
public Ship() {
shape = new Path2D.Float();
shape.moveTo(0, 0);
shape.lineTo(5, 5);
shape.lineTo(0, 10);
shape.lineTo(0, 0);
shape.closePath();
initState = true;
}
public void setDirection(int direction) {
this.direction = direction;
}
public void setVelocity(int velocity) {
this.velocity = velocity;
}
public void update(int width, int height) {
if (initState) {
y = (height - 10) / 2;
initState = false;
} else {
// Add the velocity to the speed
speed += velocity;
// Don't over accelerate
if (speed > MAX_SPEED) {
speed = MAX_SPEED;
} else if (speed < 0) {
speed = 0;
}
// Adjust out position if we're moving
if (speed > 0) {
y += (direction * speed);
}
// Bounds check...
if (y - 5 < 0) {
y = 5;
} else if (y + 5 > height) {
y = height - 5;
}
}
}
public void paint(Graphics2D g2d) {
int yPos = y - 5;
g2d.translate(10, yPos);
g2d.fill(shape);
g2d.translate(-10, -yPos);
}
}
}