0

我是java新手。我正在尝试制作小行星克隆游戏,但遇到了麻烦。

我不确定如何将 graphics2d 对象添加回 jframe。任何帮助,将不胜感激。另外,我计划在此基础上添加更多内容,如果看起来有点傻,请见谅。
目前 jframe 绘制但我没有在里面得到任何对象。

主班

import java.applet.*;
import javax.swing.*;

public class Program extends JApplet
{
public void init()
{
    String[] s = new String[0];
    main(s);
};

public static void main(String[] args)
{
    System.out.println("Launching Window");
    Window w = new Window();
    w.setSize(700, 600);
    w.setLocation(50,50);
    w.setVisible(true);
    w.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    System.out.println("Launching Game");
    Game g = new Game();

    while(g.play)
    {
        System.out.println("Looping");
        g.tick();
        w.repaint();
    };
}
};

Gui Stuff(不确定我是否需要这个或与game.java合并)

import javax.swing.*;

public class Window extends JFrame
{
    private JPanel panel;

    public Window()
    {
        panel = new JPanel();
        add(panel);
    };

};

Game.java,程序的主要逻辑,还不完整

import java.applet.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;

public class Game implements KeyListener
{
    public static int xmax = 800;
    public static int ymax = 600;

    private LinkedList<Rock> rocks = new LinkedList();
    private LinkedList<Laser> lasers = new LinkedList();
    private Ship ship;

    private int score = 0;
    public boolean play = true;

    public Game()
    {
        ship = new Ship(xmax/2, ymax/2);    //Game objects
        rocks.add(new Rock(50, 50, 50));
    };

    public void reinit()
    {
        ship = null;
        rocks.clear();
        lasers.clear();
        score = 0;
    };

    public void tick()
    {
        for(int i = 0; i < rocks.size(); i++)   //Check to see if bullets hit unit, remove that unit
        {
            for(int j = 0; j < lasers.size(); j++)
            {
                if(rocks.get(i).hit(rocks.get(j)))
                {
                    if(rocks.get(i).size > 20)  //if rocks bigger than 20 spawn some more rocks
                    {
                        rocks.add(new Rock(rocks.get(j).x, rocks.get(j).y, rocks.get(j).size/3));   //add three rocks on hit
                        rocks.add(new Rock(rocks.get(j).x, rocks.get(j).y, rocks.get(j).size/3));
                        rocks.add(new Rock(rocks.get(j).x, rocks.get(j).y, rocks.get(j).size/3));
                    };
                    rocks.remove(j);                                                                //remove hit rock
                    lasers.remove(i);                                                               //remove laser
                };
            };
        };

        for(int i = 0; i < rocks.size(); i++) //move units
        {
            rocks.get(i).tick();
        };

        for(int i = 0; i < lasers.size(); i++) //move bullets
        {
            lasers.get(i).tick();
        };

        ship.tick();
    };

    public void keyPressed(KeyEvent e) 
    {
        if(e.getKeyCode()==KeyEvent.VK_SPACE)
            lasers.add(new Laser(ship.x, ship.y, ship.angle));
        if (e.getKeyCode()==KeyEvent.VK_UP)
            ship.thrust();
        if (e.getKeyCode()==KeyEvent.VK_LEFT)
            ship.left();
        if (e.getKeyCode()==KeyEvent.VK_RIGHT)
            ship.right();
        if (e.getKeyCode()==KeyEvent.VK_Q)
            play = false;
    };

    public void keyReleased(KeyEvent e)
    {

    };

    public void keyTyped(KeyEvent e)
    {

    };

};

保存对象的通用类

import java.lang.Math;

public class Coords
{
    public static double RAD = 0.0174532925;
    public static int xmax = 800;
    public static int ymax = 600;

    public double x     = 0.0;
    public double y     = 0.0;
    public double dx    = 0.0;
    public double dy    = 0.0;
    public double vel   = 0.0;
    public double size = 0.0;
    public double angle= 0.0;
    public double da    = 0.0;

    public Coords()
    {
    };

    public void reinit()
    {
        x       = 0.0;
        y       = 0.0;
        dx      = 0.0;
        dy      = 0.0;
        vel     = 0.0;
        size    = 0.0;
        angle   = 0.0;
        da      = 0.0;
    };

    public void rot()
    {


    };

    public boolean oob(int xmax, int ymax)
    {
        if((x < xmax) && (x > 0) && (y < ymax) && (y > 0))
            return true;
        return false;
    };

    public boolean hit(Coords in)
    {
        if((in.x + in.size > x) && (in.x < x + size) && (in.y + in.size > y) && (in.y < y + size))
            return true;
        return false;
    };

    public void tick()
    {
        x = (dx + x) % xmax;
        y = (dy + y) % ymax;
        angle = (angle + da) % 360;
    };
};

宇宙飞船级

import java.applet.*;
import java.lang.Math;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;

public class Ship extends Coords
{
    private ImageIcon imageIcon;
    private Image image;

    public Ship(double x_in, double y_in)
    {
        imageIcon = new ImageIcon(getClass().getResource("images/ship.png"));
        image = imageIcon.getImage();

        x = x_in;
        y = y_in;
    };

    public void thrust()
    {
        dx = dx + (vel * Math.sin(angle * RAD));  //Move points based on speed
        dy = dy + (vel * Math.cos(angle * RAD));
    };

    public void left()
    {
        da--;
    };

    public void right()
    {
        da++;
    };

    public void draw(Graphics graphics)
    {
        Graphics2D g = (Graphics2D)graphics;
        g.rotate(angle);
        g.drawImage(image, (int)x, (int)y, null);
    };
};

激光类

import java.applet.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;

public class Laser extends Coords
{
    private ImageIcon imageIcon;
    private Image image;

    public Laser(double x_in, double y_in, double angle_in)
    {
        imageIcon = new ImageIcon(getClass().getResource("images/laser.png"));
        image = imageIcon.getImage();

        angle = angle_in;

        vel = 20;
    };

    public void draw(Graphics graphics)
    {
        Graphics2D g = (Graphics2D)graphics;
        g.rotate(angle);
        g.drawImage(image, (int)x, (int)y, null);
    };
};

小行星类

import java.applet.*;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.util.Random;

public class Rock extends Coords
{
    private ImageIcon imageIcon;
    private Image image;

    public Rock(double x_in, double y_in, double size_in)
    {
        imageIcon = new ImageIcon(getClass().getResource("images/rock.png"));
        image = imageIcon.getImage();

        x = x_in;
        y = y_in;
        size = size_in;

        Random random = new Random();
        int r = random.nextInt();
        angle = r % 360;
        vel = r % 5;
    };

    public void draw(Graphics graphics)
    {
        Graphics2D g = (Graphics2D)graphics;
        g.rotate(angle);
        g.drawImage(image, (int)x, (int)y, null);
    };
};
4

1 回答 1

1

我可以想出很多方法来解决这个问题。您将遇到的最大问题是同步更新以防止模型在开始渲染时开始更新。

基础知识

为了获得合理的结果,您将需要一个模型、一个视图和一个驱动程序/控制器。

该模型负责维护游戏资产的状态。视图负责将模型渲染到屏幕上,控制器负责更新模型和视图以及协调输入。

您或多或少地设置了这些基本概念。

认为我可能会改变。

  • 我倾向于使用s 的键绑定。KeyListener键绑定克服了KeyListener焦点管理的限制
  • 使用JPanel(或其他类似容器)而不是直接绘制到任何顶级容器(例如JFrameJApplet)。这有很多好处,Swing 容器默认是双缓冲的,您现在可以通过简单地将游戏面板添加到您选择的顶级容器来选择部署。
  • 我会从游戏面板向控制器发送关键事件(而不是将控制器作为侦听器直接附加到视图)。这主要是个人选择,但它使您能够对传入事件进行排队并更好地控制这些输入的处理方式

你需要的东西

  • 您的驱动程序将需要线程化。这意味着驱动程序将在它自己Thread的事件调度线程之外运行。这允许 UI 继续更新,同时驱动程序正在更新模型并准备输出和处理输入。线程可能需要“等待”以在周期之间保持帧速率(并允许 UI 自行绘制)。

到屏幕及其他地方

让你的游戏出现在屏幕上比某些人想象的要复杂一些。

最基本的方法是简单地更新模型并调用repaint负责绘制它的组件,然后让渲染器简单地paint处理模型。

这种方法的问题是;

  1. 不可能知道油漆何时/实际发生
  2. 在进行重绘时可能会更改模型,这将导致脏漆以及模型和视图之间的不一致

您需要的是某种方式来生成视图并将该视图更新到屏幕上。

BufferedImage基本思想是让视图生成控制器可以请求的后备缓冲区(例如 a )。然后控制器将使用来自模型的信息更新此缓冲区。然后它将请求视图使用此缓冲区更新自身。

视图需要同步请求以确保虚拟视图之间的切换不在绘制周期内执行。

这通常意味着控制器能够在绘制周期之间产生许多“更新”而不影响视图,并且视图应该始终绘制(尽可能接近)最新的“帧”。

为了同步开关,您可以使用受保护的代码块(synchronized语句中的代码块)。这样做的问题是,您还需要synchronize使用绘画方法,这绝不是一个好主意。

相反,您应该使用SwingUtilities#invokeAndWait. 这将用于调用视图的 switch 方法,传入最新的缓冲区。这将确保更新请求将在事件调度线程中发生,这意味着绘制方法不可能运行(因为它也在事件调度线程中运行)。它还会在切换发生时“停止”控制器。

我个人会设置一个BufferedImages 池(通常是两个,但它需要具有根据需要增长和缩小的能力)。这个想法是,当控制器请求一个“缓冲区”时,您弹出队列的最顶部缓冲区并返回它。如果队列中不存在,您将创建一个新的并返回它。

当缓冲区被返回(并且你切换它们)时,你将把这个缓冲区返回到队列中。

我会在后台有一些计时器定期触发以清理池(删除多余的缓冲区)以确保我们不会不必要地咀嚼内存,但这只是我;)

于 2012-11-15T19:35:19.590 回答