C# 统一-使用组件作为接口

C# 统一-使用组件作为接口,c#,unity3d,inheritance,interface,C#,Unity3d,Inheritance,Interface,需求: 可以通过界面与游戏对象实例上附加的不同类型的组件实例进行交互 例如,如果我有一个与士兵的游戏,并且假设医疗兵和狙击手都是士兵,我希望能够将士兵组件附加到士兵游戏对象,而不管该士兵实际上是狙击手还是医疗兵。然后,我可以做如下事情:soldier.GetComponent().Respawn()最终将调用Medic.Respawn()或Sniper.Respawn(),具体取决于士兵的实际类型 可能但不干净的解决方案1: 第一种简单的方法是让狙击手和医疗组件实现一个士兵接口。然而,这导致了几

需求:

可以通过
界面
游戏对象
实例上附加的不同类型的
组件
实例进行交互

例如,如果我有一个与士兵的游戏,并且假设医疗兵和狙击手都是士兵,我希望能够将
士兵
组件附加到士兵
游戏对象
,而不管该士兵实际上是
狙击手还是
医疗兵
。然后,我可以做如下事情:
soldier.GetComponent().Respawn()
最终将调用
Medic.Respawn()
Sniper.Respawn()
,具体取决于士兵的实际类型

可能但不干净的解决方案1:

第一种简单的方法是让
狙击手
医疗
组件实现一个
士兵
接口
。然而,这导致了几个问题

例如,如果您想检查
游戏对象
是否有一个实现
士兵
的组件,您不能,因为
士兵
只是一个接口,而不是实际的Unity
组件
。因此,例如,对具有
Medic
组件的
GameObject调用
GetComponent()
将不会返回该
Medic
组件,即使
Medic
确实实现了
Soldier

(实际上,您可以通过迭代所有组件并使用
is
操作符来检查这一点,但这将是肮脏而缓慢的)

可能但不干净的解决方案2:

第二种方法是创建一个基本的
组件
士兵
医疗兵
狙击手
类将从中继承

但这也带来了一些问题

首先,Unity事件(
Awake()
Start()
,等等)只在层次结构的叶类上被调用,迫使您手动调用父类上的相同函数。任何尝试过这样做的人都知道,很容易忘记调用某些东西,例如,这会导致不正确初始化对象

其次,继承的常见问题也在这里。例如,如果我希望我的
Medic
Sniper
组件不仅是
Soldier
,而且是
exploble
VehicleDriver
或其他什么,我不能,因为C不支持多重继承

我正在考虑的方法:

我考虑了一种设计代码的方法,以解决上面列出的问题

这个想法是让一个
组件
类充当接口,并让该接口组件与acutal组件在同一个
游戏对象
上共存。换句话说,让两个游戏对象。其中一个既有士兵
又有医疗组件,另一个既有士兵又有狙击手组件。所有三个组件类,即
士兵
医疗兵
狙击手
将完全独立,并且都继承自
单一行为

代码的其他部分仅与
士兵
组件交互。在本例中,您可以执行以下操作:
soldier.GetComponent().Respawn()

然后,“接口”组件(即
士兵
)有责任使用实际组件(即
医疗兵
狙击手
)执行特定动作

但是,由于
Soldier
不了解
Medic
Sniper
或将来可能添加的任何实现,因此
Soldier
组件公开了
Medic
Soldier
必须实现的实际
接口

由于使用此解决方案可以实现多个接口,因此可以使用多个“接口”组件。例如,士兵游戏对象可以有以下“接口”组件:
soldier
Explodable
,以及以下“实际”组件:
Medic
,它将实现两个接口
soldier.ISolder
Explodable.IExplodable

您觉得这个解决方案怎么样?谢谢

编辑: 我把我的想法编码了下来,看起来效果不错。我还创建了一个编辑器脚本,允许“接口”组件引用“实际”组件,而不必使用公共字段,而是使用属性。我会发布代码,以防有人需要:

WaterComponent.cs
-水对象的“接口”组件:

using System;
using UnityEngine;

public class WaterComponent : MonoBehaviour
{
    #region Interface

    public interface IWater
    {
        bool IsPointSubmerged(Vector3 worldPoint);

        Vector3 GetNormalAtPoint(Vector3 worldPoint);
    }

    #endregion Interface

    #region Properties

    public IWater Water
    {
        get
        {
            return waterImplementation;
        }
        set
        {
            Component asComponent = value as Component;

            if (null != value && null == waterComponent)
            {
                throw new ArgumentException($"The given {typeof(IWater).Name} is not a {typeof(Component).Name}.");
            }

            waterComponent = asComponent;
            waterImplementation = value;
        }
    }

    #endregion Properties

    #region Fields

    [SerializeField]
    private Component waterComponent;

    private IWater waterImplementation;

    #endregion Fields

    #region Public methods

    public bool IsPointSubmerged(Vector3 worldPoint)
    {
        return waterImplementation.IsPointSubmerged(worldPoint);
    }

    public Vector3 GetNormalAtPoint(Vector3 worldPoint)
    {
        return waterImplementation.GetNormalAtPoint(worldPoint);
    }

    #endregion Public methods

    #region Unity events

    private void Awake()
    {
        waterImplementation = waterComponent as IWater;
    }

    #endregion Unity events
}
RealWater.cs-实现“接口”组件的“实际”组件:

using UnityEngine;

public class RealWater : MonoBehaviour, WaterComponent.IWater
{
    #region WaterComponent.IWater implementation

    public bool IsPointSubmerged(Vector3 worldPoint)
    {
        return SpecificIsPointSubmerged(worldPoint);
    }

    public Vector3 GetNormalAtPoint(Vector3 worldPoint)
    {
        return SpecificGetWaterAtPoint(worldPoint);
    }

    #endregion WaterComponent.IWater implementation

    #region Non-public methods

    private bool SpecificIsPointSubmerged(Vector3 worldPoint)
    {
        return true;
    }

    private Vector3 SpecificGetWaterAtPoint(Vector3 worldPoint)
    {
        return transform.up;
    }

    #endregion Non-public methods
}
WaterComponentedEditor.cs-允许不暴露裸字段的自定义编辑器:

使用UnityEditor

[CustomEditor(typeof(WaterComponent))]
[CanEditMultipleObjects]
public class WaterComponentEditor : Editor
{
    #region Serialized properties

    private SerializedProperty waterProperty;

    #endregion Serialized properties

    #region Overridden methods

    public override void OnInspectorGUI()
    {
        serializedObject.Update();

        EditorGUI.BeginChangeCheck();

        EditorGUILayout.PropertyField(waterProperty);

        if (EditorGUI.EndChangeCheck())
        {
            ((WaterComponent) target).Water = waterProperty.exposedReferenceValue as WaterComponent.IWater;
        }

        serializedObject.ApplyModifiedProperties();
    }

    #endregion Overridden methods

    #region Unity events

    private void OnEnable()
    {
        waterProperty = serializedObject.FindProperty("waterComponent");
    }

    #endregion Unity events
}
请随意重复使用,除非你看到它有缺陷,在这种情况下,我真的很想知道

编辑:这个自定义编辑器的问题是,您可以让“接口”组件引用任何
组件
,即使后者没有实现“接口”组件公开的真实接口。仍然可以在自定义编辑器脚本中执行一些运行时检查,但这并不干净。然而,我认为与这个问题相比,它的优点仍然足够好。

好吧

GetComponent函数族现在支持将接口作为泛型参数

Unity 5.0发行说明:

不管怎样