起因

最近项目需要做游戏的 GM 指令,思前想去,想了几个方案后, 最后决定使用 UnityIngameDebugConsole提供的 GM 指令,然后封装一个热更可用的组件

仓库地址

尝试的方案

iOS 捷径

之前我很喜欢使用捷径来做一些游戏的 GM 指令

但是当区分渠道、服务器、还需要查表时,这个方案做起来就非常难以维护,而且对安卓手机没有找到合适的处理方式

GM UI

还有一些会把GM 指令在游戏中做成图形化的UI, 点击A 按钮触发 xxxx

这个方案需要设计一个如何触发 GM UI 的逻辑,而且如果增删指令,还要对 UI 布局进行调整,就很烦

UnityIngameDebugConsole

大佬写的这个组件,在真机测试碰到一些bug时,非常非常非常救命,而且本身支持 Executing Commands,而且用起来也非常简单,下面我主要讲一下如何实现

具体实现

使用效果如下

gm id // 展示用户ID
gm money 5000 // 增加5000金币

ConsoleMethod

这里之所以要写这么多是因为有些指令不需要额外的参数,有些需要,而且要防止策划或其他人员使用时,没有正确大小写导致不能正确执行

namespace ETModel {
    public class DebugConsoleGM {
        [ConsoleMethod("GM", "GM Code")]
        public static void Excute(string command, string value = null) {
            Game.EventSystem.Run(EventIdType.DebugConsoleGM, command, value);
        }

        [ConsoleMethod("gm", "GM Code")]
        public static void Excute1(string command, string value = null) {
            Game.EventSystem.Run(EventIdType.DebugConsoleGM, command, value);
        }

        [ConsoleMethod("GM", "GM Code")]
        public static void Excute2(string command) {
            Game.EventSystem.Run(EventIdType.DebugConsoleGM, command, string.Empty);
        }

        [ConsoleMethod("gm", "GM Code")]
        public static void Excute3(string command) {
            Game.EventSystem.Run(EventIdType.DebugConsoleGM, command, string.Empty);
        }
    }
}

这里需要注意在 ETModelEventIdType 类里面增加对应的类型

Hotfix 监听

这里的监听很简单,在热更中创建一个 ETModel.EventIdType 的事件即可

这里监听到后,把所有的属性和参数传给 GMCodeComponent,进行处理

namespace ETHotfix {
    [Event(ETModel.EventIdType.DebugConsoleGM)]
    public class DebugConsoleGMHandler : AEvent<string, string> {
        public override void Run(string commond, string value) {
            try{ Game.Scene.GetComponent<GMCodeComponent>().Handle(commond, value); }
            catch(Exception e){ Log.Error(e); }
        }
    }
}

IGmCodeHandler

我们给所有的指令提供一个接口

namespace ETHotfix {
    public interface IGMCodeHandler {
        void Handle(string value);
    }
}

展示ID 的指令示例

这里我同样创建了一个 GMCodeDefine,用于定义游戏中所有GM指令的名字,这个部分就不贴了

namespace ETHotfix {
    [GMCode(GMCodeDefine.ID)]
    public class GMCode_ID_Handler : IGMCodeHandler {
        public void Handle(string value) {
            try{
                // show id
            }
            catch(Exception e){ Log.Error(e); }
        }
    }
}

GMCodeComponent

这里同样对传过来的 commond 进行了 ToUpper处理,防止其他人错误的书写了大小写

注意在 GMCodeDefine 中定义的属性值最终都是大写噢

namespace ETHotfix {
    [ObjectSystem]
    public class GMCodeComponentAwakeSystem : AwakeSystem<GMCodeComponent> {
        public override void Awake(GMCodeComponent self) { self.Awake(); }
    }

    public class GMCodeComponent : Component {
        private readonly Dictionary<string, IGMCodeHandler> _gmCodes = new Dictionary<string, IGMCodeHandler>();

        public void Awake() {
            _gmCodes.Clear();
            List<Type> types = Game.EventSystem.GetTypes();
            foreach(Type type in types){
                object[] attrs = type.GetCustomAttributes(typeof(GMCodeAttribute), false);

                foreach(object attr in attrs){
                    GMCodeAttribute gmCodeAttribute = (GMCodeAttribute) attr;
                    object          obj             = Activator.CreateInstance(type);
                    IGMCodeHandler  iGMCode         = obj as IGMCodeHandler;
                    if(iGMCode == null){
                        Log.Error($"{obj.GetType().Name} is not inherit IGMCodeHandler");
                        continue;
                    }

                    if(_gmCodes.ContainsKey(gmCodeAttribute.CodeName)){ continue; }

                    _gmCodes.Add(gmCodeAttribute.CodeName, iGMCode);
                }
            }
        }

        public void Handle(string commond, string value) {
            try{
                commond = commond.ToUpper();

                Log.Debug($"commond = {commond}, value = {value}");

                if(_gmCodes.ContainsKey(commond)){ _gmCodes[commond].Handle(value); }
            }
            catch(Exception e){ Log.Error(e); }
        }
    }
}

总结

这个方案对程序很友好,如果区分渠道、服务器、查表或者实时在游戏中显示,都可以在每一个指令中做相应的控制,增删改也很方便

不过需要给使用人员维护一张指令表,且有一定的学习成本


What doesn’t kill you makes you stronger.