308 lines
8.5 KiB
C#
308 lines
8.5 KiB
C#
|
|
using CameraSystem;
|
|
using MMMaellon.LightSync;
|
|
using System.Linq;
|
|
using UdonSharp;
|
|
using UnityEngine;
|
|
using VRC.SDKBase;
|
|
using VRC.Udon.Common;
|
|
|
|
|
|
public enum PossibleFollowMethods
|
|
{
|
|
CloseUp,
|
|
HeadAndShoulders,
|
|
HipsUp,
|
|
HipsDown,
|
|
FullBody
|
|
};
|
|
|
|
|
|
[UdonBehaviourSyncMode(BehaviourSyncMode.Manual)]
|
|
public class CameraAnchor : UdonSharpBehaviour
|
|
{
|
|
|
|
[SerializeField] private CameraSystem_Console CameraSystemManager;
|
|
[Space]
|
|
[SerializeField] private float FOV = 60.0f;
|
|
[SerializeField] private float NearClippingPlane = 0.3f;
|
|
[SerializeField] private float FarClippingPlane = 1000.0f;
|
|
[Space]
|
|
[SerializeField] private Transform CameraRoot;
|
|
[Tooltip("Changing the Z scale of this object will change the FOV of the attached camera.")]
|
|
[SerializeField] private Transform CameraFOVScaler;
|
|
[Space]
|
|
[Tooltip("How the camera should follow players, if it's set to do so.")]
|
|
[SerializeField] private PossibleFollowMethods FollowMethod;
|
|
|
|
[UdonSynced] private int _AttachedCameraIndex = -1;
|
|
|
|
[UdonSynced] private string[] _FollowedPlayerNames = new string[0];
|
|
|
|
private Camera _AttachedCamera = null;
|
|
private string[] _FollowedPlayerNames_Cache = new string[0];
|
|
private VRCPlayerApi[] _FollowedPlayers = new VRCPlayerApi[0];
|
|
private float _CameraFollowSpeed = 1.0f;
|
|
|
|
|
|
void Update()
|
|
{
|
|
if (_AttachedCamera)
|
|
{
|
|
if (_AttachedCamera.transform.parent != CameraRoot)
|
|
{
|
|
Debug.Log("[CameraAnchor] Camera has been detached from " + gameObject.name + ".");
|
|
_AttachedCamera = null;
|
|
_AttachedCameraIndex = -1;
|
|
RequestSerialization();
|
|
}
|
|
else
|
|
{
|
|
_AttachedCamera.fieldOfView = FOV * CameraFOVScaler.transform.localScale.z;
|
|
}
|
|
}
|
|
|
|
if (_FollowedPlayers.Length > 0)
|
|
{
|
|
Vector3 CentroidSum = Vector3.zero;
|
|
for (int i = 0; i < _FollowedPlayers.Length; i++)
|
|
{
|
|
if (_FollowedPlayers[i] == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Vector3[] BonePositions = _BonesFromFollowMethod(_FollowedPlayers[i]);
|
|
Vector3 CentroidPointsAverage = Vector3.zero;
|
|
for (int j = 0; j < BonePositions.Length; j++)
|
|
{
|
|
CentroidPointsAverage += BonePositions[j];
|
|
}
|
|
CentroidPointsAverage /= BonePositions.Length;
|
|
CentroidSum += CentroidPointsAverage;
|
|
}
|
|
Vector3 CentroidAverage = CentroidSum / _FollowedPlayers.Length;
|
|
|
|
Vector3 LookDirection = (CentroidAverage - transform.position).normalized;
|
|
CameraRoot.transform.rotation = Quaternion.LookRotation(
|
|
Vector3.Lerp(
|
|
CameraRoot.transform.forward,
|
|
Vector3.RotateTowards(CameraRoot.transform.forward, LookDirection, 10.0f, 0.0f),
|
|
_CameraFollowSpeed / 10.0f)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
CameraRoot.transform.localRotation = Quaternion.identity;
|
|
}
|
|
}
|
|
|
|
private Vector3[] _BonesFromFollowMethod(VRCPlayerApi Player)
|
|
{
|
|
switch(FollowMethod)
|
|
{
|
|
case PossibleFollowMethods.CloseUp:
|
|
{
|
|
Vector3[] BonesToTrack = new Vector3[3];
|
|
BonesToTrack[0] = Player.GetBonePosition(HumanBodyBones.LeftEye);
|
|
BonesToTrack[1] = Player.GetBonePosition(HumanBodyBones.RightEye);
|
|
BonesToTrack[2] = Player.GetBonePosition(HumanBodyBones.Neck);
|
|
return BonesToTrack;
|
|
}
|
|
case PossibleFollowMethods.HeadAndShoulders:
|
|
{
|
|
Vector3[] BonesToTrack = new Vector3[4];
|
|
BonesToTrack[0] = Player.GetBonePosition(HumanBodyBones.LeftEye);
|
|
BonesToTrack[1] = Player.GetBonePosition(HumanBodyBones.RightEye);
|
|
BonesToTrack[2] = Player.GetBonePosition(HumanBodyBones.LeftShoulder);
|
|
BonesToTrack[3] = Player.GetBonePosition(HumanBodyBones.RightShoulder);
|
|
return BonesToTrack;
|
|
}
|
|
case PossibleFollowMethods.HipsUp:
|
|
{
|
|
Vector3[] BonesToTrack = new Vector3[4];
|
|
BonesToTrack[0] = Player.GetBonePosition(HumanBodyBones.LeftEye);
|
|
BonesToTrack[1] = Player.GetBonePosition(HumanBodyBones.RightEye);
|
|
BonesToTrack[2] = Player.GetBonePosition(HumanBodyBones.LeftUpperLeg);
|
|
BonesToTrack[3] = Player.GetBonePosition(HumanBodyBones.RightUpperLeg);
|
|
return BonesToTrack;
|
|
}
|
|
case PossibleFollowMethods.HipsDown:
|
|
{
|
|
Vector3[] BonesToTrack = new Vector3[4];
|
|
BonesToTrack[0] = Player.GetBonePosition(HumanBodyBones.LeftUpperLeg);
|
|
BonesToTrack[1] = Player.GetBonePosition(HumanBodyBones.RightUpperLeg);
|
|
BonesToTrack[2] = Player.GetBonePosition(HumanBodyBones.LeftFoot);
|
|
BonesToTrack[3] = Player.GetBonePosition(HumanBodyBones.RightFoot);
|
|
return BonesToTrack;
|
|
}
|
|
case PossibleFollowMethods.FullBody:
|
|
{
|
|
Vector3[] BonesToTrack = new Vector3[4];
|
|
BonesToTrack[0] = Player.GetBonePosition(HumanBodyBones.LeftEye);
|
|
BonesToTrack[1] = Player.GetBonePosition(HumanBodyBones.RightEye);
|
|
BonesToTrack[2] = Player.GetBonePosition(HumanBodyBones.LeftFoot);
|
|
BonesToTrack[3] = Player.GetBonePosition(HumanBodyBones.RightFoot);
|
|
return BonesToTrack;
|
|
}
|
|
default:
|
|
{
|
|
Vector3[] BonesToTrack = new Vector3[1];
|
|
BonesToTrack[0] = Player.GetPosition();
|
|
return BonesToTrack;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public override void OnDeserialization(DeserializationResult Result)
|
|
{
|
|
_AttachCamera_Synced();
|
|
_FollowPlayers_Synced();
|
|
|
|
base.OnDeserialization(Result);
|
|
}
|
|
|
|
public override void OnPlayerLeft(VRCPlayerApi LeavingPlayer)
|
|
{
|
|
//foreach (VRCPlayerApi FollowedPlayer in _FollowedPlayers)
|
|
//{
|
|
// if (FollowedPlayer.displayName == LeavingPlayer.displayName)
|
|
// {
|
|
|
|
// }
|
|
//}
|
|
|
|
base.OnPlayerLeft(LeavingPlayer);
|
|
}
|
|
|
|
|
|
public void AttachCamera(Camera CameraComponent)
|
|
{
|
|
if (CameraSystemManager == null)
|
|
{
|
|
Debug.LogError("[CameraAnchor] No CameraSystemManager set for " + gameObject.name + "; can't find a camera without it");
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < CameraSystemManager.camerasObjects.Length; i++)
|
|
{
|
|
if (CameraSystemManager.camerasObjects[i] == CameraComponent)
|
|
{
|
|
Debug.Log("[CameraAnchor] Attaching camera " + i + " to anchor " + gameObject.name);
|
|
_AttachedCameraIndex = i;
|
|
_AttachCamera_Synced();
|
|
RequestSerialization();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void FollowPlayers(string[] Players, float FollowSpeed = 1.0f)
|
|
{
|
|
_FollowedPlayerNames = Players;
|
|
_CameraFollowSpeed = FollowSpeed;
|
|
_FollowPlayers_Synced();
|
|
RequestSerialization();
|
|
}
|
|
|
|
public void StopFollowingPlayers()
|
|
{
|
|
_FollowedPlayerNames = new string[0];
|
|
_FollowPlayers_Synced();
|
|
RequestSerialization();
|
|
}
|
|
|
|
|
|
private void _AttachCamera_Synced()
|
|
{
|
|
if (_AttachedCameraIndex >= 0)
|
|
{
|
|
Camera NewCamera = CameraSystemManager.camerasObjects[_AttachedCameraIndex];
|
|
if (NewCamera != _AttachedCamera)
|
|
{
|
|
_AttachedCamera = NewCamera;
|
|
_AttachedCamera.gameObject.SetActive(true);
|
|
_AttachedCamera.fieldOfView = FOV;
|
|
_AttachedCamera.nearClipPlane = NearClippingPlane;
|
|
_AttachedCamera.farClipPlane = FarClippingPlane;
|
|
_AttachedCamera.transform.parent = CameraRoot;
|
|
_AttachedCamera.GetComponent<LightSync>().TeleportToLocalSpace(Vector3.zero, Quaternion.identity, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void _FollowPlayers_Synced()
|
|
{
|
|
if (_IsPlayerListDifferent())
|
|
{
|
|
_FollowedPlayers = new VRCPlayerApi[_FollowedPlayerNames.Length];
|
|
VRCPlayerApi[] AllPlayers = new VRCPlayerApi[VRCPlayerApi.GetPlayerCount()];
|
|
VRCPlayerApi.GetPlayers(AllPlayers);
|
|
|
|
int NumValidPlayers = 0;
|
|
for (int i = 0; i < _FollowedPlayerNames.Length; i++)
|
|
{
|
|
string PlayerName = _FollowedPlayerNames[i];
|
|
Debug.Log("[CameraAnchor] Trying to find player " + PlayerName + "...");
|
|
|
|
_FollowedPlayers[i] = null;
|
|
foreach (VRCPlayerApi Player in AllPlayers)
|
|
{
|
|
if (Player.displayName == PlayerName)
|
|
{
|
|
Debug.Log("[CameraAnchor] Found player " + PlayerName);
|
|
_FollowedPlayers[i] = Player;
|
|
NumValidPlayers++;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
Debug.Log("[CameraAnchor] Found " + NumValidPlayers + " players out of a requested " + _FollowedPlayerNames.Length);
|
|
|
|
int ValidPlayerCounter = 0;
|
|
VRCPlayerApi[] ValidPlayers = new VRCPlayerApi[NumValidPlayers];
|
|
foreach (VRCPlayerApi Player in _FollowedPlayers)
|
|
{
|
|
if (Player != null)
|
|
{
|
|
ValidPlayers[ValidPlayerCounter] = Player;
|
|
ValidPlayerCounter++;
|
|
}
|
|
}
|
|
|
|
string PlayerNamesString = string.Join(", ", _FollowedPlayerNames);
|
|
Debug.Log("[CameraAnchor] Anchor " + gameObject.name + " is now following { " + PlayerNamesString + " }");
|
|
|
|
_FollowedPlayerNames_Cache = _FollowedPlayerNames;
|
|
}
|
|
}
|
|
|
|
|
|
private bool _IsPlayerListDifferent()
|
|
{
|
|
if (_FollowedPlayerNames.Length != _FollowedPlayerNames_Cache.Length)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
for (int i = 0; i < _FollowedPlayerNames.Length; i++)
|
|
{
|
|
if (_FollowedPlayerNames[i] != _FollowedPlayerNames_Cache[i])
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
#if UNITY_EDITOR
|
|
private void OnDrawGizmos()
|
|
{
|
|
Gizmos.DrawIcon(transform.position, "CameraAnchor", true);
|
|
}
|
|
#endif
|
|
}
|