first commit
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
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<uint, HashSet<int>> m_randomPlayedHistories = new();
|
||||
readonly Dictionary<uint, Queue<int>> m_randomRecentQueues = new();
|
||||
readonly Dictionary<uint, int> m_sequenceNextIndex = new();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Shuffle 随机模式下选取 index(支持 LimitRepetition 队列)
|
||||
/// </summary>
|
||||
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<int> recent))
|
||||
{
|
||||
recent = new Queue<int>();
|
||||
this.m_randomRecentQueues[audioObject.Id] = recent;
|
||||
}
|
||||
|
||||
if (!audioObject.RandomType)
|
||||
{
|
||||
List<int> 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<int> history))
|
||||
{
|
||||
history = new HashSet<int>();
|
||||
this.m_randomPlayedHistories[audioObject.Id] = history;
|
||||
}
|
||||
|
||||
List<int> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 连续容器的 Random 模式下,带 LimitRepetition 逻辑地选取 index
|
||||
/// </summary>
|
||||
public int PickNextRandomIndex(AudioObject audioObject, List<int> remainingPool, Queue<int> recentPlayed, int limitRepetition)
|
||||
{
|
||||
if (!this.m_randomPlayedHistories.TryGetValue(audioObject.Id, out HashSet<int> history))
|
||||
{
|
||||
history = new HashSet<int>();
|
||||
this.m_randomPlayedHistories[audioObject.Id] = history;
|
||||
}
|
||||
|
||||
List<int> candidates = remainingPool
|
||||
.Where(i => !recentPlayed.Contains(i))
|
||||
.ToList();
|
||||
|
||||
if (candidates.Count == 0)
|
||||
candidates = new List<int>(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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取 Sequence 模式下的当前 index 并推进游标
|
||||
/// </summary>
|
||||
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<int> history))
|
||||
{
|
||||
history.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
public int GetHistoryCount(uint id)
|
||||
{
|
||||
return this.m_randomPlayedHistories.TryGetValue(id, out HashSet<int> history) ? history.Count : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user