首页 > 解决方案 > 使用 C# 属性访问数组元素 (Unity3D)

问题描述

我正在尝试为 Unity 游戏的简单通用输入/控制器系统找出一个好的设计模式,该系统可用于玩家、NPC、车辆等。我当前的设计使用具有静态数组的控制器超类“Actions”,每个都有关于如何触发“Action States”数组中的相应布尔值、键绑定等的基本信息。然后我可以为特定的控制器指定派生类。我对这个设计的实现如下所示。

控制器超类:

public class Controller : MonoBehaviour
{
    public class Action
    {
        public enum ActionType
        {
            Impulse,
            Hold,
            Toggle
        };

        public string name;
        public ActionType actionType;
        public KeyCode binding;
        ...
    }

    [HideInInspector]
    public static Action[] actions;
    public bool[] actionStates;
}

特定控制器示例:

public class HumanController : Controller
{
    public bool sprint { get { return actionStates[0]; } set { actionStates[0] = value; } }
    public bool jump { get { return actionStates[1]; } set { actionStates[1] = value; } }
    ...
    public bool openInventory { get { return actionStates[13]; } set { actionStates[13] = value; } }

    public HumanController()
    {
        actions = new Action[]
        {
            new Action("Sprint", Action.InputType.Hold, KeyCode.LeftShift),
            new Action("Jump", Action.InputType.Impulse, KeyCode.Space),
            ...
            new Action("Open Inventory", Action.InputType.Toggle, KeyCode.Tab),
        };
        actionStates = new bool[actions.Length];
    }
}

该系统运行良好,因为我可以轻松地在检查器中查看 actionStates 数组,可以通过属性轻松地在代码中访问它们,而不是使用字符串索引字典(我怀疑这会更慢并产生更多垃圾)等等。唯一不理想的部分是手动设置属性,我想知道是否有一种更简洁/更优雅的方式将派生类中的每个属性映射到 actionStates 数组的每个元素,而不是执行每个动作如下:

public bool action { get { return actionStates [0]; } set { actionStates[0] = value; } }

标签: c#arraysunity3dproperties

解决方案


在接受了这个建议之后...

当然,最好将带有枚举的数组作为计数器,这样 myAction[JUMP] = ... 等

我想出了以下设置。

控制器超类:

public class Controller : MonoBehaviour
{

    public class Action
    {
        public enum InputType
        {
            Impulse,
            Hold,
            Toggle
        };

        public string name;
        public InputType inputType;
        public KeyCode binding;
                ...
            }

    public virtual Action[] actions { get; }
    public bool[] actionStates;

    public Controller()
    {
        actionStates = new bool[actions.Length];
    }

}

可索引派生类(尝试将 T 约束为枚举):

public class Controller<T> : Controller where T : struct, System.IConvertible
{
    public bool this[T enumIndex]
    {
        get { return actionStates[System.Convert.ToInt32(enumIndex)]; }
        set { actionStates[System.Convert.ToInt32(enumIndex)] = value; }
    }
}

特定控制器示例:

public enum HumanControls
{
    sprint,
    jump,
    ...
    openInventory
}

public class HumanController : Controller<HumanControls>
{

    public static Action[] actionList = new Action[]
    {
        // movement
        new Action("Sprint", Action.InputType.Hold, KeyCode.LeftShift),
        new Action("Jump", Action.InputType.Impulse, KeyCode.Space),
        ...
        new Action("Open Inventory", Action.InputType.Toggle, KeyCode.Tab),
    };

    public override Action[] actions { get; } = actionList;

}

然后我可以按如下方式使用控制器:

using static HumanControls;
...
if(controller[sprint]) {
...

它不涉及太多冗余,仍然允许我像以前一样使用简单的类型名称轻松访问数组元素(而不是先将 enum 强制转换为 int 等),并且工作得和我希望的用例一样好.


推荐阅读