using System; using System.Collections; using UnityEngine; public class WorldSwitcher : MonoBehaviour { [Range(0, 5)] [SerializeField] float transitionDuration; [SerializeField] AnimationCurve transitionCurve; [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"); 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().material; worldInfos[i].GenerateRenderTexture(quadOffset); } ResetQuadPositions(); } public void SwitchWorld(bool right) { if (transition != null) return; if (right ? currentWorldIndex == worldInfos.Length - 1 : currentWorldIndex == 0) 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; if (right) currentWorldIndex++; else currentWorldIndex--; transition = StartCoroutine(TransitionCamera(right)); } void OnGUI() { if (GUILayout.Button("Left world")) SwitchWorld(false); if (GUILayout.Button("Right world")) SwitchWorld(true); } IEnumerator TransitionCamera(bool fromRight) { WorldInfo lastWorld = worldInfos[lastWorldIndex]; WorldInfo newWorld = worldInfos[currentWorldIndex]; float startTime = Time.time; Camera lastCam = lastWorld.camera; while (Time.time < startTime + transitionDuration) { float t = transitionCurve.Evaluate((Time.time - startTime) / transitionDuration); newWorld.renderQuad.position = GetQuadOffset(lastCam, newWorld, currentWorldIndex, fromRight, t); newWorld.material.SetFloat(UVOffset, GetUVOffset(t, fromRight, currentWorldIndex)); yield return null; } ResetQuadPositions(); transition = null; } Vector3 GetQuadOffset(Camera cam, WorldInfo worldInfo, int index, bool fromRight, float t = 0f) { float x = fromRight ? Mathf.Lerp(1f - tabWidth * (worldInfos.Length - index), index * tabWidth, t) : Mathf.Lerp((1 + index) * tabWidth, 1f - (worldInfos.Length - 1 - index) * tabWidth, t); Vector3 quadHalfWidthOffset = Vector3.right * (fromRight ? worldInfo.renderQuad.localScale.x / 2f : -worldInfo.renderQuad.localScale.x / 2f); //TODO Offset epsilon return cam.ViewportToWorldPoint(new Vector3( x, .5f, quadOffset - Mathf.Abs(currentWorldIndex - index) * .01f) ) + quadHalfWidthOffset; } float GetUVOffset(float t, bool fromRight, int index) { return fromRight ? Mathf.Lerp(1f - tabWidth * (worldInfos.Length - index), index * tabWidth, t): Mathf.Lerp(-1f + tabWidth * (index + 1), -(worldInfos.Length - 1 - index) * tabWidth, t); } void ResetQuadPositions() { Camera currCam = worldInfos[currentWorldIndex].camera; for (int i = 0; i < worldInfos.Length; ++i) { bool usingRenderTexture = i != currentWorldIndex; worldInfos[i].SetUsingRenderTexture(usingRenderTexture); if (usingRenderTexture) { bool fromRight = i - currentWorldIndex > 0; worldInfos[i].renderQuad.position = GetQuadOffset(currCam, worldInfos[i], i, fromRight); 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; static readonly int CameraTexture = Shader.PropertyToID("_CameraTexture"); public void SetUsingRenderTexture(bool usingTexture) { if (usingTexture) { camera.targetTexture = texture; renderQuad.gameObject.SetActive(true); }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.Atan(Mathf.Deg2Rad * camera.fieldOfView) * quadOffset; renderQuad.localScale = new Vector3(camera.aspect, 1f, 1f); texture = new RenderTexture(Screen.width, Screen.height, 32); material.SetTexture(CameraTexture, texture); if (usingTexture) camera.targetTexture = texture; } }