SoundGameController.cs 12 KB

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