有没有办法从 class 外部在 JPanel 对象上绘画?

Is there a way to paint on a JPanel object from ouside it's class?

我搜索并发现了一些措辞相似的问题,但没有一个适用于我的情况,所以我开始了。

我正在尝试制作一款具有不同级别的游戏,每个级别的玩法都完全不同。

最初,我的代码看起来像这样并且运行良好:

public class Life extends JPanel{

private Story story;

public Life(){
    story = new Story(this);

}

public void paintComponent(Graphics g) {
    super.paintComponent(g);
    story.render(g)

public void terminate(){
    story.terminate();
    System.out.println("Terminated");
}

public static void main(String[] args){
    final JFrame frame = new JFrame("Life");
    final Life life = new Life();
    frame.add(life);
}
}



public class Story {

private int phase;
private Life life;


public Story(Life life){
    this.life = life;
    phase = 0;
}

public void render(Graphics g){
    if(phase == 0) levelOneRender(g);
    if(phase == 1) levelTwoRender(g);
}
}

我担心我会在每个游戏滴答时浪费时间检查我处于哪个阶段。由于我计划有 20 个以上的阶段,代码会很快变得低效。

所以我有了一个想法,只需将 JPanel 中的图形对象从我的生活对象传递到我的故事对象,并为每个阶段在不同的 class 中绘制 Jpanel,如下所示:

public class Life extends JPanel{

public Life(){

    story = new Story(this);
}


public static void main(String[] args){
    final JFrame frame = new JFrame("Life");
    final Life life = new Life();

}

}


public class Story {

private int phase;
private Intro intro;
private Life life;


public Story(Life life){
    this.life = life;
    phase = 0;
    intro = new Intro(this);
}

public void nextPhase(){
    this.phase++;
}


public Life getLife() {
    return this.life;
}
}

public class Intro {

private static final int DELAY = 100; // in milliseconds, so 10 ticks per second

private Timer timer;
private Story story;
private Graphics g;
private int counter;


public Intro(Story story) {
    this.story = story;
    this.g = story.getLife().getGraphics();
    this.counter = 0;

    timer = new Timer(DELAY, new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {
            tick();
            story.repaint();
        }
    });
    timer.start();
}

public void tick(){
    if(counter <= 40){
        terminate();
    }
    counter++;
    render();
}

public void render(){
    story.getLife().paint(g);
    Graphics2D g2 = (Graphics2D)g;
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

    g2.draw(new Rectangle(0,0,10,10));


}

public void terminate(){
    timer.stop();
    story.nextPhase();
}
}

不幸的是,这不起作用,因为 story.getLife().paint(g);在 class Intro 抛出一个 nullPointerException,当我 运行 它。我很确定这不是我尝试这样做的唯一问题。

是否有正确的方法来实现我的目的?

非常感谢您抽出宝贵时间。任何见解将不胜感激。

public void render(Graphics g){
    if(phase == 0) levelOneRender(g);
    if(phase == 1) levelTwoRender(g);
}

这并没有你想象的那么严重。你现在拥有的很好。但是这些检查是可以避免的(正如你所要求的)。

与其在单独的方法中处理每个级别的绘画,不如在单独的对象中处理它们:

public bstract class Level {
    public abstract void paint(Graphics g);
}

public final class LevelOne extends Level {
    public void paint(Graphics g) {
        //...
    }
}

public final class LevelTwo extends Level {
    public void paint(Graphics g) {
        //...
    }
}

这样,您无需检查 int 值来查看要绘制哪个渲染,只需切换 Level 值即可渲染新关卡:

Level one = new LevelOne();
Level two = new LevelTwo();

Level currentLevel = one;

public void paintComponent(Graphics g) {
    super.paintComponent(g);

    currentLevel.paint(g);
}

只需切换level的值即可呈现不同的层次