using CameraSystem; using MMMaellon.LightSync; using UdonSharp; using UnityEngine; using VRC.SDKBase; using VRC.Udon.Common; [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; [UdonSynced] private int _AttachedCameraIndex = -1; private Camera _AttachedCamera = null; private string[] _FollowedPlayerNames = new string[0]; 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++) { Vector3 LeftEyePosition = _FollowedPlayers[i].GetBonePosition(HumanBodyBones.LeftEye); Vector3 RightEyePosition = _FollowedPlayers[i].GetBonePosition(HumanBodyBones.RightEye); Vector3 LeftFootPosition = _FollowedPlayers[i].GetBonePosition(HumanBodyBones.LeftFoot); Vector3 RightFootPosition = _FollowedPlayers[i].GetBonePosition(HumanBodyBones.RightFoot); CentroidSum += new Vector3( (LeftEyePosition.x + RightEyePosition.x + LeftFootPosition.x + RightFootPosition.x) / 4.0f, (LeftEyePosition.y + RightEyePosition.y + LeftFootPosition.y + RightFootPosition.y) / 4.0f, (LeftEyePosition.z + RightEyePosition.z + LeftFootPosition.z + RightFootPosition.z) / 4.0f); } 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; } } 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.transform.parent = CameraRoot; _AttachedCamera.fieldOfView = FOV; _AttachedCamera.nearClipPlane = NearClippingPlane; _AttachedCamera.farClipPlane = FarClippingPlane; _AttachedCamera.GetComponent().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 i = 0; foreach (string PlayerName in _FollowedPlayerNames) { foreach (VRCPlayerApi Player in AllPlayers) { if (Player.displayName == PlayerName) { _FollowedPlayers[i] = Player; i++; break; } } } Debug.Log("[CameraAnchor] Anchor " + gameObject.name + " is now following " + _FollowedPlayerNames); _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 }