#region File Description //----------------------------------------------------------------------------- // SequenceGroupData.cs // // Microsoft XNA Community Game Platform // Copyright (C) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- #endregion #region Using Statements using System; using System.Collections.Generic; using System.Diagnostics; using System.Text; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Content; using Microsoft.Xna.Framework.Graphics; #endregion namespace SceneDataLibrary { /// /// This class manages the list of pattern groups displayed in a sequence. /// In Layout, sequence groups correspond to these pattern groups. /// The Update function sets the time forward and calculates coordinates /// or color values, and the calculation results are temporarily stored /// in pattern objects. /// These results can be used to display other display items by /// synchronizing them with sequence animations. /// However, attention is required when creating scenes by Layout, because /// it is not assumed that the same pattern object data is referred to /// in the same game scene. /// /// シーケンスで表示するパターングループのリストを保持します。 /// Layoutではシーケンスグループに相当します。 /// Update関数で、時刻を進め、座標や色情報の再計算を行います。 /// 計算結果はパターンオブジェクトデータに一時保存されますので、 /// その結果を用いて、他の表示物をシーケンスアニメーションに /// 同期させて表示させることも可能です。 /// ただし、同じゲームシーン内で同じパターンオブジェクトデータが /// 参照される状況は想定していないため、 /// Layoutでシーン作成する際は注意が必要です。 /// public class SequenceGroupData { #region Public Types //Animation interpolation type // //アニメーション補間タイプ public enum Interpolation { None = 0,//No interpolation //補間無し Linear,//Linear interpolation //線形補間 Spline,//Spline interpolation //スプライン補間 } public enum PlayStatus { Playing = 0,//Playing //再生中 Stop,//Stopped //停止中 LoopEnd,//In loop //ループ中 } #endregion #region Fields private int startFrame = 0;//Start frame for animation movement private int loopNumber = 0;//Maximum loop count private TimeSpan timeFrame = new TimeSpan(0, 0, 0);//Current frame private PlayStatus playStatus = PlayStatus.Stop;//Play status private long framePerSecond = 60;//The number of frames per second private int loopCount = 0;//Current loop count private int drawObjectId = -1;//Base object (Object to be drawn) private Interpolation interpolationType = Interpolation.None; private int splineParamT = 0;//Spline curve parameter T private int splineParamC = 0;//Spline curve parameter C private int splineParamB = 0;//Spline curve parameter B private float[] tcbParam = new float[4];//Spline calculation parameter //List of sequence objects private List objectList = new List(); #endregion #region Property /// /// Obtains and sets the start frame for animation movement. /// /// アニメーション動作の開始フレームを設定取得します。 /// public int StartFrame { get { return startFrame; } set { startFrame = value; } } /// /// Obtains and sets the maximum loop count. /// /// ループ数を設定取得します。 /// public int LoopNumber { get { return loopNumber; } set { loopNumber = value; } } /// /// Obtains and sets the animation interpolation type. /// public Interpolation InterpolationType { get { return interpolationType; } set { interpolationType = value; } } /// /// Obtains and sets the spline curve parameter T. /// /// スプライン補間用のパラメータを設定取得します。 /// public int SplineParamT { get { return splineParamT; } set { splineParamT = value; } } /// /// Obtains and sets the spline curve parameter C. /// /// スプライン補間用のパラメータを設定取得します。 /// public int SplineParamC { get { return splineParamC; } set { splineParamC = value; } } /// /// Obtains and sets the spline curve parameter B. /// /// スプライン補間用のパラメータを設定取得します。 /// public int SplineParamB { get { return splineParamB; } set { splineParamB = value; } } /// /// Obtains the list of sequence objects. /// /// スプライン補間用のパラメータを設定取得します。 /// public List SequenceObjectList { get { return objectList; } } /// /// If the sequence is stopped, returns true. /// /// シーケンスが停止中ならtrue /// public bool IsStop { get { return (PlayStatus.Stop == playStatus); } } /// /// If the sequence is in a loop, returns true. /// /// シーケンスがループ中ならtrue /// public bool IsLoopEnd { get { return (PlayStatus.LoopEnd == playStatus); } } #endregion /// /// Performs initialization. /// Converts TCB curve information to runtime format. /// /// 初期化します。 /// TCB曲線の情報を実行時の形式に変換します。 /// public void Init() { //Calculates the parameters for spline interpolation. // //スプライン補間用のパラメータの計算 tcbParam[0] = (1.0f - SplineParamT) * (1.0f + SplineParamC) * (1.0f + SplineParamB); tcbParam[1] = (1.0f - SplineParamT) * (1.0f - SplineParamC) * (1.0f - SplineParamB); tcbParam[2] = (1.0f - SplineParamT) * (1.0f - SplineParamC) * (1.0f + SplineParamB); tcbParam[3] = (1.0f - SplineParamT) * (1.0f + SplineParamC) * (1.0f - SplineParamB); } /// /// Sets the animation time forward. /// Updates the current time by adding the difference between /// the current time and the forwarded time. /// If the updated time exceeds the play time of the entire animation, /// consider whether to perform loop processes and determine the frames /// corrected if necessary. /// /// アニメーションの時間を進めます。 /// 現在の時刻にすすめる時間を加算して、現在の時刻を更新します。 /// アニメーション全体の長さより現在の時刻が上回った場合は /// ループの有無などを検討し、必要であれば補正したフレームを割り出します。 /// /// /// Current time /// /// 現在の時間 /// /// /// Time to be forwarded /// /// 進める時間 /// /// /// Specifies true in case of reverse play /// /// 逆再生の場合true /// /// /// Returns the time specified in the sequence object to be displayed /// /// 表示するシーケンスオブジェクトに設定された時間を返します /// public float Update(float playFrames, TimeSpan elapsedGameTime, bool reverse) { //Updates the time. // //時間の更新 if (reverse) timeFrame += elapsedGameTime; else timeFrame += elapsedGameTime; if (TimeSpan.Zero > timeFrame) timeFrame = TimeSpan.Zero; //Clears the play status. // //再生状態のクリア playStatus = PlayStatus.Playing; BEGIN_UPDATE: //Calculates the length of the entire sequence. // //シーケンス全体の長さを計算します。 int total = StartFrame; int length = StartFrame; foreach (SequenceObjectData sequenceObject in objectList) { length += sequenceObject.Frame; } //Checks whether the process proceeds to the end of the sequence. // //シーケンスの末端まで進んだかどうか確認します。 if (timeFrame.Ticks * framePerSecond / TimeSpan.TicksPerSecond >= length) { //When the process proceeds to the end of the sequence. // //末端まで進んでいる場合。 playStatus = PlayStatus.LoopEnd; //If the maximum loop count is less than 0, // there is no limitation on the loop count. // //ループ規定数が負の場合、無限にループします。 if (0 > LoopNumber) { //Sets the status of after-loop. // //ループした後の状態について設定します。 if (1 < objectList.Count) { timeFrame = new TimeSpan( ( (timeFrame.Ticks * framePerSecond / TimeSpan.TicksPerSecond) % length ) * TimeSpan.TicksPerSecond / framePerSecond); //Performs recalculation based on the corrected frame. // //補正されたフレームを元に再計算する goto BEGIN_UPDATE; } else { drawObjectId = 0; playStatus = PlayStatus.Stop; playFrames = 0.0f; } } else { //Updates the current loop count. //ループした回数を更新します。 loopCount += (int)( (timeFrame.Ticks * framePerSecond / TimeSpan.TicksPerSecond) / length); //Updates the time. // //時間の更新 timeFrame -= new TimeSpan( ( (timeFrame.Ticks * framePerSecond / TimeSpan.TicksPerSecond) % length) * TimeSpan.TicksPerSecond / framePerSecond); if (loopCount < LoopNumber) { //Performs recalculation based on the corrected frame. // //補正されたフレームを元に再計算する goto BEGIN_UPDATE; } playStatus = PlayStatus.Stop; } } else if (reverse && TimeSpan.Zero == timeFrame) { //In reverse play, stops playing when the frame becomes 0. // //逆再生で、フレームが0になった場合停止します。 drawObjectId = 0; playStatus = PlayStatus.Stop; playFrames = 0.0f; } else if (timeFrame.Ticks * framePerSecond / TimeSpan.TicksPerSecond >= StartFrame) { //When the process does not proceed to the end of animation, and //the frame is not yet 0 in reverse play. //(In other words, when in normal interpolation display operation.) //Other than these conditions, // //アニメーション末端まで進んでいない場合かつ //逆再生でフレーム0に至っていない場合。 //つまり、通常の補間表示の場合。 //これ以外の場合でも、 drawObjectId = -1; int i = 0; //searches sequence objects to be displayed. // //表示対象にするシーケンスオブジェクトを検索します。 foreach (SequenceObjectData data in objectList) { total += data.Frame; if (timeFrame.Ticks * framePerSecond / TimeSpan.TicksPerSecond < total) { //The object to be displayed is found. // //表示するオブジェクトを発見 drawObjectId = i; break; } i++; } //When the object to be displayed is found. // //表示するオブジェクトが見つかった場合 if (0 <= drawObjectId) { //Calculates the time in the sequence object to be displayed. // //表示するシーケンスオブジェクト内での時間を計算します。 if (0 == objectList[drawObjectId].Frame) { playFrames = 0.0f; } else { playFrames = ( (float)timeFrame.Ticks * (float)framePerSecond / (float)TimeSpan.TicksPerSecond - (float)(total - objectList[drawObjectId].Frame)) / (float)objectList[drawObjectId].Frame; } } } else { //Displays nothing. // //何も表示しない drawObjectId = -1; } // Once the frame has been set up, performs interpolation // for conversion information. // //フレームが確定したので、変換情報の補間処理を行います。 updateSeq(playFrames); return playFrames; } /// /// Performs interpolation based on the provided display frame. /// Begins by determining the sequence objects to be displayed. /// Then, if the interpolation type is Linear or Spline, performs /// conversion interpolation by using the information of neighboring /// sequence objects as needed. /// /// 与えられた表示フレームを元に補間処理を行います。 /// まず、表示すべきシーケンスオブジェクトを求め、 /// 補間タイプが線形、スプラインの場合は、 /// 必要に応じて近隣のシーケンスオブジェクトの情報を用いて /// 変換の補間を行います。 /// /// /// Time in the current sequence object to be displayed /// /// 現在表示すべきシーケンスオブジェクト内での時間 /// private void updateSeq(float playFrame) { //The following objects are needed for interpolation (at a maximum): //- Base object ...baseObject //- Previous object (Object before the Base object) ...prevObject //- Target object (Object after the Base object) ...targetObject //- Next object (Object after the Target object) ...nextObject // //補間に必要なのは、最大で //・着目オブジェクト...baseObject //・着目オブジェクトの前のオブジェクト...prevObject //・着目オブジェクトの次のオブジェクト...targetObject //・着目オブジェクトの次の次のオブジェクト...nextObject //になります。 SequenceObjectData prevObject, baseObject, targetObject, nextObject; //If there is no object to be displayed, returns. // //表示するオブジェクトが無いなら戻ります。 if (0 > drawObjectId) return; //Sets the objects used for interpolation. // //補間のために使用するするオブジェクトを設定します。 //Clears the objects used for interpolation. // //補間用のオブジェクトのクリア nextObject = targetObject = prevObject = baseObject = objectList[drawObjectId]; //Obtains the Previous object. // //直前のオブジェクトの取得 if (0 < drawObjectId) { prevObject = objectList[drawObjectId - 1]; } else if (0 != loopCount) { //Otherwise, uses the last object when performing the loop process. // //そうでない場合、ループするなら最後のオブジェクトを用いる prevObject = objectList[objectList.Count - 1]; } //Obtains the Target object (and sets the Next object in advance). // //次のオブジェクトの取得("次の次"もあらかじめ設定) if (drawObjectId < objectList.Count - 1) { //When the Base object is not the last object. // //着目オブジェクトがが最後のオブジェクトではない。 nextObject = targetObject = objectList[drawObjectId + 1]; } else { //Sets the first object when performing the loop process. // //ループするなら、最初のオブジェクトに設定 if (loopCount < LoopNumber) { nextObject = targetObject = objectList[0]; } } //Obtains the Next object. // //次の次のオブジェクトの取得 if (drawObjectId < objectList.Count - 2) { //When the current index + 2 is valid. // //2つ先のIndexが有効 nextObject = objectList[drawObjectId + 2]; } else { //Loop process // //ループする if (loopCount < LoopNumber) { //When the Next object is the first object. // //次の次は最初のオブジェクト。 if (drawObjectId == objectList.Count - 2) { nextObject = objectList[0]; } else { //When there are 2 or more objects in total (Index 1 is valid). // //全体のオブジェクト数が2つ以上(Index1が有効) if (1 < objectList.Count) nextObject = objectList[1]; else nextObject = objectList[0]; } } } //For each pattern object in the sequence object to be displayed, //records the interpolated conversion information. // //表示するシーケンスオブジェクト内のパターンオブジェクトそれぞれについて //補間した変換情報を記録していきます。 for (int i = 0; i < baseObject.PatternObjectList.Count; i++) { PatternObjectData prevPattern, basePattern; PatternObjectData targetPattern, nextPattern; //Sets the interpolation target pattern. // //補間対象のパターンを設定します。 prevPattern = basePattern = targetPattern = nextPattern = baseObject.PatternObjectList[i]; if (targetObject.PatternObjectList.Count > i) targetPattern = targetObject.PatternObjectList[i]; if (prevObject.PatternObjectList.Count > i) prevPattern = prevObject.PatternObjectList[i]; if (nextObject.PatternObjectList.Count > i) nextPattern = nextObject.PatternObjectList[i]; //Calculates conversion values after interpolation. // //補間後の変換情報を計算します。 DrawData data = new DrawData(); switch (InterpolationType) { case Interpolation.None: data = basePattern.Data; break; case Interpolation.Linear: PutInfoLinearInterporlation(playFrame, data, basePattern.Data, targetPattern.Data); break; case Interpolation.Spline: PutInfoTCBSplineInterporlation(playFrame, data, prevPattern.Data, basePattern.Data, targetPattern.Data, nextPattern.Data); break; } //Records the data to the pattern object. //The data recorded here can be used as position information for //animated patterns. // //パターンオブジェクトに記録します。 //ここで記録された情報は、アニメーションされたパターンの //配置情報として利用することが可能です。 baseObject.PatternObjectList[i].InterpolationDrawData = data; } } /// /// Draws the objects by using the conversion information modified /// by the Update function. The conversion information can be also applied /// to the entire sequence (as offset). /// /// Update関数で更新された変換情報を利用しながら描画します。 /// シーケンス全体に変換を(オフセットとして)適用することも出来ます。 /// /// /// SpriteBatch /// /// スプラインバッチ /// /// /// Frame to be displayed /// /// 表示するフレーム /// /// /// Conversion information that affects the entire drawing target /// /// 描画対象全体に影響する変換情報 /// public void Draw(SpriteBatch sb, DrawData baseDrawData) { if (0 > drawObjectId) { return; } //Obtains the current Base object. // //現在着目しているシーケンスオブジェクトを取得 SequenceObjectData baseObj = objectList[drawObjectId]; //Referred to by the sequence object. // //シーケンスオブジェクトが参照する for (int i = 0; i < baseObj.PatternObjectList.Count; i++) { DrawData sqInfo = baseObj.PatternObjectList[i].InterpolationDrawData; baseObj.PatternObjectList[i].Draw(sb, sqInfo, baseDrawData); } } /// /// Performs simple linear interpolation. /// /// 単純な線形補間をします。 /// /// /// If the interpolation rate is 1.0, the value will be that of "target". /// /// 補間割合1.0ならtargetの値になります。 /// /// /// Target value /// /// 目的値 /// /// /// Initial value /// /// 初期値 /// /// /// Interpolation results /// /// 補間結果 /// private static float LinearInterporlation(float rate, float targetValue, float baseValue) { return (targetValue * rate + baseValue * (1f - rate)); } /// /// Performs linear interpolation for the conversion information /// (position, rotation, scale, center point, and color). /// /// 変換情報(位置・回転・スケール・中心点・色)を線形補間します。 /// /// /// Interpolation rate /// /// 補間割合 /// /// /// Calculation results will be stored. /// /// 計算結果が入ります。 /// /// /// Initial value /// /// 初期値 /// /// /// Target value /// /// 目的値 /// private static void PutInfoLinearInterporlation(float rate, DrawData resultData, DrawData baseData, DrawData targetData) { resultData.Position = new Point( (int)LinearInterporlation(rate, targetData.Position.X, baseData.Position.X), (int)LinearInterporlation(rate, targetData.Position.Y, baseData.Position.Y) ); resultData.Color = new Color( (byte)LinearInterporlation(rate, targetData.Color.R, baseData.Color.R), (byte)LinearInterporlation(rate, targetData.Color.G, baseData.Color.G), (byte)LinearInterporlation(rate, targetData.Color.B, baseData.Color.B), (byte)LinearInterporlation(rate, targetData.Color.A, baseData.Color.A) ); resultData.Scale = new Vector2( LinearInterporlation(rate, targetData.Scale.X, baseData.Scale.X), LinearInterporlation(rate, targetData.Scale.Y, baseData.Scale.Y)); resultData.Center = new Point( (int)LinearInterporlation(rate, targetData.Center.X, baseData.Center.X), (int)LinearInterporlation(rate, targetData.Center.Y, baseData.Center.Y)); resultData.RotateZ = LinearInterporlation(rate, targetData.RotateZ, baseData.RotateZ); } /// /// Performs spline interpolation by using TCB curve. /// /// TCB曲線を用いたスプライン補間を行います。 /// /// /// Interpolation rate /// /// 補間割合 /// /// /// Previous value of the initial value /// /// 初期値の前の値 /// /// /// Initial value /// /// 初期値 /// /// /// Target value /// /// 目標値 /// /// /// Next value of the target value /// /// 目標値の次の値 /// /// /// Calculation results /// /// 計算結果 /// private float CalcSpline(float rate, float prevValue, float baseValue, float targetValue, float nextValue) { float fRate = rate * rate, fRate2 = fRate * rate; float fQ0 = tcbParam[0] * (baseValue - prevValue) + tcbParam[1] * (targetValue - baseValue); float fQ1 = tcbParam[2] * (targetValue - baseValue) + tcbParam[3] * (nextValue - targetValue); return ((2.0f * baseValue - 2.0f * targetValue + fQ0 + fQ1) * fRate2 + (-3.0f * baseValue + 3.0f * targetValue - 2.0f * fQ0 - fQ1) * fRate + fQ0 * rate + baseValue); } /// /// Performs interpolation for the conversion information (position, rotation, /// scale, center position, color) by using TCB curve. /// /// TCB曲線を用いた変換情報(位置・回転・スケール・中心点・色)の補間を行います。 /// /// /// Interpolation rate /// /// 補間割合 /// /// /// Calculation results /// /// 計算結果 /// /// /// Previous value of the initial value /// /// 初期値の前の値 /// /// /// Initial value /// /// 初期値 /// /// /// Target value /// /// 目標値 /// /// /// Next value of the target value /// /// 目標値の次の値 /// private void PutInfoTCBSplineInterporlation(float rate, DrawData resultData, DrawData prevData, DrawData baseData, DrawData targetData, DrawData nextData) { resultData.Position = new Point( (int)CalcSpline(rate, prevData.Position.X, baseData.Position.X, targetData.Position.X, nextData.Position.X), (int)CalcSpline(rate, prevData.Position.Y, baseData.Position.Y, targetData.Position.Y, nextData.Position.Y)); resultData.Color = new Color( (byte)CalcSpline(rate, prevData.Color.R, baseData.Color.R, targetData.Color.R, nextData.Color.R), (byte)CalcSpline(rate, prevData.Color.G, baseData.Color.G, targetData.Color.G, nextData.Color.G), (byte)CalcSpline(rate, prevData.Color.B, baseData.Color.B, targetData.Color.B, nextData.Color.B), (byte)CalcSpline(rate, prevData.Color.A, baseData.Color.A, targetData.Color.A, nextData.Color.A)); resultData.Scale = new Vector2( CalcSpline(rate, prevData.Scale.X, baseData.Scale.X, targetData.Scale.X, nextData.Scale.X), CalcSpline(rate, prevData.Scale.Y, baseData.Scale.Y, targetData.Scale.Y, nextData.Scale.Y)); resultData.Center = new Point( (int)CalcSpline(rate, prevData.Center.X, baseData.Center.X, targetData.Center.X, nextData.Center.X), (int)CalcSpline(rate, prevData.Center.Y, baseData.Center.Y, targetData.Center.Y, nextData.Center.Y)); resultData.RotateZ = CalcSpline(rate, prevData.RotateZ, baseData.RotateZ, targetData.RotateZ, nextData.RotateZ); } /// /// Resets the frame and plays it from the beginning. /// /// フレームをリセットして最初から再生します。 /// public void Replay() { timeFrame = new TimeSpan(); } /// /// Based on the conversion information obtained and interpolated by the /// Update function, the sequence group displays pattern objects in the /// pattern group that is referred to by the current Base sequence object. /// This function obtains this current Base sequence object. /// /// シーケンスグループは、着目しているシーケンスオブジェクトが /// 参照するパターングループ内のパターンオブジェクトを /// Update関数で求めた補間された変換情報に基づいて表示します。 /// この、現在着目しているシーケンスオブジェクトを取得する関数です。 /// /// /// Current sequence object to be displayed /// /// 現在表示することになっているシーケンスオブジェクト /// [ContentSerializerIgnore] public SequenceObjectData CurrentObjectList { get { return (drawObjectId >= 0) ? objectList[drawObjectId] : objectList[0]; } } } }