Unity第三人稱角色控制:Cinemachine與CharacterController黃金組合實戰(zhàn)
1. 項目概述與核心價值在Unity里做第三人稱角色控制聽起來是個基礎活但真上手了才發(fā)現從“能動起來”到“手感絲滑”之間隔著十萬八千里。新手最容易掉進的坑就是自己手搓相機跟隨和角色移動邏輯結果不是角色移動時相機鬼畜抖動就是跳躍落地時穿??D調試起來讓人頭大。這個項目標題點出的“Cinemachine CharacterController”組合恰恰是Unity官方給出的、經過大量項目驗證的“黃金搭檔”它能幫你用最少的代碼實現一套穩(wěn)定、可擴展且手感舒適的控制方案。簡單來說這個方案的核心價值在于“專業(yè)的事交給專業(yè)的工具”。Cinemachine是Unity強大的相機系統套件它用虛擬相機Virtual Camera的概念通過一套復雜的算法如阻尼、噪聲、混合來處理相機的跟隨、構圖和切換你幾乎不用寫一行相機控制代碼。而CharacterController是Unity內置的一個膠囊體碰撞器組件它自帶簡單的地面檢測和坡度處理比剛體Rigidbody物理方案在操控反饋上更直接、更符合動作游戲的需求。將兩者結合你就能快速搭建一個基礎框架玩家通過輸入控制CharacterController移動和跳躍Cinemachine虛擬相機則智能地跟隨并環(huán)繞角色提供穩(wěn)定的第三人稱視角。這套方案特別適合中小型團隊、獨立開發(fā)者以及學習Unity的進階愛好者。你不需要是數學或圖形學專家也能理解其工作原理并快速上手。它解決的不僅僅是“功能實現”更是“體驗優(yōu)化”的問題。接下來我會帶你從零開始拆解每一個環(huán)節(jié)的設計思路、具體實現和那些只有踩過坑才知道的調試技巧。2. 核心組件選型與設計思路拆解為什么是Cinemachine和CharacterController而不是Rigidbody或者自己寫相機腳本這背后是一系列針對第三人稱控制特性的權衡。2.1 為何選擇CharacterController而非Rigidbody很多教程會用Rigidbody給角色添加物理屬性然后通過AddForce來移動。這在需要真實物理交互比如被爆炸炸飛、在冰面上滑行的場景下很合適。但對于一個需要精準、即時反饋的第三人稱動作或角色扮演游戲物理引擎的“慣性”和“延遲”會成為手感的敵人。CharacterController則完全不同它是一個“非物理”的碰撞體。它的移動完全由腳本驅動SimpleMove或Move方法不受物理力的持續(xù)影響。這意味著響應即時按下按鍵角色立刻開始移動松開立刻停止取決于你代碼的寫法沒有物理慣性帶來的“滑行感”??刂凭珳誓憧梢跃_控制角色在斜坡上的行為、臺階的高度通過stepOffset參數、甚至是在空中移動的速度這些都是通過參數和向量計算直接決定的而非復雜的物理模擬。性能與穩(wěn)定避免了物理引擎可能帶來的不穩(wěn)定碰撞和性能開銷對于移動平臺或角色數量多的場景更友好。當然CharacterController也有局限比如它不參與標準的物理力反饋你需要自己處理擊退效果復雜的碰撞交互需要額外處理。但對于核心的移動、跳躍、地面檢測它提供了最簡潔高效的API。2.2 為何選擇Cinemachine而非手動編寫相機腳本自己寫相機跟隨邏輯無非就是在LateUpdate里計算一個目標位置比如角色身后上方然后用Vector3.Lerp或SmoothDamp平滑地移動過去。聽起來簡單但問題一大堆碰撞處理相機穿墻怎么辦你需要寫復雜的射線檢測和位置調整邏輯。構圖死板固定的偏移量在角色貼近墻角或進行特殊動作時視角會變得很糟糕。平滑與抖動Lerp的插值系數調起來很玄學移動和旋轉不同步容易導致抖動。擴展性差想實現鏡頭震動、過場動畫切換、多相機混合幾乎要推倒重來。Cinemachine通過“虛擬相機”和“大腦Brain”的概念優(yōu)雅地解決了所有問題。你創(chuàng)建一個CinemachineVirtualCamera它定義了你想要的鏡頭“狀態(tài)”位置、旋轉、視野等。CinemachineBrain組件掛在主相機上負責評估場景中所有虛擬相機的“優(yōu)先級”和“混合”狀態(tài)并驅動真正的Main Camera。對于第三人稱跟隨我們主要使用CinemachineFreeLook組件它內置了三個軌道上、中、下環(huán)形軌道可以輕松實現圍繞角色的旋轉和俯仰控制并且自帶完善的碰撞規(guī)避通過CinemachineCollider擴展和阻尼平滑系統。你只需要在Inspector面板里拖拽設置就能獲得電影級的鏡頭效果。2.3 整體架構設計理解了“為什么選”整個系統的架構就清晰了角色控制核心一個GameObject掛載CharacterController組件和我們的自定義腳本例如ThirdPersonController。腳本負責讀取玩家輸入使用Unity的Input System或舊Input Manager調用CharacterController.Move()并處理跳躍邏輯。視角控制核心一個獨立的GameObject作為Cinemachine虛擬相機的“跟隨目標”。通常這個目標不是角色模型本身而是角色身上的一個空子物體比如叫“CameraFollowTarget”位置在角色胸部或頭部高度。這樣方便獨立控制相機的注視點。在這個目標上或同級創(chuàng)建CinemachineFreeLook虛擬相機并將其Follow和Look At目標都指向這個“CameraFollowTarget”或角色根節(jié)點。輸入流轉玩家鼠標移動控制CinemachineFreeLook的X軸旋轉和Y軸俯仰。玩家鍵盤WASD控制角色在攝像機相對方向上的移動。這里的關鍵是移動方向需要根據當前相機的朝向進行轉換這是實現“無論鏡頭在哪按W都是向前鏡頭方向跑”的關鍵。3. 詳細實現步驟與核心代碼解析理論清晰后我們進入實戰(zhàn)環(huán)節(jié)。我會假設你使用Unity較新的Input System包來處理輸入因為它更強大、更靈活。3.1 項目初始化與輸入設置首先確保你的Unity項目已安裝Cinemachine和Input System包通過Package Manager。然后創(chuàng)建一個空場景并導入一個基礎的角色模型可以使用Unity的膠囊體或方塊臨時替代。創(chuàng)建輸入Actions在Project窗口右鍵 - Create - Input Actions命名為PlayerControls。雙擊打開創(chuàng)建以下Action Maps和ActionsAction Map:PlayerAction:Move(Value Type: Vector2)。綁定鍵位WASD或左搖桿。Action:Look(Value Type: Vector2)。綁定鍵位鼠標Delta或右搖桿。Action:Jump(Button)。綁定鍵位空格鍵或游戲手柄South按鈕。保存后在Inspector中勾選Generate C# Class然后點擊Apply。這會在后臺生成一個PlayerControls的C#類供我們調用。搭建角色與相機層級創(chuàng)建空GameObject命名為Player。為其添加CharacterController組件。調整Center和Height使其匹配你的角色模型。在Player下創(chuàng)建一個空子物體命名為CameraFollowTarget。將其位置調整到角色模型的眼睛或胸部高度如(0, 1.6, 0)。在場景中創(chuàng)建一個普通的Main Camera。為其添加CinemachineBrain組件。在Hierarchy右鍵 - Cinemachine - FreeLook Camera。這會在場景中創(chuàng)建一個CM vcam1一個包含CinemachineFreeLook組件的對象。選中CM vcam1在Inspector中將Follow屬性拖拽賦值給Player/CameraFollowTarget。將Look At屬性也拖拽賦值給同一個目標或Player本身取決于你希望鏡頭始終注視角色的中心還是跟隨目標點通常用同一個即可。調整CinemachineFreeLook的軌道Rigs。它包含TopRig、MiddleRig、BottomRig分別對應鏡頭在角色上方、中間、下方時的環(huán)形軌道。你可以分別設置每個Rig的Height軌道相對于Follow目標的高度和Radius軌道半徑。一個常見的設置是三個Rig的Height依次為 2.0, 1.0, -0.5Radius為 3.0, 3.0, 3.0。這需要你根據角色大小和游戲風格反復調試。3.2 編寫角色控制器腳本在Player對象上創(chuàng)建C#腳本命名為ThirdPersonController。using UnityEngine; using UnityEngine.InputSystem; // 使用新的Input System [RequireComponent(typeof(CharacterController))] public class ThirdPersonController : MonoBehaviour { [Header(移動參數)] [SerializeField] private float moveSpeed 5f; [SerializeField] private float sprintSpeed 8f; [SerializeField] private float rotationSmoothTime 0.12f; private float currentSpeed; private float rotationVelocity; [Header(跳躍參數)] [SerializeField] private float jumpHeight 1.2f; [SerializeField] private float gravity -15.0f; [SerializeField] private float jumpTimeout 0.1f; // 跳躍后短暫時間內不允許再次跳躍 [SerializeField] private float fallTimeout 0.15f; // 落地后短暫時間才重置狀態(tài) private float jumpTimeoutDelta; private float fallTimeoutDelta; private bool grounded true; private float verticalVelocity 0.0f; [Header(地面檢測)] [SerializeField] private float groundedOffset -0.14f; // 檢測球體下移偏移 [SerializeField] private float groundedRadius 0.28f; // 檢測球體半徑 [SerializeField] private LayerMask groundLayers; // 指定哪些層是地面 [Header(相機參考)] [SerializeField] private Transform cameraTransform; // 主相機的Transform // 組件引用 private CharacterController controller; private PlayerInput playerInput; private InputAction moveAction; private InputAction lookAction; private InputAction jumpAction; // 輸入值緩存 private Vector2 moveInput; private Vector2 lookInput; private void Awake() { controller GetComponentCharacterController(); // 獲取PlayerInput組件需要掛在Player對象上 playerInput GetComponentPlayerInput(); // 從PlayerInput中獲取Actions moveAction playerInput.actions[Move]; lookAction playerInput.actions[Look]; jumpAction playerInput.actions[Jump]; // 初始化超時計時器 jumpTimeoutDelta jumpTimeout; fallTimeoutDelta fallTimeout; // 如果沒有指定相機嘗試使用主相機 if (cameraTransform null) { cameraTransform Camera.main.transform; } } private void Update() { GroundedCheck(); HandleMove(); HandleJumpAndGravity(); } // 地面檢測使用球體檢測角色底部 private void GroundedCheck() { Vector3 spherePosition new Vector3(transform.position.x, transform.position.y groundedOffset, transform.position.z); grounded Physics.CheckSphere(spherePosition, groundedRadius, groundLayers, QueryTriggerInteraction.Ignore); } // 處理移動 private void HandleMove() { // 讀取輸入 moveInput moveAction.ReadValueVector2(); // 計算目標方向將輸入轉換為基于相機朝向的世界空間方向 Vector3 inputDirection new Vector3(moveInput.x, 0.0f, moveInput.y).normalized; // 關鍵步驟將輸入方向從相機相對空間轉換到世界空間 if (inputDirection.magnitude 0.1f cameraTransform ! null) { // 獲取相機的前向和右向向量忽略Y軸水平面 Vector3 cameraForward Vector3.Scale(cameraTransform.forward, new Vector3(1, 0, 1)).normalized; Vector3 cameraRight Vector3.Scale(cameraTransform.right, new Vector3(1, 0, 1)).normalized; // 計算目標移動方向 Vector3 targetDirection cameraForward * inputDirection.z cameraRight * inputDirection.x; // 平滑旋轉角色朝向移動方向 float targetRotation Mathf.Atan2(targetDirection.x, targetDirection.z) * Mathf.Rad2Deg; float rotation Mathf.SmoothDampAngle(transform.eulerAngles.y, targetRotation, ref rotationVelocity, rotationSmoothTime); transform.rotation Quaternion.Euler(0.0f, rotation, 0.0f); // 決定移動速度是否沖刺 float targetSpeed (jumpAction.IsPressed() moveInput.magnitude 0.5f) ? sprintSpeed : moveSpeed; // 假設按住Shift沖刺 currentSpeed Mathf.Lerp(currentSpeed, targetSpeed, Time.deltaTime * 10f); // 平滑速度變化 // 移動角色 Vector3 moveDirection Quaternion.Euler(0.0f, targetRotation, 0.0f) * Vector3.forward; controller.Move(moveDirection.normalized * (currentSpeed * Time.deltaTime)); } else { // 沒有輸入時平滑減速 currentSpeed Mathf.Lerp(currentSpeed, 0f, Time.deltaTime * 5f); } } // 處理跳躍與重力 private void HandleJumpAndGravity() { if (grounded) { // 重置下落超時計時器 fallTimeoutDelta fallTimeout; // 停止下落速度如果之前在下落 if (verticalVelocity 0.0f) { verticalVelocity -2f; // 一個小的負值確保角色緊貼地面 } // 處理跳躍 if (jumpAction.triggered jumpTimeoutDelta 0.0f) { // 跳躍速度公式v sqrt(2 * g * h) verticalVelocity Mathf.Sqrt(jumpHeight * -2f * gravity); } // 更新跳躍超時計時器 if (jumpTimeoutDelta 0.0f) { jumpTimeoutDelta - Time.deltaTime; } } else { // 在空中重置跳躍超時開始下落超時 jumpTimeoutDelta jumpTimeout; // 更新下落超時計時器 if (fallTimeoutDelta 0.0f) { fallTimeoutDelta - Time.deltaTime; } } // 應用重力 if (verticalVelocity gravity) // 如果當前垂直速度大于重力極限值則應用重力 { verticalVelocity gravity * Time.deltaTime; } // 應用垂直速度到CharacterController Vector3 verticalMovement new Vector3(0.0f, verticalVelocity, 0.0f) * Time.deltaTime; controller.Move(verticalMovement); } // 在Scene視圖中繪制地面檢測球體便于調試 private void OnDrawGizmosSelected() { if (controller ! null) { Gizmos.color grounded ? Color.green : Color.red; Vector3 spherePosition new Vector3(transform.position.x, transform.position.y groundedOffset, transform.position.z); Gizmos.DrawWireSphere(spherePosition, groundedRadius); } } }代碼關鍵點解析相機相對移動HandleMove方法中的cameraForward和cameraRight計算是核心。我們通過Vector3.Scale(..., new Vector3(1,0,1))來將相機的方向向量投影到XZ水平面忽略Y軸確保移動方向是水平的。平滑旋轉使用Mathf.SmoothDampAngle來實現角色朝向平滑轉向目標移動方向rotationSmoothTime參數控制平滑程度。跳躍物理跳躍的初速度verticalVelocity由公式v sqrt(2 * g * h)計算得出這是經典力學公式確保了無論重力gravity設置為何值跳躍高度jumpHeight都是恒定的。超時機制jumpTimeout和fallTimeout是防止狀態(tài)抖動的關鍵。jumpTimeout確保在起跳后的短暫時間內即使快速再次按下跳躍鍵也不會生效防止“連跳”。fallTimeout確保角色從空中落到地面后需要經過一個短暫延遲才被判定為“完全落地”這可以避免在斜坡邊緣或不平整地面時跳躍狀態(tài)頻繁切換導致的抽搐。地面檢測我們沒有直接使用CharacterController.isGrounded因為它有時不夠精確特別是在斜坡邊緣。我們使用Physics.CheckSphere在角色腳底創(chuàng)建一個球體進行檢測更可靠且可視化通過OnDrawGizmosSelected。3.3 配置Cinemachine FreeLook相機腳本寫好后回到Unity編輯器進行配置和調試?;A綁定確保ThirdPersonController腳本上的Camera Transform字段已經拖拽賦值給了場景中的Main Camera或你的Cinemachine虛擬相機所在的GameObject。實際上由于Cinemachine Brain控制著主相機主相機的Transform會實時更新所以引用主相機即可。調試FreeLook參數Orbits調整Top/Middle/Bottom Rigs的Height和Radius找到最適合你角色和場景的鏡頭距離和高度。Body在CinemachineFreeLook組件的Body部分Binding Mode建議選擇World Space這樣鏡頭旋轉更符合直覺。Damping阻尼可以調整鏡頭跟隨的平滑度X/Y/Z分別對應不同軸向值越大越平滑但延遲也越大通常(0.2, 0.2, 0.2)是個不錯的起點。AimAim部分保持默認的Composer即可它會努力將Look At目標保持在屏幕中央。Input在Input Axis部分確保X Axis和Y Axis的Input Axis Name設置正確。如果你使用Input System這里可能需要自定義。更常見的做法是不在這里綁定輸入而是通過腳本來控制。我們可以禁用這里的Input Axis將Max Speed設為0然后通過腳本獲取鼠標輸入直接賦值給虛擬相機的X Axis和Y Axis的Value。這樣能更好地與我們的輸入系統集成。通過腳本控制Cinemachine鏡頭旋轉 創(chuàng)建另一個腳本CameraInputHandler掛在CM vcam1上。using UnityEngine; using Cinemachine; using UnityEngine.InputSystem; public class CameraInputHandler : MonoBehaviour { [SerializeField] private float lookSpeedX 0.5f; [SerializeField] private float lookSpeedY 0.3f; [SerializeField] private bool invertY false; private CinemachineFreeLook freeLookCamera; private PlayerInput playerInput; private InputAction lookAction; private void Awake() { freeLookCamera GetComponentCinemachineFreeLook(); // 假設PlayerInput在Player對象上這里通過Find查找生產環(huán)境建議用更穩(wěn)健的方式 playerInput FindObjectOfTypePlayerInput(); if (playerInput ! null) { lookAction playerInput.actions[Look]; } // 禁用Cinemachine自帶的輸入由本腳本驅動 if (freeLookCamera ! null) { freeLookCamera.m_XAxis.m_InputAxisName ; freeLookCamera.m_YAxis.m_InputAxisName ; freeLookCamera.m_XAxis.m_InputAxisValue 0; freeLookCamera.m_YAxis.m_InputAxisValue 0; } } private void Update() { if (freeLookCamera null || lookAction null) return; Vector2 lookDelta lookAction.ReadValueVector2(); // 應用靈敏度并考慮反轉 float xInput lookDelta.x * lookSpeedX * Time.deltaTime * 100; // 乘以100使數值更易調節(jié) float yInput lookDelta.y * lookSpeedY * Time.deltaTime * 100; yInput invertY ? yInput : -yInput; // 通常Y軸需要反轉 // 直接驅動Cinemachine的軸值 freeLookCamera.m_XAxis.m_InputAxisValue xInput; freeLookCamera.m_YAxis.m_InputAxisValue yInput; } }這個腳本接管了Cinemachine FreeLook的旋轉輸入使其與新的Input System無縫對接并且你可以方便地調整靈敏度和是否反轉Y軸。4. 高級調試、優(yōu)化與避坑指南基礎功能跑通后要打磨出好的手感還需要處理很多細節(jié)和邊界情況。4.1 相機碰撞規(guī)避配置默認情況下Cinemachine虛擬相機會穿墻。你需要為虛擬相機添加CinemachineCollider擴展。選中CM vcam1在Inspector中點擊Add Extension選擇CinemachineCollider。關鍵參數Avoid Obstacles: 勾選這是主要功能。Distance Limit: 相機與障礙物的最小距離例如0.5。Camera Radius: 將相機視為一個球體的半徑用于碰撞檢測例如0.2。這能防止相機卡進細小的縫隙。Damping: 規(guī)避動作的平滑度。Strategy: 通常選擇Pull Camera Forward它會將相機拉向角色。Smoothing Time: 規(guī)避動作后的平滑恢復時間。在Layer Mask中排除玩家角色層Player避免相機把角色自己當成障礙物。4.2 角色旋轉與相機旋轉的協調問題一個常見問題是當玩家使用鼠標旋轉相機時角色的移動方向是否應該立即改變這取決于游戲類型?!逗诎抵辍?《怪物獵人》風格角色移動方向始終與左搖桿鍵盤輸入方向一致與鏡頭方向無關。鏡頭旋轉只影響視角不影響移動方向。我們的代碼目前是這種?!稇?zhàn)神》新風格角色移動方向始終朝向鏡頭前方。按下“前”角色永遠向屏幕上方跑。這需要修改HandleMove讓角色的旋轉直接與相機Y軸旋轉同步而不是根據輸入方向平滑插值。我們的實現是第一種風格。如果你想要第二種可以在HandleMove中當有移動輸入時直接將角色的Y軸旋轉設置為相機的Y軸旋轉if (inputDirection.magnitude 0.1f cameraTransform ! null) { // 直接使用相機Y軸旋轉 float targetRotation cameraTransform.eulerAngles.y; transform.rotation Quaternion.Euler(0.0f, targetRotation, 0.0f); // ... 后續(xù)移動計算 }4.3 斜坡與臺階處理CharacterController自帶slopeLimit坡度限制默認45度和stepOffset臺階高度默認0.3參數。這些參數在Inspector中可以直接調整。slopeLimit角色可以行走的最大坡度。超過此坡度角色會滑落或被阻擋。對于大多數游戲45度是合理的。stepOffset角色可以自動邁上的最大臺階高度。設置得太高角色可能會“飄”上過高的障礙設置得太低上樓梯會有卡頓感。通常設置在0.2到0.4之間需要根據角色和場景比例調整。注意stepOffset的工作原理是在移動前在角色前方做一個小的向上檢測。如果檢測到臺階會嘗試將角色提升到臺階頂部。這可能會與復雜的碰撞體產生意外交互需要測試。4.4 性能與精度優(yōu)化地面檢測頻率我們的GroundedCheck每幀都在Update中調用使用Physics.CheckSphere。這是一個球形Overlap檢測開銷比射線檢測大但更準確。如果性能吃緊可以考慮每2-3幀檢測一次或者使用CharacterController.isGrounded結合射線檢測進行優(yōu)化。輸入處理在Update中每幀讀取輸入是標準做法。確保你的PlayerInput組件設置為“Send Messages”或“Invoke Unity Events”模式而不是更耗能的“Invoke CSharp Events”除非你需要其高級特性。Cinemachine更新頻率CinemachineBrain默認在LateUpdate中更新這很好。確保場景中激活的虛擬相機數量盡可能少不用的可以禁用。4.5 常見問題排查速查表問題現象可能原因解決方案角色移動時相機劇烈抖動1. Cinemachine阻尼值太小。2. 角色控制器和相機都在Update中移動存在執(zhí)行順序競爭。3.CharacterController.Move()在同一幀被多次調用。1. 增大FreeLook相機Body的Damping值如從0.1調到0.3。2. 確保相機控制Cinemachine Brain在LateUpdate執(zhí)行默認就是。角色移動在Update執(zhí)行。3. 檢查代碼確保一幀內只調用一次controller.Move。角色跳躍后在空中“黏”一下才下落重力值 (gravity) 太小或跳躍速度 (verticalVelocity) 初始值太大導致上升時間過長。增大gravity的絕對值如從-9.81加大到-15或-20。同時按公式v sqrt(2 * g * h)重新計算跳躍初速度確保跳躍高度h不變。相機穿墻或卡進模型內部1. 未添加或未正確配置CinemachineCollider。2.CinemachineCollider的Camera Radius太小。3. 障礙物碰撞體不是MeshCollider且未勾選Convex或是Trigger。1. 確保添加了CinemachineCollider擴展并勾選Avoid Obstacles。2. 適當增大Camera Radius如0.3。3. 確保環(huán)境碰撞體是有效的非Trigger如果是MeshCollider用于復雜形狀需勾選Convex。在斜坡邊緣或樓梯上跳躍狀態(tài)不穩(wěn)定頻繁切換地面檢測過于敏感。CharacterController.isGrounded或CheckSphere在邊緣處時真時假。引入fallTimeout機制如我們代碼中所做?;蛘呤褂靡粋€小的“接地緩存時間”在離開地面后的幾幀內仍視為接地。也可以稍微增大groundedRadius。鼠標控制相機旋轉不生效或卡頓1.CameraInputHandler腳本未正確獲取輸入或未綁定到虛擬相機。2. Cinemachine FreeLook的Input Axis未清空與腳本控制沖突。3. 鼠標靈敏度 (lookSpeedX/Y) 設置過低。1. 檢查CameraInputHandler是否掛載到CM vcam1上并檢查PlayerInput引用。2. 確保FreeLook的X Axis和Y Axis的Max Speed已設為0或按我們腳本那樣清空InputAxisName。3. 增大lookSpeedX/Y值。移動方向與相機朝向不一致HandleMove中計算cameraForward和cameraRight時沒有正確投影到水平面忽略了Y軸。確認使用了Vector3.Scale(cameraTransform.forward, new Vector3(1, 0, 1)).normalized來獲取水平方向。5. 功能擴展與進階思路基礎系統穩(wěn)定后你可以考慮加入更多功能來提升體驗鏡頭震動創(chuàng)建另一個低優(yōu)先級的Cinemachine虛擬相機為其添加CinemachineImpulseListener擴展。然后通過CinemachineImpulseSource組件在特定時刻如落地、受擊觸發(fā)震動Cinemachine會自動混合震動效果到主鏡頭。不同狀態(tài)下的鏡頭你可以創(chuàng)建多個CinemachineVirtualCamera不一定是FreeLook例如一個用于正常行走一個用于瞄準拉近鏡頭一個用于對話固定鏡頭。通過設置不同的Priority并在代碼中動態(tài)切換來實現鏡頭切換。使用CinemachineBlender可以定義切換時的混合曲線。更復雜的移動在HandleMove中加入加速度、減速度曲線讓起步和停止更有質感。加入慣性讓角色轉向時有一個輕微的“漂移”感。這些都可以通過Mathf.SmoothDamp或動畫曲線AnimationCurve來控制速度參數。動畫狀態(tài)機集成我們的控制器目前只處理物理移動。你需要將計算出的速度currentSpeed、是否接地grounded、垂直速度verticalVelocity等參數通過Animator.SetFloat或SetBool傳遞給角色的Animator Controller來驅動走、跑、跳、下落等動畫狀態(tài)。這套“Cinemachine CharacterController”的方案就像為你搭建了一個堅固且可塑性極強的骨架。它處理好了最棘手、最底層的相機和物理交互問題讓你能將精力集中在打磨角色動作、設計關卡和豐富游戲邏輯這些更能體現游戲魅力的部分上。

相關新聞

僅限前500名獲?。篈I邏輯思維訓練能力圖譜V2.3(含動態(tài)評估引擎+個性化訓練路徑生成器——已服務阿里達摩院/DeepMind 17個核心項目)

僅限前500名獲取:AI邏輯思維訓練能力圖譜V2.3(含動態(tài)評估引擎+個性化訓練路徑生成器——已服務阿里達摩院/DeepMind 17個核心項目)

更多請點擊: https://codechina.net 第一章:AI邏輯思維訓練的范式演進與核心價值 AI邏輯思維訓練已從早期基于規(guī)則引擎的符號推理,逐步演進為融合大語言模型理解力、強化學習反饋機制與可驗證形式化邏輯的復合范式。這一演進并非簡單替代&am…

2026/8/3 11:18:46 閱讀更多
醫(yī)療會員制預約系統設計與高并發(fā)解決方案

醫(yī)療會員制預約系統設計與高并發(fā)解決方案

1. 項目概述"會員制醫(yī)療預約服務管理信息系統"這個畢業(yè)設計選題,本質上是一個結合了醫(yī)療行業(yè)特性與會員制服務模式的數字化解決方案。我在醫(yī)療信息化領域工作多年,見過太多預約掛號系統,但真正把會員服務理念融入醫(yī)療管理的并不多見…

2026/8/3 12:18:47 閱讀更多
Ubuntu系統NVIDIA顯卡驅動安裝與深度學習環(huán)境配置全攻略

Ubuntu系統NVIDIA顯卡驅動安裝與深度學習環(huán)境配置全攻略

最近在折騰深度學習環(huán)境,被顯卡驅動、CUDA、cuDNN的版本兼容性搞得焦頭爛額。特別是新裝Ubuntu系統后,圖形界面進不去、循環(huán)登錄、黑屏等問題層出不窮,網上教程又零散過時,踩坑無數。 本文旨在提供一份從零開始的、保姆級的NVIDI…

2026/8/3 12:18:47 閱讀更多
2026平頂山黃金回收白銀回收鉑金回收靠譜臨街實體公安備案支持到店核驗門店聯系方式推薦

2026平頂山黃金回收白銀回收鉑金回收靠譜臨街實體公安備案支持到店核驗門店聯系方式推薦

2026平頂山黃金白銀鉑金回收實測榜單|公安備案臨街實體門店推薦 平頂山街頭巷尾貴金屬回收店鋪遍地叢生,行業(yè)套路層出不窮,不少市民變現時遭遇虛高報價、克扣損耗、未經同意熔金壓價等問題。為幫助本地居民規(guī)避消費陷阱,小編實地…

2026/8/3 12:18:47 閱讀更多
在電腦上免費暢玩Switch游戲:yuzu模擬器終極使用指南

在電腦上免費暢玩Switch游戲:yuzu模擬器終極使用指南

在電腦上免費暢玩Switch游戲:yuzu模擬器終極使用指南 【免費下載鏈接】yuzu 任天堂 Switch 模擬器 項目地址: https://gitcode.com/GitHub_Trending/yu/yuzu 想要在電腦上體驗任天堂Switch游戲的魅力嗎?yuzu模擬器為你打開了這扇大門。作為當前最…

2026/8/3 12:08:47 閱讀更多
3分鐘搞定!QQ空間歷史說說完整備份終極指南

3分鐘搞定!QQ空間歷史說說完整備份終極指南

3分鐘搞定!QQ空間歷史說說完整備份終極指南 【免費下載鏈接】GetQzonehistory 獲取QQ空間發(fā)布的歷史說說 項目地址: https://gitcode.com/GitHub_Trending/ge/GetQzonehistory 你是否曾想過,那些年發(fā)過的QQ空間說說,那些記錄青春的文字…

2026/8/2 0:04:01 閱讀更多
AMAT 0100-02186 I/O 分配 PCB

AMAT 0100-02186 I/O 分配 PCB

AMAT 0100-02186 I/O分配PCB板是應用材料(Applied Materials)公司生產的一款用于半導體設備的I/O信號分配電路板。該型號(0100-02186)的核心特點如下:專用于Endura等半導體工藝腔室。集成信號路由與分配功能。連接控制…

2026/8/2 2:51:21 閱讀更多
Nissei Corp FFMN-32L-10-T0 40AX 三相異步電動機

Nissei Corp FFMN-32L-10-T0 40AX 三相異步電動機

Nissei Corp FFMN-32L-10-T0 40AX 三相異步電動機是日本日清(Nissei)品牌的一款工業(yè)用三相異步電機,適用于自動化設備及通用機械驅動。該型號(FFMN-32L-10-T0 40AX)的核心特點如下:三相交流異步電動機。額定…

2026/8/2 2:52:49 閱讀更多