Files
Ipseity-Demo/Assets/_Content/Scripts/SpiralStaircase.cs
T

217 lines
6.2 KiB
C#

using OctoCore;
using OctoCore.Components;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.Rendering.Universal;
public class SpiralStaircase : MonoBehaviour
{
[System.Serializable]
public class DoorsPair
{
public Transform Left;
public Transform Right;
}
[System.Serializable]
public class State
{
public bool PlayerInside;
public int Turns;
public float TotalAngle;
[Range(0, 1)] public float Progression;
}
[System.Serializable]
public class References
{
public UniversalRendererData RendererData;
public GameObject Stairs;
public DoorsPair[] Doors;
public GameObject[] Inside;
public GameObject[] Outside;
}
[System.Serializable]
public class Settings
{
public float Offset = -12.25f;
public float StartAngle = 450;
public float EndAngle = 720;
}
[System.Serializable]
public class Events
{
public UnityEvent<bool> OnInside;
public UnityEvent OnTopReached;
public UnityEvent OnBottomReached;
}
[SerializeField, OctoReadOnly] private State _state;
[SerializeField] private References _references;
[SerializeField] private Settings _settings;
[SerializeField] private Events _events;
private List<IEnumerator> _doorsCoroutine;
void Awake()
{
_doorsCoroutine = new List<IEnumerator>();
for (int i = 0; i < _references.Doors.Length; i++)
_doorsCoroutine.Add(null);
}
void Start()
{
ManageInside();
_events.OnBottomReached?.Invoke();
}
void OnDisable()
{
_state.PlayerInside = false;
ManageInside();
}
void Update()
{
if (_state.PlayerInside)
Count();
}
void OnTriggerEnter(Collider col)
{
if (OctoPlayer.Instance && OctoPlayer.Instance.gameObject == col.gameObject)
{
if (!_state.PlayerInside)
{
_state.PlayerInside = true;
ManageInside();
}
}
}
void OnTriggerExit(Collider col)
{
if (OctoPlayer.Instance && OctoPlayer.Instance.gameObject == col.gameObject)
{
if (_state.PlayerInside)
{
_state.PlayerInside = false;
ManageInside();
}
}
}
public void OpendDoors(bool open) { for (int i = 0; i < _references.Doors.Length; i++) OpendDoor(i, open); }
public void OpendDoor(int id) { OpendDoor(id, true); }
public void CloseDoor(int id) { OpendDoor(id, false); }
public void SetDoorOpened(int id) { OpendDoor(id, true, false); }
public void SetDoorClosed(int id) { OpendDoor(id, false, false); }
private void OpendDoor(int id, bool open, bool animate = true)
{
if (_doorsCoroutine[id] != null) StopCoroutine(_doorsCoroutine[id]);
StartCoroutine(_doorsCoroutine[id] = OpenDoorCoroutine(id, open, animate));
}
IEnumerator OpenDoorCoroutine(int id, bool open, bool animate)
{
const float duration = .6f;
DoorsPair door = _references.Doors[id];
Quaternion originRotL = door.Left.transform.localRotation;
Quaternion originRotR = door.Right.transform.localRotation;
Quaternion targetRotL = Quaternion.Euler(0, open ? 0 : 13.1f, 0);
Quaternion targetRotR = Quaternion.Euler(0, open ? 0 :-13.1f, 0);
if (animate)
{
for (float t = 0; t < duration; t += Time.deltaTime)
{
float lerp = .5f - Mathf.Cos(t / duration * Mathf.PI) * .5f;
door.Left.transform.localRotation = Quaternion.Lerp(originRotL, targetRotL, lerp);
door.Right.transform.localRotation = Quaternion.Lerp(originRotR, targetRotR, lerp);
yield return new WaitForEndOfFrame();
}
}
door.Left.transform.localRotation = targetRotL;
door.Right.transform.localRotation = targetRotR;
_doorsCoroutine[id] = null;
}
private void Count()
{
if (!OctoPlayer.Instance) return;
Vector2[] steps = new Vector2[4] { Vector2.up, Vector2.left, Vector2.down, Vector2.right };
Vector2 nextStep = steps[_state.Turns % steps.Length];
Vector3 relativePos3D = transform.InverseTransformPoint(OctoPlayer.Instance.transform.position);
Vector2 relativePos2D = new Vector2(relativePos3D.x, relativePos3D.z);
float quarterAngle = Vector2.Angle(nextStep, relativePos2D) * Mathf.Sign(Vector3.Cross(nextStep, relativePos2D).z);
_state.TotalAngle = Mathf.Max(0, _state.Turns * 90 + quarterAngle);
if (quarterAngle > 90 && quarterAngle < 100)
{
_state.Turns++;
}
else if (quarterAngle < 0 && _state.Turns > 0)
{
_state.Turns--;
}
float progression = Mathf.Clamp01(Mathf.InverseLerp(_settings.StartAngle, _settings.EndAngle, _state.TotalAngle));
if (progression >= .5f && _state.Progression < .5f)
{
_events.OnTopReached?.Invoke();
}
else if (progression < .5f && _state.Progression >= .5f)
{
_events.OnBottomReached?.Invoke();
}
_state.Progression = progression;
_references.Stairs.transform.localPosition = new Vector3(0, _state.Progression * _settings.Offset, 0);
}
private void ManageInside()
{
foreach (GameObject obj in _references.Inside) obj.SetActive(_state.PlayerInside);
foreach (GameObject obj in _references.Outside) obj.SetActive(!_state.PlayerInside);
_events.OnInside?.Invoke(_state.PlayerInside);
/*Shader.SetGlobalFloat("_MaskEnabled", _state.PlayerInside ? 0 : 1);
foreach (var feature in _references.RendererData.rendererFeatures)
{
// Stencil Mask
// VisibleThroughMask
// HiddenThroughMask
if (feature.name == "HiddenThroughMask")
{
feature.SetActive(!_state.PlayerInside);
break;
}
}*/
}
}