250 lines
6.3 KiB
C#
250 lines
6.3 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using BlockSpace.Voxels;
|
|
using Photon.VR.Player;
|
|
using UnityEngine;
|
|
|
|
[DisallowMultipleComponent]
|
|
public sealed class TriggerBehavior : NodeBehavior
|
|
{
|
|
private const float PlayerTouchRadius = 0.12f;
|
|
|
|
private const float StandingRayLift = 0.15f;
|
|
|
|
private const float StandingRayDistance = 1.5f;
|
|
|
|
private const float PlayerCacheRefreshInterval = 0.25f;
|
|
|
|
private static readonly Collider[] TouchOverlapBuffer = new Collider[16];
|
|
|
|
private readonly HashSet<int> touchingPlayerIds = new HashSet<int>();
|
|
|
|
private readonly HashSet<int> currentFramePlayerIds = new HashSet<int>();
|
|
|
|
private readonly List<PhotonVRPlayer> cachedPlayers = new List<PhotonVRPlayer>();
|
|
|
|
private VoxelGroup targetGroup;
|
|
|
|
private TriggerTargetCollisionRelay collisionRelay;
|
|
|
|
private float nextPlayerCacheRefreshAt;
|
|
|
|
private void Start()
|
|
{
|
|
if (base.IsAuthoritative)
|
|
{
|
|
RefreshTargetGroup(force: true);
|
|
}
|
|
}
|
|
|
|
private void OnDisable()
|
|
{
|
|
UnbindCollisionRelay();
|
|
touchingPlayerIds.Clear();
|
|
currentFramePlayerIds.Clear();
|
|
cachedPlayers.Clear();
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (base.IsAuthoritative)
|
|
{
|
|
RefreshTargetGroup(force: false);
|
|
PollPlayerTouches();
|
|
}
|
|
}
|
|
|
|
public override void OnInputValueChanged(string inputPortName, BSValue value)
|
|
{
|
|
if (base.IsAuthoritative && string.Equals(inputPortName, "Object", StringComparison.Ordinal))
|
|
{
|
|
RefreshTargetGroup(force: true);
|
|
}
|
|
}
|
|
|
|
private void RefreshTargetGroup(bool force)
|
|
{
|
|
VoxelGroup inputGroup = GetInputGroup("Object");
|
|
if (!force && targetGroup == inputGroup)
|
|
{
|
|
return;
|
|
}
|
|
if (collisionRelay != null)
|
|
{
|
|
collisionRelay.ColliderEntered -= HandleColliderEntered;
|
|
}
|
|
targetGroup = inputGroup;
|
|
touchingPlayerIds.Clear();
|
|
currentFramePlayerIds.Clear();
|
|
collisionRelay = null;
|
|
if (targetGroup == null)
|
|
{
|
|
SetOutputValueIfChanged("Player", BSValue.None);
|
|
SetOutputValueIfChanged("Object", BSValue.None);
|
|
return;
|
|
}
|
|
collisionRelay = targetGroup.GetComponent<TriggerTargetCollisionRelay>();
|
|
if (collisionRelay == null)
|
|
{
|
|
collisionRelay = targetGroup.gameObject.AddComponent<TriggerTargetCollisionRelay>();
|
|
}
|
|
collisionRelay.ColliderEntered += HandleColliderEntered;
|
|
}
|
|
|
|
private void UnbindCollisionRelay()
|
|
{
|
|
if (collisionRelay != null)
|
|
{
|
|
collisionRelay.ColliderEntered -= HandleColliderEntered;
|
|
collisionRelay = null;
|
|
}
|
|
}
|
|
|
|
private void PollPlayerTouches()
|
|
{
|
|
if (targetGroup == null)
|
|
{
|
|
touchingPlayerIds.Clear();
|
|
return;
|
|
}
|
|
if (Time.unscaledTime >= nextPlayerCacheRefreshAt)
|
|
{
|
|
RefreshPlayerCache();
|
|
nextPlayerCacheRefreshAt = Time.unscaledTime + 0.25f;
|
|
}
|
|
currentFramePlayerIds.Clear();
|
|
for (int i = 0; i < cachedPlayers.Count; i++)
|
|
{
|
|
PhotonVRPlayer photonVRPlayer = cachedPlayers[i];
|
|
if (photonVRPlayer == null || !photonVRPlayer.gameObject.activeInHierarchy)
|
|
{
|
|
continue;
|
|
}
|
|
int playerId = GetPlayerId(photonVRPlayer);
|
|
if (playerId != 0 && IsPlayerTouchingTarget(photonVRPlayer, targetGroup))
|
|
{
|
|
currentFramePlayerIds.Add(playerId);
|
|
if (!touchingPlayerIds.Contains(playerId))
|
|
{
|
|
touchingPlayerIds.Add(playerId);
|
|
SetOutputValueIfChanged("Player", BSValue.FromPlayer(playerId));
|
|
TriggerOutput("Touched");
|
|
}
|
|
}
|
|
}
|
|
touchingPlayerIds.RemoveWhere((int item) => !currentFramePlayerIds.Contains(item));
|
|
}
|
|
|
|
private void HandleColliderEntered(Collider other)
|
|
{
|
|
if (!base.IsAuthoritative || targetGroup == null || other == null || TryResolvePlayer(other, out var _))
|
|
{
|
|
return;
|
|
}
|
|
VoxelGroup componentInParent = other.GetComponentInParent<VoxelGroup>();
|
|
if (!(componentInParent == targetGroup))
|
|
{
|
|
if (componentInParent != null)
|
|
{
|
|
SetOutputValueIfChanged("Object", BSValue.FromGroup(componentInParent.id));
|
|
}
|
|
else
|
|
{
|
|
SetOutputValueIfChanged("Object", BSValue.None);
|
|
}
|
|
TriggerOutput("Object Touched");
|
|
}
|
|
}
|
|
|
|
private void RefreshPlayerCache()
|
|
{
|
|
cachedPlayers.Clear();
|
|
PhotonVRPlayer[] array = UnityEngine.Object.FindObjectsByType<PhotonVRPlayer>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
|
for (int i = 0; i < array.Length; i++)
|
|
{
|
|
if (array[i] != null)
|
|
{
|
|
cachedPlayers.Add(array[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static bool IsPlayerTouchingTarget(PhotonVRPlayer player, VoxelGroup group)
|
|
{
|
|
if (player == null || group == null)
|
|
{
|
|
return false;
|
|
}
|
|
Collider collider = ((group.meshCollider != null && group.meshCollider.enabled) ? group.meshCollider : group.GetComponent<Collider>());
|
|
if (collider == null || !collider.enabled)
|
|
{
|
|
return false;
|
|
}
|
|
if (IsPointTouchingCollider(collider, player.Head, 0.12f) || IsPointTouchingCollider(collider, player.LeftHand, 0.12f) || IsPointTouchingCollider(collider, player.RightHand, 0.12f))
|
|
{
|
|
return true;
|
|
}
|
|
return IsStandingOnCollider(collider, player);
|
|
}
|
|
|
|
private static bool IsPointTouchingCollider(Collider targetCollider, Transform pointTransform, float radius)
|
|
{
|
|
if (targetCollider == null || pointTransform == null)
|
|
{
|
|
return false;
|
|
}
|
|
Vector3 position = pointTransform.position;
|
|
float num = radius * radius;
|
|
if (targetCollider.bounds.SqrDistance(position) > num)
|
|
{
|
|
return false;
|
|
}
|
|
int num2 = Physics.OverlapSphereNonAlloc(position, radius, TouchOverlapBuffer, -1, QueryTriggerInteraction.Collide);
|
|
for (int i = 0; i < num2; i++)
|
|
{
|
|
if (TouchOverlapBuffer[i] == targetCollider)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static bool IsStandingOnCollider(Collider targetCollider, PhotonVRPlayer player)
|
|
{
|
|
if (targetCollider == null || player == null)
|
|
{
|
|
return false;
|
|
}
|
|
Transform transform = ((player.Body != null) ? player.Body : player.Head);
|
|
if (transform == null)
|
|
{
|
|
return false;
|
|
}
|
|
if (!Physics.Raycast(new Ray(transform.position + Vector3.up * 0.15f, Vector3.down), out var hitInfo, 1.5f, -5, QueryTriggerInteraction.Ignore))
|
|
{
|
|
return false;
|
|
}
|
|
if (hitInfo.collider == targetCollider && hitInfo.distance > 0.05f)
|
|
{
|
|
return hitInfo.normal.y >= 0.35f;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private static bool TryResolvePlayer(Collider collider, out PhotonVRPlayer player)
|
|
{
|
|
player = ((collider != null) ? collider.GetComponentInParent<PhotonVRPlayer>() : null);
|
|
return player != null;
|
|
}
|
|
|
|
private static int GetPlayerId(PhotonVRPlayer player)
|
|
{
|
|
if (!(player != null) || !(player.photonView != null) || player.photonView.Owner == null)
|
|
{
|
|
return 0;
|
|
}
|
|
return player.photonView.Owner.ActorNumber;
|
|
}
|
|
}
|