using System; using System.Collections; using System.Collections.Generic; 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; GameObject Leaves; float gameStartTime; float GameEndTime; // 游戏得分统计 int score, combo, maxCombo, NGCombo, perfectCount, goodCount, poorCount, missCount, totalCount; // 游戏数据 string currentDogState = "walking"; // 是否有狗在跑 // 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()); StartCoroutine(DogComponentInstaller()); Leaves = GameObject.Find("Leaves_orange"); StartCoroutine(DogMovementController()); } // Update is called once per frame void Update() { DogStatusUpdate(); UpdateAndShowScoreUI(); } IEnumerator DogComponentInstaller() { yield return null; // 找到所有标签为 "dog" 的 GameObject GameObject[] dogObjects = GameObject.FindGameObjectsWithTag("dog"); foreach (GameObject dog in dogObjects) { // 加载指定的Animator controller Animator animator = dog.GetComponent(); DogProperty dogProperty = new DogProperty(); foreach (var dogProp in UserProperty.dogs) { if (dogProp.dog_name == dog.name) { dogProperty = dogProp; break; } } RuntimeAnimatorController animatorController = Resources.Load("WalkDogs/Animation/shibaInu"); if (dogProperty.breed == "shibaInu") { animatorController = Resources.Load("WalkDogs/Animation/shibaInu"); } animator.runtimeAnimatorController = animatorController; // 加载Rigidbody Rigidbody rigidbody = dog.AddComponent(); //rigidbody.isKinematic = true; rigidbody.mass = 10; rigidbody.linearDamping = 10; rigidbody.angularDamping = 10; //rigidbody.freezeRotation = true; rigidbody.constraints = RigidbodyConstraints.FreezePositionY | RigidbodyConstraints.FreezeRotation; rigidbody.interpolation = RigidbodyInterpolation.Interpolate; rigidbody.collisionDetectionMode = CollisionDetectionMode.ContinuousSpeculative; // 加载box collider BoxCollider boxCollider = dog.AddComponent(); boxCollider.isTrigger = false; boxCollider.center = new Vector3(0, 0.25f, 0); boxCollider.size = new Vector3(0.2f, 0.5f, 0.6f); } } IEnumerator PlaySoundGame() { // 加载背景 List backgroundList = new List { "WalkDogs/Background/bg_1", "WalkDogs/Background/bg_2", "WalkDogs/Background/bg_3", "WalkDogs/Background/bg_4", }; var Background = GameObject.Find("Background"); MeshRenderer backgroundMeshRenderer = Background.GetComponent(); float lastBackgroundTime; // 加载音乐 AudioClip audioClip = Resources.Load(soundTrackManager.soundTrack); AudioSource audioSource = gameObject.AddComponent(); audioSource.clip = audioClip; yield return new WaitForSeconds(2f); // 等待音频加载完成 audioSource.Play(); gameStartTime = Time.time; GameEndTime = gameStartTime + soundTrackManager.length; lastBackgroundTime = gameStartTime; while (Time.time < GameEndTime) { // 每隔15秒切换一次背景 if (Time.time - lastBackgroundTime > 15f) { lastBackgroundTime = Time.time; int randomIndex = UnityEngine.Random.Range(0, backgroundList.Count); string backgroundPath = backgroundList[randomIndex]; Material myMaterial = Resources.Load(backgroundPath); if (myMaterial != null) { backgroundMeshRenderer.material = myMaterial; // Debug.Log("Background changed to: " + backgroundPath); } else { Debug.LogWarning("Background material not found: " + backgroundPath); } } // 用于提前n秒创建音符 foreach (var note in soundTrackManager.soundAction) { if (!note.isActive && Time.time >= (gameStartTime + 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++; combo = 0; // 重置连击 // 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}"); // 结束游戏 UpdateAndShowScoreUI(); 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); // Debug.Log("Create Tap Note: " + noteName); // Debug.Log("Hit Time: " + hitTime + ", Action: " + action); // Debug.Log("Create Time: " + Time.time); } else { note = Instantiate(notePrefabSwipe); // Debug.Log("Create Tap Note: " + noteName); // Debug.Log("Hit Time: " + hitTime + ", Action: " + action); // Debug.Log("Create Time: " + Time.time); } 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); } } // 设置狗的状态 private void DogStatusUpdate() { GameObject[] dogObjects = GameObject.FindGameObjectsWithTag("dog"); Debug.Log("combo:" + combo); // combo = 5; // 这里是为了测试用,实际应该从游戏逻辑中获取当前连击数 if (combo >= 5 && combo < 10) { currentDogState = "running"; foreach (GameObject dog in dogObjects) { Animator animator = dog.GetComponent(); if (animator != null) { animator.SetTrigger("run"); SetLeavesParticleSpeed(5.0f, 10.0f); // 设置leaves粒子效果的Start Speed 和 Rate over Time // animator.Play("Run"); } } } else if (combo >= 10) { currentDogState = "fastRunning"; foreach (GameObject dog in dogObjects) { Animator animator = dog.GetComponent(); if (animator != null) { animator.SetTrigger("fastRun"); SetLeavesParticleSpeed(10.0f, 20.0f); // 设置leaves粒子效果的Start Speed 和 Rate over Time // animator.Play("Fast Run"); } } } else if (combo == 0) { if (currentDogState == "running" || currentDogState == "fastRunning") { currentDogState = "walking"; foreach (GameObject dog in dogObjects) { Animator animator = dog.GetComponent(); if (animator != null) { // animator.Play("Walk"); animator.SetTrigger("walk"); SetLeavesParticleSpeed(2.0f, 5.0f); // 设置leaves粒子效果的Start Speed 和 Rate over Time } } } } } // 设置leaves粒子效果的Start Speed 和 Rate over Time public void SetLeavesParticleSpeed(float speed, float rate) { if (Leaves != null) { ParticleSystem particleSystem = Leaves.GetComponent(); if (particleSystem != null) { var main = particleSystem.main; main.startSpeed = speed; var emission = particleSystem.emission; emission.rateOverTime = rate; } else { Debug.LogWarning("ParticleSystem not found on Leaves object."); } } else { Debug.LogWarning("Leaves object not found."); } } // 协程控制场景里面所有的狗静止或者在x:-0.5到0.5之间, z:-6到-7之间随机移动 IEnumerator DogMovementController() { while (true) { GameObject[] dogObjects = GameObject.FindGameObjectsWithTag("dog"); foreach (GameObject dog in dogObjects) { // 让狗在x:-0.5到0.5之间, z:-6到-7之间随机移动 float randomX = UnityEngine.Random.Range(-0.5f, 0.5f); float randomZ = UnityEngine.Random.Range(-7f, -6f); // dog.transform.Translate(new Vector3(randomX, -0.5f, randomZ) * Time.deltaTime); var position = new Vector3(randomX, dog.transform.position.y, randomZ); Debug.Log($"Dog {dog.name} moving to position: {position}"); StartCoroutine(DogMoveToPosition(dog, position, 0.5f)); } yield return new WaitForSeconds(5f); // 每5秒更新一次位置 } } // 协程控制场景里面的狗按照指定速度移动到指定的位置 IEnumerator DogMoveToPosition(GameObject dog, Vector3 targetPosition, float speed = 0.5f) { while (Vector3.Distance(dog.transform.position, targetPosition) > 0.1f) { dog.transform.position = Vector3.MoveTowards(dog.transform.position, targetPosition, speed * Time.deltaTime); yield return null; } } private void UpdateAndShowScoreUI() { // 更新分数显示逻辑 // 这里可以添加代码来更新UI上的分数显示 // 例如:scoreLabel.text = "Score: " + score; var UI = GameObject.Find("UI"); var scorePanel = UI.transform.Find("ScorePanel").gameObject; scorePanel.SetActive(true); var walkDogsScoreController = scorePanel.transform.Find("UIDocument").GetComponent(); walkDogsScoreController.score = score; walkDogsScoreController.maxCombo = maxCombo; walkDogsScoreController.perfect = perfectCount; walkDogsScoreController.good = goodCount; walkDogsScoreController.poor = poorCount; walkDogsScoreController.miss = missCount; } } 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; } }