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 = 0; int combo = 0; int PerfectCount = 0; int GoodCount = 0; int PoorCount = 0; int MissCount = 0; int totalCount = 0; // 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); } 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.resource); 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++; 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}"); // 结束游戏 break; // 跳出 while 循环 } 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 = 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++; PerfectCount++; 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++; Debug.Log("Good!" + Time.time + " total good:" + GoodCount); string noteName = soundTrackManager.soundAction.IndexOf(note).ToString(); DestroyNote(noteName); } else { score += 10; combo = 0; PoorCount++; Debug.Log("Poor!" + Time.time + " total poor:" + PoorCount); } } else { combo = 0; MissCount++; 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(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; } }