123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 |
- using Newtonsoft.Json;
- using System;
- using System.Collections.Generic;
- using UnityEngine;
- using UnityEngine.InputSystem;
- /* 本文件用于控制手势的采集和比对
- * 也包含导出和导入手势的功能
- */
- public class GestureManager
- {
- // Start is called once before the first execution of Update after the MonoBehaviour is created
- public List<GesturePointOffset> gesturePointOffsetList { set; get; } = new List<GesturePointOffset>();
- public int sampleRate { set; get; } = 20; // 采样率
- private Vector2 prePointLoc = new Vector2(0, 0); // 第一个点
- private Vector2 curPointLoc = new Vector2(0, 0); // 第二个点
- private float timer = 0f; // 配合interval使用
- public bool isRecording;
- private float interval; // 采样间隔,使用采样率计算
- private float lengthTorrence = 0.25f; // 允许的长度误差
- private DateTime startTime;
- private DateTime endTime;
- private float duration;
- private int screenWidth;
- private int screenHeight;
- public GestureManager()
- {
- interval = 1f / sampleRate;
- screenWidth = Screen.width;
- screenHeight = Screen.height;
- }
- public string ExportToJson()
- {
- string json = string.Empty;
- json = JsonConvert.SerializeObject(this);
- return json;
- }
- public void ImportFromJson(string json)
- {
- isRecording = false;
- var data = JsonConvert.DeserializeObject<Dictionary<string, object>>(json);
- sampleRate = int.Parse(data["sampleRate"].ToString());
- gesturePointOffsetList.Clear();
- GesturePointOffset[] pointOffsets = JsonConvert.DeserializeObject<GesturePointOffset[]>(data["gesturePointOffsetList"].ToString());
- foreach (var point in pointOffsets)
- {
- gesturePointOffsetList.Add(point);
- }
- }
- // 添加一个运动轨迹
- public void Record()
- {
- Vector2 pointLoc = Pointer.current.position.ReadValue();
- if (isRecording)
- {
- // if (!IsMouseOnScreen())
- // {
- // StopRecording();
- // Debug.Log("Mouse not on screen");
- // return;
- // }
- if (gesturePointOffsetList.Count == 0)
- {
- prePointLoc = pointLoc;
- curPointLoc = pointLoc;
- GesturePointOffset gesturePointMovement = new GesturePointOffset(0, 0);
- gesturePointOffsetList.Add(gesturePointMovement);
- return;
- }
- else
- {
- //Debug.Log("interval:" + interval.ToString());
- timer += Time.deltaTime;
- if (timer >= interval)
- {
- timer = 0f;
- curPointLoc = pointLoc;
- Vector2 normalizedCurPoint = NormalizePoint(curPointLoc);
- Vector2 normalizedPrePoint = NormalizePoint(prePointLoc);
- float distance = Vector2.Distance(normalizedCurPoint, normalizedPrePoint);
- Vector2 direction = curPointLoc - prePointLoc;
- float angle = Mathf.Atan2(direction.y, direction.x) * Mathf.Rad2Deg;
- // float angle = Vector2.Angle(prePointLoc, curPointLoc);
- GesturePointOffset gesturePointMovement = new GesturePointOffset(distance, angle);
- if (distance * angle != 0)
- {
- gesturePointOffsetList.Add(gesturePointMovement);
- }
- prePointLoc = curPointLoc;
- }
- }
- }
- }
- public float CompareGesture(GestureManager anotherGesture)
- {
- float score = 0f;
- if (sampleRate != anotherGesture.sampleRate)
- {
- Debug.Log("Sample rate not match");
- return -1f;
- }
- if (gesturePointOffsetList.Count == 0 || anotherGesture.gesturePointOffsetList.Count == 0)
- {
- Debug.Log("Gesture point count is 0");
- return -1f;
- }
- int maxGesturePoints = Mathf.Max(gesturePointOffsetList.Count, anotherGesture.gesturePointOffsetList.Count);
- int minGesturePoints = Mathf.Min(gesturePointOffsetList.Count, anotherGesture.gesturePointOffsetList.Count);
- for (int i = 0; i < minGesturePoints; i++)
- {
- float distanceMin = Mathf.Min(gesturePointOffsetList[i].distance, anotherGesture.gesturePointOffsetList[i].distance);
- float distanceMax = Mathf.Max(gesturePointOffsetList[i].distance, anotherGesture.gesturePointOffsetList[i].distance);
- float angleMin = Mathf.Min(gesturePointOffsetList[i].angle, anotherGesture.gesturePointOffsetList[i].angle);
- float angleMax = Mathf.Max(gesturePointOffsetList[i].angle, anotherGesture.gesturePointOffsetList[i].angle);
- float distanceScore = 0f;
- if (distanceMin == 0)
- {
- distanceScore = 1f;
- }
- else
- {
- distanceScore = distanceMin / distanceMax;
- }
- float angleDitance1 = angleMax - angleMin;
- float angleDitance2 = 360 - angleDitance1;
- float angleScore = 1 - (Mathf.Min(angleDitance1, angleDitance2) / 180);
- score = score * (i / (i + 1f)) + (distanceScore + angleScore) / 2 * (1f / (i + 1));
- }
- // 如果长度超过公差范围,就要考虑调整score
- if ((maxGesturePoints / minGesturePoints) > (1f + lengthTorrence))
- {
- int deltaGesturePoints = Mathf.RoundToInt(maxGesturePoints - minGesturePoints * (1f + lengthTorrence));
- score = score * ((float)minGesturePoints / (minGesturePoints + deltaGesturePoints));
- }
- return score;
- }
- public void ResetGesture()
- {
- gesturePointOffsetList.Clear();
- }
- public void StartRecording()
- {
- isRecording = true;
- gesturePointOffsetList.Clear();
- timer = 0f;
- prePointLoc = Vector2.zero;
- curPointLoc = Vector2.zero;
- startTime = DateTime.Now;
- }
- public bool StopRecording()
- {
- isRecording = false;
- endTime = DateTime.Now;
- duration = (float)(endTime - startTime).TotalSeconds;
- Debug.Log("gesture duration:" + duration.ToString());
- Debug.Log("gesture point count:" + gesturePointOffsetList.Count.ToString());
- // 如果采样过少,就不记录这个手势
- if (gesturePointOffsetList.Count <= sampleRate/2)
- {
- Debug.Log("gesture duration too short");
- gesturePointOffsetList.Clear();
- }
- if (gesturePointOffsetList.Count > 0)
- {
- return true;
- }
- else
- {
- return false;
- }
- }
- private bool IsMouseOnScreen()
- {
- Vector2 mousePos = Pointer.current.position.ReadValue();
- return mousePos.x >= 0 && mousePos.x < Screen.width &&
- mousePos.y >= 0 && mousePos.y < Screen.height;
- }
- private Vector2 NormalizePoint(Vector2 point)
- {
- float x = point.x / screenWidth;
- float y = point.y / screenHeight;
- return new Vector2(x, y);
- }
- private Vector2 DenormalizePoint(Vector2 point)
- {
- float x = point.x * screenWidth;
- float y = point.y * screenHeight;
- return new Vector2(x, y);
- }
- public void SetSampleRate(int rate)
- {
- sampleRate = rate;
- interval = 1f / sampleRate;
- }
- }
|