using System; using System.Collections; using UnityEngine; public class WorldSwitcher : MonoBehaviour { [Range(0, 5)] [SerializeField] float transitionDuration; [SerializeField] AnimationCurve transitionCurve; [SerializeField] WorldInfo[] worldInfos; [SerializeField] Transform renderQuad; [SerializeField] float quadOffset; [SerializeField] float tabWidth; int lastWorldIndex; int currentWorldIndex; Coroutine transition; RenderTexture renderTexture; Material renderMaterial; void Awake() { if (worldInfos.Length <= 1) Debug.LogWarning("WorldSwitcher should have multiple cameras"); currentWorldIndex = 0; lastWorldIndex = 0; renderMaterial = renderQuad.GetComponent().material; GenerateRenderTexture(); for (int i = 0; i < worldInfos.Length; i++) { WorldInfo worldInfo = worldInfos[i]; worldInfo.camera.enabled = i == 0; Vector3 tabPos = worldInfo.worldTab.position; float tabX = worldInfo.side == TabSide.Left ? 0 : ((RectTransform)worldInfo.worldTab.parent).rect.width; worldInfo.worldTab.position = new Vector3(tabX, tabPos.y, tabPos.z); } } public void SwitchWorld(int index) { if (index == currentWorldIndex) return; if (index < 0 || index >= worldInfos.Length) throw new IndexOutOfRangeException($"{index} is not an available world."); lastWorldIndex = currentWorldIndex; currentWorldIndex = index; Camera currCam = worldInfos[currentWorldIndex].camera; currCam.enabled = true; //TODO Block window resize during transition? if (Screen.width != renderTexture.width || Screen.height != renderTexture.height) GenerateRenderTexture(); currCam.targetTexture = renderTexture; renderQuad.localScale = new Vector3(worldInfos[lastWorldIndex].camera.aspect, 1f, 1f); Vector3 oldPos = worldInfos[lastWorldIndex].worldTab.position; worldInfos[lastWorldIndex].worldTab.position = new Vector3(worldInfos[lastWorldIndex].TabBaseX, oldPos.y, oldPos.z); transition = StartCoroutine(TransitionCamera()); } void OnGUI() { for (int i = 0; i < worldInfos.Length; ++i) { if (i == currentWorldIndex) continue; if (GUILayout.Button($"World {i}")) SwitchWorld(i); } } IEnumerator TransitionCamera() { WorldInfo lastWorld = worldInfos[lastWorldIndex]; WorldInfo newWorld = worldInfos[currentWorldIndex]; float startTime = Time.time; Transform lastCam = lastWorld.camera.transform; Vector3 tabStartPosition = newWorld.worldTab.position; float tabParentWidth = newWorld.TabParentWidth; while (Time.time < startTime + transitionDuration) { float t = transitionCurve.Evaluate((Time.time - startTime) / transitionDuration); t *= (tabParentWidth + tabWidth) / tabParentWidth; renderQuad.position = lastCam.position + Vector3.forward * quadOffset + Vector3.right * (2f * Mathf.Clamp01(t) - 2f); //float tabX = newWorld.side == TabSide.Left ? t * (tabParentWidth + tabWidth) : t * -(tabParentWidth + tabWidth) + tabParentWidth; newWorld.worldTab.position = new Vector3(tabParentWidth * t, tabStartPosition.y, tabStartPosition.z); yield return null; } newWorld.camera.targetTexture = null; lastWorld.camera.enabled = false; transition = null; } void GenerateRenderTexture() { renderTexture = new RenderTexture(Screen.width, Screen.height, 32); renderMaterial.mainTexture = renderTexture; } } [Serializable] struct WorldInfo { public Camera camera; public RectTransform worldTab; public ushort indexFromSide; public TabSide side; public float TabParentWidth => ((RectTransform)worldTab.parent).rect.width; public float TabBaseX => side == TabSide.Left ? indexFromSide * 6.25f : TabParentWidth - indexFromSide * 6.25f; } [Serializable] enum TabSide { Left, Right }