using cfg.ShowCfg;
using Cysharp.Threading.Tasks;
using Framework;
using Gameplay;
using PhxhSDK;
using Sirenix.OdinInspector;
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 tsShowPointRoot;
///
/// 最大抽取角色数量
///
[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 tsShowPointRoot; }
}
#endif
private void Awake()
{
if (!CommonUtils.IsInGame())
return;
ShowManager.Instance.Add(this);
currState = E_CurState.None;
#region 初始化数据
#region 获取演出点
if (tsShowPointRoot == null)
listShowPoint = new();
else
{
listShowPoint = new(tsShowPointRoot.childCount);
for (int i = 0; i < tsShowPointRoot.childCount; ++i)
{
listShowPoint.Add(tsShowPointRoot.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
int timelineID = CommonUtils.GetRandomItem(dataShowConfig.TimelineIDs);
ExtractRecord.SinglePointRecord singlePointRecord = new()
{
timelineID = timelineID
};
extractRecord.Add(singlePointRecord);
if (timelineID == 0) // 说明不站人
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, timelineID, 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.TimelineID);
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);
}
return goModel;
}
///
/// 获取Timeline资源
///
///
///
private async UniTask GetTimeLineAsset(int timelineID)
{
if (!dicTimeLineAsset.TryGetValue(timelineID, out TimelineAsset timelineAsset))
{
string timelinePath = CampTimeLineCollector.Instance[showSceneType, timelineID];
if (string.IsNullOrWhiteSpace(timelinePath))
{
DebugUtil.LogError($"找不到timelinePath,id:{timelineID}");
return null;
}
timelineAsset = await LoadAssetAsync(timelinePath);
if (timelineAsset == null)
{
DebugUtil.LogError($"找不到TimelineAsset, timelineID:{timelineID} path:{timelinePath}");
return null;
}
dicTimeLineAsset.Add(timelineID, 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,
};
}
}
///
/// 抽取流程的记录
///
[Serializable]
public struct ExtractRecord
{
///
/// 单个演出点记录
///
[Serializable]
public struct SinglePointRecord
{
[LabelText("演出点ID")]
public int showPoint;
public int timelineID;
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;
///
/// Timelineid
///
private readonly int timelineID;
///
/// 角色数据
///
private readonly CharacterDataInfo characterData;
public int GroupID
{
get { return groupID; }
}
public int ShowPointIndex
{
get { return showPointIndex; }
}
public int TimelineID
{
get { return timelineID; }
}
public CharacterDataInfo CharacterData
{
get { return characterData; }
}
public ShowPointResult(int groupID, int showPointIndex, int timelineID, CharacterDataInfo characterData)
{
this.groupID = groupID;
this.showPointIndex = showPointIndex;
this.timelineID = timelineID;
this.characterData = characterData;
}
}