Unity人形角色IK優化指南

目錄

Unity中人形角色的IKI

站立、奔跑IK

1. 接觸面法線

2. 調整質心位置

3. 保持原本朝向

攀爬IK

1. 四肢貼合

2. 保持身體與攀爬面的距離

3. 適應外拐角

瞄準IK

1. 頭部朝向

2. 手臂朝向

尾聲


本文將嘗試僅使用Untiy內置的Animator來解決常見的幾種運動所需的IK。也會給出核心功能的代碼實現。

Unity中人形角色的IKI

逆運動學(Inverse Kinematics,簡稱IK)在工業機器人領域是指通過末端執行器的位姿來求解相應關節變量的過程。游戲開發中也存在類似需求,即根據末端肢體的位姿來調整身體其他部位的位置。值得慶幸的是,Unity引擎已經幫我們處理了這個復雜的計算過程。

對于使用Avatar的人形角色動畫,Unity內置的Animator系統支持調整5個關鍵部位的IK:頭部、左手、右手、左腳和右腳。開發者只需設置好這些部位的IK位置和旋轉參數,Unity就會自動計算并調整角色的骨骼運動。

PS:左腳、右腳、左手、右手的IK設置可以通過Animator.SetIKPosition等系列函數,通過AvatarIKGoal的枚舉來選擇部位;而頭部則通過Animator.SetIKPosition等系列函數來控制。

這些函數要在OnAnimatorIK生命周期函數中調用才奏效

站立、奔跑IK

這應該是人形角色最常規的IK了,通常的站立、奔跑、行走等動畫都默認是在水平地面上的。但實際游戲地形會復雜很多,我們就需要調節足部的IK來貼合不同的地面。

1. 接觸面法線

首先要做的就是通過物理檢測找到「落腳點」,簡單的射線檢測就可以做到,射線檢測返回的RaycastHit參數會告訴我們接觸點和接觸點的法線,以此就可以來調整腳的位置與姿態。

  1. 通過animator.GetBoneTransform得到腳部骨骼的Transform,進而得到腳部骨骼position。從該位置上方一段距離開始,向下檢測接觸面。人形角色通常是膠囊體,所以邁步時,腳很有可能就超出了膠囊體范圍,而腳本身又沒有碰撞體,就容易進入碰撞體內部,這時如果只是從腳本身開始檢測就會檢測失敗,所以從上方開始檢測。

csharp

/// <summary>
/// 實現類似 pointA.axis = pointB.axis + offset 指定軸向的變化
/// </summary>
private void FoottCheck(HumanBodyBones footBone, int iKGoal_Int, Vector3 upAxis)
{var footPos = animator.GetBoneTransform(footBone).position;//足部上移一段距離后的位置作為射線起點var originPos = footPos + upAxis * upOffset;//檢測時指定地面層級遮罩,一般可以忽視觸發器if(Physics.Raycast(originPos, -upAxis, out hitInfo, checkRayLength, checkMask, QueryTriggerInteraction.Ignore)){//不直接將足部位置設置為檢測到的hit.point//而是將hit.point在upAxis上的分量賦值給足部//相當于把足部沿upAxis方向移到hit.point等高度CalculateAxisValue(ref footPos, hitInfo.point, upAxis);//記錄下調整后的足部位置iKGoalPositions[iKGoal_Int] = footPos;//記錄下從upAxis到接觸面法線所需的旋轉iKGoalRotations[iKGoal_Int] = Quaternion.FromToRotation(upAxis, hitInfo.normal);}
}/// <summary>
/// 輔助函數,功能: pointA.axis = pointB.axis + offset
/// </summary>
private void CalculateAxisValue(ref Vector3 pointA, Vector3 pointB, Vector3 axis, float offset = 0)
{pointA += axis * (Vector3.Dot(pointB - pointA, axis) + offset);
}

2. 調整質心位置

光調整腳的位置是不夠的,因為這樣容易出現一只腳夠得著平面,但另一只則?「虛空接觸」?的情況(左側就是沒調整的,右側就是調整后的):

這也是上一步中,要用別扭的方法移動腳部的原因。這樣我們就能算得哪只腳觸碰接觸面所需要移動的距離較大了,我們就將較大的這個偏移量同步應用到animator.bodyPositon就可以了!

csharp

/// <summary>
/// 根據足部在當前up軸的偏差來調整質心位置(身體升降)
/// 為了讓奔跑連貫,奔跑時不建議開啟,僅靜止時開啟
/// </summary>
/// <param name="isIdle">是否是閑置狀態</param>
private void MoveCentroidPosition(bool isIdle)
{if (isIdle && iKGoalPositions[leftFoot_Idx] != Vector3.zero && iKGoalPositions[rightFoot_Idx] != Vector3.zero && lastCentriodPosInUpAxis != 0) //非閑置時、未獲取正確信息時不做調整{var animTransform = animator.transform;//取離軀體最遠的腳(更需要貼近地面的腳)與身體的差距作為偏移值var leftOffset = Vector3.Dot(animTransform.up, iKGoalPositions[0] - animTransform.position);var rightOffset = Vector3.Dot(animTransform.up, iKGoalPositions[1] - animTransform.position);finalCentroidOffset = leftOffset < rightOffset ? leftOffset : rightOffset;//在指定方向上線性逼近Vector3 newCentroidPos = animator.bodyPosition + animTransform.up * finalCentroidOffset;float newCentroidPosInUpAxis = Vector3.Dot(animTransform.up, newCentroidPos);//用插值的方式改變質心位置,更自然newCentroidPosInUpAxis = Mathf.Lerp(lastCentriodPosInUpAxis, newCentroidPosInUpAxis, centroidMoveSpeed);CalculateAxisValue(ref newCentroidPos, Vector3.zero, animTransform.up, newCentroidPosInUpAxis);//應用調整后的位置animator.bodyPosition = newCentroidPos; }//將當前質心位置記錄為「上次質心在upAxis上的位置」,方便下一幀判斷lastCentriodPosInUpAxis = Vector3.Dot(animTransform.up, animator.bodyPosition);
}

你可能注意到了,質心調整并不一定要時時開啟,否則像快速上樓梯等斜面變化頻繁的情況,可能會劇烈抖動

3. 保持原本朝向

我們希望足部在調整后仍能保持動畫原本的偏航角,(也就是說該外八的還是外八,內八的還是內八;而如果像這么做的話,就會導致腳筆直朝向玩家前方:

csharp

iKGoalRot = iKGoalRotations[leftFoot_Int] * animator.transform.rotation;
animator.SetIKRotation(AvatarIKGoal.LeftFoot, iKGoalRot);

顯然,問題就出在我們是基于animator.transform.rotation來調整的。所以我們應該在真正調整朝向前,先記錄腳部IK原本的朝向,再在記錄下的這個朝向上應用步驟1中得到的「貼合地面的旋轉」。

csharp

private void MoveFeetToIKPos(AvatarIKGoal iKGoal, int iKGoal_Int)
{//真正調整前,先記錄原本IK的位置和朝向var animTransform = animator.transform;var iKGoalPos = animator.GetIKPosition(iKGoal);var iKGoalRot = animator.GetIKRotation(iKGoal);//如果FixedUpdate中沒有檢測到信息就不更新IKif(iKGoalPositions[iKGoal_Int] != Vector3.zero) {//將當前IKGoal位置和目標IKGoal位置都轉到當前坐標系下iKGoalPos = animTransform.InverseTransformPoint(iKGoalPos);iKGoalPositions[iKGoal_Int] = animTransform.InverseTransformPoint(iKGoalPositions[iKGoal_Int]);//從當前坐標的y方向線性逼近目標IKGoal,同樣插值逼近顯得自然var upVar = Mathf.Lerp(lastPosInUpAxis[iKGoal_Int], iKGoalPositions[iKGoal_Int].y, footIKMoveSpeed);iKGoalPos.y += upVar;lastPosInUpAxis[iKGoal_Int] = upVar;//將調整后的位置轉回世界坐標空間(因為SetIKPosition是根據世界坐標的)iKGoalPos = animTransform.TransformPoint(iKGoalPos);//四元數旋轉:原本足部旋轉的基礎上 + 地面貼合旋轉iKGoalRot = iKGoalRotations[iKGoal_Int] * iKGoalRot;animator.SetIKRotation(iKGoal, iKGoalRot);}animator.SetIKPosition(iKGoal, iKGoalPos);//清空信息,以待下次FixedUpdate提供信息iKGoalPositions[iKGoal_Int] = Vector3.zero;
}

攀爬IK

通常人形動畫的攀爬要調整的是四肢的位置,使其貼合墻面。

攀爬IK的設置方式其實和你所實現攀爬系統的邏輯密切相關,我就暫定現在我們已經實現好了一個攀爬系統,它能時時獲取攀爬法線

1. 四肢貼合

最簡單的環境,實現思路與足部貼合地面類似,獲取四肢IKGaol的位置,然后沿角色后方遠離一段距離作為射線檢測的起點,往角色的前方進行檢測。如下圖所示(紅色端為射線起點)

csharp

/// <summary>
/// 通過射線檢測調整攀爬時四肢IK位置、旋轉,并將結果存儲在數組中
/// </summary>
private void LimbsClimb_Solver(int iKGoal_Int, LayerMask climbMask)
{var animTransform = animator.transform;//這里假設在攀爬系統的作用下,角色總能面朝攀爬面,故用forwardvar origin = limbsPositions[iKGoal_Int] - animTransform.forward * limbOffset;if(Physics.Raycast(origin, animTransform.forward, out hitInfo, climbRayLength, climbMask, QueryTriggerInteraction.Ignore)){iKGoalPositions[iKGoal_Int] = hitInfo.point;iKGoalRotations[iKGoal_Int] = Quaternion.FromToRotation(animTransform.forward, -hitInfo.normal);return;}
}

「遠離一段距離」還有其它好處,比如貼合這種上沿或者內拐角

2. 保持身體與攀爬面的距離

讓角色的身體與墻面保持一定距離,可以讓動畫看起來更順眼。因為這位置只和墻面有關,所以調整起來也很簡單(需要用到攀爬法線climbNormal):

csharp

/// <summary>
/// 調整身體離墻的距離
/// </summary>
private void AdjustBodyPos(Vector3 climbNormal, LayerMask climbMask)
{if(Physics.Raycast(animator.bodyPosition, -climbNormal, out hitInfo, climbCornerRayLength, climbMask, QueryTriggerInteraction.Ignore)){animator.bodyPosition = hitInfo.point + climbNormal * climbDisWithWall;}
}

3. 適應外拐角

有一種比較麻煩的地方是「外拐角」,步驟1中的前向射線檢測會撲空。我們需要從兩側向中間檢測

具體思路就是四肢向內側方向進行檢測。而且要多段檢測,也就是將射線起點向前移動幾次,能更好貼合V形角(就算沒有刻意的V形墻面,當角色爬過外墻角時也會變成面向V形角的情況)

我們對步驟1中的函數進行補充:

csharp

/// <summary>
/// 通過射線檢測調整攀爬時四肢IK位置、旋轉,并將結果存儲在數組中
/// </summary>
private void LimbsClimb_Solver(int iKGoal_Int, LayerMask climbMask)
{var animTransform = animator.transform;//這里假設在攀爬系統的作用下,角色總能面朝攀爬面,故用forwardvar origin = limbsPositions[iKGoal_Int] - animTransform.forward * limbOffset;if(Physics.Raycast(origin, animTransform.forward, out hitInfo, climbRayLength, climbMask, QueryTriggerInteraction.Ignore)){iKGoalPositions[iKGoal_Int] = hitInfo.point;iKGoalRotations[iKGoal_Int] = Quaternion.FromToRotation(animTransform.forward, -hitInfo.normal);return;}//——————————————————新增部分————————————————else //當前向射線檢測不到時,大概率進入了外拐角{//射線起點回到原本位置origin += animTransform.forward * limbOffset;//根據肢體所屬左右來設置檢測方向var dir = (iKGoal_Int & 1) == 0 ? animTransform.right : -animTransform.right;//向中間進行多次射線檢測for(int i = 0; i < cornerRayCount; ++i){if(Physics.Raycast(origin, dir, out hitInfo, climbCornerRayLength, climbMask, QueryTriggerInteraction.Ignore)){iKGoalPositions[iKGoal_Int] = hitInfo.point;iKGoalRotations[iKGoal_Int] = Quaternion.FromToRotation(animTransform.forward, -hitInfo.normal);return;}//如果這次沒檢測到,就將起點前移origin += cornerRayGap * animTransform.forward;}}
}

瞄準IK

第三人稱射擊游戲的瞄準,需要讓玩家的頭能朝向瞄準的地方,玩家拿槍的手也指向瞄準的地方。

1. 頭部朝向

頭部的處理,我倒是比較簡單。因為我的角色會轉身,所以頭部只需要調整俯仰角就可以了。而頭部朝向不一定要百分百朝著瞄準點,看著像個樣子就差不多,所以我的選擇是——看向手里武器

csharp

public void HeadLookAt(Vector3 weaponPos, float weight)
{animator.SetLookAtPosition(weaponPos);animator.SetLookAtWeight(weight);
}

2. 手臂朝向

調整手臂朝向的一大難點是保持手部姿勢,直接設置朝向容易破壞持械姿勢。

我的想法是:讓雙手IK的上下活動限制在一個球面上,這樣一來,無論雙臂朝向何方手臂伸展的距離都不會變化,這樣就能保證動畫的姿勢維持。

至于這個球心位置,我是簡單地選擇角色胸骨骼位置,效果還行,動作變形程度不會很大(也可能是因為角色拿著手槍的原因)

csharp

public void BodyLookAt(Vector3 pos)
{//奔跑時胸骨骼會上下移動,瞄準方向會劇烈變化,選bodyPosition來算方向更穩定Vector3 handIKPos, dir = (pos - animator.bodyPosition).normalized;Vector3 chestPos = animator.GetBoneTransform(HumanBodyBones.Chest).position;//雙手IK位置調整var handIKGoal = AvatarIKGoal.LeftHand;handIKPos = animator.GetIKPosition(handIKGoal);var originDis = (chestPos - handIKPos).magnitude; //保持半徑距離,圓形擺動handIKPos = chestPos + dir * originDis;//奔跑時胸骨骼可能會小幅度上下移動,讓手部IK位置也做同樣移動animator.SetIKPosition(handIKGoal, handIKPos + animTransform.up * animator.deltaPosition.y);animator.SetIKPositionWeight(handIKGoal, 1);var handIKGoal = AvatarIKGoal.RightHand;handIKPos = animator.GetIKPosition(handIKGoal);var originDis = (chestPos - handIKPos).magnitude; handIKPos = chestPos + dir * originDis;animator.SetIKPosition(handIKGoal, handIKPos + animTransform.up * animator.deltaPosition.y);animator.SetIKPositionWeight(handIKGoal, 1);
}

尾聲

還是再次聲明一下,這些調整策略都是經驗之談,一定還有更好的調整方式。而且追求更高質量的IK或更多部位IK的調整,可以使用商店插件,或者Unity包里的Animator Rigging。本文就當拋磚引玉了捏!(′▽`)

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/93378.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/93378.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/93378.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于wireshark的USB 全速硬件抓包工具USB Sniffer Lite的使用

1、前言 隨著MCU的發展和需求的增多&#xff0c;USB已成為主流MCU的標配外設&#xff0c;但很多還是全速或低速IP&#xff0c;因此往往用不上高速抓包設備。 2、安裝wireshark和拷貝抓包插件 將抓包插件拷貝到wireshark的extcap目錄里&#xff0c;可參考基于wireshark的USB …

easyexcel模板導出Map數據時空值列被下一行列非空數據覆蓋

場景是&#xff1a;我用模板導出數據&#xff0c;sheet數據是一個List<String,Object>集合&#xff0c;然后發現第一行的第三列應該為空&#xff0c;但是不為空&#xff0c;填上了第二行的第三列數據&#xff1b;就像按列寫數據&#xff0c;碰到空值&#xff0c;下一行數…

并行Builder-輸出型流程編排的新思路

如果對于框架的介紹不感興趣的可以直接跳到Getting Started快速開始 在設計一款數據加載編排框架時&#xff0c;除了任何框架都必須具備的可靠性與穩定性之外&#xff0c;對于本次編排框架的設計&#xff0c;我們把核心目標放在高性能與易用性上。這不僅要求框架能夠快速、高效…

C#WPF實戰出真汁03--登錄界面設計

1、登錄界面設計要點簡潔直觀的布局 登錄界面應避免復雜元素&#xff0c;突出核心功能。通常包含用戶名/郵箱輸入框、密碼輸入框、登錄按鈕及可選功能&#xff08;如“記住我”“忘記密碼”&#xff09;。保持表單字段不超過5個&#xff0c;減少用戶認知負擔。清晰的視覺層次 通…

前端css學習筆記6:盒子模型

本文為個人學習總結&#xff0c;如有謬誤歡迎指正。前端知識眾多&#xff0c;后續將繼續記錄其他知識點&#xff01; 目錄 前言 一、組成 ?編輯content padding border margin margin塌陷 margin合并 使用場景 標題與段落間距 卡片列表布局 二、內容溢出—overflo…

以下是對智能電梯控制系統功能及系統云端平臺設計要點的詳細分析,結合用戶提供的梯控系統網絡架構設計和系統軟硬件組成,分點論述并補充關鍵要點:

智能電梯控制系統功能及系統云端平臺設計要點一、梯控系統網絡架構設計服務本地化&#xff1a;電梯門禁服務器本地化部署&#xff1a;核心服務器部署在項目本地&#xff0c;確保數據安全、運維及時性&#xff0c;減少網絡依賴。需支持本地獨立運行&#xff0c;避免云端故障影響…

全球電商業財一體化:讓出海品牌實現“看得見的增長“

內外貿并行的數字化挑戰在數字經濟浪潮下&#xff0c;中國品牌呈現"雙輪驅動"增長態勢&#xff1a;一邊深耕天貓、京東、抖音等國內主流平臺&#xff0c;一邊通過Amazon、Tiktok、eBay、Temu等渠道拓展全球市場。然而&#xff0c;多平臺、多幣種、多地區的復雜運營環…

Nacos-5--Nacos2.x版本的通信原理

Nacos 2.x引入了gRPC作為其主要的通信協議&#xff0c;取代1.x版本中的HTTP長輪詢和UDP通信方式&#xff0c;顯著提升了性能、實時性和穩定性。gRPC是一個高性能、開源的遠程過程調用&#xff08;RPC&#xff09;框架&#xff0c;它基于HTTP/2標準設計&#xff0c;并使用Protoc…

如何以開發者的身份開發出比python更好的應用軟件?

作為一名擁有多年軟件架構經驗的開發者,我見證了Python從實驗室腳本語言成長為數字時代基礎設施的完整歷程。2008年我參與歐洲核子研究中心的粒子數據分析系統時,Python還是輔助工具,而今天它已成為驅動LIGO引力波探測的核心引擎——這種躍遷絕非偶然。 一、Python的巔峰應…

zynq代辦事項

測試verilog按鍵 1.0 按鍵->隊列->串口 1.1 按鍵模塊ming_key包括 按下,松開,單擊,雙擊,長按,事件 1.2 隊列模塊ming_fifo存儲按鍵發出的[事件和事件戳] 1.3 頂層模塊TOP 輪詢 ming_fifo,將讀到的事件用串口封裝成數據包發給串口助手 測試zynq的M_AXI_GP0 1.0 用axi_li…

【Redis】Redis典型應用——緩存

目錄 一.什么是緩存 二.使用Redis作為緩存 2.1.關系型數據庫的缺點 2.2.使用Redis作為MySQL的緩存 三. 緩存更新策略:識別熱點數據 3.1.定期更新 3.2.實時生成 四.緩存的使用注意事項 4.1.緩存預熱(Cache preheating) 4.2.關于緩存穿透 (Cache penetration) 4.3..關…

C#控制臺項目,鼠標點擊后線程會暫停

C#控制臺應用程序&#xff0c;點擊后就會暫停運行&#xff0c;但是我想讓它運行不受鼠標點擊的影響。 下面是程序演示&#xff1a;class Program{static void Main(string[] args){Console.WriteLine("Hello");int index 0;while(true){Console.WriteLine($"in…

云計算-實戰 OpenStack 私有云運維:服務部署、安全加固、性能優化、從服務部署到性能調優(含數據庫、內核、組件優化)全流程

簡介 此次圍繞OpenStack 私有云平臺的運維與開發展開,涵蓋了從核心服務安裝到深度優化的全流程實戰內容。文中詳細介紹了 OpenStack 各關鍵組件(如 Keystone、Glance、Nova、Neutron、Cinder 等)的安裝部署方法,包括使用腳本快速搭建服務、創建用戶、上傳鏡像、配置網絡等…

流水的 AI,鐵打的騰訊

騰訊 昨天騰訊公布了 2025 年第二季度的業績報告。 就還是那只鵝&#xff0c;就還是那個超預期。 總營收 1845 億&#xff0c;同比增長 15%&#xff1b;凈利潤 556.3 億&#xff0c;同比增長 17%&#xff1b;經營利潤 692.5 億&#xff0c;同比增長 18%。 這里面最炸裂的&#…

再回C的進制轉換--負數

概念 負數在計算機中以補碼的形式保存&#xff0c;以int類型的-15為例&#xff0c;求補碼先對-15取絕對值&#xff0c;然后對其按位取反(得到反碼)&#xff0c;然后加1&#xff0c;就可以得到其的補碼。 二進制的補碼 -15 (取絕對值)–> 15 --> (十六進制表示)0x000f (按…

項目績效域-筆記

一、項目管理績效域 1. 價值驅動的項目管理知識體系 1&#xff09;體系構成要素 核心轉變&#xff1a;從預測型生命周期&#xff08;計劃驅動&#xff09;轉向價值驅動體系&#xff0c;融合預測型和敏捷方法組成要素&#xff1a; 12個項目管理原則&#xff08;基礎&#xff09;…

怎么判斷晶振的好壞,有什么簡單的辦法

今天來聊聊晶振的好壞判斷方法&#xff0c;3個步驟輕松搞定。外觀檢查&#xff1a;先看臉&#xff0c;再看腳晶振體積雖小&#xff0c;但問題往往寫在“臉上”。第一步&#xff0c;用肉眼觀察&#xff1a;裂痕與破損&#xff1a;晶振表面如果有明顯裂紋或缺口&#xff0c;大概率…

mac下載maven并配置,以及idea配置

文章目錄下載配置settingsidea配置下載 https://maven.apache.org/download.cgi 我下的3.6.3 https://archive.apache.org/dist/maven/maven-3/3.6.3/binaries/ 配置 open ~/.zprofile添加&#xff0c;根據自己安裝路徑修改 export MAVEN_HOME/Users/xxx/tools/apache-mave…

基于機器視覺的車道線檢測與跟蹤關鍵技術研究

摘 要 隨著自動駕駛技術的迅速發展&#xff0c;車道線檢測與跟蹤技術在提高道路安全性和駕駛自動化水平方面發揮著至關重要的作用。本文針對基于機器視覺的車道線檢測與跟蹤關鍵技術進行了深入研究&#xff0c;旨在提升車道線檢測的準確性與系統的實時響應能力。通過采用先進的…

flutter 跨平臺編碼庫 protobuf 工具使用

1 安裝依賴 dependencies:protobuf: ^3.1.0 # 或最新版本flutter pub get安裝成功之后 1 lib 下創建文件夾 testProto 2 創建文件Student.proto 文件Student.proto 文件內容 syntax "proto3"; package example2;//導入其它proto文件 import "testProto/user.…