using UnityEngine; using Thousandto.Core.Asset; using System; using System.Collections.Generic; namespace Thousandto.Cinematic { //剧情对象管理,流程管理 public class CinematicObjManager { private CinematicObj _cObj; private FGameObjectVFXBox _vfxbox = new FGameObjectVFXBox(); //上一个含有位移事件的关键帧 private CinematicKeyframe _preTransKeyframe; //上一个含有动画世界的关键帧 private CinematicKeyframe _preAnimKeyframe; //上一个同步事件关键帧 private CinematicKeyframe _preKeyframeWithSyncEvent; private int _curFrame; private float _framesAfterPreKey; public static bool IsEditorPreview; //剧情播放前的场景音效 private string _lastSceneMusicName = null; //正在播放的音效 private int _playingSpeech = -1; private int _playingSfx = -1; public void Prepare(CinematicObj obj) { _curFrame = -1; _cObj = obj; _preTransKeyframe = null; _cObj.Prepare(); } public void Destory() { _vfxbox.Clear(); if (_cObj != null) _cObj.Destory(); StopCinematicAudio(); } public int GetCurFrame() { return _curFrame; } public Dictionary GetVfxDatas() { Dictionary vfxResDict = new Dictionary(); var keyframeList = _cObj.KeyframeList; for(int i = 0; i < keyframeList.Count; ++i) { int keyframe = keyframeList[i].Keyframe; var eventDatas = keyframeList[i].EventData; for(int j = 0; j < eventDatas.Count; ++j) { if(eventDatas[j].EventTypeEx == KeyFrameEvent.播放场景特效 || eventDatas[j].EventTypeEx == KeyFrameEvent.播放绑定特效) { if (vfxResDict.ContainsKey(keyframe)) vfxResDict[keyframe] = eventDatas[j].Param; else vfxResDict.Add(keyframe, eventDatas[j].Param); } } } return vfxResDict; } public bool IsEnd(int frameNum) { return _cObj == null || _cObj.IsEndFrame(frameNum); } //frameNum 当前第几帧 public void Update(int frameNum) { if (_cObj == null) return; Update(); var curFrameData = _cObj.GetKeyframeData(frameNum); if(curFrameData != null) { //上一个同步帧 if (curFrameData.HasSyncTrans) _preKeyframeWithSyncEvent = curFrameData; if (curFrameData.TransformEvent != null) { //直到走到有位移的帧,同步帧置空 _preTransKeyframe = curFrameData; _preKeyframeWithSyncEvent = null; } if (curFrameData.AnimEvent != null) _preAnimKeyframe = curFrameData; } else if (frameNum < _curFrame) { _preTransKeyframe = _cObj.GetPreTransKeyframeData(frameNum); _preAnimKeyframe = _cObj.GetPreAnimKeyframeData(frameNum); _preKeyframeWithSyncEvent = null; } _curFrame = frameNum; if (_preTransKeyframe == null && curFrameData == null && _preAnimKeyframe == null && _preKeyframeWithSyncEvent == null) return; //处理当前帧数据 HandleCurFrameData(curFrameData); //如果当前帧没数据,则处理前后帧的位移 if (_preTransKeyframe != null) { //当前帧与上一个关键帧的偏移量 _framesAfterPreKey = frameNum - _preTransKeyframe.Keyframe; HandleTransform(_preTransKeyframe, _framesAfterPreKey); } if (_preAnimKeyframe != null) { HandleAnimation(_preAnimKeyframe, _curFrame); } //开始时候同步位置 if(_preKeyframeWithSyncEvent != null && _preKeyframeWithSyncEvent.HasSyncTrans && _preKeyframeWithSyncEvent.PreEventData == null) { float durationFrame = _preKeyframeWithSyncEvent.NextTransKeyframe.Keyframe - _preKeyframeWithSyncEvent.Keyframe; float deltaFrame = frameNum - _preKeyframeWithSyncEvent.Keyframe; _cObj.SetTransform(_preKeyframeWithSyncEvent.TransformEvent, _preKeyframeWithSyncEvent.NextTransKeyframe.TransformEvent, durationFrame, deltaFrame); } //结束前同步位置 if (_preTransKeyframe != null && _preTransKeyframe.NextKeyframe != null && _preTransKeyframe.NextKeyframe.HasSyncTrans) { //if (_preTransKeyframe.NextKeyframe.TransformEvent == null) { _preTransKeyframe.NextKeyframe.NeedSetPositionNow = false; PreHandleSyncEvent(_preTransKeyframe.NextKeyframe); } float durationFrame = _preTransKeyframe.NextKeyframe.Keyframe - _preTransKeyframe.Keyframe; float deltaFrame = frameNum - _preTransKeyframe.Keyframe; //上一个位移帧的位移,移动到当前帧上面存放的需要同步的位置 _cObj.SetTransform(_preTransKeyframe.TransformEvent, _preTransKeyframe.NextKeyframe.TransformEvent, durationFrame, deltaFrame); } } //小于1帧,需要平滑设置位移和动画效果 public void UpdateSmooth(float deltaFrameLessThanOne) { //如果当前帧没数据,则处理前后帧的位移 if (_preTransKeyframe != null) { //当前帧与上一个关键帧的偏移量 _framesAfterPreKey = _curFrame + deltaFrameLessThanOne - _preTransKeyframe.Keyframe; HandleTransform(_preTransKeyframe, _framesAfterPreKey); } if (_preAnimKeyframe != null) { HandleAnimation(_preAnimKeyframe, _curFrame + deltaFrameLessThanOne); } } public void Update() { if (_cObj != null) _cObj.Update(); _vfxbox.Update(Time.deltaTime); } private void HandleCurFrameData(CinematicKeyframe curFrame) { for (int i = 0; curFrame != null && i < curFrame.EventData.Count; ++i) { curFrame.EventData[i].ParentGo = _cObj.GameObject; curFrame.EventData[i].CinematicObj = _cObj; //跟随相机事件后续的事件都不做处理 if (_cObj.IsFollowCamera && curFrame.EventData[i].EventTypeEx != KeyFrameEvent.本地相机同步到剧情坐标 && curFrame.EventData[i].EventTypeEx != KeyFrameEvent.同步到本地角色坐标 && curFrame.EventData[i].EventTypeEx != KeyFrameEvent.同步到本地相机坐标 && curFrame.EventData[i].EventTypeEx != KeyFrameEvent.本地角色同步到剧情坐标) continue; if (curFrame.EventData[i].EventTypeEx == KeyFrameEvent.相机跟随) _cObj.IsFollowCamera = true; switch (curFrame.EventData[i].EventTypeEx) { case KeyFrameEvent.加载模型: if (!HandleEvent(KeyFrameEvent.加载模型, curFrame.EventData[i])) _cObj.LoadPrefab(curFrame.EventData[i].Param, curFrame.EventData[i].EditorParam); break; case KeyFrameEvent.坐标变换: _cObj.SetTransform(curFrame.EventData[i]); _cObj.ResetTransState(); //设置下一个同步事件 //if(curFrame.NextTransKeyframe != null && curFrame.NextTransKeyframe.HasSyncTrans) //{ // if(_cObj.IsCamera) // { // HandleEvent(KeyFrameEvent.同步到本地相机坐标, curFrame.NextTransKeyframe); // } // else // { // HandleEvent(KeyFrameEvent.同步到本地角色坐标, curFrame.NextTransKeyframe); // } //} break; case KeyFrameEvent.播放场景特效: { var vfx = new FGameObjectVFX(); vfx.Initialize(curFrame.EventData[i].Param.Replace("\\", "/"), true, false); vfx.SetParent(_cObj.GameObject.transform, true); vfx.SetLayer(LayerUtils.Default); vfx.Play(1); _vfxbox.Add(vfx); //Debug.LogError("播放场景特效:" + _cObj.GameObject.name + ";;;" + curFrame.EventData[i].Param); } break; case KeyFrameEvent.播放动作: if (!HandleEvent(KeyFrameEvent.播放动作, curFrame.EventData[i])) { _cObj.PlayAnimation(curFrame.EventData[i]); //UnityEngine.Debug.LogError("handle KeyFrameEvent.PlayAnim fail, please check!!!"); } break; case KeyFrameEvent.播放绑定特效: { var vfx = new FGameObjectVFX(); vfx.Initialize(curFrame.EventData[i].Param.Replace("\\", "/"), true, false); vfx.SetParent(_cObj.GameObject.transform, true); if(_cObj.IsCamera) { var cam = _cObj.GameObject.GetComponent(); if(cam != null) Plugins.Common.UniScene.BaseScene.FixCameraVfx(vfx, cam); } vfx.SetLayer(LayerUtils.Default); vfx.Play(1); _vfxbox.Add(vfx); //Debug.LogError("播放绑定特效:" + _cObj.GameObject.name+ ";;;"+ curFrame.EventData[i].Param); } //VFXPlayer.SyncPlayOnTransform(curFrame.EventData[i].Param, _cObj.GameObject.transform, LayerUtils.UIRoleStory); break; case KeyFrameEvent.播放场景音效: if(System.Enum.IsDefined(typeof(AudioTypeCode), curFrame.EventData[i].EditorParam)) { var type = (AudioTypeCode)System.Enum.Parse(typeof(AudioTypeCode), curFrame.EventData[i].EditorParam); PlayAudio(type, null, curFrame.EventData[i].Param); } break; case KeyFrameEvent.播放定点音效: if (System.Enum.IsDefined(typeof(AudioTypeCode), curFrame.EventData[i].EditorParam)) { var type = (AudioTypeCode)System.Enum.Parse(typeof(AudioTypeCode), curFrame.EventData[i].EditorParam); PlayAudio(type, _cObj.GameObject, curFrame.EventData[i].Param); } break; case KeyFrameEvent.暂停: float pauseFrame; if(float.TryParse(curFrame.EventData[i].Param, out pauseFrame)) { CinematicManager.PauseFrame = pauseFrame; } break; case KeyFrameEvent.设置隐藏或显示: //显示或隐藏 _cObj.GameObject.SetActive(!curFrame.EventData[i].IsLocalPlayer); break; case KeyFrameEvent.绑定坐骑: //绑定坐骑 if(!HandleEvent(KeyFrameEvent.绑定坐骑, curFrame.EventData[i])) UnityEngine.Debug.LogError("请先运行游戏才能显示绑定的坐骑"); break; case KeyFrameEvent.同步到本地相机坐标: //同步本地相机位置 if (!HandleEvent(KeyFrameEvent.同步到本地相机坐标, curFrame)) UnityEngine.Debug.LogError("请先运行游戏才能SyncLocalCameraTrans"); _preKeyframeWithSyncEvent = curFrame; _cObj.SetCameraVisiable(); break; case KeyFrameEvent.同步到本地角色坐标: //同步本地角色位置 if (!HandleEvent(KeyFrameEvent.同步到本地角色坐标, curFrame)) UnityEngine.Debug.LogError("请先运行游戏才能SyncLocalCameraTrans"); _preKeyframeWithSyncEvent = curFrame; break; case KeyFrameEvent.本地相机同步到剧情坐标: //同步本地角色位置到剧情 if (!HandleEvent(KeyFrameEvent.本地相机同步到剧情坐标, curFrame)) UnityEngine.Debug.LogError("请先运行游戏才能LocalCameraSyncTrans"); break; case KeyFrameEvent.本地角色同步到剧情坐标: //同步本地角色位置到剧情 if (!HandleEvent(KeyFrameEvent.本地角色同步到剧情坐标, curFrame)) UnityEngine.Debug.LogError("请先运行游戏才能LocalPlayerSyncTrans"); break; case KeyFrameEvent.慢动作: //时间缩放 float scale = 0; if(float.TryParse(curFrame.EventData[i].Param, out scale)) { if (scale <= 0) scale = 0.1f; if (scale >= 2) scale = 2.0f; CinematicManager.Instance.TimeScale = scale; Time.timeScale = scale; } break; case KeyFrameEvent.振动效果: OnCinematicShakeObj(curFrame.EventData[i]); break; case KeyFrameEvent.相机运动模糊效果: CameraBlurEffect(_cObj.GameObject, curFrame.EventData[i]); break; case KeyFrameEvent.相机渐黑效果: CameraBlackTransEffect(_cObj.GameObject, curFrame.EventData[i]); break; case KeyFrameEvent.场景物件: _cObj.LoadPrefabFromScene(curFrame.EventData[i].Param); break; //UI效果+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ case KeyFrameEvent.显示对话: case KeyFrameEvent.显示boss名字: case KeyFrameEvent.显示Texture: case KeyFrameEvent.清除Texture: case KeyFrameEvent.播放UI特效: case KeyFrameEvent.隐藏UI特效: case KeyFrameEvent.屏幕渐亮效果: case KeyFrameEvent.屏幕渐黑效果: case KeyFrameEvent.显示上下边框: case KeyFrameEvent.隐藏上下边框: case KeyFrameEvent.UI图片Alpha效果: case KeyFrameEvent.相机跟随: if (!HandleEvent(KeyFrameEvent.UI效果, curFrame.EventData[i])) UnityEngine.Debug.LogError("请先运行游戏才能显示Texture"); break; //------------------------------------------------------------------ default: HandleEvent(curFrame.EventData[i].EventTypeEx, curFrame.EventData[i]); break; } } } private void PlayAudio(AudioTypeCode type, GameObject target, string audioName) { switch(type) { case AudioTypeCode.Ambient: AudioPlayer.PlayAmbient(target, audioName); break; case AudioTypeCode.Music: { if (string.IsNullOrEmpty(_lastSceneMusicName)) _lastSceneMusicName = AudioPlayer.GetPlayingMusicName(); AudioPlayer.PlayMusic(audioName, true); } break; case AudioTypeCode.Sfx: _playingSfx = AudioPlayer.PlaySfx(target, audioName); break; case AudioTypeCode.Speech: _playingSpeech = AudioPlayer.PlaySpeech(target, audioName); break; case AudioTypeCode.UI: AudioPlayer.PlayUI(audioName); break; } } public void StopCinematicAudio() { AudioPlayer.ChannelManager.Stop(_playingSfx); AudioPlayer.ChannelManager.Stop(_playingSpeech); //恢复之前的背景音乐 if (!string.IsNullOrEmpty(_lastSceneMusicName)) AudioPlayer.PlayMusic(_lastSceneMusicName, true); _lastSceneMusicName = null; } //预处理同步事件,需要事先获取下一帧同步的位置,不然没有位移变换 private void PreHandleSyncEvent(CinematicKeyframe curFrame) { for(int i = 0; i < curFrame.EventData.Count; ++i) { var eventData = curFrame.EventData[i]; switch(eventData.EventTypeEx) { case KeyFrameEvent.同步到本地相机坐标: //同步本地相机位置 if (!HandleEvent(KeyFrameEvent.同步到本地相机坐标, curFrame)) UnityEngine.Debug.LogError("请先运行游戏才能SyncLocalCameraTrans"); _preKeyframeWithSyncEvent = curFrame; _cObj.SetCameraVisiable(); break; case KeyFrameEvent.同步到本地角色坐标: //同步本地角色位置 if (!HandleEvent(KeyFrameEvent.同步到本地角色坐标, curFrame)) UnityEngine.Debug.LogError("请先运行游戏才能SyncLocalCameraTrans"); _preKeyframeWithSyncEvent = curFrame; break; case KeyFrameEvent.本地相机同步到剧情坐标: //同步本地角色位置到剧情 if (!HandleEvent(KeyFrameEvent.本地相机同步到剧情坐标, curFrame)) UnityEngine.Debug.LogError("请先运行游戏才能LocalCameraSyncTrans"); break; case KeyFrameEvent.本地角色同步到剧情坐标: //同步本地角色位置到剧情 if (!HandleEvent(KeyFrameEvent.本地角色同步到剧情坐标, curFrame)) UnityEngine.Debug.LogError("请先运行游戏才能LocalPlayerSyncTrans"); break; } } } //处理事件 private bool HandleEvent(KeyFrameEvent eventType, ICinematic eventData) { bool ret = false; Func handler = CinematicManager.Instance.GetEventHandler(eventType); if (handler != null) { ret = handler(eventData); } return ret; } //处理位移 private void HandleTransform(CinematicKeyframe frameWithTransData, float frameDelta) { if (frameDelta == 0) return; if(frameWithTransData.TransformEvent == null) { for (int i = 0; frameWithTransData != null && i < frameWithTransData.EventData.Count; ++i) { if(frameWithTransData.EventData[i].EventTypeEx == KeyFrameEvent.坐标变换) { frameWithTransData.TransformEvent = frameWithTransData.EventData[i]; break; } } } if(frameWithTransData.TransformEvent != null) { var nextKeyframe = frameWithTransData.NextKeyframe; //下一个关键帧是同步帧,则不做曲线变换,直接跳过 if (nextKeyframe != null && nextKeyframe.HasSyncTrans) { //if(nextKeyframe.TransformEvent == null) PreHandleSyncEvent(nextKeyframe); return; } _cObj.SetTransformCurveTo(frameWithTransData.TransformEvent, frameWithTransData, frameWithTransData.Keyframe, frameDelta); } } //处理动作,逐帧播放的动作 private void HandleAnimation(CinematicKeyframe frameWithAnimData, float curFrame) { if(frameWithAnimData.AnimEvent == null) { for (int i = 0; frameWithAnimData != null && i < frameWithAnimData.EventData.Count; ++i) { switch (frameWithAnimData.EventData[i].EventTypeEx) { case KeyFrameEvent.播放动作: frameWithAnimData.AnimEvent = frameWithAnimData.EventData[i]; break; default: break; } } } _cObj.PlayAnimationByFrame(frameWithAnimData.AnimEvent, frameWithAnimData.Keyframe, curFrame); } //运动模糊效果 private void CameraBlurEffect(GameObject cameraGo, CinematicEventData eventData) { float duration = 0; float strenth = 3; if (!float.TryParse(eventData.Param, out duration)) { duration = 2.0f; } if (!float.TryParse(eventData.EditorParam, out strenth)) { strenth = 3f; } var blurScript = RequireComponent(_cObj.GameObject); blurScript.StartBlur(duration, strenth); } //相机渐黑效果 private void CameraBlackTransEffect(GameObject cameraGo, CinematicEventData eventData) { float duration = 0; if (!float.TryParse(eventData.Param, out duration)) { duration = 2.0f; } var script = RequireComponent(_cObj.GameObject); script.StartTrans(duration); } //振动效果 public bool OnCinematicShakeObj(CinematicEventData eventData) { float duration = 0; float strenth = 3; if (!float.TryParse(eventData.Param, out duration)) { duration = 2.0f; } if (!float.TryParse(eventData.EditorParam, out strenth)) { strenth = 3f; } var shakeScript = RequireComponent(eventData.ParentGo); shakeScript.StartShake(duration, strenth); return true; } private T RequireComponent(GameObject go) where T: MonoBehaviour { T script = go.GetComponent(); if (script == null) script = go.AddComponent(); return script; } } }