2014-03-12 215 views
0

我想用Java編寫我的第一個遊戲。我學習了一些教程,並學習瞭如何使用Canvas加載和更新背景以及如何加載和移動玩家精靈。我分別做了這兩個,他們工作得很好,但是當我把兩者放在一起並嘗試移動播放器時,遊戲速度減慢到無法播放的程度。只有當我按住箭頭鍵移動播放器時纔會發生這種情況;如果我快速點擊箭頭鍵,遊戲實際上會「順利」運行。在經過相當多的測試之後,我確信在每幀重繪背景時會出現問題。任何其他改進也將不勝感激。玩家移動時Java遊戲變慢

代碼(所有的話):

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

    } 

} 
+1

這是很多代碼,你可以簡化它到你認爲是造成問題嗎? – BitNinja

+0

我相信它在Game.java的渲染方法或Level.java中的某處 – ChaosNova7

回答

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(); 
0

你真的應該使用定時器。它會解決你所有的問題。

每一個勾號,你重繪所有你需要的東西。

而每一個滴答聲,你應該檢查,哪些鍵被按下,哪些不是,而不是添加監聽器。爲了跟蹤這個,你總是必須記住在「之前」按下的按鍵。

你甚至可以創建兩個定時器,一個用於圖形重繪,另一個用於遊戲邏輯。

即使定時器可以被延遲或者什麼,通常的方法是找出經過了多少時間(例如System.nanoTime)並計算您應該轉發多少遊戲邏輯以保持遊戲始終不流暢。