SoundGameController.cs 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. using System.Collections;
  2. using UnityEngine;
  3. using UnityEngine.InputSystem;
  4. /* * WalkDogs 场景挂载在2D Player上
  5. * SoundGameController.cs
  6. * Author: 2023-10-12
  7. * Description: 音乐游戏控制器
  8. * 1. 读取音轨数据
  9. * 2. 创建音符
  10. * 3. 检测音符点击
  11. */
  12. public class SoundGameController : MonoBehaviour
  13. {
  14. SoundTrackManager soundTrackManager = new();
  15. GameObject notePrefabSwipe, notePrefabTap;
  16. float gameStartTime;
  17. float GameEndTime;
  18. // 游戏得分统计
  19. int score = 0;
  20. int combo = 0;
  21. int PerfectCount = 0;
  22. int GoodCount = 0;
  23. int PoorCount = 0;
  24. int MissCount = 0;
  25. int totalCount = 0;
  26. // TapOrSwipe 触控检测
  27. Vector2 pressDownPosition = new Vector2(0, 0);
  28. Vector2 pressUpPosition = new Vector2(0, 0);
  29. float pressDownTime = 0f;
  30. float pressUpTime = 0f;
  31. // Start is called once before the first execution of Update after the MonoBehaviour is created
  32. void Start()
  33. {
  34. TextAsset soundTrackJson = Resources.Load<TextAsset>("WalkDogs/SoundTrack/track_1");
  35. if (soundTrackJson != null)
  36. {
  37. string soundTrackString = soundTrackJson.text;
  38. soundTrackManager.ImportFromJson(soundTrackString);
  39. }
  40. notePrefabSwipe = Resources.Load<GameObject>("WalkDogs/Note/NoteSwipe");
  41. notePrefabTap = Resources.Load<GameObject>("WalkDogs/Note/NoteTap");
  42. StartCoroutine(PlaySoundGame());
  43. }
  44. // Update is called once per frame
  45. void Update()
  46. {
  47. }
  48. IEnumerator PlaySoundGame()
  49. {
  50. // 加载音乐
  51. AudioClip audioClip = Resources.Load<AudioClip>(soundTrackManager.resource);
  52. AudioSource audioSource = gameObject.AddComponent<AudioSource>();
  53. audioSource.clip = audioClip;
  54. audioSource.Play();
  55. gameStartTime = Time.time;
  56. GameEndTime = gameStartTime + soundTrackManager.length;
  57. while (Time.time < GameEndTime)
  58. {
  59. // 用于提前n秒创建音符
  60. foreach (var note in soundTrackManager.soundAction)
  61. {
  62. if (!note.isActive && Time.time >= (note.time - GameData.noteStartLeadTime))
  63. {
  64. note.isActive = true;
  65. CreateNote(note.time, note.action, soundTrackManager.soundAction.IndexOf(note).ToString());
  66. continue;
  67. }
  68. }
  69. // 检测音符是否超时未被点击
  70. foreach (var note in soundTrackManager.soundAction)
  71. {
  72. float TapTimeDeadline = note.time + gameStartTime + 0.35f; // 计算点击事件超时
  73. if (note.isActive && !note.isTapped && Time.time > TapTimeDeadline)
  74. {
  75. note.isTapped = true;
  76. MissCount++;
  77. Debug.Log("Miss!" + Time.time + " Tap time deadline: " + TapTimeDeadline + " total miss:" + MissCount);
  78. Debug.Log("index of note:" + soundTrackManager.soundAction.IndexOf(note));
  79. break;
  80. }
  81. }
  82. // 检测游戏是否结束并统计分数
  83. totalCount = PerfectCount + GoodCount + PoorCount + MissCount;
  84. if (totalCount == soundTrackManager.soundAction.Count)
  85. {
  86. GameEndTime = Time.time;
  87. Debug.Log("Game Over!");
  88. Debug.Log($"Perfect: {PerfectCount}, Good: {GoodCount}, Poor: {PoorCount}, Miss: {MissCount}");
  89. Debug.Log($"Total Count: {totalCount}");
  90. Debug.Log($"Score: {score}");
  91. Debug.Log($"Combo: {combo}");
  92. // 结束游戏
  93. break; // 跳出 while 循环
  94. }
  95. yield return new WaitForSeconds(0.01f);
  96. }
  97. yield return null;
  98. }
  99. private void CreateNote(float hitTime, string action, string noteName)
  100. {
  101. GameObject note;
  102. if (action == "tap")
  103. {
  104. note = Instantiate(notePrefabTap);
  105. }
  106. else
  107. {
  108. note = Instantiate(notePrefabSwipe);
  109. }
  110. note.gameObject.name = noteName;
  111. NoteController noteController = note.GetComponent<NoteController>();
  112. noteController.speed = soundTrackManager.speed;
  113. noteController.hitTime = hitTime;
  114. noteController.action = action;
  115. }
  116. // 触控检测,检测通过按下和和抬起的触控点位置计算点击或者上下左右滑动
  117. public void TapOrSwipe(InputAction.CallbackContext context)
  118. {
  119. if (context.phase == InputActionPhase.Started)
  120. {
  121. pressDownPosition = Pointer.current.position.ReadValue();
  122. pressDownTime = Time.time;
  123. return;
  124. }
  125. if (context.phase == InputActionPhase.Canceled)
  126. {
  127. pressUpPosition = Pointer.current.position.ReadValue();
  128. pressUpTime = Time.time;
  129. float deltaX = pressUpPosition.x - pressDownPosition.x;
  130. float deltaY = pressUpPosition.y - pressDownPosition.y;
  131. float distance = Mathf.Sqrt(deltaX * deltaX + deltaY * deltaY);
  132. // Debug.Log($"deltaX: {deltaX}, deltaY: {deltaY}, distance: {distance}");
  133. float timeDelta = pressUpTime - pressDownTime;
  134. string action;
  135. if (timeDelta > 0.2f)
  136. {
  137. return;
  138. }
  139. else
  140. {
  141. if (distance < 20f)
  142. {
  143. action = "tap";
  144. }
  145. else
  146. {
  147. if (Mathf.Abs(deltaX) > Mathf.Abs(deltaY))
  148. {
  149. if (deltaX > 0)
  150. {
  151. action = "right";
  152. }
  153. else
  154. {
  155. action = "left";
  156. }
  157. }
  158. else
  159. {
  160. if (deltaY > 0)
  161. {
  162. action = "up";
  163. }
  164. else
  165. {
  166. action = "down";
  167. }
  168. }
  169. }
  170. }
  171. TouchResponse touchResponse = new TouchResponse(action, pressUpTime);
  172. // Debug.Log($"TouchResponse: {touchResponse.action}, {touchResponse.time}");
  173. // 检测点击的音符
  174. ScoreCheck(touchResponse);
  175. }
  176. return;
  177. }
  178. private void ScoreCheck(TouchResponse touchResponse)
  179. {
  180. if (touchResponse != null)
  181. {
  182. foreach (var note in soundTrackManager.soundAction)
  183. {
  184. float TapTimeDelta = Mathf.Abs(touchResponse.time - (note.time + gameStartTime)); // 计算点击时间和音符时间的差值
  185. if (note.isActive && !note.isTapped && TapTimeDelta < 0.3f)
  186. {
  187. note.isTapped = true;
  188. if (touchResponse.action == note.action)
  189. {
  190. if (TapTimeDelta < 0.1f)
  191. {
  192. score += 100;
  193. combo++;
  194. PerfectCount++;
  195. Debug.Log("Perfect!" + Time.time + " total perfect:" + PerfectCount);
  196. string noteName = soundTrackManager.soundAction.IndexOf(note).ToString();
  197. DestroyNote(noteName);
  198. }
  199. else if (TapTimeDelta < 0.2f)
  200. {
  201. score += 30;
  202. combo = 0;
  203. GoodCount++;
  204. Debug.Log("Good!" + Time.time + " total good:" + GoodCount);
  205. string noteName = soundTrackManager.soundAction.IndexOf(note).ToString();
  206. DestroyNote(noteName);
  207. }
  208. else
  209. {
  210. score += 10;
  211. combo = 0;
  212. PoorCount++;
  213. Debug.Log("Poor!" + Time.time + " total poor:" + PoorCount);
  214. }
  215. }
  216. else
  217. {
  218. combo = 0;
  219. MissCount++;
  220. Debug.Log("Wrong!" + Time.time + " total miss:" + MissCount);
  221. }
  222. Debug.Log("index of note:" + soundTrackManager.soundAction.IndexOf(note));
  223. break;
  224. }
  225. }
  226. }
  227. }
  228. private void DestroyNote(string noteName)
  229. {
  230. GameObject note = GameObject.Find(noteName);
  231. if (note != null)
  232. {
  233. Destroy(note);
  234. }
  235. else
  236. {
  237. Debug.Log("Note not found: " + noteName);
  238. }
  239. }
  240. }
  241. public class TouchResponse
  242. {
  243. public string action = ""; // 触控动作 up, down, left, right, tap, none(表示无效)
  244. public float time;
  245. public TouchResponse(string action, float time)
  246. {
  247. this.action = action;
  248. this.time = time;
  249. }
  250. }