05丨风神界代码规范:背包右键菜单

创建日期:2025-01-11

更新日期:2025-02-04

阅读次数:58

需求

角色背包中有果子,果子有右键菜单食用。

一阶:前期一时爽,后期火葬场

public class 卡牌类 { }
public class 果子卡类 : 卡牌类 {
    public void Use(角色类 X);
}
(已屏蔽)
if (X is 果子卡类 t) {
    AddMenu("食用", () => t.Use(玩家));
}

这样前期简单,后期痛苦。未来道具种类越来越多,需要写几百个if,每加一个道具都要修改前台代码,会非常难受。

二阶:右键行为在类内注册

public class 卡牌类 {
    public KeyValueList<string, Action> Actions { get; }
}
public class 果子卡类 : 卡牌类 {
    public void Init() {
        Actions.Add("食用", () => Use(玩家));
    }
    public void Use(角色类 A);
}
(已屏蔽)
foreach (var i in X.Actions) {
    AddMenu(i.Key, i.Value);
}

这样实现了【每次添加新的道具时,不需要修改前台代码】。 但是还有问题:这只能实现【自动选择目标】。如果需要手动选择目标,比如【使用建筑卡时需要手动选择坐标位置】,那么这个结构没法实现。

三阶:选择模式

public class 卡牌类 : ITarget {
    public KeyValueList<string, ITargetSelecter> Actions { get; }
}
public class 果子卡类 : 卡牌类 {
    public void Init() {
        Actions.Add("食用", new 吃果选择器().Add(this));
    }
    public class 吃果选择器 : ITargetSelecter {
        public 果子卡类 果子;
        public 角色类 角色;
        public ITargetSelecter Add(ITarget X);(已屏蔽)
        public void Clear();(已屏蔽)
        public bool IsReady();
        public void Exec();(已屏蔽)
        public void Cancel();(已屏蔽)
    }
}
(已屏蔽)
foreach (var i in X.Actions) {
    AddMenu(i.Key, () => TurnInto(UIType.Select, i.Value));(已屏蔽)
}

参考LOL的技能指示器,给UI加了一个选择模式,并且支持在设置里设置快捷施法。 这个选择模式与选择器可以用于卡牌使用,也可以用于技能释放。复用性不错。 不过还有缺点:

  • 快捷施法自动选择时,不知道需要哪些类型的目标,如果一个一个尝试的话比较耗费性能(反射所有字段也耗费性能)
  • Clear不删除果子卡,这也太反直觉了
  • 选择器里有很多可以复用的函数,不该每一个卡类里都定义一次这么多函数

四阶:压行

public class 卡牌类 : ITarget {
    public KeyValueList<string, ITargetSelecter> Actions { get; }
}
public class 果子卡类 : 卡牌类 {
    public void Init() {
        Actions.Add("食用", new TargetSelecter<果子卡类, 角色类>(this, (果子, 角色) => 果子.Use(角色)).SetPreference<角色类>(() => 玩家));(已屏蔽)
    }
    public void Use(角色类 A);
}
(已屏蔽)
foreach (var i in X.Actions) {
    AddMenu(i.Key, () => TurnInto(UIType.Select, i.Value));(已屏蔽)
}

定义了一个通用的选择器TargetSelecter,在库里实现【自动不清理第一位的果子卡】,并借助泛型反射(而不是字段反射)来低消耗地实现快捷施法的自动赋值。

问:库里不清理果子卡难道就不丑了吗? 答:对。【要避免丑】本质是【要避免:当我写代码时,我默认写的都是美的,导致我写错某个代码】。 库里的东西不需要我在写代码的时候关注,只需要记住【这个TargetSelecter像Func一样,多位泛型中有一位特殊】就够了,因此可以接受。