using System; using System.Collections; using System.Collections.Generic; using System.Linq; using cfg.CampCfg; using Cysharp.Threading.Tasks; using Framework; using Gameplay; using PhxhSDK; using Sirenix.OdinInspector; using UnityEngine; using UnityEngine.Playables; using UnityEngine.Serialization; using UnityEngine.Timeline; using Object = UnityEngine.Object; public class CampShowConfigGroup { public int groupID; public List cfgs = new(); public bool hasToBeFull => cfgs.Any(cfg => cfg.HasCharacter); } public class CampShowPointResult { public int groupID; public int showPoint; public int timelineID; public CharacterDataInfo characterDataInfo; } //抽取流程的记录 [Serializable] public class ExtractRecord { [Serializable] public class SinglePointRecord { [LabelText("演出点ID")] public int showPoint; public int timelineID; public uint characterID; } [LabelText("组ID")] public int groupID; [LabelText("是否抛弃")] public bool isDispose; [LabelText("抛弃原因")] [ShowIf("isDispose")] public EDisposeReason disposeReason; [LabelText("角色记录")] public List records = new(); } //被弃用的原因 public enum EDisposeReason { None, [LabelText("演出点重复")] ShowPointRepeat, [LabelText("组内人数不满")] CharacterCountNotFull, [LabelText("固定角色不存在")] FixedCharacterNotExist, } public class CampSceneManager : MonoBehaviour { private enum ECurrState { None = 0, LoadingCampObjects, LoadingCharacters, Present, } private const string TIMELINE_ASSET_PATH_PREFIX = "Assets/Art/Camp/Timeline/camp_timeline_{0}.playable"; [LabelText("抽取角色数")] [SerializeField] private int _characterMaxCount = 20; [LabelText("timeline导演")] [HideInInspector] [SerializeField] private PlayableDirector _director; [LabelText("角色根节点")] [HideInInspector] [SerializeField] private Transform _characterRoot; [LabelText("营地物品根节点")] [HideInInspector] [SerializeField] private Transform _campObjectsRoot; [LabelText("演出点")] [FormerlySerializedAs("interactionPoints")] [ListDrawerSettings(ShowIndexLabels = true, AlwaysAddDefaultValue = true)] [SerializeField] private List _showPoints = new(); [ReadOnly] [LabelText("抽取记录")] [SerializeField] [ShowIf("@_extractRecords.Count > 0")] private List _extractRecords = new(); private List _allCharacterInfos = new(); private Dictionary _allCampShowConfigGroups = new(); private List _remindCampShowConfigGroups = new(); private List _remindCharacters = new(); private List _campShowPointResults = new(); private TimelineAsset _mainTimeline; public IEnumerable ShowPoints => _showPoints; //asset相关 private Dictionary _models = new(); private Dictionary _timeLines = new(); private HashSet _assetToUnload = new(); public int CharacterMaxCount => Mathf.Min(_allCharacterInfos.Count, _characterMaxCount, _showPoints.Count); private ECurrState _currState = ECurrState.None; private void Awake() { if (!CommonUtils.IsInGame()) return; _currState = ECurrState.None; _mainTimeline = _director.playableAsset as TimelineAsset; LoadData(); } private void OnDestroy() { foreach (var path in _assetToUnload) { AssetManager.Instance.Unload(path); } } public async UniTask Refresh() { await RefreshCampObjects(); await RefreshCampShow(); _currState = ECurrState.Present; } private async UniTask RefreshCampObjects() { _currState = ECurrState.LoadingCampObjects; return; } private async UniTask RefreshCampShow() { _currState = ECurrState.LoadingCharacters; RefreshCampShowData(); CampShowExtract(); await ExecuteCampShowResult(); } private void LoadData() { _allCharacterInfos = new List(CharacterDataInfoManager.Instance.DataList); var campShowConfigList = TableManager.Instance.Tables.CampShowConfig.DataList; _allCampShowConfigGroups.Clear(); foreach (var campShowConfig in campShowConfigList) { var groupID = campShowConfig.GroupID; if (!_allCampShowConfigGroups.TryGetValue(groupID, out var group)) { group = new(); _allCampShowConfigGroups.Add(groupID, group); } group.cfgs.Add(campShowConfig); group.groupID = groupID; } foreach (var group in _allCampShowConfigGroups.Values) { //有指定ID的先选 group.cfgs.Sort((a, b) => b.CharacterID.CompareTo(a.CharacterID)); } } private void RefreshCampShowData() { _remindCampShowConfigGroups.Clear(); _remindCharacters.Clear(); _campShowPointResults.Clear(); _extractRecords.Clear(); _remindCampShowConfigGroups = new(_allCampShowConfigGroups.Values); _remindCharacters = new(_allCharacterInfos); } //抽取 private void CampShowExtract() { List _tempResultList = new(); while (_campShowPointResults.Count < CharacterMaxCount) { var randomGroup = CommonUtils.GetRandomItem(_remindCampShowConfigGroups, true); if (randomGroup == null) { //如果所有组都抽完了,则直接返回 return; } ExtractRecord record = new() { groupID = randomGroup.groupID }; _extractRecords.Add(record); //如果组里的演出点和当前已经使用的演出点重复,则跳过该组 if (IsShowPointExist(randomGroup)) { record.isDispose = true; record.disposeReason = EDisposeReason.ShowPointRepeat; continue; } //如果组里有限定演员,但演员已使用,则跳过该组 if (!CanUseCharacter(randomGroup)) { record.isDispose = true; record.disposeReason = EDisposeReason.FixedCharacterNotExist; continue; } //临时存放抽取的结果 _tempResultList.Clear(); foreach (var cfg in randomGroup.cfgs) { //如果当前没有剩余的演员,则直接跳出抽取 if (_remindCharacters.Count == 0) break; ExtractRecord.SinglePointRecord singlePointRecord = new(); record.records.Add(singlePointRecord); //抽取timeline var timelineID = CommonUtils.GetRandomItem(cfg.TimelineIDs); singlePointRecord.timelineID = timelineID; if (timelineID == 0) //说明不站人 continue; CharacterDataInfo characterDataInfo = null; if (cfg.CharacterID > 0) { //抽取指定演员 var index = _remindCharacters.FindIndex(characterInfo => characterInfo.ID == cfg.CharacterID); characterDataInfo = _remindCharacters[index]; _remindCharacters.RemoveAt(index); } else { //随机抽取演员 characterDataInfo = CommonUtils.GetRandomItem(_remindCharacters, true); } CampShowPointResult result = new() { groupID = randomGroup.groupID, showPoint = cfg.PointID, timelineID = timelineID, characterDataInfo = characterDataInfo }; singlePointRecord.showPoint = cfg.PointID; singlePointRecord.characterID = characterDataInfo.ID; _tempResultList.Add(result); } if (randomGroup.hasToBeFull && _tempResultList.Count < randomGroup.cfgs.Count) { //如果该组必须满员但没满员 //取出来的CharacterDataInfo归还 foreach (var result in _tempResultList) { _remindCharacters.Add(result.characterDataInfo); } record.isDispose = true; record.disposeReason = EDisposeReason.CharacterCountNotFull; } else { //添加到最后的result列表中 _campShowPointResults.AddRange(_tempResultList); } } } //执行抽取结果 private async UniTask ExecuteCampShowResult() { if (_campShowPointResults.Count == 0) return; foreach (var result in _campShowPointResults) { var showPointID = result.showPoint; if (showPointID < 0 || showPointID >= _showPoints.Count || _showPoints[showPointID] == null) { DebugUtil.LogError($"找不到演出点!演出点ID:{showPointID}"); continue; } var showPointTransform = _showPoints[showPointID]; var model = await GetModel(result.characterDataInfo); if (model == null) { continue; } model.transform.position = showPointTransform.transform.position; var timelineAsset = await GetTimeLineAsset(result.timelineID); if (timelineAsset == null) { continue; } var director = model.GetComponent(); if (director == null) { continue; } director.playableAsset = timelineAsset; director.extrapolationMode = DirectorWrapMode.Loop; foreach (var playableBinding in timelineAsset.outputs) { director.SetGenericBinding(playableBinding.sourceObject, model.GetComponentInChildren()); } director.Play(); } } private async UniTask GetModel(CharacterDataInfo characterDataInfo) { if (!_models.TryGetValue(characterDataInfo, out var model)) { string prefabPath = characterDataInfo.PrefabPath; model = await AssetManager.Instance.LoadAssetAsync(prefabPath); if (model == null) { DebugUtil.LogError($"找不到模型, path:{prefabPath}"); return null; } model = Instantiate(model, _characterRoot); model.AddComponent(); model.SetLayerEx(LayerMask.NameToLayer("UIView")); _assetToUnload.Add(prefabPath); _models.Add(characterDataInfo, model); } return model; } private async UniTask GetTimeLineAsset(int timelineID) { if (!_timeLines.TryGetValue(timelineID, out var timelineAsset)) { string timelinePath = string.Format(TIMELINE_ASSET_PATH_PREFIX, timelineID); timelineAsset = await AssetManager.Instance.LoadAssetAsync(timelinePath); if (timelineAsset == null) { DebugUtil.LogError($"找不到TimelineAsset, timelineID:{timelineID} path:{timelinePath}"); } else { _assetToUnload.Add(timelinePath); } _timeLines.Add(timelineID, timelineAsset); } return timelineAsset; } private bool IsShowPointExist(CampShowConfigGroup group) { return group.cfgs.Any(cfg => IsShowPointExist(cfg.PointID)); } private bool IsShowPointExist(int showPointID) { return _campShowPointResults.Any(result => result.showPoint == showPointID); } private bool CanUseCharacter(CampShowConfigGroup group) { return !group.cfgs.Any(cfg => cfg.CharacterID > 0 && !CanUseCharacter(cfg.CharacterID)); } private bool CanUseCharacter(int characterID) { return _remindCharacters.Any(characterInfo => characterInfo.ID == characterID); } public float GetRefreshProcess() { switch (_currState) { case ECurrState.None: return 0; case ECurrState.LoadingCampObjects: return 0.5f; case ECurrState.LoadingCharacters: return 0.5f + (float)_campShowPointResults.Count / CharacterMaxCount * 0.5f; case ECurrState.Present: return 1; default: return 0; } } }