Skip to content

Instantly share code, notes, and snippets.

@planaria
Created May 24, 2023 11:50
Show Gist options
  • Save planaria/545c833947f92bffe88cc565d408ccd0 to your computer and use it in GitHub Desktop.
Save planaria/545c833947f92bffe88cc565d408ccd0 to your computer and use it in GitHub Desktop.
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
namespace SuzuFactory.EccentricRooms
{
[UdonBehaviourSyncMode(BehaviourSyncMode.None)]
public class Rooms : UdonSharpBehaviour
{
public Camera refCamera;
public Camera trackingCamera1;
public Camera trackingCamera2;
public Camera trackingCamera3;
public Camera trackingCamera4;
public Transform audioListener;
public GameObject room11Mirror;
private Transform roomsTransform;
private PlayerStation[] stations;
private PlayerState[] states;
private Teleport[] teleports;
private Vector3 room2Center;
private Vector3 room2Min;
private Vector3 room2Max;
private Vector3 room3Center;
private Vector3 room3Min;
private Vector3 room3Max;
private Vector3 room4Center;
private Collider room4FloorCollider;
private Transform room4SphereCollider;
private Vector3 room4LastLocalPosition;
private Quaternion room4LastLocalRotation;
private Vector3 room4Position;
private Quaternion room4Rotation;
private Vector3 room5Center;
private Vector3 room5Min;
private Vector3 room5Max;
private Vector3 room7Min;
private Vector3 room7Max;
private Vector3 room8Min;
private Vector3 room8Max;
private Vector3 room8Start;
private Vector3 room8End;
private Vector3 room9Min;
private Vector3 room9Max;
private Vector3 room9Center;
private Vector3 room9Offset;
private Vector3 room10Min;
private Vector3 room10Max;
private Vector3 room10Base;
private Vector3 room10External;
private Transform[] room10Pedestals;
private Vector3 room11Min;
private Vector3 room11Center;
private Vector3 room11Max;
private int myIndex = -1;
private int numPlayers = 0;
private int room = 0;
private bool sitting = false;
private int lastTime = 0;
private Vector3 basePosition;
private Quaternion baseRotation = Quaternion.identity;
private int numTemporaryDisabledColliders = 0;
private Collider[] temporaryDisabledColliders;
void Start()
{
audioListener.gameObject.SetActive(true);
roomsTransform = transform.Find("Rooms");
var stationsTransform = transform.Find("Stations");
stations = new PlayerStation[stationsTransform.childCount];
for (int i = 0; i < stationsTransform.childCount; ++i)
{
stations[i] = stationsTransform.GetChild(i).GetComponent<PlayerStation>();
}
var statesTransform = transform.Find("States");
states = new PlayerState[statesTransform.childCount];
for (int i = 0; i < statesTransform.childCount; ++i)
{
states[i] = statesTransform.GetChild(i).GetComponent<PlayerState>();
}
var teleportsTransform = transform.Find("Teleports");
teleports = new Teleport[teleportsTransform.childCount];
for (int i = 0; i < teleportsTransform.childCount; ++i)
{
teleports[i] = teleportsTransform.GetChild(i).GetComponent<Teleport>();
}
temporaryDisabledColliders = new Collider[1];
var room2 = roomsTransform.Find("Room2");
room2Center = room2.position;
room2Min = room2.Find("Min").position;
room2Max = room2.Find("Max").position;
var room3 = roomsTransform.Find("Room3");
room3Center = room3.position;
room3Min = room3.Find("Min").position;
room3Max = room3.Find("Max").position;
var room4 = roomsTransform.Find("Room4");
room4Center = room4.position;
room4FloorCollider = (Collider)room4.Find("FloorCollider").GetComponent(typeof(Collider));
room4SphereCollider = room4.Find("SphereCollider");
var room5 = roomsTransform.Find("Room5");
room5Center = room5.position;
room5Min = room5.Find("Min").position;
room5Max = room5.Find("Max").position;
var room7 = roomsTransform.Find("Room7");
room7Min = room7.Find("Min").position;
room7Max = room7.Find("Max").position;
var room8 = roomsTransform.Find("Room8");
room8Min = room8.Find("Min").position;
room8Max = room8.Find("Max").position;
room8Start = room8.Find("Start").position;
room8End = room8.Find("End").position;
var room9 = roomsTransform.Find("Room9");
room9Center = room9.position;
room9Min = room9.Find("Min").position;
room9Max = room9.Find("Max").position;
room9Offset = room9.Find("Offset").position;
var room10 = roomsTransform.Find("Room10");
room10Min = room10.Find("Min").position;
room10Max = room10.Find("Max").position;
room10External = room10.Find("External").position;
var room10PedestalsContainer = room10.Find("Pedestals");
room10Base = room10PedestalsContainer.position;
room10Pedestals = new Transform[room10PedestalsContainer.childCount];
for (int i = 0; i < room10PedestalsContainer.childCount; ++i)
{
var t = room10PedestalsContainer.GetChild(i);
var z = (i % 2 == 0 ? i : -(i + 1)) / 2 * 0.05f;
t.localPosition += new Vector3(0.0f, 0.0f, z);
room10Pedestals[i] = t;
}
var room11 = roomsTransform.Find("Room11");
room11Min = room11.Find("Min").position;
room11Center = room11.Find("Center").position;
room11Max = room11.Find("Max").position;
// Physics.gravity = Vector3.zero;
}
private const float ROOM2_INTERVAL = 20.0f;
private const float ROOM2_INTERVAL_HALF = ROOM2_INTERVAL / 2.0f;
private const float ROOM4_RADIUS = 2.0f;
void Update()
{
while (numTemporaryDisabledColliders != 0)
{
--numTemporaryDisabledColliders;
temporaryDisabledColliders[numTemporaryDisabledColliders].enabled = true;
}
var localPlayer = Networking.LocalPlayer;
if (localPlayer == null)
{
return;
}
if (myIndex == -1)
{
for (int i = 0; i < stations.Length; ++i)
{
if (stations[i].playerId == localPlayer.playerId)
{
myIndex = i;
break;
}
}
}
if (myIndex != -1)
{
var floorNow = Mathf.FloorToInt(Time.time);
if (!sitting || floorNow != lastTime)
{
Networking.SetOwner(localPlayer, states[myIndex].gameObject);
stations[myIndex].UseStation();
lastTime = floorNow;
sitting = true;
}
var localPos = localPlayer.GetPosition();
var localRot = localPlayer.GetRotation();
Vector3 position;
Quaternion rotation;
switch (room)
{
case 4:
{
var deltaPos = localPos - room4LastLocalPosition;
var deltaRot = Quaternion.Inverse(room4LastLocalRotation) * localRot;
room4LastLocalPosition = localPos;
room4LastLocalRotation = localRot;
room4Position += room4Rotation * Quaternion.Inverse(localRot) * deltaPos;
var v = room4Position - room4Center;
room4Rotation = room4Rotation * deltaRot;
room4Rotation = Quaternion.FromToRotation(room4Rotation * Vector3.up, v) * room4Rotation;
position = room4Position;
rotation = room4Rotation;
break;
}
default:
{
position = basePosition + baseRotation * localPos;
rotation = baseRotation * localRot;
break;
}
}
switch (room)
{
case 8:
{
var ratio = Mathf.Clamp01(Mathf.InverseLerp(room8Start.z, room8End.z, position.z));
var rot = Quaternion.Euler(0.0f, 0.0f, ratio * -360.0f);
position -= room8Start;
position = rot * position;
position += room8Start;
rotation = rot * rotation;
break;
}
}
var invRot = Quaternion.Inverse(rotation);
var roomPos = localPos - localRot * invRot * position;
var roomRot = localRot * invRot;
roomsTransform.SetPositionAndRotation(roomPos, roomRot);
room4SphereCollider.rotation = Quaternion.identity;
int count = 0;
for (int i = 0; i < stations.Length; ++i)
{
var station = stations[i];
if (station.playerId == -1)
{
continue;
}
var t = station.transform;
var playerState = states[i];
Vector3 playerPosition = playerState.position;
Quaternion playerRotation = playerState.rotation;
switch (playerState.room)
{
case 4:
break;
default:
playerPosition = playerState.basePosition + playerState.baseRotation * playerPosition;
playerRotation = playerState.baseRotation * playerRotation;
break;
}
switch (playerState.room)
{
case 8:
{
var ratio = Mathf.Clamp01(Mathf.InverseLerp(room8Start.z, room8End.z, playerPosition.z));
var rot = Quaternion.Euler(0.0f, 0.0f, ratio * -360.0f);
playerPosition -= room8Start;
playerPosition = rot * playerPosition;
playerPosition += room8Start;
playerRotation = rot * playerRotation;
break;
}
}
if (room == 3)
{
var r = (float)count / (float)VRCPlayerApi.GetPlayerCount();
var angle = 2.0f * Mathf.PI * r;
playerPosition = room3Center + new Vector3(Mathf.Cos(angle), 0.0f, Mathf.Sin(angle)) * 1.5f;
playerRotation = Quaternion.AngleAxis(-angle * Mathf.Rad2Deg - 90.0f, new Vector3(0.0f, 1.0f, 0.0f));
}
else if (room == 10)
{
var pedestal = room10Pedestals[i];
playerPosition = room10External + pedestal.localPosition * 10.0f;
playerRotation = pedestal.localRotation;
}
else if (playerState.room == 11)
{
playerPosition.z = Mathf.LerpUnclamped(playerPosition.z, room11Center.z, 2.0f);
var angles = playerRotation.eulerAngles;
playerRotation = Quaternion.Euler(angles.x, 180.0f - angles.y, angles.z);
}
else if (playerState.room == 2)
{
if (playerPosition.x < position.x - ROOM2_INTERVAL_HALF)
{
playerPosition.x += ROOM2_INTERVAL;
}
else if (playerPosition.x > position.x + ROOM2_INTERVAL_HALF)
{
playerPosition.x -= ROOM2_INTERVAL;
}
}
var stationPos = localPos - localRot * invRot * (position - playerPosition);
var stationRot = localRot * invRot * playerRotation;
t.SetPositionAndRotation(stationPos, stationRot);
++count;
// if (count == numPlayers)
// {
// break;
// }
}
switch (room)
{
case 0:
CheckTeleports(position);
if (IsInsideRoom2(position))
{
room = 2;
}
else if (IsInsideRoom3(position))
{
room = 3;
}
else if (IsInsideRoom5(position))
{
room = 5;
}
else if (IsInsideRoom7(position))
{
room = 7;
}
else if (IsInsideRoom8(position))
{
room = 8;
}
else if (IsInsideRoom9(position))
{
room = 9;
}
else if (IsInsideRoom10(position))
{
room = 10;
}
else if (IsInsideRoom11(position))
{
room = 11;
}
break;
case 2:
if (!IsInsideRoom2(position))
{
room = 0;
}
break;
case 3:
if (!IsInsideRoom3(position))
{
room = 0;
}
break;
case 5:
if (!IsInsideRoom5(position))
{
room = 0;
}
break;
case 7:
if (!IsInsideRoom7(position))
{
room = 0;
}
break;
case 8:
if (!IsInsideRoom8(position))
{
room = 0;
}
break;
case 9:
if (!IsInsideRoom9(position))
{
room = 0;
}
break;
case 10:
if (!IsInsideRoom10(position))
{
room = 0;
}
break;
case 11:
if (!IsInsideRoom11(position))
{
room = 0;
}
break;
}
if (room == 4)
{
room4FloorCollider.enabled = false;
Physics.gravity = new Vector3(0.0f, -2.0f, 0.0f);
}
else
{
room4FloorCollider.enabled = true;
Physics.gravity = new Vector3(0.0f, -9.8f, 0.0f);
}
switch (room)
{
case 2:
{
trackingCamera1.enabled = true;
trackingCamera2.enabled = true;
trackingCamera3.enabled = false;
trackingCamera4.enabled = false;
var headTracker = localPlayer.GetTrackingData(VRCPlayerApi.TrackingDataType.Head);
var v = new Vector3(ROOM2_INTERVAL, 0.0f, 0.0f);
UpdateTrackingCamera(trackingCamera1, headTracker.position + v, headTracker.rotation, 1.0f);
UpdateTrackingCamera(trackingCamera2, headTracker.position - v, headTracker.rotation, 1.0f);
if (position.x - room2Center.x < -ROOM2_INTERVAL_HALF)
{
basePosition.x += ROOM2_INTERVAL;
}
else if (position.x - room2Center.x > ROOM2_INTERVAL_HALF)
{
basePosition.x -= ROOM2_INTERVAL;
}
break;
}
case 5:
{
trackingCamera1.enabled = true;
trackingCamera2.enabled = false;
trackingCamera3.enabled = false;
trackingCamera4.enabled = false;
var headTracker = localPlayer.GetTrackingData(VRCPlayerApi.TrackingDataType.Head);
var pos = headTracker.position;
var rot = headTracker.rotation;
var room5CenterTrans = Quaternion.Inverse(baseRotation) * (room5Center - basePosition);
pos.x += (room5CenterTrans.x - pos.x) * 2.0f;
pos.z += (room5CenterTrans.z - pos.z) * 2.0f;
rot = Quaternion.Euler(0.0f, 180.0f, 0.0f) * rot;
UpdateTrackingCamera(trackingCamera1, pos, rot, 1.0f);
break;
}
case 7:
{
trackingCamera1.enabled = false;
trackingCamera2.enabled = false;
trackingCamera3.enabled = true;
trackingCamera4.enabled = false;
var headTracker = localPlayer.GetTrackingData(VRCPlayerApi.TrackingDataType.Head);
var pos = headTracker.position;
var rot = headTracker.rotation;
pos -= rot * new Vector3(0.0f, 0.0f, 1.0f);
pos = basePosition + baseRotation * pos;
var margin = new Vector3(0.2f, 0.2f, 0.2f);
pos = Vector3.Max(pos, room7Min + margin);
pos = Vector3.Min(pos, room7Max - margin);
pos = Quaternion.Inverse(baseRotation) * (pos - basePosition);
UpdateTrackingCamera(trackingCamera3, pos, rot, 1.0f);
break;
}
case 9:
{
trackingCamera1.enabled = true;
trackingCamera2.enabled = true;
trackingCamera3.enabled = false;
trackingCamera4.enabled = false;
var headTracker = localPlayer.GetTrackingData(VRCPlayerApi.TrackingDataType.Head);
var pos1 = headTracker.position;
pos1 = basePosition + baseRotation * pos1;
var pos2 = pos1;
pos1 -= room9Offset;
pos1 *= 10.0f;
pos1 += room9Center;
pos1 = Quaternion.Inverse(baseRotation) * (pos1 - basePosition);
pos2 -= room9Center;
pos2 *= 0.1f;
pos2 += room9Offset;
pos2 = Quaternion.Inverse(baseRotation) * (pos2 - basePosition);
UpdateTrackingCamera(trackingCamera1, pos1, headTracker.rotation, 10.0f);
UpdateTrackingCamera(trackingCamera2, pos2, headTracker.rotation, 0.1f);
break;
}
case 10:
{
trackingCamera1.enabled = false;
trackingCamera2.enabled = false;
trackingCamera3.enabled = false;
trackingCamera4.enabled = true;
var headTracker = localPlayer.GetTrackingData(VRCPlayerApi.TrackingDataType.Head);
var pos = headTracker.position;
pos = basePosition + baseRotation * pos;
pos -= room10Base;
pos *= 10.0f;
pos += room10External;
pos = Quaternion.Inverse(baseRotation) * (pos - basePosition);
UpdateTrackingCamera(trackingCamera4, pos, headTracker.rotation, 10.0f);
break;
}
default:
{
trackingCamera1.enabled = false;
trackingCamera2.enabled = false;
trackingCamera3.enabled = false;
trackingCamera4.enabled = false;
break;
}
}
var state = states[myIndex];
state.room = room;
state.basePosition = basePosition;
state.baseRotation = baseRotation;
switch (room)
{
case 4:
state.position = position;
state.rotation = rotation;
break;
default:
state.position = localPos;
state.rotation = localRot;
break;
}
}
room11Mirror.SetActive(room == 11);
}
private void UpdateTrackingCamera(Camera camera, Vector3 position, Quaternion rotation, float scaleMultiplier)
{
var localPlayer = Networking.LocalPlayer;
var scale = 1.0f / audioListener.localScale.x;
var cameraTracker = camera.transform.parent;
if (localPlayer.IsUserInVR())
{
var rot = rotation * Quaternion.Inverse(camera.transform.localRotation);
var pos = position - rot * camera.transform.localPosition * scale * scaleMultiplier;
cameraTracker.SetPositionAndRotation(pos, rot);
cameraTracker.localScale = scale * scaleMultiplier * Vector3.one;
camera.nearClipPlane = refCamera.nearClipPlane * scaleMultiplier;
camera.farClipPlane = refCamera.farClipPlane * scaleMultiplier;
}
else
{
cameraTracker.SetPositionAndRotation(position, rotation);
camera.nearClipPlane = refCamera.nearClipPlane * scaleMultiplier;
camera.farClipPlane = refCamera.farClipPlane * scaleMultiplier;
}
}
public override void OnPlayerRespawn(VRCPlayerApi player)
{
if (!player.isLocal)
{
return;
}
basePosition = Vector3.zero;
baseRotation = Quaternion.identity;
room = 0;
sitting = false;
}
private bool IsInsideRoom2(Vector3 position)
{
return position.x > room2Min.x && position.z > room2Min.z && position.x < room2Max.x && position.z < room2Max.z;
}
private bool IsInsideRoom3(Vector3 position)
{
return position.x > room3Min.x && position.z > room3Min.z && position.x < room3Max.x && position.z < room3Max.z;
}
private bool IsInsideRoom5(Vector3 position)
{
return position.x > room5Min.x && position.z > room5Min.z && position.x < room5Max.x && position.z < room5Max.z;
}
private bool IsInsideRoom7(Vector3 position)
{
return position.x > room7Min.x && position.z > room7Min.z && position.x < room7Max.x && position.z < room7Max.z;
}
private bool IsInsideRoom8(Vector3 position)
{
return position.x > room8Min.x && position.z > room8Min.z && position.x < room8Max.x && position.z < room8Max.z;
}
private bool IsInsideRoom9(Vector3 position)
{
return position.x > room9Min.x && position.z > room9Min.z && position.x < room9Max.x && position.z < room9Max.z;
}
private bool IsInsideRoom10(Vector3 position)
{
return position.x > room10Min.x && position.z > room10Min.z && position.x < room10Max.x && position.z < room10Max.z;
}
private bool IsInsideRoom11(Vector3 position)
{
return position.x > room11Min.x && position.z > room11Min.z && position.x < room11Max.x && position.z < room11Max.z;
}
private void CheckTeleports(Vector3 position)
{
for (int i = 0; i < teleports.Length; ++i)
{
var teleport = teleports[i];
if (CheckTeleport(position, teleport.position1, teleport.rotation1, teleport.position2, teleport.rotation2, teleport.transform1.position, teleport.collider1, i, teleport.reversed))
{
break;
}
if (CheckTeleport(position, teleport.position2, teleport.rotation2, teleport.position1, teleport.rotation1, teleport.transform2.position, teleport.collider2, i, !teleport.reversed))
{
break;
}
}
}
private bool CheckTeleport(Vector3 position, Vector3 position1, Quaternion rotation1, Vector3 position2, Quaternion rotation2, Vector3 transformedPosition1, Collider collider, int i, bool reversed)
{
var v = position - position1;
if (v.magnitude > 1.0f)
{
return false;
}
var dot = Vector3.Dot(v, rotation1 * Vector3.right);
if ((reversed ? dot : -dot) > -0.1f)
{
return false;
}
var rot = rotation2 * Quaternion.Inverse(rotation1);
basePosition = position2 - rot * baseRotation * transformedPosition1;
baseRotation = rot * baseRotation;
collider.enabled = false;
temporaryDisabledColliders[numTemporaryDisabledColliders++] = collider;
return true;
}
public void InteractPlanet()
{
var localPlayer = Networking.LocalPlayer;
if (localPlayer == null)
{
return;
}
if (room == 0)
{
room = 4;
room4LastLocalPosition = localPlayer.GetPosition();
room4LastLocalRotation = localPlayer.GetRotation();
room4Position = room4Center + new Vector3(0.0f, ROOM4_RADIUS, 0.0f);
room4Rotation = Quaternion.identity;
}
else
{
room = 0;
basePosition = room4Center - localPlayer.GetPosition();
baseRotation = Quaternion.identity;
}
}
public override void OnPlayerJoined(VRCPlayerApi player)
{
++numPlayers;
if (!Networking.IsMaster)
{
return;
}
int index = -1;
for (int i = 0; i < stations.Length; ++i)
{
if (stations[i].playerId == -1)
{
index = i;
break;
}
}
if (index != -1)
{
var s = stations[index];
s.playerId = player.playerId;
s.RequestSerialization();
}
}
public override void OnPlayerLeft(VRCPlayerApi player)
{
--numPlayers;
if (!Networking.IsMaster)
{
return;
}
for (int i = 0; i < stations.Length; ++i)
{
var s = stations[i];
if (s.playerId == player.playerId)
{
s.playerId = -1;
s.ResetStation();
s.RequestSerialization();
break;
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment