NLDClient-yudde/ProjectNLD/Assets/Code/Scripts/Gameplay/Camp/CampSceneManager.cs

452 lines
13 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 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<DataCampShowConfig> 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<SinglePointRecord> records = new();
}
//被弃用的原因
public enum EDisposeReason
{
None,
[LabelText("演出点重复")]
ShowPointRepeat,
[LabelText("组内人数不满")]
CharacterCountNotFull,
[LabelText("固定角色不存在")]
FixedCharacterNotExist,
}
public class CampSceneManager : MonoBehaviour
{
private enum ECurrState
{
None = 0,
LoadingCampObjects,
LoadingCharacters,
Present,
}
[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<GameObject> _showPoints = new();
[ReadOnly]
[LabelText("抽取记录")]
[SerializeField]
[ShowIf("@_extractRecords.Count > 0")]
private List<ExtractRecord> _extractRecords = new();
private List<CharacterDataInfo> _allCharacterInfos = new();
private Dictionary<int, CampShowConfigGroup> _allCampShowConfigGroups = new();
private List<CampShowConfigGroup> _remindCampShowConfigGroups = new();
private List<CharacterDataInfo> _remindCharacters = new();
private List<CampShowPointResult> _campShowPointResults = new();
private TimelineAsset _mainTimeline;
public IEnumerable<GameObject> ShowPoints => _showPoints;
//asset相关
private Dictionary<CharacterDataInfo, GameObject> _models = new();
private Dictionary<int, TimelineAsset> _timeLines = new();
private HashSet<string> _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<CharacterDataInfo>(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<CampShowPointResult> _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<PlayableDirector>();
if (director == null)
{
continue;
}
director.playableAsset = timelineAsset;
director.extrapolationMode = DirectorWrapMode.Loop;
foreach (var playableBinding in timelineAsset.outputs)
{
director.SetGenericBinding(playableBinding.sourceObject, model.GetComponentInChildren<Animator>());
}
director.Play();
}
}
private async UniTask<GameObject> GetModel(CharacterDataInfo characterDataInfo)
{
if (!_models.TryGetValue(characterDataInfo, out var model))
{
string prefabPath = characterDataInfo.PrefabPath;
model = await AssetManager.Instance.LoadAssetAsync<GameObject>(prefabPath);
if (model == null)
{
DebugUtil.LogError($"找不到模型, path:{prefabPath}");
return null;
}
model = Instantiate(model, _characterRoot);
model.AddComponent<PlayableDirector>();
model.SetLayerEx(LayerMask.NameToLayer("UIView"));
_assetToUnload.Add(prefabPath);
_models.Add(characterDataInfo, model);
}
return model;
}
private async UniTask<TimelineAsset> GetTimeLineAsset(int timelineID)
{
if (!_timeLines.TryGetValue(timelineID, out var timelineAsset))
{
string timelinePath = CampTimeLineCollector.Instance[timelineID];
if (string.IsNullOrEmpty(timelinePath))
{
DebugUtil.LogError($"找不到timelinePath,id:{timelineID}");
return null;
}
timelineAsset = await AssetManager.Instance.LoadAssetAsync<TimelineAsset>(timelinePath);
if (timelineAsset == null)
{
DebugUtil.LogError($"找不到TimelineAsset, timelineID:{timelineID} path:{timelinePath}");
return null;
}
_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;
}
}
}