最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

Unity-耦合動畫和導(dǎo)航

2021-03-13 15:36 作者:unity_某某師_高錦錦  | 我要投稿

本文檔的目標(biāo)是指導(dǎo)您設(shè)置人形角色的導(dǎo)航以使用導(dǎo)航系統(tǒng)進行移動。

我們將使用 Unity 的內(nèi)置動畫和導(dǎo)航系統(tǒng)以及自定義腳本來實現(xiàn)這一目標(biāo)。

本文假設(shè)您已熟悉 Unity 和 Mecanim 動畫系統(tǒng)的基礎(chǔ)知識。

此處提供了一個示例項目,因此無需從頭開始添加腳本或設(shè)置動畫和動畫控制器:

  • NavigationAnimation_53.zip?適用于 Unity 5.3+

創(chuàng)建動畫控制器

為了獲得響應(yīng)迅速且多功能的動畫控制器(涵蓋各種動作),我們需要一組向不同方向移動的動畫。有時將其稱為掃射集 (strafe-set)。

除了移動動畫,我們還需要一段站立角色的動畫。

我們繼續(xù)將掃射集組織在 2D 混合樹中;選擇混合類型:2D Simple Directional,并使用?Compute Positions > Velocity XZ?放置動畫

為進行混合控制,我們添加兩個浮點參數(shù)?velx?和?vely,并將它們分配給混合樹。

在這里,我們將放置 7 段奔跑動畫,每段都有不同的速度。除了前進(+左/右)和后退(+左/右),我們還使用了原地奔跑的動畫剪輯。后者在下面的 2D 混合圖的中心位置進行了突出顯示。采用原地奔跑動畫有兩個原因,首先,該動畫可在與其他動畫混合時保持奔跑風(fēng)格;其次,該動畫可以防止混合時出現(xiàn)腳滑。

然后,我們在空閑節(jié)點 (Idle) 本身中添加空閑動畫剪輯。 我們現(xiàn)在有兩個獨立動畫狀態(tài),我們將它們與 2 個過渡耦合。

為了控制移動狀態(tài)和空閑狀態(tài)之間的切換,我們添加一個布爾值控制參數(shù)?move。然后,對過渡禁用?Has Exit Time?屬性。如此便可在動畫期間的任何時間觸發(fā)過渡。為獲得快速響應(yīng)的過渡,過渡時間應(yīng)設(shè)置為約 0.10 秒。

現(xiàn)在將新創(chuàng)建的動畫控制器放在要移動的角色上。

按 Play 并在?Hierarchy 窗口中選擇該角色。現(xiàn)在可在?Animator 窗口中手動控制動畫值,并更改移動狀態(tài)和速度。

下一步是創(chuàng)建其他控制動畫參數(shù)的方法。

導(dǎo)航控制

在角色上放置一個?NavMeshAgent?組件,調(diào)整半徑和高度,并匹配角色(另外更改速度屬性以匹配動畫混合樹中的最大速度)。

為放入角色的場景創(chuàng)建導(dǎo)航網(wǎng)格。

接下來,我們需要告訴角色要導(dǎo)航的目標(biāo)。此設(shè)置通常與具體應(yīng)用有非常大的關(guān)聯(lián)性。在這里,我們選擇“單擊進行移動”(click to move) 行為:根據(jù)用戶點擊屏幕的位置,角色移動到世界中的相應(yīng)點。

// ClickToMove.cs?

using UnityEngine;?

using UnityEngine.AI;?

[RequireComponent (typeof (NavMeshAgent))]?

public class ClickToMove : MonoBehaviour { ? ?

RaycastHit hitInfo = new RaycastHit(); ? ?

NavMeshAgent agent; ? ?

void Start (){ ? ? ? ?

agent = GetComponent<NavMeshAgent> (); ? ?

} ? ?

void Update (){ ? ? ? ?

if(Input.GetMouseButtonDown(0)) { ? ? ? ? ? ?

Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); ? ? ? ? ? ?

if (Physics.Raycast(ray.origin, ray.direction, out hitInfo)) ? ? ? ? ? ? ? ?agent.destination = hitInfo.point; ? ? ? ?

}}}

現(xiàn)在按下 Play,然后在場景中單擊,便會看到角色在場景中移動。但是,動畫與動作完全不符。我們需要將代理的狀態(tài)和速度傳達給動畫控制器。

為了將代理的速度和狀態(tài)信息傳輸?shù)絼赢嬁刂破鳎覀儗⑻砑恿硪粋€腳本。

// LocomotionSimpleAgent.cs?

using UnityEngine;?

using UnityEngine.AI;?

?[RequireComponent (typeof (NavMeshAgent))]?

[RequireComponent (typeof (Animator))]?

public class LocomotionSimpleAgent : MonoBehaviour {

Animator anim;

NavMeshAgent agent;

Vector2 smoothDeltaPosition = Vector2.zero;

Vector2 velocity = Vector2.zero;

void Start (){

anim = GetComponent<Animator> ();

agent = GetComponent<NavMeshAgent> (); ? ? ? ?

// 不要自動更新位置 ? ? ? ?

agent.updatePosition = false;

}

void Update (){ ? ? ? ?

Vector3 worldDeltaPosition = agent.nextPosition - transform.position; ? ? ? // 將"worldDeltaPosition"映射到局部空間
float dx = Vector3.Dot (transform.right, worldDeltaPosition);
float dy = Vector3.Dot (transform.forward, worldDeltaPosition);
Vector2 deltaPosition = new Vector2 (dx, dy);
// 對 deltaMove 進行低通濾波
float smooth = Mathf.Min(1.0f, Time.deltaTime/0.15f);
smoothDeltaPosition = Vector2.Lerp (smoothDeltaPosition, deltaPosition, smooth); // 如果時間推進,則更新速度
if (Time.deltaTime > 1e-5f)
????velocity = smoothDeltaPosition / Time.deltaTime;
????bool shouldMove = velocity.magnitude > 0.5f && agent.remainingDistance > agent.radius;

// 更新動畫參數(shù)

anim.SetBool("move", shouldMove);

anim.SetFloat ("velx", velocity.x); ? ? ? ?

anim.SetFloat ("vely", velocity.y); ? ? ? ?

GetComponent<LookAt>().lookAtTargetPosition = agent.steeringTarget + transform.forward;} ? ?

void OnAnimatorMove (){

// 將位置更新到代理位置

transform.position = agent.nextPosition; ? ?

} }

對于此腳本,需要進行一點說明。此腳本放置在角色上,而角色已附加?Animator?和?NavMeshAgent?組件以及上面的 click to move 腳本。

首先,腳本告訴代理不要自動更新角色位置。我們處理腳本中最后的位置更新。方向由代理進行更新。

通過讀取代理速度來控制動畫混合。該速度轉(zhuǎn)換為相對速度(基于角色方向),然后經(jīng)過平滑。然后,轉(zhuǎn)換后的水平速度分量將傳遞到?Animator,另外,空閑狀態(tài)和移動狀態(tài)之間的狀態(tài)切換由速度(即速度幅度)進行控制。

在?OnAnimatorMove()?回調(diào)中,我們更新角色的位置以便與?NavMeshAgent?匹配。

再次播放場景顯示動畫在最大限度上與動作匹配。

提高導(dǎo)航角色的質(zhì)量

為了提高動畫和導(dǎo)航角色的質(zhì)量,我們將探索幾個可能性。

注視

讓角色注視和轉(zhuǎn)向興趣點對于表現(xiàn)注意力和期待效果十分重要。我們將使用動畫系統(tǒng) lookat API。因此需要另一個腳本。

// LookAt.cs?

using UnityEngine;?

using System.Collections;?

[RequireComponent (typeof (Animator))]?

public class LookAt : MonoBehaviour {
public Transform head = null;
public Vector3 lookAtTargetPosition;
public float lookAtCoolTime = 0.2f;
public float lookAtHeatTime = 0.2f;
public bool looking = true;
private Vector3 lookAtPosition;
private Animator animator;
private float lookAtWeight = 0.0f;
void Start (){
if (!head){
Debug.LogError("No head transform - LookAt disabled");
enabled = false;
return;
}
animator = GetComponent<Animator> ();
lookAtTargetPosition = head.position + transform.forward;
lookAtPosition = lookAtTargetPosition;
}
void OnAnimatorIK ()
{
lookAtTargetPosition.y = head.position.y;
float lookAtTargetWeight = looking ? 1.0f : 0.0f;
Vector3 curDir = lookAtPosition - head.position;
Vector3 futDir = lookAtTargetPosition - head.position;
curDir = Vector3.RotateTowards(curDir, futDir, 6.28f*Time.deltaTime, float.PositiveInfinity);
lookAtPosition = head.position + curDir;
float blendTime = lookAtTargetWeight > lookAtWeight ? lookAtHeatTime : lookAtCoolTime;
lookAtWeight = Mathf.MoveTowards (lookAtWeight, lookAtTargetWeight, Time.deltaTime/blendTime);
animator.SetLookAtWeight (lookAtWeight, 0.2f, 0.5f, 0.7f, 0.5f);
animator.SetLookAtPosition (lookAtPosition);
} }

將該腳本添加到角色,并將 head 屬性分配給角色變換層級視圖中的 head 變換。LookAt 腳本沒有導(dǎo)航控制的概念;所以為了控制注視位置,我們回到?LocomotionSimpleAgent.cs?腳本,并添加幾行代碼來控制注視。在?Update()?末尾添加:

LookAt lookAt = GetComponent<LookAt> ();
if (lookAt)
????lookAt.lookAtTargetPosition = agent.steeringTarget + transform.forward;

這樣就會告訴?LookAt?腳本將興趣點設(shè)置為沿路徑的大致下一個角點,或者如果沒有角落,設(shè)置為路徑的末端。

自己嘗試一下。

使用導(dǎo)航的動畫驅(qū)動角色

到目前為止,角色完全由代理指定的位置控制。這確保了對其他角色和障礙物的躲避直接轉(zhuǎn)換為角色位置。但是,如果動畫未跟上建議的速度,則可能導(dǎo)致腳滑現(xiàn)象。在這里,我們將稍微放松一下對角色的約束。大體上,我們將用躲避質(zhì)量換取動畫質(zhì)量。

將?LocomotionSimpleAgent.cs?腳本上的?OnAnimatorMove()?回調(diào)行替換為以下代碼

void OnAnimatorMove (){ ? ? ? ? ? ?
// 根據(jù)動畫移動情況使用導(dǎo)航表面高度來更新位置 ? ? ? ? ? ?
Vector3 position = anim.rootPosition; ? ? ? ? ? ?
position.y = agent.nextPosition.y; ? ? ? ? ? ?
transform.position = position;
}

嘗試運行此代碼時,您可能會注意到,角色現(xiàn)在可以游離于代理位置(綠色線框圓柱體)。您可能需要限制該角色動畫游離問題。為此,可將代理拉向角色,或者將角色拉向代理位置。在?LocomotionSimpleAgent.cs?腳本上的?Update()?方法末尾添加以下代碼。

// 將角色拉向代理 ? ? ? ? ? ? ? ?
if (worldDeltaPosition.magnitude > agent.radius)
????transform.position = agent.nextPosition - 0.9f*worldDeltaPosition;

或者,如果希望代理跟隨角色,請?zhí)砑右韵麓a。

// 將代理拉向角色 ? ? ? ? ? ? ? ?
if (worldDeltaPosition.magnitude > agent.radius)
?agent.nextPosition = transform.position + 0.9f*worldDeltaPosition;

具體哪種方法最合適取決于具體的用例。

結(jié)論

我們已經(jīng)設(shè)置一個使用導(dǎo)航系統(tǒng)移動的角色并相應(yīng)地設(shè)置了動畫。調(diào)整混合時間數(shù)字、注視權(quán)重等可以改善視覺效果,也是進一步探索此設(shè)置的好方法。


Unity-耦合動畫和導(dǎo)航的評論 (共 條)

分享到微博請遵守國家法律
偏关县| 灵川县| 蓝山县| 离岛区| 石柱| 奉新县| 盘山县| 凯里市| 客服| 江陵县| 电白县| 金堂县| 自治县| 米脂县| 嘉义市| 金湖县| 泸溪县| 罗田县| 彭州市| 新巴尔虎左旗| 关岭| 梅河口市| 泾阳县| 肥城市| 南京市| 昌江| 噶尔县| 泰宁县| 通山县| 金沙县| 泗水县| 新邵县| 射阳县| 定陶县| 临海市| 定兴县| 惠州市| 社会| 桂东县| 康定县| 四子王旗|