侧面滚动背景2d XNA游戏,帮助迫切需要



我试图在XNA (monogame)中制作一款简单的横向滚动游戏作为学习练习,但我在滚动关卡时遇到了一些问题。基本上,我尝试着创造一款简单的游戏,即关卡从左向右滚动,玩家除了在跳跃障碍时保持静止。

我最初采用了旧的平台游戏入门工具包,并剥离了其大部分功能,如玩家,宝石和敌人。基本上,剩下的就是从文本文件加载关卡的功能。它循环遍历文件的每一行,确定存在什么类型的贴图,然后为贴图绘制一个新的Texture 2D。

我已经遵循了一些教程关于使背景从右向左滚动,但我不能让瓷砖自己滚动。

我想创建一个固定的视图,其中有玩家,然后将世界的其他部分移到左边。

我通常不会粘贴这么多源代码,因为我怀疑有人会费心去看它(希望如此),但这里是关卡类,主(程序)类(玩家类没有真正的功能,因为它只是将精灵绘制到选定的vector2)。

水平:

using System;
using System.Collections.Generic;
using System.IO;
使用Microsoft.Xna.Framework

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

namespace WP8_Game

{公共类{//关卡的物理结构。private Tile[,] tiles;private Layer[] layers;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);
    }
}
}

任何建议将不胜感激,我是如此迷失,不知道从哪里开始

您需要在tile绘制的位置进行偏移,如下所示:

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

当你这样做时,你不需要偏移任何东西,所以你需要拿出你已经得到的所有相机偏移代码。按它们本来的样子画,在绘制时变换整个视图。

最后要注意的是,如果你使用鼠标或触摸坐标,你还需要反向转换输入

最新更新