当玩家移动时,Java游戏会变慢

时间:2014-03-12 23:03:47

标签: java

我试图用Java编写我的第一个游戏。我按照一些教程学习了如何使用Canvas加载和更新背景以及如何加载和移动玩家精灵。我分别做了这两个并且它们工作正常,但是当我把两者放在一起并尝试移动玩家时,游戏会慢下来到无法播放的程度。这只发生在我按住箭头键移动播放器时;游戏实际上运行顺畅"如果我快速点击箭头键。经过相当多的测试后,我确信在每帧重绘背景时会出现问题。任何其他改进也将不胜感激。

代码(全部):

Game.Java:     包游戏;

import Level.Level;
import Player.Player;
import Sprites.SpriteSheetLoader;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferStrategy;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferInt;
import javax.swing.JFrame;

public class Game extends Canvas implements Runnable {

    // Set dimensions of the game.
    public static final int HEIGHT = 320;
    public static final int WIDTH = 480;
    public static final int SCALE = 2;
    public static Dimension GAME_DIM = new Dimension(WIDTH * SCALE, HEIGHT * SCALE);

    private BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
    private int[] pixels = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();

    public SpriteSheetLoader loader;
    public Screen screen;
    public Level level;
    public InputHandler input = new InputHandler(this);
    public Player player = new Player();

    private boolean running = false;
    private boolean moving = true;

    private int FPS = 60;
    private long targetTime = 1000 / FPS;

    // Set character's starting position at the center.  I have no idea why I had to add the "- 50" to each value.
    public int x = GAME_DIM.width / 2 - 50;
    public int y = GAME_DIM.height / 2 - 50;

    public int xScroll = 0;
    public int yScroll = 0;

    public int col = 0;
    public int row = 0;

    public int ticks = 0;
    public int frame = 0;

    public static void main(String[] args) {

        Game game = new Game();
        game.setPreferredSize(new Dimension(GAME_DIM));
        game.setMaximumSize(new Dimension(GAME_DIM));
        game.setMinimumSize(new Dimension(GAME_DIM));

        JFrame frame = new JFrame("Valkyrie Game");

        frame.add(game);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setResizable(true);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

        game.start();

    }

    public void start() {
        running = true;
        new Thread(this).start();
    }

    public Game() {

    }

    public void init() {

        loader = new SpriteSheetLoader();
        screen = new Screen(WIDTH, HEIGHT);

        level = new Level(16, 16);

    }

    public void run() {
        init();

        long start, elapsed, wait;

        while (running) {

            start = System.nanoTime();

            render();
            tick();

            elapsed = System.nanoTime() - start;
            //System.out.println("Elapsed: " + elapsed);

            wait = targetTime - elapsed / 1000000;
            if(wait < 0) {
                wait = 5;
            }

            try {
                Thread.sleep(wait);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

    public void stop() {
        running = false;
    }

    public void tick() {

        // Movement
        if (input.right) {
            xScroll++;
            player.setAnimation(player.walkRight);
            //x++;
            row = 2;

            ticks++;
            if(ticks < 10) {
                frame = 1;
            } else if(ticks == 10) {
                frame = 2;
            } else if(ticks == 20) {
                frame = 3;
            } else if(ticks == 30) {
                frame = 2;
            } else if(ticks == 40) {
                frame = 1;
            } else if(ticks == 50) {
                ticks = 0;
                frame = 0;
            }

            moving = true;

        } else if (input.left) {
            xScroll--;
            player.setAnimation(player.walkLeft);
            //x--;
            row = 1;

            ticks++;
            if(ticks < 10) {
                frame = 1;
            } else if(ticks == 10) {
                frame = 2;
            } else if(ticks == 20) {
                frame = 3;
            } else if(ticks == 30) {
                frame = 2;
            } else if(ticks == 40) {
                frame = 1;
            } else if(ticks == 50) {
                ticks = 0;
                frame = 0;
            }

            moving = true;

        } else if (input.up) {
            yScroll--;
            player.setAnimation(player.walkUp);
            //y--;
            row = 3;

            ticks++;
            if(ticks < 10) {
                frame = 1;
            } else if(ticks == 10) {
                frame = 2;
            } else if(ticks == 20) {
                frame = 3;
            } else if(ticks == 30) {
                frame = 2;
            } else if(ticks == 40) {
                frame = 1;
            } else if(ticks == 50) {
                ticks = 0;
                frame = 0;
            }

            moving = true;

        } else if (input.down) {
            yScroll++;
            player.setAnimation(player.walkDown);
            //y++;
            row = 0;

            ticks++;
            if(ticks < 10) {
                frame = 1;
            } else if(ticks == 10) {
                frame = 2;
            } else if(ticks == 20) {
                frame = 3;
            } else if(ticks == 30) {
                frame = 2;
            } else if(ticks == 40) {
                frame = 1;
            } else if(ticks == 50) {
                ticks = 0;
                frame = 0;
            }

            moving = true;

        }

        if (!input.down && !input.left && !input.right && !input.up) {
            player.setAnimation(player.stand);
            frame = 0;
            ticks = 1;
            moving = false;
        }

        //System.out.println("Tick: " + ticks);

    }

    public void render() {
        BufferStrategy bs = getBufferStrategy();
        if (bs == null) {
            createBufferStrategy(3);
            requestFocus();

            return;
        }

        do {
            Graphics g = bs.getDrawGraphics();
            try {
                for (int i = 0; i < ticks; i++) {

                    g.drawImage(image, 0, 0, getWidth(), getHeight(), null);
                    g.drawImage(player.Player(frame, row), x, y, null);

                    level.renderBackground(xScroll, yScroll, screen);

                    for (int y = 0; y < this.screen.h; y++) {
                        for (int x = 0; x < screen.w; x++) {
                            pixels[x + (y * WIDTH)] = screen.pixels[x + (y * screen.w)];
                        }
                    }
                }
            } finally {
                g.dispose();
            }
            bs.show();
            this.update(bs.getDrawGraphics());
        } while (bs.contentsLost());

//        Graphics g = bs.getDrawGraphics();
//        
//        g.dispose();
//        bs.show();
    }

}

InputHandler.Java:

package Game;

import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;

public class InputHandler implements KeyListener {

    public boolean up = false;
    public boolean down = false;
    public boolean left = false;
    public boolean right = false;

    public InputHandler(Game game) {

        game.addKeyListener(this);

    }

    public void toggle(KeyEvent ke, boolean pressed) {
        int keyCode = ke.getKeyCode();

        if(keyCode == KeyEvent.VK_UP) up = pressed;
        if(keyCode == KeyEvent.VK_DOWN) down = pressed;
        if(keyCode == KeyEvent.VK_LEFT) left = pressed;
        if(keyCode == KeyEvent.VK_RIGHT) right = pressed;
    }

    public void keyTyped(KeyEvent e) {
    }

    public void keyPressed(KeyEvent e) {
        toggle(e, true);
    }

    public void keyReleased(KeyEvent e) {
        toggle(e, false);
    }

}

Screen.Java:

package Game;

import Sprites.Sprite;

public class Screen {

    public int w, h;
    int xOffset = 0;
    int yOffset = 0;
    public int[] pixels;

    public Screen(int w, int h) {
        this.w = w; // 480
        this.h = h; // 320

        pixels = new int[w * h];  // 153600
    }

    public void renderSprite(int xPos, int yPos, Sprite sprite) {

        int height = sprite.h;
        int width = sprite.w;

        xPos -= xOffset;
        yPos -= yOffset;

        for(int y = 0; y < height; y++) {
            if(yPos + y < 0 || yPos + y >= h) continue;

            for(int x = 0; x < width; x++) {
                if(xPos + x < 0 || xPos + x >= w) continue;

                int col = sprite.pixels[x + (y * height)];
                if(col != -65281 && col < 0) pixels[(x + xPos) + (y + yPos) *w]= col;
            }
        }

    }

    public void setOffs(int xOffs, int yOffs) {
        xOffset = xOffs;
        yOffset = yOffs;
    }
}

Level.Java:

package Level;

import Game.Screen;
import Sprites.Sprite;
import Sprites.Sprites;
import Tiles.Tile;

public class Level {

    int w, h;

    public int[] tiles;

    public Level(int w, int h) {
        this.w = w;
        this.h = h;

        tiles = new int[w * h];

        loadMap(0, 0, 0, 0);
    }

    public void renderBackground(int xScroll, int yScroll, Screen screen) {
        int xo = xScroll >> 4;
        int yo = yScroll >> 4;

        int w = (screen.w + 15) >> 4;
        int h = (screen.h + 15) >> 4;

        screen.setOffs(xScroll, yScroll);

        for(int y = yo; y <= h + yo; y++) {
            for(int x = xo; x <= w + xo; x++) {
                getTile(x, y).render(x, y, screen);
            }

        }

        screen.setOffs(0, 0);
    }

    public Tile getTile(int x, int y) {
        if(x < 0 || y < 0 || x >= w || y >= h) return Tile.rockTile;
        return Tile.tiles[tiles[x + y * w]];
    }

    public void loadMap(int x0, int y0, int x1, int y1) {
        Sprite sprite = Sprites.level[x0][y0];

        for(int y = 0; y < sprite.h; y++) {
            for(int x = 0; x < sprite.w; x++) {
                if(sprite.pixels[x + y * sprite.h] == -9276814) {
                    tiles[x + x1 + (y + y1) * h] = Tile.rockTile.id;
                } else {
                    tiles[x + x1 + (y + y1) * h] = Tile.grassTile.id;
                }
            }
        }
    }

}

Player.Java:

package Player;

import Animation.Animation;
import Sprites.Sprite;
import java.awt.image.BufferedImage;

public class Player {

    // Images for each animation
    private BufferedImage[] walkingLeft = {Sprite.getSprite(0, 1), Sprite.getSprite(1, 1), Sprite.getSprite(2, 1)}; // Gets the upper left images of my sprite sheet
    private BufferedImage[] walkingRight = {Sprite.getSprite(0, 2), Sprite.getSprite(1, 2), Sprite.getSprite(2, 2)};
    private BufferedImage[] walkingUp = {Sprite.getSprite(0, 3), Sprite.getSprite(1, 3), Sprite.getSprite(2, 3)};
    private BufferedImage[] walkingDown = {Sprite.getSprite(0, 0), Sprite.getSprite(1, 0), Sprite.getSprite(2, 0)};
    private BufferedImage[] standing = {Sprite.getSprite(1, 0)};

    // These are animation states.
    public Animation walkLeft = new Animation(walkingLeft, 10);
    public Animation walkRight = new Animation(walkingRight, 10);
    public Animation walkUp = new Animation(walkingUp, 10);
    public Animation walkDown = new Animation(walkingDown, 10);
    public Animation stand = new Animation(standing, 10);

    // This is the actual animation
    public Animation animation = stand;

    public BufferedImage Player(int x, int y) {

        BufferedImage player = Sprite.getSprite(x, y);

        return player;

    }

    public void update() {

        animation.update();

    }

    public void render() {



    }

    public void setAnimation(Animation animation) {
        this.animation = animation;
    }

}

Sprite.Java:

package Sprites;

import Game.Game;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;

public class Sprite {

    public int w, h;
    public int[] pixels;

    public static BufferedImage sprite = null;

    public Sprite(int w, int h) {
        this.w = w;
        this.h = h;
        pixels = new int[w * h];
    }

    public void clear(int color) {
        for(int i = 0; i < pixels.length; i++) {
            pixels[i] = color;
        }
    }

    private static BufferedImage spriteSheet;
    private static final int TILE_SIZE = 80;

    public static BufferedImage loadSprite() {



        try {

            sprite = ImageIO.read(Game.class.getResource("/valkyrie.png"));

        } catch (IOException e) {
            e.printStackTrace();
        }

        return sprite;

    }

    public static BufferedImage getSprite(int xGrid, int yGrid) {

        if(spriteSheet == null) {
            spriteSheet = loadSprite();
        }

        // xGrid and yGrid refer to each individual sprite
        return spriteSheet.getSubimage(xGrid * TILE_SIZE, yGrid * TILE_SIZE, TILE_SIZE, TILE_SIZE);

    }

}

3 个答案:

答案 0 :(得分:0)

虽然我无法完全通过代码,但似乎你不会做双重缓冲,这会严重影响性能。

在程序的相关部分尝试:

JFrame frame = new JFrame("Valkyrie Game");
frame.add(game);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setResizable(true);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.setDoubleBuffered(true); //added line, rest is the same

game.start();

答案 1 :(得分:0)

你真的应该使用Timer。它将解决您的所有问题。

每次打勾,都会重新绘制所需内容。

每次打勾,你应该检查,哪些键被按下,哪些不是,而不是添加听众。为了跟踪这一点,你必须记住“之前”按下的键。

你甚至可以创建两个计时器,一个用于图形重绘,一个用于游戏逻辑。

即使是定时器也可以延迟或者其他东西,通常的方法是找出,已经过了多少时间(例如System.nanoTime)并计算你应该转发多少游戏逻辑以保持游戏总是不合时宜和流利。

答案 2 :(得分:0)

这需要双重缓冲。任何有很多事情发生的游戏都需要双重缓冲。

How do you double buffer in java for a game?