gpt4 book ai didi

java - 使用计时器的基本java子弹动画

转载 作者:行者123 更新时间:2023-12-01 12:09:27 24 4
gpt4 key购买 nike

我正在尝试创建一个基本的子弹动画,当按下空格键时,计时器会关闭,大约每秒将子弹的 y 值增加 100。然而,当我按下空格键时,不会出现任何内容,就好像子弹没有得到它的 y 坐标一样。这一切都发生在子弹离开屏幕时的 while 循环内

Timer t = new Timer();

if (e.getKeyCode() == KeyEvent.VK_SPACE) {
bullet.x = 100;
while (bullet.y<400) {
t.schedule(new TimerTask() {
@Override
public void run() {
bullet.y = bullet.y + 100;
}
}, 800);
}
}

最佳答案

我整理了一个基本的项目符号演示。

项目符号(椭圆形)以每秒 100 像素的速度从左向右(x 坐标)移动。当您按下空格键时,子弹会从 x = 20 像素和随机 y 坐标处发射。项目符号在距绘图面板右边缘 20 像素处消失。

Bullet Demonstration

我使用了model / view / controller pattern将这个演示放在一起。我编写了 2 个模型类、2 个 View 类、2 个 Controller 类和 1 个用于启动 Java Swing 应用程序的类。

第一个类 BulletDemo 启动 Java Swing 应用程序。

package com.ggl.bullet;

import javax.swing.SwingUtilities;

import com.ggl.bullet.model.BulletDemoModel;
import com.ggl.bullet.view.BulletDemoFrame;

public class BulletDemo implements Runnable {

@Override
public void run() {
new BulletDemoFrame(new BulletDemoModel());
}

public static void main(String[] args) {
SwingUtilities.invokeLater(new BulletDemo());
}

}

19 行 Swing 精华。该类做了 3 件事来启动 Swing 应用程序。

  1. 将 Swing 应用程序放在 Event Dispatch thread 上(EDT) 调用 SwingUtilities invokeLater 方法。
  2. 实例化 Bullet 演示模型。
  3. 实例化 Bullet 演示框架。

接下来,我们将了解 BulletDemoModel 类。

package com.ggl.bullet.model;

import java.awt.Dimension;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.List;

public class BulletDemoModel {

private int panelWidth = 600;
private int panelHeight = 300;

private List<Bullet> bullets;

public BulletDemoModel() {
this.bullets = new ArrayList<Bullet>();
}

public Dimension getPanelDimension() {
return new Dimension(panelWidth, panelHeight);
}

public int getPanelWidth() {
return panelWidth;
}

public int getPanelHeight() {
return panelHeight;
}

public void addBullet(Bullet bullet) {
this.bullets.add(bullet);
}

public void removeBullets() {
for (int i = bullets.size() - 1; i >= 0; i--) {
if (!bullets.get(i).onScreen()) {
bullets.remove(i);
}
}
}

public void moveBullets(int time) {
for (Bullet bullet : bullets) {
bullet.moveBullet(time);
}
}

public void draw(Graphics g) {
for (Bullet bullet : bullets) {
bullet.draw(g);
}
}

}

此类保留我们稍后将讨论的绘图面板的宽度和高度。这个类还跟踪子弹。可以添加、移动和删除项目符号。

draw 方法包含在模型中,因为对象更容易绘制自己。 draw 方法作为 View 的一部分执行,因此我们仍然需要关注点分离。

接下来,我们将看看另一个模型类 Bullet。

package com.ggl.bullet.model;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.geom.Point2D;

public class Bullet {

private int maxX;

private Point2D location;

public Bullet(Point2D location, int maxX) {
this.location = location;
this.maxX = maxX;
}

public void moveBullet(int time) {
double x = this.location.getX();
double y = this.location.getY();

x += 100D * (double) time / 6000D;

this.location.setLocation(x, y);
}

public boolean onScreen() {
int x = (int) Math.round(this.location.getX());
return x < maxX;
}

public void draw(Graphics g) {
int x = (int) Math.round(this.location.getX());
int y = (int) Math.round(this.location.getY());

if (onScreen()) {
g.setColor(Color.BLACK);
g.fillOval(x, y, 10, 10);
}
}

}

此类封装了与项目符号相关的字段和方法。

接下来,我们将查看 View 类,从 BulletDemoFrame 类开始。

package com.ggl.bullet.view;

import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;

import com.ggl.bullet.controller.BulletRunnable;
import com.ggl.bullet.controller.ShootBulletAction;
import com.ggl.bullet.model.BulletDemoModel;

public class BulletDemoFrame {

private BulletDemoModel model;

private BulletRunnable bulletRunnable;

private DrawingPanel drawingPanel;

private JFrame frame;

public BulletDemoFrame(BulletDemoModel model) {
this.model = model;
createPartControl();
}

private void createPartControl() {
drawingPanel = new DrawingPanel(model);

frame = new JFrame();
frame.setTitle("Bullet Demo");
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent event) {
exitProcedure();
}
});

setKeyBindings();

JPanel mainPanel = new JPanel();
mainPanel.add(drawingPanel);

frame.add(mainPanel);
frame.setLocationByPlatform(true);
frame.pack();
frame.setVisible(true);

this.bulletRunnable = new BulletRunnable(this, model);
new Thread(bulletRunnable).start();
}

private void setKeyBindings() {
InputMap inputMap = drawingPanel
.getInputMap(JPanel.WHEN_IN_FOCUSED_WINDOW);
inputMap.put(KeyStroke.getKeyStroke("SPACE"), "shoot bullet");

inputMap = drawingPanel.getInputMap(JPanel.WHEN_FOCUSED);
inputMap.put(KeyStroke.getKeyStroke("SPACE"), "shoot bullet");

drawingPanel.getActionMap().put("shoot bullet",
new ShootBulletAction(model));
}

public void exitProcedure() {
bulletRunnable.setRunning(false);
frame.dispose();
System.exit(0);
}

public void repaintDrawingPanel() {
drawingPanel.repaint();
}

}

注意我们如何使用 JFrame。我们不会扩展 Swing 组件或任何 Java 类,除非我们想要重写其中一个类方法。

createPartControl 方法主要是样板文件,适用于几乎所有 Swing 应用程序。

setKeyBindings 方法是我们为 ShootBulletAction 类设置空格键的地方,我们将在稍后讨论。

windowListener 和 exitProcedure 允许我们在退出之前停止子弹线程。

接下来,我们将了解 DrawingPanel 类。

package com.ggl.bullet.view;

import java.awt.Graphics;

import javax.swing.JPanel;

import com.ggl.bullet.model.BulletDemoModel;

public class DrawingPanel extends JPanel {

private static final long serialVersionUID = 6510468728309920700L;

private BulletDemoModel model;

public DrawingPanel(BulletDemoModel model) {
this.model = model;
this.setPreferredSize(model.getPanelDimension());
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
model.draw(g);
}

}

这里没有太多内容,因为所有绘图代码都在模型中。

我们扩展了 JPanel,以便可以重写 PaintComponent 方法。在paintComponent方法中,我们调用super方法,然后重绘整个面板。做得足够快就会产生动画的错觉。

接下来,我们将看看 Controller 类。我们首先看一下 ShootBulletAction 类。

package com.ggl.bullet.controller;

import java.awt.event.ActionEvent;
import java.awt.geom.Point2D;
import java.util.Random;

import javax.swing.AbstractAction;

import com.ggl.bullet.model.Bullet;
import com.ggl.bullet.model.BulletDemoModel;

public class ShootBulletAction extends AbstractAction {

private static final long serialVersionUID = -5783106403902351044L;

private BulletDemoModel model;

private Random random;

public ShootBulletAction(BulletDemoModel model) {
this.model = model;
this.random = new Random();
}

@Override
public void actionPerformed(ActionEvent event) {
double x = 20D;
double y = (double) random.nextInt(model.getPanelHeight() - 20) + 10;
Bullet bullet = new Bullet(new Point2D.Double(x, y),
model.getPanelWidth() - 20);
model.addBullet(bullet);
}

}

每次按下空格键时,此类都会创建一个项目符号。

最后,我们来看看 BulletRunnable 类。

package com.ggl.bullet.controller;

import javax.swing.SwingUtilities;

import com.ggl.bullet.model.BulletDemoModel;
import com.ggl.bullet.view.BulletDemoFrame;

public class BulletRunnable implements Runnable {

private volatile boolean running;

private long sleepTime = 100L;

private BulletDemoFrame frame;

private BulletDemoModel model;

public BulletRunnable(BulletDemoFrame frame, BulletDemoModel model) {
this.frame = frame;
this.model = model;
}

@Override
public void run() {
running = true;

while (running) {
model.moveBullets((int) sleepTime);
redraw();
sleep();
}
}

private void sleep() {
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {

}
}

private void redraw() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
frame.repaintDrawingPanel();
}
});
}

public synchronized void setRunning(boolean running) {
this.running = running;
}

}

该类是控制大部分动画的基本更新模型、绘制模型、 sleep 循环。

我们以每秒 10 帧的速度运行,这有点不稳定。如果您愿意,您可以减少 sleep 时间并每秒运行更多帧。

我知道这个答案很长,但这就是在 Swing 中制作基本动画的方法。感谢您的阅读。

关于java - 使用计时器的基本java子弹动画,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27345547/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com