【问题标题】:Slow movement using paintComponent method使用paintComponent方法进行缓慢移动
【发布时间】:2013-05-31 20:23:44
【问题描述】:

我决定使用 Swing 的绘画技术 paintComponent() 方法重新编写我的游戏(SO 上的某个人实际上告诉我使用这种方法)。我决定使用 JPanel 而不是 Canvas 作为游戏的基础。我之前编写的游戏使用 Canvas,但该游戏无法显示在我的 64 位桌面上,但可以显示在我的 32 位 labtop 上,这就是为什么必须重新编写这款游戏。

现在的问题是,虽然船的运动正常,但与我之前使用 AWT 的双缓冲绘图技术所做的相比,绘图似乎非常缓慢(除非是我的笔记本电脑问题?)。我花了一整天的时间,却想不出什么能让船跑得更快。

   public class Ship extends JLabel implements KeyListener{

        private Image image;
        private boolean turnRight;
        private int x;
        private int y;
        private int speed = 5;
        private boolean turnLeft;

        public Ship(int x, int y)
        {
            this.x = x;
            this.y = y;

            try {
                image = ImageIO.read(new File("Ship/Ship.PNG"));
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            addKeyListener(this);

        }

        public Image getImage()
        {
            return image;
        }

        @Override
        public void keyPressed(KeyEvent e) {
            // TODO Auto-generated method stub

            if(e.getKeyCode() == KeyEvent.VK_RIGHT)
            {

                x += speed;
                setTurnRight(true);
                setTurnLeft(false);

            }
            else if(e.getKeyCode() == KeyEvent.VK_LEFT)
            {

                x -= speed;
                setTurnLeft(true);
                setTurnRight(false);
            }


            // redraw yourself
            repaint();

        }

        private void setTurnLeft(boolean turnLeft) {
            // TODO Auto-generated method stub
            this.turnLeft = turnLeft;
        }

        // swing custom painting 
        public void paintComponent(Graphics g)
        {
            if(x <= 0)
            {
                x = 0;

            }
            else if(x >= 610)
            {
                x = 610;
            }

            g.drawImage(getImage(), x, y, null);

        }

        public void setTurnRight(boolean turnRight)
        {
            this.turnRight = turnRight;
        }

        public boolean getTurnLeft()
        {
            return turnLeft;
        }

        public boolean getTurnRight()
        {
            return turnRight;
        }

        @Override
        public void keyReleased(KeyEvent e) {
            // TODO Auto-generated method stub

        }

        @Override
        public void keyTyped(KeyEvent e) {
            // TODO Auto-generated method stub

        }

    }

【问题讨论】:

  • 对我来说,这似乎很奇怪。您的 Ship 扩展了 JLabel,并且您在 JLabel 中渲染 Ship 的图像位置。 JLable 能够渲染自己的图像。但是,您实际上应该在位置 0x0 处渲染图像,并允许标签的容器决定实际渲染它的位置。
  • 我可能应该扩展 JComponent。正确的?我认为在编程中你必须显式地调用方法。当你说 0x0 时,你的意思是 (0,0)?想法是将一些 JLabel 对象放置在 JFrame 之上并使用 keyListeners 控制它
  • 0x0 应该是组件的左上角。组件大小应该是图片的大小

标签: java graphics paintcomponent paint


【解决方案1】:

通常,我会创建一个可渲染元素的概念。我会维护这些元素的列表,并在我的主循环中相应地更新它们。

至少,每个都有位置、方向和旋转的概念(如果需要),它们也可以被绘制。

在我的主要组件中,我将简单地循环遍历所有这些元素并“绘制”它们,偏移Graphics 上下文以允许在游戏空间中定位。

但这不是你正在做的......

记住,组件已经有了位置和大小的感觉,你不应该尝试重新实现这个要求,相反,你应该找到利用它的方法......(即,不要维护对 x/y 值的引用 ;))

下面是一个简单的例子。它使用JPanel 来渲染主图像。主循环(在本例中为 javax.swing.Timer)告诉组件它应该根据需要更新其运动。

船本身通过将旋转值更改为给定的变量增量来响应关键事件。这使您可以根据需要控制旋转的速度(我一开始故意将其设置为低,所以请尝试一下)

你应该拒绝做的是改变帧速率;)

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.LineBorder;

public class BattleShipGame {

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

    public BattleShipGame() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new OceanPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class OceanPane extends JPanel {

        private BattleShip ship;

        public OceanPane() {
            setLayout(new GridBagLayout());
            ship = new BattleShip();
            add(ship);

            Timer timer = new Timer(40, new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    ship.move();
                    revalidate();
                    repaint();
                }
            });
            timer.setRepeats(true);
            timer.setCoalesce(true);
            timer.start();
        }

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

    public static class BattleShip extends JPanel {

        protected static final int MAX_TURN_RATE = 5;

        private BufferedImage ship;
        private float angle;
        private float angleDelta;

        public BattleShip() {
            setOpaque(false);
            try {
                ship = ImageIO.read(new File("BattleShip.png"));
            } catch (IOException ex) {
                ex.printStackTrace();
            }
            setFocusable(true);
            InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
            ActionMap am = getActionMap();

            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, 0), "leftTurn");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, 0), "rightTurn");

            am.put("leftTurn", new TurnAction(-0.1f));
            am.put("rightTurn", new TurnAction(0.1f));
        }

        public void move() {

            angle += angleDelta;

        }

        public void setAngle(float angle) {
            this.angle = angle;
        }

        public float getAngle() {
            return angle;
        }

        @Override
        public Dimension getPreferredSize() {
            Dimension size = new Dimension(0, 0);
            if (ship != null) {
                double rads = Math.toRadians(getAngle());
                double sin = Math.abs(Math.sin(rads)), cos = Math.abs(Math.cos(rads));
                int w = ship.getWidth();
                int h = ship.getHeight();
                size.width = (int) Math.floor(w * cos + h * sin);
                size.height = (int) Math.floor(h * cos + w * sin);
            }
            return size;
        }

        @Override
        public Dimension getMinimumSize() {
            return getPreferredSize();
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (ship != null) {
                Graphics2D g2d = (Graphics2D) g.create();

                double rads = Math.toRadians(getAngle());
                double sin = Math.abs(Math.sin(rads)), cos = Math.abs(Math.cos(rads));
                int w = ship.getWidth();
                int h = ship.getHeight();
                int newWidth = (int) Math.floor(w * cos + h * sin);
                int newHeight = (int) Math.floor(h * cos + w * sin);

                AffineTransform at = new AffineTransform();
                at.translate((newWidth - w) / 2, (newHeight - h) / 2);
                at.rotate(Math.toRadians(getAngle()), w / 2, h / 2);

                g2d.drawImage(ship, at, this);
                g2d.dispose();
            }
        }

        protected class TurnAction extends AbstractAction {

            protected float delta;

            public TurnAction(float delta) {
                this.delta = delta;
            }

            @Override
            public void actionPerformed(ActionEvent e) {
                angleDelta += delta;
                if (angleDelta > MAX_TURN_RATE) {
                    angleDelta = MAX_TURN_RATE;
                } else if (angleDelta < (MAX_TURN_RATE * -1)) {
                    angleDelta = (MAX_TURN_RATE * -1);
                }
            }

        }
    }
}

【讨论】:

    【解决方案2】:

    我建议有一个扩展 JPanel 的类,在其中使用 javax.swing.Timer,定义您的 1000/fps 和 ActionListener,在其中您使用 repaint(),它使用您将制作的 paintComponent调用 Ship 中的 draw 方法,现在称为paintComponent。

    所以,由于那个解释很糟糕,这里有一些代码:

    public class Class_Name extends JPanel()
    {
        Ship ship = new Ship(0,0);
        javax.swing.Timer timer = new javax.swing.Timer(1000/60, new ActionListener(){
            repaint();
        });
    
        public void paintComponent(Graphics g)
        {
            super.paintComponent();
            ship.draw(g);
        }
    }
    
    and the draw is, what is now called paintComponent.
    

    如果这没有回答您的问题,请告诉我。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-08
      • 1970-01-01
      • 2010-09-27
      • 2014-05-23
      相关资源
      最近更新 更多