继续上一节,今天主要是要完成游戏中地图的绘制;这里将会用到tiling方法,通过拼图的方式绘制整幅地图。任务如下:搭建有关Tile engine的整体架构
第一步就是把我们游戏中要用到的Tile下载下来,并添加到EyesOfTheDragonContent中去,下载地址: http://xnagpa.net/xna4/downloads/tilesets.zip
我们这里使用的Tile引擎是最简单的一种,是分层拼图引擎,很好理解,将地图上的精灵分层绘制,不同层次有不同的精灵元素。在XRpgLibrary下添加一个新的文件夹TileEngine,添加Engine类如下
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Xna.Framework; namespace XRpgLibrary.TileEngine { public class Engine { #region Field Region static int tileWidth;//屏幕宽度方向上tile的个数
static int tileHeight;//屏幕高度方向上tile的个数 #endregion #region Property Region//这两个属性的对外接口 public static int TileWidth { get { return tileWidth; } } public static int TileHeight { get { return tileHeight; } } #endregion #region Constructors public Engine(int tileWidth, int tileHeight) { Engine.tileWidth = tileWidth; Engine.tileHeight = tileHeight; } #endregion #region Methods//获取一个向量在tile矩阵中的位置 public static Point VectorToCell(Vector2 position) { return new Point((int)position.X / tileWidth, (int)position.Y / tileHeight); } #endregion } }
地图上的tile当然不是一个,我们要构建一个tileset类来对一个tile集合进行管理
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace XRpgLibrary.TileEngine { public class Tileset { #region Fields and PropertiesTexture2D image; int tileWidthInPixels;//图片中一个tile的宽度(以像素作为计量) int tileHeightInPixels; int tilesWide;//图片宽度方向的tile个数 int tilesHigh; Rectangle[] sourceRectangles;//图片中每个tile对应矩形的集合数组 #endregion #region Property Region//对外接口 public Texture2D Texture { get { return image; } private set { image = value; } } public int TileWidth { get { return tileWidthInPixels; } private set { tileWidthInPixels = value; } } public int TileHeight { get { return tileHeightInPixels; } private set { tileHeightInPixels = value; } } public int TilesWide { get { return tilesWide; } private set { tilesWide = value; } } public int TilesHigh { get { return tilesHigh; } private set { tilesHigh = value; } } public Rectangle[] SourceRectangles { get { return (Rectangle[])sourceRectangles.Clone(); } } #endregion #region Constructor Region public Tileset(Texture2D image, int tilesWide, int tilesHigh, int tileWidth, int tileHeight) { Texture = image; TileWidth = tileWidth; TileHeight = tileHeight; TilesWide = tilesWide; TilesHigh = tilesHigh; int tiles = tilesWide * tilesHigh; sourceRectangles = new Rectangle[tiles]; int tile = 0;
//初始化图片中tile所对应的矩形区域 for (int y = 0; y < tilesHigh; y++) for (int x = 0; x < tilesWide; x++) { sourceRectangles[tile] = new Rectangle( x * tileWidth, y * tileHeight, tileWidth, tileHeight); tile++; } } #endregion #region Method Region #endregion } }
以上代码可以看出,对于tile对象的管理是通过其在tileset中的索引来实现的,为了更好的管理tile,同时也为实现多层的map,构建以tile类来对其信息进行管理
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace XRpgLibrary.TileEngine{ public class Tile { #region Field Region int tileIndex;//tile在当前集合中的索引 int tileset;//tile所在集合的索引 #endregion #region Property Region public int TileIndex { get { return tileIndex; } private set { tileIndex = value; } } public int Tileset { get { return tileset; } private set { tileset = value; } } #endregion #region Constructor Region public Tile(int tileIndex, int tileset) { TileIndex = tileIndex; Tileset = tileset; } #endregion } }
下面构建一个地图层级类,来管理每一层地图的绘制,代码如下
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace XRpgLibrary.TileEngine { public class MapLayer { #region Field Region Tile[,] map;//Tile类型的数组,用于存储一层中所有Tile对象 #endregion #region Property Region public int Width { get { return map.GetLength(1); }//获得地图的宽 } public int Height { get { return map.GetLength(0); }//获得地图的高 } #endregion #region Constructor Region//用tile数组对象来初始化地图层对象 public MapLayer(Tile[,] map) { this.map = (Tile[,])map.Clone(); }
//用宽,高来初始化一层地图对象 public MapLayer(int width, int height) { map = new Tile[height, width]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { map[y, x] = new Tile(0, 0);//地图中的每个对象都初始化为第0个tileset集合中的第0个tile } } } #endregion #region Method Region//对外接口 public Tile GetTile(int x, int y) { return map[y, x]; } public void SetTile(int x, int y, Tile tile) { map[y, x] = tile; } public void SetTile(int x, int y, int tileIndex, int tileset) { map[y, x] = new Tile(tileIndex, tileset); } #endregion } }
紧接着就是构造TileMap类,这也是整个地图机制中的核心类,绘制地图的主要逻辑实现都在该类中,先看代码吧
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; namespace XRpgLibrary.TileEngine { public class TileMap { #region Field Region List<Tileset> tilesets;//tile集合队列 List<MapLayer> mapLayers;//地图层级队列 #endregion #region Property Region #endregion #region Constructor Region//构造函数 public TileMap(List<Tileset> tilesets, List<MapLayer> layers) { this.tilesets = tilesets; this.mapLayers = layers; }public TileMap(Tileset tileset, MapLayer layer) { tilesets = new List<Tileset>(); tilesets.Add(tileset); mapLayers = new List<MapLayer>(); mapLayers.Add(layer); } #endregion #region Method Region
//地图绘制 public void Draw(SpriteBatch spriteBatch) {
//根据Engine对象初始化一个绘制矩形区域 Rectangle destination = new Rectangle(0, 0, Engine.TileWidth, Engine.TileHeight); Tile tile;
//最外层的是地图层级的遍历 foreach (MapLayer layer in mapLayers) {
//对该层地图上的每个tile进行遍历,绘制 for (int y = 0; y < layer.Height; y++) { destination.Y = y * Engine.TileHeight; for (int x = 0; x < layer.Width; x++) { tile = layer.GetTile(x, y); destination.X = x * Engine.TileWidth; spriteBatch.Draw( tilesets[tile.Tileset].Texture, destination, tilesets[tile.Tileset].SourceRectangles[tile.TileIndex], Color.White); } } } } #endregion } }
最后一步就是要将地图的绘制到GamePlayScreen页面上,该页面代码更改如下
#region Field Region Engine engine = new Engine(32, 32); Tileset tileset; TileMap map; #endregion
先添加几个类级变量,接下来要在LoadContent方法中加载绘制地图要用的相关对象
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); base.LoadContent(); }
最后是绘制地图
public override void Draw(GameTime gameTime) { GameRef.SpriteBatch.Begin( SpriteSortMode.Immediate, BlendState.AlphaBlend, SamplerState.PointClamp, null, null, null, Matrix.Identity); map.Draw(GameRef.SpriteBatch); base.Draw(gameTime); GameRef.SpriteBatch.End(); }