using UnityEngine; using System.Collections.Generic; using System.Runtime.Serialization.Formatters.Binary; using System.IO; using System; namespace Thousandto.Cinematic { //剧情对象序列化,以及对clip的增删改 [Serializable] public class CinematicSerialize : ICinematic { public List ObjList = new List(); public int FrameRate = 30; public CinematicSerialize() { } public void WriteAll(string clipName) { //事先计算一些数据保存下来 HandleDataBeforeSave(); PrepareWrite(clipName); WriteList(ObjList); WriteInt(FrameRate); EndWrite(); } public void ReadAll(string clipName) { PrepareRead(clipName); ObjList = ReadList(); FrameRate = ReadInt(); EndRead(); } //保存前,处理数据,做一些事先计算 public void HandleDataBeforeSave() { for(int i = 0; i < ObjList.Count; ++i) { var keyframeList = ObjList[i].KeyframeList; List transEventDataList = new List(); List positionList = new List(); for(int m = 0; m < keyframeList.Count; ++m) { var eventDataList = keyframeList[m].EventData; CinematicEventData data = null; bool hasSyncEvent = false; for(int n = 0; n < eventDataList.Count; ++n) { var oneEvent = eventDataList[n]; if(oneEvent.EventTypeEx == KeyFrameEvent.坐标变换) { keyframeList[m].TransformEvent = oneEvent; keyframeList[m].TransPercentageInCurve = 0; transEventDataList.Add(keyframeList[m]); //positionList.Add(oneEvent.Position); data = oneEvent; } //需要同步的时候,忽略当前关键帧的位移事件 if (oneEvent.EventTypeEx == KeyFrameEvent.同步到本地相机坐标 || oneEvent.EventTypeEx == KeyFrameEvent.同步到本地角色坐标) { hasSyncEvent = true; break; } } if(!hasSyncEvent && data != null) positionList.Add(data.Position); } if (positionList.Count > 1) { ObjList[i].PathV3Array = PathControlPointGenerator(positionList.ToArray()); CalcPercentageEachPath(positionList.ToArray(), transEventDataList); } else { ObjList[i].PathV3Array = positionList.ToArray(); } } } //andeeee from the Unity forum's steller Catmull-Rom class ( http://forum.unity3d.com/viewtopic.php?p=218400#218400 ): public static Vector3 Interp(Vector3[] pts, float t) { if (pts.Length == 1) return pts[0]; int numSections = pts.Length - 3; int currPt = Mathf.Min(Mathf.FloorToInt(t * (float)numSections), numSections - 1); float u = t * (float)numSections - (float)currPt; Vector3 a = pts[currPt]; Vector3 b = pts[currPt + 1]; Vector3 c = pts[currPt + 2]; Vector3 d = pts[currPt + 3]; return .5f * ( (-a + 3f * b - 3f * c + d) * (u * u * u) + (2f * a - 5f * b + 4f * c - d) * (u * u) + (-a + c) * u + 2f * b ); } private static void CalcPercentageEachPath(Vector3[] path, List keyFrameListWithTransEvent) { if (path.Length < 2) return; Vector3[] vector3s = PathControlPointGenerator(path); //Line Draw: Vector3 prevPt = Interp(vector3s, 0); int SmoothAmount = path.Length * 100; int index = 1; float allDistance = 0; for (int i = 1; i <= SmoothAmount; i++) { float pm = (float)i / SmoothAmount; Vector3 currPt = Interp(vector3s, pm); //与上一个差值点的距离 float disPrev = Vector3.Distance(currPt, prevPt); allDistance += disPrev; if (currPt.x == path[index].x && currPt.y == path[index].y && currPt.z == path[index].z) { keyFrameListWithTransEvent[index].TransPercentageInCurve = pm; index++; } else { int tempIndex = index; //这里写成循环,是避免下一个关键帧和当前关键帧在同一个位置 for(; tempIndex < path.Length; ++tempIndex) { float keyDis = Vector3.Distance(currPt, path[tempIndex]); if (keyDis <= disPrev) { float curPm = pm * (allDistance + keyDis) / allDistance; keyFrameListWithTransEvent[index].TransPercentageInCurve = curPm; index = tempIndex + 1; } else break; } } if (index >= path.Length) { keyFrameListWithTransEvent[index - 1].TransPercentageInCurve = 1; return; } prevPt = currPt; } } public static Vector3[] PathControlPointGenerator(Vector3[] path) { Vector3[] suppliedPath; Vector3[] vector3s; //create and store path points: suppliedPath = path; //populate calculate path; int offset = 2; vector3s = new Vector3[suppliedPath.Length + offset]; Array.Copy(suppliedPath, 0, vector3s, 1, suppliedPath.Length); //populate start and end control points: //vector3s[0] = vector3s[1] - vector3s[2]; vector3s[0] = vector3s[1] + (vector3s[1] - vector3s[2]); vector3s[vector3s.Length - 1] = vector3s[vector3s.Length - 2] + (vector3s[vector3s.Length - 2] - vector3s[vector3s.Length - 3]); //is this a closed, continuous loop? yes? well then so let's make a continuous Catmull-Rom spline! if (vector3s[1] == vector3s[vector3s.Length - 2]) { Vector3[] tmpLoopSpline = new Vector3[vector3s.Length]; Array.Copy(vector3s, tmpLoopSpline, vector3s.Length); tmpLoopSpline[0] = tmpLoopSpline[tmpLoopSpline.Length - 3]; tmpLoopSpline[tmpLoopSpline.Length - 1] = tmpLoopSpline[2]; vector3s = new Vector3[tmpLoopSpline.Length]; Array.Copy(tmpLoopSpline, vector3s, tmpLoopSpline.Length); } return (vector3s); } public static void BinarySerialize(string clipName, List ObjList, int frameRate = 30) { if (ObjList == null || ObjList.Count == 0) return; CinematicSerialize cs = new CinematicSerialize(); cs.FrameRate = frameRate; cs.ObjList = ObjList; cs.WriteAll(clipName); } public static CinematicSerialize BinaryDeserialize(string clipName) { CinematicSerialize cs = new CinematicSerialize(); try { cs.ReadAll(clipName); } catch (Exception) { UnityEngine.Debug.LogWarning("Load cinematic fail, may be not exist! clipName = " + clipName); cs.EndRead(); } return cs; } public static void SerializeEx(string fileName, CinematicSerialize cs) { if (cs.ObjList == null || cs.ObjList.Count == 0) return; string path = _rootPath + "/" + fileName + _extension; Dictionary data = cs.Serialize(); if (File.Exists(path)) File.Delete(path); FileStream fileStream = File.OpenWrite(path); BinaryFormatter b = new BinaryFormatter(); b.Serialize(fileStream, data); fileStream.Close(); } /// /// 将自定义数据类型转换成基础类型保存,便于跨项目使用 /// /// /// /// public static void SerializeEx(string fileName, List ObjList, int frameRate = 30) { if (ObjList == null || ObjList.Count == 0) return; CinematicSerialize cs = new CinematicSerialize(); cs.FrameRate = frameRate; cs.ObjList = ObjList; string path = _rootPath + "/" + fileName + _extension; SerializeEx(fileName, cs); } /// /// 读取基础数据类型,然后转换成自定义类型 /// /// /// public static CinematicSerialize DeserializeEx(string fileName) { string path = _rootPath + "/" + fileName + _extension; Dictionary data; FileStream fileStream = null; CinematicSerialize cs = new CinematicSerialize(); try { fileStream = new FileStream(path, FileMode.Open); BinaryFormatter b = new BinaryFormatter(); data = b.Deserialize(fileStream) as Dictionary; cs = cs.Deserialize(data) as CinematicSerialize; } catch (Exception) { cs.ObjList = new List(); } finally { if (fileStream != null) fileStream.Close(); } for(int i = 0; i < cs.ObjList.Count; ++i) { cs.ObjList[i].SortKeyframe(); } return cs; } public static void Serialize(string fileName, CinematicSerialize cs) { if (cs.ObjList == null || cs.ObjList.Count == 0) return; string path = _rootPath + "/" + fileName + _extension; FileStream fileStream = new FileStream(path, FileMode.OpenOrCreate); BinaryFormatter b = new BinaryFormatter(); b.Serialize(fileStream, cs); fileStream.Close(); } public static void Serialize(string fileName, List ObjList, int frameRate = 30) { if (ObjList == null || ObjList.Count == 0) return; CinematicSerialize cs = new CinematicSerialize(); cs.FrameRate = frameRate; cs.ObjList = ObjList; string path = _rootPath + "/" + fileName + _extension; Serialize(fileName, cs); } public static CinematicSerialize Deserialize(string fileName) { string path = _rootPath + "/" + fileName + _extension; CinematicSerialize ret = null; FileStream fileStream = null; try { fileStream = new FileStream(path, FileMode.OpenOrCreate); BinaryFormatter b = new BinaryFormatter(); ret = b.Deserialize(fileStream) as CinematicSerialize; } catch(Exception) { } finally { if(ret == null) { ret = new CinematicSerialize(); ret.ObjList = new List(); } if (fileStream != null) fileStream.Close(); } return ret; } public static void CreateEmptyFile(string fileName) { string path = _rootPath + "/" + fileName + _extension; if(!File.Exists(path)) { File.Create(path).Close(); } } public static void RenameFile(string fileName, string newFileName) { string oldPath = _rootPath + "/" + fileName + _extension; string newPath = _rootPath + "/" + newFileName + _extension; if (!File.Exists(oldPath)) return; if (fileName == newFileName) return; File.Copy(oldPath, newPath, true); File.Delete(oldPath); } public static void DeleteFile(string fileName) { string path = _rootPath + "/" + fileName + _extension; if(File.Exists(path)) { File.Delete(path); } } //获取所有剧情文件 public static List GetAllCinematicFiles() { string path = _rootPath; List retList = new List(); string[] fileArray = Directory.GetFiles(path, "*" + _extension); for(int i = 0; i < fileArray.Length; ++i) { retList.Add(Path.GetFileNameWithoutExtension(fileArray[i])); } return retList; } } }