瓷砖运动面临的问题 - Flappy Bird - Java 游戏编程

问题描述 投票:0回答:1

我正在尝试开发一款飞扬的小鸟游戏。现在我面临着背景移动的问题。就我的代码而言,鸟在移动,然后背景也在移动。我的游戏世界的大小需要大于物理窗口游戏的大小。在这种类型中 在游戏中,世界应该看起来围绕玩家移动,而不是玩家移动 世界(当世界在玩家下方移动时,玩家将显得静止)。任何人都可以帮我将背景从右上方移动到左上方。这里有一朵云从右向左移动。这正在起作用。但背景图块没有发生移动。

 import java.awt.event.KeyEvent;
import java.io.InputStream;
import java.util.ArrayList;

import javax.sound.midi.MidiSystem;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;

import java.awt.*;


import game2D.*;

@SuppressWarnings("serial")
public class Game extends GameCore 
{
 // Useful game constants
 static int screenWidth = 612;
 static int screenHeight = 484;

 // Game constants
 float   lift = 0.005f;
 float   gravity = 0.0001f;
 float   fly = -0.04f;
 float   moveSpeed = 0.05f;
 
 // Game state flags
 boolean flap = false;
 boolean moveRight = false;
 boolean debug = true;       

 // Game resources
 Animation landing;
 
 Sprite  player = null;
 ArrayList<Sprite>   clouds = new ArrayList<Sprite>();
 ArrayList<Tile>     collidedTiles = new ArrayList<Tile>();

 TileMap tmap = new TileMap();   // Our tile map, note that we load it in init()
 
 long total;                     // The score will be the total time elapsed since a crash
 private boolean gameOver = false;
 private Sound backgroundSound;
 private Sound gameOverSound;
 private Sequencer sequencer; // For playing MIDI music

 /**
  * The obligatory main method that creates
  * an instance of our class and starts it running
  * 
  * @param args  The list of parameters this program might use (ignored)
  */
 public static void main(String[] args) {

     Game gct = new Game();
     gct.init();
     // Start in windowed mode with the given screen height and width
     gct.run(false,screenWidth,screenHeight);
 }

 /**
  * Initialise the class, e.g. set up variables, load images,
  * create animations, register event handlers.
  * 
  * This shows you the general principles but you should create specific
  * methods for setting up your game that can be called again when you wish to 
  * restart the game (for example you may only want to load animations once
  * but you could reset the positions of sprites each time you restart the game).
  */
 public void init()
 {         
     Sprite s;   // Temporary reference to a sprite

     // Load the tile map and print it out so we can check it is valid
     tmap.loadMap("maps", "map.txt");
     
     setSize(tmap.getPixelWidth()/4, tmap.getPixelHeight());
     adjustWindowSize(); // Adjust window size to match game size
     setVisible(true);

     // Create a set of background sprites that we can 
     // rearrange to give the illusion of motion
     
     landing = new Animation();
     landing.loadAnimationFromSheet("images/landbird.png", 4, 1, 60);
     
     // Initialise the player with an animation
     player = new Sprite(landing);
     
     // Load a single cloud animation
     Animation ca = new Animation();
     ca.addFrame(loadImage("images/cloud.png"), 1000);
     
     // Create 3 clouds at random positions off the screen
     // to the right
     for (int c=0; c<3; c++)
     {
         s = new Sprite(ca);
         s.setX(screenWidth + (int)(Math.random()*200.0f));
         s.setY(30 + (int)(Math.random()*150.0f));
         s.setVelocityX(-0.02f);
         s.show();
         clouds.add(s);
     }
    
     initialiseGame();
         
     System.out.println(tmap);
     try {
         sequencer = MidiSystem.getSequencer();
         sequencer.open();
         InputStream is = getClass().getResourceAsStream("background.mid");
         if (is != null) {
             Sequence sequence = MidiSystem.getSequence(is);
             sequencer.setSequence(sequence);
             sequencer.start(); // Start playback
         } else {
             System.err.println("MIDI file not found");
         }
     } catch (Exception e) {
         e.printStackTrace();
     }
     
 }

 /**
  * You will probably want to put code to restart a game in
  * a separate method so that you can call it when restarting
  * the game when the player loses.
  */
 public void initialiseGame()
 {
     total = 0;
           
     player.setPosition(200,200);
     player.setVelocity(0,0);
     player.show();
     gameOver=false;
     // Start playing background music
     if (sequencer != null && sequencer.isOpen()) {
         sequencer.stop(); // Stop the music if it's currently playing
         sequencer.setMicrosecondPosition(0); // Rewind the music to the beginning
         sequencer.start(); // Start playing from the beginning
         sequencer.setLoopCount(Sequencer.LOOP_CONTINUOUSLY);
     }
 }
 /**
  * Adjusts the size of the window to match the size of the game.
  */
 private void adjustWindowSize() {
     Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();

     // Set the game size to match the tile map size
     int gameWidth = tmap.getPixelWidth();
     int gameHeight = tmap.getPixelHeight();

     // Calculate the position to center the game on the screen
     int x = (screenSize.width - gameWidth) / 2;
     int y = (screenSize.height - gameHeight) / 2;

     // Update the size of the window to match the game rectangle
     setSize(gameWidth, gameHeight);
     setLocation(x, y);
 }




 /**
  * Draw the current state of the game. Note the sample use of
  * debugging output that is drawn directly to the game screen.
  */
 public void draw(Graphics2D g)
 {       
     // Be careful about the order in which you draw objects - you
     // should draw the background first, then work your way 'forward'

     // First work out how much we need to shift the view in order to
     // see where the player is. To do this, we adjust the offset so that
     // it is relative to the player's position along with a shift
     int xo = -(int)player.getX() + 200;
     int yo = -(int)player.getY() + 200;

     g.setColor(Color.white);
     g.fillRect(0, 0, getWidth(), getHeight());
     g.setFont(new Font("Arial", Font.BOLD, 10));

     // Apply offsets to sprites then draw them
     for (Sprite s: clouds)
     {
         s.setOffsets(xo,yo);
         s.draw(g);
     }

     // Apply offsets to tile map and draw  it
     tmap.draw(g,xo,yo); 

     // Apply offsets to player and draw 
     player.setOffsets(xo, yo);
     player.draw(g);
             
     
     // Show score and status information
     String msg = String.format("Score: %d", total/100);
     g.setColor(Color.darkGray);
     g.drawString(msg, getWidth() - 100, 50);
     
     if (debug)
     {

         // When in debug mode, you could draw borders around objects
         // and write messages to the screen with useful information.
         // Try to avoid printing to the console since it will produce 
         // a lot of output and slow down your game.
         tmap.drawBorder(g, xo, yo, Color.black);

         g.setColor(Color.red);
         player.drawBoundingBox(g);
     
         g.drawString(String.format("Player: %.0f,%.0f", player.getX(),player.getY()),
                                     getWidth() - 100, 70);
         
         drawCollidedTiles(g, tmap, xo, yo);
     }

 }

 public void drawCollidedTiles(Graphics2D g, TileMap map, int xOffset, int yOffset)
 {
     if (collidedTiles.size() > 0)
     {   
         
         int tileWidth = map.getTileWidth();
         int tileHeight = map.getTileHeight();
         
         g.setColor(Color.blue);
         for (Tile t : collidedTiles)
         {
             g.drawRect(t.getXC()+xOffset, t.getYC()+yOffset, tileWidth, tileHeight);
         }
         player.setPosition(player.getX(), player.getY());
         showGameOver(g);
         showRestartOption(g);
         gameOver = true; // Set game over flag  
     }
 }
 public void showGameOver(Graphics2D g) {
     g.setColor(Color.RED);
     String gameOverMessage = "Game Over, Your score is "+total;
     
     int x = (screenWidth - g.getFontMetrics().stringWidth(gameOverMessage)) / 2;
     int y = screenHeight / 2;
     g.drawString(gameOverMessage, x, y);
    
     
  // Stop background music
     if (sequencer != null && sequencer.isOpen()) {
         sequencer.stop();
     }
     
 }
 public void showRestartOption(Graphics2D g) {
     g.setColor(Color.BLACK);
     String restartMessage = "Press Enter to Restart";
     int x = (screenWidth - g.getFontMetrics().stringWidth(restartMessage)) / 2;
     int y = screenHeight / 2 + 30;
     g.drawString(restartMessage, x, y);
 }
 /**
  * Update any sprites and check for collisions
  * 
  * @param elapsed The elapsed time between this call and the previous call of elapsed
  */    
 public void update(long elapsed) {
     boolean playerMoved = false;

     // Check if the player has reached the right edge of the screen
     if (player.getX() + player.getWidth() >= tmap.getPixelWidth()) {
         // Move player to the left side of the screen
         player.setX(0);

         // Repeat the background sprite by adjusting the x position of the clouds
         for (Sprite s : clouds) {
             s.setX(s.getX() - tmap.getPixelWidth()); // Move the cloud sprite back by the width of the map
             s.update(elapsed);
         }
     }

     // Update player's movement and track if it moves horizontally
     if (flap || moveRight) {
         playerMoved = true;
         player.setVelocityY(player.getVelocityY() + (gravity * elapsed));
         player.setAnimationSpeed(1.0f);
         if (flap) {
             player.setAnimationSpeed(1.8f);
             player.setVelocityY(fly);
         }
         if (moveRight) {
             player.setVelocityX(moveSpeed);
         }
     }

     // Update total score based on player's movement
     if (!gameOver && playerMoved) {
         total += elapsed; // Increment the total score by the elapsed time
     }

     // Make adjustments to the speed of the sprite due to gravity
     player.setVelocityY(player.getVelocityY() + (gravity * elapsed));

     player.setAnimationSpeed(1.0f);

     if (flap) {
         player.setAnimationSpeed(1.8f);
         player.setVelocityY(fly);
     }

     if (moveRight) {
         player.setVelocityX(moveSpeed);
     } else {
         player.setVelocityX(0);
     }

     // Update the clouds
     for (Sprite s : clouds) {
         s.update(elapsed);
         if (s.getX() + s.getWidth() < 0) {
             // If the cloud has moved off the left side of the screen
             // Move it to the right side of the screen
             s.setX(screenWidth);
             // Optionally, randomize the Y position to create variety
             s.setY(30 + (int)(Math.random() * 150.0f));
         }
     }

     // Update the player's animation and position
     player.update(elapsed);

     // Then check for any collisions that may have occurred
     handleScreenEdge(player, tmap, elapsed);
     checkTileCollision(player, tmap);
 }


 
 
 /**
  * Checks and handles collisions with the edge of the screen. You should generally
  * use tile map collisions to prevent the player leaving the game area. This method
  * is only included as a temporary measure until you have properly developed your
  * tile maps.
  * 
  * @param s         The Sprite to check collisions for
  * @param tmap      The tile map to check 
  * @param elapsed   How much time has gone by since the last call
  */
 public void handleScreenEdge(Sprite s, TileMap tmap, long elapsed)
 {
     // This method just checks if the sprite has gone off the bottom screen.
     // Ideally you should use tile collision instead of this approach
     
     float heightDifference = s.getY() + s.getHeight() - tmap.getPixelHeight();
     if (heightDifference > 0)
     {
         // Put the player back on the map according to how far over they were
         s.setY(tmap.getPixelHeight() - s.getHeight() - (int)(heightDifference)); 
         
         // and make them bounce
         s.setVelocityY(-s.getVelocityY()*0.75f);
     }
     float widthDifference = s.getX() + s.getWidth() - tmap.getPixelWidth();
     if (widthDifference > 0)
     {
         // Put the player back on the map according to how far over they were
         s.setX(tmap.getPixelWidth() - s.getWidth() - (int)(widthDifference)); 
         
         // and make them bounce
         s.setVelocityX(-s.getVelocityX()*0.75f);
     }
     
 }
 
 
  
 /**
  * Override of the keyPressed event defined in GameCore to catch our
  * own events
  * 
  *  @param e The event that has been generated
  */
 public void keyPressed(KeyEvent e) 
 { 
     int key = e.getKeyCode();
     
     if (gameOver) { // Check if the game is over 
         if (key == KeyEvent.VK_ENTER) {
             initialiseGame(); // Restart the game
         }
         return;
     }
     
     switch (key) {
         case KeyEvent.VK_UP     : flap = true; break;
         case KeyEvent.VK_RIGHT  : moveRight = true; break;
         case KeyEvent.VK_S      : Sound s = new Sound("sounds/caw.wav"); 
                                   s.start();
                                   break;                
         case KeyEvent.VK_ESCAPE : stop(); break;
         case KeyEvent.VK_B      : debug = !debug; break; // Flip the debug state
         default :  break;
     }
 
 }

 /** Use the sample code in the lecture notes to properly detect
  * a bounding box collision between sprites s1 and s2.
  * 
  * @return  true if a collision may have occurred, false if it has not.
  */
 public boolean boundingBoxCollision(Sprite s1, Sprite s2)
 {
     return false;       
 }
 
 /**
  * Check and handles collisions with a tile map for the
  * given sprite 's'. Initial functionality is limited...
  * 
  * @param s         The Sprite to check collisions for
  * @param tmap      The tile map to check 
  */

 public void checkTileCollision(Sprite s, TileMap tmap)
 {
     // Empty out our current set of collided tiles
     collidedTiles.clear();
     
     // Take a note of a sprite's current position
     float sx = s.getX();
     float sy = s.getY();
     
     // Find out how wide and how tall a tile is
     float tileWidth = tmap.getTileWidth();
     float tileHeight = tmap.getTileHeight();
     
     // Divide the sprite’s x coordinate by the width of a tile, to get
     // the number of tiles across the x axis that the sprite is positioned at 
     int xtile = (int)(sx / tileWidth);
     // The same applies to the y coordinate
     int ytile = (int)(sy / tileHeight);
     
     // What tile character is at the top left of the sprite s?
     Tile tl = tmap.getTile(xtile, ytile);
     
     
     if (tl != null && tl.getCharacter() != '.') // If it's not a dot (empty space), handle it
     {
         // Here we just stop the sprite. 
         s.stop();
         collidedTiles.add(tl);
         
         // You should move the sprite to a position that is not colliding
     }
     
     // We need to consider the other corners of the sprite
     // The above looked at the top left position, let's look at the bottom left.
     xtile = (int)(sx / tileWidth);
     ytile = (int)((sy + s.getHeight())/ tileHeight);
     Tile bl = tmap.getTile(xtile, ytile);
     
     // If it's not empty space
     if (bl != null && bl.getCharacter() != '.') 
     {
         // Let's make the sprite bounce
         s.setVelocityY(-s.getVelocityY()*0.6f); // Reverse velocity 
         collidedTiles.add(bl);
     }
 }


 public void keyReleased(KeyEvent e) { 

     int key = e.getKeyCode();

     switch (key)
     {
         case KeyEvent.VK_ESCAPE : stop(); break;
         case KeyEvent.VK_UP     : flap = false; break;
         case KeyEvent.VK_RIGHT  : moveRight = false; break;
         default :  break;
     }
 }
 public void stop() {
     // Stop background music
     if (sequencer != null && sequencer.isOpen()) {
         sequencer.stop();
     }
 }
}

import javax.swing.ImageIcon;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.util.HashMap;
import java.util.Map;
import java.io.*;



/**
 * TileMap enables you to load a character based tile map from a text file. An
 * example of the format for such a text file is given below:

10 5 32 32
// The first line should contain the width and height of the
// map and the width and height of each tile. A list of character to
// tile mappings is then provided where each character is preceded by a
// # character. The dot character always defaults to a blank space 
// Note that the referenced files should be in the same directory as the
// tile map.
#b=orangeblock.png
#c=greencircle.png
#g=glasses.png
// The actual tile map is preceded by the #map line
#map
bbbbbbbbbb
b........b
b..g.....b
bccccccccb
bbbbbbbbbb

}
 * @author David Cairns
 */
public class TileMap 
{

    private Tile [][] tmap;     // The tile map grid, initially null
    private int mapWidth=0;     // The maps width in tiles
    private int mapHeight=0;    // The maps height in tiles
    private int tileWidth=0;    // The width of a tile in pixels
    private int tileHeight=0;   // The height of a tile in pixels
    
    // imagemap contains a set of character to image mappings for
    // quick loop up of the image associated with a given character.
    private Map<String,Image> imagemap = new HashMap<String,Image>();
    
    /**
     * @return The map height in tiles
     */
    public int getMapHeight() {
        return mapHeight;
    }

    /**
     * @return The map width in tiles
     */
    public int getMapWidth() {
        return mapWidth;
    }
    
    /**
     * @return The height of a tile in pixels.
     */
    public int getTileHeight() {
        return tileHeight;
    }

    /**
     * @return The width of a tile in pixels.
     */
    public int getTileWidth() {
        return tileWidth;
    }

    /**
     * @return The map height in pixels
     */
    public int getPixelHeight() {
        return mapHeight * tileHeight;
    }

    /**
     * @return The map width in pixels
     */
    public int getPixelWidth() {
        return mapWidth * tileWidth;
    }
    
    /**
     * Loads a 'mapfile' that is contained in the given 'folder'. It is expected that
     * the images associated with the map will also be in 'folder'.
     *  
     * @param folder The folder the tile map and images are located in
     * @param mapfile The name of the map file in the map folder
     * @return true if the map loaded successfully, false otherwise
     */
    public boolean loadMap(String folder, String mapfile)
    {
        // Create a full path to the tile map by sticking the folder and mapfile together
        String path = folder + "/" + mapfile;
        int row=0;
        
        try
        {
            BufferedReader in = new BufferedReader(new FileReader(path));
            String line="";
            String trimmed="";
            String [] vals;
            
            // First we need to clear out the old image map
            imagemap.clear();
            
            // Read the first line of the tile map to find out
            // the relevant dimensions of the map plus the tiles
            line = in.readLine();
            vals = line.split(" ");
            // Check that we read 4 values
            if (vals.length != 4)
            {
                System.err.println("Incorrect number of parameters in the TileMap header:" + vals.length);
                in.close();
                return false;
            }
            
            // Read in the map dimensions
            mapWidth = Integer.parseInt(vals[0]);
            mapHeight = Integer.parseInt(vals[1]);
            tileWidth = Integer.parseInt(vals[2]);
            tileHeight = Integer.parseInt(vals[3]);
            
            // Now look for the character assignments
            while ((line = in.readLine()) != null)
            {
                trimmed = line.trim();
                // Skip the current line if it's a comment
                if (trimmed.startsWith("//")) continue;
                // Break out of the loop if we find the map
                if (trimmed.startsWith("#map")) break;
                
                if (trimmed.charAt(0) == '#') // Look for a character to image map
                {
                    // Extract the character
                    
                    String ch = "" + trimmed.charAt(1);
                    // and it's file name
                    String fileName = trimmed.substring(3,trimmed.length());
                    
                    Image img  = new ImageIcon(folder + "/" + fileName).getImage();
                    // Now add this character->image mapping to the map
                    if (img != null)
                        imagemap.put(ch,img);
                    else
                        System.err.println("Failed to load image '" + folder + "/" + fileName + "'");
                }
            }
            
            // Check the map dimensione are at least > 0
            if ((mapWidth > 0) && (mapHeight > 0))
            {
                tmap = new Tile[mapWidth][mapHeight]; 
            }
            else
            {
                System.err.println("Incorrect image map dimensions.");
                trimmed = "";
            }
            
            // Now read in the tile map structure
            if (trimmed.startsWith("#map"))
            {
                row=0;
                while ((line = in.readLine()) != null)
                {
                    if (line.trim().startsWith("//")) continue;
                
                    if (line.length() != mapWidth)
                    {
                        System.err.println("Incorrect line length in map");
                        System.err.println(row + " : " + line);
                        continue;
                    }
                    
                    for (int col=0; col<mapWidth && col<line.length(); col++)
                        tmap[col][row] = new Tile(line.charAt(col),col*tileWidth,row*tileHeight);
                    row++;
                    
                    if (row >= mapHeight) break;
                }
            }
            
            in.close();
            
        }
        catch (Exception e)
        {
            System.err.println("Failed to read in tile map '" + path + "':" + e);
            return false;
        }
        
        if (row != mapHeight)
        {
            System.err.println("Map failed to load. Incorrect rows in map");
            return false;
        }
        
        return true;
    }
    
    /**
     * Generate the tile map as a String so we can inspect its current state
     */
    public String toString()
    {
        StringBuffer s = new StringBuffer();
        for (int r=0; r<mapHeight; r++)
        {
            for (int c=0; c<mapWidth; c++)
                s.append(tmap[c][r].getCharacter());
    
            s.append('\n');
        }
        return s.toString();
    }
    
    /**
     * Get the Image object associated with the tile at position 'x','y'
     * 
     * @param x The x tile coordinate (in tiles, not pixels)
     * @param y The y tile coordinate (in tiles, not pixels)
     * @return The Image object associated with the tile at position 'x,y', null if blank or not found
     */
    public Image getTileImage(int x, int y)
    {
        if (!valid(x,y)) return null;
        Tile t = tmap[x][y];
        if (t == null) return null;
        char ch = t.getCharacter();
        if (ch == '.') return null; // Blank space
        return imagemap.get(ch + "");
    }
    
    /**
     * Get the top left pixel x coordinate of a tile at position 'x,y' in the tile map
     *  
     * @param x The x tile coordinate (in tiles, not pixels)
     * @param y The y tile coordinate (in tiles, not pixels)
     * @return The top left pixel x coordinate of a tile at position 'x,y' in the tile map
     */
    public int getTileXC(int x, int y)
    {
        if (!valid(x,y)) return 0;
        return tmap[x][y].getXC();
    }
    
    /**
     * Get the top left pixel y coordinate of a tile at position 'x,y' in the tile map
     *  
     * @param x The x tile coordinate (in tiles, not pixels)
     * @param y The y tile coordinate (in tiles, not pixels)
     * @return The top left pixel y coordinate of a tile at position 'x,y' in the tile map
     */ 
    public int getTileYC(int x, int y)
    {
        if (!valid(x,y)) return 0;
        return tmap[x][y].getYC();
    }
    
    /**
     * 
     * @param x The x tile coordinate (in tiles, not pixels)
     * @param y The y tile coordinate (in tiles, not pixels)
     * @return true if tile coordinate 'x,y' is a valid position in the tile map
     */
    public boolean valid(int x, int y)
    {
        return (x >= 0 && y >= 0 && x<mapWidth && y<mapHeight);
    }
    
    /**
     * Sets the tile character at position 'x,y' to the value of 'ch'.
     * 
     * @param ch The character to set the tile to.
     * @param x The x tile coordinate (in tiles, not pixels)
     * @param y The y tile coordinate (in tiles, not pixels)
     * @return true if the character was correctly set
     */
    public boolean setTileChar(char ch, int x, int y)
    {
        if (!valid(x,y)) return false;
        tmap[x][y].setCharacter(ch);
        return true;
    }
    
    /**
     * Gets the tile character at position 'x,y'
     * 
     * @param x The x tile coordinate (in tiles, not pixels)
     * @param y The y tile coordinate (in tiles, not pixels)
     * @return The character the tile is currently set to.
     */
    public char getTileChar(int x, int y)
    {
        if (!valid(x,y)) return '?';
        return tmap[x][y].getCharacter();
    }

    /**
     * Gets the tile object at position 'x,y'
     * 
     * @param x The x tile coordinate (in tiles, not pixels)
     * @param y The y tile coordinate (in tiles, not pixels)
     * @return The tile object at position 'x,y'.
     */
    public Tile getTile(int x, int y)
    {
        if (!valid(x,y)) return null;
        return tmap[x][y];
    }
    
    /**
     * Draws the tile map to the graphics device pointed to by 'g'.
     * 
     * @param g The graphics device to draw to
     * @param xoff The xoffset to shift the tile map by
     * @param yoff The yoffset to shift the tile map by
     */
    public void draw(Graphics2D g, int xoff, int yoff)
    {
        if (g == null) return;
    
        Image img=null;
        Rectangle rect = (Rectangle)g.getClip();
        int xc,yc;
        
        for (int r=0; r<mapHeight; r++)
        {
            for (int c=0; c<mapWidth; c++)
            {
                img = getTileImage(c, r);
                if (img == null) continue;
                xc = xoff + c*tileWidth;
                yc = yoff + r*tileHeight;
                
                // Only draw the tile if it is on screen, otherwise go back round the loop
                if (xc+tileWidth < 0 || xc >= rect.x + rect.width) continue;
                if (yc+tileHeight < 0 || yc >= rect.y + rect.height) continue;
                g.drawImage(img,xc,yc,null);
            }
        }       
    }
    
    /**
     * Draw a border around the tile map using the given colour and offsets.
     * This may be useful for debugging purposes.
     * 
     * @param g The graphics device to draw to
     * @param xoff The xoffset to shift the tile map by
     * @param yoff The yoffset to shift the tile map by
     * @param colour The colour to draw the border with
     */
    public void drawBorder(Graphics2D g, int xoff, int yoff, Color colour)
    {
        if (g == null) return;
            
        g.setColor(colour);
        g.drawRect(xoff, yoff, getPixelWidth(), getPixelHeight());      
    }
    
}
java graphics
1个回答
0
投票

在你的Game类中创建一个固定间隔更新方法,比如FixedUpdate,还有另一个类,比如FixedUpdater,扩展TimerTask类。这个FixedUpdater只会在一个名为run的方法中调用固定更新方法。回到游戏类,在游戏开始时,将固定更新设置为定期运行。并生成您的第一个滚动背景精灵。使精灵具有与云相同或相似的类。确保左侧和右侧正确匹配,否则 2 个精灵之间的接缝将是清晰的。在FixedUpdate方法中,一旦旧的背景精灵完成滚动,就生成自定义背景精灵。下面是一些应该传达想法的粗略代码,但可能不适用于您的库。

import java.time.*;
public class FixedUpdater extends TimerTasK{
    public void run(){
        Game.FixedUpdate();
    }
}

public class Game{
    double elapsedTime = 0; // seconds
    double fixedUpdateInterval = 1/30; // seconds
    int imgSpeed = -100; // pixels per second 
    int worldWidth = 1000; // pixels
    int worldHeight = 400; // pixels
    int imgWidth = 3000; // pixels
    int imgHeigt = 400; // pixels

    public Game(){
        Timer timer = new Timer();
        timer.schedule(new FixedUpdater(), 0, 1 * 1000);
        // runs the FixedUpdater's run method every 1 seconds
        new Image(worldWidth * 1.1, 0, imgSpeed, 0, Sprite.background);
        // (x, y, xspeed, yspeed, sprite to show);
        /* assumes the 0,0 is top left of screen and the constructors     
        positions are for the top left corner of the image*/
    }

    public void FixedUpdate(){
        elapsedTime += fixedUpdateInterval;
        if(elapsedTime % (imgWidth / imgSpeed) = 0){
            new Image(worldWidth * 1.1, 0, imgSpeed, 0, Sprite.background);
        }
    }   
}

请小心确保图像滚动速度不会太快。在上面的代码中,如果屏幕长度为 1,单个像素的速度为 worldwidth / imgSpeed。在代码中,该值为 10,并且由于使用的单位是秒,因此背景上的单个像素将在 10 秒内穿过用户的屏幕。还要确保在精灵到达屏幕边缘时尊重它,以节省内存和处理能力。

© www.soinside.com 2019 - 2024. All rights reserved.