using System; using System.Collections.Generic; using FMOD; using FMOD.Studio; using FMODUnity; namespace AibisDream.Kit { internal interface IFmodGlobalParameterApi { RESULT SetParameterByName(string name, float value, bool ignoreSeekSpeed); RESULT GetParameterByName(string name, out float value, out float finalValue); RESULT GetParameterDescriptionByName(string name, out PARAMETER_DESCRIPTION description); RESULT SetParameterById(PARAMETER_ID id, float value, bool ignoreSeekSpeed); } internal sealed class RuntimeFmodGlobalParameterApi : IFmodGlobalParameterApi { public RESULT SetParameterByName(string name, float value, bool ignoreSeekSpeed) { return RuntimeManager.StudioSystem.setParameterByName(name, value, ignoreSeekSpeed); } public RESULT GetParameterByName(string name, out float value, out float finalValue) { return RuntimeManager.StudioSystem.getParameterByName(name, out value, out finalValue); } public RESULT GetParameterDescriptionByName( string name, out PARAMETER_DESCRIPTION description) { return RuntimeManager.StudioSystem.getParameterDescriptionByName(name, out description); } public RESULT SetParameterById(PARAMETER_ID id, float value, bool ignoreSeekSpeed) { return RuntimeManager.StudioSystem.setParameterByID(id, value, ignoreSeekSpeed); } } /// /// 只记录曾被 Yarn set_global_param 成功修改的参数名。 /// 参数值与默认值始终以 FMOD Studio System 为唯一来源。 /// internal sealed class YarnGlobalParameterRegistry { private readonly IFmodGlobalParameterApi _api; private readonly HashSet _trackedNames = new(StringComparer.OrdinalIgnoreCase); internal YarnGlobalParameterRegistry(IFmodGlobalParameterApi api) { _api = api ?? throw new ArgumentNullException(nameof(api)); } internal IReadOnlyCollection TrackedNames => _trackedNames; internal RESULT SetFromYarn(string name, float value) { if (string.IsNullOrWhiteSpace(name)) { return RESULT.ERR_INVALID_PARAM; } var result = _api.SetParameterByName(name, value, ignoreSeekSpeed: false); if (result == RESULT.OK) { _trackedNames.Add(name); } return result; } internal Dictionary CaptureFinalValues(Action warn) { var values = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var name in _trackedNames) { var result = _api.GetParameterByName(name, out _, out var finalValue); if (result == RESULT.OK) { values[name] = finalValue; continue; } warn?.Invoke( $"[AudioManager] 捕获 Yarn 全局参数 {name} 失败:{result}。本次存档跳过该参数。"); } return values; } internal void ResetToDefaults(Action warn) { var names = new List(_trackedNames); foreach (var name in names) { var descriptionResult = _api.GetParameterDescriptionByName(name, out var description); if (descriptionResult != RESULT.OK) { warn?.Invoke( $"[AudioManager] 查询 Yarn 全局参数 {name} 默认值失败:{descriptionResult}。保留追踪以便稍后重试。"); continue; } var resetResult = _api.SetParameterById( description.id, description.defaultvalue, ignoreSeekSpeed: true); if (resetResult != RESULT.OK) { warn?.Invoke( $"[AudioManager] 重置 Yarn 全局参数 {name} 失败:{resetResult}。保留追踪以便稍后重试。"); continue; } _trackedNames.Remove(name); } } internal void RestoreFinalValues( IReadOnlyDictionary values, Action warn) { if (values == null) { return; } foreach (var pair in values) { var result = _api.SetParameterByName( pair.Key, pair.Value, ignoreSeekSpeed: true); if (result == RESULT.OK) { _trackedNames.Add(pair.Key); continue; } warn?.Invoke( $"[AudioManager] 恢复 Yarn 全局参数 {pair.Key} 失败:{result}。跳过该参数。"); } } } }