GestureManager.cs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. using Newtonsoft.Json;
  2. using System;
  3. using System.Collections.Generic;
  4. using UnityEngine;
  5. using UnityEngine.InputSystem;
  6. /* 本文件用于控制手势的采集和比对
  7. * 也包含导出和导入手势的功能
  8. */
  9. public class GestureManager
  10. {
  11. // Start is called once before the first execution of Update after the MonoBehaviour is created
  12. public List<GesturePointOffset> gesturePointOffsetList { set; get; } = new List<GesturePointOffset>();
  13. public int sampleRate { set; get; } = 20; // 采样率
  14. private Vector2 prePointLoc = new Vector2(0, 0); // 第一个点
  15. private Vector2 curPointLoc = new Vector2(0, 0); // 第二个点
  16. private float timer = 0f; // 配合interval使用
  17. public bool isRecording;
  18. private float interval; // 采样间隔,使用采样率计算
  19. private float lengthTorrence = 0.25f; // 允许的长度误差
  20. private DateTime startTime;
  21. private DateTime endTime;
  22. private float duration;
  23. private int screenWidth;
  24. private int screenHeight;
  25. public GestureManager()
  26. {
  27. interval = 1f / sampleRate;
  28. screenWidth = Screen.width;
  29. screenHeight = Screen.height;
  30. }
  31. public string ExportToJson()
  32. {
  33. string json = string.Empty;
  34. json = JsonConvert.SerializeObject(this);
  35. return json;
  36. }
  37. public void ImportFromJson(string json)
  38. {
  39. isRecording = false;
  40. var data = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
  41. sampleRate = int.Parse(data["sampleRate"].ToString());
  42. gesturePointOffsetList.Clear();
  43. GesturePointOffset[] pointOffsets = JsonConvert.DeserializeObject<GesturePointOffset[]>(data["gesturePointOffsetList"].ToString());
  44. foreach (var point in pointOffsets)
  45. {
  46. gesturePointOffsetList.Add(point);
  47. }
  48. }
  49. // 添加一个运动轨迹
  50. public void Record()
  51. {
  52. Vector2 pointLoc = Pointer.current.position.ReadValue();
  53. if (isRecording)
  54. {
  55. // if (!IsMouseOnScreen())
  56. // {
  57. // StopRecording();
  58. // Debug.Log("Mouse not on screen");
  59. // return;
  60. // }
  61. if (gesturePointOffsetList.Count == 0)
  62. {
  63. prePointLoc = pointLoc;
  64. curPointLoc = pointLoc;
  65. GesturePointOffset gesturePointMovement = new GesturePointOffset(0, 0);
  66. gesturePointOffsetList.Add(gesturePointMovement);
  67. return;
  68. }
  69. else
  70. {
  71. //Debug.Log("interval:" + interval.ToString());
  72. timer += Time.deltaTime;
  73. if (timer >= interval)
  74. {
  75. timer = 0f;
  76. curPointLoc = pointLoc;
  77. Vector2 normalizedCurPoint = NormalizePoint(curPointLoc);
  78. Vector2 normalizedPrePoint = NormalizePoint(prePointLoc);
  79. float distance = Vector2.Distance(normalizedCurPoint, normalizedPrePoint);
  80. Vector2 direction = curPointLoc - prePointLoc;
  81. float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
  82. // float angle = Vector2.Angle(prePointLoc, curPointLoc);
  83. GesturePointOffset gesturePointMovement = new GesturePointOffset(distance, angle);
  84. if (distance * angle != 0)
  85. {
  86. gesturePointOffsetList.Add(gesturePointMovement);
  87. }
  88. prePointLoc = curPointLoc;
  89. }
  90. }
  91. }
  92. }
  93. public float CompareGesture(GestureManager anotherGesture)
  94. {
  95. float score = 0f;
  96. if (sampleRate != anotherGesture.sampleRate)
  97. {
  98. Debug.Log("Sample rate not match");
  99. return -1f;
  100. }
  101. if (gesturePointOffsetList.Count == 0 || anotherGesture.gesturePointOffsetList.Count == 0)
  102. {
  103. Debug.Log("Gesture point count is 0");
  104. return -1f;
  105. }
  106. int maxGesturePoints = Mathf.Max(gesturePointOffsetList.Count, anotherGesture.gesturePointOffsetList.Count);
  107. int minGesturePoints = Mathf.Min(gesturePointOffsetList.Count, anotherGesture.gesturePointOffsetList.Count);
  108. for (int i = 0; i < minGesturePoints; i++)
  109. {
  110. float distanceMin = Mathf.Min(gesturePointOffsetList[i].distance, anotherGesture.gesturePointOffsetList[i].distance);
  111. float distanceMax = Mathf.Max(gesturePointOffsetList[i].distance, anotherGesture.gesturePointOffsetList[i].distance);
  112. float angleMin = Mathf.Min(gesturePointOffsetList[i].angle, anotherGesture.gesturePointOffsetList[i].angle);
  113. float angleMax = Mathf.Max(gesturePointOffsetList[i].angle, anotherGesture.gesturePointOffsetList[i].angle);
  114. float distanceScore = 0f;
  115. if (distanceMin == 0)
  116. {
  117. distanceScore = 1f;
  118. }
  119. else
  120. {
  121. distanceScore = distanceMin / distanceMax;
  122. }
  123. float angleDitance1 = angleMax - angleMin;
  124. float angleDitance2 = 360 - angleDitance1;
  125. float angleScore = 1 - (Mathf.Min(angleDitance1, angleDitance2) / 180);
  126. score = score * (i / (i + 1f)) + (distanceScore + angleScore) / 2 * (1f / (i + 1));
  127. }
  128. // 如果长度超过公差范围,就要考虑调整score
  129. if ((maxGesturePoints / minGesturePoints) > (1f + lengthTorrence))
  130. {
  131. int deltaGesturePoints = Mathf.RoundToInt(maxGesturePoints - minGesturePoints * (1f + lengthTorrence));
  132. score = score * ((float)minGesturePoints / (minGesturePoints + deltaGesturePoints));
  133. }
  134. return score;
  135. }
  136. public void ResetGesture()
  137. {
  138. gesturePointOffsetList.Clear();
  139. }
  140. public void StartRecording()
  141. {
  142. isRecording = true;
  143. gesturePointOffsetList.Clear();
  144. timer = 0f;
  145. prePointLoc = Vector2.zero;
  146. curPointLoc = Vector2.zero;
  147. startTime = DateTime.Now;
  148. }
  149. public bool StopRecording()
  150. {
  151. isRecording = false;
  152. endTime = DateTime.Now;
  153. duration = (float)(endTime - startTime).TotalSeconds;
  154. Debug.Log("gesture duration:" + duration.ToString());
  155. Debug.Log("gesture point count:" + gesturePointOffsetList.Count.ToString());
  156. // 如果采样过少,就不记录这个手势
  157. if (gesturePointOffsetList.Count <= sampleRate/2)
  158. {
  159. Debug.Log("gesture duration too short");
  160. gesturePointOffsetList.Clear();
  161. }
  162. if (gesturePointOffsetList.Count > 0)
  163. {
  164. return true;
  165. }
  166. else
  167. {
  168. return false;
  169. }
  170. }
  171. private bool IsMouseOnScreen()
  172. {
  173. Vector2 mousePos = Pointer.current.position.ReadValue();
  174. return mousePos.x >= 0 && mousePos.x < Screen.width &&
  175. mousePos.y >= 0 && mousePos.y < Screen.height;
  176. }
  177. private Vector2 NormalizePoint(Vector2 point)
  178. {
  179. float x = point.x / screenWidth;
  180. float y = point.y / screenHeight;
  181. return new Vector2(x, y);
  182. }
  183. private Vector2 DenormalizePoint(Vector2 point)
  184. {
  185. float x = point.x * screenWidth;
  186. float y = point.y * screenHeight;
  187. return new Vector2(x, y);
  188. }
  189. public void SetSampleRate(int rate)
  190. {
  191. sampleRate = rate;
  192. interval = 1f / sampleRate;
  193. }
  194. }