zoukankan      html  css  js  c++  java
  • XNA之RPG游戏开发教程之五

    上一节我们说到游戏中的地图绘制,我们在TileMap类中为GameplayScreen绘制地图,但要模拟真实的游戏地图,需要做到地图的滚动,以前我们经常会用现成的图片,利用图片的滚动模拟背景移动。现在要实现游戏地图的个性化,要用Tile绘制地图,就需要一个2D的Camera类,站在player的Position上会有一个视野范围,就在这个视野范围内绘制地图,用这种方法来实现动态地图的模拟。接下来建立一个Camera类如下

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;
    namespace XRpgLibrary.TileEngine
    {
    public class Camera
     {
     #region Field Region
    Vector2 position;//Camera位置
    float speed;//Camera的移动速度
    float zoom;//Camera的放大缩小倍数
    Rectangle viewportRectangle;
     #endregion
     #region Property Region
    public Vector2 Position
     {
    get { return position; }
    private set { position = value; }
     }
    public float Speed
     {
    get { return speed; }
    set
     {
     speed = (float)MathHelper.Clamp(speed, 1f, 16f);//该函数的作用是将speed值限定在1和16之间,大于16,返回16,小于1,返回1,在两者之间返回true
     }
     }
    public float Zoom
     {get { return zoom; }
     }
     #endregion
     #region Constructor Region
    public Camera(Rectangle viewportRect)//用视野矩阵来初始化相机
     {
     speed = 4f;
     zoom = 1f;
     viewportRectangle = viewportRect;
     }
    public Camera(Rectangle viewportRect, Vector2 position)
     {
     speed = 4f;
     zoom = 1f;
     viewportRectangle = viewportRect;
     Position = position;
     }
     #endregion
     #region Method Region//根据键盘控制移动,实际上将Camera与Player联系在一起
    public void Update(GameTime gameTime)
     {
    if (InputHandler.KeyDown(Keys.Left))
     position.X -= speed;
    else if (InputHandler.KeyDown(Keys.Right))
     position.X += speed;
    if (InputHandler.KeyDown(Keys.Up))
     position.Y -= speed;
    else if (InputHandler.KeyDown(Keys.Down))
     position.Y += speed;
     }
     #endregion
     }
    }

    接下来就是为游戏中添加玩家player类

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.Xna.Framework;
    using Microsoft.Xna.Framework.Graphics;
    using Microsoft.Xna.Framework.Input;
    using XRpgLibrary;
    using XRpgLibrary.TileEngine;
    namespace EyesOfTheDragon.Components
    {
    public class Player
     {
     #region Field Region
    Camera camera;//Camera对象,将其与玩家联系起来
    Game1 gameRef;//Game对象,将其与玩家联系起来
     #endregion
     #region Property Region
    public Camera Camera
     {
    get { return camera; }
    set { camera = value; }
     }
     #endregion
     #region Constructor Region在构建玩家的同时构建Camera
    public Player(Game game)
     { 
    gameRef
    = (Game1)game; camera = new Camera(gameRef.ScreenRectangle); } #endregion #region Method Region public void Update(GameTime gameTime) { camera.Update(gameTime); } public void Draw(GameTime gameTime, SpriteBatch spriteBatch) { } #endregion } }

    接着就是将Player对象添加到GamePlayScreen中去,这样就会为游戏页面实例化一个玩家对象,同时实例化一个Camera对象来实现地图的滚动

    using EyesOfTheDragon.Components;//添加空间引用
    #region Field Region
    Engine engine = new Engine(32, 32);
    Tileset tileset; 
    TileMap map;
    Player player;//定义对象
    #endregion
    #region Constructor Region
    public GamePlayScreen(Game game, GameStateManager manager)
     : base(game, manager)
    {
     player = new Player(game);//构造函数中实例化对象
    }
    #endregion

    在游戏页面的更新中也要更新玩家

    public override void Update(GameTime gameTime)
    {
     player.Update(gameTime);
     base.Update(gameTime);
    }

    接下来要更改的就是TileMap类,上节说过,该类是地图绘制的核心类,地图的绘制在Draw方法中实现,上节中是通过不断更改矩形变量destination的X,Y坐标来绘制每个tile,现在要用Camera来更新地图,所以要将Camera作为参数传入Draw方法中,由其来决定绘制tile的X,Y坐标,更新如下

    public void Draw(SpriteBatch spriteBatch, Camera camera)
    {
    Rectangle destination = new Rectangle(0, 0, Engine.TileWidth, Engine.TileHeight);
    Tile tile;
    foreach (MapLayer layer in mapLayers)
     {
    for (int y = 0; y < layer.Height; y++)
     {
      destination.Y = y * Engine.TileHeight - (int)camera.Position.Y;//获得绘制tile的Y坐标
    for (int x = 0; x < layer.Width; x++)
     {
     tile = layer.GetTile(x, y);
    if (tile.TileIndex == -1 || tile.Tileset == -1)
    continue;
     destination.X = x * Engine.TileWidth - (int)camera.Position.X;//获得绘制tile的X坐标
     spriteBatch.Draw(
     tilesets[tile.Tileset].Texture,
     destination,
     tilesets[tile.Tileset].SourceRectangles[tile.TileIndex],
    Color.White);
     }
     }
     }
    }

    这里关于游戏中摄像机的原理,自己解释不太清楚,可以从http://blog.csdn.net/beyondma/article/details/6766914这里获得相关的理论知识

    到这里我们运行程序,移动Camera绘制地图,会发现有绘出地图的现象,即Camera移出了窗体边界。这就需要根据地图的大小来控制Camera的移动区域,需要在TileMap类中添加地图宽度,高度的对外属性,在Camera类中根据这些属性来控制Camera的移动区域,地图的左边和上边最好控制,只要保证Camera的Position的X,Y坐标都是非负就能保证其没有越过左边界和上边界,至于下边界和右边界就用地图的宽度和高度来控制

    TileMap类中的更新代码如下

    #region Field Region
    List<Tileset> tilesets;
    List<MapLayer> mapLayers;
    static int mapWidth;//地图的宽度(tile的个数)
    static int mapHeight;//地图的高度
    #endregion
    #region Property Region
    public static int WidthInPixels返回地图宽度方向的像素值
    {
      get { return mapWidth * Engine.TileWidth; }
    }
    public static int HeightInPixels
    {
      get { return mapHeight * Engine.TileHeight; }
    }
    #endregion
    #region Constructor Region
    public TileMap(List<Tileset> tilesets, List<MapLayer> layers)
    {
     this.tilesets = tilesets;
     this.mapLayers = layers;
     mapWidth = mapLayers[0].Width;//以游戏最底层的MapLayer的高度,宽度来初始化
     mapHeight = mapLayers[0].Height;
    for (int i = 1; i < layers.Count; i++)
     {
    //检测到各个层之间的宽度,高度不一致,抛出异常
    if (mapWidth != mapLayers[i].Width || mapHeight != mapLayers[i].Height) throw new Exception("Map layer size exception"); } } public TileMap(Tileset tileset, MapLayer layer) { tilesets = new List<Tileset>(); tilesets.Add(tileset); mapLayers = new List<MapLayer>();
    mapLayers.Add(layer); mapWidth
    = mapLayers[0].Width;//初始化 mapHeight = mapLayers[0].Height; } #endregion

    Camera类中的更新如下

    public void Update(GameTime gameTime)
    {
    Vector2 motion = Vector2.Zero;//定义一个向量对象,用来确定Camera的移动方向
    if (InputHandler.KeyDown(Keys.Left))
     motion.X = -speed;
    else if (InputHandler.KeyDown(Keys.Right))
     motion.X = speed;
    if (InputHandler.KeyDown(Keys.Up))
     motion.Y = -speed;
    else if (InputHandler.KeyDown(Keys.Down))
     motion.Y = speed;
    if (motion != Vector2.Zero)
     motion.Normalize();//对向量进行标准化,然后再乘以Speed才能真的模拟玩家的移动
     position += motion * speed;
     LockCamera();//对Camera的位置进行设定
    }
    private void LockCamera()
    {
     position.X = MathHelper.Clamp(position.X,
     0,
    TileMap.WidthInPixels - viewportRectangle.Width);//将Camera的X坐标限定在0和(地图宽度-Camera视野宽度)之间,特别注意要减去Camera视野宽度,这样就不会出现移出游戏窗体的现象
     position.Y = MathHelper.Clamp(position.Y,
     0,
    TileMap.HeightInPixels - viewportRectangle.Height);
    }
    #endregion

    最后我们要实现多层地图的绘制,首先要在TileMap类中添加一个AddLayer方法来添加一层地图

    public void AddLayer(MapLayer layer)
    {
    //首先判断每层的宽度,高度是否相同
    if (layer.Width != mapWidth && layer.Height != mapHeight) throw new Exception("Map layer size exception"); mapLayers.Add(layer); }

    接着就是在GamePlayScreen类中添加这一层地图

    protected override void LoadContent()
    { 
     Texture2D tilesetTexture = Game.Content.Load<Texture2D>(@"Tilesets\tileset1");
     tileset = new Tileset(tilesetTexture, 8, 8, 32, 32);
     MapLayer layer = new MapLayer(40, 40);
    for (int y = 0; y < layer.Height; y++)
     {
    for (int x = 0; x < layer.Width; x++)
     {
      Tile tile = new Tile(0, 0);
      layer.SetTile(x, y, tile);
     }
    } map
    = new TileMap(tileset, layer); MapLayer splatter = new MapLayer(40, 40); Random random = new Random();
    //在40*40的图层上添加80个tile对象
    for (int i = 0; i < 80; i++) { int x = random.Next(0, 40); int y = random.Next(0, 40);
    //在2到14这些图片中随机选择一个
    int index = random.Next(2, 14); Tile tile = new Tile(index, 0);//利用tile索引来初始化tile对象 splatter.SetTile(x, y, tile);//往新的图层上添加tile对象 } map.AddLayer(splatter); base.LoadContent(); }

    接着我们想在游戏中添加一些除了草地,植物以为的城市tile,这就需要在GameplayScreen类中新添加一个tileset对象,将从 http://xnagpa.net/xna4/downloads/tilesets2.zip获得的tilesets2.png添加到Content中,更新GamePlayScreen的LoadContent()方法如下

    protected override void LoadContent()
    {
    base.LoadContent();
      Texture2D tilesetTexture = Game.Content.Load<Texture2D>(@"Tilesets\tileset1");
     Tileset tileset1 = new Tileset(tilesetTexture, 8, 8, 32, 32);
     tilesetTexture = Game.Content.Load<Texture2D>(@"Tilesets\tileset2");
     Tileset tileset2 = new Tileset(tilesetTexture, 8, 8, 32, 32);//新建立的tileset对象
     List<Tileset> tilesets = new List<Tileset>();
     tilesets.Add(tileset1);
     tilesets.Add(tileset2);
    MapLayer layer = new MapLayer(40, 40);
    for (int y = 0; y < layer.Height; y++) { for (int x = 0; x < layer.Width; x++) { Tile tile = new Tile(0, 0); layer.SetTile(x, y, tile); } } MapLayer splatter = new MapLayer(40, 40); Random random = new Random(); for (int i = 0; i < 80; i++) { int x = random.Next(0, 40); int y = random.Next(0, 40); int index = random.Next(2, 14); Tile tile = new Tile(index, 0);//这里的0代表的是第一个tileset集合 splatter.SetTile(x, y, tile); }
    //在第二个tileset集合中选择对象设定固定位置的tile splatter.SetTile(
    1, 0, new Tile(0, 1)); splatter.SetTile(2, 0, new Tile(2, 1)); splatter.SetTile(3, 0, new Tile(0, 1)); List<MapLayer> mapLayers = new List<MapLayer>(); mapLayers.Add(layer); mapLayers.Add(splatter); map = new TileMap(tilesets, mapLayers); }
    public override void Draw(GameTime gameTime)
    {
     GameRef.SpriteBatch.Begin(
     SpriteSortMode.Immediate,
     BlendState.AlphaBlend,
     SamplerState.PointClamp,
     null,
     null,
     null,
     Matrix.Identity);
     map.Draw(GameRef.SpriteBatch, player.Camera);//绘制地图
    base.Draw(gameTime); GameRef.SpriteBatch.End(); }

    OK,今天主要是实现了利用摄像机原理来更新地图,同时实现了多层地图。

  • 相关阅读:
    JavaScript模态对话框类
    事件模块的演变(1)
    html5中可通过document.head获取head元素
    How to search for just a specific file type in Visual Studio code?
    What do 'lazy' and 'greedy' mean in the context of regular expressions?
    正则非获取匹配 Lookahead and Lookbehind ZeroLength Assertions
    regex length 正则长度问题
    Inversion of Control vs Dependency Injection
    How to return View with QueryString in ASP.NET MVC 2?
    今天才发现Google Reader
  • 原文地址:https://www.cnblogs.com/zcftech/p/2999531.html
Copyright © 2011-2022 走看看