Java 使用多个类渲染矩形

Java 使用多个类渲染矩形,java,graphics,Java,Graphics,我正在做一个简单的游戏项目,当我试图创建一个在屏幕上移动的矩形时遇到了一个问题。 以下是主要课程: `公共类Main扩展画布实现Runnable{ private static final long serialVersionUID = 1L; private JFrame frame; boolean running = false; Graphics g; static int HEIGHT = 500; static int WIDTH = HEIGHT * 16 / 9; Sou

我正在做一个简单的游戏项目,当我试图创建一个在屏幕上移动的矩形时遇到了一个问题。 以下是主要课程:

`公共类Main扩展画布实现Runnable{

private static final long serialVersionUID = 1L;

private JFrame frame;
boolean running = false;

Graphics g;

static int HEIGHT = 500;
static int WIDTH = HEIGHT * 16 / 9;

SoundHandler sh = new SoundHandler();

//Game state manager
private GameStateManager gsm;

public Main()
{
    //window
    frame = new JFrame("Game");

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setLayout(new BorderLayout());

    frame.add(this, BorderLayout.CENTER);
    frame.pack();

    frame.setSize(WIDTH, HEIGHT);
    frame.setResizable(false);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);

    init();
}

void init()
{
    gsm = new GameStateManager();
    sh.playMusic("Undertale.wav", 1);
}

public synchronized void start(){
    running = true;
    new Thread(this).start();
}

public synchronized void stop(){
    running = false;
}

//game loop
public void run() 
{
    //init time loop variables
    long lastLoopTime = System.nanoTime();
    final int TARGET_FPS = 60;
    final long OPTIMAL_TIME = 1000000000 / TARGET_FPS;
    double lastFpsTime = 0;
    int fps = 0;

    while(running)
    {
        //work out how long its been since last update
        //will be used to calculate how entities should
        //move this loop
        long now = System.nanoTime();
        long updateLength = now - lastLoopTime;
        lastLoopTime = now;
        double delta = updateLength / ((double)OPTIMAL_TIME);

        //update frame counter
        lastFpsTime += updateLength;
        fps++;

        //update FPS counter
        if(lastFpsTime >= 1000000000)
        {
            System.out.println("FPS " + fps);
            lastFpsTime = 0;
            fps = 0;
        }

        //game updates
        update(delta);

        //draw
        draw(g);

        try{
            Thread.sleep((lastLoopTime - System.nanoTime() + OPTIMAL_TIME)/1000000 );
        }catch(InterruptedException e){
            System.out.println("Error in sleep");
        }
    }
}

private void update(double delta)
{
    //updates game state code
    gsm.update(delta);
}

public void draw(Graphics g)
{
    gsm.draw(g);
}`
这是我想用来画矩形的类

package me.mangodragon.gamestate;
导入java.awt.Graphics

公共类主体扩展了游戏状态{

int x;

public MainState(GameStateManager gsm){
    this.gsm = gsm;
}

public void init() {

}

public void update(double delta) {
    x += 2 * delta;
}

public void draw(Graphics g) {
    g.drawRect(x, 0, 50, 50);
    g.dispose();
}

public void keyPressed(int k) {

}

public void keyReleased(int k) {

}
} 我不断地发现这个错误:

Exception in thread "Thread-4" java.lang.NullPointerException
at me.mangodragon.gamestate.MainState.draw(MainState.java:22)
at me.mangodragon.gamestate.GameStateManager.draw(GameStateManager.java:37)
at me.mangodragon.main.Main.draw(Main.java:118)
at me.mangodragon.main.Main.run(Main.java:100)
at java.lang.Thread.run(Unknown Source)
我试图修复它,但找不到问题所在


谢谢!

您将
g
定义为:

Graphics g;
但从来没有给它一个价值

无论如何,这不是绘制形状的方式。相反,在类
Main
中重写paint方法(从画布继承):

@Override
public void paint(Graphics2D g) {
    //Drawing code goes in here.  This runs whenever the Canvas is rendered.
}
然后,当您想要更新它时,例如在
while
循环中,运行

this.repaint();  //note that this doesn't take arguments
如果要在另一个类中使用
draw(Graphics g)
方法,请在
paint()
中调用它


你从来没有给
g
Graphics
)分配过任何东西。现在,在你跑开并试图弄清楚怎么做之前,我强烈建议你去掉这个变量,它会给你带来太多问题

通常,当系统希望绘制组件时,它会调用
paint
方法,并向您传递它希望您绘制的
Graphics
上下文。这种方法称为被动绘制,因为绘制请求随机出现,而这并不是您真正想要的。另一个问题是
java.awt.Canvas
不是双缓冲的,这将导致组件更新时出现闪烁

您可能需要查看和了解更多详细信息

您可以使用
JPanel
,它是双缓冲的,但是使用
java.awt.Canvas
的主要原因是您可以使用API。这不仅提供了双缓冲,还提供了一种方法,您可以通过它直接控制绘制过程(或活动绘制)


有关更多详细信息,请参见和。问题在于您尚未定义
g
,因此它为空。在大多数情况下,您永远不应该创建新的
图形
对象,而是从某处获取它

由于您继承的是
画布
,因此可以非常轻松地完成此操作

首先,您应该将
draw
方法更改为这样

private void draw() {
    BufferStrategy bs = this.getBufferStrategy();
    if (bs == null) {
        this.createBufferStrategy(3);
        return;
    }

    Graphics g = bs.getDrawGraphics();
    // Draw your game here, using the g declared above
    g.dispose();
    bs.show();
}
前几行创建了一个名为
BufferStrategy
的东西,您可以阅读更多关于它的内容,但它本质上允许Java提前渲染接下来的几帧,这样您就不会看到任何闪烁

BufferStrategy
中,您可以获得要绘制的
图形
对象


最后,您必须处理
图形
对象,然后显示
缓冲区
,这样您所做的一切都会显示在屏幕上。

为什么技术上正确,使用画布的原因是您可以使用缓冲区策略,它可以直接控制绘制过程并提供双缓冲区ing,这不包括使用您建议的方法。所以答案是,要么使用JPanel(和paintComponent),要么使用BufferStratgeyOh,对吧,我更习惯使用JPanel而不是Canvas。对此我深表歉意!@MadProgrammer
private void draw() {
    BufferStrategy bs = this.getBufferStrategy();
    if (bs == null) {
        this.createBufferStrategy(3);
        return;
    }

    Graphics g = bs.getDrawGraphics();
    // Draw your game here, using the g declared above
    g.dispose();
    bs.show();
}