+-
c#-将输入与Unity中其他脚本解耦的最佳做法
我有一个输入脚本,可以在Update循环中将触摸转换为幅度为(0-1)的方向(左,右,上,下),并且当检测到输入时,该脚本会触发UnityEvent:

public class TouchAnalogStickInput : MonoBehaviour
{
    [System.Serializable]
    public class AnalogStickInputEvent : UnityEvent<Direction, float> { }

    [Header("Events")]
    [Space]
    [Tooltip("Fired when a successful swipe occurs. Event Args: Swipe Direction, A normalized input magnitude between 0 and 1.")]
    public AnalogStickInputEvent OnAnalogStickInput;

    void Update()
    {
        ...
        if (successfulInputDetected)
        {
            OnAnalogStickInput.Invoke(mInputDirection, normalizedInputMag);
        }
    }
}

我正在从Unity Inspector订阅此OnAnalogStickInput事件,以调用我的CharacterController2D.Move方法,该方法需要一个Direction和一个大小:

public class CharacterController2D : MonoBehaviour
{
    public void Move(Direction movementDirection, float normalizedInputMagnitude)
    {
        // Move the character.
    } 
}

然后,我想使用相同的输入脚本旋转另一个GameObject.因此,我已将TouchAnalogStickInput和SnapRotator附加到此GameObject,并订阅OnAnalogStickInput事件以调用SnapRotator.Rotate:

public class SnapRotator : MonoBehaviour
{
    public void Rotate(Direction movementDirection, float normalizedInputMagnitude)
    {
        // Rotate object.
    }
}

在这一点上,我开始意识到我不再负责这些方法从哪个游戏循环中调用,例如我应该在Update中检测输入,使用FixedUpdate中的此输入进行移动,也许就我而言,我想在LateUpdate中最后进行旋转.而是从运行输入代码的Update循环中触发了CharacterController2D.Move和SnapRotator.Rotate.

我能想到的唯一其他选择可能是将Input脚本的代码重构为方法调用.然后让CharacterController2D和SnapRotator在Update循环中调用此方法,根据需要在FixedUpdate或LateUpdate循环中执行移动/旋转,例如:

public class CharacterController2D : MonoBehaviour
{
    public TouchAnalogStickInput Input;

    private var mMovementInfo;

    void Update()
    {
        // Contains a Direction and a Normalized Input Magnitude 
        mMovementInfo = Input.DetectInput();
    }

    void FixedUpdate()
    {
        if (mMovementInfo == Moved)
            // Move the character.
    } 
}

我的问题是:在Unity中解耦此类脚本的最佳实践是什么?还是我对可重用性的考虑过于广泛/过于害怕在游戏开发中耦合子类/组件?

如果这对其他人有帮助,这是我的最终解决方案,用半sudocode表示感谢Ruzihm:

实用程序类,用于存储有关检测到的输入的信息:

public class InputInfo
{
    public Direction Direction { get; set; } = Direction.None;
    public float NormalizedMagnitude { get; set; } = 0f;
    public TouchPhase? CurrentTouchPhase { get; set; } = null;

    public InputInfo(Direction direction, float normalizedMagnitude, TouchPhase currentTouchPhase)
    {
        Direction = direction;
        NormalizedMagnitude = normalizedMagnitude;
        CurrentTouchPhase = currentTouchPhase;
    }

    public InputInfo()
    {

    }
}

输入类别:

public class TouchAnalogStickInput : MonoBehaviour
{
    [System.Serializable]
    public class AnalogStickInputEvent : UnityEvent<InputInfo> { }

    [Header("Events")]
    [Space]
    [Tooltip("Fired from the Update loop when virtual stick movement occurs. Event Args: Swipe Direction, A normalized input magnitude between 0 and 1.")]
    public AnalogStickInputEvent OnUpdateOnAnalogStickInput;

    [Tooltip("Fired from the FixedUpdate loop when virtual stick movement occurs. Event Args: Swipe Direction, A normalized input magnitude between 0 and 1.")]
    public AnalogStickInputEvent OnFixedUpdateOnAnalogStickInput;

    [Tooltip("Fired from the LateUpdate loop when virtual stick movement occurs. Event Args: Swipe Direction, A normalized input magnitude between 0 and 1.")]
    public AnalogStickInputEvent OnLateUpdateOnAnalogStickInput;

    private bool mInputFlag;
    private InputInfo mInputInfo;

    void Update()
    {
        // Important - No input until proven otherwise, reset all members.
        mInputFlag = false;
        mInputInfo = new InputInfo();

        // Logic to detect input
        ...
        if (inputDetected)
        {
            mInputInfo.Direction = direction;
            mInputInfo.NormalizedMagnitude = magnitude;
            mInputInfo.CurrentTouchPhase = touch.phase;

            // Now that the Input Info has been fully populated set the input detection flag.
            mInputFlag = true;

            // Fire Input Event to listeners
            OnUpdateOnAnalogStickInput.Invoke(mInputInfo);
        }

        void FixedUpdate()
        {
            if (mInputFlag)
            {
                OnFixedUpdateOnAnalogStickInput.Invoke(mInputInfo);
            }
        }

        void LateUpdate()
        {
            OnLateUpdateOnAnalogStickInput.Invoke(mInputInfo);
        }
    }
}

订阅OnFixedUpdateOnAnalogStickInput事件的字符控制器.

public class CharacterController2D : MonoBehaviour
{
    public void Move(InputInfo inputInfo)
    {
        // Use inputInfo to decide how to move.
    }
}

旋转类几乎相同,但是订阅了OnLateUpdateOnAnalogStickInput事件.

最佳答案
这是一种替代方法,主要需要在TouchAnalogStickInput类中进行更改.

在更新中设置输入状态标志并触发任何相关事件.只有这一次,您才触发“ OnUpdate”事件(在您的特定情况下,该事件不会注册任何东西):

void Update()
{
    ...
    inputFlag_AnalogStickInput = false;
    ...

    if (successfulInputDetected)
    {
        inputFlag_AnalogStickInput = true;
        OnUpdateOnAnalogStickInput.Invoke(mInputDirection, normalizedInputMag);
    }
}

然后在TouchAnalogStickInput.FixedUpdate中,调用OnFixedUpdateOnAnalogStickInput,它将在您的Move中注册

void FixedUpdate()
{
    ...
    if (inputFlag_AnalogStickInput)
    {
        OnFixedUpdateOnAnalogStickInput.Invoke(mInputDirection, normalizedInputMag);
    }
}

以此类推,使用LateUpdate,它会触发一个注册您的Rotate的事件.

void LateUpdate()
{
    ...
    if (inputFlag_AnalogStickInput)
    {
        OnLateUpdateOnAnalogStickInput.Invoke(mInputDirection, normalizedInputMag);
    }
}

当然,这些OnFixedUpdateOn …事件会在该标志为true的每个FixedUpdate上触发.在大多数情况下(包括“移动”),这可能是适当的,但在其他情况下则可能不希望使用.因此,您可以添加其他事件,这些事件仅在更新后的第一个FixedUpdate发生时触发.例如.:

void Update()
{
    ...
    firstFixedUpdateAfterUpdate = true;
    inputFlag_AnalogStickInput = false;
    ...

    if (successfulInputDetected)
    {
        inputFlag_AnalogStickInput = true;
        OnUpdateOnAnalogStickInput.Invoke(mInputDirection, normalizedInputMag);
    }
}



void FixedUpdate()
{
    ...
    if (inputFlag_AnalogStickInput)
    {
        OnFixedUpdateOnAnalogStickInput.Invoke(mInputDirection, normalizedInputMag); 

    }
    if (inputFlag_AnalogStickInput && firstFixedUpdateAfterUpdate)  
    {
        OnFirstFixedUpdateOnAnalogStickInput.Invoke(mInputDirection, normalizedInputMag); 
    }
    ...
    firstFixedUpdateAfterUpdate = false;
}

希望这是有道理的.

点击查看更多相关文章

转载注明原文:c#-将输入与Unity中其他脚本解耦的最佳做法 - 乐贴网