簡體   English   中英

2D XNA游戲的側面滾動背景,迫切需要幫助

[英]Side Scrolling Background for 2d XNA game, Help desperately needed

我正在嘗試使用XNA(monogame)制作一個簡單的側滾動游戲,作為學習練習,但是我在滾動關卡時遇到了一些問題。 基本上,我正在嘗試制作一個簡單的游戲,其中一個關卡從左到右滾動,而玩家在跳過障礙物時保持靜止。

最初,我使用了舊的Platformer入門套件,並剝離了大部分功能,例如播放器,寶石和敵人。 本質上,剩下的就是從文本文件加載關卡的功能。 它遍歷文件的每一行,確定存在哪種類型的圖塊,然后為圖塊繪制新的Texture 2D。

我遵循了一些有關使背景從右向左滾動的教程,但是我無法讓磁貼本身滾動。

我想創建一個包含播放器的固定視圖,然后將世界其他地方向左移動。

我通常不會粘貼太多的源代碼,因為我懷疑有人會不願意去看它(這里就是希望),但是這里是關卡類,主(程序)類(玩家類沒有真正的功能,因為它只是畫圖而已)精靈到選定的向量2。

水平:

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

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

namespace WP8_Game

{public class Level {//級別的物理結構。 私人Tile [,] tile; 私有Layer []層; 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);
    }
}
}

任何建議將不勝感激,我很迷茫,不確定從哪里開始。

您需要偏移繪制圖塊的位置,如下所示:

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

變成

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

這是對DrawTiles方法的編輯。

使用攝像機移動場景的更典型方法是使用變換矩陣。 我要做的是創建一個具有各種屬性(位置,旋轉,縮放,原點)的Camera類,並具有進行這種轉換的方法:

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

當您以這種方式執行此操作時,您無需偏移任何內容,因此您需要取出所有已經獲得的相機偏移代碼。 只需按其原樣繪制事物,並在繪制時變換整個視圖。

最后要注意的一點是,如果您使用鼠標或觸摸坐標,則還需要反向轉換輸入。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM