using System.Collections.Generic; using System.Linq; using UnityEngine; namespace OCES.Audio { public class AudioContainerSelector { // TODO: 根据 audioObject.ContainerScope 控制随机历史的共享范围: // - Global(当前实现):同一 AudioObject 的所有并发实例共享同一份 playHistory 和 limitRepetition 队列, // 不放回随机在全局层面生效,并发实例之间互相感知已播放记录。 // - PerInstance:每个并发播放实例持有独立的 playHistory 和 limitRepetition 队列, // 不放回随机仅在该实例内生效,实例之间完全隔离。 // 实现思路:PerInstance 模式下将 HashSet/Queue 作为局部变量传入协程,不再写入这两个 Dictionary。 readonly Dictionary> m_randomPlayedHistories = new(); readonly Dictionary> m_randomRecentQueues = new(); readonly Dictionary m_sequenceNextIndex = new(); /// /// Shuffle 随机模式下选取 index(支持 LimitRepetition 队列) /// public int PickShuffleIndex(AudioObject audioObject) { int count = audioObject.Name.Count; // LimitRepetition 队列 int limitRepetition = Mathf.Clamp(audioObject.LimitRepetition, 0, count - 1); if (!this.m_randomRecentQueues.TryGetValue(audioObject.Id, out Queue recent)) { recent = new Queue(); this.m_randomRecentQueues[audioObject.Id] = recent; } if (!audioObject.RandomType) { List candidates = Enumerable.Range(0, count) .Where(i => !recent.Contains(i)) .ToList(); if (candidates.Count == 0) candidates = Enumerable.Range(0, count).ToList(); int chosen = candidates[Random.Range(0, candidates.Count)]; if (limitRepetition > 0) { recent.Enqueue(chosen); if (recent.Count > limitRepetition) recent.Dequeue(); } return chosen; } // 不放回随机 if (!this.m_randomPlayedHistories.TryGetValue(audioObject.Id, out HashSet history)) { history = new HashSet(); this.m_randomPlayedHistories[audioObject.Id] = history; } List available = Enumerable.Range(0, count) .Where(i => !history.Contains(i) && !recent.Contains(i)) .ToList(); if (available.Count == 0) { history.Clear(); available = Enumerable.Range(0, count) .Where(i => !recent.Contains(i)) .ToList(); if (available.Count == 0) available = Enumerable.Range(0, count).ToList(); } int chosenIndex = available[Random.Range(0, available.Count)]; history.Add(chosenIndex); if (limitRepetition > 0) { recent.Enqueue(chosenIndex); if (recent.Count > limitRepetition) recent.Dequeue(); } return chosenIndex; } /// /// 连续容器的 Random 模式下,带 LimitRepetition 逻辑地选取 index /// public int PickNextRandomIndex(AudioObject audioObject, List remainingPool, Queue recentPlayed, int limitRepetition) { if (!this.m_randomPlayedHistories.TryGetValue(audioObject.Id, out HashSet history)) { history = new HashSet(); this.m_randomPlayedHistories[audioObject.Id] = history; } List candidates = remainingPool .Where(i => !recentPlayed.Contains(i)) .ToList(); if (candidates.Count == 0) candidates = new List(remainingPool); int index = candidates[Random.Range(0, candidates.Count)]; if (audioObject.RandomType) remainingPool.Remove(index); if (limitRepetition > 0) recentPlayed.Enqueue(index); if (recentPlayed.Count > limitRepetition) recentPlayed.Dequeue(); history.Add(index); return index; } /// /// 获取 Sequence 模式下的当前 index 并推进游标 /// public int GetNextSequenceIndex(AudioObject audioObject) { int current = this.m_sequenceNextIndex.GetValueOrDefault(audioObject.Id, 0); this.m_sequenceNextIndex[audioObject.Id] = (current + 1) % audioObject.Name.Count; return current; } public void ResetHistory(uint id) { if (this.m_randomPlayedHistories.TryGetValue(id, out HashSet history)) { history.Clear(); } } public int GetHistoryCount(uint id) { return this.m_randomPlayedHistories.TryGetValue(id, out HashSet history) ? history.Count : 0; } } }