245 lines
7.7 KiB
C#
245 lines
7.7 KiB
C#
using System;
|
|
using System.Collections;
|
|
using UnityEngine;
|
|
using UnityEngine.Events;
|
|
|
|
[Serializable]
|
|
public class IntEvent : UnityEvent<int> {}
|
|
|
|
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;
|
|
|
|
[SerializeField] float healthBarPosFromTabEdge;
|
|
[SerializeField] float buttonPosFromTabEdge;
|
|
[SerializeField] float alertPosFromTabEdge;
|
|
|
|
//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");
|
|
|
|
public IntEvent OnChangeWorld;
|
|
|
|
public int CurrentWorldIndex => currentWorldIndex;
|
|
|
|
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;
|
|
|
|
OnChangeWorld.Invoke(currentWorldIndex);
|
|
|
|
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;
|
|
|
|
if (fromRight) {
|
|
for (int i = lastWorldIndex - 1; i >= currentWorldIndex; --i) {
|
|
worldInfos[i].healthBar.gameObject.SetActive(false);
|
|
worldInfos[i].button.gameObject.SetActive(false);
|
|
worldInfos[i].alert.gameObject.SetActive(false);
|
|
}
|
|
} else {
|
|
for (int i = lastWorldIndex + 1; i <= currentWorldIndex; ++i) {
|
|
worldInfos[i].healthBar.gameObject.SetActive(false);
|
|
worldInfos[i].button.gameObject.SetActive(false);
|
|
worldInfos[i].alert.gameObject.SetActive(false);
|
|
}
|
|
}
|
|
|
|
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, true, true, t);
|
|
worldInfos[i].material.SetFloat(UVOffset, GetUVOffset(t, true, i));
|
|
}
|
|
} else {
|
|
for (int i = lastWorldIndex + 1; i <= currentWorldIndex; ++i) {
|
|
worldInfos[i].renderQuad.position = GetQuadOffset(lastCam, worldInfos[i], i, false, true, t);
|
|
worldInfos[i].material.SetFloat(UVOffset, GetUVOffset(t, false, 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);
|
|
}
|
|
|
|
float GetTabUIPos(float t, bool fromRight, int index, float offset) {
|
|
return fromRight ?
|
|
Mathf.Lerp(tabWidth * (index + 1), -(worldInfos.Length - 1 - index) * tabWidth, t) - offset :
|
|
Mathf.Lerp(1f - tabWidth * (worldInfos.Length - index), index * tabWidth, t) + offset;
|
|
}
|
|
|
|
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));
|
|
worldInfos[i].healthBar.gameObject.SetActive(true);
|
|
worldInfos[i].healthBar.position = new Vector3(
|
|
Screen.width * GetTabUIPos(0f, fromRight, i, healthBarPosFromTabEdge),
|
|
worldInfos[i].healthBar.position.y,
|
|
worldInfos[i].healthBar.position.z
|
|
);
|
|
worldInfos[i].button.gameObject.SetActive(true);
|
|
worldInfos[i].button.position = new Vector3(
|
|
Screen.width * GetTabUIPos(0f, fromRight, i, buttonPosFromTabEdge),
|
|
worldInfos[i].button.position.y,
|
|
worldInfos[i].button.position.z
|
|
);
|
|
worldInfos[i].alert.gameObject.SetActive(true);
|
|
worldInfos[i].alert.position = new Vector3(
|
|
Screen.width * GetTabUIPos(0f, fromRight, i, alertPosFromTabEdge),
|
|
worldInfos[i].alert.position.y,
|
|
worldInfos[i].alert.position.z
|
|
);
|
|
}else {
|
|
worldInfos[i].healthBar.gameObject.SetActive(false);
|
|
worldInfos[i].button.gameObject.SetActive(false);
|
|
worldInfos[i].alert.gameObject.SetActive(false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[Serializable]
|
|
struct WorldInfo {
|
|
public Camera camera;
|
|
[HideInInspector] public RenderTexture texture;
|
|
public Transform renderQuad;
|
|
[HideInInspector] public Material material;
|
|
public RectTransform healthBar;
|
|
public RectTransform button;
|
|
public RectTransform alert;
|
|
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;
|
|
}
|
|
} |