如何主动更新图像的位置?

时间:2013-04-06 22:25:24

标签: java swing

我希望屏幕上有一个图像,只需移动即可。我能够让它显示在屏幕上,但我无法让它移动。这是一些代码:

播放器:

public class Player {

double positionX;
double positionY;
int destinationX;//Used when moving from place to place
int destinationY;
Tool currentTool;
int direction; //Position the image is facing
int dx;
int dy;
private String girl = "girl.png";
ImageIcon ii = new ImageIcon(this.getClass().getResource(girl));
private Image image = ii.getImage();
private boolean visible = true;

Image playerImage;

public Player(){
    positionX=30;
    positionY=20;
    dx = 2;
    dy = 2;
    destinationX=(int)positionX;
    destinationY=(int)positionY;

    //this.playerImage=playerImage;
}

public void doAction() {
    //currentTool.getNum();
}

public boolean isVisible() {
    return visible;
}

public void setVisible(Boolean visible) {
    this.visible = visible;
}

public Image getImage() {
    return image;
}

public void move(){
    //MOVE LEFT AND RIGHT
    if(destinationX<positionX){
        positionX-=dx;
    }
    if(destinationX>positionX){
        positionX+=dx;
    }

    //MOVE UP AND DOWN
    if(destinationY<positionY){
        positionY-=dy;
    }
    if(destinationY>positionY){
        positionY+=dy;
    }
}

public void setDestination(int x, int y){
    positionX=x;
    positionY=y;
    System.out.println(x + "," + y);
}

public void draw(Graphics g,ImageObserver io){
    g.drawImage(image, (int)positionX,(int) positionY,io);
}

Board(实现游戏元素/组件):

public class Board extends JPanel implements Runnable {

private static final int NO_DELAYS_PER_YIELD = 16;
/* Number of frames with a delay of 0 ms before the
   animation thread yields to other running threads. */

private static int MAX_FRAME_SKIPS = 5;
// no. of frames that can be skipped in any one animation loop
// i.e the games state is updated but not rendered


private Thread animator;
int x, y;
final int frameCount = 8;
BufferedImage flowers;
private int[][] fPos = {{232, 15},{400, 200},{335, 335}}; // flower coordinates 
private static int bWIDTH = 500; // width of window 
private static int bHEIGHT = 400;// height of window
private Font font;
private FontMetrics metrics;
private House house = new House();
private Flower flower = new Flower(); 
private Player girlP = new Player();
private int px = 200;
private int py = 400;



private long period;

private volatile boolean running = false;
private volatile boolean gameOver = false;
private volatile boolean isPaused = false;

private Graphics dbg;
private Image dbImage = null;


public Board(long period) {

    this.period = period;

    setBackground(Color.white);
    setPreferredSize(new Dimension(bWIDTH, bHEIGHT));

    setFocusable(true);
    requestFocus();     //JPanel now receives key events
    readyForTermination();

    // create game components

    // listen for mouse presses
    addMouseListener(new MouseAdapter() {
        public void mousePressed(MouseEvent e) {
            testPress(e.getX(), e.getY());
        }
    }); 

    // set up message font
    font = new Font("SansSerif", Font.BOLD, 24);
    metrics = this.getFontMetrics(font);

    x = 15;
    y = 150;
}   // end of 'Board()'

public void addNotify() {
    super.addNotify();
    startGame();
}

public void startGame() {
    if (animator == null || !running) {
        animator = new Thread(this);
        animator.start();
    }
}

public void pauseGame() {
    isPaused = true;
}

public void resumeGame() {
    isPaused = false;
}

public void stopGame() {
    running = false;
}

private void readyForTermination() {
    addKeyListener( new KeyAdapter() {
        public void keyPressed(KeyEvent e) {

            // listen for escape, q, or ctrl-c
            int keyCode = e.getKeyCode();
            if ((keyCode == KeyEvent.VK_ESCAPE) ||
                    (keyCode == KeyEvent.VK_Q) ||
                    (keyCode == KeyEvent.VK_END) ||
                    ((keyCode == KeyEvent.VK_C) && e.isControlDown()) ){
                running = false;
            }
        }
    });
}

private void testPress(int x, int y) {
    if (!isPaused && !gameOver) {
        // do something..
        px = x;
        py = y;
        System.out.println(px + ", " + py);
    }
}

private void gameRender() {
    if (dbImage == null) { // creating the buffer
        //dbImage = createImage(bWIDTH, bHEIGHT);
        if (dbImage == null) {
            //System.out.println("dbImage is null");
            return;
        }
        else 
            dbg = dbImage.getGraphics();
    }

    // clearing the background
    dbg.setColor(Color.gray);
    //dbg.fillRect(0, 0, bWIDTH, bHEIGHT);

    dbg.setColor(Color.blue);
    dbg.setFont(font);


    //drawing game elements....

    if (gameOver) {
        gameOverMessage(dbg);
    } // end of gameRender()
}

private void gameUpdate() {
    if (!isPaused && !gameOver) {
        //girlP.move();
    }
}

private void gameOverMessage(Graphics g)
// center the game-over message
{ // code to calculate x and y...
    String msg = "Game Over";
    g.drawString(msg, x, y);
}  // end of gameOverMessage( )


public void paint(Graphics g) {
    super.paint(g);

    if (dbImage != null) {
        g.drawImage(dbImage, 0, 0, null);   
    }

//      if (house.isVisible()) {
//          g.drawImage(house.getImage(), 10, 10, this);
//      }


    if (flower.isVisible()) {
        for (int i = 0; i < 3; i++) {
            g.drawImage(flower.getImage(), fPos[i][0], fPos[i][1],this);
        }
    }

    girlP.draw(g, this);


    //girlP.setDestination(0, 0);
    //girlP.move();

//      int red = 103;
//      int green = 10;
//      int blue = 100;
//      Color square = new Color(red, green, blue);
//      g.fillRect(x, y, sqW, sqH);


    //Toolkit.getDefaultToolkit().sync();
}

private void paintScreen() {
    // actively render the buffer to the screen

    Graphics g;
    try {
        g = this.getGraphics(); // get the panel's graphic context
        if ((g != null) && (dbImage != null))
            g.drawImage(dbImage, 0, 0, null);
        Toolkit.getDefaultToolkit().sync(); // sync the display on some systems
        g.dispose();
    }catch (Exception e) {
        System.out.println("Graphics context error: " + e);
    }
}


public void run() {
    // repeatedly update, render, and sleep

    long beforeTime, afterTime, timeDiff, sleep;
    long overSleepTime = 0L;
    int noDelays = 0;
    long excess = 0L;


    beforeTime = System.nanoTime();

    running = true;
    while (running) {
        gameUpdate();
        gameRender();
        paintScreen(); // this will draw buffer to screen instead of repaint()
        //updateChange();
        girlP.move();

        afterTime = System.nanoTime();
        timeDiff = afterTime - beforeTime;
        sleep = (period - timeDiff) - overSleepTime; // time left in this loop

        if (sleep > 0) { // some time left in this cycle

            try {
                Thread.sleep(sleep/1000000L); // nano -> ms
            }catch (InterruptedException e) {
                System.out.println("Interrupted");
            }   
            overSleepTime = (System.nanoTime() - afterTime) - sleep;


        }
        else {      // sleep <= 0; frame took longer than the delay
            excess -= sleep; // store excess time value
            overSleepTime = 0L;

            if (++noDelays >= NO_DELAYS_PER_YIELD) {
                Thread.yield( );   // give another thread a chance to run
                noDelays = 0;
            }
        }

        beforeTime = System.nanoTime();

        /* If frame animation is taking too long, update the game state
           without rendering it, to get the updates/sec nearer to
           the required FPS. */
        int skips = 0;
        while((excess > period) && (skips < MAX_FRAME_SKIPS)) {
            excess -= period;
            gameUpdate();      // update state but don't render
            skips++;
        }

    }
    System.exit(0);
} // end of run();

}

This is what I'm getting so far

1 个答案:

答案 0 :(得分:2)

有很多事情让我很突出。

使用getGraphics是一个非常糟糕的主意。 Swing使用由RepaintManager控制的被动渲染引擎。这基本上意味着虽然您可以请求重新绘制,但您无法控制何时可能发生更新。

摆动油漆过程也是无国籍的。这意味着之前绘制的内容将被丢弃,您想要绘制的任何内容都必须在paint方法之一中重新应用。

你的动作代码错了。您将值直接分配给position而不是destination变量。如果不需要移动,我修改了移动代码什么都不做,我实际上是出于调试目的而这样做,但干草。

你的核心渲染引擎......很奇怪......

你不太可能需要更多大约25 fps或等效物,所以使用纳秒可能有点过分,毫秒精度可能就足够了。当我改变延迟引擎时,我得到了更好的结果。

我还改变了核心渲染过程。引擎现在使用页面翻转过程。它使用两个BufferedImages,一个是绘制到屏幕的activeBuffer,另一个是scratchBuffer,用于更新游戏状态。然后根据需要翻转。这意味着屏幕上绘制的内容受到引擎渲染的内容的影响。

可能还有十几个其他的东西,但你可以自己比较差异;)

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.tools.Tool;

public class AnimationEngine {

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

    public AnimationEngine() {
        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 Board(40));
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public static class Board extends JPanel implements Runnable {

        protected static final Object UPDATE = new Object();
        private static final int NO_DELAYS_PER_YIELD = 16;
        /* Number of frames with a delay of 0 ms before the
         animation thread yields to other running threads. */
        private static int MAX_FRAME_SKIPS = 5;
// no. of frames that can be skipped in any one animation loop
// i.e the games state is updated but not rendered
        private Thread animator;
        int x, y;
        final int frameCount = 8;
        BufferedImage flowers;
        private int[][] fPos = {{232, 15}, {400, 200}, {335, 335}}; // flower coordinates 
        private static int bWIDTH = 500; // width of window 
        private static int bHEIGHT = 400;// height of window
        private Font font;
        private FontMetrics metrics;
//        private House house = new House();
//        private Flower flower = new Flower();
        private Player girlP = new Player();
        private int px = 200;
        private int py = 400;
        private long period;
        private volatile boolean running = false;
        private volatile boolean gameOver = false;
        private volatile boolean isPaused = false;
//        private Graphics dbg;
        private BufferedImage activeBuffer = null;
        private BufferedImage scratchBuffer = null;

        public Board(long period) {

            this.period = period;

            setBackground(Color.white);
            setPreferredSize(new Dimension(bWIDTH, bHEIGHT));

            setFocusable(true);
            requestFocus();     //JPanel now receives key events
            readyForTermination();

            // create game components

            // listen for mouse presses
            addMouseListener(new MouseAdapter() {
                public void mousePressed(MouseEvent e) {
                    System.out.println("Clicked");
                    girlP.setDestination(e.getX(), e.getY());
                }
            });

            // set up message font
            font = new Font("SansSerif", Font.BOLD, 24);
            metrics = this.getFontMetrics(font);

            x = 15;
            y = 150;
        }   // end of 'Board()'

        public void addNotify() {
            super.addNotify();
            startGame();
        }

        public void startGame() {
            if (animator == null || !running) {
                animator = new Thread(this);
                animator.start();
            }
        }

        public void pauseGame() {
            isPaused = true;
        }

        public void resumeGame() {
            isPaused = false;
        }

        public void stopGame() {
            running = false;
        }

        private void readyForTermination() {
            addKeyListener(new KeyAdapter() {
                public void keyPressed(KeyEvent e) {

                    // listen for escape, q, or ctrl-c
                    int keyCode = e.getKeyCode();
                    if ((keyCode == KeyEvent.VK_ESCAPE)
                                    || (keyCode == KeyEvent.VK_Q)
                                    || (keyCode == KeyEvent.VK_END)
                                    || ((keyCode == KeyEvent.VK_C) && e.isControlDown())) {
                        running = false;
                    }
                }
            });
        }

        @Override
        public void invalidate() {
            synchronized (UPDATE) {
                activeBuffer = null;
                scratchBuffer = null;
            }
            super.invalidate();
        }

        private void gameRender() {
            synchronized (UPDATE) {
                if (getWidth() > 0 && getHeight() > 0) {
                    if (scratchBuffer == null) {
                        scratchBuffer = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
                    }

                    Graphics2D dbg = scratchBuffer.createGraphics();

                    // clearing the background
                    dbg.setColor(Color.GRAY);
                    dbg.fillRect(0, 0, scratchBuffer.getWidth(), scratchBuffer.getHeight());

                    dbg.setColor(Color.blue);
                    dbg.setFont(font);


                    //drawing game elements....
                    girlP.draw(dbg, this);

                    if (gameOver) {
                        gameOverMessage(dbg);
                    } // end of gameRender()
                    dbg.dispose();
                }

                BufferedImage tmp = activeBuffer;
                activeBuffer = scratchBuffer;
                scratchBuffer = tmp;
            }
        }

        private void gameUpdate() {
            if (!isPaused && !gameOver) {
                girlP.move();
            }
        }

        private void gameOverMessage(Graphics g) // center the game-over message
        { // code to calculate x and y...
            String msg = "Game Over";
            g.drawString(msg, x, y);
        }  // end of gameOverMessage( )

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (activeBuffer != null) {
                g.drawImage(activeBuffer, 0, 0, null);
            }
        }

        public void run() {

            long beforeTime, afterTime, timeDiff, sleep;
            beforeTime = System.currentTimeMillis();

            running = true;
            while (running) {
                gameUpdate();
                gameRender();

                repaint();

                afterTime = System.currentTimeMillis();
                timeDiff = afterTime - beforeTime;
                sleep = (period - timeDiff); // - overSleepTime; // time left in this loop

                if (sleep > 0) { // some time left in this cycle
                    try {
                        Thread.sleep(sleep); // nano -> ms
                    } catch (InterruptedException e) {
                        System.out.println("Interrupted");
                    }
                } else {      // sleep <= 0; frame took longer than the delay
                    System.out.println("Over...");
                }

                beforeTime = System.currentTimeMillis();
            }
            System.exit(0);
        } // end of run();
    }

    public static class Player {

        double positionX;
        double positionY;
        int destinationX;//Used when moving from place to place
        int destinationY;
        Tool currentTool;
        int direction; //Position the image is facing
        int dx;
        int dy;
        private String girl = "/Player01.png";
        ImageIcon ii = new ImageIcon(this.getClass().getResource(girl));
        private Image image = ii.getImage();
        private boolean visible = true;
        Image playerImage;

        public Player() {
            positionX = 30;
            positionY = 20;
            dx = 4;
            dy = 4;
            destinationX = (int) positionX;
            destinationY = (int) positionY;

            //this.playerImage=playerImage;
        }

        public void doAction() {
            //currentTool.getNum();
        }

        public boolean isVisible() {
            return visible;
        }

        public void setVisible(Boolean visible) {
            this.visible = visible;
        }

        public Image getImage() {
            return image;
        }

        public void move() {
            if (destinationX != positionX || destinationY != positionY) {
                //MOVE LEFT AND RIGHT
                if (destinationX < positionX) {
                    positionX -= dx;
                }
                if (destinationX > positionX) {
                    positionX += dx;
                }

                //MOVE UP AND DOWN
                if (destinationY < positionY) {
                    positionY -= dy;
                }
                if (destinationY > positionY) {
                    positionY += dy;
                }
            }
        }

        public void setDestination(int x, int y) {
            destinationX = x;
            destinationY = y;
        }

        public void draw(Graphics g, ImageObserver io) {
            g.drawImage(image, (int) positionX, (int) positionY, io);
        }
    }
}