我正在尝试通过我正在观看的教程学习如何构建一个简单的游戏。到目前为止一切都很好,但是当我移动图像时,前一个图像不会被删除或丢弃。我不确定到底是什么错,或者为什么会发生错误。我有3个类,一个主类,一个播放器类和一个bufferimageloader类。
主要课程:
import java.awt.Canvas;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.swing.JFrame;
public class Main extends Canvas implements Runnable {
private boolean running = false;
private Thread thread;
private BufferedImage player;
private Player p;
public void init(){ // load and initiliaze
BufferedImageLoader loader = new BufferedImageLoader();
try {
player = loader.loadImage("/player_shotgun2.png");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
p = new Player(100, 100, this);
}
private synchronized void start(){
if(running)
return;
running = true;
thread = new Thread(this);
thread.start();
}
private synchronized void stop(){
if(!running)
return;
running = false;
try {
thread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.exit(1);
}
public void run() {
init();
long lastTime = System.nanoTime();
final double amountOfTicks = 60.0;
double ns = 1000000000 / amountOfTicks;// 1 second divided by 60, run 60 times per second
double delta = 0;
int updates = 0;
int frames = 0;
long timer = System.currentTimeMillis();
System.out.println("hi");
while(running){
long now = System.nanoTime();
delta += (now - lastTime) / ns;
lastTime = now;
if(delta >= 1){// delta = 1 = 1 second
tick();
updates++;
delta--;
}
render();
frames++;
if(System.currentTimeMillis() - timer > 1000){
timer+= 1000;
System.out.println(updates + " Ticks, Fps " + frames);
updates = 0;
frames = 0;
}
}
stop();
}
// Everything thats is updated in the game
private void tick(){
p.tick();
}
// Everything that is rendered in the game
private void render(){
BufferStrategy bs = this.getBufferStrategy();
if(bs == null){
createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
//////////////////////////////
p.render(g);
//////////////////////////////
g.dispose();
bs.show();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Main main = new Main();
JFrame window = new JFrame();
window.setSize(500,600);
window.setTitle("Zombie Game");
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
window.add(main);
main.start();
}
public BufferedImage getPlayerImage(){
return player;
}
}
玩家类:
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import javax.swing.JPanel;
public class Player extends JPanel {
private double x;
private double y;
public BufferedImage player;
public Player(double x, double y, Main main){
this.x = x;
this.y = y;
player = main.getPlayerImage();
}
public void tick(){
x++;
}
public void render(Graphics g){
super.paintComponent(g);
g.drawImage(player, (int)x, (int)y, null);
g.dispose();
}
}
Bufferedimageloader类:
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
public class BufferedImageLoader {
private BufferedImage image;
public BufferedImage loadImage(String path) throws IOException{
image = ImageIO.read(getClass().getResource(path));
return image;
}
}
这是我启动时获得的输出并且图像移动:
答案 0 :(得分:4)
这是一个名为Moving Eyes的简单Swing应用程序。当您在GUI的绘图区域中移动光标时,GUI中的眼球会跟随鼠标光标。
我意识到它没有做你想做的事。我提供此代码,以便您可以看到如何进行简单的Swing动画。您可以使用此代码作为自己动画的基础。
这是Swing GUI。
我在创建这个Swing GUI时使用了model / view / controller model。这意味着:
基本上,模型不了解视图和控制器。这允许您将视图和控制器从Swing更改为网站或Android应用程序。
模型/视图/控制器模式允许您一次关注Swing GUI的一部分。通常,您将首先创建模型,然后创建视图,最后创建控制器。您将不得不返回并向模型添加字段。
这是代码:
package com.ggl.testing;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class MovingEyes implements Runnable {
private static final int drawingWidth = 400;
private static final int drawingHeight = 400;
private static final int eyeballHeight = 150;
private static final int eyeballWidthMargin = 125;
private DrawingPanel drawingPanel;
private Eye[] eyes;
private JFrame frame;
public static void main(String[] args) {
SwingUtilities.invokeLater(new MovingEyes());
}
public MovingEyes() {
this.eyes = new Eye[2];
this.eyes[0] = new Eye(new Point(eyeballWidthMargin, eyeballHeight));
this.eyes[1] = new Eye(new Point(drawingWidth - eyeballWidthMargin,
eyeballHeight));
}
@Override
public void run() {
frame = new JFrame("Moving Eyes");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
drawingPanel = new DrawingPanel(drawingWidth, drawingHeight);
frame.add(drawingPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public class DrawingPanel extends JPanel {
private static final long serialVersionUID = -2977860217912678180L;
private static final int eyeballOuterRadius = 50;
private static final int eyeballInnerRadius = 20;
public DrawingPanel(int width, int height) {
this.addMouseMotionListener(new EyeballListener(this,
eyeballOuterRadius - eyeballInnerRadius - 5));
this.setBackground(Color.WHITE);
this.setPreferredSize(new Dimension(width, height));
}
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLACK);
for (Eye eye : eyes) {
drawCircle(g, eye.getOrigin(), eyeballOuterRadius);
fillCircle(g, eye.getEyeballOrigin(), eyeballInnerRadius);
}
}
private void drawCircle(Graphics g, Point origin, int radius) {
g.drawOval(origin.x - radius, origin.y - radius, radius + radius,
radius + radius);
}
private void fillCircle(Graphics g, Point origin, int radius) {
g.fillOval(origin.x - radius, origin.y - radius, radius + radius,
radius + radius);
}
}
public class Eye {
private final Point origin;
private Point eyeballOrigin;
public Eye(Point origin) {
this.origin = origin;
this.eyeballOrigin = origin;
}
public Point getEyeballOrigin() {
return eyeballOrigin;
}
public void setEyeballOrigin(Point eyeballOrigin) {
this.eyeballOrigin = eyeballOrigin;
}
public Point getOrigin() {
return origin;
}
}
public class EyeballListener extends MouseMotionAdapter {
private final double eyeballDistance;
private final DrawingPanel drawingPanel;
public EyeballListener(DrawingPanel drawingPanel, double eyeballDistance) {
this.drawingPanel = drawingPanel;
this.eyeballDistance = eyeballDistance;
}
@Override
public void mouseMoved(MouseEvent event) {
Point p = event.getPoint();
for (Eye eye : eyes) {
Point origin = eye.getOrigin();
double theta = Math.atan2((double) (p.y - origin.y),
(double) (p.x - origin.x));
int x = (int) Math.round(Math.cos(theta) * eyeballDistance)
+ origin.x;
int y = (int) Math.round(Math.sin(theta) * eyeballDistance)
+ origin.y;
eye.setEyeballOrigin(new Point(x, y));
}
drawingPanel.repaint();
}
}
}
Eye类是一个Java对象,它保存眼睛的原点(圆圈)和眼球的起源。 Eye类是这个简单示例中的模型。
MovingEyes类是定义JFrame的类。 MovingEyes类是视图的一部分。此类的主要方法调用SwingUtilities invokeLater方法,以确保在Event Dispatch线程上定义和修改Swing组件。
我们使用JFrame。我们不扩展JFrame。扩展Swing组件或任何Java类的唯一时间是要覆盖其中一个类方法。当我谈到DrawingPanel时,我们会看到这一点。
MovingEyes类的构造函数定义了Eye类的2个实例。 run方法定义JFrame。对于所有Swing GUI,run方法中的代码都是类似的。
DrawingPanel类构成视图的其余部分。 DrawingPanel类扩展了JPanel,因为我们想要覆盖paintComponent方法。 DrawingPanel类的构造函数设置绘图区域的首选大小,并添加鼠标移动侦听器。鼠标移动侦听器是此Swing GUI的控制器。
DrawingPanel类的paintComponent方法首先调用super paintComponent方法。这样可以保持Swing绘制链,并且应该始终是覆盖的paintComponent方法的第一个语句。
DrawingPanel类的paintComponent方法中的其余代码吸引眼球。我们在paintComponent方法中只有绘图(绘画)代码。控制代码属于控制器。
EyeballListener类是控制器类。在更复杂的Swing GUI中,您可以拥有多个控制器类。
EyeballListener类扩展了MouseMotionAdapter。您可以实现MouseMotionListener。我正在覆盖一个方法,因此当我扩展MouseMotionAdapter时代码会更短。
移动鼠标时,EyeballListener类的mouseMoved方法会触发MouseEvent。我们通过找到从眼睛中心到鼠标位置的θ角来计算眼球中心的新位置。 θ角用于计算眼球的新中心。
每个Eye实例在for循环中单独更新。双眼更新后,绘图面板将重新绘制。这种情况发生得如此之快,以至于不需要在单独的线程中进行动画循环。
动画循环更新模型,绘制视图,并等待指定的时间段。您将为动画循环使用单独的线程,以便Event Dispatch线程上的GUI保持响应。如果您的GUI没有响应,那么您可能在Event Dispatch线程上做了太多工作。
答案 1 :(得分:2)
您是否看过BufferStrategy的示例代码? https://docs.oracle.com/javase/7/docs/api/java/awt/image/BufferStrategy.html
您只需要在程序开头创建一个BufferStrategy对象,而不是每一帧。但是您的旧图像不会被删除的原因是因为您永远不会删除它。你可以调用fillRect来做到这一点。