Unity:Transform之四元數的移動端單指滑動旋轉模型操作


  首先我們來參考一下四元數在Unity中的應用:

      unity 3D 詳解Quaternion類(一

  unity3D 詳解Quaternion類(二

  四元數quaternion的變換比較復雜,但是在unity中已經給我們寫好了相應的函數實現對transform的操作。

  在最近的一個項目中,遇到了一個單手指滑動手機屏幕實現對模型的一個旋轉操作,在嘗試了各種unity中的旋轉函數之后都沒能夠達到想要的效果之后,我選擇了用Quaternion.AngleAxis的函數來實現旋轉的操作效果。

  首先我們來分析一下Quaternion.AngleAxis(angle,axis),參數angle和axis代表了物體的旋轉角度和旋轉軸心。如下圖:紅色箭頭方向代表物體所圍繞的旋轉軸,旋轉角度可以是自定義的。

  

  

  接下來,我們就要做兩件事情,確定axis和計算angle。在這個項目中,我們是根據單個手指在手機屏幕上滑動,我們通過記錄滑動的距離,X方向的增量,以及Y軸方向的增量來為后面計算axis和angle打下基礎。unity的Input函數有GetTouch這個函數,我們只需要調用這個函數的相關方法就可以實現需求。

  現在,我們在unity中新建一個場景,在場景中新建一個立方塊。

  

  注意立方塊的世界坐標軸,Z軸的朝向應該是朝着攝像機的。根據之前對四元數腳本的分析,立方體的旋轉腳本為:

  Gesture.cs:

 1 using UnityEngine;
 2 using System.Collections;
 3 
 4 public class gesture : MonoBehaviour {
 5     public Transform Cube;
 6     private float radius = 1080;
 7     private Vector3 originalDir = new Vector3(0f,0f,1080f);
 8     private Vector3 CenterPos = new Vector3(0, 0, 0);
 9     private Vector2 startPos;
10     private Vector2 tempPos;
11     private Vector3 tempVec;
12     private Vector3 normalAxis;
13     private float angle;
14     // Use this for initialization
15     void Start () {
16         Cube = GameObject.Find("Cube").transform;
17     }
18     
19     // Update is called once per frame
20     void Update () {
21         if (Input.touchCount == 1)
22         {
23             //Vector2 startPos = Input.compositionCursorPos;
24             if (Input.GetTouch(0).phase == TouchPhase.Began)
25             {
26                 startPos = Input.GetTouch(0).position;
27                 //tempPos = startPos;
28             }
29             //if (Input.GetTouch(0).phase == TouchPhase.Ended)
30             //{
31             //    tempPos = startPos;
32             //}
33             if (Input.GetTouch(0).phase == TouchPhase.Moved)
34             {
35                 tempPos = Event.current.mousePosition;
36 
37                 float tempX = tempPos.x - startPos.x;
38                     
39                 float tempY = tempPos.y - startPos.y;
40 
41                 //tempPos = Input.GetTouch(0).deltaPosition;
42                 //float tempX = tempPos.x;
43 
44                 //float tempY = tempPos.y;
45 
46                 float tempZ = Mathf.Sqrt(radius * radius - tempX * tempX - tempY * tempY);
47 
48                 tempVec = new Vector3(tempX, tempY, tempZ);
49 
50                 angle = Mathf.Acos(Vector3.Dot(originalDir.normalized, tempVec.normalized)) * Mathf.Rad2Deg;
51 
52                 normalAxis = getNormal(CenterPos, originalDir, tempVec);
53 
54                 Cube.rotation = Quaternion.AngleAxis(2 *angle, normalAxis);
55 
56             }
57         }
58     }
59 
60     void OnGUI()
61     {
62         GUILayout.Label("StartPos 的坐標值為: "+startPos);
63         GUILayout.Label("tempPos 的坐標值為: " + tempPos);
64         GUILayout.Label("tempVec 的坐標值為: " + tempVec);
65         GUILayout.Label("normalAxis 的坐標值為: " + normalAxis);
66         GUILayout.Label("旋轉角度的值為: " + 2*angle);
67         GUILayout.Label("Cube的四元數角度: " + Cube.rotation);
68         GUILayout.Label("Cube de rotation.x: " + Cube.rotation.eulerAngles.x);
69         GUILayout.Label("Cube de rotation.y: " + Cube.rotation.eulerAngles.y);
70         GUILayout.Label("Cube de rotation.z: " + Cube.rotation.eulerAngles.z);
71     }
72 
73     private Vector3 getNormal(Vector3 p1,Vector3 p2,Vector3 p3)
74     {
75         float a = ((p2.y - p1.y) * (p3.z - p1.z) - (p2.z - p1.z) * (p3.y - p1.y));
76 
77         float b = ((p2.z - p1.z) * (p3.x - p1.x) - (p2.x - p1.x) * (p3.z - p1.z));
78 
79         float c = ((p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x));
80         //a對應的屏幕的垂直方向,b對應的屏幕的水平方向。
81         return new Vector3(a, -b, c);
82     }
83 }

  如果我們將這個在新機上運行,會發現在第一次手指滑動旋轉是正常的,但是第二次就會有個跳動的過程。在這里我們需要注意一個問題,四元數函數Quaternion.AngleAxis是將立方體以初始的旋轉角度來進行圍繞着軸Axis旋轉Angle角度,不是在上一個狀態下的增量。如果需要延續上一次的旋轉狀態,就需要將這個物體的rotation恢復到初始的狀態。按照這個思路,我在Cube添加了一個父對象,我們在操作的時候對這個父對象進行操作,然后手指在離開屏幕的時候,將Cube脫離父對象,然后將父對象的rotation進行還原,再將Cube綁定為父物體的子對象,在一下次手指旋轉之后就會接着上一次的旋轉狀態進行旋轉,實現了旋轉的延續。

  

  實現的代碼為:

 1 using UnityEngine;
 2 using System.Collections;
 3 
 4 public class gesture : MonoBehaviour {
 5     public Transform Cube;
 6     public Transform RotObj;
 7     private float radius = 1080;
 8     private Vector3 originalDir = new Vector3(0f,0f,1080f);
 9     private Vector3 CenterPos = new Vector3(0, 0, 0);
10     private Vector2 startPos;
11     private Vector2 tempPos;
12     private Vector3 tempVec;
13     private Vector3 normalAxis;
14     private float angle;
15     // Use this for initialization
16     void Start () {
17         Cube = GameObject.Find("Cube").transform;
18     }
19     
20     // Update is called once per frame
21     void Update () {
22         if (Input.touchCount == 1)
23         {
24             //Vector2 startPos = Input.compositionCursorPos;
25             if (Input.GetTouch(0).phase == TouchPhase.Began)
26             {
27                 startPos = Input.GetTouch(0).position;
28             }
29             if (Input.GetTouch(0).phase == TouchPhase.Moved)
30             {
31                 tempPos = Event.current.mousePosition;
32 
33                 float tempX = tempPos.x - startPos.x;
34                     
35                 float tempY = tempPos.y - startPos.y;
36 
37                 float tempZ = Mathf.Sqrt(radius * radius - tempX * tempX - tempY * tempY);
38 
39                 tempVec = new Vector3(tempX, tempY, tempZ);
40 
41                 angle = Mathf.Acos(Vector3.Dot(originalDir.normalized, tempVec.normalized)) * Mathf.Rad2Deg;
42 
43                 normalAxis = getNormal(CenterPos, originalDir, tempVec);
44 
45                 RotObj.rotation = Quaternion.AngleAxis(2 *angle, normalAxis);
46 
47             }
48             if (Input.GetTouch(0).phase == TouchPhase.Ended)
49             {
50                 Cube.transform.parent = null;
51                 RotObj.rotation = Quaternion.identity;
52                 Cube.parent = RotObj;
53             }
54         }
55     }
56 
57     void OnGUI()
58     {
59         GUILayout.Label("StartPos 的坐標值為: "+startPos);
60         GUILayout.Label("tempPos 的坐標值為: " + tempPos);
61         GUILayout.Label("tempVec 的坐標值為: " + tempVec);
62         GUILayout.Label("normalAxis 的坐標值為: " + normalAxis);
63         GUILayout.Label("旋轉角度的值為: " + 2*angle);
64         GUILayout.Label("Cube的四元數角度: " + Cube.rotation);
65         GUILayout.Label("Cube de rotation.x: " + Cube.rotation.eulerAngles.x);
66         GUILayout.Label("Cube de rotation.y: " + Cube.rotation.eulerAngles.y);
67         GUILayout.Label("Cube de rotation.z: " + Cube.rotation.eulerAngles.z);
68     }
69 
70     private Vector3 getNormal(Vector3 p1,Vector3 p2,Vector3 p3)
71     {
72         float a = ((p2.y - p1.y) * (p3.z - p1.z) - (p2.z - p1.z) * (p3.y - p1.y));
73 
74         float b = ((p2.z - p1.z) * (p3.x - p1.x) - (p2.x - p1.x) * (p3.z - p1.z));
75 
76         float c = ((p2.x - p1.x) * (p3.y - p1.y) - (p2.y - p1.y) * (p3.x - p1.x));
77         //a對應的屏幕的垂直方向,b對應的屏幕的水平方向。
78         return new Vector3(a, -b, c);
79     }
80 }

  現在對應着手指的滑動距離,然后調整參數radius,就可以實現比較順滑的旋轉效果,真機實現的效果就不展示了。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM