using cfg.ShowCfg; using Cysharp.Threading.Tasks; using Framework; using Gameplay; using PhxhSDK; using Sirenix.OdinInspector; using Sirenix.Utilities; using SRF; using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.Playables; using UnityEngine.Timeline; using static ShowComponent; using Object = UnityEngine.Object; /// /// 演出组件 /// public sealed class ShowComponent : MonoBehaviour, IShow { /// /// 当前状态 /// private enum E_CurState { None = 0, /// /// 加载角色 /// LoadingCharacters, /// /// 当前 /// Present, } /// /// 被弃用的原因 /// public enum E_DisposeReason { None, /// /// 演出点重复 /// [LabelText("演出点重复")] ShowPointRepeat, /// /// 组内人数不满 /// [LabelText("组内人数不满")] CharacterCountNotFull, /// /// 固定角色不存在 /// [LabelText("固定角色不存在")] FixedCharacterNotExist, } #region 外部序列化字段 /// /// 演出场景类型 /// [LabelText("演出场景类型")] [SerializeField] private E_ShowSceneType showSceneType; /// /// 角色根节点 /// [LabelText("角色根节点")] [SerializeField] private Transform tsCharacterRoot; /// /// 收集演出点根节点 /// [LabelText("收集演出点的根节点")] [SerializeField] private Transform tsCollectRoot; /// /// 角色演出点根节点 /// [LabelText("角色演出点根节点")] [SerializeField] private Transform tsCharacterShowPointRoot; /// /// 道具演出点根节点 /// [LabelText("道具演出点根节点")] [SerializeField] private Transform tsItemShowPointRoot; /// /// 最大抽取角色数量 /// [LabelText("最大抽取角色数")] [SerializeField] private int characterMaxCount = 20; /// /// 是否忽略看板娘 /// [LabelText("是否忽略看板娘")] [SerializeField] private bool IsIgnoreKanbanban; /// /// 抽取记录 /// [ReadOnly] [LabelText("抽取记录")] [SerializeField] [ShowIf("@listExtractRecord.Count > 0")] private List listExtractRecord = new(); #endregion /// /// 当前场景所有演出点 /// private List listShowPoint; /// /// 当前场景所有演出组字典 key:组id value:演出组数据 /// private readonly Dictionary dicShowGroup = new(); /// /// 最终的演出点数据 /// private readonly List listShowPointResult = new(); /// /// Timeline资源字典 /// private readonly Dictionary dicTimeLineAsset = new(); /// /// 已经加载的资源集合 /// private readonly HashSet setAssetToUnload = new(); /// /// 角色模型字典 /// private readonly Dictionary dicCharacterModel = new(); /// /// 当前状态 /// private E_CurState currState = E_CurState.None; #region (刷新数据,抽取)过程临时变量 /// /// 可以抽取的演出组数据(排序用) /// private readonly List listRemindShowGroup = new(); /// /// 可以抽取演出的角色数据(排序用) /// private readonly List listRemindCharacter = new(); /// /// 随机抽取的演出组链表 /// private LinkedList linkShowGroup; /// /// 随机抽取的角色链表 /// private LinkedList linkCharacter; /// /// 已经抽取的演出点索引集合 /// private readonly HashSet setUseShowPointIndex = new(); /// /// 已经抽取的角色id /// private readonly HashSet setUseCharactrtId = new(); #endregion /// /// 可以抽取的角色最大数量 /// public int CharacterMaxCount { get { return Mathf.Min(listRemindCharacter.Count, characterMaxCount, listShowPoint.Count); } } public E_ShowSceneType ShowSceneTyp { get { return showSceneType; } } #if UNITY_EDITOR /// /// 演出点根节点 /// public Transform TsShowPointRoot { get { return tsCharacterShowPointRoot; } } #endif private void Awake() { if (!CommonUtils.IsInGame()) return; ShowManager.Instance.Add(this); currState = E_CurState.None; #region 初始化数据 #region 获取演出点 if (tsCharacterShowPointRoot == null) listShowPoint = new(); else { listShowPoint = new(tsCharacterShowPointRoot.childCount); for (int i = 0; i < tsCharacterShowPointRoot.childCount; ++i) { listShowPoint.Add(tsCharacterShowPointRoot.GetChild(i)); } } #endregion #region 获取演出组配置 List listShowConfig = TableManager.Instance.Tables.ShowConfig.DataList; foreach (DataShowConfig showConfig in listShowConfig) { if (showSceneType != showConfig.ShowSceneType) continue; int groupID = showConfig.GroupID; if (!dicShowGroup.TryGetValue(groupID, out ShowGroupData group)) { group = new(groupID); dicShowGroup.Add(groupID, group); } group.Add(showConfig); } foreach (ShowGroupData group in dicShowGroup.Values) { group.Shuffle(); } #endregion #endregion } private void OnDestroy() { foreach (string path in setAssetToUnload) { AssetManager.Instance.Unload(path); } } /// /// 刷新演出 /// public async UniTask RefreshShow() { currState = E_CurState.LoadingCharacters; RefreshShowData(); ExtractShowCharacter(); await ExecuteShowResult(); currState = E_CurState.Present; } /// /// 刷新演出数据 /// private void RefreshShowData() { listRemindShowGroup.Clear(); listRemindCharacter.Clear(); listRemindShowGroup.AddRange(dicShowGroup.Values); #region 获取看板娘id int kanbanbanId = 0; if (IsIgnoreKanbanban) { MainPanelSceneStoreData storeData = StorageMgr.Instance.GetStorage("MainPanelSceneStoreData"); if (storeData == null) DebugUtil.LogError("没有看板娘数据"); else kanbanbanId = storeData.PosterID; } #endregion foreach (CharacterDataInfo characterDataInfo in CharacterDataInfoManager.Instance.DataList) { if (IsIgnoreKanbanban && characterDataInfo.Cfg.RoleID == kanbanbanId) continue; // TODO 看板娘不加进可以演出的抽取列表 listRemindCharacter.Add(characterDataInfo); } CommonUtils.Shuffle(listRemindShowGroup); CommonUtils.Shuffle(listRemindCharacter); linkShowGroup = new(listRemindShowGroup); linkCharacter = new(listRemindCharacter); #region 隐藏之前的角色模型 foreach (GameObject go in dicCharacterModel.Values) { go.SetActive(false); } #endregion } /// /// 抽取演出角色 /// private void ExtractShowCharacter() { listExtractRecord.Clear(); listShowPointResult.Clear(); List listTempResult = new(); // 临时存放抽取的结果 while (linkShowGroup.First != null) { ShowGroupData groupData = linkShowGroup.First.Value; linkShowGroup.RemoveFirst(); // 如果组里的演出点和当前已经使用的演出点重复,则踢出该组 if (IsShowPointExist(groupData)) { listExtractRecord.Add(new ExtractRecord(groupData.GroupID, E_DisposeReason.ShowPointRepeat)); continue; } // 如果是固定角色的组的角色已经被使用,则踢出该组 if (!IsCanUseFixCharacter(groupData)) { listExtractRecord.Add(new ExtractRecord(groupData.GroupID, E_DisposeReason.FixedCharacterNotExist)); continue; } listTempResult.Clear(); ExtractRecord extractRecord = new(groupData.GroupID, E_DisposeReason.None); foreach (DataShowConfig dataShowConfig in groupData.ListConfig) { // 如果当前没有剩余的演员,则直接跳出抽取 if (linkCharacter.First == null) break; // 抽取timeline string timelineName = CommonUtils.GetRandomItem(dataShowConfig.TimelineNames); ExtractRecord.SinglePointRecord singlePointRecord = new() { timelineName = timelineName }; extractRecord.Add(singlePointRecord); if (timelineName == "0" || timelineName.IsNullOrWhitespace()) // 说明不站人 continue; #region 角色抽取 CharacterDataInfo characterDataInfo = null; if (ValueValidator.IsIdValid(dataShowConfig.CharacterID)) // 抽取指定演员 { foreach (CharacterDataInfo character in linkCharacter) { if ((int)character.ID == dataShowConfig.CharacterID) { characterDataInfo = character; break; } } if (characterDataInfo == null) continue; else linkCharacter.Remove(characterDataInfo); } else { characterDataInfo = linkCharacter.First.Value; linkCharacter.RemoveFirst(); } #endregion ShowPointResult result = new(groupData.GroupID, dataShowConfig.PointID, timelineName, characterDataInfo); singlePointRecord.showPoint = dataShowConfig.PointID; singlePointRecord.characterID = characterDataInfo.ID; listTempResult.Add(result); } if (groupData.IsMustHasCharacter && listTempResult.Count < groupData.ListConfig.Count) { // 如果该组必须满员但没满员 // 取出来的CharacterDataInfo归还 foreach (ShowPointResult result in listTempResult) { listRemindCharacter.Add(result.CharacterData); } listExtractRecord.Add(new ExtractRecord(groupData.GroupID, E_DisposeReason.CharacterCountNotFull)); } else // 添加到最后的result列表中 { foreach (ShowPointResult result in listTempResult) { listShowPointResult.Add(result); setUseCharactrtId.Add((int)result.CharacterData.ID); setUseShowPointIndex.Add(result.ShowPointIndex); } } } setUseShowPointIndex.Clear(); setUseCharactrtId.Clear(); } /// /// 执行演出结果 /// private async UniTask ExecuteShowResult() { if (listShowPointResult.Count == 0) return; foreach (ShowPointResult result in listShowPointResult) { int showPointIndex = result.ShowPointIndex; if (showPointIndex < 0 || showPointIndex >= listShowPoint.Count || listShowPoint[showPointIndex] == null) { DebugUtil.LogError($"找不到演出点!演出点索引:{showPointIndex}"); continue; } Transform tsShowPoint = listShowPoint[showPointIndex].transform; GameObject goModel = await GetModel(result.CharacterData); if (goModel == null) continue; goModel.SetActive(true); goModel.transform.SetPositionAndRotation(tsShowPoint.position, tsShowPoint.rotation); TimelineAsset timelineAsset = await GetTimeLineAsset(result.TimelineName); if (timelineAsset == null) continue; if (!goModel.TryGetComponent(out PlayableDirector director)) { DebugUtil.LogError($"{goModel.name}未绑定PlayableDirector组件"); continue; } director.playableAsset = timelineAsset; director.extrapolationMode = DirectorWrapMode.Loop; foreach (PlayableBinding playableBinding in timelineAsset.outputs) { director.SetGenericBinding(playableBinding.sourceObject, goModel.GetComponentInChildren()); } director.Play(); } } /// /// 异步加载资源 /// /// /// /// private async UniTask LoadAssetAsync(string path) where T : Object { T res = await AssetManager.Instance.LoadAssetAsync(path); if (res != null) setAssetToUnload.Add(path); return res; } /// /// 获取角色模型 /// /// /// private async UniTask GetModel(CharacterDataInfo characterDataInfo) { if (!dicCharacterModel.TryGetValue(characterDataInfo.Cfg.RoleID, out GameObject goModel)) { string prefabPath = characterDataInfo.PrefabPath; goModel = await LoadAssetAsync(prefabPath); if (goModel == null) { DebugUtil.LogError($"找不到模型, path:{prefabPath}"); return null; } goModel = Instantiate(goModel, tsCharacterRoot); goModel.GetComponentOrAdd(); goModel.SetLayerEx(Framework.Constants.Layer.UIVIEW); dicCharacterModel.Add(characterDataInfo.Cfg.RoleID, goModel); } HideCharacterWeapon(goModel); return goModel; } /// /// 隐藏角色武器 /// private void HideCharacterWeapon(GameObject goCharacter) { if (goCharacter.transform.childCount < 1) { DebugUtil.LogError("角色节点错误"); return; } // TODO 临时隐藏武器方式 Transform tsRoot = goCharacter.transform.GetChild(0).Find("Root"); Transform tsWeaponRoot = tsRoot.Find("Bip001/Bip001 Spine/Bip001 Spine1/Bip001 R Clavicle/Bip001 R UpperArm/Bip001 R Forearm/Bip001 R Hand/Grip_point01"); if (tsWeaponRoot != null) tsWeaponRoot.gameObject.SetActive(false); } /// /// 获取Timeline资源 /// /// /// private async UniTask GetTimeLineAsset(string timelineName) { if (!dicTimeLineAsset.TryGetValue(timelineName, out TimelineAsset timelineAsset)) { string timelinePath = CampTimeLineCollector.Instance[showSceneType, timelineName]; if (string.IsNullOrWhiteSpace(timelinePath)) { DebugUtil.LogError($"找不到timelinePath,{nameof(timelineName)}:{timelineName}"); return null; } timelineAsset = await LoadAssetAsync(timelinePath); if (timelineAsset == null) { DebugUtil.LogError($"找不到TimelineAsset, {nameof(timelineName)}:{timelineName} path:{timelinePath}"); return null; } dicTimeLineAsset.Add(timelineName, timelineAsset); } return timelineAsset; } /// /// 演出点是否重复 /// /// /// private bool IsShowPointExist(ShowGroupData group) { foreach (DataShowConfig showConfig in group.ListConfig) { if (setUseShowPointIndex.Contains(showConfig.PointID)) return true; } return false; } /// /// 是否可以使用限定角色 /// /// /// private bool IsCanUseFixCharacter(ShowGroupData group) { foreach (DataShowConfig showConfig in group.ListConfig) { if (!ValueValidator.IsIdValid(showConfig.CharacterID) && !IsCanUseCharacter(showConfig.CharacterID)) return false; } return true; } /// /// 角色是否可用 /// /// /// private bool IsCanUseCharacter(int characterId) { return !setUseCharactrtId.Contains(characterId); } /// /// 获取进度 /// /// 0f-1f public float GetRefreshProcess() { return currState switch { E_CurState.None => 0f, E_CurState.LoadingCharacters => (float)listShowPointResult.Count / CharacterMaxCount, E_CurState.Present => 1f, _ => 0f, }; } /// /// 收集演出点 /// [Button("收集演出点")] public void CollectShowPoint() { if (tsCharacterShowPointRoot == null) { Debug.LogError("角色演出点为空"); return; } if (tsItemShowPointRoot == null) { Debug.LogError("道具演出点为空"); return; } #region 先清除原有演出点 Transform[] tsShowPoints = tsCharacterShowPointRoot.GetComponentsInChildren(); foreach (Transform ts in tsShowPoints) { if (ts == tsCharacterShowPointRoot) continue; DestroyImmediate(ts.gameObject); } tsShowPoints = tsItemShowPointRoot.GetComponentsInChildren(); foreach (Transform ts in tsShowPoints) { if (ts == tsItemShowPointRoot) continue; DestroyImmediate(ts.gameObject); } #endregion ShowPoint[] showPoints = tsCollectRoot.GetComponentsInChildren(); int characterIndex = 0; int itemIndex = 0; foreach (ShowPoint showPoint in showPoints) { switch (showPoint.ShowPointType) { case E_ShowPointType.Character: { GameObject go = new($"{characterIndex++:D3}_{showPoint.Name}"); go.transform.SetPositionAndRotation(showPoint.gameObject.transform.position, showPoint.gameObject.transform.rotation); go.transform.parent = tsCharacterShowPointRoot; } break; case E_ShowPointType.Item: { GameObject go = new($"{itemIndex++:D3}_{showPoint.Name}"); go.transform.SetPositionAndRotation(showPoint.gameObject.transform.position, showPoint.gameObject.transform.rotation); go.transform.parent = tsItemShowPointRoot; } break; default: { Debug.LogError("未处理类型"); } break; } } } } /// /// 抽取流程的记录 /// [Serializable] public struct ExtractRecord { /// /// 单个演出点记录 /// [Serializable] public struct SinglePointRecord { [LabelText("演出点ID")] public int showPoint; public string timelineName; public uint characterID; } [LabelText("组ID")] private int groupID; [LabelText("抛弃原因")] [ShowIf("isDispose")] private E_DisposeReason disposeReason; [LabelText("角色记录")] private List listRecord; public ExtractRecord(int groupID, E_DisposeReason disposeReason) { this.groupID = groupID; this.disposeReason = disposeReason; listRecord = new(); } public readonly void Add(SinglePointRecord singlePointRecord) { listRecord.Add(singlePointRecord); } } /// /// 演出组数据 /// public struct ShowGroupData { /// /// 演出组id /// private readonly int groupId; /// /// 是否必须有角色 /// private bool isMustHasCharacter; /// /// 该组演出点配置数据 /// private readonly List listConfig; /// /// 演出组id /// public readonly int GroupID { get { return groupId; } } /// /// 是否必须有角色 /// public readonly bool IsMustHasCharacter { get { return isMustHasCharacter; } } /// /// 该组演出点配置数据 /// public readonly List ListConfig { get { return listConfig; } } public ShowGroupData(int groupId) { this.groupId = groupId; isMustHasCharacter = false; listConfig = new(); } public void Add(DataShowConfig dataShowConfig) { isMustHasCharacter |= dataShowConfig.HasCharacter; listConfig.Add(dataShowConfig); } /// /// 打乱顺序 /// public readonly void Shuffle() { CommonUtils.Shuffle(listConfig); } } /// /// 演出点结果 /// public readonly struct ShowPointResult { /// /// 演出组id /// private readonly int groupID; /// /// 演出点索引 /// private readonly int showPointIndex; /// /// Timeline名 /// private readonly string timelineName; /// /// 角色数据 /// private readonly CharacterDataInfo characterData; public int GroupID { get { return groupID; } } public int ShowPointIndex { get { return showPointIndex; } } public string TimelineName { get { return timelineName; } } public CharacterDataInfo CharacterData { get { return characterData; } } public ShowPointResult(int groupID, int showPointIndex, string timelineName, CharacterDataInfo characterData) { this.groupID = groupID; this.showPointIndex = showPointIndex; this.timelineName = timelineName; this.characterData = characterData; } }