- Camera follower will now track the upper-midsections of VRChat players.

- Camera tracking now has a follow speed setting.
- Camera anchor FOV scaler is now a separate object.
- Camera overlays now overlay properly once again.
- Implemented a much better check for whether a camera was detached.
- Improved code that detects when a game is over, and why it ended.
- Tweaked ending camera animation timing.
This commit is contained in:
2026-03-21 01:02:39 -04:00
parent 6b376463ba
commit 0b6a8440fc
8 changed files with 423 additions and 166 deletions
+185 -83
View File
@@ -44,7 +44,7 @@ MonoBehaviour:
Data:
- Name:
Entry: 12
Data: 10
Data: 12
- Name:
Entry: 7
Data:
@@ -329,7 +329,7 @@ MonoBehaviour:
Data: 20|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
- Name:
Entry: 12
Data: 3
Data: 2
- Name:
Entry: 7
Data: 21|UnityEngine.SpaceAttribute, UnityEngine.CoreModule
@@ -341,7 +341,61 @@ MonoBehaviour:
Data:
- Name:
Entry: 7
Data: 22|UnityEngine.TooltipAttribute, UnityEngine.CoreModule
Data: 22|UnityEngine.SerializeField, UnityEngine.CoreModule
- Name:
Entry: 8
Data:
- Name:
Entry: 13
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 7
Data:
- Name: $k
Entry: 1
Data: CameraFOVScaler
- Name: $v
Entry: 7
Data: 23|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
- Name: <Name>k__BackingField
Entry: 1
Data: CameraFOVScaler
- Name: <UserType>k__BackingField
Entry: 9
Data: 19
- Name: <SystemType>k__BackingField
Entry: 9
Data: 19
- Name: <SyncMode>k__BackingField
Entry: 7
Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib
- Name:
Entry: 6
Data:
- Name:
Entry: 8
Data:
- Name: <IsSerialized>k__BackingField
Entry: 5
Data: true
- Name: _fieldAttributes
Entry: 7
Data: 24|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
- Name:
Entry: 12
Data: 2
- Name:
Entry: 7
Data: 25|UnityEngine.TooltipAttribute, UnityEngine.CoreModule
- Name: tooltip
Entry: 1
Data: Changing the Z scale of this object will change the FOV of the attached
@@ -351,7 +405,7 @@ MonoBehaviour:
Data:
- Name:
Entry: 7
Data: 23|UnityEngine.SerializeField, UnityEngine.CoreModule
Data: 26|UnityEngine.SerializeField, UnityEngine.CoreModule
- Name:
Entry: 8
Data:
@@ -375,13 +429,13 @@ MonoBehaviour:
Data: _AttachedCameraIndex
- Name: $v
Entry: 7
Data: 24|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
Data: 27|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
- Name: <Name>k__BackingField
Entry: 1
Data: _AttachedCameraIndex
- Name: <UserType>k__BackingField
Entry: 7
Data: 25|System.RuntimeType, mscorlib
Data: 28|System.RuntimeType, mscorlib
- Name:
Entry: 1
Data: System.Int32, mscorlib
@@ -390,7 +444,7 @@ MonoBehaviour:
Data:
- Name: <SystemType>k__BackingField
Entry: 9
Data: 25
Data: 28
- Name: <SyncMode>k__BackingField
Entry: 7
Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib
@@ -405,13 +459,13 @@ MonoBehaviour:
Data: false
- Name: _fieldAttributes
Entry: 7
Data: 26|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
Data: 29|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
- Name:
Entry: 12
Data: 1
- Name:
Entry: 7
Data: 27|UdonSharp.UdonSyncedAttribute, UdonSharp.Runtime
Data: 30|UdonSharp.UdonSyncedAttribute, UdonSharp.Runtime
- Name:
Entry: 8
Data:
@@ -433,72 +487,18 @@ MonoBehaviour:
- Name: $k
Entry: 1
Data: _AttachedCamera
- Name: $v
Entry: 7
Data: 28|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
- Name: <Name>k__BackingField
Entry: 1
Data: _AttachedCamera
- Name: <UserType>k__BackingField
Entry: 7
Data: 29|System.RuntimeType, mscorlib
- Name:
Entry: 1
Data: UnityEngine.Camera, UnityEngine.CoreModule
- Name:
Entry: 8
Data:
- Name: <SystemType>k__BackingField
Entry: 9
Data: 29
- Name: <SyncMode>k__BackingField
Entry: 7
Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib
- Name:
Entry: 6
Data:
- Name:
Entry: 8
Data:
- Name: <IsSerialized>k__BackingField
Entry: 5
Data: false
- Name: _fieldAttributes
Entry: 7
Data: 30|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
- Name:
Entry: 12
Data: 0
- Name:
Entry: 13
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 7
Data:
- Name: $k
Entry: 1
Data: _FollowedPlayerNames
- Name: $v
Entry: 7
Data: 31|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
- Name: <Name>k__BackingField
Entry: 1
Data: _FollowedPlayerNames
Data: _AttachedCamera
- Name: <UserType>k__BackingField
Entry: 7
Data: 32|System.RuntimeType, mscorlib
- Name:
Entry: 1
Data: System.String[], mscorlib
Data: UnityEngine.Camera, UnityEngine.CoreModule
- Name:
Entry: 8
Data:
@@ -540,19 +540,25 @@ MonoBehaviour:
Data:
- Name: $k
Entry: 1
Data: _FollowedPlayerNames_Cache
Data: _FollowedPlayerNames
- Name: $v
Entry: 7
Data: 34|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
- Name: <Name>k__BackingField
Entry: 1
Data: _FollowedPlayerNames_Cache
Data: _FollowedPlayerNames
- Name: <UserType>k__BackingField
Entry: 9
Data: 32
Entry: 7
Data: 35|System.RuntimeType, mscorlib
- Name:
Entry: 1
Data: System.String[], mscorlib
- Name:
Entry: 8
Data:
- Name: <SystemType>k__BackingField
Entry: 9
Data: 32
Data: 35
- Name: <SyncMode>k__BackingField
Entry: 7
Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib
@@ -567,7 +573,7 @@ MonoBehaviour:
Data: false
- Name: _fieldAttributes
Entry: 7
Data: 35|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
Data: 36|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
- Name:
Entry: 12
Data: 0
@@ -588,25 +594,19 @@ MonoBehaviour:
Data:
- Name: $k
Entry: 1
Data: _FollowedPlayers
Data: _FollowedPlayerNames_Cache
- Name: $v
Entry: 7
Data: 36|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
Data: 37|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
- Name: <Name>k__BackingField
Entry: 1
Data: _FollowedPlayers
Data: _FollowedPlayerNames_Cache
- Name: <UserType>k__BackingField
Entry: 7
Data: 37|System.RuntimeType, mscorlib
- Name:
Entry: 1
Data: VRC.SDKBase.VRCPlayerApi[], VRCSDKBase
- Name:
Entry: 8
Data:
Entry: 9
Data: 35
- Name: <SystemType>k__BackingField
Entry: 9
Data: 37
Data: 35
- Name: <SyncMode>k__BackingField
Entry: 7
Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib
@@ -637,6 +637,108 @@ MonoBehaviour:
- Name:
Entry: 8
Data:
- Name:
Entry: 7
Data:
- Name: $k
Entry: 1
Data: _FollowedPlayers
- Name: $v
Entry: 7
Data: 39|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
- Name: <Name>k__BackingField
Entry: 1
Data: _FollowedPlayers
- Name: <UserType>k__BackingField
Entry: 7
Data: 40|System.RuntimeType, mscorlib
- Name:
Entry: 1
Data: VRC.SDKBase.VRCPlayerApi[], VRCSDKBase
- Name:
Entry: 8
Data:
- Name: <SystemType>k__BackingField
Entry: 9
Data: 40
- Name: <SyncMode>k__BackingField
Entry: 7
Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib
- Name:
Entry: 6
Data:
- Name:
Entry: 8
Data:
- Name: <IsSerialized>k__BackingField
Entry: 5
Data: false
- Name: _fieldAttributes
Entry: 7
Data: 41|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
- Name:
Entry: 12
Data: 0
- Name:
Entry: 13
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 7
Data:
- Name: $k
Entry: 1
Data: _CameraFollowSpeed
- Name: $v
Entry: 7
Data: 42|UdonSharp.Compiler.FieldDefinition, UdonSharp.Editor
- Name: <Name>k__BackingField
Entry: 1
Data: _CameraFollowSpeed
- Name: <UserType>k__BackingField
Entry: 9
Data: 8
- Name: <SystemType>k__BackingField
Entry: 9
Data: 8
- Name: <SyncMode>k__BackingField
Entry: 7
Data: System.Nullable`1[[UdonSharp.UdonSyncMode, UdonSharp.Runtime]], mscorlib
- Name:
Entry: 6
Data:
- Name:
Entry: 8
Data:
- Name: <IsSerialized>k__BackingField
Entry: 5
Data: false
- Name: _fieldAttributes
Entry: 7
Data: 43|System.Collections.Generic.List`1[[System.Attribute, mscorlib]], mscorlib
- Name:
Entry: 12
Data: 0
- Name:
Entry: 13
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 8
Data:
- Name:
Entry: 13
Data:
+45 -16
View File
@@ -2,7 +2,6 @@
using CameraSystem;
using MMMaellon.LightSync;
using UdonSharp;
using UnityEditor.Overlays;
using UnityEngine;
using VRC.SDKBase;
using VRC.Udon.Common;
@@ -17,8 +16,9 @@ public class CameraAnchor : UdonSharpBehaviour
[SerializeField] private float NearClippingPlane = 0.3f;
[SerializeField] private float FarClippingPlane = 1000.0f;
[Space]
[Tooltip("Changing the Z scale of this object will change the FOV of the attached camera.")]
[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;
@@ -26,19 +26,23 @@ public class CameraAnchor : UdonSharpBehaviour
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 LateUpdate()
void Update()
{
if (_AttachedCamera)
{
if (CameraRoot.childCount <= 0)
if (_AttachedCamera.transform.parent != CameraRoot)
{
Debug.Log("[CameraAnchor] Camera has been detached from " + gameObject.name + ".");
_AttachedCamera = null;
_AttachedCameraIndex = -1;
RequestSerialization();
}
else
{
_AttachedCamera.fieldOfView = FOV * CameraRoot.transform.localScale.z;
_AttachedCamera.fieldOfView = FOV * CameraFOVScaler.transform.localScale.z;
}
}
@@ -57,8 +61,15 @@ public class CameraAnchor : UdonSharpBehaviour
(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;
CameraRoot.LookAt(CentroidSum / _FollowedPlayers.Length, Vector3.up);
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
{
@@ -66,6 +77,7 @@ public class CameraAnchor : UdonSharpBehaviour
}
}
public override void OnDeserialization(DeserializationResult Result)
{
_AttachCamera_Synced();
@@ -74,6 +86,19 @@ public class CameraAnchor : UdonSharpBehaviour
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)
{
@@ -96,9 +121,10 @@ public class CameraAnchor : UdonSharpBehaviour
}
}
public void FollowPlayers(string[] Players)
public void FollowPlayers(string[] Players, float FollowSpeed = 1.0f)
{
_FollowedPlayerNames = Players;
_CameraFollowSpeed = FollowSpeed;
_FollowPlayers_Synced();
RequestSerialization();
}
@@ -113,16 +139,19 @@ public class CameraAnchor : UdonSharpBehaviour
private void _AttachCamera_Synced()
{
Camera NewCamera = CameraSystemManager.camerasObjects[_AttachedCameraIndex];
if (NewCamera != _AttachedCamera)
if (_AttachedCameraIndex >= 0)
{
_AttachedCamera = NewCamera;
_AttachedCamera.gameObject.SetActive(true);
_AttachedCamera.transform.parent = CameraRoot;
_AttachedCamera.fieldOfView = FOV;
_AttachedCamera.nearClipPlane = NearClippingPlane;
_AttachedCamera.farClipPlane = FarClippingPlane;
_AttachedCamera.GetComponent<LightSync>().TeleportToLocalSpace(Vector3.zero, Quaternion.identity, true);
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<LightSync>().TeleportToLocalSpace(Vector3.zero, Quaternion.identity, true);
}
}
}
@@ -117,18 +117,18 @@ public class CameraControllerRound3 : CameraControllerBase
}
public void OverheadCamera_FollowPlayers(string[] Players)
public void OverheadCamera_FollowPlayers(string[] Players, float FollowSpeed = 1.0f)
{
OverheadCameraAnchor.FollowPlayers(Players);
OverheadCameraAnchor.FollowPlayers(Players, FollowSpeed);
}
public void OverheadCamera_StopFollowingPlayers()
{
OverheadCameraAnchor.StopFollowingPlayers();
}
public void FrontCamera_FollowPlayers(string[] Players)
public void FrontCamera_FollowPlayers(string[] Players, float FollowSpeed = 1.0f)
{
FrontCameraAnchor.FollowPlayers(Players);
FrontCameraAnchor.FollowPlayers(Players, FollowSpeed);
}
public void FrontCamera_StopFollowingPlayers()
{
@@ -1,7 +1,6 @@
using UdonSharp;
using UnityEngine;
using UnityEngine.Video;
using VRC.SDK3.Data;
using VRC.SDK3.UdonNetworkCalling;
using VRC.SDKBase;
@@ -96,6 +95,8 @@ public class GameManagerRound3 : GameManagerBase
_CameraControllerRound3.InitialiseCameras();
_CameraControllerRound3.PlayIFeelGood(true);
DeinitialiseGameplayCameraFollowers();
DeinitialiseEndingCameraFollowers();
RequestSerialization();
}
@@ -200,6 +201,8 @@ public class GameManagerRound3 : GameManagerBase
_CameraControllerRound3.DisableAllSwitchers();
_CameraControllerRound3.HostPlayerTimerToggle.Activate = false;
_CameraControllerRound3.ActivatePlayfieldSwitchTriggerGroup = true;
InitialiseGameplayCameraFollowers();
GetCurrentMarker().SendCustomNetworkEvent(NetworkEventTarget.Owner, "Activated", true);
_EndingPlayer.LoadRandomVideo();
@@ -231,7 +234,7 @@ public class GameManagerRound3 : GameManagerBase
_Timer--;
if (_Timer < 0)
{
SendCustomNetworkEvent(NetworkEventTarget.Owner, nameof(GameHasBeenLost), false);
SendCustomNetworkEvent(NetworkEventTarget.Owner, nameof(GameIsOver), false, false);
return;
}
@@ -252,7 +255,7 @@ public class GameManagerRound3 : GameManagerBase
SuccessCounter++;
if (SuccessCounter >= MAX_SUCCESS_COUNT)
{
GameHasBeenWon();
GameIsOver(true, false);
}
else
{
@@ -323,7 +326,7 @@ public class GameManagerRound3 : GameManagerBase
if (ActiveMarker >= FloorMap.MAX_SELECTED_COUNTRIES)
{
// If we ran out of countries, lose the game here.
SendCustomNetworkEvent(NetworkEventTarget.Owner, nameof(GameHasBeenLost), true);
SendCustomNetworkEvent(NetworkEventTarget.Owner, nameof(GameIsOver), false, true);
return;
}
@@ -335,7 +338,21 @@ public class GameManagerRound3 : GameManagerBase
}
[NetworkCallable]
public void GameHasBeenWon()
public void GameIsOver(bool GameWon, bool HaveWeRunOutOfMarkers)
{
if (GameWon)
{
_GameHasBeenWon();
}
else
{
_GameHasBeenLost(HaveWeRunOutOfMarkers);
}
InitialiseEndingCameraFollowers();
}
private void _GameHasBeenWon()
{
_RunTimer = false;
@@ -380,18 +397,17 @@ public class GameManagerRound3 : GameManagerBase
_NewspaperPublicDisplay.Activate(true);
}
[NetworkCallable]
public void GameHasBeenLost(bool RanOutOfMarkers)
private void _GameHasBeenLost(bool HaveWeRunOutOfMarkers)
{
_RunTimer = false;
if (_GameStatus == GameStatus.Begin)
{
GameStatusUpdate(RanOutOfMarkers ? GameStatus.RanOutOfMarkers : GameStatus.RanOutOfTime);
GameStatusUpdate(HaveWeRunOutOfMarkers ? GameStatus.RanOutOfMarkers : GameStatus.RanOutOfTime);
HostCardBetweenRoundsInterface GameLossInterface =
(HostCardBetweenRoundsInterface)GetHostCardInterface(RoundSegmentType.BetweenSegments);
GameLossInterface.HeaderUI.text = "The player has run out of " + (RanOutOfMarkers ? "markers" : "time") + ".";
GameLossInterface.HeaderUI.text = "The player has run out of " + (HaveWeRunOutOfMarkers ? "markers" : "time") + ".";
GameLossInterface.CommentUI.text =
"- Found " + SuccessCounter + " countries in " + TIMER_LENGTH + " seconds.\n\n" +
"- There's one more thing I want you to do for us. You know what it is...";
@@ -477,7 +493,7 @@ public class GameManagerRound3 : GameManagerBase
_EndingPlayer.PlayVideo = false;
_AudioManager.SendCustomNetworkEvent(NetworkEventTarget.All, "FadeOutMusic");
_CameraControllerRound3.SwitchToOverheadCamera();
_CameraControllerRound3.SwitchToFrontCamera();
_CameraControllerRound3.DeinitialiseCameras();
_CaseManager.EndGame();
@@ -546,6 +562,30 @@ public class GameManagerRound3 : GameManagerBase
return CurrentWinner[0];
}
private void InitialiseGameplayCameraFollowers()
{
_CameraControllerRound3.FrontCamera_FollowPlayers(new string[1] { GetRound3PlayerName() });
}
private void DeinitialiseGameplayCameraFollowers()
{
_CameraControllerRound3.FrontCamera_StopFollowingPlayers();
}
private void InitialiseEndingCameraFollowers()
{
string[] PlayerFollows = new string[2] {
_CaseManager.GetHostOwner().displayName,
GetRound3PlayerName()
};
_CameraControllerRound3.FrontCamera_FollowPlayers(PlayerFollows);
_CameraControllerRound3.OverheadCamera_FollowPlayers(PlayerFollows);
}
private void DeinitialiseEndingCameraFollowers()
{
_CameraControllerRound3.OverheadCamera_StopFollowingPlayers();
}
protected override HostCardInterfaceBase GetHostCardInterface(RoundSegmentType Question)
{