SimpleInputManager.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. // Magica Cloth.
  2. // Copyright (c) MagicaSoft, 2020-2022.
  3. // https://magicasoft.jp
  4. using UnityEngine;
  5. using UnityEngine.Events;
  6. using UnityEngine.EventSystems;
  7. namespace MagicaCloth
  8. {
  9. /// <summary>
  10. /// 入力マネージャ
  11. /// ・簡単なタップやフリック判定
  12. /// ・PCの場合はマウスによる自動エミュレーション
  13. /// </summary>
  14. public class SimpleInputManager : CreateSingleton<SimpleInputManager>
  15. {
  16. // 最大タッチ数
  17. private const int MaxFinger = 3;
  18. /// <summary>
  19. /// タップ有効半径(cm)
  20. /// </summary>
  21. public float tapRadiusCm = 0.5f;
  22. /// <summary>
  23. /// フリック判定距離(cm)
  24. /// </summary>
  25. public float flickRangeCm = 0.01f;
  26. /// <summary>
  27. /// フリック判定速度(cm/s)
  28. /// </summary>
  29. public float flickCheckSpeed = 1.0f;
  30. /// <summary>
  31. /// マウスホイールのピンチイン・ピンチアウト速度係数
  32. /// </summary>
  33. public float mouseWheelSpeed = 5.0f;
  34. // 入力情報管理
  35. private int mainFingerId = -1;
  36. private int subFingerId = -1;
  37. private Vector2[] downPos; // 入力開始座標(スクリーン)
  38. private Vector2[] lastPos;
  39. private Vector2[] flickDownPos; // 入力開始座標(スクリーン)
  40. private float[] flickDownTime;
  41. private float lastTime = 0; // バックボタンの連続入力防止用
  42. // モバイル情報管理
  43. private bool mobilePlatform = false;
  44. // マウスエミュレーション情報管理
  45. private bool[] mouseDown;
  46. private Vector2[] mouseOldMovePos;
  47. // モニタ情報
  48. private float screenDpi; // スクリーンDPI値
  49. private float screenDpc; // スクリーンDots per cm値(1cm当たりのピクセル数)
  50. //------------------------------ モバイルタッチパネル/マウスエミュレーション ------------------
  51. // タッチ開始通知
  52. // タッチされた時に、フィンガーID、その位置(スクリーン)を通知します。
  53. public static UnityAction<int, Vector2> OnTouchDown;
  54. // 移動通知
  55. // タッチされたまま移動された場合に、フィンガーID、その位置(スクリーン)、速度(スクリーン比率/s)、速度(cm/s)を通知します。
  56. public static UnityAction<int, Vector2, Vector2, Vector2> OnTouchMove;
  57. // ダブルタッチされたまま移動された場合に、フィンガーID、その位置(スクリーン)、速度(スクリーン比率/s)、速度(cm/s)を通知します。
  58. public static UnityAction<int, Vector2, Vector2, Vector2> OnDoubleTouchMove;
  59. // タッチ終了通知
  60. // タッチが離されたフィンガーID、位置(スクリーン)を通知します。
  61. public static UnityAction<int, Vector2> OnTouchUp;
  62. // タッチキャンセル通知
  63. // タッチ移動がキャンセル(主に画面外に移動)された場合に、フィンガーID、その最終位置(スクリーン)を通知します。
  64. public static UnityAction<int, Vector2> OnTouchMoveCancel;
  65. // タップ通知
  66. // タップされた時に、フィンガーID、その位置(スクリーン)を通知します。
  67. public static UnityAction<int, Vector2> OnTouchTap;
  68. // フリック通知
  69. // フリック判定された場合に、フィンガーID、その位置(スクリーン)、フリック速度(スクリーン比率/s)、速度(cm/s)を通知します。
  70. public static UnityAction<int, Vector2, Vector2, Vector2> OnTouchFlick;
  71. // ピンチイン/アウト通知
  72. // ピンチイン/アウトの速度(スクリーン比率/s)、速度(cm/s)を通知します。
  73. public static UnityAction<float, float> OnTouchPinch;
  74. // バックボタン通知(Androiddeでは戻るボタン、PCでは BackSpace ボタン)
  75. public static UnityAction OnBackButton;
  76. //=========================================================================================
  77. /// <summary>
  78. /// Reload Domain 対策
  79. /// </summary>
  80. [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
  81. private static void Init()
  82. {
  83. InitMember();
  84. }
  85. //=========================================================================================
  86. protected override void InitSingleton()
  87. {
  88. // スクリーン情報
  89. CalcScreenDpi();
  90. // 情報初期化
  91. downPos = new Vector2[MaxFinger];
  92. lastPos = new Vector2[MaxFinger];
  93. flickDownPos = new Vector2[MaxFinger];
  94. flickDownTime = new float[MaxFinger];
  95. // マウス用
  96. mouseDown = new bool[3];
  97. mouseOldMovePos = new Vector2[3];
  98. AllResetTouchInfo();
  99. // モバイルプラットフォーム判定
  100. mobilePlatform = Application.isMobilePlatform;
  101. }
  102. void Update()
  103. {
  104. // 入力タイプ別更新処理
  105. if (mobilePlatform)
  106. {
  107. // モバイル用タッチ入力
  108. UpdateMobile();
  109. }
  110. else
  111. {
  112. // マウスエミュレーション
  113. UpdateMouse();
  114. }
  115. }
  116. //=========================================================================================
  117. /// <summary>
  118. /// スクリーンのDPI値(Dots per inchi)1インチ当たりのピクセル数を取得する
  119. /// </summary>
  120. public static float ScreenDpi
  121. {
  122. get
  123. {
  124. return Instance.screenDpi;
  125. }
  126. }
  127. /// <summary>
  128. /// スクリーンのDPC値(Dots per cm)1cm当たりのピクセル数を取得する
  129. /// </summary>
  130. public static float ScreenDpc
  131. {
  132. get
  133. {
  134. return Instance.screenDpc;
  135. }
  136. }
  137. /// <summary>
  138. /// スクリーンDpi/Dpcの再計算
  139. /// </summary>
  140. private void CalcScreenDpi()
  141. {
  142. screenDpi = Screen.dpi;
  143. if (screenDpi == 0.0f)
  144. {
  145. screenDpi = 96; // ダミー
  146. }
  147. screenDpc = screenDpi / 2.54f; // インチをcmに変換
  148. }
  149. // タッチ入力情報リセット
  150. private void AllResetTouchInfo()
  151. {
  152. mainFingerId = -1;
  153. subFingerId = -1;
  154. for (int i = 0; i < 3; i++)
  155. {
  156. mouseDown[i] = false;
  157. }
  158. }
  159. public int GetTouchCount()
  160. {
  161. return Input.touchCount;
  162. }
  163. public bool IsUI()
  164. {
  165. if (mobilePlatform)
  166. {
  167. // モバイル用タッチ入力
  168. return EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId);
  169. }
  170. else
  171. {
  172. // マウスエミュレーション
  173. return EventSystem.current.IsPointerOverGameObject();
  174. }
  175. }
  176. //=========================================================================================
  177. /// <summary>
  178. /// モバイル用入力更新
  179. /// </summary>
  180. private void UpdateMobile()
  181. {
  182. int count = Input.touchCount;
  183. if (count == 0)
  184. {
  185. AllResetTouchInfo();
  186. // バックボタン
  187. if (Application.platform == RuntimePlatform.Android)
  188. {
  189. if (Input.GetKey(KeyCode.Escape) && lastTime + 0.2f < Time.time)
  190. {
  191. lastTime = Time.time;
  192. if (OnBackButton != null)
  193. {
  194. OnBackButton();
  195. }
  196. return;
  197. }
  198. }
  199. }
  200. else
  201. {
  202. // メイン
  203. for (int i = 0; i < count; i++)
  204. {
  205. Touch touch = Input.GetTouch(i);
  206. int fid = touch.fingerId;
  207. // フィンガーIDが0と1以外は無視する
  208. if (fid >= 2)
  209. {
  210. continue;
  211. }
  212. if (touch.phase == TouchPhase.Began)
  213. {
  214. if (IsUI())
  215. continue;
  216. // down pos
  217. downPos[fid] = touch.position;
  218. lastPos[fid] = touch.position;
  219. flickDownPos[fid] = touch.position;
  220. if (fid == 0)
  221. {
  222. mainFingerId = fid;
  223. }
  224. else
  225. {
  226. subFingerId = fid;
  227. }
  228. // Downはメインフィンガーのみ
  229. if (fid == 0)
  230. {
  231. flickDownTime[fid] = Time.time;
  232. if (OnTouchDown != null)
  233. {
  234. OnTouchDown(fid, touch.position);
  235. }
  236. }
  237. }
  238. else if (touch.phase == TouchPhase.Moved)
  239. {
  240. // ピンチイン/アウト判定
  241. if (mainFingerId >= 0 && subFingerId >= 0)
  242. {
  243. Vector2 t1pos = Vector2.zero;
  244. Vector2 t2pos = Vector2.zero;
  245. Vector2 t1delta = Vector2.zero;
  246. Vector2 t2delta = Vector2.zero;
  247. int setcnt = 0;
  248. for (int j = 0; j < count; j++)
  249. {
  250. Touch t = Input.GetTouch(j);
  251. if (mainFingerId == t.fingerId)
  252. {
  253. t1pos = t.position;
  254. t1delta = t.deltaPosition;
  255. setcnt++;
  256. }
  257. else if (subFingerId == t.fingerId)
  258. {
  259. t2pos = t.position;
  260. t2delta = t.deltaPosition;
  261. setcnt++;
  262. }
  263. }
  264. if (setcnt == 2)
  265. {
  266. float nowdist = Vector2.Distance(t1pos, t2pos);
  267. float olddist = Vector2.Distance(t1pos - t1delta, t2pos - t2delta);
  268. float dist = nowdist - olddist;
  269. // cm/sに変換
  270. float distcm = dist / screenDpc; // 移動量(cm)
  271. float speedcm = distcm / Time.deltaTime; // 速度(cm/s)
  272. // スクリーン比率の速度
  273. float speedscr = (dist / (Screen.width + Screen.height) * 0.5f) / Time.deltaTime;
  274. // ピンチ通知(移動量(cm), 速度(cm/s))
  275. if (OnTouchPinch != null)
  276. {
  277. OnTouchPinch(speedscr, speedcm);
  278. }
  279. }
  280. if (fid == 0)
  281. {
  282. Vector2 distVec2 = touch.position - lastPos[fid];
  283. Vector2 distcm = distVec2 / screenDpc; // 移動量(cm)
  284. Vector2 speedcm = distcm / Time.deltaTime; // 速度(cm/s)
  285. // 速度(スクリーン比率)
  286. Vector2 speedscr = CalcScreenRatioVector(distVec2) / Time.deltaTime;
  287. // 移動通知(現在スクリーン座標、速度(スクリーン比率), 速度(cm/s))
  288. if (OnDoubleTouchMove != null)
  289. {
  290. OnDoubleTouchMove(fid, touch.position, speedscr, speedcm);
  291. }
  292. lastPos[fid] = touch.position;
  293. }
  294. }
  295. else
  296. {
  297. // Moveはメインフィンガーのみ
  298. if (fid == 0 && mainFingerId >= 0)
  299. {
  300. Vector2 distVec2 = touch.position - lastPos[fid];
  301. Vector2 distcm = distVec2 / screenDpc; // 移動量(cm)
  302. Vector2 speedcm = distcm / Time.deltaTime; // 速度(cm/s)
  303. // 速度(スクリーン比率)
  304. Vector2 speedscr = CalcScreenRatioVector(distVec2) / Time.deltaTime;
  305. // 移動通知(現在スクリーン座標、速度(スクリーン比率), 速度(cm/s))
  306. if (OnTouchMove != null)
  307. {
  308. OnTouchMove(fid, touch.position, speedscr, speedcm);
  309. }
  310. // フリックダウン位置更新
  311. flickDownPos[fid] = (flickDownPos[fid] + touch.position) * 0.5f;
  312. flickDownTime[fid] = Time.time;
  313. }
  314. lastPos[fid] = touch.position;
  315. }
  316. }
  317. else if (touch.phase == TouchPhase.Ended)
  318. {
  319. // フィンガーIDのリリース
  320. if (fid == 0)
  321. {
  322. mainFingerId = -1;
  323. subFingerId = -1;
  324. }
  325. else
  326. {
  327. subFingerId = -1;
  328. }
  329. // End, Tap はメインフィンガーのみ
  330. if (fid == 0)
  331. {
  332. // タップ判定
  333. float dist = Vector2.Distance(downPos[fid], touch.position);
  334. float distcm = dist / screenDpc;
  335. if (distcm <= tapRadiusCm)
  336. {
  337. // タップ通知
  338. if (OnTouchTap != null)
  339. {
  340. OnTouchTap(fid, touch.position);
  341. }
  342. }
  343. // フリック判定
  344. else
  345. {
  346. CheckFlic(fid, downPos[fid], touch.position, flickDownPos[fid], flickDownTime[fid]);
  347. }
  348. // タップアップ通知
  349. if (OnTouchUp != null)
  350. {
  351. OnTouchUp(fid, touch.position);
  352. }
  353. }
  354. }
  355. else if (touch.phase == TouchPhase.Canceled)
  356. {
  357. // フィンガーIDのリリース
  358. if (fid == 0)
  359. {
  360. mainFingerId = -1;
  361. subFingerId = -1;
  362. }
  363. else
  364. {
  365. subFingerId = -1;
  366. }
  367. // Cancelはメインフィンガーのみ
  368. if (fid == 0)
  369. {
  370. if (OnTouchMoveCancel != null)
  371. {
  372. OnTouchMoveCancel(fid, touch.position);
  373. }
  374. }
  375. }
  376. }
  377. }
  378. }
  379. /// <summary>
  380. /// スクリーン比率に変換したベクトルを求める
  381. /// </summary>
  382. /// <param name="vec"></param>
  383. /// <returns></returns>
  384. private Vector2 CalcScreenRatioVector(Vector2 vec)
  385. {
  386. return new Vector2(vec.x / Screen.width, vec.y / Screen.height);
  387. }
  388. /// <summary>
  389. /// フリック判定
  390. /// </summary>
  391. /// <param name="oldpos"></param>
  392. /// <param name="nowpos"></param>
  393. /// <param name="downpos"></param>
  394. /// <param name="flicktime"></param>
  395. /// <returns></returns>
  396. private bool CheckFlic(int fid, Vector2 oldpos, Vector2 nowpos, Vector2 downpos, float flicktime)
  397. {
  398. // フリック判定
  399. float dist = Vector2.Distance(nowpos, downpos);
  400. float distcm = dist / screenDpc;
  401. if (distcm > flickRangeCm)
  402. {
  403. {
  404. // 移動ピクセルをcm変換し、速度cm/sを割り出す
  405. Vector2 distVec = (nowpos - downpos);
  406. Vector2 distVec2 = distVec / screenDpc; // cmへ変換(移動量(cm))
  407. float timeInterval = Time.time - flicktime;
  408. float speedX = distVec2.x / timeInterval; // 速度(cm/s)
  409. float speedY = distVec2.y / timeInterval; // 速度(cm/s)
  410. //Develop.Log("distVec", distVec * 100);
  411. //Develop.Log("sppedX:", speedX, " speedY:", speedY);
  412. if (Mathf.Abs(speedX) >= flickCheckSpeed || Mathf.Abs(speedY) >= flickCheckSpeed)
  413. {
  414. // フリック通知(スクリーン位置,速度(スクリーン比率/s),速度(cm/s))
  415. if (OnTouchFlick != null)
  416. {
  417. OnTouchFlick(fid, nowpos, CalcScreenRatioVector(distVec) / timeInterval, new Vector2(speedX, speedY));
  418. }
  419. return true;
  420. }
  421. }
  422. }
  423. return false;
  424. }
  425. //=========================================================================================
  426. /// <summary>
  427. /// 入力情報更新(PC用)
  428. /// マウスエミュレーション
  429. /// ・右クリックは使わない。
  430. /// ・ピンチイン/アウトはマウスホイール。
  431. /// </summary>
  432. private void UpdateMouse()
  433. {
  434. // BackSpace を Android 端末のバックボタンに割り当てる
  435. if (Input.GetKeyDown(KeyCode.Backspace))
  436. {
  437. if (OnBackButton != null)
  438. OnBackButton();
  439. return;
  440. }
  441. for (int i = 0; i < 3; i++)
  442. {
  443. // マウスボタンダウン
  444. if (Input.GetMouseButtonDown(i))
  445. {
  446. if (IsUI())
  447. continue;
  448. if (mouseDown[i] == false && i == 0)
  449. {
  450. flickDownTime[i] = Time.time;
  451. }
  452. mouseDown[i] = true;
  453. // 入力位置を記録
  454. downPos[i] = Input.mousePosition;
  455. mouseOldMovePos[i] = Input.mousePosition;
  456. if (i == 0)
  457. flickDownPos[i] = Input.mousePosition;
  458. // タッチダウンイベント発行
  459. if (OnTouchDown != null)
  460. OnTouchDown(i, Input.mousePosition);
  461. }
  462. // マウスボタンアップ
  463. if (Input.GetMouseButtonUp(i) && mouseDown[i])
  464. {
  465. mouseDown[i] = false;
  466. // フリック判定
  467. if (i == 0)
  468. {
  469. CheckFlic(i, mouseOldMovePos[i], Input.mousePosition, flickDownPos[i], flickDownTime[i]);
  470. }
  471. mouseOldMovePos[i] = Vector2.zero;
  472. // タッチアップイベント
  473. if (OnTouchUp != null)
  474. OnTouchUp(i, Input.mousePosition);
  475. // タップ判定
  476. float distcm = Vector2.Distance(downPos[0], Input.mousePosition) / screenDpc;
  477. if (distcm <= tapRadiusCm)
  478. {
  479. if (OnTouchTap != null)
  480. OnTouchTap(i, Input.mousePosition);
  481. }
  482. }
  483. // 移動
  484. if (mouseDown[i])
  485. {
  486. Vector2 spos = new Vector2(Input.mousePosition.x, Input.mousePosition.y);
  487. Vector2 delta = spos - mouseOldMovePos[i];
  488. if (spos != mouseOldMovePos[i])
  489. {
  490. // 速度
  491. Vector3 deltacm = delta / screenDpc; // 移動量(cm)
  492. Vector2 speedcm = deltacm / Time.deltaTime; // 速度(cm/s)
  493. // 移動通知(現在スクリーン座標、速度(スクリーン比率/s)、速度(cm/s))
  494. if (OnTouchMove != null)
  495. OnTouchMove(i, Input.mousePosition, CalcScreenRatioVector(delta) / Time.deltaTime, speedcm);
  496. }
  497. mouseOldMovePos[i] = Input.mousePosition;
  498. // フリックダウン位置更新
  499. flickDownPos[i] = (flickDownPos[i] + spos) * 0.5f;
  500. flickDownTime[i] = Time.time;
  501. }
  502. }
  503. // ピンチイン/アウト
  504. float w = Input.GetAxis("Mouse ScrollWheel");
  505. if (Mathf.Abs(w) > 0.01f)
  506. {
  507. // モバイル入力とスケール感を合わせるために係数を掛ける
  508. w *= mouseWheelSpeed;
  509. float speedcm = w / Time.deltaTime;
  510. float speedscr = (w / (Screen.width + Screen.height) * 0.5f) / Time.deltaTime;
  511. // 通知(速度(スクリーン比率/s)、速度(cm/s)
  512. if (OnTouchPinch != null)
  513. OnTouchPinch(speedscr, speedcm);
  514. }
  515. }
  516. }
  517. }