458 lines
20 KiB
C#
458 lines
20 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Drawing;
|
|
|
|
namespace SpriteLibrary
|
|
{
|
|
/// <summary>
|
|
/// A single frame of an animation
|
|
/// </summary>
|
|
internal class AnimationSingleFrame
|
|
{
|
|
public Image Frame;
|
|
public Image ResizedFrame;
|
|
public int ID;
|
|
public AnimationSingleFrame(Image SpriteImage, int id)
|
|
{
|
|
Frame = SpriteImage;
|
|
ID = id;
|
|
ResizedFrame = null;
|
|
}
|
|
}
|
|
internal class AnimationFrame
|
|
{
|
|
public int SingleFrameID; //The ID number of the image we want to view
|
|
public TimeSpan Duration; //How long does this image stay there.
|
|
public AnimationFrame(int ID, TimeSpan HowLong)
|
|
{
|
|
SingleFrameID = ID;
|
|
Duration = HowLong;
|
|
}
|
|
|
|
}
|
|
|
|
/// <summary>
|
|
/// One animation. A series of images.
|
|
/// </summary>
|
|
internal class Animation
|
|
{
|
|
public int AnimationID;
|
|
public List<AnimationFrame> Frames = new List<AnimationFrame>();
|
|
|
|
//The simplest case. It is just one image. No animation.
|
|
public Animation(SmartImage Smart_Image, Image SpriteImage)
|
|
{
|
|
//We create a new frame for it and make an animation of just one frame
|
|
AnimationID = Smart_Image.AnimationCount;
|
|
AnimationSingleFrame newSingle = new AnimationSingleFrame(SpriteImage, Smart_Image.FrameCount);
|
|
AnimationFrame newFrame = new AnimationFrame(Smart_Image.FrameCount, TimeSpan.FromMilliseconds(500));
|
|
Frames.Add(newFrame);
|
|
Smart_Image.AddFrame(newSingle);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create an image from an image that has a bunch of frames in the one image.
|
|
/// Start at the specified position (Start), and grab Count items (if we can find them)
|
|
/// </summary>
|
|
/// <param name="Count">The number of frames to grab</param>
|
|
/// <param name="Start">A point in the image where we start capturing frames</param>
|
|
/// <param name="Smart_Image">The smart image this is part of</param>
|
|
/// <param name="SpriteImage">the image we use for the sprite. Should have lots of images as a part of it.</param>
|
|
/// <param name="width">the width of each frame</param>
|
|
/// <param name="height">the height of each frame</param>
|
|
/// <param name="duration">The duration in miliseconds for this frame</param>
|
|
internal Animation(Point Start, SmartImage Smart_Image, Image SpriteImage, int width, int height, int duration, int Count)
|
|
{
|
|
//We create a new animation number for this new animation
|
|
AnimationID = Smart_Image.AnimationCount;
|
|
|
|
//Now, we make new frames for each image we can find.
|
|
int x = Start.X;
|
|
int y = Start.Y;
|
|
int counter = 0;
|
|
Rectangle where;
|
|
Image newImage;
|
|
while (y + height <= SpriteImage.Height)
|
|
{
|
|
while (x + width <= SpriteImage.Width)
|
|
{
|
|
where = new Rectangle(x, y, width, height);
|
|
Bitmap tImage = (Bitmap)SpriteImage;
|
|
newImage = (Bitmap)tImage.Clone(where, tImage.PixelFormat);
|
|
AnimationSingleFrame newSingle = new AnimationSingleFrame(newImage, Smart_Image.FrameCount);
|
|
AnimationFrame newFrame = new AnimationFrame(Smart_Image.FrameCount, TimeSpan.FromMilliseconds(duration));
|
|
Frames.Add(newFrame);
|
|
Smart_Image.AddFrame(newSingle);
|
|
x += width;
|
|
counter++;
|
|
if (counter >= Count)
|
|
return; //Stop when we have reached Count of them
|
|
}
|
|
y += height;
|
|
x = 0;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create an image from an image that has a bunch of frames in the one image.
|
|
/// Start at (0,0) with the specified height and width. Pull out as many images as we can
|
|
/// </summary>
|
|
/// <param name="Smart_Image">The smart image this is part of</param>
|
|
/// <param name="SpriteImage">the image we use for the sprite. Should have lots of images as a part of it.</param>
|
|
/// <param name="width">the width of each frame</param>
|
|
/// <param name="height">the height of each frame</param>
|
|
/// <param name="duration">The duration in miliseconds for this frame</param>
|
|
internal Animation(SmartImage Smart_Image, Image SpriteImage, int width, int height, int duration)
|
|
{
|
|
//We create a new animation number for this new animation
|
|
AnimationID = Smart_Image.AnimationCount;
|
|
|
|
//Now, we make new frames for each image we can find.
|
|
int x = 0;
|
|
int y = 0;
|
|
Rectangle where;
|
|
Image newImage;
|
|
while (y + height <= SpriteImage.Height)
|
|
{
|
|
while (x + width <= SpriteImage.Width)
|
|
{
|
|
where = new Rectangle(x, y, width, height);
|
|
Bitmap tImage = (Bitmap)SpriteImage;
|
|
newImage = (Bitmap)tImage.Clone(where, tImage.PixelFormat);
|
|
AnimationSingleFrame newSingle = new AnimationSingleFrame(newImage, Smart_Image.FrameCount);
|
|
AnimationFrame newFrame = new AnimationFrame(Smart_Image.FrameCount, TimeSpan.FromMilliseconds(duration));
|
|
Frames.Add(newFrame);
|
|
Smart_Image.AddFrame(newSingle);
|
|
x += width;
|
|
}
|
|
y += height;
|
|
x = 0;
|
|
}
|
|
}
|
|
|
|
internal Animation(SmartImage Smart_Image, int AnimationToCopy, bool MirrorHorizontally, bool MirrorVertically)
|
|
{
|
|
//We create a new animation number for this new animation
|
|
AnimationID = Smart_Image.AnimationCount;
|
|
|
|
Animation One = Smart_Image.getAnimation(AnimationToCopy);
|
|
Image MeImage;
|
|
|
|
for (int i = 0; i < One.Frames.Count; i++)
|
|
{
|
|
AnimationFrame AF = Smart_Image.GetAnimationFrame(AnimationToCopy, i);
|
|
AnimationSingleFrame ASF = Smart_Image.GetSingleFrame(AnimationToCopy, i);
|
|
MeImage = ASF.Frame;
|
|
TimeSpan Duration = AF.Duration;
|
|
Image MirrorImage = new Bitmap(MeImage);
|
|
if (MirrorHorizontally) MirrorImage.RotateFlip(RotateFlipType.RotateNoneFlipX);
|
|
if (MirrorVertically) MirrorImage.RotateFlip(RotateFlipType.RotateNoneFlipY);
|
|
//GC.Collect();
|
|
MeImage = MirrorImage;
|
|
|
|
AnimationSingleFrame newSingle = new AnimationSingleFrame(MeImage, Smart_Image.FrameCount);
|
|
AnimationFrame newFrame = new AnimationFrame(Smart_Image.FrameCount, Duration);
|
|
Frames.Add(newFrame);
|
|
Smart_Image.AddFrame(newSingle);
|
|
}
|
|
}
|
|
|
|
internal Animation(SmartImage Smart_Image, int AnimationToCopy, int DegreesOfRotation)
|
|
{
|
|
//We create a new animation number for this new animation
|
|
AnimationID = Smart_Image.AnimationCount;
|
|
|
|
GraphicsUnit tUnit = GraphicsUnit.Pixel;
|
|
Animation One = Smart_Image.getAnimation(AnimationToCopy);
|
|
Image MeImage;
|
|
|
|
for (int i = 0; i < One.Frames.Count; i++)
|
|
{
|
|
AnimationFrame AF = Smart_Image.GetAnimationFrame(AnimationToCopy, i);
|
|
AnimationSingleFrame ASF = Smart_Image.GetSingleFrame(AnimationToCopy, i);
|
|
MeImage = ASF.Frame;
|
|
TimeSpan Duration = AF.Duration;
|
|
Image rotatedImage = new Bitmap(MeImage.Width, MeImage.Height);
|
|
using (Graphics g = Graphics.FromImage(rotatedImage))
|
|
{
|
|
g.TranslateTransform(MeImage.Width / 2, MeImage.Height / 2); //set the rotation point as the center into the matrix
|
|
g.RotateTransform(DegreesOfRotation * -1); //rotate. The rotation direction we use is opposite of what they use
|
|
g.TranslateTransform(-MeImage.Width / 2, -MeImage.Height / 2); //restore rotation point into the matrix
|
|
g.DrawImage(MeImage, MeImage.GetBounds(ref tUnit), rotatedImage.GetBounds(ref tUnit), tUnit); //draw the image on the new bitmap
|
|
}
|
|
//GC.Collect();
|
|
MeImage = rotatedImage;
|
|
|
|
AnimationSingleFrame newSingle = new AnimationSingleFrame(MeImage, Smart_Image.FrameCount);
|
|
AnimationFrame newFrame = new AnimationFrame(Smart_Image.FrameCount, Duration);
|
|
Frames.Add(newFrame);
|
|
Smart_Image.AddFrame(newSingle);
|
|
}
|
|
}
|
|
|
|
}
|
|
/// <summary>
|
|
/// This is the holder and parser for images within the AnimatedSprite world
|
|
/// It allows you to store and access animations. A smart image might be a "troll"
|
|
/// that has a series of animations for up, down, left, right, and die.
|
|
/// </summary>
|
|
internal class SmartImage
|
|
{
|
|
SpriteController MyController;
|
|
List<Animation> Animations = new List<Animation>();
|
|
List<AnimationSingleFrame> Frames = new List<AnimationSingleFrame>();
|
|
public int FrameCount { get { return Frames.Count; } }
|
|
public int AnimationCount { get { return Animations.Count; } }
|
|
|
|
public SmartImage(SpriteController Controller, Image SpriteImage)
|
|
{
|
|
MyController = Controller;
|
|
AddAnimation(SpriteImage);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Make an animated image from an image that contains multiple frames
|
|
/// </summary>
|
|
/// <param name="Controller">The sprite controller this is attached to</param>
|
|
/// <param name="SpriteImage">The image we use to draw the animation from</param>
|
|
/// <param name="width">The width of the image to cut out of the main image</param>
|
|
/// <param name="height">The height of the image to cut out of the main image</param>
|
|
/// <param name="duration">The duration in miliseconds</param>
|
|
public SmartImage(SpriteController Controller, Image SpriteImage, int width, int height, int duration)
|
|
{
|
|
MyController = Controller;
|
|
AddAnimation(SpriteImage, width, height, duration);
|
|
}
|
|
public SmartImage(Point Start, SpriteController Controller, Image SpriteImage, int width, int height, int duration, int Count)
|
|
{
|
|
MyController = Controller;
|
|
AddAnimation(Start, SpriteImage, width, height, duration, Count);
|
|
}
|
|
|
|
|
|
public Animation getAnimation(int index)
|
|
{
|
|
if (index < 0 || index > AnimationCount) return null;
|
|
return Animations[index];
|
|
}
|
|
|
|
public void AddAnimation(Image SpriteImage)
|
|
{
|
|
Animation tAnimation = new Animation(this, SpriteImage);
|
|
Animations.Add(tAnimation);
|
|
}
|
|
public void AddAnimation(Image SpriteImage, int width, int height, int duration)
|
|
{
|
|
Animation tAnimation = new Animation(this, SpriteImage, width, height, duration);
|
|
Animations.Add(tAnimation);
|
|
}
|
|
public void AddAnimation(Point Start, Image SpriteImage, int width, int height, int duration, int Count)
|
|
{
|
|
Animation tAnimation = new Animation(Start, this, SpriteImage, width, height, duration, Count);
|
|
Animations.Add(tAnimation);
|
|
}
|
|
|
|
public void AddAnimation(int AnimationToCopy, bool MirrorHorizontal, bool MirrorVertical)
|
|
{
|
|
if (AnimationToCopy < 0) return;
|
|
if (AnimationToCopy >= AnimationCount) return;
|
|
|
|
Animation tAnimation = new Animation(this, AnimationToCopy, MirrorHorizontal, MirrorVertical);
|
|
Animations.Add(tAnimation);
|
|
}
|
|
public void AddAnimation(int AnimationToCopy, int RotationDegrees)
|
|
{
|
|
if (AnimationToCopy < 0) return;
|
|
if (AnimationToCopy >= AnimationCount) return;
|
|
Animation tAnimation = new Animation(this, AnimationToCopy, RotationDegrees);
|
|
Animations.Add(tAnimation);
|
|
}
|
|
|
|
public void ReplaceImage(Image newimage, int animation, int frame)
|
|
{
|
|
Image SpriteImage;
|
|
if (animation >= 0 && animation < Animations.Count)
|
|
{
|
|
if (frame >= 0 && frame < Animations[animation].Frames.Count)
|
|
{
|
|
int wFrame = Animations[animation].Frames[frame].SingleFrameID;
|
|
for (int looper = 0; looper < Frames.Count; looper++)
|
|
{
|
|
if (Frames[looper].ID == wFrame)
|
|
{
|
|
SpriteImage = Frames[looper].Frame;
|
|
if (SpriteImage == null) return;
|
|
Graphics.FromImage(SpriteImage).Clear(Color.Transparent); //Erase the old image
|
|
Graphics.FromImage(SpriteImage).DrawImage(newimage, 0, 0, SpriteImage.Width, SpriteImage.Height);
|
|
Frames[looper].ResizedFrame = null; //make sure we redraw the resized frame if we need to do that.
|
|
return; //Now that we have found it, return from the loop
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public AnimationFrame GetAnimationFrame(int animation, int frame)
|
|
{
|
|
if (animation >= 0 && animation < Animations.Count)
|
|
{
|
|
if (frame >= 0 && frame < Animations[animation].Frames.Count)
|
|
{
|
|
return Animations[animation].Frames[frame];
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public AnimationSingleFrame GetSingleFrame(int animation, int frame)
|
|
{
|
|
AnimationFrame AF = GetAnimationFrame(animation, frame);
|
|
int wFrame = AF.SingleFrameID;
|
|
for (int looper = 0; looper < Frames.Count; looper++)
|
|
{
|
|
if (Frames[looper].ID == wFrame)
|
|
return Frames[looper];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public Image GetImage(int animation, int frame, Size HowBig)
|
|
{
|
|
Image tImage;
|
|
bool NeedsResize = false;
|
|
//This will change.
|
|
if (animation >= 0 && animation < Animations.Count)
|
|
{
|
|
if (frame >= 0 && frame < Animations[animation].Frames.Count)
|
|
{
|
|
int wFrame = Animations[animation].Frames[frame].SingleFrameID;
|
|
for (int looper = 0; looper < Frames.Count; looper++)
|
|
{
|
|
if (Frames[looper].ID == wFrame)
|
|
{
|
|
tImage = Frames[looper].Frame;
|
|
if (!MyController.OptimizeForLargeSpriteImages) return tImage; //If we are not set to optimize, do not try to do so.
|
|
if (HowBig.Width == 0) return tImage;
|
|
if (Math.Abs(((double)tImage.Size.Width - (double)HowBig.Width) / HowBig.Width) - 1 > .3)
|
|
NeedsResize = true;
|
|
if (Math.Abs(((double)tImage.Size.Height - (double)HowBig.Height) / HowBig.Height) - 1 > .3)
|
|
NeedsResize = true;
|
|
if(NeedsResize)
|
|
{
|
|
Size newsize = new Size(HowBig.Width + (int)(HowBig.Width * .2), HowBig.Height + (int)(HowBig.Height * .2));
|
|
if(Frames[looper].ResizedFrame == null || Frames[looper].ResizedFrame.Size != newsize)
|
|
{
|
|
Frames[looper].ResizedFrame = new Bitmap(newsize.Width, newsize.Height);
|
|
Rectangle newrec = new Rectangle(0, 0, newsize.Width, newsize.Height);
|
|
Graphics.FromImage(Frames[looper].ResizedFrame).DrawImage(tImage, newrec);
|
|
}
|
|
return Frames[looper].ResizedFrame;
|
|
}
|
|
return tImage;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public TimeSpan GetCurrentDuration(int animation, int frame)
|
|
{
|
|
if (animation < 0 || animation >= Animations.Count) return new TimeSpan();
|
|
if (frame < 0 || frame >= Animations[animation].Frames.Count) return new TimeSpan();
|
|
return Animations[animation].Frames[frame].Duration;
|
|
}
|
|
|
|
public void AddFrame(AnimationSingleFrame ToAdd)
|
|
{
|
|
Frames.Add(ToAdd);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return true if the specified animation and frame for that animation needs
|
|
/// to be changed due to the time passing.
|
|
/// </summary>
|
|
/// <param name="animation">The animation index</param>
|
|
/// <param name="frame">the frame index</param>
|
|
/// <param name="duration">The time that has passed since the last frame was displayed.</param>
|
|
/// <returns></returns>
|
|
public bool NeedsNewImage(int animation, int frame, TimeSpan duration)
|
|
{
|
|
//If we do not have a valid index, return true
|
|
if (frame < 0) return true;
|
|
if (animation < 0) return true;
|
|
if (animation >= Animations.Count) return true; //Hopefully we never get here
|
|
if (frame >= Animations[animation].Frames.Count) return true;
|
|
|
|
//If no duration is set, we never have to change it.
|
|
if (Animations[animation].Frames[frame].Duration.TotalMilliseconds == 0)
|
|
return true;
|
|
|
|
//If we get here, we the current index is a valid one. Now, see if the timeframe needs to be changed
|
|
if (duration > Animations[animation].Frames[frame].Duration)
|
|
return true;
|
|
|
|
//If we get here, it does not need to be changed.
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check to see if the animation is in the last frame. Only works if animateonce is set to true
|
|
/// </summary>
|
|
/// <param name="AnimateOnce">The animateOnce value of the sprite</param>
|
|
/// <param name="animation">The animation we think we are on</param>
|
|
/// <param name="frame">The frame we think we are on</param>
|
|
/// <returns></returns>
|
|
public bool AnimationDone(int animation, int frame, bool AnimateOnce)
|
|
{
|
|
if (!AnimateOnce) return false;
|
|
if (frame == Animations[animation].Frames.Count - 1)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return the number of frames that the specified animation has.
|
|
/// </summary>
|
|
/// <param name="Animation">What animation to check</param>
|
|
/// <returns>The number of animation frames found in that animation</returns>
|
|
public int AnimationFrameCount(int Animation)
|
|
{
|
|
if (Animation < 0 || Animation >= Animations.Count) return 0;
|
|
return Animations[Animation].Frames.Count;
|
|
}
|
|
|
|
public TimeSpan ChangeIndex(int animation, ref int frame, TimeSpan duration, bool AnimateOnce = false)
|
|
{
|
|
TimeSpan remainder = duration;
|
|
if (frame < 0) frame = 0;
|
|
if (animation < 0) return remainder;
|
|
if (animation >= Animations.Count) return remainder; //Hopefully we never get here
|
|
if (frame >= Animations[animation].Frames.Count) return remainder;
|
|
|
|
//we have a valid index
|
|
while (remainder.TotalMilliseconds >= Animations[animation].Frames[frame].Duration.TotalMilliseconds &&
|
|
Animations[animation].Frames[frame].Duration.TotalMilliseconds != 0)
|
|
{
|
|
remainder = remainder - Animations[animation].Frames[frame].Duration;
|
|
frame++;
|
|
if (AnimateOnce && frame >= Animations[animation].Frames.Count)
|
|
{
|
|
//we stay on the last frame. We also stop decrementing the timeamount. That lets us know when the last frame terminates.
|
|
frame = Animations[animation].Frames.Count - 1;
|
|
remainder = remainder + Animations[animation].Frames[frame].Duration;
|
|
return remainder;
|
|
}
|
|
else if (frame >= Animations[animation].Frames.Count)
|
|
{
|
|
frame = 0;
|
|
}
|
|
}
|
|
return remainder;
|
|
}
|
|
}
|
|
}
|