如何从启动器运行Java Swing游戏的动画线程



我对线程有点陌生,所以请耐心等待。所有相关的类都将放在文本下方的一个位置,以便于参考。

后台:

我按照这个教程创建了一个简单的类似乒乓球的游戏:http://www.edu4java.com/en/game/game1.html

一切都很完美,然后我进行了修改,以更好地了解它是如何工作的。在本教程中,有一种连续播放动画的主要方法。根据教程作者的说法,Thread.sleep(10)"…告诉处理器正在运行的线程必须睡眠10毫秒,这允许处理器执行其他线程,特别是调用绘制方法的AWT-EventQueue线程。"

现在,我的问题是:

(只是为了好玩和练习Java,)我为我制作的各种小程序和游戏创建了一个"启动器"。我还没有让乒乓球游戏在发射器内工作。如果在pong帧内没有主方法,动画将永远不会运行。我在下面的代码中留下了主方法,这样它就可以工作了。我该如何从main以外的地方启动动画?

这是代码:

框架和主要方法:

package pongGame;
import javax.swing.*;
public class PongMainGUI extends JFrame
{
    private static final int WINDOW_WIDTH = 500;
    private static final int WINDOW_HEIGHT = 800;
    private static AnimationPanel panel;
    public PongMainGUI()
    {
        //This line sets the title, and, since it calls the super constructor, it calls setTitle().
        super("Pong!");
        panel = new AnimationPanel(this);
        //This method simply makes the screen appear in the center of whatever size screen you are using.
        setLocationRelativeTo(null);
        setSize(WINDOW_WIDTH,WINDOW_HEIGHT);
        add(panel);
        setVisible(true);
    }
    public static void main(String args[]) throws InterruptedException
    {
        new PongMainGUI();
        while(true)
        {
            System.out.println("PongMainGUI");
            panel.repaint();
            panel.move();
            Thread.sleep(10);
        }
    }
}

动画面板:

package pongGame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.event.MouseInputListener;
@SuppressWarnings("serial")
public class AnimationPanel extends JPanel
{   
    PongMainGUI frame;
    Ball ballClass;
    Racquet racquetClass;
    boolean bool = false;
    public AnimationPanel(PongMainGUI frame)
    {
        this.frame = frame;
        addMouseListener(new MouseListener()
        {
            @Override
            public void mouseClicked(MouseEvent arg0) 
            {
            }
            @Override
            public void mouseEntered(MouseEvent arg0) 
            {
            }
            @Override
            public void mouseExited(MouseEvent arg0) 
            {
            }
            @Override
            public void mousePressed(MouseEvent arg0) 
            {
            }
            @Override
            public void mouseReleased(MouseEvent arg0) 
            {
            }
        });
        addMouseMotionListener(new MouseMotionListener()
        {
            @Override
            public void mouseDragged(MouseEvent e) 
            {
            }
            @Override
            public void mouseMoved(MouseEvent e) 
            {
            }
        });
        addKeyListener(new KeyListener()
        {
            @Override
            public void keyPressed(KeyEvent e) 
            {
                racquetClass.keyPressed(e);
            }
            @Override
            public void keyReleased(KeyEvent e) 
            {
                racquetClass.keyReleased(e);
            }
            @Override
            public void keyTyped(KeyEvent e) 
            {
            }
        });
        //This is needed to ensure that the keyboard will register properly and receive focus.
        setFocusable(true);
        ballClass = new Ball(this);
        racquetClass = new Racquet(this);
    }
    public void move()
    {
        //ballClass.moveBall();
        racquetClass.moveRacquet();
    }
    @Override
    public void paint(Graphics g) 
    {
        System.out.println("AnimationPanel paint method");
        //This method clears the panel so it appears as if the circle is moving.
        super.paint(g);
        //Better version of Graphics.
        Graphics2D g2d = (Graphics2D) g;
        //This method turns antialiasing on, which cleans up the corners.
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        ballClass.paint(g2d);
        racquetClass.paint(g2d);
    }
    public void gameOver()
    {
        System.out.println("Game over method");
        JOptionPane.showMessageDialog(null, "Game Over", "Game Over", JOptionPane.YES_NO_OPTION);
        System.exit(ABORT);
    }
}

球"精灵":

package pongGame;
import java.awt.Graphics2D;
import java.awt.Rectangle;
public class Ball 
{
    int xCoordinate = 0;
    int yCoordinate = 0;
    //1 = right movement, -1 = left
    int xDirection = 1;
    int yDirection = 1;
    private final static byte ballWidth = 30;
    private final static byte ballHeight = 30;
    private AnimationPanel panel;
    public Ball(AnimationPanel panel)
    {
        this.panel = panel;
    }
    public void paint(Graphics2D g2d)
    {
        //This creates the actual circle with a specified width and height.
        //Because super.paint(g) is called at the start, a new circle is created each time.
        g2d.fillOval(xCoordinate, yCoordinate, ballWidth, ballHeight);
        System.out.println("Ball paint method");
        moveBall();
    }
        //What this method does is add 1 to the x and y coordinates each time it's called.  However, getWidth() and getHeight() are used to determine the current panel size, not the frame size.
        //Then, whatever the width and/or height is is subtracted so the circle does not completely disappear from view.
        public void moveBall() 
        {
            if (xCoordinate + xDirection < 0)
            {
                xDirection = 1;
            }
            else if (xCoordinate + xDirection > panel.getWidth() - ballWidth)
            {
                xDirection = -1;
            }
            if (yCoordinate + yDirection < 0)
            {
                yDirection = 1;
            }
            else if (yCoordinate + yDirection > panel.getHeight() - ballHeight)
            {
                System.out.println("Ball moveBall method");
                panel.gameOver();
            }
            if (collision() == true)
            {
                yDirection = -1;
                yCoordinate = panel.racquetClass.getPaddleHeight() - ballHeight;
            }
            xCoordinate = xCoordinate + xDirection;
            yCoordinate = yCoordinate + yDirection;
        }
        public Rectangle getBounds() 
        {
            return new Rectangle(xCoordinate, yCoordinate, ballWidth, ballHeight);
        }
        private boolean collision() 
        {
            return panel.racquetClass.getBounds().intersects(getBounds());
        }
}

最后,Racquet的"精灵":

package pongGame;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
public class Racquet 
{
    private AnimationPanel panel;
    private int xCoordinate = 0;
    //0 = no movement, 1 is right, -1 is left.
    private byte direction = 0;
    //All of the following values are in pixels.
    private final static byte PADDLE_OFFSET = 100;
    private final static byte PADDLE_WIDTH = 120;
    private final static byte PADDLE_HEIGHT = 10;
    public Racquet(AnimationPanel panel) 
    {
        this.panel = panel;
    }
    public void moveRacquet() 
    {
        if (xCoordinate + direction > 0 && xCoordinate + direction < panel.getWidth()-60)
            xCoordinate = xCoordinate + direction;
    }
    public void paint(Graphics2D g) 
    {
        g.fillRect(xCoordinate, getPaddleHeight(), PADDLE_WIDTH, PADDLE_HEIGHT);
        //move();
    }
    public void keyReleased(KeyEvent e) 
    {
        direction = 0;
    }
    public void keyPressed(KeyEvent e) 
    {
        if (e.getKeyCode() == KeyEvent.VK_LEFT)
            direction = -1;
        if (e.getKeyCode() == KeyEvent.VK_RIGHT)
            direction = 1;
    }
    public Rectangle getBounds() 
    {
        return new Rectangle(xCoordinate, getPaddleHeight(), PADDLE_WIDTH, PADDLE_HEIGHT);
    }
    public int getPaddleHeight()
    {
        return panel.getHeight() - PADDLE_OFFSET;
    }
}

这可能有帮助,也可能没有帮助,但这是我想用来打开游戏的启动器的代码:

这是"主菜单":

package GUI;
import javax.swing.*;
import painter.MainPainterGUI;
import java.awt.*;
import java.awt.event.*;
/**
 * This class serves to create the launcher gui for the program.
 * It extends JFrame.
 * @author Jackson Murrell
 */
@SuppressWarnings("serial")
public class LauncherGUI extends JFrame implements ActionListener
{
    //A couple constants that are used for sizing things.
    private final short WINDOW_HEIGHT = 225;
    private final short WINDOW_WIDTH = 550;
    private final byte BLANK_SPACE = 25;
    //Panels to use for adding in components.
    JPanel textPanel, buttonPanel, mainPanel;
    //Buttons for user input and selection.
    JButton calculator, colorChooser, timer, exit, primeNumberTester, game, painter;
    //A text label that will be used for giving the user
    //instructions on the program.
    JLabel textLabel;
    //A constructor to create the GUI components when an object of this class is created.
    public LauncherGUI()
    {
        //This call's the parent method's (JFrame) setTitle method.
        super("Omni-program");
        //These methods set various options for the JFrame.
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
        setLocationRelativeTo(null);
        textPanel = new JPanel();
        buttonPanel = new JPanel();
        mainPanel = new JPanel();
        calculator = new JButton("Calculator");
        colorChooser = new JButton("Color Chooser");
        timer = new JButton("Timer");
        primeNumberTester = new JButton("Prime Number Tester");
        game = new JButton("Games");
        exit = new JButton("Exit Launcher and Programs");
        painter = new JButton("Painter");
        calculator.addActionListener(this);
        colorChooser.addActionListener(this);
        timer.addActionListener(this);
        exit.addActionListener(this);
        primeNumberTester.addActionListener(this);
        game.addActionListener(this);
        painter.addActionListener(this);
        textLabel = new JLabel("Welcome to the launcher!  Click the button for the mini-program you would like to run.", 0);
        textPanel.add(Box.createVerticalStrut(BLANK_SPACE));
        textPanel.add(textLabel);
        buttonPanel.add(calculator);
        buttonPanel.add(colorChooser);
        buttonPanel.add(timer);
        buttonPanel.add(primeNumberTester);
        buttonPanel.add(game);
        buttonPanel.add(painter);
        buttonPanel.add(exit);
        mainPanel.setLayout(new GridLayout(2,1));
        mainPanel.add(textPanel);
        mainPanel.add(buttonPanel);
        //mainPanel.add(Box.createVerticalStrut(BLANK_SPACE));
        add(mainPanel);
        //pack();
        //Having this line at the end instead of the top ensures that once everything is added it is all set to be visible.
        setVisible(true);
    }
    //This method is required since ActionListener is implemented.
    //It will be used to process user input.
    @Override
    public void actionPerformed(ActionEvent e) 
    {
        if (e.getSource() == calculator)
        {
            new CalculatorGUI();
            dispose();
        }
        else if (e.getSource() == colorChooser)
        {
            new ColorChooserGUI();
            dispose();
        }
        else if(e.getSource() == timer)
        {
            new TimerGUI();
            dispose();
        }
        else if (e.getSource() == primeNumberTester)
        {
            new PrimeNumberTesterGUI();
            dispose();
        }
        else if(e.getSource() == exit)
        {
            System.exit(0);
        }
        else if(e.getSource() == painter)
        {
            new MainPainterGUI();
            dispose();
        }
        else if(e.getSource() == game)
        {
            new GameLauncherGUI();
            dispose();
        }
    }
}

这是实际的游戏启动器:

package GUI;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.*;
import pongGame.PongMainGUI;
public class GameLauncherGUI extends JFrame implements ActionListener
{
    //A couple constants that are used for sizing things.
    private final short WINDOW_HEIGHT = 225;
    private final short WINDOW_WIDTH = 550;
    private JButton adventureGame, pong, back;
    private JLabel label;
    private JPanel mainPanel, buttonPanel, textPanel;
    public GameLauncherGUI()
    {
        //This call's the parent method's (JFrame) setTitle method.
        super("Omni-program");
        //These methods set various options for the JFrame.
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
        setLocationRelativeTo(null);
        adventureGame = new JButton("Adventure Game (Broken)");
        adventureGame.addActionListener(this);
        pong = new JButton("Pong");
        pong.addActionListener(this);
        back = new JButton("Back");
        back.addActionListener(this);
        label = new JLabel("Click the button below for the game you wish to play,nor click back to go to the previous screen.");
        mainPanel = new JPanel();
        buttonPanel = new JPanel();
        textPanel = new JPanel();
        textPanel.add(label);
        buttonPanel.add(adventureGame);
        buttonPanel.add(pong);
        buttonPanel.add(back);
        mainPanel.add(textPanel);
        mainPanel.add(buttonPanel);
        add(mainPanel);
        //Having this line at the end instead of the top ensures that once everything is added it is all set to be visible.
        setVisible(true);
    }
    @Override
    public void actionPerformed(ActionEvent e) 
    {
        if(e.getSource() == back)
        {
            new LauncherGUI();
            dispose();
        }
        else if(e.getSource() == pong)
        {
            new PongMainGUI();
            dispose();
        }
    }
}

main和其他方法一样是一个静态方法,因此您可以从启动器调用它:

PongMainGUI.main(null); // launch the pong game

但是,请注意,为了避免很多麻烦,必须从事件调度线程创建Swing组件,如本例所示。因此,您应该将主方法的内容封装在Runnable中,并使用SwingUtilities.invokeLater()启动它。

但是,通过这样做,Thread.sleep(10)将在EDT上运行,再次阻止GUI响应。幸运的是,Swing考虑到了这个问题,创建了一个名为javax.swing.Timer的实用程序,它可以在EDT上定期运行任务(而不会阻塞它):

public static void main(String args[])
{
   SwingUtilities.invokeLater(new Runnable(){
      public void run(){
         new PongMainGUI();
         Timer timer = new Timer(10, new ActionListener(){
            public void actionPerformed(ActionEvent e){
               System.out.println("PongMainGUI");
               panel.repaint();
               panel.move();
            }
         });
         timer.start();
      }
   });
}

main()方法将在独立环境中或从您的启动器中安全运行。

最新更新