382 lines
14 KiB
C#
382 lines
14 KiB
C#
|
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<CinematicObj> ObjList = new List<CinematicObj>();
|
|||
|
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<CinematicObj>();
|
|||
|
FrameRate = ReadInt();
|
|||
|
EndRead();
|
|||
|
}
|
|||
|
|
|||
|
//保存前,处理数据,做一些事先计算
|
|||
|
public void HandleDataBeforeSave()
|
|||
|
{
|
|||
|
for(int i = 0; i < ObjList.Count; ++i)
|
|||
|
{
|
|||
|
var keyframeList = ObjList[i].KeyframeList;
|
|||
|
List<CinematicKeyframe> transEventDataList = new List<CinematicKeyframe>();
|
|||
|
List<Vector3> positionList = new List<Vector3>();
|
|||
|
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<CinematicKeyframe> 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<CinematicObj> 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<string, object> 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();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 将自定义数据类型转换成基础类型保存,便于跨项目使用
|
|||
|
/// </summary>
|
|||
|
/// <param name="fileName"></param>
|
|||
|
/// <param name="ObjList"></param>
|
|||
|
/// <param name="frameRate"></param>
|
|||
|
public static void SerializeEx(string fileName, List<CinematicObj> 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);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 读取基础数据类型,然后转换成自定义类型
|
|||
|
/// </summary>
|
|||
|
/// <param name="fileName"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public static CinematicSerialize DeserializeEx(string fileName)
|
|||
|
{
|
|||
|
string path = _rootPath + "/" + fileName + _extension;
|
|||
|
Dictionary<string, object> 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<string, object>;
|
|||
|
cs = cs.Deserialize(data) as CinematicSerialize;
|
|||
|
}
|
|||
|
catch (Exception)
|
|||
|
{
|
|||
|
cs.ObjList = new List<CinematicObj>();
|
|||
|
}
|
|||
|
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<CinematicObj> 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<CinematicObj>();
|
|||
|
}
|
|||
|
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<string> GetAllCinematicFiles()
|
|||
|
{
|
|||
|
string path = _rootPath;
|
|||
|
List<string> retList = new List<string>();
|
|||
|
string[] fileArray = Directory.GetFiles(path, "*" + _extension);
|
|||
|
for(int i = 0; i < fileArray.Length; ++i)
|
|||
|
{
|
|||
|
retList.Add(Path.GetFileNameWithoutExtension(fileArray[i]));
|
|||
|
}
|
|||
|
|
|||
|
return retList;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|