主要技术:同异场景传送,协程,ShaderGraph,TimeLine切换动画
传送门
传送门:同场景传送 不同场景传送
- 可传送状态(Player 碰撞传送门触发)且按下E
- 判断 传送场景类型 TransitionType { SameScene, DifferentScene }
- 遍历该场景内的终点,选择对应终点 destinationTag
- 通过协程传送 destinationTag 位置给 Player 位置 SetPositionAndRotation
Shader Graph 创造传送门
同场景传送
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| # TransitionPoint
public class TransitionPoint : MonoBehaviour { public enum TransitionType { SameScene, DifferentScene };
[Header("Transition Info")] public string sceneName; public TransitionType transitionType; public TransitionDestination.DestinationTag destinationTag; private bool canTrans;
void Update() { if (Input.GetKeyDown(KeyCode.E) && canTrans) { SceneControllor.Instance.TransitionToDestination(this); } } void OnTriggerStay(Collider other) { if (other.CompareTag("Player")) canTrans = true; } void OnTriggerExit(Collider other) { if (other.CompareTag("Player")) canTrans = false; } }
|
1 2 3 4 5 6 7 8
| # TransitionPoint public class TransitionDestination : MonoBehaviour { public enum DestinationTag { ENTER, A, B, C }; public DestinationTag destinationTag; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| # SceneControllor public class SceneControllor : Singleton<SceneControllor> { GameObject player; NavMeshAgent playAgent;
public void TransitionToDestination(TransitionPoint transitionPoint) { switch (transitionPoint.transitionType) { case TransitionPoint.TransitionType.SameScene: StartCoroutine(Transition(SceneManager.GetActiveScene().name, transitionPoint.destinationTag)); break; case TransitionPoint.TransitionType.DifferentScene: break; } } IEnumerator Transition(string sceneName, TransitionDestination.DestinationTag destinationTag) { player = GameManager.Instance.playStats.gameObject; playAgent = player.GetComponent<NavMeshAgent>(); playAgent.enabled = false; player.transform.SetPositionAndRotation(GetDestination(destinationTag).transform.position, GetDestination(destinationTag).transform.rotation); playAgent.enabled = true; yield return null; } private TransitionDestination GetDestination(TransitionDestination.DestinationTag destinationTag) { var entrances = FindObjectsOfType<TransitionDestination>(); for (int i = 0; i < entrances.Length; i++) { if (entrances[i].destinationTag == destinationTag) return entrances[i]; } return null; } }
|
1 2 3 4 5
| # MouseManager if (hitInfo.collider.gameObject.CompareTag("Portal")) { OnMouseClicked?.Invoke(hitInfo.point); }
|
不同场景传送
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| # SceneControllor public class SceneControllor : Singleton<SceneControllor> { GameObject player; NavMeshAgent playAgent; public GameObject playerPrefab; protected override void Awake() { base.Awake(); DontDestroyOnLoad(this); } public void TransitionToDestination(TransitionPoint transitionPoint) { switch (transitionPoint.transitionType) { case TransitionPoint.TransitionType.SameScene: StartCoroutine(Transition(SceneManager.GetActiveScene().name, transitionPoint.destinationTag)); break; case TransitionPoint.TransitionType.DifferentScene: StartCoroutine(Transition(transitionPoint.sceneName, transitionPoint.destinationTag)); break; } } IEnumerator Transition(string sceneName, TransitionDestination.DestinationTag destinationTag) { if (SceneManager.GetActiveScene().name != sceneName) { yield return SceneManager.LoadSceneAsync(sceneName); yield return Instantiate(playerPrefab, GetDestination(destinationTag).transform.position, GetDestination(destinationTag).transform.rotation); yield break; } else { player = GameManager.Instance.playStats.gameObject; playAgent = player.GetComponent<NavMeshAgent>(); playAgent.enabled = false; player.transform.SetPositionAndRotation(GetDestination(destinationTag).transform.position, GetDestination(destinationTag).transform.rotation); playAgent.enabled = true; yield return null; } } TransitionDestination GetDestination(TransitionDestination.DestinationTag destinationTag) { var entrances = FindObjectsOfType<TransitionDestination>(); for (int i = 0; i < entrances.Length; i++) { if (entrances[i].destinationTag == destinationTag) return entrances[i]; } return null; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| # PlayerController void OnEnable() { MouseManager.Instance.OnMouseClicked += MoveToTarget; MouseManager.Instance.OnEnemyClicked += EventAttack; } void Start() { GameManager.Instance.RigisterPlay(characterStates); } void OnDisable() { if (!MouseManager.IsInitialized) return; MouseManager.Instance.OnMouseClicked -= MoveToTarget; MouseManager.Instance.OnEnemyClicked -= EventAttack; }
|
SceneFader 场景转换
TimeLine
新建 TimeLine
Windows -> Sequencing -> TimeLine
新建 TimeLine 拖动 Hierarchy 中的 GameObject 或 Animation,Animation Track
设置关键帧动画,EventSystem 控制动画期间 UI 不可用

- MainMenu 控制 TimeLine 播放时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| # MainMenu using UnityEngine.Playables;
public class MainMenu : MonoBehaviour { PlayableDirector director;
private void Awake() { director = FindObjectOfType<PlayableDirector>(); director.stopped += NewGame; }
void PlayTimeLine() { director.Play(); }
void NewGame(PlayableDirector obj) { PlayerPrefs.DeleteAll(); SceneControllor.Instance.TransitionToFirstLevel(); } }
|

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| # SceneFader public class SceneFader : MonoBehaviour { CanvasGroup canvasGroup;
public float fadeInDuration; public float fadeOutDuration;
void Awake() { canvasGroup = GetComponent<CanvasGroup>(); DontDestroyOnLoad(gameObject); } public IEnumerator FadeOutIn() { yield return FadeOut(fadeOutDuration); yield return FadeIn(fadeOutDuration); } public IEnumerator FadeOut(float time) { while(canvasGroup.alpha < 1) { canvasGroup.alpha += Time.deltaTime / time; yield return null; } } public IEnumerator FadeIn(float time) { while (canvasGroup.alpha != 0) { canvasGroup.alpha -= Time.deltaTime / time; yield return null; } Destroy(gameObject); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| # SceneControllor // 主要控制场景切换 IEnumerator LoadLevel(string scene) { SceneFader fade = Instantiate(sceneFaderPrefab); if (scene != "") { yield return StartCoroutine(fade.FadeOut(2.5f)); yield return SceneManager.LoadSceneAsync(scene); yield return player = Instantiate(playerPrefab, GameManager.Instance.GetEntrance().position, GameManager.Instance.GetEntrance().rotation); SaveManager.Instance.SavePlayerData(); yield return StartCoroutine(fade.FadeIn(2.5f)); yield break; } }
|
角色死亡切换主菜单
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| # SceneControllor void Start() { GameManager.Instance.AddObserver(this); fadeFinished = true; } IEnumerator LoadMain() { SceneFader fade = Instantiate(sceneFaderPrefab); yield return StartCoroutine(fade.FadeOut(2.5f)); yield return SceneManager.LoadSceneAsync("Menu"); yield return StartCoroutine(fade.FadeIn(2.5f)); yield break; } public void EndNotify() { if (fadeFinished) { fadeFinished = false; StartCoroutine(LoadMain()); } }
|
Reference:【Unity2020 3DRPG游戏开发教程|Core核心功能01:Create Project 创建项目导入素材|Unity中文课堂】 https://www.bilibili.com/video/BV1rf4y1k7vE?share_source=copy_web&vd_source=d83ce8fdf322bae01b4efc0e4798d0b9