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 Gameplay.Emoji; using UnityEngine; using UnityEngine.Playables; using UnityEngine.Timeline; using static ShowComponent; using Constants = Framework.Constants; using Object = UnityEngine.Object; using System.Linq; using Random = UnityEngine.Random; /// /// 演出组件 /// public class ShowComponent : MonoBehaviour, IShow { /// /// 当前状态 /// protected 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 tsItemRoot; /// /// 收集演出点根节点 /// [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; /// /// 相机Timeline /// [LabelText("相机PlayableDirector")] [SerializeField] private PlayableDirector cameraPlayable; /// /// 抽取记录 /// [ReadOnly] [LabelText("抽取记录")] [SerializeField] [ShowIf("@listExtractRecord.Count > 0")] private List listExtractRecord = new(); #endregion /// /// 演出Timeline路径 /// private const string SHOW_TIMELINE_PATH = "Assets/Art/Show/"; /// /// 道具Timeline上角色轨道名 /// private const string ITEM_TIMELINE_TRACK_NAME = "Character"; /// /// 角色Activation轨道名 /// private const string CHARACTER_ACTIVATION_TRACK_NAME = "characterActivationTrack"; /// /// 当前场景所有角色演出点 /// private List listCharacterShowPoint; /// /// 当前场景所有道具演出点 /// private List listItemShowPoint; /// /// 当前场景所有演出组字典 key:组id value:演出组数据 /// private readonly Dictionary dicShowGroup = new(); /// /// 最终的演出点数据 /// private readonly List listShowPointResult = new(); /// /// Timeline资源字典 /// private readonly Dictionary dicTimeLineAsset = new(); /// /// 道具字典,部分Timeline挂载到道具上,优先使用道具上的Timeline /// private readonly Dictionary> dicItemModel = new(); /// /// 角色模型字典 /// private readonly Dictionary dicCharacterModel = new(); /// /// 道具相对于角色的相对位置 /// private readonly Dictionary dicItemOffset = new(); /// /// 缓存的表情 /// private readonly List listCacheEmoji = new(); /// /// 已经加载的资源集合 /// private readonly HashSet setAssetToUnload = new(); /// /// 当前状态 /// protected E_CurState currState = E_CurState.None; /// /// 相机动画回调 /// private Action cameraAction; #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, listCharacterShowPoint.Count); } } public E_ShowSceneType ShowSceneTyp { get { return showSceneType; } } #if UNITY_EDITOR /// /// 角色演出点根节点 /// public Transform TsShowPointRoot { get { return tsCharacterShowPointRoot; } } /// /// 道具演出点根节点 /// public Transform TsItemShowPointRoot { get { return tsItemShowPointRoot; } } #endif private void Awake() { if (!CommonUtils.IsInGame()) return; ShowManager.Instance.Add(this); currState = E_CurState.None; #region 初始化数据 #region 获取角色演出点 if (tsCharacterShowPointRoot == null) listCharacterShowPoint = new(); else { listCharacterShowPoint = new(tsCharacterShowPointRoot.childCount); for (int i = 0; i < tsCharacterShowPointRoot.childCount; ++i) { listCharacterShowPoint.Add(tsCharacterShowPointRoot.GetChild(i)); } } #endregion #region 获取道具演出点 if (tsItemShowPointRoot == null) listItemShowPoint = new(); else { listItemShowPoint = new(tsItemShowPointRoot.childCount); for (int i = 0; i < tsItemShowPointRoot.childCount; ++i) { listItemShowPoint.Add(tsItemShowPointRoot.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() { dicTimeLineAsset.Clear(); dicCharacterModel.Clear(); dicItemModel.Clear(); dicItemOffset.Clear(); foreach (string path in setAssetToUnload) { AssetManager.Instance.Unload(path); } foreach (var unitEmojiManager in listCacheEmoji) { unitEmojiManager.Dispose(); } } /// /// 刷新演出 /// public virtual async UniTask RefreshShow(object data = null) { 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; #region 抽取timeline string timelineName = CommonUtils.GetRandomItem(dataShowConfig.TimelineNames); ExtractRecord.SinglePointRecord singlePointRecord = new() { timelineName = timelineName }; extractRecord.Add(singlePointRecord); if (timelineName == "0" || timelineName.IsNullOrWhitespace()) // 说明不站人 continue; #endregion #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; // 道具抽取 if (dataShowConfig.ItemNames.Count() > 0) { string itemName = CommonUtils.GetRandomItem(dataShowConfig.ItemNames); result = new(groupData.GroupID, dataShowConfig.PointID, timelineName, characterDataInfo, dataShowConfig.ItemPointId, itemName); } else { result = new(groupData.GroupID, dataShowConfig.PointID, timelineName, characterDataInfo, dataShowConfig.ItemPointId, string.Empty); } 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(); } /// /// 执行演出结果 /// protected virtual async UniTask ExecuteShowResult() { if (listShowPointResult.Count == 0) return; Dictionary> dicTempGoItem = new(); // 临时道具字典 foreach (ShowPointResult result in listShowPointResult) { #region 获取角色演出点 int showPointIndex = result.ShowPointIndex; if (showPointIndex < 0 || showPointIndex >= listCharacterShowPoint.Count || listCharacterShowPoint[showPointIndex] == null) { DebugUtil.LogError($"找不到演出点!演出点索引:{showPointIndex}"); continue; } Transform tsShowPoint = listCharacterShowPoint[showPointIndex].transform; #endregion int itemShowPointIndex = result.ItemPointIndex; if (itemShowPointIndex >= 0 && itemShowPointIndex < listItemShowPoint.Count && listItemShowPoint[itemShowPointIndex] != null) { Transform tsItemShowPoint = listItemShowPoint[itemShowPointIndex].transform; // 获取道具演出点,有道具演出 GameObject goFixItemModel = await GetItemModel(result.ItemName); // 加载固定道具 if (goFixItemModel != null) goFixItemModel.transform.SetPositionAndRotation(tsItemShowPoint.position, tsItemShowPoint.rotation); } GameObject goCharacterModel = await GetCharacterModel(result.CharacterData); if (goCharacterModel == null) continue; goCharacterModel.SetActive(true); goCharacterModel.transform.SetPositionAndRotation(tsShowPoint.position, tsShowPoint.rotation); GameObject goItemModel = await GetItemModel(result.TimelineName);// 优先使用道具上的Timeline if (goItemModel == null) // 没有道具,直接加载Timeline { TimelineAsset timelineAsset = await GetTimeLineAsset(result.TimelineName); if (timelineAsset == null) continue; PlayableDirector playableDirector = goCharacterModel.GetComponentOrAdd(); playableDirector.playableAsset = timelineAsset; playableDirector.extrapolationMode = DirectorWrapMode.Loop; foreach (PlayableBinding playableBinding in timelineAsset.outputs) { playableDirector.SetGenericBinding(playableBinding.sourceObject, goCharacterModel.GetComponentInChildren()); } foreach (TrackAsset trackAsset in timelineAsset.GetOutputTracks()) { if (trackAsset.name.Equals(CHARACTER_ACTIVATION_TRACK_NAME)) playableDirector.SetGenericBinding(trackAsset, goCharacterModel.transform.GetChild(0)?.gameObject); } playableDirector.enabled = true; playableDirector.time = Random.Range(0f, (float)playableDirector.duration); playableDirector.Play(); } else // 有道具,不加载Timeline { #region 临时记录使用过的道具GameObject,方便后续归还给池子 if (!dicTempGoItem.TryGetValue(result.TimelineName, out Stack goStack)) { goStack = new(); dicTempGoItem[result.TimelineName] = goStack; } goStack.Push(goItemModel); #endregion PlayableDirector playableDirector = goItemModel.GetComponentOrAdd(); TimelineAsset timelineAsset = playableDirector.playableAsset as TimelineAsset; if (timelineAsset == null) { DebugUtil.LogError($"{goItemModel.name} 没有TimelineAsset"); continue; } if (dicItemOffset.TryGetValue(goItemModel.GetInstanceID(), out Vector3 offset)) goItemModel.transform.SetPositionAndRotation(goCharacterModel.transform.position + offset, Quaternion.identity); else goItemModel.transform.SetPositionAndRotation(goCharacterModel.transform.position, Quaternion.identity); goItemModel.transform.RotateAround(goCharacterModel.transform.position, Vector3.up, goCharacterModel.transform.localEulerAngles.y); PlayableDirector cPlayableDirector = goCharacterModel.GetComponentOrAdd(); cPlayableDirector.enabled = false; foreach (TrackAsset trackAsset in timelineAsset.GetOutputTracks()) { if (trackAsset.name.Equals(ITEM_TIMELINE_TRACK_NAME)) playableDirector.SetGenericBinding(trackAsset, goCharacterModel.GetComponentInChildren()); if (trackAsset.name.Equals(CHARACTER_ACTIVATION_TRACK_NAME)) { Transform tsGo = goCharacterModel.transform.GetChild(0); if (null == tsGo) DebugUtil.LogError("赋值空的角色到Timeline轨道上"); else playableDirector.SetGenericBinding(trackAsset, tsGo.gameObject); } } playableDirector.time = Random.Range(0f, (float)playableDirector.duration); playableDirector.Play(); } } #region 放回道具 foreach (var kv in dicTempGoItem) { if (!dicItemModel.TryGetValue(kv.Key, out Stack stack)) continue; stack ??= new(); while (kv.Value.Count > 0) { stack.Push(kv.Value.Pop()); } } dicTempGoItem.Clear(); #endregion } /// /// 初始化emoji /// /// /// /// private UnitEmojiManager InitEmoji(GameObject cObj, CharacterDataInfo dataInfo) { var config = dataInfo.Cfg; var skinID = dataInfo.SkinID > 0 ? (int)dataInfo.SkinID : Constants.DEFULT_SKINID; var skinData = TableManager.Instance.Tables.SkinCfg.Get(skinID, dataInfo.Cfg.RoleID); if (skinData == null) { DebugUtil.LogError($"找不到皮肤数据, skinID:{skinID}, roleID:{dataInfo.Cfg.RoleID}"); return null; } var eyebowsType = string.IsNullOrEmpty(config.EmojiEyeBowsType) ? "1" : config.EmojiEyeBowsType; var eyesColor = string.IsNullOrEmpty(config.EmojiEyesColor) ? "#ff0000" : config.EmojiEyesColor; var eyebowsColor = string.IsNullOrEmpty(config.EmojiEyeBowsColor) ? "#ff0000" : config.EmojiEyeBowsColor; var unitEmojiManager = new UnitEmojiManager(cObj, eyesColor, eyebowsType, eyebowsColor); unitEmojiManager.SwitchEmoji(config.DefaultDisplayEmojiId); return unitEmojiManager; } /// /// 播放相机动画 /// /// public void PlayeCameraAnim(Action callbackAction, params string[] args) { if (null == cameraPlayable) { DebugUtil.LogError("相机Playable未赋值"); return; } cameraAction = callbackAction; cameraPlayable.Play(); } /// /// 相机动画播放完成 /// public void OnPlayCameraAnimStop() { cameraAction?.Invoke(); cameraAction = null; } /// /// 异步加载资源 /// /// /// /// 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 GetCharacterModel(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.SetLayerEx(Constants.Layer.UIVIEW); HideCharacterWeapon(goModel); UnitEmojiManager unitEmojiManager = InitEmoji(goModel, characterDataInfo); if (unitEmojiManager != null) { // 缓存起来 listCacheEmoji.Add(unitEmojiManager); } dicCharacterModel.Add(characterDataInfo.Cfg.RoleID, goModel); } return goModel; } /// /// 获取道具模型 /// /// /// private async UniTask GetItemModel(string name) { if (!dicItemModel.TryGetValue(name, out Stack stackItem) || stackItem.Count <=0) { string itemPath = $"{SHOW_TIMELINE_PATH}{showSceneType}/{name}.prefab"; if (!AssetManager.Instance.CanLocateAsset(itemPath)) return null; stackItem ??= new(); GameObject goItem = await LoadAssetAsync(itemPath); if (goItem == null) return null; goItem = Instantiate(goItem, tsItemRoot); goItem.SetLayerEx(Constants.Layer.UIVIEW); dicItemModel[name] = stackItem; dicItemOffset[goItem.GetInstanceID()] = goItem.transform.position; return goItem; } return stackItem.Pop(); } /// /// 隐藏角色武器 /// private void HideCharacterWeapon(GameObject goCharacter) { if (goCharacter.transform.childCount < 1) { DebugUtil.LogError("角色节点错误:" + goCharacter.name); return; } // TODO 临时隐藏武器方式 Transform tsRoot = goCharacter.transform.GetChild(0).Find("Root"); if (null == tsRoot) return; 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 = $"{SHOW_TIMELINE_PATH}{showSceneType}/{timelineName}.playable"; 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 class 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 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; /// /// 道具演出点索引 /// private readonly int itemPointIndex; /// /// Timeline名 /// private readonly string timelineName; /// /// 道具名 /// private readonly string itemName; /// /// 角色数据 /// private readonly CharacterDataInfo characterData; #region 属性 public int GroupID { get { return groupID; } } public int ShowPointIndex { get { return showPointIndex; } } public string TimelineName { get { return timelineName; } } public int ItemPointIndex { get { return itemPointIndex; } } public string ItemName { get { return itemName; } } public CharacterDataInfo CharacterData { get { return characterData; } } #endregion public ShowPointResult(int groupID, int showPointIndex, string timelineName, CharacterDataInfo characterData, int itemPointIndex, string itemName) { this.groupID = groupID; this.showPointIndex = showPointIndex; this.timelineName = timelineName; this.characterData = characterData; this.itemPointIndex = itemPointIndex; this.itemName = itemName; } }