C#服务端的微信小游戏——多人在线角色扮演(八)
动态加载地图虽然会消耗一定的系统资源,但是由此可以实现近似无限地图的游戏空间,让游戏乐趣达到人生的巅峰。
——茂叔
小狗小猫已经可以蹦蹦跳跳了,但是为了让它们跑得更远,我们需要把地图的空间尽量放大。当然,因为内存永远是有限的,所以要在有限的地图里面实现更大的空间,我们需要卸载那些没有玩家的空间,让系统资源为更多玩家服务。这就是为什么要把地图分成一块一块的原因。
每一块地图的大小为5个单位,即X,Y的取值范围是0-4。
我们定义两个全局变量来描述地图块的大小,这样如果将来想要变更起来也比较方便。
public static byte MapSize = 5;
这样一来,GameCharacter
的Move()
可以修改为:
public bool Move(Direction dir)
{
switch (dir)
{
case Direction.NONE:
break;
case Direction.EAST:
if ((X + 1) == G.MapSize)
return false;
else
X++;
break;
case Direction.WEST:
if ((X - 1) < 0)
return false;
else
X--;
break;
case Direction.NORTH:
if ((Y - 1) < 0)
return false;
else
Y--;
break;
case Direction.SOUTH:
if ((Y + 1) == G.MapSize)
return false;
else
Y++;
break;
default:
break;
}
return true;
}
}
给Move()
一个返回值,这样就可以判断移动是否成功了。
通过这个修改,我们把地图的范围给确定出来了。但是我们还需要去判断这个方向上是不是有别的地图,对不对……还记得GameMap
类有四个成员用来记录各个方向的地图的么?
public ExistenceID NorthMap = ExistenceID.Null;
public ExistenceID SouthMap = ExistenceID.Null;
public ExistenceID WestMap = ExistenceID.Null;
public ExistenceID EastMap = ExistenceID.Null;
该它们上场了。
最简单的方法,就是在GameCharacter
里面增加一个方法,让它从所在地图的gObjects
系统到目标地图的gObjects
里去,再调整一下在目标地图里的坐标就可以了。
但是……
这样做,就势必需要GameCharacter
去访问GameMap
。我们知道,GameMap
其实是GameCharacter
的容器,如果让容器的内容直接操作容器本身,这既不符合软件基本逻辑,更有安全上的风险,如果多个内容都在访问自己的容器,很难保证容器的强度经得起这样折腾。所以,我们只知道有List.Add()
,很少有哪个对象提供Object.AddToList()
这样的方法的。
在这里,我们使用Event
机制来实现这个功能。
在GameCharacter
需要跨越地图的时候,我们发起一个Event
。让GameWorld
来处理,从而实现地图的跨越。
首先,我们在Type.cs
里定义一个事件代理类型:
delegate void CrossMapHandler(GameObject OBJ,Direction dir);
然后,我们在GameMap
增加一个该类型的代理,这样就可以在Refresh
生成内容的时候传递给他们。注意,只有GameCharacter
及其派生类可以移动,因此需要在添加事件处理程序时要先判断类型。
public CrossMapHandler ObjectCrossMapMethod;
……
public void Refresh()//刷新
{
LOG("重新生成地图(" + EID + ")内容");
lock (this)
{
gObjects.Clear();
foreach (MapItem item in MapItems)
{
GameObject obj = ObjectFactory.Make(item.ClassID, LOG);
if (obj != null)
{
obj.MapEID = EID;
obj.X = item.X;
obj.Y = item.Y;
if (obj.Category == ObjectCategory.ANIMAL || obj.Category == ObjectCategory.CHARACTER)
{
((GameCharacter)obj).CrossMap += new CrossMapHandler(ObjectCrossMapMethod);
}
gObjects.Add(obj);
LOG("加入 " + obj.Name + " 成功");
}
}
}
LastRefreshTime = DateTime.Now;
}
这样,我们就可以为GameCharacter
添加一个处理跨地图事件,并在Move()
发起这个事件:
public event CrossMapHandler CrossMap;
……
public bool Move(Direction dir)
{
bool ret = false;
switch (dir)
{
case Direction.NONE:
ret = true;
break;
case Direction.EAST:
if (X < (G.MapSize - 1))
{
X++;
ret = true;
}
else
{
CrossMap?.Invoke(this,dir);//事件处理方法不为空则调用
}
break;
case Direction.WEST:
if (X > 0)
{
X--;
ret = true;
}
else
{
CrossMap?.Invoke(this,dir);
}
break;
case Direction.NORTH:
if (Y > 0)
{
Y--;
ret = true;
}
else
{
CrossMap?.Invoke(this,dir);
}
break;
case Direction.SOUTH:
if (Y < (G.MapSize - 1))
{
Y++;
ret = true;
}
else
{
CrossMap?.Invoke(this,dir);
}
break;
default:
break;
}
return ret;
}
最后,在GameWorld
的来处理这个事件,并把这个处理方法传递给每个地图的ObjectCrossMapMethod
,顺便在GameWorld
创建地图的地方把地图名字加上,然后在多建立两个地图,记得把地图之间的联系设置好,所有这些一定要在调用Refresh
之前设置好:
public GameWorld(Action<string> LOGFUN = null) : base(LOGFUN)
{
gMaps = new List<GameMap>();
LOG("GameWorld(" + EID + ")创建成功");
GameMap Map = new GameMap(LOG);
Map.Name = "中央地图";
Map.MapItems.Add(new MapItem()
{
ClassID = ObjectClassID.DOG,
X = 1,
Y = 1
}
);
Map.MapItems.Add(new MapItem()
{
ClassID = ObjectClassID.CAT,
X = 1,
Y = 2
}
);
Map.ObjectCrossMapMethod = ObjectCrossToMap;
Map.Refresh();
gMaps.Add(Map);
GameMap MapEast = new GameMap(LOG);
MapEast.Name = "东部地图";
MapEast.WestMap = Map.EID;
Map.EastMap = MapEast.EID;
Map.ObjectCrossMapMethod = ObjectCrossToMap;
MapEast.Refresh();
gMaps.Add(MapEast);
GameMap MapSouth = new GameMap(LOG);
MapSouth.Name = "南部地图";
MapSouth.NorthMap = Map.EID;
Map.SouthMap = MapSouth.EID;
Map.ObjectCrossMapMethod = ObjectCrossToMap;
MapSouth.Refresh();
gMaps.Add(MapSouth);
}
这个处理内容跨越地图事件的方法就是ObjectCrossToMap
具体代码如下,是GameWorld
的成员:
private void ObjectCrossToMap(GameObject OBJ,Direction dir)
{
GameMap Map = GetMap(OBJ.MapEID);
LOG(OBJ.Name + " 尝试从 "+Map.Name+" 向 "+dir + " 越地图边界");
ExistenceID NewMapID = ExistenceID.Null;
if (Map != null)
{
switch (dir)
{
case Direction.EAST:
NewMapID = Map.EastMap;
break;
case Direction.SOUTH:
NewMapID = Map.SouthMap;
break;
case Direction.WEST:
NewMapID = Map.WestMap;
break;
case Direction.NORTH:
NewMapID = Map.NorthMap;
break;
}
GameMap NewMap = GetMap(NewMapID);
if (NewMap != null)
{
Map.gObjects.Remove(OBJ);
switch (dir)
{
case Direction.EAST:
OBJ.X = 0;
break;
case Direction.SOUTH:
OBJ.Y = 0;
break;
case Direction.WEST:
OBJ.X = (byte)(G.MapSize - 1);
break;
case Direction.NORTH:
OBJ.Y = (byte)(G.MapSize - 1);
break;
}
OBJ.MapEID = NewMap.EID;
NewMap.gObjects.Add(OBJ);
LOG(OBJ.Name + " 越地图边界到达 "+NewMap.Name+" (X:" + OBJ.X + ",Y:" + OBJ.Y + ")");
}
else
{
LOG(OBJ.Name + " 越地图边界失败,前方没有地图。");
}
OBJ.Status = ObjectStatus.IDEL;
}
}
调试一下,可以看到结果:
小狗和小猫可以在各个地图间愉快的奔跑了,如果内存足够大,我们可以加载更多的地图让它们跑个够~~!
不过,光是用LOG来显示小动物们的行动,感觉很不过瘾啊……
下一篇,我们将开始进行客户端的开发,很快的,我们就可以在客户端上直观的看见小动物们的表现了。
是不是有点小激动呢?
上一篇:C#服务端的微信小游戏——多人在线角色扮演(七)
下一篇:C#服务端的微信小游戏——多人在线角色扮演(九)
请用微信扫描查看游戏效果演示