在大部分的Unity遊戲開發中,移動是極其重要的一部分,移動的手感決定著遊戲的成敗,一個優秀的移動手感無疑可以給遊戲帶來非常舒服的體驗。而Unity中有多種移動方法,使用Transform,使用剛體Rigidbody,使用CharacterController,使用NavMesh導航系統等等等等。當然,對於新手來說,最常見的莫過於使用Transform和Rigidbody這兩種元件的移動方案。所以,這篇文章將就這兩種移動方案進行分析講解。
注意!!!以下程式碼均為2D場景,3D同理
Transform元件是GameObject的變換元件,可以操縱GameObject的位置(Position),大小(Scale),旋轉(Rotation)等等。所以,使用Transform元件進行物體移動是一個非常不錯的選擇,以下是通過Transform元件實現的幾種移動方式,以及對應的場景。
使用Transform的Translate函數可以在GameObject的本地座標系下進行平移。可以傳入一個位移向量作為引數,指定平移的方向和距離。
[SerializeField] private float moveSpeed; private void Update() { //自動向右移動 transform.Translate(Vector2.right * moveSpeed * Time.deltaTime); } //物體沿向量指向方向移動 //Vector2.right 向右移動向量,也可以寫成自己定義的 //moveSpeed 移動速度,通常為float型
Translate方法在遊戲中可以用作物體移動,適用簡單的移動方式,例如箱子在平面上自動移動等等。
使用Transform的MoveTowards函數可以實現直線移動到目標位置。可以傳入當前位置、目標位置和移動速度來控制移動的速度和到達目標位置。
MoveTowards函數對應的三個引數分別為(當前位置,目標位置,移動速度),前兩個為Vector型別,最後一個為float型別,也可以寫成整型等。
例如:將物體移動到(5,5)的位置
[SerializeField] private float moveSpeed; private void Update() { transform.position = Vector2.MoveTowards(transform.position,new Vector2(5,5),moveSpeed); }
使用Transform的Lerp函數可以實現平滑插值移動。可以傳入起始位置、目標位置和插值比例來控制移動的過渡效果。
Lerp函數對應的三個引數分別為(當前位置,目標位置,插值比例),前兩個為Vector型別,最後一個為float型=型別,插值比例範圍是[0,1],當lerp取0時,物體不移動,lerp取1時,物體直接移動到目標位置,lerp取值越大,物體移動越快。
例如:將物體移動到(5,5)
[SerializeField] private float moveSpeed; [SerializeField] private float lerp; private void Update() { transform.position = Vector3.Lerp(transform.position, new Vector2(5,5), lerp); }
接下來,將講解一下Lerp函數的移動原理:
插值係數lerp本質上是物體每次移動距離與物體當前位置到目標位置的比值,物體每次移動後,都會重新重置下一步移動距離,但是比例不變,也就是說,物體朝目標點移動,每次移動的距離都會變短。聽起來非常繞口對吧,下面我們用一幅圖來講解一下這個原理。
紅色豎線為第一次移動到的位置,那麼它的移動距離L1=S1lerp,藍色豎線第二次移動到的位置,那麼第二次移動的距離L2=S2lerp,同理,L3=S3lerp。由圖可知,物體每次移動的距離都在縮短,但是,它們每次移動的距離與當前位置到目標位置的距離的比值不變。並且,我們也可以發現,lerp值越大,單次移動距離越大,即速度越快,相反,lerp越小,單詞移動距離也就越小。最後,我們不難發現,在Lerp函數中,物體移動的距離永遠是當前位置到目標位置的距離lerp,也就是說,物體永遠不可能到達目標位置,只會無限接近目標位置。所以,為了使物體可以到達目標位置,我們可以新增一個if條件,當物體的目標位置的距離小於某一值時,物體位置變為目標位置。
if (Vector2.Distance(transform.position, new Vector2(5, 5))<0.1f) { transform.position = new Vector2(5, 5); }
以上便是使用Transform移動物體的幾種方案,當然使用Transform元件移動物體的方案有很多種形式,具體可以自行探索。
當然,使用Transform元件移動物體有時會出現一個小小的bug,我們將在Rigidbody中說明。
Rigidbody,剛體元件,在這個元件中,我們可以使用物理學的定義進行物體移動等操作。並且,這也是最經常用的操控玩家移動的元件,。當然剛體元件不僅僅只用來移動GameObject,還有很多操作,在這裡,我們只講移動方面的使用。
上文說了,Transform有一個小小的bug,那就是會引起穿模,也就是說,物體在進行移動時,碰到障礙物繼續移動,會導致穿過障礙物,這是一個致命的bug。但是,剛體元件就可以很好的解決這個bug。在這裡,我查閱了一些資料,大致便是,Transform元件是位置的改變,也就是一次一次的發生位置變化,也就是相當於每次移動都是一段瞬移閃現,第一時間在一個位置,下一時間又瞬移到下一個位置,這樣的話,在和障礙物進行擠壓時,就極其容易導致物體和障礙物發生交叉,導致碰撞體檢測出現異常,從而導致穿模,而剛體元件相當於拉著物體移動,就不存在這樣的bug。對這方面感興趣可以查閱相關資料。
下面繼續講解Rigidbody元件控制GameObject移動。
使用AddForce函數給剛體施加力來移動物體,想要朝哪個方向移動,就在哪個方向新增力。
AddForce函數的引數為AddForce(方向向量 * 力的大小);
[SerializeField] private float force; private Rigidbody2D rigidbody2D; private void Start() { //獲取掛載指令碼的物體的剛體元件 rigidbody2D = GetComponent<Rigidbody2D>(); } private void Update() { //向上施加一個大小為force的力 rigidbody2D.AddForce(Vector2.up * force); }
MovePosition函數可以直接設定物體的位置。
MovePosition函數的引數為MovePosition(位置(例如tramsform.position))
下面的程式碼是物體每次向右閃現/瞬移speed的長度,注意,這個方法也有可能導致穿模
[SerializeField] private float speed; private Rigidbody2D rigidbody2D; private void Start() { //獲取掛載指令碼的物體的剛體元件 rigidbody2D = GetComponent<Rigidbody2D>(); } private void Update() { //向右移動,2D中為向右/前,X軸正方向 rigidbody2D.MovePosition(transform.position + Vector3.right * speed * Time.deltaTime); }
當然,也可以直接只填入目標位置,使得物體閃現到指定目標位置
[SerializeField] private Transform targetTransform; private Rigidbody2D rigidbody2D; private void Start() { //獲取掛載指令碼的物體的剛體元件 rigidbody2D = GetComponent<Rigidbody2D>(); } private void Update() { //傳送到targetTransform的位置 rigidbody2D.MovePosition(targetTransform.position); }
首先說明的是velocity不是函數,而是一個引數,也就是物體的速度。
所以我們通過對物體將要移動的方向上新增速度,也就可使物體超指定方向以固定的速度進行移動。
[SerializeField] private float moveSpeed_X; [SerializeField] private float moveSpeed_Y; private Rigidbody2D rigidbody2D; private void Start() { //獲取掛載指令碼的物體的剛體元件 rigidbody2D = GetComponent<Rigidbody2D>(); } private void Update() { //水平方向 float horizontal = Input.GetAxis("Horizontal"); //豎直方向 float vertical = Input.GetAxis("Vertical"); rigidbody2D.velocity=new Vector2 (horizontal*moveSpeed_X*Time.deltaTime, vertical* moveSpeed_Y * Time.deltaTime); //也可以只改變x或y的值 rigidbody2D.velocity = new Vector2(horizontal * moveSpeed_X * Time.deltaTime, rigidbody2D.velocity.y); rigidbody2D.velocity = new Vector2(rigidbody2D.velocity.x, vertical * moveSpeed_Y * Time.deltaTime); }
以上便是幾種簡單的物體移動方式,當然使物體移動的方法有很多種,這裡只列舉了幾種,感興趣的小夥伴可以深究一下。