NLDClient-yudde/ProjectNLD/Assets/Editor/Story/Utils/TimelineUtils.cs

717 lines
29 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using Framework;
using Gameplay.Story.Cmp.Data;
using Gameplay.Story.Cmp.Timeline.AudioAction;
using Gameplay.Story.Cmp.Timeline.BlockChoiceAction;
using Gameplay.Story.Cmp.Timeline.CameraPosAction;
using Gameplay.Story.Cmp.Timeline.CameraPropAction;
using Gameplay.Story.Cmp.Timeline.CameraRotAction;
using Gameplay.Story.Cmp.Timeline.CommonAction;
using Gameplay.Story.Cmp.Timeline.MsgAction;
using Gameplay.Story.Cmp.Timeline.PlayerAction;
using Gameplay.Story.Cmp.Timeline.PlayerEmojiAction;
using Gameplay.Story.Cmp.Timeline.PlayerPosAction;
using Gameplay.Story.Cmp.Timeline.PlayerRotAction;
using Gameplay.Story.Cmp.Timeline.PlayerTalkAction;
using Gameplay.Story.Cmp.Timeline.SceneSwitchAction;
using Gameplay.Story.Cmp.Timeline.SetNameAction;
using Gameplay.Story.Cmp.Timeline.UIImageSetAction;
using Gameplay.Story.Cmp.Timeline.UIModChangeAction;
using Gameplay.Story.Cmp.Timeline.WeaponHideAction;
using Sirenix.Utilities.Editor;
using UnityEditor;
using UnityEngine;
using UnityEngine.Playables;
using UnityEngine.Timeline;
namespace Gameplay.Story.Cmp.Utils
{
public static class TimelineUtils
{
private static readonly Dictionary<int, AnimationTrack> PlayerActionTrackDic = new Dictionary<int, AnimationTrack>();
private static readonly Dictionary<int, PlayerEmojiActionTrack> PlayerEmojiActionTrackDic = new Dictionary<int, PlayerEmojiActionTrack>();
private static readonly Dictionary<int, PlayerPosActionTrack> PlayerPosTrackDic = new Dictionary<int, PlayerPosActionTrack>();
private static readonly Dictionary<int, PlayerTalkActionTrack> PlayerTalkTrackDic = new Dictionary<int, PlayerTalkActionTrack>();
private static readonly Dictionary<int, PlayerRotActionTrack> PlayerRotTrackDic = new Dictionary<int, PlayerRotActionTrack>();
private static readonly Dictionary<int, CachePlayerAnimClips> PlayerClipDic = new Dictionary<int, CachePlayerAnimClips>();
private static readonly Dictionary<int, WeaponHideActionTrack> WeaponHideActionTrackDic = new Dictionary<int, WeaponHideActionTrack>();
private static readonly Dictionary<int, SetNameActionTrack> SetNameActionTrackDic = new Dictionary<int, SetNameActionTrack>();
private static readonly Dictionary<int, AudioActionTrack> AudioActionTrackDic = new Dictionary<int, AudioActionTrack>();
private static readonly List<StoryExcelPlayerData> PlayerDataList = new List<StoryExcelPlayerData>();
private static MsgActionTrack _msgActionTrack;
private static BlockChoiceActionTrack _blockChoiceTrack;
private static SceneSwitchActionTrack _sceneSwitchTrack;
private static CameraPosActionTrack _cameraPosActionTrack;
private static CameraRotActionTrack _cameraRotActionTrack;
private static CameraPropActionTrack _cameraPropActionTrack;
private static UIModChangeActionTrack _uiModChangeTrack;
private const float SampleTime = 1f;
public static TimelineAsset CreateTimelineAsset(string savePath,
StoryExcelData storyExcelData)
{
// 判断文件是否存在,如果存在则先删除
var fullPath = Application.dataPath.Replace("Assets", "") + savePath;
if (System.IO.File.Exists(fullPath))
{
// 先进行checkout
// VersionControlSystem.Checkout(savePath);
AssetDatabase.DeleteAsset(savePath);
AssetDatabase.Refresh();
}
var timelineAsset = ScriptableObject.CreateInstance<TimelineAsset>();
AssetDatabase.CreateAsset(timelineAsset, savePath);
PlayerActionTrackDic.Clear();
PlayerEmojiActionTrackDic.Clear();
PlayerPosTrackDic.Clear();
PlayerTalkTrackDic.Clear();
PlayerRotTrackDic.Clear();
SetNameActionTrackDic.Clear();
PlayerClipDic.Clear();
WeaponHideActionTrackDic.Clear();
AudioActionTrackDic.Clear();
PlayerDataList.Clear();
PlayerDataList.AddRange(storyExcelData.playerList);
_CreateGroupTracks(timelineAsset);
var currentTime = 0f;
var onlyOnce = true;
for (var i = 0; i < storyExcelData.blockList.Count; i++)
{
var block = storyExcelData.blockList[i];
var totalTime = _CreatePlayers(block, currentTime);
if (onlyOnce)
{
_CreateCameraPosTrack(block, currentTime, totalTime);
_CreateCameraRotTrack(block, currentTime, totalTime);
_CreateSceneSwitch(currentTime, totalTime);
onlyOnce = false;
}
_CreateMsgAction(_msgActionTrack, block, currentTime, totalTime);
_CreateBlockChoice(currentTime, totalTime);
currentTime += totalTime;
}
// 角色相关的只创建第一次
var playerList = PlayerDataList;
for (var i = 0; i < playerList.Count; i++)
{
var playerData = playerList[i];
_CreatePlayerPos(PlayerPosTrackDic[playerData.playerId], playerData, 0, SampleTime);
_CreatePlayerRot(PlayerRotTrackDic[playerData.playerId], playerData, 0, SampleTime);
_CreateWeaponHides(WeaponHideActionTrackDic[playerData.playerId], playerData, 0, SampleTime);
}
// 保存到指定路径
// 保存时间轴资源为.asset文件
EditorUtility.SetDirty(timelineAsset);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
return timelineAsset;
}
private static void _CreateCameraRotTrack(StoryExcelBlockData block,
float startTime,
float during)
{
var clip = _cameraRotActionTrack.CreateClip<CameraRotActionClip>();
clip.start = startTime;
clip.duration = during;
}
private static void _CreateCameraPosTrack(StoryExcelBlockData block,
float startTime,
float during)
{
var clip = _cameraPosActionTrack.CreateClip<CameraPosActionClip>();
clip.start = startTime;
clip.duration = during;
}
private static void _CreateGroupTracks(TimelineAsset timelineAsset)
{
// 创建剧情轨道
var groupBlockTrack = timelineAsset.CreateTrack<GroupTrack>(null, "剧情") as GroupTrack;
_msgActionTrack = timelineAsset.CreateTrack<MsgActionTrack>(groupBlockTrack, "剧情文字");
_blockChoiceTrack = timelineAsset.CreateTrack<BlockChoiceActionTrack>(groupBlockTrack, "剧情选项");
_sceneSwitchTrack = timelineAsset.CreateTrack<SceneSwitchActionTrack>(groupBlockTrack, "场景切换");
// StoryManager.instance.msgManager.msgTrack = _blockLine;
// 创建角色轨道
var playerGroupTrack = timelineAsset.CreateTrack<GroupTrack>(null, "角色") as GroupTrack;
foreach (var playerData in PlayerDataList)
{
var combieName = playerData.playerName + playerData.playerId;
var groupTrackSplit = timelineAsset.CreateTrack<GroupTrack>(playerGroupTrack, combieName);
// 创建角色说话轨道
var playerTalkActionTrack = timelineAsset.CreateTrack<PlayerTalkActionTrack>(groupTrackSplit, "说话") as PlayerTalkActionTrack;
playerTalkActionTrack.playerId = playerData.playerId;
PlayerTalkTrackDic.Add(playerData.playerId, playerTalkActionTrack);
// 创建角色动作轨道
// var playerActionTrack = timelineAsset.CreateTrack<PlayerActionTrack>(groupTrackSplit, "动作") as PlayerActionTrack;
// playerActionTrack.playerId = playerData.playerId;
var playerActionTrack = timelineAsset.CreateTrack<AnimationTrack>(groupTrackSplit, "动作") as AnimationTrack;
playerActionTrack.applyAvatarMask = false;
PlayerActionTrackDic.Add(playerData.playerId, playerActionTrack);
PlayerClipDic.Add(playerData.playerId, new CachePlayerAnimClips()
{
playerId = playerData.playerId
});
// 创建角色表情轨道
var playerEmojiActionTrack = timelineAsset.CreateTrack<PlayerEmojiActionTrack>(groupTrackSplit, "表情") as PlayerEmojiActionTrack;
playerEmojiActionTrack.playerId = playerData.playerId;
PlayerEmojiActionTrackDic.Add(playerData.playerId, playerEmojiActionTrack);
// 创建角色位置轨道
var playerPosActionTrack = timelineAsset.CreateTrack<PlayerPosActionTrack>(groupTrackSplit, "位置");
PlayerPosTrackDic.Add(playerData.playerId, playerPosActionTrack);
playerPosActionTrack.playerId = playerData.playerId;
// 创建角色朝向轨道
var playerRotActionTrack = timelineAsset.CreateTrack<PlayerRotActionTrack>(groupTrackSplit, "朝向");
PlayerRotTrackDic.Add(playerData.playerId, playerRotActionTrack);
// 创建角色武器隐藏轨道
var weaponHideActionTrack = timelineAsset.CreateTrack<WeaponHideActionTrack>(groupTrackSplit, "武器隐藏");
weaponHideActionTrack.playerId = playerData.playerId;
WeaponHideActionTrackDic.Add(playerData.playerId, weaponHideActionTrack);
var setNameActionTrack = timelineAsset.CreateTrack<SetNameActionTrack>(groupTrackSplit, "名字");
setNameActionTrack.playerId = playerData.playerId;
SetNameActionTrackDic.Add(playerData.playerId, setNameActionTrack);
//创建角色音效轨道
var audioActionTrack = timelineAsset.CreateTrack<AudioActionTrack>(groupTrackSplit, "音频");
audioActionTrack.playerId = playerData.playerId;
AudioActionTrackDic.Add(playerData.playerId, audioActionTrack);
}
// 创建相机轨道
var cameraGroupTrack = timelineAsset.CreateTrack<GroupTrack>(null, "相机");
{
// 创建相机位置轨道
var cameraPosActionTrack = timelineAsset.CreateTrack<CameraPosActionTrack>(cameraGroupTrack, "位置");
_cameraPosActionTrack = cameraPosActionTrack;
// 创建相机朝向轨道
var cameraRotActionTrack = timelineAsset.CreateTrack<CameraRotActionTrack>(cameraGroupTrack, "朝向");
_cameraRotActionTrack = cameraRotActionTrack;
// 创建相机size轨道
var cameraSizeActionTrack = timelineAsset.CreateTrack<CameraPropActionTrack>(cameraGroupTrack, "属性");
_cameraPropActionTrack = cameraSizeActionTrack;
// 创建一个空的clip
var clip = cameraSizeActionTrack.CreateClip<CameraPropActionClip>();
clip.start = 0f;
clip.duration = 1f;
}
// 创建UI轨道
var UIGroupTrack = timelineAsset.CreateTrack<GroupTrack>(null, "UI");
{
// 创建UI模式切换
var uimodChangeTrack = timelineAsset.CreateTrack<UIModChangeActionTrack>(UIGroupTrack, "模式切换");
_uiModChangeTrack = uimodChangeTrack;
// 默认显示3D相机内容
var clip = uimodChangeTrack.CreateClip<UIModChangeActionClip>();
clip.start = 0f;
clip.duration = 5f;
// 创建前景切换
var uiFrontChangeTrack = timelineAsset.CreateTrack<UIImageSetActionTrack>(UIGroupTrack, "前景切换");
uiFrontChangeTrack.imgPosType = 1;
// 创建背景切换
var uiBackChangeTrack = timelineAsset.CreateTrack<UIImageSetActionTrack>(UIGroupTrack, "背景切换");
uiBackChangeTrack.imgPosType = 0;
}
// 创建通用轨道
var commonGroupTrack = timelineAsset.CreateTrack<GroupTrack>(null, "通用");
{
// 创建通用轨道
var commonActionTrack = timelineAsset.CreateTrack<CommonActionTrack>(commonGroupTrack, "通用1");
}
}
private static float _ReadStartMotionLength(string motionName)
{
var motionConfig = EditorTableManager.instance.tables.MotionMapperConfig.Get(motionName);
if (motionConfig == null)
{
Debug.LogError("无效的动作名字:" + motionName);
return 1f;
}
var motionPath = motionConfig.MotionFullPath;
var loadDatas = AssetDatabase.LoadAllAssetsAtPath(motionPath);
AnimationClip clipStart = null;
for (int i = 0; i < loadDatas.Length; i++)
{
var loadData = loadDatas[i];
if (loadData is AnimationClip loadClip)
{
if (loadClip.name.EndsWith("_start"))
{
clipStart = loadClip;
}
}
}
if (clipStart == null)
{
return 1f;
}
return clipStart.length;
}
private static float _CreatePlayers(StoryExcelBlockData block,
float currentTime)
{
var totalTime = SampleTime;
var blockMsg = block.blockMsg;
var msgTime = blockMsg == null ? 0f : blockMsg.Length / 8f;
var playerDatas = block.playerDatas;
for (var i = 0; i < playerDatas.Count; i++)
{
var playerData = playerDatas[i];
// 读取startAnim
playerData = _SearchPlayerData(playerDatas, playerData.playerId);
var motionName = playerData.motionName;
var startMotionLength = _ReadStartMotionLength(motionName);
totalTime = Mathf.Max(totalTime, startMotionLength, msgTime);
if (playerData.isTalk)
{
_CreatePlayerTalks(PlayerTalkTrackDic[playerData.playerId], playerData, currentTime);
}
_CreatePlayerEmojis(PlayerEmojiActionTrackDic[playerData.playerId], playerData, currentTime,
totalTime);
}
// 所有角色都会创建位置轨道
var playerList = PlayerDataList;
for (var i = 0; i < playerList.Count; i++)
{
var playerData = playerList[i];
// _CreatePlayerPos(PlayerPosTrackDic[playerData.playerId], playerData, currentTime, totalTime);
// _CreatePlayerRot(PlayerRotTrackDic[playerData.playerId], playerData, currentTime, totalTime);
// _CreateWeaponHides(WeaponHideActionTrackDic[playerData.playerId], playerData, currentTime, totalTime);
playerData = _SearchPlayerData(playerDatas, playerData.playerId);
_CreatePlayerActions(PlayerActionTrackDic[playerData.playerId], playerData, currentTime, totalTime);
_CreateSetNameClips(playerData, currentTime);
}
return totalTime;
}
private static void _CreateSetNameClips(StoryExcelPlayerData playerData,
float currentTime)
{
var cacheData = PlayerClipDic[playerData.playerId];
var track = SetNameActionTrackDic[playerData.playerId];
var needCreate = false;
if (string.IsNullOrEmpty(cacheData.name))
{
// 创建新的
cacheData.name = playerData.playerName;
needCreate = true;
}
else
{
if (cacheData.name != playerData.playerName)
{
cacheData.name = playerData.playerName;
needCreate = true;
}
}
if (needCreate)
{
var clip = track.CreateClip<SetNameActionClip>();
clip.start = currentTime;
clip.duration = 1f;
var setNameActionClip = clip.asset as SetNameActionClip;
if (setNameActionClip == null)
{
return;
}
setNameActionClip.setName = playerData.playerName;
}
}
private static StoryExcelPlayerData _SearchPlayerData(List<StoryExcelPlayerData> searchList,
int playerId)
{
for (int i = 0; i < searchList.Count; i++)
{
var searchData = searchList[i];
if (searchData.playerId == playerId)
{
return searchData;
}
}
return new StoryExcelPlayerData()
{
playerId = playerId
};
}
private static void _CreateWeaponHides(WeaponHideActionTrack track,
StoryExcelPlayerData playerData,
float currentTime,
float during)
{
var clip = track.CreateClip<WeaponHideActionClip>();
clip.start = currentTime;
clip.duration = during;
var weaponHideActionClip = clip.asset as WeaponHideActionClip;
if (weaponHideActionClip == null)
{
return;
}
weaponHideActionClip.playerId = playerData.playerId;
}
private static void _CreatePlayerTalks(PlayerTalkActionTrack track,
StoryExcelPlayerData playerData,
float currentTime)
{
var clip = track.CreateClip<PlayerTalkActionClip>();
clip.start = currentTime;
}
private static void _CreatePlayerEmojis(PlayerEmojiActionTrack track,
StoryExcelPlayerData playerData,
float currentTime,
float during)
{
var emojiNames = playerData.faceNames;
var splitDuring = during / emojiNames.Count;
for (int i = 0; i < emojiNames.Count; i++)
{
var emojiName = emojiNames[i];
_CreatePlayerEmoji(track, playerData, currentTime, emojiName, splitDuring);
currentTime += splitDuring;
}
}
private static void _CreatePlayerEmoji(PlayerEmojiActionTrack track,
StoryExcelPlayerData playerData,
float currentTime,
string emojiName,
float during)
{
var clip = track.CreateClip<PlayerEmojiActionClip>();
clip.start = currentTime;
clip.duration = during;
var playerEmojiActionClip = clip.asset as PlayerEmojiActionClip;
if (playerEmojiActionClip == null)
{
return;
}
playerEmojiActionClip.playerId = playerData.playerId;
playerEmojiActionClip.emojiName = emojiName;
var emojiConfig = EditorTableManager.instance.tables.EmojiMapperConfig.Get(emojiName);
if (emojiConfig == null)
{
Debug.LogError("无效的表情名字:" + emojiName);
return;
}
playerEmojiActionClip.emojiId = emojiConfig.EmojiId;
}
private static void _CreatePlayerPos(PlayerPosActionTrack track,
StoryExcelPlayerData playerData,
float currentTime,
float during)
{
var clip = track.CreateClip<PlayerPosActionClip>();
var playerPosActionClip = clip.asset as PlayerPosActionClip;
if (playerPosActionClip == null)
{
return;
}
clip.start = currentTime;
clip.duration = during;
}
private static void _CreatePlayerRot(PlayerRotActionTrack track,
StoryExcelPlayerData playerData,
float currentTime,
float during)
{
var clip = track.CreateClip<PlayerRotActionClip>();
var playerRotActionClip = clip.asset as PlayerRotActionClip;
if (playerRotActionClip == null)
{
return;
}
playerRotActionClip.playerId = playerData.playerId;
clip.start = currentTime;
clip.duration = during;
}
private static void _CreatePlayerActions(AnimationTrack track,
StoryExcelPlayerData playerData,
double currentTime,
double during)
{
var cacheData = PlayerClipDic[playerData.playerId];
var motionName = playerData.motionName;
if (string.IsNullOrEmpty(motionName))
{
// 如果之前有,则把之前的loop拉长,end后移
if (cacheData.clipLoop != null
&& cacheData.clipEnd != null)
{
cacheData.clipLoop.duration += during;
cacheData.clipEnd.start = currentTime + during;
}
return;
}
var motionConfig = EditorTableManager.instance.tables.MotionMapperConfig.Get(motionName);
if (motionConfig == null)
{
Debug.LogError("无效的动作名字:" + motionName);
return;
}
var motionPath = motionConfig.MotionFullPath;
var loadDatas = AssetDatabase.LoadAllAssetsAtPath(motionPath);
AnimationClip clipStart = null, clipLoop = null, clipEnd = null;
for (int i = 0; i < loadDatas.Length; i++)
{
var loadData = loadDatas[i];
if (loadData is AnimationClip loadClip)
{
if (loadClip.name.EndsWith("_start"))
{
clipStart = loadClip;
}
if (loadClip.name.EndsWith("_loop"))
{
clipLoop = loadClip;
}
if (loadClip.name.EndsWith("_end"))
{
clipEnd = loadClip;
}
}
}
var workTime = currentTime;
var passTime = 0f;
if (clipStart != null)
{
var createClip = track.CreateClip(clipStart);
createClip.start = workTime;
if (cacheData.clipEnd != null)
{
createClip.start = cacheData.clipEnd.start;
}
createClip.duration = clipStart.length;
workTime += clipStart.length;
passTime += clipStart.length;
cacheData.clipStart = createClip;
}
else
{
Debug.Log("未找到start动作:" + motionName);
cacheData.clipStart = null;
}
if (clipLoop != null)
{
var createClip = track.CreateClip(clipLoop);
createClip.start = workTime;
createClip.duration = during - passTime;
cacheData.clipLoop = createClip;
}
else
{
Debug.Log("未找到loop动作:" + motionName);
cacheData.clipLoop = null;
}
if (clipEnd != null)
{
var createClip = track.CreateClip(clipEnd);
createClip.start = currentTime + during;
createClip.duration = clipEnd.length;
cacheData.clipEnd = createClip;
}
else
{
Debug.Log("未找到end动作:" + motionName);
cacheData.clipEnd = null;
}
}
private static float _CreatePlayerAction(AnimationTrack track,
StoryExcelPlayerData playerData,
float currentTime,
string motionName)
{
var clip = track.CreateClip<PlayerActionClip>();
var playerActionClip = clip.asset as PlayerActionClip;
if (playerActionClip == null)
{
return 0f;
}
playerActionClip.playerId = playerData.playerId;
playerActionClip.playerName = playerData.playerName;
playerActionClip.motionName = motionName;
clip.start = currentTime;
var motionConfig = EditorTableManager.instance.tables.MotionMapperConfig.Get(motionName);
if (motionConfig == null)
{
Debug.LogError("无效的动作名字:" + motionName);
return 0f;
}
var motionPath = motionConfig.MotionFullPath;
var motion = AssetDatabase.LoadAssetAtPath<AnimationClip>(motionPath);
if (motion == null)
{
Debug.LogError("无效的动作路径:" + motionPath);
return 0f;
}
playerActionClip.motionClip = motion;
clip.duration = motion.length;
return motion.length;
}
private static void _CreateSceneSwitch(float currentTime,
float during)
{
var clip = _sceneSwitchTrack.CreateClip<SceneSwitchActionClip>();
clip.start = currentTime;
clip.duration = during;
var sceneSwitchActionClip = clip.asset as SceneSwitchActionClip;
if (sceneSwitchActionClip == null)
{
return;
}
sceneSwitchActionClip.showIndex = 0;
}
private static void _CreateBlockChoice(float currentTime,
float during)
{
var clip = _blockChoiceTrack.CreateClip<BlockChoiceActionClip>();
clip.start = currentTime;
clip.duration = during;
var blockChoiceActionClip = clip.asset as BlockChoiceActionClip;
if (blockChoiceActionClip == null)
{
return;
}
blockChoiceActionClip.choiceMsgs = Array.Empty<string>();
blockChoiceActionClip.choiceBlockIds = Array.Empty<int>();
}
private static void _CreateMsgAction(MsgActionTrack blockLine,
StoryExcelBlockData block,
float currentTime,
float during)
{
var clip = blockLine.CreateClip<MsgActionClip>();
var msgActionClip = clip.asset as MsgActionClip;
if (msgActionClip == null)
{
return;
}
msgActionClip.blockId = block.blockId;
msgActionClip.msg = block.blockMsg;
clip.start = currentTime;
clip.duration = during;
}
public static TrackAsset SearchTrack(TimelineAsset timeline,
PlayableAsset clip)
{
var allTracks = timeline.GetOutputTracks();
foreach (var track in allTracks)
{
var clips = track.GetClips();
foreach (var trackClip in clips)
{
if (trackClip.asset == clip)
{
return track;
}
}
}
return null;
}
public static TimelineClip SearchBeforeClip(TrackAsset track,
PlayableAsset clip)
{
var clips = track.GetClips().ToArray();
for (int i = 0; i < clips.Length; i++)
{
var trackClip = clips[i];
if (trackClip.asset == clip)
{
if (i == 0)
{
return null;
}
return clips[i - 1];
}
}
return null;
}
public static TimelineClip SeachBehindClip(TrackAsset track,
PlayableAsset clip)
{
var clips = track.GetClips().ToArray();
for (int i = 0; i < clips.Length; i++)
{
var trackClip = clips[i];
if (trackClip.asset == clip)
{
if (i == clips.Length - 1)
{
return null;
}
var nextIndex = i + 1;
if (nextIndex >= clips.Length)
{
return null;
}
return clips[nextIndex];
}
}
return null;
}
private static void _CreatePlayerAudioTrack()
{
}
private static void _CreateAudioTrack(TimelineAsset timelineAsset)
{
var audioActionTrack = timelineAsset.CreateTrack<AudioActionTrack>();
//AudioActionTrackDic.Add(audioActionTrack);
}
private static void _CreateAudioClip()
{
}
}
}