Added Unity project files
This commit is contained in:
@ -0,0 +1,193 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using VRC.SDKBase;
|
||||
using VRC.Udon;
|
||||
using VRC.Udon.Common;
|
||||
|
||||
namespace VRC.SDK3.ClientSim
|
||||
{
|
||||
/// <summary>
|
||||
/// System responsible for listening to Input Events and sending them to UdonBehaviours.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Listens to Events:
|
||||
/// - ClientSimMenuStateChangedEvent
|
||||
/// Listens to Input Events:
|
||||
/// - Jump
|
||||
/// - Grab
|
||||
/// - Use
|
||||
/// - Drop
|
||||
/// </remarks>
|
||||
public class ClientSimUdonInput : IDisposable
|
||||
{
|
||||
private const float MINIMUM_MOVE_INPUT_EPS = 1e-3f;
|
||||
private const float MINIMUM_LOOK_INPUT_EPS = 1e-5f;
|
||||
|
||||
private readonly IClientSimInput _input;
|
||||
private readonly IClientSimEventDispatcher _eventDispatcher;
|
||||
/// <summary>
|
||||
/// A wrapper for sending events to all UdonBehaviours to create unit tests not dependent on UdonManager.
|
||||
/// </summary>
|
||||
private readonly IClientSimUdonInputEventSender _udonInputEventSender;
|
||||
|
||||
private Vector2 _prevInput;
|
||||
private Vector2 _prevLookInput = Vector2.zero;
|
||||
private Vector2 _prevMoveAxes = Vector2.zero;
|
||||
|
||||
private bool _isMenuOpen;
|
||||
private int _lastMenuUpdateFrame; // TODO update based on processing tick instead of time to allow for better testing.
|
||||
|
||||
// All button based events are queued until process time. This is done to ensure that all udon input events
|
||||
// happen at the same time in the frame and not mixed between when unity's update for the new Input Manager
|
||||
// sends events. Without this, input events would happen before UdonBehaviour.Update, causing strange out of
|
||||
// order issues.
|
||||
private readonly Queue<Action> _queuedEvents = new Queue<Action>();
|
||||
|
||||
public ClientSimUdonInput(
|
||||
IClientSimEventDispatcher eventDispatcher,
|
||||
IClientSimInput input,
|
||||
IClientSimUdonInputEventSender udonInputEventSender)
|
||||
{
|
||||
_input = input;
|
||||
_eventDispatcher = eventDispatcher;
|
||||
_udonInputEventSender = udonInputEventSender;
|
||||
|
||||
_eventDispatcher.Subscribe<ClientSimMenuStateChangedEvent>(SetMenuOpen);
|
||||
|
||||
// Input will be null with incorrect Unity input project settings.
|
||||
_input?.SubscribeJump(JumpInput);
|
||||
_input?.SubscribeUse(UseInput);
|
||||
_input?.SubscribeGrab(GrabInput);
|
||||
_input?.SubscribeDrop(DropInput);
|
||||
_input?.SubscribeInputChangedEvent(SendInputChangedEvent);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_eventDispatcher?.Unsubscribe<ClientSimMenuStateChangedEvent>(SetMenuOpen);
|
||||
|
||||
_input?.UnsubscribeJump(JumpInput);
|
||||
_input?.UnsubscribeUse(UseInput);
|
||||
_input?.UnsubscribeGrab(GrabInput);
|
||||
_input?.UnsubscribeDrop(DropInput);
|
||||
_input?.UnsubscribeInputChangedEvent(SendInputChangedEvent);
|
||||
}
|
||||
|
||||
#region ClientSim Events
|
||||
|
||||
private void SetMenuOpen(ClientSimMenuStateChangedEvent stateChangedEvent)
|
||||
{
|
||||
_lastMenuUpdateFrame = Time.frameCount;
|
||||
_isMenuOpen = stateChangedEvent.isMenuOpen;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ClientSim Input
|
||||
|
||||
private void JumpInput(bool value, HandType hand)
|
||||
{
|
||||
QueueButtonInputEvent(value, hand, UdonManager.UDON_INPUT_JUMP);
|
||||
}
|
||||
|
||||
private void UseInput(bool value, HandType hand)
|
||||
{
|
||||
QueueButtonInputEvent(value, hand, UdonManager.UDON_INPUT_USE);
|
||||
}
|
||||
|
||||
private void GrabInput(bool value, HandType hand)
|
||||
{
|
||||
QueueButtonInputEvent(value, hand, UdonManager.UDON_INPUT_GRAB);
|
||||
}
|
||||
|
||||
private void DropInput(bool value, HandType hand)
|
||||
{
|
||||
QueueButtonInputEvent(value, hand, UdonManager.UDON_INPUT_DROP);
|
||||
}
|
||||
|
||||
private void SendInputChangedEvent(VRCInputMethod inputMethod)
|
||||
{
|
||||
_queuedEvents.Enqueue(() =>
|
||||
{
|
||||
UdonManager.Instance.RunEvent(UdonManager.UDON_EVENT_ONINPUTMETHODCHANGED, ("inputMethod", inputMethod));
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private void SendUdonInputBoolEvent(bool value, HandType hand, string eventName)
|
||||
{
|
||||
var args = new UdonInputEventArgs(value, hand);
|
||||
_udonInputEventSender.RunInputAction(eventName, args);
|
||||
}
|
||||
|
||||
private void SendUdonInputFloatEvent(float value, HandType hand, string eventName)
|
||||
{
|
||||
var args = new UdonInputEventArgs(value, hand);
|
||||
_udonInputEventSender.RunInputAction(eventName, args);
|
||||
}
|
||||
|
||||
private void QueueButtonInputEvent(bool value, HandType hand, string eventName)
|
||||
{
|
||||
// Do not queue event if the menu is open.
|
||||
if (IsMenuOpen())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_queuedEvents.Enqueue(() =>
|
||||
{
|
||||
SendUdonInputBoolEvent(value, hand, eventName);
|
||||
});
|
||||
}
|
||||
|
||||
public void ProcessInputEvents()
|
||||
{
|
||||
// If the menu is open or the menu was updated on this frame, skip sending input events.
|
||||
if (IsMenuOpen())
|
||||
{
|
||||
_queuedEvents.Clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure that all udon input events happen at the same time in the frame and not mixed between when unity's
|
||||
// update for the new Input Manager sends events. Without this, input events would happen before
|
||||
// UdonBehaviour.Update, causing strange out of order issues.
|
||||
while (_queuedEvents.Count > 0)
|
||||
{
|
||||
Action inputEvent = _queuedEvents.Dequeue();
|
||||
inputEvent?.Invoke();
|
||||
}
|
||||
|
||||
Vector2 lookAxes = _input.GetLookAxes();
|
||||
if (Mathf.Abs(lookAxes.x - _prevLookInput.x) > MINIMUM_LOOK_INPUT_EPS)
|
||||
{
|
||||
SendUdonInputFloatEvent(lookAxes.x, HandType.RIGHT, UdonManager.UDON_LOOK_HORIZONTAL);
|
||||
}
|
||||
if (Mathf.Abs(lookAxes.y - _prevLookInput.y) > MINIMUM_LOOK_INPUT_EPS)
|
||||
{
|
||||
SendUdonInputFloatEvent(lookAxes.y, HandType.RIGHT, UdonManager.UDON_LOOK_VERTICAL);
|
||||
}
|
||||
_prevLookInput = lookAxes;
|
||||
|
||||
|
||||
Vector2 moveAxes = _input.GetMovementAxes();
|
||||
if (Mathf.Abs(_prevMoveAxes.x - moveAxes.x) > MINIMUM_MOVE_INPUT_EPS)
|
||||
{
|
||||
SendUdonInputFloatEvent(moveAxes.x, HandType.LEFT, UdonManager.UDON_MOVE_HORIZONTAL);
|
||||
}
|
||||
if (Mathf.Abs(_prevMoveAxes.y - moveAxes.y) > MINIMUM_MOVE_INPUT_EPS)
|
||||
{
|
||||
SendUdonInputFloatEvent(moveAxes.y, HandType.LEFT, UdonManager.UDON_MOVE_VERTICAL);
|
||||
}
|
||||
_prevMoveAxes = moveAxes;
|
||||
}
|
||||
|
||||
private bool IsMenuOpen()
|
||||
{
|
||||
return _isMenuOpen || _lastMenuUpdateFrame == Time.frameCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user