You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
364 lines
12 KiB
C#
364 lines
12 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text.RegularExpressions;
|
|
|
|
namespace Otter {
|
|
/// <summary>
|
|
/// Class used for animations in Spritemap.
|
|
/// </summary>
|
|
public class Anim {
|
|
|
|
#region Static Methods
|
|
|
|
/// <summary>
|
|
/// Returns an array of numbers from min to max. Useful for passing in arguments for long animations.
|
|
/// </summary>
|
|
/// <param name="min">The start of the animation (includes this number.)</param>
|
|
/// <param name="max">The end of the animation (includes this number.)</param>
|
|
/// <returns>The array of ints representing an animation.</returns>
|
|
public static int[] FramesRange(int min, int max) {
|
|
int[] f = new int[max - min + 1];
|
|
for (int i = min; i <= max; i++) {
|
|
f[i - min] = i;
|
|
}
|
|
return f;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates an array of frames from a string expression. The expression must be similar to the following format:
|
|
/// "0,3,7-11,2,5"
|
|
/// Whitespace is permitted, and commas are optional.
|
|
/// <param name="input">A string formatted as above, describing the frames to generate.</param>
|
|
/// </summary>
|
|
public static int[] ParseFrames(string input) {
|
|
// Make sure the pattern matches, and alert the user if it doesn't
|
|
var parse = SyntaxCheck.Match(input);
|
|
if (!parse.Success)
|
|
throw new Exception(string.Format("Invalid format: {0}", input));
|
|
|
|
// Get all numbers/ranges in the input string.
|
|
var frames = new List<int>();
|
|
foreach (Match match in GetMatches.Matches(input)) {
|
|
var range = GetRange.Match(match.Value);
|
|
if (range.Success) {
|
|
int from = int.Parse(range.Groups[1].Value);
|
|
int to = int.Parse(range.Groups[2].Value);
|
|
|
|
// Support ascending and descending ranges
|
|
if (from < to) {
|
|
while (from <= to)
|
|
frames.Add(from++);
|
|
}
|
|
else {
|
|
while (from >= to)
|
|
frames.Add(from--);
|
|
}
|
|
}
|
|
else {
|
|
frames.Add(int.Parse(match.Value));
|
|
}
|
|
}
|
|
|
|
return frames.ToArray();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private Static Functions
|
|
|
|
// Matches strings containing only numbers and number ranges, separated by single commas and/or whitespace
|
|
static readonly Regex SyntaxCheck = new Regex(@"^(?:\d+\s*-\s*\d+|\d\s*,?\s*)*$");
|
|
|
|
// Extracts a number or number range from a string
|
|
static readonly Regex GetMatches = new Regex(@"((?:\d+\s*-\s*\d+)|(?:\d+))");
|
|
|
|
// Extracts two numbers from a string containing a range
|
|
static readonly Regex GetRange = new Regex(@"(\d+)-(\d+)");
|
|
|
|
#endregion
|
|
|
|
#region Private Fields
|
|
|
|
bool pingPong;
|
|
int loopBack = 0;
|
|
float timer;
|
|
float delay;
|
|
int direction;
|
|
int repeatsCounted = 0;
|
|
|
|
int currentFrame;
|
|
|
|
#endregion
|
|
|
|
#region Public Fields
|
|
|
|
/// <summary>
|
|
/// An action to run when the animation finishes playing.
|
|
/// </summary>
|
|
public Action OnComplete = delegate { };
|
|
|
|
/// <summary>
|
|
/// An action that is called when the Anim switches to a new frame.
|
|
/// </summary>
|
|
public Action OnNewFrame = delegate { };
|
|
|
|
/// <summary>
|
|
/// Determines if the animation is active (playing.)
|
|
/// </summary>
|
|
public bool Active;
|
|
|
|
#endregion
|
|
|
|
#region Public Properties
|
|
|
|
/// <summary>
|
|
/// The overall playback speed of the animation.
|
|
/// </summary>
|
|
public float PlaybackSpeed { get; private set; }
|
|
|
|
/// <summary>
|
|
/// The repeat count of the animation.
|
|
/// </summary>
|
|
public int RepeatCount { get; private set; }
|
|
|
|
/// <summary>
|
|
/// The frames used in the animation.
|
|
/// </summary>
|
|
public List<int> Frames { get; private set; }
|
|
|
|
/// <summary>
|
|
/// The frame delays used in the animation.
|
|
/// </summary>
|
|
public List<float> FrameDelays { get; private set; }
|
|
|
|
/// <summary>
|
|
/// The total number of frames in this animation.
|
|
/// </summary>
|
|
public int FrameCount {
|
|
get { return Frames.Count; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// The current frame of the animation.
|
|
/// </summary>
|
|
public int CurrentFrame {
|
|
get { return Frames[currentFrame]; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// The current frame index of the animation.
|
|
/// </summary>
|
|
public int CurrentFrameIndex {
|
|
get { return currentFrame; }
|
|
set { currentFrame = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// The total duration of the animation.
|
|
/// </summary>
|
|
public float TotalDuration {
|
|
get {
|
|
float delay = 0;
|
|
foreach (float d in FrameDelays) {
|
|
delay += d;
|
|
}
|
|
return delay;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Constructors
|
|
|
|
/// <summary>
|
|
/// Creates a new Anim with an array of ints for frames, and an array of floats for frameDelays.
|
|
/// </summary>
|
|
/// <param name="frames">The frames from the sprite sheet to display.</param>
|
|
/// <param name="frameDelays">The time that each frame should be displayed.</param>
|
|
public Anim(int[] frames, float[] frameDelays = null) {
|
|
Initialize(frames, frameDelays);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a new Anim with a string of ints for frames, and a string of floats for frameDelays.
|
|
/// </summary>
|
|
/// <param name="frames">A string of frames separated by a delim character. Example: "0,1,2-7,9,11"</param>
|
|
/// <param name="frameDelays">A string of floats separated by a delim character. Example: "0.5f,1,0.5f,1"</param>
|
|
/// <param name="delim">The string of characters to parse the string by. Default is ","</param>
|
|
public Anim(string frames, string frameDelays) {
|
|
string[] frameDelaysParts = Regex.Split(frameDelays.Replace(" ", ""), ",");
|
|
float[] framedelaysfloat = new float[frameDelaysParts.Length];
|
|
|
|
for (int i = 0; i < frameDelaysParts.Length; i++) {
|
|
framedelaysfloat[i] = float.Parse(frameDelaysParts[i]);
|
|
}
|
|
|
|
Initialize(ParseFrames(frames), framedelaysfloat);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private Methods
|
|
|
|
void Initialize(int[] frames, float[] frameDelays = null) {
|
|
Frames = new List<int>();
|
|
FrameDelays = new List<float>();
|
|
|
|
for (int i = 0; i < frames.Length; i++) {
|
|
Frames.Add(frames[i]);
|
|
if (frameDelays != null) {
|
|
if (i >= frameDelays.Length) {
|
|
FrameDelays.Add(frameDelays[i % frameDelays.Length]);
|
|
}
|
|
else {
|
|
FrameDelays.Add(frameDelays[i]);
|
|
}
|
|
}
|
|
else {
|
|
FrameDelays.Add(1);
|
|
}
|
|
}
|
|
|
|
RepeatCount = -1;
|
|
timer = 0;
|
|
delay = 0;
|
|
direction = 1;
|
|
PlaybackSpeed = 1;
|
|
|
|
Active = true;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public Methods
|
|
|
|
/// <summary>
|
|
/// Determines how many times this animation loops. -1 for infinite.
|
|
/// </summary>
|
|
/// <param name="times">How many times the animation should repeat.</param>
|
|
/// <returns>The anim object.</returns>
|
|
public Anim Repeat(int times = -1) {
|
|
RepeatCount = times;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Disables repeating. Animations default to repeat on.
|
|
/// </summary>
|
|
/// <returns>The anim object.</returns>
|
|
public Anim NoRepeat() {
|
|
RepeatCount = 0;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines if the animation will repeat by going back and forth between the start and end.
|
|
/// </summary>
|
|
/// <param name="pingpong">True for yes, false for no no no.</param>
|
|
/// <returns>The anim object.</returns>
|
|
public Anim PingPong(bool pingpong = true) {
|
|
pingPong = pingpong;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines the playback speed of the animation. 1 = 1 frame.
|
|
/// </summary>
|
|
/// <param name="speed">The new speed.</param>
|
|
/// <returns>The anim object.</returns>
|
|
public Anim Speed(float speed) {
|
|
PlaybackSpeed = speed;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines which frame the animation will loop back to when it repeats.
|
|
/// </summary>
|
|
/// <param name="frame">The frame to loop back to (from 0 to frame count - 1)</param>
|
|
/// <returns>The anim object.</returns>
|
|
public Anim LoopBackTo(int frame = 0) {
|
|
loopBack = frame;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stops the animation and returns it to the first frame.
|
|
/// </summary>
|
|
/// <returns>The anim object.</returns>
|
|
public Anim Stop() {
|
|
Active = false;
|
|
currentFrame = 0;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resets the animation back to frame 0 but does not stop it.
|
|
/// </summary>
|
|
/// <returns>The anim object.</returns>
|
|
public Anim Reset() {
|
|
timer = 0;
|
|
currentFrame = 0;
|
|
return this;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the Anim object. Handled by the Spritemap usually. If this doesn't run the animation will not play.
|
|
/// </summary>
|
|
/// <param name="t">The time scale.</param>
|
|
public void Update(float t = 1f) {
|
|
if (Active) {
|
|
timer += PlaybackSpeed * Game.Instance.DeltaTime * t;
|
|
}
|
|
|
|
delay = FrameDelays[currentFrame];
|
|
|
|
while (timer >= delay) {
|
|
timer -= delay;
|
|
currentFrame += direction;
|
|
|
|
if (currentFrame == Frames.Count) {
|
|
if (repeatsCounted < RepeatCount || RepeatCount < 0) {
|
|
repeatsCounted++;
|
|
if (pingPong) {
|
|
direction *= -1;
|
|
currentFrame = Frames.Count - 2;
|
|
}
|
|
else {
|
|
currentFrame = loopBack;
|
|
}
|
|
}
|
|
else {
|
|
if (pingPong) {
|
|
direction *= -1;
|
|
currentFrame = Frames.Count - 2;
|
|
}
|
|
else {
|
|
OnComplete();
|
|
Stop();
|
|
currentFrame = Frames.Count - 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (currentFrame < loopBack) {
|
|
if (pingPong) {
|
|
if (repeatsCounted < RepeatCount || RepeatCount < 0) {
|
|
repeatsCounted++;
|
|
direction *= -1;
|
|
currentFrame = loopBack + 1;
|
|
}
|
|
else {
|
|
OnComplete();
|
|
Stop();
|
|
}
|
|
}
|
|
}
|
|
|
|
OnNewFrame();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
} |