Added Unity project files
This commit is contained in:
@ -0,0 +1,276 @@
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using VRC.SDKBase;
|
||||
using VRC.Udon.Common;
|
||||
|
||||
namespace VRC.SDK3.ClientSim
|
||||
{
|
||||
/// <summary>
|
||||
/// This system is responsible for handling finding objects that can be interacted with or picked up.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Sends Events:
|
||||
/// - ClientSimRaycastHitResultsEvent
|
||||
/// - ClientSimInteractEvent
|
||||
/// Listens to Events:
|
||||
/// - ClientSimOnPlayerMovedEvent
|
||||
/// - ClientSimPlayerDeathStatusChangedEvent
|
||||
/// Listens to Input Events:
|
||||
/// - Use
|
||||
/// </remarks>
|
||||
[AddComponentMenu("")]
|
||||
// Unity Event System Updates at -1000. Send raycast events before then to ensure UI interactions happen same frame.
|
||||
[DefaultExecutionOrder(-2000)]
|
||||
public class ClientSimPlayerRaycaster : ClientSimBehaviour, IDisposable
|
||||
{
|
||||
[SerializeField]
|
||||
private ClientSimPlayerHand leftHand;
|
||||
[SerializeField]
|
||||
private ClientSimPlayerHand rightHand;
|
||||
|
||||
private IClientSimEventDispatcher _eventDispatcher;
|
||||
private IClientSimInput _input;
|
||||
private IClientSimPlayerApiProvider _playerApiProvider;
|
||||
private IClientSimHighlightManager _highlightManager;
|
||||
private IClientSimTooltipManager _tooltipManager;
|
||||
private IClientSimInteractiveLayerProvider _interactiveLayerProvider;
|
||||
private IClientSimInteractManager _interactManager;
|
||||
private IClientSimTrackingProvider _trackingProvider;
|
||||
private IClientSimPlayerStationManager _stationManager;
|
||||
|
||||
private ClientSimRaycaster _leftHandRaycaster;
|
||||
private ClientSimRaycaster _rightHandRaycaster;
|
||||
|
||||
private ClientSimRaycastResults _hoverLeft;
|
||||
private ClientSimRaycastResults _hoverRight;
|
||||
|
||||
public void Initialize(
|
||||
IClientSimEventDispatcher eventDispatcher,
|
||||
IClientSimInput input,
|
||||
IClientSimPlayerApiProvider playerApiProvider,
|
||||
IClientSimPlayerPickupData pickupData,
|
||||
IClientSimHighlightManager highlightManager,
|
||||
IClientSimTooltipManager tooltipManager,
|
||||
IClientSimInteractiveLayerProvider interactiveLayerProvider,
|
||||
IClientSimPlayerCameraProvider cameraProvider,
|
||||
IClientSimMousePositionProvider mousePositionProvider,
|
||||
IClientSimInteractManager interactManager,
|
||||
IClientSimTrackingProvider trackingProvider,
|
||||
IClientSimPlayerStationManager stationManager)
|
||||
{
|
||||
_eventDispatcher = eventDispatcher;
|
||||
_input = input;
|
||||
_playerApiProvider = playerApiProvider;
|
||||
_highlightManager = highlightManager;
|
||||
_tooltipManager = tooltipManager;
|
||||
_interactiveLayerProvider = interactiveLayerProvider;
|
||||
_interactManager = interactManager;
|
||||
_trackingProvider = trackingProvider;
|
||||
_stationManager = stationManager;
|
||||
|
||||
leftHand.Initialize(_eventDispatcher, _input, trackingProvider, _playerApiProvider, pickupData);
|
||||
rightHand.Initialize(_eventDispatcher, _input, trackingProvider, _playerApiProvider, pickupData);
|
||||
|
||||
// Input will be null with incorrect Unity input project settings.
|
||||
_input?.SubscribeUse(UseInput);
|
||||
_eventDispatcher.Subscribe<ClientSimOnPlayerMovedEvent>(OnPlayerMoved);
|
||||
_eventDispatcher.Subscribe<ClientSimPlayerDeathStatusChangedEvent>(CombatStatusEvent);
|
||||
|
||||
// Create raycasters
|
||||
if (_trackingProvider.IsVR())
|
||||
{
|
||||
_leftHandRaycaster = new ClientSimRaycaster(
|
||||
new ClientSimTransformRayProvider(_trackingProvider.GetHandRaycastTransform(HandType.LEFT)),
|
||||
_interactiveLayerProvider,
|
||||
_interactManager);
|
||||
|
||||
_rightHandRaycaster = new ClientSimRaycaster(
|
||||
new ClientSimTransformRayProvider(_trackingProvider.GetHandRaycastTransform(HandType.RIGHT)),
|
||||
_interactiveLayerProvider,
|
||||
_interactManager);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Left hand is always null for desktop users.
|
||||
_leftHandRaycaster = null;
|
||||
|
||||
// Right hand is from the player's camera.
|
||||
_rightHandRaycaster = new ClientSimRaycaster(
|
||||
new ClientSimCameraRayProvider(cameraProvider, mousePositionProvider),
|
||||
_interactiveLayerProvider,
|
||||
_interactManager);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
{
|
||||
Dispose();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_input?.UnsubscribeUse(UseInput);
|
||||
_eventDispatcher.Unsubscribe<ClientSimOnPlayerMovedEvent>(OnPlayerMoved);
|
||||
_eventDispatcher.Unsubscribe<ClientSimPlayerDeathStatusChangedEvent>(CombatStatusEvent);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
UpdateHandPositions();
|
||||
|
||||
SetHoverLeft(_leftHandRaycaster?.CheckForInteracts());
|
||||
SetHoverRight(_rightHandRaycaster.CheckForInteracts());
|
||||
}
|
||||
|
||||
private void SetHoverLeft(ClientSimRaycastResults raycastResults)
|
||||
{
|
||||
SetHover(ref _hoverLeft, HandType.LEFT, leftHand, raycastResults);
|
||||
}
|
||||
|
||||
private void SetHoverRight(ClientSimRaycastResults raycastResults)
|
||||
{
|
||||
SetHover(ref _hoverRight, HandType.RIGHT, rightHand, raycastResults);
|
||||
}
|
||||
|
||||
private void SetHover(
|
||||
ref ClientSimRaycastResults handHover,
|
||||
HandType handType,
|
||||
ClientSimPlayerHand playerHand,
|
||||
ClientSimRaycastResults raycastResults)
|
||||
{
|
||||
// TODO optimize this to check if previous was the same as new and not disable/re-enable.
|
||||
if (handHover != null && handHover.interactable != null)
|
||||
{
|
||||
_highlightManager.DisableObjectHighlight(handHover.hitObject);
|
||||
_tooltipManager.DisableTooltip(handHover.interactable);
|
||||
}
|
||||
|
||||
raycastResults = FilterRaycastResults(raycastResults);
|
||||
|
||||
// If the player is not holding something, or the hit object is a UIShape, set the hover.
|
||||
// This allows players to interact with UI while still holding objects,
|
||||
// but holding objects will block pickups and interacts.
|
||||
if (!playerHand.IsHolding() || (raycastResults != null && raycastResults.uiShape != null))
|
||||
{
|
||||
handHover = raycastResults;
|
||||
}
|
||||
else
|
||||
{
|
||||
handHover = null;
|
||||
}
|
||||
|
||||
// Highlight the object if it has an interactable
|
||||
if (handHover != null && handHover.interactable != null)
|
||||
{
|
||||
_highlightManager.EnableObjectHighlight(handHover.hitObject);
|
||||
_tooltipManager.DisplayTooltip(handHover.interactable);
|
||||
}
|
||||
|
||||
// If the hovered object has a pickupable that can be interacted with, set that as this hand's hovered pickup.
|
||||
IClientSimPickupable pickupable = handHover?.GetPickupable();
|
||||
if (pickupable != null && !_interactManager.CanInteract(pickupable, handHover.distance))
|
||||
{
|
||||
pickupable = null;
|
||||
}
|
||||
playerHand.SetHoverPickupable(pickupable);
|
||||
|
||||
_eventDispatcher.SendEvent(new ClientSimRaycastHitResultsEvent
|
||||
{
|
||||
handType = handType,
|
||||
raycastResults = handHover
|
||||
});
|
||||
}
|
||||
|
||||
private ClientSimRaycastResults FilterRaycastResults(ClientSimRaycastResults results)
|
||||
{
|
||||
if (results == null || results.hitObject == null)
|
||||
{
|
||||
return results;
|
||||
}
|
||||
|
||||
// If the user is in a station with "CanUseStationFromStation" set to false, disable interacts
|
||||
// for any object that contains a station script.
|
||||
if (_stationManager.InStation()
|
||||
&& !_stationManager.GetCurrentStation().CanUseStationFromStation()
|
||||
&& results.hitObject.GetComponent<IClientSimStation>() != null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
private void TryInteract(HandType handType, ClientSimRaycastResults hover)
|
||||
{
|
||||
// Nothing to interact with.
|
||||
if (hover == null || hover.interactable == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Interact with the object and get the components that were interacted with.
|
||||
var interacts = _interactManager.Interact(hover.hitObject, hover.distance);
|
||||
|
||||
// Notify ClientSim of interacted objects.
|
||||
_eventDispatcher.SendEvent(new ClientSimInteractEvent
|
||||
{
|
||||
handType = handType,
|
||||
interactObject = hover.hitObject,
|
||||
interactDistance = hover.distance,
|
||||
interacts = interacts,
|
||||
});
|
||||
}
|
||||
|
||||
private void UpdateHandPositions()
|
||||
{
|
||||
// TODO do not update hands if player controller is stuck in a collider.
|
||||
// Note that these hands are only for pickups and that raycast logic uses the tracking provider hands.
|
||||
|
||||
var leftHandData = _trackingProvider.GetTrackingData(VRCPlayerApi.TrackingDataType.LeftHand);
|
||||
leftHand.transform.SetPositionAndRotation(leftHandData.position, leftHandData.rotation);
|
||||
leftHand.UpdatePosition();
|
||||
|
||||
var rightHandData = _trackingProvider.GetTrackingData(VRCPlayerApi.TrackingDataType.RightHand);
|
||||
rightHand.transform.SetPositionAndRotation(rightHandData.position, rightHandData.rotation);
|
||||
rightHand.UpdatePosition();
|
||||
}
|
||||
|
||||
#region ClientSim Input
|
||||
|
||||
private void UseInput(bool value, HandType hand)
|
||||
{
|
||||
if (!value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (hand == HandType.LEFT)
|
||||
{
|
||||
TryInteract(HandType.LEFT, _hoverLeft);
|
||||
}
|
||||
if (hand == HandType.RIGHT)
|
||||
{
|
||||
TryInteract(HandType.RIGHT, _hoverRight);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ClientSim Events
|
||||
|
||||
private void OnPlayerMoved(ClientSimOnPlayerMovedEvent moveEvent)
|
||||
{
|
||||
UpdateHandPositions();
|
||||
}
|
||||
|
||||
private void CombatStatusEvent(ClientSimPlayerDeathStatusChangedEvent combatStatusEvent)
|
||||
{
|
||||
leftHand.ForceDrop();
|
||||
rightHand.ForceDrop();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user