using System;
using UnityEngine;
using UnityEngine.EventSystems;
using VRC.SDKBase;
using VRC.Udon.Common;
namespace VRC.SDK3.ClientSim
{
///
/// The system responsible for managing the mouse position for both ClientSim raycasting
/// and for Unity's EventSystem to know where to raycast for UI elements.
///
///
/// Sends Events:
/// - ClientSimMouseReleasedEvent
/// - ClientSimCurrentHandEvent
/// Listens to Events:
/// - ClientSimMenuStateChangedEvent
/// - ClientSimRaycastHitResultsEvent
/// Listens to Input Events:
/// - Use
/// - ReleaseMouse
///
[AddComponentMenu("")]
// Update FrameTick at the end of frame so that Input from playmode and test can be processed in the same order.
[DefaultExecutionOrder(10000)]
class ClientSimBaseInput : BaseInput, IClientSimMousePositionProvider, IDisposable
{
private IClientSimEventDispatcher _eventDispatcher;
private IClientSimInput _input;
private ClientSimSettings _settings;
private bool _menuIsOpen;
private bool _mouseReleaseKeyIsDown;
private bool _prevMouseReleased;
// Used for interacting with UI
private int _frameTick = 0;
private bool _rightUseDown = false;
private int _rightUseChangeTick = -1;
private bool _leftUseDown = false;
private int _leftUseChangeTick = -1;
private HandType _lastHandUsed = HandType.RIGHT;
private Camera _playerCamera = null;
private bool _uiShapeHit = false;
private Vector3 _raycastMousePosition;
public static Vector2 GetScreenCenter()
{
return new Vector2(Screen.width, Screen.height) * 0.5f;
}
public Vector2 GetMousePosition()
{
if (IsMouseFree())
{
// Due to having multiple inputs enabled or disabled, this method ensures no errors are thrown even
// if setup is incorrect.
#if ENABLE_INPUT_SYSTEM
// TODO if gamepad input, emulate mouse position to allow clicking on menus.
return UnityEngine.InputSystem.Mouse.current?.position.ReadValue() ?? Vector2.zero;
#elif ENABLE_LEGACY_INPUT_MANAGER
return base.mousePosition;
#else
return Vector2.zero;
#endif
}
return GetScreenCenter();
}
protected override void Awake()
{
base.Awake();
this.PreventComponentFromSaving();
}
public void Initialize(
IClientSimEventDispatcher eventDispatcher,
IClientSimInput input,
ClientSimSettings settings)
{
// Do not lock mouse if the player is never spawned.
if (!settings.spawnPlayer)
{
enabled = false;
return;
}
_eventDispatcher = eventDispatcher;
_input = input;
_settings = settings;
_eventDispatcher.Subscribe(SetMenuOpen);
_eventDispatcher.Subscribe(OnRaycastHit);
// Input will be null with incorrect Unity input project settings.
_input?.SubscribeReleaseMouse(InputMouseReleased);
_input?.SubscribeUse(InputUse);
}
protected override void Start()
{
base.Start();
// TODO properly pass in the camera provider instead of using this method.
_playerCamera = VRC_UiShape.GetEventCamera?.Invoke(this.gameObject);
foreach (var canvas in FindObjectsByType