
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事件.
在更新中设置输入状态标志并触发任何相关事件.只有这一次,您才触发“ 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中其他脚本解耦的最佳做法 - 乐贴网