From 7c76f6a3b938adac52d0337259fa5b603da2ec66 Mon Sep 17 00:00:00 2001
From: yyl <yyl>
Date: 星期四, 27 十一月 2025 18:40:00 +0800
Subject: [PATCH] 125 战斗 新增音效

---
 Main/System/Battle/BattleField/BattleField.cs       |   20 ++
 Main/System/Battle/Sound/BattleSoundManager.cs.meta |   11 +
 Main/System/Battle/BattleManager.cs                 |   34 ++++
 Main/System/Battle/Sound.meta                       |    8 +
 Main/System/Battle/BattleEffectMgr.cs               |    2 
 Main/System/Battle/Sound/BattleSoundManager.cs      |  393 +++++++++++++++++++++++++++++++++++++++++++++++++
 Main/Component/UI/Effect/BattleEffectPlayer.cs      |    8 
 7 files changed, 474 insertions(+), 2 deletions(-)

diff --git a/Main/Component/UI/Effect/BattleEffectPlayer.cs b/Main/Component/UI/Effect/BattleEffectPlayer.cs
index 2867856..8539e27 100644
--- a/Main/Component/UI/Effect/BattleEffectPlayer.cs
+++ b/Main/Component/UI/Effect/BattleEffectPlayer.cs
@@ -102,6 +102,8 @@
 
     public GameObjectPoolManager.GameObjectPool pool;
 
+    public BattleField battleField;
+
     public Action onComplete;
 
     private bool isPlaying = false;
@@ -343,6 +345,9 @@
         {
             PlayUnityEffect();
         }
+
+        battleField?.soundManager.PlayEffectSound(effectConfig.audio);
+
         OnAlphaChanged();
     }
 
@@ -518,7 +523,7 @@
 
 
     //  鍒涘缓鍚庣殑鐗规晥浼氳嚜鍔ㄩ殣钘� 闇�瑕佹墜鍔ㄨ皟鐢≒lay鎵嶈兘鎾斁
-    public static BattleEffectPlayer Create(int effectId, Transform parent, bool isRedCamp)
+    public static BattleEffectPlayer Create(int effectId, BattleField _battleField, Transform parent, bool isRedCamp)
     {
         // 鐩存帴鍒涘缓鐗规晥鎾斁鍣紝涓嶄娇鐢ㄥ璞℃睜
         BattleEffectPlayer battleEffectPlayer = null;
@@ -526,6 +531,7 @@
         GameObject newGo = new GameObject("BattleEffectPlayer_" + effectId);
         newGo.transform.SetParent(parent, false);
         battleEffectPlayer = newGo.AddComponent<BattleEffectPlayer>();
+        battleEffectPlayer.battleField = _battleField;
         battleEffectPlayer.rectTrans = newGo.AddMissingComponent<RectTransform>();
         
         battleEffectPlayer.effectId = effectId;
diff --git a/Main/System/Battle/BattleEffectMgr.cs b/Main/System/Battle/BattleEffectMgr.cs
index 038e40a..a93f8a7 100644
--- a/Main/System/Battle/BattleEffectMgr.cs
+++ b/Main/System/Battle/BattleEffectMgr.cs
@@ -84,7 +84,7 @@
 
         bool isRedCamp = camp == BattleCamp.Red;
 
-        BattleEffectPlayer effectPlayer = BattleEffectPlayer.Create(effectId, battleField.battleRootNode.transform, isRedCamp);
+        BattleEffectPlayer effectPlayer = BattleEffectPlayer.Create(effectId, battleField, battleField.battleRootNode.transform, isRedCamp);
         // 璁剧疆鐗规晥缂╂斁鍜屾柟鍚�
 
         effectPlayer.transform.position = parent.position;
diff --git a/Main/System/Battle/BattleField/BattleField.cs b/Main/System/Battle/BattleField/BattleField.cs
index 8a9fe29..122ef9e 100644
--- a/Main/System/Battle/BattleField/BattleField.cs
+++ b/Main/System/Battle/BattleField/BattleField.cs
@@ -13,6 +13,8 @@
 
     public Action<float> OnSpeedRatioChange; // 娣诲姞鎴樻枟閫熷害鍙樺寲鐨勫洖璋�
 
+    public Action<bool> OnFocusChange;
+
     public Action OnBattleRun; // 娣诲姞鎴樻枟杩愯鏃剁殑鍥炶皟
 
     public BattleObjMgr battleObjMgr;
@@ -56,6 +58,8 @@
         }
     }
 
+    protected bool isFocus = false;
+
     public BattleRootNode battleRootNode;
     private BattleMode battleMode;
     public event Action<BattleMode> ChangeBattleModeEvent;
@@ -67,6 +71,8 @@
 
     // 璁板綍姝e湪澶勭悊姝讳骸鐨勮鑹睮D锛岄槻姝㈤噸澶嶅鐞�
     private HashSet<uint> processingDeathObjIds = new HashSet<uint>();
+
+    public BattleSoundManager soundManager;
 
 #if UNITY_EDITOR
     public static Dictionary<string, string> battleHpRecorder = new Dictionary<string, string>();
@@ -85,6 +91,7 @@
         battleEffectMgr = new BattleEffectMgr();
         battleTweenMgr = new BattleTweenMgr();
         recordPlayer = new RecordPlayer();
+        soundManager = new BattleSoundManager(this);
 
         processingDeathObjIds = new HashSet<uint>();
     }
@@ -519,6 +526,8 @@
         //         adjuster.SetParentCanvas(canvas);
         //     }
         // }
+
+        BattleManager.Instance.FocusBattleField(this);
     }
 
     public void StartBattle(Action onMoveComplete)
@@ -730,4 +739,15 @@
 
 
     }
+
+    public void SetFocus(bool v)
+    {
+        isFocus = v;
+        OnFocusChange?.Invoke(isFocus);
+    }
+
+    public bool IsFocus()
+    {
+        return isFocus;
+    }
 }
diff --git a/Main/System/Battle/BattleManager.cs b/Main/System/Battle/BattleManager.cs
index 6257ef5..e98dbec 100644
--- a/Main/System/Battle/BattleManager.cs
+++ b/Main/System/Battle/BattleManager.cs
@@ -681,4 +681,38 @@
         }
     }
 
+    public void FocusBattleField(BattleField battleField)
+    {
+        foreach (var kvp in battleFields)
+        {
+            BattleField bf = kvp.Value;
+            if (bf == null)
+                continue;
+
+            if (bf == battleField)
+            {
+                bf.SetFocus(true);
+            }
+            else
+            {
+                bf.SetFocus(false);
+            }
+        }
+    }
+
+    public BattleField GetFocusBattleField()
+    {
+        foreach (var kvp in battleFields)
+        {
+            BattleField bf = kvp.Value;
+            if (bf == null)
+                continue;
+
+            if (bf.IsFocus())
+            {
+                return bf;
+            }
+        }
+        return null;
+    }
 }
\ No newline at end of file
diff --git a/Main/System/Battle/Sound.meta b/Main/System/Battle/Sound.meta
new file mode 100644
index 0000000..1f891d9
--- /dev/null
+++ b/Main/System/Battle/Sound.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: afe4364444c3cfd4389d3921d2266dec
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Main/System/Battle/Sound/BattleSoundManager.cs b/Main/System/Battle/Sound/BattleSoundManager.cs
new file mode 100644
index 0000000..1483615
--- /dev/null
+++ b/Main/System/Battle/Sound/BattleSoundManager.cs
@@ -0,0 +1,393 @@
+using UnityEngine;
+using System.Collections.Generic;
+
+/// <summary>
+/// 鎴樻枟闊虫晥绠$悊鍣�
+/// 鑱岃矗锛氭挱鏀炬垬鏂椾腑鐨勭煭淇冩妧鑳藉姩浣滃拰鐗规晥闊虫晥
+/// </summary>
+public class BattleSoundManager
+{
+    private BattleField battleField;
+    private GameObject audioSourceObject;
+    
+    // 闊虫晥姹犻厤缃�
+    private const int INITIAL_AUDIO_POOL = 15; // 鍒濆姹犲ぇ灏�
+    private const int MAX_AUDIO_SOURCES = 30; // AudioSource 鎬绘暟涓婇檺
+    private Queue<AudioSource> audioSourcePool = new Queue<AudioSource>();
+    private List<AudioSource> activeAudioSources = new List<AudioSource>();
+    
+    // 鍚屼竴闊虫晥鍚屾椂鎾斁鐨勬渶澶ф暟閲�
+    private const int MAX_SAME_AUDIO_COUNT = 3;
+    // 璁板綍姣忎釜闊虫晥ID褰撳墠鎾斁鐨凙udioSource
+    private Dictionary<int, List<AudioSource>> audioIdToSources = new Dictionary<int, List<AudioSource>>();
+    
+    // 闊抽鍓緫缂撳瓨
+    private Dictionary<int, AudioClip> audioClipCache = new Dictionary<int, AudioClip>();
+    
+    // 褰撳墠鎾斁閫熷害
+    private float currentSpeedRatio = 1f;
+    
+    // 鏄惁鏈夌劍鐐�
+    private bool hasFocus = true;
+    
+    public BattleSoundManager(BattleField _battleField)
+    {
+        battleField = _battleField;
+        InitializeAudioSources();
+        
+        // 鐩戝惉鎴樺満閫熷害鍙樺寲
+        if (battleField != null)
+        {
+            battleField.OnSpeedRatioChange += OnSpeedRatioChanged;
+            battleField.OnFocusChange += OnFocusChanged;
+            currentSpeedRatio = battleField.speedRatio;
+            hasFocus = battleField.IsFocus();
+        }
+    }
+    
+    /// <summary>
+    /// 鍒濆鍖栭煶棰戞簮姹�
+    /// </summary>
+    private void InitializeAudioSources()
+    {
+        // 浣跨敤鎴樺満鏍硅妭鐐逛綔涓� AudioSource 鐨勮浇浣�
+        audioSourceObject = battleField.battleRootNode.gameObject;
+        
+        // 鍒涘缓鍒濆闊抽婧愭睜
+        for (int i = 0; i < INITIAL_AUDIO_POOL; i++)
+        {
+            var source = audioSourceObject.AddComponent<AudioSource>();
+            source.playOnAwake = false;
+            source.loop = false;
+            source.spatialBlend = 0f; // 2D闊虫晥
+            audioSourcePool.Enqueue(source);
+        }
+    }
+    
+    /// <summary>
+    /// 姣忓抚杩愯锛屾竻鐞嗗凡瀹屾垚鐨勯煶棰戞簮
+    /// </summary>
+    public void Run()
+    {
+        // 姣忓抚娓呯悊锛屾�ц兘寮�閿�寰堝皬
+        CleanupFinishedAudioSources();
+    }
+    
+    /// <summary>
+    /// 棰勭儹闊抽锛堟殏鏃朵负绌猴紝鐣欑粰绉诲姩绔紭鍖栵級
+    /// </summary>
+    public void PrewarmAudio(params int[] audioIds)
+    {
+        // TODO: 绉诲姩绔紭鍖栨椂瀹炵幇
+    }
+    
+    /// <summary>
+    /// 棰勭儹闊抽锛堟殏鏃朵负绌猴紝鐣欑粰绉诲姩绔紭鍖栵級
+    /// </summary>
+    public void PrewarmAudio(List<int> audioIds)
+    {
+        // TODO: 绉诲姩绔紭鍖栨椂瀹炵幇
+    }
+    
+    /// <summary>
+    /// 鎾斁鎶�鑳介煶鏁�
+    /// </summary>
+    /// <param name="audioId">闊虫晥ID</param>
+    public void PlaySkillSound(int audioId)
+    {
+        if (audioId <= 0)
+        {
+            return;
+        }
+        
+        PlaySound(audioId);
+    }
+    
+    /// <summary>
+    /// 鎾斁鐗规晥闊虫晥
+    /// </summary>
+    /// <param name="audioId">闊虫晥ID</param>
+    public void PlayEffectSound(int audioId)
+    {
+        if (audioId <= 0)
+        {
+            return;
+        }
+        
+        PlaySound(audioId);
+    }
+    
+    /// <summary>
+    /// 鏍稿績鎾斁鏂规硶
+    /// </summary>
+    private void PlaySound(int audioId)
+    {
+        // 妫�鏌ユ槸鍚︽湁鐒︾偣锛屾棤鐒︾偣鏃朵笉鎾斁
+        if (!hasFocus)
+        {
+            return;
+        }
+        
+        // 妫�鏌ヨ闊虫晥鏄惁宸茶揪鍒版挱鏀句笂闄�
+        if (!CanPlayAudio(audioId))
+        {
+            return;
+        }
+        
+        var audioClip = GetAudioClip(audioId);
+        if (audioClip == null)
+        {
+            return;
+        }
+        
+        // 浠庢睜涓幏鍙栧彲鐢ㄧ殑闊抽婧�
+        AudioSource source = GetAvailableAudioSource();
+        if (source == null)
+        {
+            return;
+        }
+        
+        // 璁剧疆闊抽噺锛堜娇鐢ㄩ煶鏁堥煶閲忚缃級
+        source.volume = SystemSetting.Instance.GetSoundEffect();
+        
+        // 璁剧疆鎾斁閫熷害锛屼娇鐢╬itch鏉ユ帶鍒�
+        // pitch鑼冨洿寤鸿鍦�0.5-2.0涔嬮棿浠ラ伩鍏嶅け鐪�
+        float pitch = Mathf.Clamp(currentSpeedRatio, 0.5f, 2.0f);
+        source.pitch = pitch;
+        
+        // 鎾斁闊虫晥
+        source.PlayOneShot(audioClip);
+        
+        // 鏍囪涓烘椿璺�
+        if (!activeAudioSources.Contains(source))
+        {
+            activeAudioSources.Add(source);
+        }
+        
+        // 璁板綍璇ラ煶鏁圛D涓嶢udioSource鐨勫叧鑱�
+        if (!audioIdToSources.ContainsKey(audioId))
+        {
+            audioIdToSources[audioId] = new List<AudioSource>();
+        }
+        audioIdToSources[audioId].Add(source);
+    }
+    
+    /// <summary>
+    /// 鑾峰彇闊抽鍓緫锛堜紭鍏堜粠缂撳瓨鑾峰彇锛�
+    /// </summary>
+    private AudioClip GetAudioClip(int audioId)
+    {
+        // 鍏堜粠缂撳瓨涓煡鎵�
+        if (audioClipCache.TryGetValue(audioId, out AudioClip cachedClip))
+        {
+            return cachedClip;
+        }
+        
+        // 缂撳瓨涓病鏈夛紝鍒欏姞杞藉苟缂撳瓨
+        var audioClip = LoadAudioClip(audioId);
+        if (audioClip != null)
+        {
+            audioClipCache[audioId] = audioClip;
+        }
+        return audioClip;
+    }
+    
+    /// <summary>
+    /// 妫�鏌ユ槸鍚﹀彲浠ユ挱鏀捐闊虫晥
+    /// </summary>
+    private bool CanPlayAudio(int audioId)
+    {
+        if (!audioIdToSources.ContainsKey(audioId))
+        {
+            return true;
+        }
+        
+        // 杩囨护鎺夊凡缁忓仠姝㈡挱鏀剧殑AudioSource
+        var sources = audioIdToSources[audioId];
+        sources.RemoveAll(s => s == null || !s.isPlaying);
+        
+        // 妫�鏌ュ悓鏃舵挱鏀剧殑鏁伴噺
+        return sources.Count < MAX_SAME_AUDIO_COUNT;
+    }
+    
+    /// <summary>
+    /// 鍔犺浇闊抽鍓緫
+    /// </summary>
+    private AudioClip LoadAudioClip(int audioId)
+    {
+        var config = AudioConfig.Get(audioId);
+        if (config != null)
+        {
+            return AudioLoader.LoadAudio(config.Folder, config.Audio);
+        }
+        return null;
+    }
+    
+    /// <summary>
+    /// 鑾峰彇鍙敤鐨勯煶棰戞簮
+    /// </summary>
+    private AudioSource GetAvailableAudioSource()
+    {
+        // 灏濊瘯浠庢睜涓幏鍙�
+        if (audioSourcePool.Count > 0)
+        {
+            return audioSourcePool.Dequeue();
+        }
+        
+        // 濡傛灉姹犱负绌猴紝妫�鏌ユ槸鍚﹀彲浠ュ姩鎬佸垱寤�
+        if (audioSourceObject == null)
+        {
+            Debug.LogError("BattleSoundManager: audioSourceObject 涓虹┖锛屾棤娉曞垱寤� AudioSource");
+            return null;
+        }
+        
+        // 璁$畻褰撳墠鎬荤殑 AudioSource 鏁伴噺
+        int totalAudioSources = audioSourceObject.GetComponents<AudioSource>().Length;
+        
+        if (totalAudioSources >= MAX_AUDIO_SOURCES)
+        {
+            // 杈惧埌涓婇檺锛屼笉鍐嶅垱寤猴紝涓㈠純杩欐鎾斁璇锋眰
+            Debug.LogWarning($"BattleSoundManager: AudioSource 鏁伴噺宸茶揪涓婇檺 {MAX_AUDIO_SOURCES}锛屾棤娉曟挱鏀炬柊闊虫晥");
+            return null;
+        }
+        
+        // 鍦� battleRootNode 涓婂姩鎬佸垱寤烘柊鐨� AudioSource
+        var source = audioSourceObject.AddComponent<AudioSource>();
+        source.playOnAwake = false;
+        source.loop = false;
+        source.spatialBlend = 0f; // 2D闊虫晥
+        
+        return source;
+    }
+    
+    /// <summary>
+    /// 娓呯悊宸叉挱鏀惧畬鎴愮殑闊抽婧�
+    /// </summary>
+    private void CleanupFinishedAudioSources()
+    {
+        // 娓呯悊 activeAudioSources 鍒楄〃
+        for (int i = activeAudioSources.Count - 1; i >= 0; i--)
+        {
+            var source = activeAudioSources[i];
+            if (source == null || !source.isPlaying)
+            {
+                activeAudioSources.RemoveAt(i);
+                if (source != null)
+                {
+                    audioSourcePool.Enqueue(source);
+                }
+            }
+        }
+        
+        // 娓呯悊 audioIdToSources 涓凡鍋滄鎾斁鐨� AudioSource
+        foreach (var kvp in audioIdToSources)
+        {
+            kvp.Value.RemoveAll(s => s == null || !s.isPlaying);
+        }
+    }
+    
+    /// <summary>
+    /// 鎴樺満閫熷害鍙樺寲鍥炶皟
+    /// </summary>
+    private void OnSpeedRatioChanged(float newSpeedRatio)
+    {
+        currentSpeedRatio = newSpeedRatio;
+        
+        // 鏇存柊鎵�鏈夋鍦ㄦ挱鏀剧殑闊虫晥鐨勯�熷害
+        foreach (var source in activeAudioSources)
+        {
+            if (source != null && source.isPlaying)
+            {
+                float pitch = Mathf.Clamp(newSpeedRatio, 0.5f, 2.0f);
+                source.pitch = pitch;
+            }
+        }
+    }
+    
+    /// <summary>
+    /// 鐒︾偣鍙樺寲鍥炶皟
+    /// </summary>
+    private void OnFocusChanged(bool isFocus)
+    {
+        hasFocus = isFocus;
+        
+        // 澶卞幓鐒︾偣鏃讹紝鍋滄鎵�鏈夋鍦ㄦ挱鏀剧殑闊虫晥
+        if (!hasFocus)
+        {
+            StopAllSounds();
+        }
+    }
+    
+    /// <summary>
+    /// 鍋滄鎵�鏈夐煶鏁�
+    /// </summary>
+    public void StopAllSounds()
+    {
+        foreach (var source in activeAudioSources)
+        {
+            if (source != null && source.isPlaying)
+            {
+                source.Stop();
+            }
+        }
+        
+        // 娓呯悊骞跺洖鏀舵墍鏈夐煶棰戞簮
+        foreach (var source in activeAudioSources)
+        {
+            if (source != null)
+            {
+                audioSourcePool.Enqueue(source);
+            }
+        }
+        activeAudioSources.Clear();
+        audioIdToSources.Clear();
+    }
+    
+    /// <summary>
+    /// 璁剧疆闊抽噺
+    /// </summary>
+    public void SetVolume(float volume)
+    {
+        volume = Mathf.Clamp01(volume);
+        foreach (var source in activeAudioSources)
+        {
+            if (source != null)
+            {
+                source.volume = volume;
+            }
+        }
+    }
+    
+    /// <summary>
+    /// 閲婃斁璧勬簮
+    /// </summary>
+    public void Release()
+    {
+        if (battleField != null)
+        {
+            battleField.OnSpeedRatioChange -= OnSpeedRatioChanged;
+            battleField.OnFocusChange -= OnFocusChanged;
+        }
+        
+        StopAllSounds();
+        
+        // 閿�姣佹墍鏈� AudioSource 缁勪欢
+        if (audioSourceObject != null)
+        {
+            var sources = audioSourceObject.GetComponents<AudioSource>();
+            foreach (var source in sources)
+            {
+                if (source != null)
+                {
+                    GameObject.Destroy(source);
+                }
+            }
+            audioSourceObject = null;
+        }
+        
+        audioSourcePool.Clear();
+        activeAudioSources.Clear();
+        audioIdToSources.Clear();
+        audioClipCache.Clear();
+    }
+}
\ No newline at end of file
diff --git a/Main/System/Battle/Sound/BattleSoundManager.cs.meta b/Main/System/Battle/Sound/BattleSoundManager.cs.meta
new file mode 100644
index 0000000..2ebdc34
--- /dev/null
+++ b/Main/System/Battle/Sound/BattleSoundManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 06f271d8301f0a14aa83f97a1728166a
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

--
Gitblit v1.8.0