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

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

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

用内容清单来实现地图的刷新,根据开发需求的细化来优化代码结构。
——茂叔

地图上来了只小狗

上一篇里,我们成功让游戏世界有了心跳,然而地图上任然空空如也。现在我们要在地图上加入内容GameObject了。
最开始,还是要先把GameObject的基本需求整理一下。由于GameObject继承自Existence,所以标识、名称这些可以不考虑了。除此之外,GameObject还需要下面这些信息:

  1. 位置,地图标识、X、Y
  2. 介绍描述,这是一个程序猿……
  3. 对应清单的ObjectClassId,有了这个,就知道是具体个什么东西或者不是个东西了。例如,这是一把剑,还是一把刀,是关云长的冷艳锯还是是吕奉先的方天戟。
  4. 类型,这是一种武器、一个角色还是一种药物……同一类型的东西具有某些共性,例如武器就都可以装备,而药品都可以服用。
  5. 图像,在客户端显示出来是个什么样子。我们把图像信息分解为图像文件编号和图像编号,这样可以把不同类型的东西放在不同的图像文件里面,方便维护。
  6. 状态,这个东西目前是什么状态,是已经腐烂,还是正在移动,或者是遭到了攻击。
  7. 动作阶段,一个动作可能需要不同的阶段,即便是静止的,也需要有云舒云展或者花开花落,我们需要根据不同的动作阶段在客户端显示不同的图像。
  8. 能不能穿过,一堵墙和一颗小草的区别。
  9. 主人,这是谁扔的?
  10. 物品……怀璧其罪。
    ……

别的以后再补充吧。 给GameObject添加以下成员:

        public string Description;
        public string MapEID;
        public sbyte X;
        public sbyte Y;
        public ushort Category;//类型
        public ushort ClassID;
        public byte Status;
        public byte StatusStep;
        public byte ImgFileID;
        public byte ImgID;
        public bool Passable;
        public GameObject[] Inventory;
        public string OwnerEID;

由于我们需要从地图内容清单来生成内容,这种工作其他地方也会用到,因此,我们定义一个ObjectFactory来完成这个工作。
GameLib项目中添加一个ObjectFactory类。

class ObjectFactory
    {
        public static GameObject Make(ushort ClassID, Action<string> LOGFUN = null)
        {
            GameObject ret = null;
            switch (ClassID)
            {
                case 0://生成一只小狗
                    {
                        ret = new GameObject(LOGFUN)
                        {
                            Name = "小狗旺财",
                            Description = "一只小狗,就是你",
                            CanPass = false,
                            ClassID = ClassID,
                            Category = ObjectCategory.ANIMAL,
                            ImgFileID = 0,
                            ImgID = 0,
                            Status = ObjectStatus.IDEL,
                            StatusStep = 0,
                            OwnerEID = null,
                            Inventory = new GameObject[8]//可以带8个物品
                        };
                    }
                    break;
                case 1://生成一只小猫
                    break;
                case 3://生成一块石头
                    break;
                case 4://生成一块草地
                    break;
                default:
                    break;
            }
            return ret;
        }
    }

然后修改GameMapRefresh方法如下:

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.X = item.X;
                        obj.Y = item.Y;
                        gObjects.Add(obj);
                        LOG("加入 " + obj.Name + " 成功");
                    }
                }
            }
            LastRefreshTime = DateTime.Now;
        }

GameWorld类的构造函数里面去配置第一张地图的内容清单。

public GameWorld(Action<string> LOGFUN = null) : base(LOGFUN)
        {
            gMaps = new List<GameMap>();
            LOG("GameWorld(" + EID + ")创建成功");
            GameMap Map = new GameMap(LOG);
            Map.MapItems.Add(new GameMap.MapItem()
            {
                ClassID= 0,
                X = 1,
                Y = 1
            }
            );
            Map.Refresh();
            gMaps.Add(Map);
        }

修改GameObject的心跳:

public override void HeartBeat()
        {
            LOG("GameObject[" + Name + "](" + EID + ")正在心跳 " + G.GlobeTime);
        }

调试看看效果:
调试效果
嗯,完全如我们的预期,因为我在写这篇文章的时候已经把坑都填了……

优化代码

在完成上面的工作的时候,我们发现了一些问题。

  1. 无论是类型Category还是ClassID都是数字,以后多了记不住咋办?这一问题在各个状态值上也存在。
  2. MapItem被定义在GameMap内部,使用上不方便。
  3. EID被定义成字符串,又是MD5码,将来使用起来不直观,容易出错。

为了解决上述问题,并有利于之后的开发,我们将所有需要制定的类型都放在一个叫Type.cs的文件里面,让代码看起来更有逼格。在GameLib项目新建一个Type类,删除缺省的class Type将文件内容修改如下:

    public enum ObjectCategory : ushort
    {
        NONE = 0,
        ANIMAL = 1,
        TERRAIN = 2
    }
    public enum ObjectClassID : ushort
    {
        NONE = 0,
        DOG = 1,
        CAT = 2,
        GRASS = 3,
        ROCK = 4
    }
    public enum ObjectStatus : byte
    {
        IDEL = 0,
        MOVING = 1,
        FIGHTING = 2
    }
    public struct MapItem
    {
        public ObjectClassID ClassID;
        public sbyte X;
        public sbyte Y;
    }
    public struct ExistenceID
    {
        private string value;

        public ExistenceID(string v)
        {
            if (v == null)
                value = "";
            else if (Regex.Match(v, @"^[A-Z0-9]{64}$").Success)
                value = v;
            else
                throw new InvalidCastException("ExistenceID只能为64位大写字母和数字构成的字符串");
        }

        public struct ExistenceID
    {
        private string value;

        public ExistenceID(string v)
        {
            if (v == null || v == "")
                value = "";
            else if (Regex.Match(v, @"^[A-Z0-9]{64}$").Success)
                value = v;
            else
                throw new InvalidCastException("ExistenceID只能为64位大写字母和数字构成的字符串");

        }

        public static ExistenceID NewValue
        {
            get
            {
                return G.MakeMD5(DateTime.Now.ToString("yyyyMMddHHmmssffff") + G.RND(10000, 99999).ToString() + G.RND(10000, 99999).ToString())
            + G.MakeMD5(G.RND(10000, 99999).ToString() + G.RND(10000, 99999).ToString());
            }

        }

        public static ExistenceID Null
        {
            get
            {
                return "";
            }

        }

        public override bool Equals(object obj)=> value == obj.ToString();

        public override int GetHashCode()
        {
            return base.GetHashCode();
        }

        public override string ToString()
        {
            return value;
        }

        public static implicit operator ExistenceID(string v)
        {
            return new ExistenceID(v);
        }
    }

这样我们就可以把GameObject的成员写成这样:

        public string Description;
        public ExistenceID MapEID;
        public sbyte X;
        public sbyte Y;
        public ObjectCategory Category;
        public ObjectClassID ClassID;
        public ObjectStatus Status;
        public byte StatusStep;
        public byte ImgFileID;
        public byte ImgID;
        public bool CanPass;
        public GameObject[] Inventory;
        public ExistenceID OwnerEID;

ObjectFactoryMake方法写成这样:

……
switch (ClassID)
            {
                case ObjectClassID.NONE:
                    break;
                case ObjectClassID.DOG:
                    {
                        ret = new GameObject(LOGFUN)
                        {
                            Name = "小狗旺财",
                            Description = "一只小狗,就是你",
                            CanPass = false,
                            ClassID = ClassID,
                            Category = ObjectCategory.ANIMAL,
                            ImgFileID = 0,
                            ImgID = 0,
                            Status = ObjectStatus.IDEL,
                            StatusStep = 0,
                            OwnerEID = null,
                            Inventory = new GameObject[8]//可以带8个物品
                        };

                    }
                    break;
                case ObjectClassID.CAT:
                    break;
                case ObjectClassID.GRASS:
                    break;
                case ObjectClassID.ROCK:
                    break;
                default:
                    break;
            }
……

GameWorld的构造函数里添加地图内容清单也不用引用GameMap了:

Map.MapItems.Add(new MapItem()
            {
                ClassID= ObjectClassID.DOG,
                X = 1,
                Y = 1
            }

别忘了还有GameMap哦:


        public ExistenceID NorthMap = ExistenceID.Null;
        public ExistenceID SouthMap = ExistenceID.Null;
        public ExistenceID WestMap = ExistenceID.Null;
        public ExistenceID EastMap = ExistenceID.Null;

改完调试一下,调试结果一模一样,但是这样的代码,是不是好看多了,而且将来维护扩充都更方便,也不容易出错。

好了,下一篇我们将实现更多的内容细节,并进一步讨论各种内容的组织架构。

上一篇:C#服务端的微信小游戏——多人在线角色扮演(五)
下一篇:C#服务端的微信小游戏——多人在线角色扮演(七)

请用微信扫描查看游戏效果演示

演示入口

猜你喜欢

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