unity 多種角色控制方式介紹 以及 CharacterController角色控制器詳解

簡介

操作人物移動,我們一般有以下幾種辦法:

  1. 直接操作座標
  2. 使用物理引擎操控
  3. 使用動作 Root Motion
  4. 使用角色控制器
關於座標操作

座標操作,就是通過設置transoform.position座標 或是Vector.transform 等方法使物體移動到指定座標。
該方式的優點,當然是簡單暴力。壞處麼,各種無互動無反饋,效果太楞。只適合簡單的變化操作或是2d遊戲。如果需要複雜點的效果,比如曲線過度,重力下降都要自己去實現。需要自己實現呀,兄弟們我們使用引擎的目的是什麼,當然是使用提供的工具提升工作效率。不能什麼都自己搞呀。所以這個方式只推薦應用在npc或是機關變化。

關於物理引擎操作

物理引擎操作的好處是可以使用諸多物理特性。當遊戲有大量碰撞或移動特性時,使用這個比較好。比如賽車遊戲。你可能需要設置賽車的重量來決定過彎的表現和撞車時力反饋的計算,又或是爆炸時被波及的力度。
使用物理引擎操作的主要方式就是添加力。由此可知,向前移動就是添加向前的作用力,跳躍就是添加向上的作用力,物理效果會讓人物跳躍後自動下降。

關於Root Motion

這個方式是我認爲最合理,過度最平滑,效果最好的方式。只可惜成本太高實現太難。
他所需要的技術包括:角色骨骼綁定 + Animation動作庫 + Animator動畫控制器 + Root Motion根座標移動
這就需要我們不僅擁有質量高且數量龐大的動作庫,以及各種對動畫的精確控制才能實現。我們使用市面上已有的動作庫可以實現基本的移動等操作。但總的來說,性能還是不如下面那個,而且開發成本還是比較高的,調試周期特別長,在遇到一些自適應需求的時候開發難度也會增加。
我相信,不久的將來它會成爲遊戲引擎中的操控最高級形態,並且在技術的推動下變得更加易用。

使用角色控制器(CharacterController)操作

這是個unity的組件。一般情況下控制人物的不二選擇。那麼爲什麼首選角色控制器呢,有以下幾個理由:

  1. 首先他簡化了剛體組件,去掉了很多不必要的計算。在人物比較多的場景中,這節省了大量的物理計算。例如物理引擎中的翻轉、重力等效果,你肯定不希望你在移動過程中,會因爲碰撞到物體而自動變換方向吧,一般做法就是用freezon來限制,其實這些都是沒有必要的性能浪費。如果場景中人物較多時,這種優化就等於性能的巨幅提升。
  2. 其次,他內置了爬坡、登階閾值設置。可以比較簡單的處理多路況的移動問題。
  3. 第三,這個控制器,非常適合與nevmesh agent連用。即unity自帶的巡路系統。當我們使用尋路系統時,如果人物身上有物理剛體,那麼不僅會被沿途障礙物所影響,還會出現各種意想不到的情況比如慣性等性質。 如果使用角色控制器。則不必擔心出現這種情況。一切都那麼順滑,防側漏,夜夜安心。
  4. 不必擔心被座標被動作庫寫死。更不必擔心由於動作庫的座標移動而出現意外的座標干擾。

角色控制器(CharacterController)組件參數介紹

在目標物體的inspector窗口中 點擊add components ,添加character controller 即可完成組件添加。
在這裏插入圖片描述
【Slope Limit:】:斜坡角度。允許角色在指定的坡度上行動。
比如在手遊和平精英中,你可能會發現某些房頂的斜坡就能走上去,某些房頂斜坡就走不上去。這都是這個參數所致。雖然和平精英用的是ue4引擎,但使用了和unity原理相似的控制組件。他們都有這個斜坡設置。
【Step Offset】:臺階高度。允許角色自動越過多高的障礙物。 這個設置就是諸如樓梯,臺階,馬路牙子之類的障礙,你總不能遇到個小東西就卡住吧,有了這個就可以越過去。有人說,我沒有用character controller,用剛體也能越過比較低矮的障礙物啊,怎麼回事呢? 那是因爲你的碰撞盒下方並不平整,比如膠囊或圓形碰撞盒,由於下方是曲線過度的,所以會在接觸物體時通過物理計算的推動下摩擦過去。這種方式不容易控制並且會可能發生意外情況。
【Skin Width】:皮膚寬度。這個就相當於第二層碰撞盒。這個值不能大於下方的碰撞盒的Radius值。否則會卡在某個地方。一般情況這個值應該在Radius的5%左右會比較好
【Min Move Distance】:最小移動範圍。當你在代碼中使用Move方法移動時,小於這個值的移動將被忽略。這個值可以幫你減少角色因操作產生的抖動。
【Center】:角色碰撞盒偏移。設置物理空間的位置,你需要通過調整這個來使碰撞盒與角色模型重合。
【Radius】:碰撞盒半徑。角色控制器的自帶碰撞盒是膠囊,這個值用來設置膠囊半徑。這個設置還有另一個影響,這個半徑值將影響腳本中的isGrounded屬性的作用範圍。這對我們實現控制角色y座標位置,比如跳躍等功能極其重要。
【Height】:碰撞盒高度。同上,膠囊高度。

角色控制器(CharacterController)使用

在添加了CharacterController的物體上新建一個腳本。

//獲取 CharacterController對象
CharacterController _characterController = gameObject.GetComponent<CharacterController> ();
移動的實現

以下是兩種移動方式。分別是世界座標和本地座標

//移動方向
private Vector3 moveDirection = Vector3.zero;
//移動速度
float movespeed  = 2f;
void FixedUpdate(){
	//世界座標移動  適用於2d遊戲,有固定的方向
	_characterController.Move(Vector3.up * movespeed * Time.deltaTime);

	//角色座標移動實現   基於當前角色視角的方向移動,適用於3d遊戲
	//根據輸入的x y值獲得移動方向
	float vertical = Input.GetAxis ("Vertical");
    float horizontal = Input.GetAxis ("Horizontal");
    moveDirection = new Vector3(horizontal, 0f, vertical);
    // 計算 基於當前物體座標,要移動的方向
    moveDirection = transform.TransformDirection(moveDirection) * movespeed;
    // 移動
    _characterController.Move(Vector3.up * movespeed * Time.deltaTime);
}
跳躍的實現
public float jumpPower = 3f;
public float speed = 10f;
public float gravity = 10f;
private Vector3 moveDirection = Vector3.zero;
void FixedUpdate() {
   //isGrounded屬性 用於 檢測當前角色下方是否接觸到地面。這個屬性表現並不穩定,至少在我的unity2020中他不夠穩定,經常閃爍。所以需要我們再用一個射線檢測的方法來輔助。
    if(_characterController.isGrounded || IsGrounded(0.2f)) {
       moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0f, 				Input.GetAxis("Vertical"));
       moveDirection = transform.TransformDirection(moveDirection);
       moveDirection *= speed;
       if (Input.GetButton("Jump"))
           moveDirection.y = jumpPower ;
       }
       // 角色在世界中自動下降,模擬重力
       moveDirection.y -= gravity * Time.deltaTime;
       controller.Move(moveDirection * Time.deltaTime);
}
// 射線檢測,拋磚引玉 我這裏使用五個射線。實際上不需要這麼多
 public bool IsGrounded(float distance){
 		//pointOffset: 點的偏移位置, distance:檢測物體與地面的距離
        float pointOffset= 0.12f;
        bool b = Physics.Raycast(new Vector3(transform.position.x, transform.position.y, transform.position.z), -Vector3.up, distance);
        bool b1 = Physics.Raycast(new Vector3(transform.position.x - pointOffset, transform.position.y, transform.position.z), -Vector3.up, distance);
        bool b2 = Physics.Raycast(new Vector3(transform.position.x + pointOffset, transform.position.y, transform.position.z), -Vector3.up, distance);
        bool b3 = Physics.Raycast(new Vector3(transform.position.x, transform.position.y, transform.position.z + pointOffset), -Vector3.up, distance);
        bool b4 = Physics.Raycast(new Vector3(transform.position.x, transform.position.y, transform.position.z - pointOffset), -Vector3.up, distance);
        return b || b1 || b2 || b3 || b4;
    }

這裏只是簡單的實現,在實際的需求中,你可能要捕捉人物的跳躍狀態,爲人物跳躍指定起跳、浮空、落地等動作,還要設置原地起跳高度,移動跳躍距離等設定等。

注意事項
  1. 如果主人公身上有animator 需要看情況去掉root motion選項。該選項有可能導致座標衝突
  2. CharacterController.isGrounded 這個會閃爍。所以會導致很多意外情況。可選的解決方案是:
    一、碰撞盒替代。是比較理想的方案。
    二、CharacterController.isGrounded + 射線輔助的方式。性能會好一些。不可以單獨只使用射線。這樣會存在盲點。

完成的效果

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章