NLDClient-yudde/ProjectNLD/Assets/Code/Scripts/Gameplay/Show/ShowComponent.cs

1156 lines
35 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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;
using cfg.CharacterCfg;
/// <summary>
/// 演出组件
/// </summary>
public class ShowComponent : MonoBehaviour, IShow
{
/// <summary>
/// 当前状态
/// </summary>
protected enum E_CurState
{
None = 0,
/// <summary>
/// 加载角色
/// </summary>
LoadingCharacters,
/// <summary>
/// 当前
/// </summary>
Present,
}
/// <summary>
/// 被弃用的原因
/// </summary>
public enum E_DisposeReason
{
None,
/// <summary>
/// 演出点重复
/// </summary>
[LabelText("演出点重复")]
ShowPointRepeat,
/// <summary>
/// 组内人数不满
/// </summary>
[LabelText("组内人数不满")]
CharacterCountNotFull,
/// <summary>
/// 固定角色不存在
/// </summary>
[LabelText("固定角色不存在")]
FixedCharacterNotExist,
}
#region 外部序列化字段
/// <summary>
/// 演出场景类型
/// </summary>
[LabelText("演出场景类型")]
[SerializeField]
private E_ShowSceneType showSceneType;
/// <summary>
/// 角色根节点
/// </summary>
[LabelText("角色根节点")]
[SerializeField]
private Transform tsCharacterRoot;
/// <summary>
/// 道具根节点
/// </summary>
[LabelText("道具根节点")]
[SerializeField]
private Transform tsItemRoot;
/// <summary>
/// 收集演出点根节点
/// </summary>
[LabelText("收集演出点的根节点")]
[SerializeField]
private Transform tsCollectRoot;
/// <summary>
/// 角色演出点根节点
/// </summary>
[LabelText("角色演出点根节点")]
[SerializeField]
private Transform tsCharacterShowPointRoot;
/// <summary>
/// 道具演出点根节点
/// </summary>
[LabelText("道具演出点根节点")]
[SerializeField]
private Transform tsItemShowPointRoot;
/// <summary>
/// 最大抽取角色数量
/// </summary>
[LabelText("最大抽取角色数")]
[SerializeField]
private int characterMaxCount = 20;
/// <summary>
/// 是否忽略看板娘
/// </summary>
[LabelText("是否忽略看板娘")]
[SerializeField]
private bool IsIgnoreKanbanban;
/// <summary>
/// 相机Timeline
/// </summary>
[LabelText("相机PlayableDirector")]
[SerializeField]
private PlayableDirector cameraPlayable;
/// <summary>
/// 抽取记录
/// </summary>
[ReadOnly]
[LabelText("抽取记录")]
[SerializeField]
[ShowIf("@listExtractRecord.Count > 0")]
private List<ExtractRecord> listExtractRecord = new();
#endregion
/// <summary>
/// 演出Timeline路径
/// </summary>
private const string SHOW_TIMELINE_PATH = "Assets/Art/Show/";
/// <summary>
/// 道具Timeline上角色轨道名
/// </summary>
private const string ITEM_TIMELINE_TRACK_NAME = "Character";
/// <summary>
/// 角色Activation轨道名
/// </summary>
private const string CHARACTER_ACTIVATION_TRACK_NAME = "characterActivationTrack";
/// <summary>
/// 当前场景所有角色演出点
/// </summary>
private List<Transform> listCharacterShowPoint;
/// <summary>
/// 当前场景所有道具演出点
/// </summary>
private List<Transform> listItemShowPoint;
/// <summary>
/// 当前场景所有演出组字典 key:组id value:演出组数据
/// </summary>
private readonly Dictionary<int, ShowGroupData> dicShowGroup = new();
/// <summary>
/// 最终的演出点数据
/// </summary>
private readonly List<ShowPointResult> listShowPointResult = new();
/// <summary>
/// Timeline资源字典
/// </summary>
private readonly Dictionary<string, TimelineAsset> dicTimeLineAsset = new();
/// <summary>
/// 道具字典部分Timeline挂载到道具上优先使用道具上的Timeline
/// </summary>
private readonly Dictionary<string, Stack<GameObject>> dicItemModel = new();
/// <summary>
/// 角色模型字典
/// </summary>
private readonly Dictionary<int, GameObject> dicCharacterModel = new();
/// <summary>
/// 道具相对于角色的相对位置
/// </summary>
private readonly Dictionary<int, Vector3> dicItemOffset = new();
/// <summary>
/// 缓存的表情
/// </summary>
private readonly List<UnitEmojiManager> listCacheEmoji = new();
/// <summary>
/// 已经加载的资源集合
/// </summary>
private readonly HashSet<string> setAssetToUnload = new();
/// <summary>
/// 当前状态
/// </summary>
protected E_CurState currState = E_CurState.None;
/// <summary>
/// 相机动画回调
/// </summary>
private Action cameraAction;
#region (刷新数据,抽取)过程临时变量
/// <summary>
/// 可以抽取的演出组数据(排序用)
/// </summary>
private readonly List<ShowGroupData> listRemindShowGroup = new();
/// <summary>
/// 可以抽取演出的角色数据(排序用)
/// </summary>
private readonly List<CharacterDataInfo> listRemindCharacter = new();
/// <summary>
/// 随机抽取的演出组链表
/// </summary>
private LinkedList<ShowGroupData> linkShowGroup;
/// <summary>
/// 随机抽取的角色链表
/// </summary>
private LinkedList<CharacterDataInfo> linkCharacter;
/// <summary>
/// 已经抽取的演出点索引集合
/// </summary>
private readonly HashSet<int> setUseShowPointIndex = new();
/// <summary>
/// 已经抽取的角色id
/// </summary>
private readonly HashSet<int> setUseCharactrtId = new();
#endregion
/// <summary>
/// 可以抽取的角色最大数量
/// </summary>
public int CharacterMaxCount
{
get { return Mathf.Min(listRemindCharacter.Count, characterMaxCount, listCharacterShowPoint.Count); }
}
public E_ShowSceneType ShowSceneTyp
{
get { return showSceneType; }
}
#if UNITY_EDITOR
/// <summary>
/// 角色演出点根节点
/// </summary>
public Transform TsShowPointRoot
{
get { return tsCharacterShowPointRoot; }
}
/// <summary>
/// 道具演出点根节点
/// </summary>
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<DataShowConfig> 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();
}
}
/// <summary>
/// 刷新演出
/// </summary>
public virtual async UniTask RefreshShow(object data = null)
{
currState = E_CurState.LoadingCharacters;
RefreshShowData();
ExtractShowCharacter();
await ExecuteShowResult();
currState = E_CurState.Present;
}
/// <summary>
/// 刷新演出数据
/// </summary>
private void RefreshShowData()
{
listRemindShowGroup.Clear();
listRemindCharacter.Clear();
listRemindShowGroup.AddRange(dicShowGroup.Values);
#region 获取看板娘id
int kanbanbanId = 0;
if (IsIgnoreKanbanban)
{
MainPanelSceneStoreData storeData = StorageMgr.Instance.GetStorage<MainPanelSceneStoreData>("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
}
/// <summary>
/// 抽取演出角色
/// </summary>
private void ExtractShowCharacter()
{
listExtractRecord.Clear();
listShowPointResult.Clear();
List<ShowPointResult> 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();
}
/// <summary>
/// 执行演出结果
/// </summary>
protected virtual async UniTask ExecuteShowResult()
{
if (listShowPointResult.Count == 0)
return;
Dictionary<string, Stack<GameObject>> 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>();
playableDirector.playableAsset = timelineAsset;
playableDirector.extrapolationMode = DirectorWrapMode.Loop;
foreach (PlayableBinding playableBinding in timelineAsset.outputs)
{
playableDirector.SetGenericBinding(playableBinding.sourceObject, goCharacterModel.GetComponentInChildren<Animator>());
}
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<GameObject> goStack))
{
goStack = new();
dicTempGoItem[result.TimelineName] = goStack;
}
goStack.Push(goItemModel);
#endregion
PlayableDirector playableDirector = goItemModel.GetComponentOrAdd<PlayableDirector>();
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<PlayableDirector>();
cPlayableDirector.enabled = false;
foreach (TrackAsset trackAsset in timelineAsset.GetOutputTracks())
{
if (trackAsset.name.Equals(ITEM_TIMELINE_TRACK_NAME))
playableDirector.SetGenericBinding(trackAsset, goCharacterModel.GetComponentInChildren<Animator>());
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<GameObject> stack))
continue;
stack ??= new();
while (kv.Value.Count > 0)
{
stack.Push(kv.Value.Pop());
}
}
dicTempGoItem.Clear();
#endregion
}
/// <summary>
/// 初始化emoji
/// </summary>
/// <param name="cObj"></param>
/// <param name="dataInfo"></param>
/// <returns></returns>
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;
}
/// <summary>
/// 播放相机动画
/// </summary>
/// <exception cref="NotImplementedException"></exception>
public void PlayeCameraAnim(Action callbackAction, params string[] args)
{
if (null == cameraPlayable)
{
DebugUtil.LogError("相机Playable未赋值");
return;
}
cameraAction = callbackAction;
cameraPlayable.Play();
}
/// <summary>
/// 相机动画播放完成
/// </summary>
public void OnPlayCameraAnimStop()
{
cameraAction?.Invoke();
cameraAction = null;
}
/// <summary>
/// 异步加载资源
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="path"></param>
/// <returns></returns>
private async UniTask<T> LoadAssetAsync<T>(string path) where T : Object
{
T res = await AssetManager.Instance.LoadAssetAsync<T>(path);
if (res != null)
setAssetToUnload.Add(path);
return res;
}
/// <summary>
/// 获取角色模型
/// </summary>
/// <param name="characterDataInfo"></param>
/// <returns></returns>
private async UniTask<GameObject> GetCharacterModel(CharacterDataInfo characterDataInfo)
{
if (!dicCharacterModel.TryGetValue(characterDataInfo.Cfg.RoleID, out GameObject goModel))
{
string prefabPath = CommonUtils.GetCharacterSkinPath(characterDataInfo);
goModel = await LoadAssetAsync<GameObject>(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;
}
/// <summary>
/// 获取道具模型
/// </summary>
/// <param name="characterDataInfo"></param>
/// <returns></returns>
private async UniTask<GameObject> GetItemModel(string name)
{
if (!dicItemModel.TryGetValue(name, out Stack<GameObject> stackItem) || stackItem.Count <=0)
{
string itemPath = $"{SHOW_TIMELINE_PATH}{showSceneType}/{name}.prefab";
if (!AssetManager.Instance.CanLocateAsset<GameObject>(itemPath))
return null;
stackItem ??= new();
GameObject goItem = await LoadAssetAsync<GameObject>(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();
}
/// <summary>
/// 隐藏角色武器
/// </summary>
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);
}
/// <summary>
/// 获取Timeline资源
/// </summary>
/// <param name="timelineName"></param>
/// <returns></returns>
private async UniTask<TimelineAsset> 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<TimelineAsset>(timelinePath);
if (timelineAsset == null)
{
DebugUtil.LogError($"找不到TimelineAsset, {nameof(timelineName)}:{timelineName} path:{timelinePath}");
return null;
}
dicTimeLineAsset.Add(timelineName, timelineAsset);
}
return timelineAsset;
}
/// <summary>
/// 演出点是否重复
/// </summary>
/// <param name="group"></param>
/// <returns></returns>
private bool IsShowPointExist(ShowGroupData group)
{
foreach (DataShowConfig showConfig in group.ListConfig)
{
if (setUseShowPointIndex.Contains(showConfig.PointID))
return true;
}
return false;
}
/// <summary>
/// 是否可以使用限定角色
/// </summary>
/// <param name="group"></param>
/// <returns></returns>
private bool IsCanUseFixCharacter(ShowGroupData group)
{
foreach (DataShowConfig showConfig in group.ListConfig)
{
if (!ValueValidator.IsIdValid(showConfig.CharacterID) && !IsCanUseCharacter(showConfig.CharacterID))
return false;
}
return true;
}
/// <summary>
/// 角色是否可用
/// </summary>
/// <param name="characterId"></param>
/// <returns></returns>
private bool IsCanUseCharacter(int characterId)
{
return !setUseCharactrtId.Contains(characterId);
}
/// <summary>
/// 获取进度
/// </summary>
/// <returns>0f-1f</returns>
public float GetRefreshProcess()
{
return currState switch
{
E_CurState.None => 0f,
E_CurState.LoadingCharacters => (float)listShowPointResult.Count / CharacterMaxCount,
E_CurState.Present => 1f,
_ => 0f,
};
}
/// <summary>
/// 收集演出点
/// </summary>
[Button("收集演出点")]
public void CollectShowPoint()
{
if (tsCharacterShowPointRoot == null)
{
Debug.LogError("角色演出点为空");
return;
}
if (tsItemShowPointRoot == null)
{
Debug.LogError("道具演出点为空");
return;
}
#region 先清除原有演出点
Transform[] tsShowPoints = tsCharacterShowPointRoot.GetComponentsInChildren<Transform>();
foreach (Transform ts in tsShowPoints)
{
if (ts == tsCharacterShowPointRoot)
continue;
DestroyImmediate(ts.gameObject);
}
tsShowPoints = tsItemShowPointRoot.GetComponentsInChildren<Transform>();
foreach (Transform ts in tsShowPoints)
{
if (ts == tsItemShowPointRoot)
continue;
DestroyImmediate(ts.gameObject);
}
#endregion
ShowPoint[] showPoints = tsCollectRoot.GetComponentsInChildren<ShowPoint>();
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;
}
}
}
}
/// <summary>
/// 抽取流程的记录
/// </summary>
[Serializable]
public class ExtractRecord
{
/// <summary>
/// 单个演出点记录
/// </summary>
[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<SinglePointRecord> listRecord;
public ExtractRecord(int groupID, E_DisposeReason disposeReason)
{
this.groupID = groupID;
this.disposeReason = disposeReason;
listRecord = new();
}
public void Add(SinglePointRecord singlePointRecord)
{
listRecord.Add(singlePointRecord);
}
}
/// <summary>
/// 演出组数据
/// </summary>
public struct ShowGroupData
{
/// <summary>
/// 演出组id
/// </summary>
private readonly int groupId;
/// <summary>
/// 是否必须有角色
/// </summary>
private bool isMustHasCharacter;
/// <summary>
/// 该组演出点配置数据
/// </summary>
private readonly List<DataShowConfig> listConfig;
/// <summary>
/// 演出组id
/// </summary>
public readonly int GroupID
{
get { return groupId; }
}
/// <summary>
/// 是否必须有角色
/// </summary>
public readonly bool IsMustHasCharacter { get { return isMustHasCharacter; } }
/// <summary>
/// 该组演出点配置数据
/// </summary>
public readonly List<DataShowConfig> 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);
}
/// <summary>
/// 打乱顺序
/// </summary>
public readonly void Shuffle()
{
CommonUtils.Shuffle(listConfig);
}
}
/// <summary>
/// 演出点结果
/// </summary>
public readonly struct ShowPointResult
{
/// <summary>
/// 演出组id
/// </summary>
private readonly int groupID;
/// <summary>
/// 演出点索引
/// </summary>
private readonly int showPointIndex;
/// <summary>
/// 道具演出点索引
/// </summary>
private readonly int itemPointIndex;
/// <summary>
/// Timeline名
/// </summary>
private readonly string timelineName;
/// <summary>
/// 道具名
/// </summary>
private readonly string itemName;
/// <summary>
/// 角色数据
/// </summary>
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;
}
}