大家好,小弟最近我研究小東西,在這邊分享一下我的研究成果。
對了!這邊預告一下,小弟我數學不好QAQ都還給高職數學老師了XD
如有錯誤的部分或是有更好的方法,各位大大們還多多指教喔(=w=)a
個人分享偏好內容只聚焦在主題,覺得這樣簡單直覺會比較好消化。如果有功能不夠的部分,可能要請大家自己額外添加(=w=)a
簡介
這個研究簡單來說是應用三角函數運算Input.GetAxis("Vertical")及("Horizontal")獲得角度,角度是用來決定角色轉向角度或是移動方向等等,總之就看個人需求瞜。
因為最近想做出通用於類比搖桿和按鍵的移動腳本,目前想到是應用Input.GetAxis("Vertical")及("Horizontal")來實現,所以這研究算是前置作業。
想要完整程式或目前最佳化程式可以到最下面找到。
那廢說不多說了,讓我們開始這篇的主題吧!
Input.GetAxis("Vertical")、("Horizontal")
首先,實作第一步我們先來了解Input.GetAxis是什麼吧?
Input.GetAxis是Unity內建偵測輸入的函式,會回傳Float或是Bool給我們,
想要設定Input.GetAxis可以到Edit->Project Setting->Input打開InputManager介面設定。
而我們的主角之一Input.GetAxis("Vertical"),會回傳-1到1之間的浮點數,
按W鍵回傳值會遞增,按S鍵則會遞減,不按鍵會漸漸回復到零,
而Input.GetAxis("Horizontal")是用來偵測A鍵和D鍵。
然後我們來寫一段程式測試一下這是不是我們要的結果。
我們先宣告兩個Float變數來接Input.GetAxis("Vertical")及("Horizontal")的回傳值,分別命名為input_V及input_H,並放到FixedUpdate ()重複更新。
/// <summary>
/// 垂直輸入量
/// </summary>
[SerializeField]
[Header("垂直輸入量")]
private float input_V;
/// <summary>
/// 水平輸入量
/// </summary>
[SerializeField]
[Header("水平輸入量")]
private float input_H;
void FixedUpdate ()//固定頻率重複執行
{
//接Input.GetAxis("Vertical")及("Horizontal")的回傳值
input_V = Input.GetAxis ("Vertical");
input_H = Input.GetAxis ("Horizontal");
}
上面影片我可以看到input_V及input_H的變化規則是長這樣。
為了方便解釋我們先只看input_H。
上圖的意思是當你按住D鍵,input_H遞增到1,但下一瞬間你按A鍵,這時input_H會回歸到零才開始遞減。
也就是說遇到反向輸入時,Input.GetAxis("Vertical")及("Horizontal")會歸零才開始計算。
像這樣數值突然的大量變動會造成計算角色轉向後會有瞬間轉向的問題。
不過別擔心,只要到InputManager介面把Horizontal和Vertical展開,找到Snap,再來把勾勾拿掉,就不會歸零才開始計算了。
應用三角函數轉換成角度
接著我們要把input_V及input_H去做三角函數運算啦!!
首先,我們先分析一下我們的需求。
我們先把input_V和input_H當作直角三角形的兩邊,接著我們要利用這已知的兩邊去求我們想要知道的夾角θ(角色轉向角度),如下圖。
再來Google大神告訴我們只知道鄰邊、對邊要求夾角θ的話要帶這個公式。
θ = atan(對邊 / 鄰邊) / (π / 180)
鄰邊:夾角θ鄰近的邊。
對邊:夾角θ對面的邊。
然後有問題出現了! 當input_V小於0的時候,求出來了夾角θ會相差180°。
由於小弟我功力不足,沒辦法為大家解釋,總之大家遇到input_V小於0時,幫夾角θ+180°。
知道了這些需求之後,我們接著把它轉換成程式吧!
/// <summary>
/// 垂直輸入量
/// </summary>
[SerializeField]
[Header("垂直輸入量")]
private float input_V;
/// <summary>
/// 水平輸入量
/// </summary>
[SerializeField]
[Header("水平輸入量")]
private float input_H;
/// <summary>
/// 結果角度
/// </summary>
[SerializeField]
[Header("結果角度")]
private float angle_Sum;
void FixedUpdate ()//固定頻率重複執行
{
//Input.GetAxis("Vertical")及("Horizontal")的回傳值
input_V = Input.GetAxis ("Vertical");
input_H = Input.GetAxis ("Horizontal");
//三角函數計算
angle_Sum = Mathf.Atan (input_H / input_V) / (Mathf.PI / 180);
angle_Sum = input_V < 0 ? angle_Sum + 180 : angle_Sum;
//角色轉向
transform.eulerAngles = new Vector3(transform.eulerAngles.x, angle_Sum, transform.eulerAngles.z);
}
額外處理
再來又有問題出現了XD 從上面影片可以看出兩個問題
第一個問題
當input_V及input_H都等於0時,input_V / input_H會變成,0 / 0會得到NaN(無窮值)。
解決方法:
用float.IsNaN()函式判斷angle_Sum是不是NaN,如果是讓它等於0。
if(float.IsNaN(angle_Sum))
angle_Sum = 0;
第二個問題
因為在放開按鍵時,input_V及input_H都會漸漸回歸到0,所以運算獲得的角度會漸漸回到0。
解決方法:
如果按住WSAD任何一按鍵,才執行以下程式。
因為沒按鍵時不執行程式,所以角度會停留放開前的狀態。
if(Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.D))
所以我們可以將程式改寫成
void FixedUpdate ()//固定頻率重複執行
{
//如果按住WSAD任何一按鍵,才執行以下程式
if(Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.D))
{
//接Input.GetAxis("Vertical")及("Horizontal")的回傳值
input_V = Input.GetAxis ("Vertical");
input_H = Input.GetAxis ("Horizontal");
//三角函數計算
angle_Sum = Mathf.Atan (input_H / input_V) / (Mathf.PI / 180);
angle_Sum = input_V < 0 ? angle_Sum + 180 : angle_Sum;
//如果角度是NaN,讓他變成0
if(float.IsNaN(angle_Sum))
angle_Sum = 0;
//角色轉向
transform.eulerAngles = new Vector3(transform.eulerAngles.x, angle_Sum, transform.eulerAngles.z);
}
}
因為考慮到文章篇幅,就先寫到這邊告一個段落,還請多多包涵瞜(=w=)a
結論
這次研究嘗試使用Input.GetAxis("Vertical")及("Horizontal")來實作轉向,以往土炮用按鍵if else判斷比起來,角度有更多的細節變化。
雖然轉向經過補間處理後兩者效果可能看起來差不多XD。
不過應用在實作任何方位移動速度一致,就很符合我要的效果。
(因為移動程式之後想再寫文章解釋,所以這邊先不分享了)
不過考慮到未來想實作出通用於類比搖桿和按鍵的移動腳本,這前置的研究是不可或缺的,之後有順利實作出來在分享給大家。
完整程式碼
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
-
- public class Character_Angle : MonoBehaviour
- {
- /// <summary>
- /// 垂直輸入量
- /// </summary>
- [SerializeField]
- [Header("垂直輸入量")]
- private float input_V;
-
- /// <summary>
- /// 水平輸入量
- /// </summary>
- [SerializeField]
- [Header("水平輸入量")]
- private float input_H;
-
- /// <summary>
- /// 結果角度
- /// </summary>
- [SerializeField]
- [Header("結果角度")]
- private float angle_Sum;
-
- void FixedUpdate ()//固定頻率重複執行
- {
- //接Input.GetAxis("Vertical")及("Horizontal")的回傳值
- input_V = Input.GetAxis ("Vertical");
- input_H = Input.GetAxis ("Horizontal");
-
- //如果按住WSAD任何一按鍵,才執行以下程式
- if(Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.D) || Input.GetKey(KeyCode.S))
- {
- //三角函數計算
- angle_Sum = Mathf.Atan (input_H / input_V) / (Mathf.PI / 180);
- angle_Sum = input_V < 0 ? angle_Sum + 180 : angle_Sum;
-
- //如果角度是NaN,讓他變成0
- if(float.IsNaN(angle_Sum))
- angle_Sum = 0;
-
- //角色轉向
- transform.eulerAngles = new Vector3(transform.eulerAngles.x, angle_Sum, transform.eulerAngles.z);
- }
- }
- }
目前最佳化程式碼
- using System.Collections;
- using System.Collections.Generic;
- using UnityEngine;
- public class Character_Angle : MonoBehaviour
- {
- /// <summary>
- /// 垂直輸入量
- /// </summary>
- [SerializeField]
- [Header("垂直輸入量")]
- private float input_V;
- /// <summary>
- /// 水平輸入量
- /// </summary>
- [SerializeField]
- [Header("水平輸入量")]
- private float input_H;
- /// <summary>
- /// 結果角度
- /// </summary>
- [SerializeField]
- [Header("結果角度")]
- private float angle_Sum;
- void FixedUpdate ()//固定頻率重複執行
- {
- //接Input.GetAxis("Vertical")及("Horizontal")的回傳值
- input_V = Input.GetAxis ("Vertical");
- input_H = Input.GetAxis ("Horizontal");
- //用GetAxisRaw判斷是否按到移動鍵,是的話執行以下程式,放開可以保留角度狀態,也能避免NaN的狀況
- if(Input.GetAxisRaw("Vertical") != 0 || Input.GetAxisRaw ("Horizontal") != 0)
- {
- //三角函數計算
- angle_Sum = Mathf.Atan2 (input_H, input_V) / (Mathf.PI / 180);
- //角色轉向
- transform.eulerAngles = new Vector3(transform.eulerAngles.x, angle_Sum, transform.eulerAngles.z);
- }
- }
- }
-