using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Drawing; namespace SpriteLibrary { /// /// A single frame of an animation /// 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; } } /// /// One animation. A series of images. /// internal class Animation { public int AnimationID; public List Frames = new List(); //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); } /// /// 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) /// /// The number of frames to grab /// A point in the image where we start capturing frames /// The smart image this is part of /// the image we use for the sprite. Should have lots of images as a part of it. /// the width of each frame /// the height of each frame /// The duration in miliseconds for this frame 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; } } /// /// 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 /// /// The smart image this is part of /// the image we use for the sprite. Should have lots of images as a part of it. /// the width of each frame /// the height of each frame /// The duration in miliseconds for this frame 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); } } } /// /// 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. /// internal class SmartImage { SpriteController MyController; List Animations = new List(); List Frames = new List(); public int FrameCount { get { return Frames.Count; } } public int AnimationCount { get { return Animations.Count; } } public SmartImage(SpriteController Controller, Image SpriteImage) { MyController = Controller; AddAnimation(SpriteImage); } /// /// Make an animated image from an image that contains multiple frames /// /// The sprite controller this is attached to /// The image we use to draw the animation from /// The width of the image to cut out of the main image /// The height of the image to cut out of the main image /// The duration in miliseconds 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); } /// /// Return true if the specified animation and frame for that animation needs /// to be changed due to the time passing. /// /// The animation index /// the frame index /// The time that has passed since the last frame was displayed. /// 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; } /// /// Check to see if the animation is in the last frame. Only works if animateonce is set to true /// /// The animateOnce value of the sprite /// The animation we think we are on /// The frame we think we are on /// public bool AnimationDone(int animation, int frame, bool AnimateOnce) { if (!AnimateOnce) return false; if (frame == Animations[animation].Frames.Count - 1) return true; return false; } /// /// Return the number of frames that the specified animation has. /// /// What animation to check /// The number of animation frames found in that animation 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; } } }