使用paintComponent方法缓慢移动 [英] Slow movement using paintComponent method

查看:129
本文介绍了使用paintComponent方法缓慢移动的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



现在的问题是,当船的运动起作用时,绘图看起来非常慢(除非它是我的笔记本电脑问题?)与之前我使用AWT的双缓冲绘图技术做的相比。我花了一整天的时间,但无法弄清楚什么可能会使船运行得更快。

  public class Ship扩展JLabel实现KeyListener {

私人图片图片;
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;

尝试{
image = ImageIO.read(新文件(Ship / Ship.PNG));
} catch(IOException e){
// TODO自动生成的catch块
e.printStackTrace();
}

addKeyListener(this);


$ b public Image getImage()
{
return image;
}

@Override
public void keyPressed(KeyEvent e){
// TODO自动生成的方法存根

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

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

$ b else if(e.getKeyCode()== KeyEvent.VK_LEFT)
{

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


//重绘自己
repaint();



private void setTurnLeft(boolean turnLeft){
// TODO自动生成的方法存根
this.turnLeft = turnLeft;


//定制绘画
public void paintComponent(Graphics g)
{
if(x <= 0)
{
x = 0;

$ b $ 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自动生成的方法存根

}

@Override
public void keyTyped(KeyEvent e){
// TODO自动生成的方法存根

}

}


解决方案

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

至少每个元素都有一个位置,方向和旋转的概念(if在我的主要组件中,我只是遍历所有这些元素并绘制它们,抵消了 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){
BattleShipGame();

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

JFrame框架= 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);
}
});
}

公共类OceanPane扩展JPanel {

私人BattleShip船;

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

定时器定时器=新定时器(40,新的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;

私人BufferedImage出货;
私人浮动角度;
私人浮动angleDelta;

public BattleShip(){
setOpaque(false);
尝试{
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,新的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);
}
返回大小;
}

@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(); ((newWidth - w)/ 2,(newHeight - h)/ 2);
at.translate
at.rotate(Math.toRadians(getAngle()),w / 2,h / 2);

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

受保护的类TurnAction扩展了AbstractAction {

受保护的浮动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);
}
}

}
}
}


I decided to re-write my game using Swing's painting technique paintComponent() method(someone on SO actually told me to use this method). I decided to use JPanel as the game's base instead of Canvas. My previous written game uses a Canvas but the game could not show up on my 64 bit desktop but could show up on my 32 bit labtop which is why this game had to be re-written.

Problem now is, while the ship's movement works, the drawing seems awfully slow(unless it is my laptop problem?) compare to what I did before which was using AWT's double buffering drawing technique. I spend a whole day but could not figure out what could possibly make the ship run faster.

   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

        }

    }

解决方案

Normally, I would create a concept of a renderable element. I would maintain a list of these elements and update them accordingly within my main loop.

At the very least, each would have a concept of location, direction and rotation (if required), they would also be capable of been painted.

Within my main component, I would simply loop through all these elements and "draw" them, offset the Graphics context to allow for there position within the game space.

But that's not what you are doing...

Remember, components have a sense of location and size already, you shouldn't be trying to re-implement this requirement, instead, you should be finding ways to take advantage of it...(ie, don't maintain a reference to the x/y values ;))

The following is a simple example. It uses a JPanel to render the main image. The main loop (in this case a javax.swing.Timer), tells the component that it should update it's movement as required.

The ship itself is responding to key events by changing the rotation value by a given, variable, delta. This allows you to control the speed of the spin as you need (I've deliberately set it low to start with, so play around with it)

What you should resist doing, is changing the frame rate ;)

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);
                }
            }

        }
    }
}

这篇关于使用paintComponent方法缓慢移动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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