2013-06-01 42 views
1

我想在XNA(monogame)作爲一個學習練習做一個簡單的側滾動遊戲,但我有一些問題讓我的頭滾動關卡。基本上,我試圖做一個簡單的遊戲,其中關卡從左到右滾動,玩家在跳過障礙物時保持靜止狀態。側滾動背景2d XNA遊戲,急需幫助

我最初採用了舊的平臺遊戲初學者工具包,並將其大部分功能都剝離出來,就像玩家,寶石和敵人一樣。從本質上講,剩下的就是從文本文件加載級別的功能。它循環遍歷文件的每一行,確定存在哪種類型的圖塊,然後爲圖塊繪製新的紋理2D。

我已經按照從右到左進行背景滾動的幾個教程,但我無法讓瓷磚本身滾動。

我想創建一個固定的視圖與其中的球員,然後將世界的其餘部分向左。

我永遠不會粘貼這麼多的源代碼,因爲我懷疑任何人都會費心去查看它(這裏是希望),但這裏是級別類,主類(程序)類(玩家類沒有真正的功能因爲它只是繪製一個子畫面到所選擇的vector2

級別:使用Microsoft.Xna.Framework

using System; 
using System.Collections.Generic; 
using System.IO; 

; 使用Microsoft.Xna.Framework.Graphics; 使用Microsoft.Xna.Framework.Content ;

namespace WP8_Game 

{ public class Level { //級別的物理結構。 私人瓷磚[,]瓷磚; 私人圖層[]圖層; int mLineIndex;

// The layer which entities are drawn on top of. 
    private const int EntityLayer = 2; 

    private Vector2 cameraPosition; 

    // Level content.   
    public ContentManager Content 
    { 
     get { return content; } 
    } 
    ContentManager content; 

    #region Loading 

    public Level(IServiceProvider serviceProvider, Stream fileStream, int lineIndex) 
    { 
     // Create a new content manager to load content used just by this level. 
     content = new ContentManager(serviceProvider, "Content"); 

     mLineIndex = lineIndex; 

     LoadTiles(fileStream); 

     layers = new Layer[3]; 
     layers[0] = new Layer(Content, "Backgrounds/Layer0", 0.2f); 
     layers[1] = new Layer(Content, "Backgrounds/Layer1", 0.5f); 
     layers[2] = new Layer(Content, "Backgrounds/Layer2", 0.8f); 
    } 

    /// <summary> 
    /// Iterates over every tile in the structure file and loads its 
    /// appearance and behavior. This method also validates that the 
    /// file is well-formed with a player start point, exit, etc. 
    /// </summary> 
    /// <param name="fileStream"> 
    /// A stream containing the tile data. 
    /// </param> 
    private void LoadTiles(Stream fileStream) 
    { 
     // Load the level and ensure all of the lines are the same length. 
     int width; 
     List<string> lines = new List<string>(); 
     using (StreamReader reader = new StreamReader(fileStream)) 
     { 
      string line = reader.ReadLine(); 
      width = line.Length; 
      while (line != null) 
      { 
       lines.Add(line); 
       if (line.Length != width) 
        throw new Exception(String.Format("The length of line {0} is different from all preceeding lines.", lines.Count)); 
       line = reader.ReadLine(); 
      } 
     } 

     // Allocate the tile grid. 
     tiles = new Tile[width, lines.Count]; 

     // Loop over every tile position, 
     for (int y = 0; y < Height; ++y) 
     { 
      for (int x = 0; x < Width; ++x) 
      { 
       // to load each tile. 
       char tileType = lines[y][x]; 
       tiles[x, y] = LoadTile(tileType, x, y); 
      } 
     } 
    } 

    /// <summary> 
    /// Width of level measured in tiles. 
    /// </summary> 
    public int Width 
    { 
     get { return tiles.GetLength(0); } 
    } 

    /// <summary> 
    /// Height of the level measured in tiles. 
    /// </summary> 
    public int Height 
    { 
     get { return tiles.GetLength(1); } 
    } 

    /// <summary> 
    /// Loads an individual tile's appearance and behavior. 
    /// </summary> 
    /// <param name="tileType"> 
    /// The character loaded from the structure file which 
    /// indicates what should be loaded. 
    /// </param> 
    /// <param name="x"> 
    /// The X location of this tile in tile space. 
    /// </param> 
    /// <param name="y"> 
    /// The Y location of this tile in tile space. 
    /// </param> 
    /// <returns>The loaded tile.</returns> 
    private Tile LoadTile(char tileType, int x, int y) 
    { 
     switch (tileType) 
     { 
      // Blank space 
      case '.': 
       return new Tile(null, new Vector2(x, y), TileCollision.Passable); 

      // Impassable block 
      case '#': 
       return LoadTile("BlockA0", x, y, TileCollision.Impassable); 

      // Unknown tile type character 
      default: 
       throw new NotSupportedException(String.Format("Unsupported tile type character '{0}' at position {1}, {2}.", tileType, x, y)); 
     } 
    } 

    /// <summary> 
    /// Creates a new tile. The other tile loading methods typically chain to this 
    /// method after performing their special logic. 
    /// </summary> 
    /// <param name="name"> 
    /// Path to a tile texture relative to the Content/Tiles directory. 
    /// </param> 
    /// <param name="collision"> 
    /// The tile collision type for the new tile. 
    /// </param> 
    /// <returns>The new tile.</returns> 
    private Tile LoadTile(string name, int x, int y, TileCollision collision) 
    { 
     return new Tile(Content.Load<Texture2D>("Tiles/" + name), new Vector2(x, y), collision); 
    } 

    /// <summary> 
    /// Unloads the level content. 
    /// </summary> 
    public void Dispose() 
    { 
     Content.Unload(); 
    } 

    #endregion 

    #region Bounds and collision 


    /// <summary> 
    /// Gets the bounding rectangle of a tile in world space. 
    /// </summary>   
    public Rectangle GetBounds(int x, int y) 
    { 
     return new Rectangle(x * Tile.Width, y * Tile.Height, Tile.Width, Tile.Height); 
    } 

    #endregion 


    #region Draw 


    /// <summary> 
    /// Draw everything in the level from background to foreground. 
    /// </summary> 
    public void Draw(GameTime gameTime, SpriteBatch spriteBatch) 
    { 
     spriteBatch.Begin(); 
     for (int i = 0; i <= EntityLayer; ++i) 
      layers[i].Draw(spriteBatch, cameraPosition); 
     spriteBatch.End(); 

     ScrollCamera(spriteBatch.GraphicsDevice.Viewport, gameTime); 
     Matrix cameraTransform = Matrix.CreateTranslation(-cameraPosition.X, 0.0f, 0.0f); 
     spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.LinearClamp, DepthStencilState.Default, RasterizerState.CullCounterClockwise, null, cameraTransform); 
     DrawTiles(spriteBatch); 



     spriteBatch.End(); 

     spriteBatch.Begin(); 
     for (int i = EntityLayer + 1; i < layers.Length; ++i) 
      layers[i].Draw(spriteBatch, cameraPosition); 
     spriteBatch.End();  
    } 


    private void ScrollCamera(Viewport viewport, GameTime gameTime) 
    { 
     //Add to the camera positon, So we can see the origin 
     cameraPosition.X = cameraPosition.X + (viewport.Width/2); 
     cameraPosition.Y = cameraPosition.Y + (viewport.Height/2); 



     //Smoothly move the camera towards the player 
     cameraPosition.X = MathHelper.Lerp(cameraPosition.X, 10, 0.1f); 
     cameraPosition.Y = MathHelper.Lerp(cameraPosition.Y, 10, 0.1f); 
     //Undo the origin because it will be calculated with the Matrix (I know this isnt the best way but its what I had real quick) 
     cameraPosition.X = cameraPosition.X - (viewport.Width/2); 
     cameraPosition.Y = cameraPosition.Y - (viewport.Height/2); 

     //Shake the camera, Use the mouse to scroll or anything like that, add it here (Ex, Earthquakes) 

     //Round it, So it dosent try to draw in between 2 pixels 
     cameraPosition.Y = (float)Math.Round(cameraPosition.Y); 
     cameraPosition.X = (float)Math.Round(cameraPosition.X); 


     //Clamp it off, So it stops scrolling near the edges 
     cameraPosition.X = MathHelper.Clamp(cameraPosition.X, 1f, Width * Tile.Width); 
     cameraPosition.Y = MathHelper.Clamp(cameraPosition.Y, 1f, Height * Tile.Height); 
    } 



    /// <summary> 
    /// Draws each tile in the level. 
    /// </summary> 
    private void DrawTiles(SpriteBatch spriteBatch) 
    { 
     // For each tile position 
     for (int y = 0; y < Height; ++y) 
     { 
      for (int x = 0; x < Width; ++x) 
      { 
       // If there is a visible tile in that position 
       Texture2D texture = tiles[x, y].Texture; 
       if (texture != null) 
       { 
        // Draw it in screen space. 
        Vector2 position = new Vector2(x, y) * Tile.Size; 
        spriteBatch.Draw(texture, position, Color.White); 
       } 
      } 
     } 
    } 
    #endregion 

    #region Update 

    /// <summary> 
    /// Updates all objects in the level 
    /// </summary> 
    public void Update(GameTime gameTime) 
    { 
     // For each tile position 
     for (int y = 0; y < Height; ++y) 
     { 
      for (int x = 0; x < Width; ++x) 
      { 
       // If there is a visible tile in that position 
       Texture2D texture = tiles[x, y].Texture; 
       if (texture != null) 
       { 
        // Draw it in screen space. 
        // Vector2 cameraOffset = new Vector2(10, 0); 
        tiles[x, y].Position = new Vector2(x--, y); 
       } 
      } 
     } 

    } 

    #endregion 

} 
} 

計劃:

using System; 
using System.IO; 

using Microsoft.Xna.Framework; 
using Microsoft.Xna.Framework.Graphics; 

namespace WP8_Game 
{ 
/// <summary> 
/// This is the main type for your game 
/// </summary> 
public class Program : Game 
{ 
    GraphicsDeviceManager graphics; 
    SpriteBatch spriteBatch; 

    private Player player; 

    // Meta-level game state. 
    private int levelIndex = -1; 
    private Level level; 

    // The number of levels in the Levels directory of our content. We assume that 
    // levels in our content are 0-based and that all numbers under this constant 
    // have a level file present. This allows us to not need to check for the file 
    // or handle exceptions, both of which can add unnecessary time to level loading. 
    private const int numberOfLevels = 3; 

    public Program() 
    { 
     graphics = new GraphicsDeviceManager(this); 
     Content.RootDirectory = "Content"; 

     player = new Player(); 
    } 

    /// <summary> 
    /// Allows the game to perform any initialization it needs to before starting to run. 
    /// This is where it can query for any required services and load any non-graphic 
    /// related content. Calling base.Initialize will enumerate through any components 
    /// and initialize them as well. 
    /// </summary> 
    protected override void Initialize() 
    { 
     // TODO: Add your initialization logic here 

     base.Initialize(); 
    } 

    /// <summary> 
    /// LoadContent will be called once per game and is the place to load 
    /// all of your content. 
    /// </summary> 
    protected override void LoadContent() 
    { 
     // Create a new SpriteBatch, which can be used to draw textures. 
     spriteBatch = new SpriteBatch(GraphicsDevice); 

     // Load the player resources 
     Vector2 playerPosition = new Vector2(100, 100); 
     player.Initialize(Content.Load<Texture2D>("Sprites/Player/player"), playerPosition); 


     //Load the next level 
     LoadNextLevel(); 

    } 

    private void LoadNextLevel() 
    { 
     // move to the next level 
     levelIndex = (levelIndex + 1) % numberOfLevels; 

     // Unloads the content for the current level before loading the next one. 
     if (level != null) 
      level.Dispose(); 

     // Load the level. 
     string levelPath = string.Format("Content/Levels/{0}.txt", levelIndex); 
     using (Stream fileStream = TitleContainer.OpenStream(levelPath)) 
      level = new Level(Services, fileStream, levelIndex); 
    } 

    private void ReloadCurrentLevel() 
    { 
     --levelIndex; 
     LoadNextLevel(); 
    } 

    /// <summary> 
    /// UnloadContent will be called once per game and is the place to unload 
    /// all content. 
    /// </summary> 
    protected override void UnloadContent() 
    { 
     // TODO: Unload any non ContentManager content here 
    } 

    /// <summary> 
    /// Allows the game to run logic such as updating the world, 
    /// checking for collisions, gathering input, and playing audio. 
    /// </summary> 
    /// <param name="gameTime">Provides a snapshot of timing values.</param> 
    protected override void Update(GameTime gameTime) 
    { 

     //camera.Update(gameTime, player); 

     base.Update(gameTime); 
    } 

    /// <summary> 
    /// This is called when the game should draw itself. 
    /// </summary> 
    /// <param name="gameTime">Provides a snapshot of timing values.</param> 
    protected override void Draw(GameTime gameTime) 
    { 
     GraphicsDevice.Clear(Color.Black); 

     // Start drawing 
     spriteBatch.Begin(); 


     // Draw the Player 
     player.Draw(spriteBatch); 


     //Draw the level 
     level.Draw(gameTime, spriteBatch); 

     // Stop drawing 
     spriteBatch.End(); 

     base.Draw(gameTime); 
    } 
} 
} 

任何意見,將不勝感激,我很失落,我不知道從哪裏開始。

+0

這就是所謂的視差滾動,一個例子可以在這裏找到:http://www.david-gouveia.com/2d-camera-with-parallax-scrolling-in-xna/ – Deukalion

回答

1

您需要抵消平鋪在那裏繪製,就像這樣:

Vector2 position = new Vector2(x, y) * Tile.Size;

變成

Vector2 position = new Vector2(x * Tile.Size + cameraOffset.X, y * Tile.Size + cameraOffset.Y);

這是一個編輯您DrawTiles方法。

+0

嗨邁克,感謝您的意見,但我仍然有點困惑。如果我在繪製時將它平移,它只是最初移動它,但在此之後它保持不變。我是否需要將它加入Level中的更新方法? 我已經嘗試過實現這個(編輯的問題來顯示),但有一點歡樂/效果。 –

+0

你需要重繪任何時候你想看到一個變化,所以你應該做的是改變偏移量(基於用戶輸入,時間等),然後重畫場景。 –

0

使用相機移動場景的更典型方法是使用變換矩陣。我要做的就是創建具有各種性能的一個相機類別(位置,旋轉,縮放,產地)和對方法做出改變這樣的:

public Matrix GetTransform() 
    { 
     var translationMatrix = Matrix.CreateTranslation(new Vector3(-Position.X, -Position.Y, 0)); 
     var rotationMatrix = Matrix.CreateRotationZ(Rotation); 
     var scaleMatrix = Matrix.CreateScale(new Vector3(Zoom, Zoom, 1)); 
     var originMatrix = Matrix.CreateTranslation(new Vector3(Origin.X, Origin.Y, 0)); 

     return translationMatrix * rotationMatrix * scaleMatrix * originMatrix; 
    } 

然後當你畫你的精靈批,你可以簡單地傳遞矩陣到像這樣的最後一個參數:

_spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, null, null, null, _camera.GetTransform()); 

當你做這種方式,你不需要任何對衝,所以你必須拿出所有的相機偏移碼你已經有了。只需按照它們的方式繪製事物,並在繪製時改變整個視圖。

最後需要注意的是,如果您使用的是鼠標或觸摸座標,則還需要反轉輸入。