Java 游戏开发:图形

2022-01-24 00:00:00 java swing jpanel paintcomponent jframe

我是新来的.希望您能提供帮助.

问题:在 JFrame 上显示动画时出现问题.似乎我想念/不太了解 Java 的图形是如何工作的.

全球理念:可以说我想做一个游戏/电影/剪辑.为此,我需要这个(不)简单的动画开始工作.

这个问题的一个例子:我得到了 Screen 类,它有屏幕的东西——JFrame 的声明,设置它的配置(大小、关闭操作等),然后创建类 Box 的对象,以显示在框架上.请检查这个类的图像/图表(希望我写得正确):.

它拥有启动简单 2D 游戏所需的一切,例如游戏循环、逻辑、像素碰撞检测、动画(即在多个精灵之间交换以创建精灵集的动画)等等,但根本区别在于使用对象作为游戏实体,即一个类将保存要绘制的对象所需的所有信息,IMO 这就是游戏应该完成的方式,事情可能会变得图形密集,并且有许多 JPanel 想知道屏幕肯定会降低整体 FPS

i'm new here. Hope you will be able to help.

Problem: Problem with displaying Animation on JFrame. Seems I miss/don't understand enough how Java's graphics works.

Global idea: Lets say I want make a game/movie/clip. For this I need this (not)simple animation get working.

An example for this question: I got class Screen, which has the screen stuff- Declaration of the JFrame, setting up its configuration(size, close operation, etc) and then creating the objects of class Box, to be showed on the frame. Check please this image/diagram of the classes(hope I wrote it the right way): ClassesDiagram

Now, class Box extends JPanel. I inherit from JPanel the method Paint() and override it, painting boxes.

In the Screen class, after I created the two Boxes, i .add() them to the JFrame. Next, starts a loop while(true), and updates the box's position every 200 milisec by making the thread sleep that amount. (in this case, just simple x++ or y++ depends on which box, box1 or box2).

Main Problem 1): The program runs, and shows the JFrame, but on the JFrame it shows only the last added object/component- Box. It doesn't show the other one. Why?

I found a topic, How to add multiple components to a JFrame?, and tried the tips the most voted post gave, by jjnguy Nov 15 '10 at 17:02. For some odd reason, not the first, nor the second tip worked for me.

Main Problem 2): As far as I understand, i need layout manager. Why do I need it, if I just want to paint() at specific X,Y on the frame?

Found other post(cant find it again)+Oracle's guidelines about layouts, suggested that I need consider using setLayout(null); I tried to do so, but then there's a problem once again.

Main Problem 3): The JFrame shows up, it shows only 1 box(the green one wont show up, whatever you will do. Don't know why) and when it DOES move- it gets erased from the other side. Here: Box Movement

Thank you in advance for any help, tips, and explanation! Hope the post is clear, organised, and nice looking.


public class Screen {

public static void main(String[] args) {
    new Screen();
}

private JFrame window;

public Screen() {
    window = new JFrame("Multiply components panel");
    //window.setLayout(null);
    window.setSize(200, 200);

    window.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);

    Box b1 = new Box(10,10, "box1");
    //b1.setBounds(10, 10, 50, 50);

    Box b2 = new Box(100,100, "box2");
    //b2.setBounds(100, 100, 50, 50);


    window.add(b1);
    //window.add(b2);


    window.setVisible(true);

    while (true){

        b1.update();
        b2.update();

        try {
            Thread.sleep(200);
        } catch (Exception e) {
            // TODO: handle exception
        }
    }

}
}


public class Box extends JPanel{

int x, y, w, h;
private String name;

public Box(int x, int y, String name) {
    this.x = x;
    this.y = y;
    this.w = 100;
    this.h = 100;
    this.name=name;
}


public void paint(Graphics g){
    System.out.println(this.name.equalsIgnoreCase("box1"));
    if(this.name.equalsIgnoreCase("box1")){
        g.setColor(Color.BLACK);
        g.fillRect(x, y, w, h);
    }else{
        g.setColor(Color.GREEN);
        g.fillRect(x, y, w, h);
    }


}


public void update() {
    if(this.name.equalsIgnoreCase("box1"))
        x++;
    else
        y++;
    //this.setBounds(x, y, w, h);
    System.out.println("Current "+name+": X: "+x+", Y: "+y+", W: "+w+", H: "+h);
    repaint();
}

}

解决方案

Main Problem 1): The program runs, and shows the JFrame, but on the JFrame it shows only the last added object/component- Box. It doesn't show the other one. Why?

You do window.add(b1); window.add(b2);, by default JFrame has BorderLayout thus you are replacing the last added box when you do add(..).

Main Problem 2): As far as I understand, i need layout manager. Why do I need it, if I just want to paint() at specific X,Y on the frame?

If you are using JPanels as objects in the game than, this would be the only time to use setLayout(null) as we want full control over the JPanels placing.

Main Problem 3): The JFrame shows up, it shows only 1 box(the green one wont show up, whatever you will do. Don't know why) and when it DOES move- it gets erased from the other side. Here: Box Movement

because you you do this g.fillRect(x,y,w,h), it should be g.fillRect(0,0,w,h)

Other problems:

1) I think a major problem you have is here:

public class Box extends JPanel{

    ...

    public void paint(Graphics g){

       ...
    }

}

You should override paintComponent of JPanel, and remember to call super.paintComponent(g) as first call in overridden method.

3) you should override getPreferredSize of JPanel and return correct dimensions which match the image or contents of the JPanel

4) dont call setSize on JFrame use correct Layoutmanager and/or override getPreferredSize of necessary containers than simply call pack() on JFrame before setting it visible.

5) as said by @MadProgrammer have a read on Concurrency in Swing but basically all Swing components should be created/manipulated on EDT via SwingUtilities.inokeXXX block.

6) doing this is definetly bad:

while (true){

    b1.update();
    b2.update();

    try {
        Thread.sleep(200);
    } catch (Exception e) {
        // TODO: handle exception
    }
}

as you are not only creating a continuous loop but also calling Thread.sleep on the Thread which you created you GUI, thus is will seem to freeze. Have a look at How to use Swing Timers, also see this similar question.answer on the above topic.

Here is code with above fixes implemented:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class Screen {

    private JFrame window;

    public static void main(String[] args) {

        //creat UI on EDT
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                new Screen();
            }
        });
    }

    public Screen() {
        window = new JFrame("Multiply components panel") {
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(600, 400);
            }
        };

        window.setLayout(null);//only reason this is warrented is because its a gmae using JPanels as game objects thus we need full control over its placing
        window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//changed from DISPOSE to EXIT so Timers  will be exited too

        final Box b1 = new Box(10, 10, 50, 50, "box1");

        final Box b2 = new Box(100, 100, 50, 50, "box2");

        window.add(b1);
        window.add(b2);

        window.pack();
        window.setVisible(true);

        Timer timer = new Timer(200, new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                b1.update();
                b2.update();
            }
        });
        timer.setInitialDelay(0);
        timer.start();

    }
}

class Box extends JPanel {

    int x, y, w, h;
    private String name;

    public Box(int x, int y, int w, int h, String name) {
        this.x = x;
        this.y = y;
        this.w = w;
        this.h = h;
        this.name = name;
        setBounds(x, y, w, h);//let the Box class handle setting the bounds more elegant OOP
    }

    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        System.out.println(this.name.equalsIgnoreCase("box1"));
        if (this.name.equalsIgnoreCase("box1")) {
            g.setColor(Color.BLACK);
            g.fillRect(0, 0, w, h);
        } else {
            g.setColor(Color.GREEN);
            g.fillRect(0, 0, w, h);
        }
    }

    @Override
    public Dimension getPreferredSize() {
        return new Dimension(w, h);
    }

    public void update() {
        if (this.name.equalsIgnoreCase("box1")) {
            x++;
        } else {
            y++;
        }
        this.setBounds(x, y, w, h);//set the new bounds so box may be updated
        System.out.println("Current " + name + ": X: " + x + ", Y: " + y + ", W: " + w + ", H: " + h);
        revalidate();
        repaint();
    }
}

Lastly have a look at my tutorial/code snippet Game Development Loop, Logic and Collision detection Java Swing 2D.

It has all you need to start a simple 2D game, like game loop, logic, pixel collision detection, animation ( ie swapping between multiple sprites to create animation of the sprite set), and more, the fundamental difference though is it uses objects as the game entities, i.e a class will hold all the needed information for an object to be drawn, IMO this is how games should be done, a things might get graphically intense and have many JPanels wondering the screen will definitely drop overall FPS

相关文章