基於 Unity 引擎的遊戲開發進階之 逆向動力學

前進動力學

  • 大多數角色動畫都是通過將骨骼的關節角度旋轉到預定值來實現的;
  • 一個子關節的位置是由它的父節點的旋轉角度來決定;
  • 節點鏈末端的節點位置,是由節點鏈上各個節點的旋轉角度和相對位置來決定的。

這種決定骨骼節點位置的方式就是前進動力學。

給定末端節點的位置,從而逆推出節點鏈上所有其他節點的合理位置的方法稱為逆向動力學 Inverse Kinematic ,簡稱IK。

Unity 中的 Mecanim 動畫系統,允許開發者通過腳本,實現角色模型的逆向動力學,涉及的函數和欄位包括:

  • SetIKPosition();
  • SetIKRotation();
  • SetLookAtPosition();
  • SetIKPositionWeight();
  • SetRotationWeight();
  • SetPosition/bodyRotation();

我們現在實現的是一個 FPS 遊戲,以下是實現玩家持槍動作的基本步驟:

1.在玩家動畫控制器 Animator 中開啟 IK 功能

2.創建球體遊戲對象,作為 IK 標記物

圖中的 BodyObj, LeftHandObj, RightHandObj, GunBarrelEnd 都是球體對象。

3.通過 IK 控制腳本,把 IK 標記物的 Position 和 Rotation 屬性,賦值給角色模型頭部、手部和軀幹等骨骼節點相應的 IK 屬性

給玩家添加 IKController 腳本,分別將上述四個球體對象賦值。禁用 IK 標記物球體對象的 MeshRenderer 組件和移除 SphereCollider 組件。

public class IKController : MonoBehaviour {nntAnimator animator;tttttt// 玩家動畫控制器ntpublic bool isActive = true;ttt// 是否啟用IKntpublic Transform lookObj = null;tt// 玩家頭部IK標記物ntpublic Transform leftHandObj = null;t// 玩家左手IK標記物ntpublic Transform rightHandObj = null;t// 玩家右手IK標記物ntpublic Transform bodyObj = null;tt// 玩家身體IK標記物nntvoid Start(){ntt//獲取遊戲對象的 Animator 組件nttanimator = GetComponent<Animator> ();nt}nntvoid OnAnimatorIK()nt{nttif (animator) {ntttif (isActive) {nttttif (lookObj != null) {nttttt//設置玩家的頭部IK,使玩家的頭部面向頭部IK標記物所在的位置ntttttanimator.SetLookAtWeight (1.0f);ntttttanimator.SetLookAtPosition (lookObj.position);ntttt}nttttif (bodyObj != null) {nttttt//設置玩家軀幹IK,使玩家軀幹的旋轉角與bodyObj對象的旋轉角度相同ntttttanimator.bodyRotation = bodyObj.rotation;ntttt}nttttif (leftHandObj != null) {nttttt//設置玩家左手的IK,使玩家左手的位置盡量靠近leftHandObj對象,左手的朝向與leftHandObj相同ntttttanimator.SetIKPositionWeight (AvatarIKGoal.LeftHand, 1.0f);ntttttanimator.SetIKRotationWeight (AvatarIKGoal.LeftHand, 1.0f);ntttttanimator.SetIKPosition (AvatarIKGoal.LeftHand, leftHandObj.position);ntttttanimator.SetIKRotation (AvatarIKGoal.LeftHand, leftHandObj.rotation);ntttt}nttttif (rightHandObj != null) {nttttt//設置玩家右手的IK,使玩家右手的位置盡量靠近RightHandObj對象,右手的朝向與RightHandObj相同ntttttanimator.SetIKPositionWeight (AvatarIKGoal.RightHand, 1.0f);ntttttanimator.SetIKRotationWeight (AvatarIKGoal.RightHand, 1.0f);ntttttanimator.SetIKPosition (AvatarIKGoal.RightHand, rightHandObj.position);ntttttanimator.SetIKRotation (AvatarIKGoal.RightHand, rightHandObj.rotation);ntttt}nttt} else {ntttt//取消玩家角色的IK,使玩家角色的頭部,驅趕,左右手的位置和朝向受正向動力學控制nttttanimator.SetLookAtWeight (0);nttttanimator.SetIKPositionWeight (AvatarIKGoal.LeftHand, 0);nttttanimator.SetIKRotationWeight (AvatarIKGoal.LeftHand, 0);nttttanimator.SetIKPositionWeight (AvatarIKGoal.RightHand, 0);nttttanimator.SetIKRotationWeight (AvatarIKGoal.RightHand, 0);nttt}ntt}nt}n}n

4.在預覽狀態下,調整 IK 標記物的位置和朝向,使玩家頭部,手部,軀幹等骨骼節點,具有合適的位置和朝向,實現玩家的持槍動作。

實現玩家換槍功能

不同的槍械具有不同的外觀,射擊速度,攻擊距離,傷害等。玩家可選擇合適的槍械進行戰鬥。

玩家換槍功能的實現步驟:

  1. 為玩家對象添加新的槍械模型
  2. 為新的槍械模型設置正確的角色模型 IK
  3. 為新的槍械模型添加攻擊腳本,設置攻擊力,攻擊距離,攻擊速度和攻擊特效等屬性
  4. 為玩家添加換槍腳本,換槍腳本的武器列表用來記錄玩家所有的槍械對象

前三步與上文沒什麼區別,主要介紹第四步。

public class PlayerWeaponSwitcher : MonoBehaviour {nntpublic Transform[] weaponList;t//玩家武器列表nntprivate IKController ikController;t//玩家IK控制器ntprivate int currentIdx = 0;t//當前使用的槍序號ntprivate int weaponNum = 0;t//武器列表中的槍總數nntvoid Start () {ntt//獲取玩家IK控制器nttikController = transform.GetComponent<IKController> ();tntt//獲取玩家當前槍的數量nttweaponNum = weaponList.Length;ntt//設置當前槍序號為0;nttcurrentIdx = 0;ntt//讓玩家使用下一把搶nttchangeNextWeapon ();nt}nntvoid Update () {ntt//玩家按下Fire2,執行換槍邏輯nttif (CrossPlatformInputManager.GetButtonDown("Fire2")) {ntttchangeNextWeapon ();ntt}nt}nntpublic void changeNextWeapon()nt{ntt//只有武器列表中的槍支數量大於1,才執行換槍動作nttif (weaponNum <= 1) ntttreturn;nntt//取出下一把槍的序號nttint newIdx = (currentIdx + 1) % weaponNum;nntt//使用下一把槍的IK標記物,設置玩家角色的IK,使玩家正確持槍nttTransform newWeapon = weaponList [newIdx];nttTransform rightHand = newWeapon.Find ("RightHandObj");nttTransform leftHand = newWeapon.Find ("LeftHandObj");nttTransform gunBarrelEnd = newWeapon.Find ("GunBarrelEnd");nttikController.leftHandObj = leftHand;nttikController.rightHandObj = rightHand;nttikController.lookObj = gunBarrelEnd;nntt//激活新槍,禁用舊槍nttnewWeapon.gameObject.SetActive (true);nttweaponList [currentIdx].gameObject.SetActive (false);nntt//更新當前使用的武器序號nttcurrentIdx = newIdx;nt}n}n

推薦閱讀:

TAG:Unity游戏引擎 | 物理学 | 游戏开发 |