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,今天主要是实现了利用摄像机原理来更新地图,同时实现了多层地图。

 

转载于:https://www.cnblogs.com/zcftech/archive/2013/04/05/2999531.html

以下是对提供的参考资料的总结,按照要求结构化多个要点分条输出: 4G/5G无线网络优化与网规案例分析: NSA站点下终端掉4G问题:部分用户反馈NSA终端频繁掉4G,主要因终端主动发起SCGfail导致。分析显示,在信号较好的环境下,终端可能因节能、过热保护等原因主动释放连接。解决方案建议终端侧进行分析处理,尝试关闭节电开关等。 RSSI算法识别天馈遮挡:通过计算RSSI平均值及差值识别天馈遮挡,差值大于3dB则认定有遮挡。不同设备分组规则不同,如64T和32T。此方法可有效帮助现场人员识别因环境变化引起的网络问题。 5G 160M组网小区CA不生效:某5G站点开启100M+60M CA功能后,测试发现UE无法正常使用CA功能。问题原因在于CA频点集标识配置错误,修正后测试正常。 5G网络优化与策略: CCE映射方式优化:针对诺基亚站点覆盖农村区域,通过优化CCE资源映射方式(交织、非交织),提升RRC连接建立成功率和无线接通率。非交织方式相比交织方式有显著提升。 5G AAU两扇区组网:与三扇区组网相比,AAU两扇区组网在RSRP、SINR、下载速率和上传速率上表现不同,需根据具体场景选择适合的组网方式。 5G语音解决方案:包括沿用4G语音解决方案、EPS Fallback方案和VoNR方案。不同方案适用于不同的5G组网策略,如NSA和SA,并影响语音连续性和网络覆盖。 4G网络优化与资源利用: 4G室分设备利旧:面对4G网络投资压减与资源需求矛盾,提出利旧多维度调优策略,包括资源整合、统筹调配既有资源,以满足新增需求和提质增效。 宏站RRU设备1托N射灯:针对5G深度覆盖需求,研究使用宏站AAU结合1托N射灯方案,快速便捷地开通5G站点,提升深度覆盖能力。 基站与流程管理: 爱立信LTE基站邻区添加流程:未提供具体内容,但通常涉及邻区规划、参数配置、测试验证等步骤,以确保基站间顺畅切换和覆盖连续性。 网络规划与策略: 新高铁跨海大桥覆盖方案试点:虽未提供详细内容,但可推测涉及高铁跨海大桥区域的4G/5G网络覆盖规划,需考虑信号穿透、移动性管理、网络容量等因素。 总结: 提供的参考资料涵盖了4G/5G无线网络优化、网规案例分析、网络优化策略、资源利用、基站管理等多个方面。 通过具体案例分析,展示了无线网络优化中的常见问题及解决方案,如NSA终端掉4G、RSSI识别天馈遮挡、CA不生效等。 强调了5G网络优化与策略的重要性,包括CCE映射方式优化、5G语音解决方案、AAU扇区组网选择等。 提出了4G网络优化与资源利用的策略,如室分设备利旧、宏站RRU设备1托N射灯等。 基站与流程管理方面,提到了爱立信LTE基站邻区添加流程,但未给出具体细节。 新高铁跨海大桥覆盖方案试点展示了特殊场景下的网络规划需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值