我的Java(JFrame)播放器移动脚本无法正常工作 [英] My java(JFrame) player movement script won't work

查看:56
本文介绍了我的Java(JFrame)播放器移动脚本无法正常工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在制作一个自上而下的2D生存游戏,玩家必须躲避敌人的弹丸才能生存,但是我遇到了一个问题,可能会稍微影响我的第一个游戏的玩法真正的Java项目,问题是玩家无法以任何方式移动形状或形式,如果已经实施了弹丸,那么玩家会在生成这个世界后立即迷失方向.

下面是代码:

package maximiza;

import java.awt.*;
import java.awt.event.KeyEvent;

import javax.swing.*;

public class Game extends JFrame {

    private static final long serialVersionUID = 1L;

    public int xPosition = 240;
    public int yPosition = 240;
    public int speed = 10;
    
    public int playerSize = 20;
    
    public Game()
    {
        setTitle("Maximiza");
        setSize(500, 500);
        setVisible(true);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }
    
    public void paint(Graphics g)
    {
        g.setColor(Color.yellow);
        g.fillRect(xPosition, yPosition, playerSize, playerSize);
    }
    
    public void init()
    {
        addKeyListener(new Input(this));
    }
    
    public static void main(String [] args)
    {
        boolean living = true;
        Game g = new Game();
        g.init();
        while(living)
        {
            g.paint(null);
        }
    }

    public void keyPressed(KeyEvent e) {
        int key = e.getKeyCode();
    
        if(key == KeyEvent.VK_UP) {
            yPosition += speed;
        } else if(key == KeyEvent.VK_DOWN) {
            yPosition -= speed;
        }
    }
    
    public void keyReleased(KeyEvent e) {
        
    }
}

输入脚本:

package maximiza;

import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

public class Input extends KeyAdapter {
    
    Game game;
    
    public Input(Game game) {
        this.game = game;
    }
    
    public void keyPressed(KeyEvent e) {
        game.keyPressed(e);
    }
    
    public void keyReleased(KeyEvent e) {
        game.keyReleased(e);
    }
}

感谢您提供的任何帮助.

解决方案

首先有几个问题:

  1. 不要不必要地扩展JFrame,您可以创建JPanel并将其简单地添加到JFrame
  2. 切勿覆盖paint,而应使用JComponent之类的JComponent并覆盖paintComponent,不要忘记将super.paintComponent用作覆盖方法中的第一条语句.也不要直接调用paintpaintComponent,而要使用JComponent#repaint() 这里是上面的好读物
  3. 所有Swing组件都应在 EDT 上创建
  4. 不要使用适当的布局管理器调用setSize,并在需要的地方覆盖getPreferredSize以向布局管理器提供尺寸提示,然后调用框架设置为可见
  5. 我建议使用 KeyBindings ,而不要使用KeyListener
  6. 您不能在与UI组件相同的线程上使用 while循环,否则UI将冻结且不会更新,而应使用 Swing游戏库,您可以将其用作将来的参考您可能会遇到的困难或问题,例如添加游戏循环,将游戏对象逻辑与游戏面板/屏幕分开,抗锯齿等.

    下面是实现上述建议以帮助您入门的代码,以及一些注释,指出了您可以添加或更改的其他内容,以使游戏随着增长而更加可重用:

    import java.awt.*;
    import java.awt.event.ActionEvent;
    import java.awt.event.KeyEvent;
    import javax.swing.*;
    
    public class Game {
    
        private GamePanel gamePanel;
    
        public Game() {
            createAndShowUI();
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(Game::new);
        }
    
        private void createAndShowUI() {
            JFrame frame = new JFrame("Maximiza");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            gamePanel = new GamePanel();
    
            gamePanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "UP pressed");
            gamePanel.getActionMap().put("UP pressed", new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    gamePanel.moveUp(); // this would be something like gameObject.moveDown()
                }
            });
            gamePanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "DOWN pressed");
            gamePanel.getActionMap().put("DOWN pressed", new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    gamePanel.moveDown(); // this would be something like gameObject.moveDown()
                }
            });
    
            frame.add(gamePanel);
            frame.pack();
            frame.setVisible(true);
    
            // a more approrpiate game loop can be used as we only need to call repaint 60 times per second not more or less.
            // fixed step: https://stackoverflow.com/questions/13739693/java-swing-based-game-framework-any-advice/13740162#13740162
            // variable step: https://github.com/davidkroukamp/SwingGameLibrary/blob/main/src/za/co/swinggamelibrary/GameLoop.java
            Thread gameLoop = new Thread(() -> {
                boolean living = true;
                while (living) {
                    gamePanel.repaint();
    
                    // lets sleep a bit not to eat up processor time unnecessarily needs a better implementation of a game loop but thread sleep will do okay for now
                    try {
                        Thread.sleep(16);
                    } catch (InterruptedException ex) {
                    }
                }
            });
            gameLoop.start();
        }
    
        public class GamePanel extends JPanel {
    
            // these properties should go into its own game object which is separated from the game panel, and can simply be added to the game panel via a list
            // and in the paintComponent method you simply iterate all the game objects and draw them
            // https://github.com/davidkroukamp/SwingGameLibrary-Samples/blob/main/sample1/src/sgltest/Player.java
            public int xPosition = 240;
            public int yPosition = 240;
            public int speed = 10;
    
            public int playerSize = 20;
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
    
                // can make graphics look better too using some rendering hints https://github.com/davidkroukamp/SwingGameLibrary/blob/main/src/za/co/swinggamelibrary/Graphics2DHelper.java
                // should move to game object inside a render(Graphics2D g2d) method which gets called for each game object passing in the graphics parameter
                g.setColor(Color.yellow);
                g.fillRect(xPosition, yPosition, playerSize, playerSize);
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(500, 500);
            }
    
            // these methods would go into the game object class too under a single move methid which checks booleans on whether to move up or down or whatever
            public void moveUp() {
                yPosition -= speed; // this could probably just set a boolean which your game object will check and update its position accordingly via a move() method thats called before gamePanel.repaint in the thread
            }
    
            public void moveDown() {
                yPosition += speed; // this could probably just set a boolean which your game object will check and update its position accordingly via a move() method thats called before gamePanel.repaint in the thread
            }
    
        }
    }
    

    再次重申,您可以进行更多更改(我已在代码中注释了提示),但是我没有为您做这些,因为那只会破坏您的生命! :)

    I am making a top down 2d survival game where the player must dodge projectiles from enemies in order to survive, but I've run into an issue that may, slightly influence the gameplay of my first real java project, that issue being that the player cannot move in any way shape or form and if the projectiles had of been implemented, then the player would lose immediately after spawning into this world.

    here is the code:

    package maximiza;
    
    import java.awt.*;
    import java.awt.event.KeyEvent;
    
    import javax.swing.*;
    
    public class Game extends JFrame {
    
        private static final long serialVersionUID = 1L;
    
        public int xPosition = 240;
        public int yPosition = 240;
        public int speed = 10;
        
        public int playerSize = 20;
        
        public Game()
        {
            setTitle("Maximiza");
            setSize(500, 500);
            setVisible(true);
            setDefaultCloseOperation(EXIT_ON_CLOSE);
        }
        
        public void paint(Graphics g)
        {
            g.setColor(Color.yellow);
            g.fillRect(xPosition, yPosition, playerSize, playerSize);
        }
        
        public void init()
        {
            addKeyListener(new Input(this));
        }
        
        public static void main(String [] args)
        {
            boolean living = true;
            Game g = new Game();
            g.init();
            while(living)
            {
                g.paint(null);
            }
        }
    
        public void keyPressed(KeyEvent e) {
            int key = e.getKeyCode();
        
            if(key == KeyEvent.VK_UP) {
                yPosition += speed;
            } else if(key == KeyEvent.VK_DOWN) {
                yPosition -= speed;
            }
        }
        
        public void keyReleased(KeyEvent e) {
            
        }
    }
    

    Input script:

    package maximiza;
    
    import java.awt.event.KeyAdapter;
    import java.awt.event.KeyEvent;
    
    public class Input extends KeyAdapter {
        
        Game game;
        
        public Input(Game game) {
            this.game = game;
        }
        
        public void keyPressed(KeyEvent e) {
            game.keyPressed(e);
        }
        
        public void keyReleased(KeyEvent e) {
            game.keyReleased(e);
        }
    }
    

    thank you for any help provided.

    解决方案

    A couple of issues first of all:

    1. Dont extend JFrame unecessarily, you can create a JPanel and simply add it to the JFrame
    2. Never override paint instead use a JComponent like a JPanel and override paintComponent, not forgetting to call super.paintComponent as the first statement in the overriden method. Also never call paint or paintComponent directly instead use JComponent#repaint() Here is a good read on the above
    3. All Swing components should be created on the EDT
    4. Don't call setSize use an appropriate layout manager, and override getPreferredSize where needed to provide size hints to the layout manager, then call JFrame#pack() before setting the frame to be visible
    5. I suggest using KeyBindings as opposed to a KeyListener
    6. You cannot use a while loop on the same thread as your UI components or the UI will freeze and not update, instead use a Thread

    I myself am creating my own Swing Game Library, which you might use as a reference for future endeavors or issues you might be facing, such as the addition of a game loop, separating your game objects logic from the game panel/screen, anti-aliasing etc.

    Below is your code with the above suggestions implemented to help you get started, as well as some comments pointing you to what else you could add or change to make your game more re-usable as it grows:

    import java.awt.*;
    import java.awt.event.ActionEvent;
    import java.awt.event.KeyEvent;
    import javax.swing.*;
    
    public class Game {
    
        private GamePanel gamePanel;
    
        public Game() {
            createAndShowUI();
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(Game::new);
        }
    
        private void createAndShowUI() {
            JFrame frame = new JFrame("Maximiza");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    
            gamePanel = new GamePanel();
    
            gamePanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "UP pressed");
            gamePanel.getActionMap().put("UP pressed", new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    gamePanel.moveUp(); // this would be something like gameObject.moveDown()
                }
            });
            gamePanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "DOWN pressed");
            gamePanel.getActionMap().put("DOWN pressed", new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    gamePanel.moveDown(); // this would be something like gameObject.moveDown()
                }
            });
    
            frame.add(gamePanel);
            frame.pack();
            frame.setVisible(true);
    
            // a more approrpiate game loop can be used as we only need to call repaint 60 times per second not more or less.
            // fixed step: https://stackoverflow.com/questions/13739693/java-swing-based-game-framework-any-advice/13740162#13740162
            // variable step: https://github.com/davidkroukamp/SwingGameLibrary/blob/main/src/za/co/swinggamelibrary/GameLoop.java
            Thread gameLoop = new Thread(() -> {
                boolean living = true;
                while (living) {
                    gamePanel.repaint();
    
                    // lets sleep a bit not to eat up processor time unnecessarily needs a better implementation of a game loop but thread sleep will do okay for now
                    try {
                        Thread.sleep(16);
                    } catch (InterruptedException ex) {
                    }
                }
            });
            gameLoop.start();
        }
    
        public class GamePanel extends JPanel {
    
            // these properties should go into its own game object which is separated from the game panel, and can simply be added to the game panel via a list
            // and in the paintComponent method you simply iterate all the game objects and draw them
            // https://github.com/davidkroukamp/SwingGameLibrary-Samples/blob/main/sample1/src/sgltest/Player.java
            public int xPosition = 240;
            public int yPosition = 240;
            public int speed = 10;
    
            public int playerSize = 20;
    
            @Override
            protected void paintComponent(Graphics g) {
                super.paintComponent(g);
    
                // can make graphics look better too using some rendering hints https://github.com/davidkroukamp/SwingGameLibrary/blob/main/src/za/co/swinggamelibrary/Graphics2DHelper.java
                // should move to game object inside a render(Graphics2D g2d) method which gets called for each game object passing in the graphics parameter
                g.setColor(Color.yellow);
                g.fillRect(xPosition, yPosition, playerSize, playerSize);
            }
    
            @Override
            public Dimension getPreferredSize() {
                return new Dimension(500, 500);
            }
    
            // these methods would go into the game object class too under a single move methid which checks booleans on whether to move up or down or whatever
            public void moveUp() {
                yPosition -= speed; // this could probably just set a boolean which your game object will check and update its position accordingly via a move() method thats called before gamePanel.repaint in the thread
            }
    
            public void moveDown() {
                yPosition += speed; // this could probably just set a boolean which your game object will check and update its position accordingly via a move() method thats called before gamePanel.repaint in the thread
            }
    
        }
    }
    

    To re-iterate, there are many more changes you could make (I've commented around the code to give you hints), but I did not do these for you as that would just be a spoiler! :)

    这篇关于我的Java(JFrame)播放器移动脚本无法正常工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆