You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# Unity中如何优化ShipComponent抽象类的虚/抽象方法实现,减少重复代码并统一管理委托监听?

C# Unity中如何优化ShipComponent抽象类的虚/抽象方法实现,减少重复代码并统一管理委托监听?

看起来你已经搭好了一个挺清晰的组件化船舰架构,但每个ShipComponent子类都要重复写Init、IsPlayer、IsNotPlayer这些方法,大部分逻辑都是雷同的,只有委托监听和个别协程不一样,这确实太繁琐了。我之前做Unity游戏架构的时候也遇到过类似的问题,重复代码一多维护起来真的头疼,咱们一步步来优化。

一、收拢通用逻辑到基类,只给子类留扩展点

首先,咱们可以把基类里的通用逻辑固化,比如Init方法里的shipControl赋值是所有子类都需要的,那基类就直接实现这个逻辑,然后提供一个虚的扩展方法让子类实现自己的初始化(比如找子物体、启动专属协程)。同理,IsPlayer、IsNotPlayer里的通用流程也可以放到基类,子类只需要补充自己的特殊逻辑。

修改后的ShipComponent基类示例:

public abstract class ShipComponent : MonoBehaviour
{
    [HideInInspector] public ShipControl shipControl;

    // 通用初始化逻辑,子类不用重写这个
    public void Init(ShipControl control)
    {
        this.shipControl = control;
        // 调用子类的专属初始化逻辑
        InitComponent();
        // 统一设置监听
        SetListeners();
    }

    // 子类重写这个方法实现自己的初始化(比如找子物体、启动协程)
    protected virtual void InitComponent() { }

    // 通用的IsPlayer逻辑,子类可以选择重写扩展
    public virtual void IsPlayer()
    {
        SetListeners();
        // 调用子类的Player模式专属逻辑
        OnEnterPlayerMode();
    }

    // 子类重写这个方法处理Player模式下的特殊逻辑(比如启动协程)
    protected virtual void OnEnterPlayerMode() { }

    public virtual void IsNotPlayer()
    {
        // 统一移除所有监听
        RemoveAllListeners();
        // 调用子类的非Player模式专属逻辑
        OnExitPlayerMode();
    }

    // 子类重写这个方法处理非Player模式下的特殊逻辑(比如停止协程)
    protected virtual void OnExitPlayerMode() { }

    public virtual void ReEnable()
    {
        SetListeners();
        // 调用子类重新启用时的专属逻辑
        OnReEnable();
    }

    // 子类重写这个方法处理重新启用时的特殊逻辑
    protected virtual void OnReEnable() { }

    // 子类需要实现这个方法,返回自己的委托绑定关系
    protected abstract IEnumerable<(Delegate Event, Delegate Handler)> GetEventBindings();

    // 基类统一处理监听添加
    private void SetListeners()
    {
        // 先移除避免重复绑定
        RemoveAllListeners();
        if (!shipControl.shipWrapper.ship.IsPlayer) return;

        foreach (var (Event, Handler) in GetEventBindings())
        {
            // 根据事件类型适配添加逻辑
            if (Event is Action actionEvent && Handler is Action actionHandler)
                actionEvent += actionHandler;
            else if (Event is Action<int> intActionEvent && Handler is Action<int> intActionHandler)
                intActionEvent += intActionHandler;
            // 其他事件类型(比如Action<bool>)同理扩展
        }
    }

    // 基类统一处理监听移除
    private void RemoveAllListeners()
    {
        foreach (var (Event, Handler) in GetEventBindings())
        {
            if (Event is Action actionEvent && Handler is Action actionHandler)
                actionEvent -= actionHandler;
            else if (Event is Action<int> intActionEvent && Handler is Action<int> intActionHandler)
                intActionEvent -= intActionHandler;
            // 其他事件类型同理扩展
        }
    }

    // 统一处理Disable时的清理逻辑
    protected virtual void OnDisable()
    {
        RemoveAllListeners();
        StopAllCoroutines();
    }
}

二、简化子类实现,只关注特有逻辑

现在子类只需要实现自己的专属逻辑和返回委托绑定关系就行,不用再重复写那些通用的方法了。比如修改后的Rudder类:

public class Rudder : ShipComponent
{
    [Header("Settings")]
    public Transform rudder;
    [Header("Debug Info")]
    [SerializeField] float rudderSpeed;
    [SerializeField][Range(-45, 45)] int setRudderAngle = 0;
    [SerializeField][Range(-45f, 45f)] float realRudderAngle = 0f;

    protected override void InitComponent()
    {
        rudder = transform.GetChild(0).GetChild(4);
        StartCoroutine(SmoothRudderChange());
    }

    protected override IEnumerable<(Delegate Event, Delegate Handler)> GetEventBindings()
    {
        // 返回自己需要绑定的事件和处理方法
        yield return (PlayerShipControl.OnRudderChange, (Action<int>)SetRudder);
    }

    // 重新启用时的特殊逻辑
    protected override void OnReEnable()
    {
        StartCoroutine(SmoothRudderChange());
    }

    void SetRudder(int angle)
    {
        setRudderAngle = angle;
        // 你的舵机逻辑...
    }

    IEnumerator SmoothRudderChange()
    {
        // 你的平滑旋转逻辑...
        yield return null;
    }
}

再看简化后的Guns类:

public class Guns : ShipComponent
{
    private IEnumerator mouseAimCycle;

    protected override void InitComponent()
    {
        InitCannons();
    }

    protected override void OnEnterPlayerMode()
    {
        mouseAimCycle = AimCycle();
        StartCoroutine(mouseAimCycle);
    }

    protected override void OnExitPlayerMode()
    {
        StopCoroutine(mouseAimCycle);
    }

    protected override IEnumerable<(Delegate Event, Delegate Handler)> GetEventBindings()
    {
        yield return (PlayerShipControl.OnFireGuns, (Action)TryFire);
    }

    void TryFire()
    {
        // 你的开火逻辑...
    }

    IEnumerator AimCycle()
    {
        // 你的瞄准循环逻辑...
        yield return null;
    }

    void InitCannons()
    {
        // 你的大炮初始化逻辑...
    }
}

三、额外优化:用泛型统一处理委托(可选)

如果你的项目里事件类型很多,上面的类型判断会显得繁琐,可以用泛型绑定类来优化:

// 定义不同类型的绑定类
public class ActionBinding { public Action Event; public Action Handler; }
public class ActionIntBinding { public Action<int> Event; public Action<int> Handler; }
public class ActionBoolBinding { public Action<bool> Event; public Action<bool> Handler; }

然后基类里可以定义多个抽象方法让子类返回对应类型的绑定,或者用一个统一的列表来容纳,这样处理起来更灵活,不过这属于进阶优化,当前的方案已经能解决你的核心痛点了。

总结

优化之后,每个子类只需要关注自己的核心逻辑:初始化专属资源、处理Player/非Player模式的特殊逻辑、返回自己需要监听的事件和处理方法。所有通用的监听管理、初始化流程、Disable清理都收拢到了基类里,再也不用复制粘贴那些重复的代码了,维护起来也方便很多。

备注:内容来源于stack exchange,提问作者user20494201

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.23 07:04:12