using System; using System.Collections; using UnityEngine; public class WorldSwitcher : MonoBehaviour { [Range(0, 5)] [SerializeField] float transitionDuration; [SerializeField] AnimationCurve transitionCurve; [SerializeField] WorldInfo[] worldInfos; [SerializeField] float quadOffset; [Range(0, 1)] [SerializeField] float tabWidth; int lastWorldIndex; int currentWorldIndex; Coroutine transition; static readonly int UVOffset = Shader.PropertyToID("_UVOffset"); void Start() { 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(); } ResetQuadPositions(); } public void SwitchWorld(bool right) { 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(); } 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; //float startX = currentWorldIndex - lastWorldIndex > 0 ? -newWorld.texture.width / 2f + tabWidth * Screen.width : newWorld.texture.width / 2f - tabWidth * Screen.width + Screen.width; 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, fromRight ? 1f - t - tabWidth : -1f + t + tabWidth); yield return null; } ResetQuadPositions(); transition = null; } Vector3 GetQuadOffset(Camera cam, WorldInfo worldInfo, int index, bool fromRight, float t = 0f) { float x = fromRight ? 1f - tabWidth * (worldInfos.Length - index) - t : tabWidth * (1 + index) + t; Vector3 quadHalfWidthOffset = Vector3.right * (fromRight ? worldInfo.renderQuad.localScale.x / 2f : -worldInfo.renderQuad.localScale.x / 2f); return cam.ViewportToWorldPoint(new Vector3(x, .5f, quadOffset)) + quadHalfWidthOffset; } 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, fromRight ? 1f - tabWidth : -1f + tabWidth); } } } } [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() { renderQuad.localScale = new Vector3(camera.aspect, 1f, 1f); texture = new RenderTexture(Screen.width, Screen.height, 32); material.SetTexture(CameraTexture, texture); } }