我有一个输入脚本,它在Update
循环中将触摸转换为大小为(0-1)的方向(左,右,上,下),并且当检测到输入时,该脚本会触发{ {1}}:
UnityEvent
我正在从Unity Inspector订阅此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);
}
}
}
事件来调用我的OnAnalogStickInput
方法,该方法需要一个Direction和一个幅度:
CharacterController2D.Move
然后我有另一个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
中的输入进行移动,也许就我而言,我想在{{1}中最后进行旋转}。 相反正在从运行输入代码的FixedUpdate
循环中触发LateUpdate
和CharacterController2D.Move
。
我能想到的唯一其他选择可能是将Input脚本的代码重构为方法调用。然后让SnapRotator.Rotate
和Update
在CharacterController2D
循环中调用此方法,并根据需要在SnapRotator
或Update
循环中执行移动/旋转,例如:>
FixedUpdate
我的问题是:在Unity中解耦此类脚本的最佳实践是什么?还是我对可重用性的考虑过于广泛/过于害怕在游戏开发中耦合子类/组件?
万一这对其他人有帮助,这是我的最终解决方案,用半sudocode表示感谢Ruzihm:
实用程序类,用于存储有关检测到的输入的信息:
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.
}
}
订阅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
事件。
答案 0 :(得分:3)
这是一种替代方法,主要需要在您的TouchAnalogStickInput
类中进行更改。
在Update
中设置输入状态标志,并触发所有相关事件。只有这一次,您才触发“ 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
上触发。在大多数情况下-包括Move
-这可能是适当的,但在其他情况下则可能不理想。因此,您可以添加其他事件,这些事件仅在更新后的第一个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;
}
希望这是有道理的。
答案 1 :(得分:1)
您不妨考虑自定义Script Execution Order并让手势处理器先运行。
希望有帮助。 =)