using System.Collections; using UnityEngine; using UnityEngine.InputSystem; /* * WalkDogs 场景挂载在2D Player上 * SoundGameController.cs * Author: 2023-10-12 * Description: 音乐游戏控制器 * 1. 读取音轨数据 * 2. 创建音符 * 3. 检测音符点击 */ public class SoundGameController : MonoBehaviour { SoundTrackManager soundTrackManager = new(); GameObject notePrefabSwipe, notePrefabTap; float gameStartTime; float GameEndTime; // 游戏得分统计 int score, combo, maxCombo, NGCombo, perfectCount, goodCount, poorCount, missCount, totalCount; // TapOrSwipe 触控检测 Vector2 pressDownPosition = new Vector2(0, 0); Vector2 pressUpPosition = new Vector2(0, 0); float pressDownTime = 0f; float pressUpTime = 0f; // Start is called once before the first execution of Update after the MonoBehaviour is created void Start() { TextAsset soundTrackJson = Resources.Load("WalkDogs/SoundTrack/track_1"); if (soundTrackJson != null) { string soundTrackString = soundTrackJson.text; soundTrackManager.ImportFromJson(soundTrackString); soundTrackManager.GenRandomActions(); // soundTrackManager.speed = 4; } notePrefabSwipe = Resources.Load("WalkDogs/Note/NoteSwipe"); notePrefabTap = Resources.Load("WalkDogs/Note/NoteTap"); StartCoroutine(PlaySoundGame()); } // Update is called once per frame void Update() { } IEnumerator PlaySoundGame() { // 加载音乐 AudioClip audioClip = Resources.Load(soundTrackManager.soundTrack); AudioSource audioSource = gameObject.AddComponent(); audioSource.clip = audioClip; audioSource.Play(); gameStartTime = Time.time; GameEndTime = gameStartTime + soundTrackManager.length; while (Time.time < GameEndTime) { // 用于提前n秒创建音符 foreach (var note in soundTrackManager.soundAction) { if (!note.isActive && Time.time >= (note.time - GameData.noteStartLeadTime)) { note.isActive = true; CreateNote(note.time, note.action, soundTrackManager.soundAction.IndexOf(note).ToString()); continue; } } // 检测音符是否超时未被点击 foreach (var note in soundTrackManager.soundAction) { float TapTimeDeadline = note.time + gameStartTime + 0.35f; // 计算点击事件超时 if (note.isActive && !note.isTapped && Time.time > TapTimeDeadline) { note.isTapped = true; missCount++; NGCombo++; Debug.Log("Miss!" + Time.time + " Tap time deadline: " + TapTimeDeadline + " total miss:" + missCount); Debug.Log("index of note:" + soundTrackManager.soundAction.IndexOf(note)); break; } } // 检测游戏是否结束并统计分数 totalCount = perfectCount + goodCount + poorCount + missCount; if (totalCount == soundTrackManager.soundAction.Count) { GameEndTime = Time.time; Debug.Log("Game Over!"); Debug.Log($"Perfect: {perfectCount}, Good: {goodCount}, Poor: {poorCount}, Miss: {missCount}"); Debug.Log($"Total Count: {totalCount}"); Debug.Log($"Score: {score}"); Debug.Log($"Combo: {combo}"); Debug.Log($"Max Combo: {maxCombo}"); // 结束游戏 break; // 跳出 while 循环 } // 当NGCombo大于5时,播放音效提示 if (NGCombo > 5) { SoundGameEffectController.Instance.PlaySoundEffect(0); NGCombo = 0; } yield return new WaitForSeconds(0.01f); } yield return null; } private void CreateNote(float hitTime, string action, string noteName) { GameObject note; if (action == "tap") { note = Instantiate(notePrefabTap); } else { note = Instantiate(notePrefabSwipe); } note.gameObject.name = "note_" + noteName; NoteController noteController = note.GetComponent(); noteController.speed = soundTrackManager.speed; noteController.hitTime = hitTime; noteController.action = action; } // 触控检测,检测通过按下和和抬起的触控点位置计算点击或者上下左右滑动 public void TapOrSwipe(InputAction.CallbackContext context) { if (context.phase == InputActionPhase.Started) { pressDownPosition = Pointer.current.position.ReadValue(); pressDownTime = Time.time; return; } if (context.phase == InputActionPhase.Canceled) { pressUpPosition = Pointer.current.position.ReadValue(); pressUpTime = Time.time; float deltaX = pressUpPosition.x - pressDownPosition.x; float deltaY = pressUpPosition.y - pressDownPosition.y; float distance = Mathf.Sqrt(deltaX * deltaX + deltaY * deltaY); // Debug.Log($"deltaX: {deltaX}, deltaY: {deltaY}, distance: {distance}"); float timeDelta = pressUpTime - pressDownTime; string action; if (timeDelta > 0.2f) { return; } else { if (distance < 20f) { action = "tap"; } else { if (Mathf.Abs(deltaX) > Mathf.Abs(deltaY)) { if (deltaX > 0) { action = "right"; } else { action = "left"; } } else { if (deltaY > 0) { action = "up"; } else { action = "down"; } } } } TouchResponse touchResponse = new TouchResponse(action, pressUpTime); // Debug.Log($"TouchResponse: {touchResponse.action}, {touchResponse.time}"); // 检测点击的音符 ScoreCheck(touchResponse); } return; } private void ScoreCheck(TouchResponse touchResponse) { if (touchResponse != null) { foreach (var note in soundTrackManager.soundAction) { float TapTimeDelta = Mathf.Abs(touchResponse.time - (note.time + gameStartTime)); // 计算点击时间和音符时间的差值 if (note.isActive && !note.isTapped && TapTimeDelta < 0.3f) { note.isTapped = true; if (touchResponse.action == note.action) { if (TapTimeDelta < 0.1f) { score += 100; combo++; if (combo > maxCombo) { maxCombo = combo; } perfectCount++; NGCombo = 0; Debug.Log("Perfect!" + Time.time + " total perfect:" + perfectCount); string noteName = soundTrackManager.soundAction.IndexOf(note).ToString(); DestroyNote(noteName); } else if (TapTimeDelta < 0.2f) { score += 30; combo = 0; goodCount++; NGCombo = 0; Debug.Log("Good!" + Time.time + " total good:" + goodCount); string noteName = soundTrackManager.soundAction.IndexOf(note).ToString(); DestroyNote(noteName); } else { score += 10; combo = 0; poorCount++; NGCombo++; Debug.Log("Poor!" + Time.time + " total poor:" + poorCount); } } else { combo = 0; missCount++; NGCombo++; Debug.Log("Wrong!" + Time.time + " total miss:" + missCount); } Debug.Log("index of note:" + soundTrackManager.soundAction.IndexOf(note)); break; } } } } private void DestroyNote(string noteName) { GameObject note = GameObject.Find("note_" + noteName); if (note != null) { Destroy(note); } else { Debug.Log("Note not found: " + noteName); } } } public class TouchResponse { public string action = ""; // 触控动作 up, down, left, right, tap, none(表示无效) public float time; public TouchResponse(string action, float time) { this.action = action; this.time = time; } }