说明
实现一个鼠标悬停主界面菜单时,自动变更转换镜头的效果。
效果预览
使用Cinemachine的轨道相机,可以实现游览模式。通过修改虚拟相机的权重就可以自动转换当前使用的虚拟相机;Cinemachine系统会自动进行过渡切换。
源码实现
鼠标悬停菜单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
| using TMPro; using UnityEngine; using UnityEngine.EventSystems;
public class Move : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler { private TextMeshProUGUI text;
private void Awake() { text = GetComponent<TextMeshProUGUI>(); }
public int index;
public void OnPointerEnter(PointerEventData eventData) { Debug.Log($"进入{index}");
text.color = new Color(0.4f, 0.4f, 0.7f);
CameraController.Instance.MoveToStart(index); }
public void OnPointerExit(PointerEventData eventData) { text.color = Color.white; } }
|
摄像机控制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 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
| using System; using UnityEngine; using Cinemachine;
public class CameraController : MonoBehaviour { public static CameraController Instance; [Header("Virtual Cameras")] [SerializeField] private CinemachineVirtualCamera[] virtualCameras; [Header("Settings")] [SerializeField] private float transitionTime = 1f; [Header("Idle Settings")] [SerializeField] private float idleTimeBeforeLoop = 30f; [SerializeField] private bool enableIdleDetection = true;
private CinemachineBrain cinemachineBrain;
[SerializeField] private CinemachineVirtualCamera loopVirtualCamera; private float lastActivityTime; private bool isIdleModeActive; private int previousActiveCameraIndex = -1;
private void Awake() { if (Instance != null && Instance != this) { Destroy(gameObject); return; } Instance = this; cinemachineBrain = GetComponent<CinemachineBrain>() ?? Camera.main.GetComponent<CinemachineBrain>();
SetTransitionTime(1f); if (cinemachineBrain == null) { Debug.LogError("CinemachineBrain not found! Please add CinemachineVirtualCamera to your scene."); } } private void Start() { lastActivityTime = Time.time; isIdleModeActive = false; }
private void Update() { if (!enableIdleDetection || loopVirtualCamera == null) return; if (Input.anyKeyDown || Input.GetAxis("Mouse X") != 0 || Input.GetAxis("Mouse Y") != 0) { lastActivityTime = Time.time; if (isIdleModeActive) { ExitIdleMode(); } } if (!isIdleModeActive && Time.time - lastActivityTime >= idleTimeBeforeLoop) { EnterIdleMode(); } }
public void MoveToStart(int index) { if (virtualCameras == null || virtualCameras.Length == 0) { Debug.LogWarning("No virtual cameras assigned!"); return; } if (index < 0 || index >= virtualCameras.Length) { Debug.LogError($"Invalid camera index: {index}. Valid range: 0 - {virtualCameras.Length - 1}"); return; } if (virtualCameras[index] != null) { previousActiveCameraIndex = index; virtualCameras[index].Priority = 100; for (int i = 0; i < virtualCameras.Length; i++) { if (i != index && virtualCameras[i] != null) { virtualCameras[i].Priority = 0; } } lastActivityTime = Time.time; } else { Debug.LogError($"Virtual camera at index {index} is null!"); } } public void SetTransitionTime(float time) { transitionTime = time; if (cinemachineBrain != null) { cinemachineBrain.m_DefaultBlend.m_Time = time; } } private void EnterIdleMode() { isIdleModeActive = true; for (int i = 0; i < virtualCameras.Length; i++) { if (virtualCameras[i] != null) { virtualCameras[i].Priority = 0; } } loopVirtualCamera.Priority = 100; } private void ExitIdleMode() { isIdleModeActive = false; loopVirtualCamera.Priority = 0; previousActiveCameraIndex = -1; } public void ResetIdleTimer() { lastActivityTime = Time.time; } }
|