如何使用Java Swing通过持续更新坐标实现弹幕游戏抛射物移动?
Java弹幕游戏抛射物移动实现方案
核心思路
别直接写死坐标,把每个抛射物的位置、速度、形状数据封装成独立对象,在游戏循环里持续更新位置,再根据新位置重新绘制形状——这是2D游戏里移动元素的标准做法。
步骤1:封装抛射物类
把抛射物的属性(位置、速度、形状偏移)和绘制方法打包成类,批量管理子弹会更方便:
import java.awt.Color; import java.awt.Graphics; import java.awt.Polygon; public class Projectile { // 抛射物中心坐标 private int x; private int y; // 移动速度(x、y方向的增量) private int dx; private int dy; // 形状相对于中心的偏移量(从你原来的固定坐标提取) private static final int[] RELATIVE_X = {0, 4, 7, 7, 2, -4, -12, -12, -7, -5}; private static final int[] RELATIVE_Y = {-20, -14, -10, 8, 20, 20, 8, -10, -16, -20}; private static final double INNER_SCALE = 0.8; // 内部高亮的缩放比例 public Projectile(int startX, int startY, int speedX, int speedY) { this.x = startX; this.y = startY; this.dx = speedX; this.dy = speedY; } // 更新位置 public void update() { x += dx; y += dy; } // 绘制抛射物 public void draw(Graphics g) { // 绘制外层橙色多边形 g.setColor(Color.ORANGE); Polygon outer = new Polygon(); for (int i = 0; i < RELATIVE_X.length; i++) { outer.addPoint(x + RELATIVE_X[i], y + RELATIVE_Y[i]); } g.fillPolygon(outer); // 绘制内层青色高亮 g.setColor(Color.CYAN); Polygon inner = new Polygon(); for (int i = 0; i < RELATIVE_X.length; i++) { int scaledX = (int) (RELATIVE_X[i] * INNER_SCALE); int scaledY = (int) (RELATIVE_Y[i] * INNER_SCALE); inner.addPoint(x + scaledX, y + scaledY); } g.fillPolygon(inner); } }
说明:RELATIVE_X/Y是从你原来的固定坐标(中心220,120)计算出的偏移量,比如原来的点220,100对应偏移0,-20,移动时只用改x/y中心值,不用重新定义所有点。
步骤2:实现游戏循环与渲染
用Swing的Timer驱动游戏循环,定时更新所有抛射物位置并重新绘制:
import java.awt.Graphics; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.Timer; public class BulletHellGame extends JPanel implements ActionListener { private ArrayList<Projectile> projectiles = new ArrayList<>(); private Timer gameTimer; public BulletHellGame() { // 初始化游戏循环(每16ms触发一次,约60帧) gameTimer = new Timer(16, this); gameTimer.start(); // 添加测试抛射物,比如向下移动,速度(0,3) projectiles.add(new Projectile(220, 120, 0, 3)); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); // 清屏(背景色设为黑色) g.setColor(Color.BLACK); g.fillRect(0, 0, getWidth(), getHeight()); // 更新并绘制所有抛射物 for (Projectile p : projectiles) { p.update(); p.draw(g); } } @Override public void actionPerformed(ActionEvent e) { // 触发重绘 repaint(); } public static void main(String[] args) { JFrame frame = new JFrame("Bullet Hell Game"); frame.setSize(800, 600); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(new BulletHellGame()); frame.setVisible(true); } }
说明:
paintComponent是Swing的绘制入口,每次repaint()都会调用它- 先清屏再绘制,避免残留上一帧的图像
Timer的16ms间隔对应约60FPS,保证移动流畅
扩展建议
- 添加生命周期判断:抛射物超出屏幕后从列表移除,避免内存占用
- 用浮点数存储位置和速度,实现更平滑的移动(需把x/y改为double类型,绘制时强转int)
- 批量生成抛射物时,直接往
projectiles列表中添加新对象即可
内容的提问来源于stack exchange,提问作者shge
相关产品推荐
相关产品推荐

