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

Unity中多控制对象InputActionMap共性重构方案咨询

这个问题我在Unity项目里踩过坑——泛型基类的继承加上重复的输入绑定代码确实让人头大。咱们可以从几个方向重构,既解决代码重复,又能让子类正常调用派生类的方法:

方案一:泛型子类的分层继承

把RunnerControls改成带约束的泛型类,让它能适配Runner及其子类,这样JumperControls继承它时就能保留Jumper的类型,同时复用Run动作的绑定逻辑。

首先修复原代码里的一个小问题:perform事件需要绑定的是Action<InputAction.CallbackContext>委托,直接写controlled.Run()会立即执行方法,而不是绑定回调,得用lambda表达式包裹。

修改后的代码如下:

// 基类保持不变
public abstract class Controls<T> {
    protected T controlled;
    public Controls(InputActionMap actionMap, T controlled) {
        SetupInputCallbacks(actionMap);
        this.controlled = controlled;
    }
    protected abstract void SetupInputCallbacks(InputActionMap actionMap);
}

// 改成泛型子类,约束T必须是Runner或其子类
public class RunnerControls<T> : Controls<T> where T : Runner {
    public RunnerControls(InputActionMap actionMap, T controlled) : base(actionMap, controlled) { }

    protected override void SetupInputCallbacks(InputActionMap actionMap) {
        InputAction runAction = actionMap.FindAction("Run");
        // 用lambda绑定回调,传递CallbackContext参数
        runAction.perform += (ctx) => controlled.Run();
    }
}

// JumperControls继承泛型的RunnerControls,指定T为Jumper
public class JumperControls : RunnerControls<Jumper> {
    public JumperControls(InputActionMap actionMap, Jumper controlled) : base(actionMap, controlled) { }

    protected override void SetupInputCallbacks(InputActionMap actionMap) {
        // 先调用父类方法绑定Run动作,避免重复代码
        base.SetupInputCallbacks(actionMap);

        // 再绑定Jump动作
        InputAction jumpAction = actionMap.FindAction("Jump");
        jumpAction.perform += (ctx) => controlled.Jump();
    }
}

这个方案完美贴合你原有的继承架构,既复用了Run的绑定逻辑,又让controlled保留了Jumper的类型,能正常调用Jump()。

方案二:提取通用绑定逻辑到工具类(组合模式)

如果不想用复杂的泛型继承,可以把通用的输入绑定逻辑抽成静态工具类,通过组合而非继承来复用代码,更简洁灵活。

// 通用输入绑定工具类
public static class InputBindingUtils {
    public static void BindRunAction(InputActionMap actionMap, Runner runner) {
        InputAction runAction = actionMap.FindAction("Run");
        runAction.perform += (ctx) => runner.Run();
    }
}

// RunnerControls直接调用工具类方法
public class RunnerControls : Controls<Runner> {
    public RunnerControls(InputActionMap actionMap, Runner controlled) : base(actionMap, controlled) { }

    protected override void SetupInputCallbacks(InputActionMap actionMap) {
        InputBindingUtils.BindRunAction(actionMap, controlled);
    }
}

// JumperControls同样复用工具类的Run绑定,再添加自己的Jump绑定
public class JumperControls : Controls<Jumper> {
    public JumperControls(InputActionMap actionMap, Jumper controlled) : base(actionMap, controlled) { }

    protected override void SetupInputCallbacks(InputActionMap actionMap) {
        InputBindingUtils.BindRunAction(actionMap, controlled);
        
        InputAction jumpAction = actionMap.FindAction("Jump");
        jumpAction.perform += (ctx) => controlled.Jump();
    }
}

这种方式把通用逻辑集中到工具类,后续修改Run动作的绑定逻辑时,只需要改工具类即可,不用修改多个Controls子类。

方案三:基于接口的契约绑定(扩展性最强)

定义接口描述对象的动作能力,让基类自动根据接口绑定对应的动作,彻底消除重复代码,后续新增动作或对象类型时扩展性极强。

// 定义动作接口
public interface ICanRun {
    void Run();
}
public interface ICanJump {
    void Jump();
}

// 让对象实现对应接口
public class Runner : MonoBehaviour, ICanRun {
    public void Run() { /* ... */ }
}
public class Jumper : Runner, ICanJump {
    public void Jump() { /* ... */ }
}

// 修改基类,自动根据接口绑定动作
public abstract class Controls<T> {
    protected T controlled;
    public Controls(InputActionMap actionMap, T controlled) {
        this.controlled = controlled;
        SetupInputCallbacks(actionMap);
    }

    protected virtual void SetupInputCallbacks(InputActionMap actionMap) {
        // 如果对象实现了ICanRun,绑定Run动作
        if (controlled is ICanRun runner) {
            InputAction runAction = actionMap.FindAction("Run");
            runAction.perform += (ctx) => runner.Run();
        }
        // 如果对象实现了ICanJump,绑定Jump动作
        if (controlled is ICanJump jumper) {
            InputAction jumpAction = actionMap.FindAction("Jump");
            jumpAction.perform += (ctx) => jumper.Jump();
        }
    }
}

// 子类可以简化成空类(如果不需要额外逻辑)
public class RunnerControls : Controls<Runner> {
    public RunnerControls(InputActionMap actionMap, Runner controlled) : base(actionMap, controlled) { }
}
public class JumperControls : Controls<Jumper> {
    public JumperControls(InputActionMap actionMap, Jumper controlled) : base(actionMap, controlled) { }
}

这个方案的优势在于:后续新增Swimmer类实现ICanSwim,只需要在基类里加一段ICanSwim的检查逻辑,所有SwimmerControls都会自动绑定Swim动作,完全不需要重复编写绑定代码。

内容的提问来源于stack exchange,提问作者user17646270

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 18:42:50