C#服务端的微信小游戏——多人在线角色扮演(八)

知识共享许可协议 版权声明:署名,允许他人基于本文进行创作,且必须基于与原先许可协议相同的许可协议分发本文 (Creative Commons

C#服务端的微信小游戏——多人在线角色扮演(八)

动态加载地图虽然会消耗一定的系统资源,但是由此可以实现近似无限地图的游戏空间,让游戏乐趣达到人生的巅峰。
——茂叔

小狗小猫已经可以蹦蹦跳跳了,但是为了让它们跑得更远,我们需要把地图的空间尽量放大。当然,因为内存永远是有限的,所以要在有限的地图里面实现更大的空间,我们需要卸载那些没有玩家的空间,让系统资源为更多玩家服务。这就是为什么要把地图分成一块一块的原因。

每一块地图的大小为5个单位,即X,Y的取值范围是0-4。

我们定义两个全局变量来描述地图块的大小,这样如果将来想要变更起来也比较方便。

public static byte MapSize = 5;

这样一来,GameCharacterMove()可以修改为:

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#服务端的微信小游戏——多人在线角色扮演(九)
请用微信扫描查看游戏效果演示

演示入口

猜你喜欢

转载自blog.csdn.net/foomow/article/details/92081819