creative-jam-20/Assets/Scripts/WorldSwitcher.cs
2022-05-15 13:24:19 -04:00

187 lines
5.6 KiB
C#

using System;
using System.Collections;
using UnityEngine;
public class WorldSwitcher : MonoBehaviour {
[Range(0, 5)]
[SerializeField] float transitionDuration;
/*[Range(0, 5)]
[SerializeField] float fadeDuration;*/
[SerializeField] AnimationCurve transitionCurve;
//[SerializeField] AnimationCurve fadeCurve;
[SerializeField] WorldInfo[] worldInfos;
[Range(0, 1)]
[SerializeField] float tabWidth;
//TODO Hardcode
[SerializeField]
float quadOffset;
int lastWorldIndex;
int currentWorldIndex;
Coroutine transition;
static readonly int UVOffset = Shader.PropertyToID("_UVOffset");
//static readonly int Opacity = Shader.PropertyToID("_Opacity");
void Awake() {
if (worldInfos.Length != 3)
Debug.LogWarning("For now, WorldSwitcher should have 3 worlds.");
currentWorldIndex = 1;
lastWorldIndex = 0;
for (int i = 0; i < worldInfos.Length; i++) {
worldInfos[i].material = worldInfos[i].renderQuad.GetComponent<Renderer>().material;
worldInfos[i].GenerateRenderTexture(quadOffset);
}
ResetQuadPositions();
}
public void SwitchWorld(int index) {
if (transition != null)
return;
if (index == currentWorldIndex || index < 0 || index > worldInfos.Length - 1)
return;
//TODO Block window resize during transition?
foreach (WorldInfo worldInfo in worldInfos) {
if (Screen.width != worldInfo.texture.width || Screen.height != worldInfo.texture.height)
worldInfo.GenerateRenderTexture(quadOffset);
}
lastWorldIndex = currentWorldIndex;
currentWorldIndex = index;
transition = StartCoroutine(TransitionCamera(currentWorldIndex - lastWorldIndex < 0));
}
/*void OnGUI() {
for (int i = 0; i < worldInfos.Length; ++i) {
if (GUILayout.Button($"World {i}"))
SwitchWorld(i);
}
}*/
IEnumerator TransitionCamera(bool fromRight) {
WorldInfo lastWorld = worldInfos[lastWorldIndex];
float startTime = Time.time;
Camera lastCam = lastWorld.camera;
while (Time.time < startTime + transitionDuration) {
float t = transitionCurve.Evaluate((Time.time - startTime) / transitionDuration);
if (fromRight) {
for (int i = lastWorldIndex - 1; i >= currentWorldIndex; --i) {
worldInfos[i].renderQuad.position = GetQuadOffset(lastCam, worldInfos[i], i, fromRight, true, t);
worldInfos[i].material.SetFloat(UVOffset, GetUVOffset(t, fromRight, i));
}
} else {
for (int i = lastWorldIndex + 1; i <= currentWorldIndex; ++i) {
worldInfos[i].renderQuad.position = GetQuadOffset(lastCam, worldInfos[i], i, fromRight, true, t);
worldInfos[i].material.SetFloat(UVOffset, GetUVOffset(t, fromRight, i));
}
}
yield return null;
}
/*startTime = Time.time;
while (Time.time < startTime + fadeDuration) {
worldInfos[currentWorldIndex].material.SetFloat(
Opacity,
1f - fadeCurve.Evaluate((Time.time - startTime) / fadeDuration)
);
yield return null;
}*/
ResetQuadPositions();
transition = null;
}
Vector3 GetQuadOffset(Camera cam, WorldInfo worldInfo, int index, bool fromRight, bool moving, float t = 0f) {
float x = fromRight ?
Mathf.Lerp((1 + index) * tabWidth, 1f - (worldInfos.Length - 1 - index) * tabWidth, t) :
Mathf.Lerp(1f - tabWidth * (worldInfos.Length - index), index * tabWidth, t);
Vector3 quadHalfWidthOffset = Vector3.right * (fromRight ? -worldInfo.renderQuad.localScale.x / 2f : worldInfo.renderQuad.localScale.x / 2f);
float depthOffset = moving ?
Mathf.Abs(currentWorldIndex - index) * .0001f :
-Mathf.Abs(currentWorldIndex - index) * .0001f;
//TODO Offset epsilon
return cam.ViewportToWorldPoint(
new Vector3(
x,
.5f,
quadOffset + depthOffset
)
) + quadHalfWidthOffset;
}
float GetUVOffset(float t, bool fromRight, int index) {
return fromRight ?
Mathf.Lerp(-1f + tabWidth * (index + 1), -(worldInfos.Length - 1 - index) * tabWidth, t):
Mathf.Lerp(1f - tabWidth * (worldInfos.Length - index), index * tabWidth, t);
}
void ResetQuadPositions() {
Camera currCam = worldInfos[currentWorldIndex].camera;
LayerMask currLayer = worldInfos[currentWorldIndex].layer;
for (int i = 0; i < worldInfos.Length; ++i) {
bool usingRenderTexture = i != currentWorldIndex;
worldInfos[i].SetUsingRenderTexture(usingRenderTexture, currLayer);
//worldInfos[i].material.SetFloat(Opacity, 1f);
if (usingRenderTexture) {
bool fromRight = i - currentWorldIndex < 0;
worldInfos[i].renderQuad.position = GetQuadOffset(currCam, worldInfos[i], i, fromRight, false);
worldInfos[i].material.SetFloat(UVOffset, GetUVOffset(0f, fromRight, i));
}
}
}
}
[Serializable]
struct WorldInfo {
public Camera camera;
[HideInInspector] public RenderTexture texture;
public Transform renderQuad;
[HideInInspector] public Material material;
public int layer;
static readonly int CameraTexture = Shader.PropertyToID("_CameraTexture");
public void SetUsingRenderTexture(bool usingTexture, int layer) {
if (usingTexture) {
camera.targetTexture = texture;
renderQuad.gameObject.SetActive(true);
renderQuad.gameObject.layer = layer;
}else {
camera.targetTexture = null;
renderQuad.gameObject.SetActive(false);
}
}
public void GenerateRenderTexture(float quadOffset) {
bool usingTexture = !ReferenceEquals(camera.targetTexture, null);
if (usingTexture)
camera.targetTexture = null;
float height = 2f * Mathf.Tan(Mathf.Deg2Rad * camera.fieldOfView / 2f) * quadOffset;
renderQuad.localScale = new Vector3(camera.aspect * height, height, 1f);
texture = new RenderTexture(Screen.width, Screen.height, 32);
material.SetTexture(CameraTexture, texture);
if (usingTexture)
camera.targetTexture = texture;
}
}