using System; using System.Collections.Generic; namespace Otter { /// /// Entity that is a quick way to make a particle. Has lots of parameters that can be set, so /// use this with that constructor where you do { } and put a bunch of properties inside. /// /// var p = new Particle(0, 0, "particle.png", 100, 100) { /// LifeSpan = 30, /// Angle = 10, /// FinalScaleX = 0, /// LockScaleRatio = true /// }; /// /// public class Particle : Entity { #region Private Fields Color color; bool hasColor; bool useFrameList; float speedLen; bool hasSpeedLen; float speedDir; bool hasSpeedDir; int frameCount; bool hasFrameCount; float finalSpeedX; bool hasFinalSpeedX; float initSpeedX; float finalSpeedY; bool hasFinalSpeedY; float initSpeedY; float finalScaleX; bool hasFinalScaleX; float initScaleX; float finalScaleY; bool hasFinalScaleY; float initScaleY; float finalAngle; bool hasFinalAngle; float initAngle; float finalX; bool hasFinalX; float initX; float finalY; bool hasFinalY; float initY; float finalAlpha; bool hasFinalAlpha; float initAlpha; float finalColorR; bool hasFinalColorR; float initColorR; float finalColorG; bool hasFinalColorG; float initColorG; float finalColorB; bool hasFinalColorB; float initColorB; Color finalColor; bool hasFinalColor; Color initColor; float finalSpeedLen; bool hasFinalSpeedLen; float initSpeedLen; float finalSpeedDir; bool hasFinalSpeedDir; float initSpeedDir; float originX; float originY; bool useOrigin; bool useSpeedXY = true; bool tweenPosition = false; float xpos, ypos; float delayTimer = 0; float colorLerp; float initColorLerp; #endregion #region Public Static Fields public static int ActiveCount = 0; #endregion #region Public Fields /// /// The ease function to use when interpolating values. /// public Func Ease; /// /// The ImageSet that the Particle is rendering. /// public ImageSet Image; /// /// The default life span for all Particle Entities. /// public static int DefaultLifeSpan = 60; /// /// The initial horizontal speed. /// public float SpeedX; /// /// The initial vertical speed. /// public float SpeedY; /// /// The horizontal offset from the spawn position. /// public float OffsetX; /// /// The vertical offset from the spawn position. /// public float OffsetY; /// /// The initial X scale of the rendered Image. /// public float ScaleX = 1; /// /// The initial Y scale fo the rendered Image. /// public float ScaleY = 1; /// /// The angle of the Image. /// public float Angle; /// /// The initial alpha of the Image. /// public float Alpha = 1; /// /// The initial Color R component of the Image. /// public float ColorR = 1; /// /// The initial Color G component of the Image. /// public float ColorG = 1; /// /// The initial Color B component of the Image. /// public float ColorB = 1; /// /// The amount of time that must pass before the Entity is spawned. /// public float Delay; /// /// How many steps the particle should move by its speed when first created. /// public int AdvanceSteps; /// /// Determines if the image angle should be locked to the direction of the particle's movement. /// public bool MotionAngle; /// /// The BlendMode of the Image rendered. /// public BlendMode Blend = BlendMode.Alpha; /// /// The starting frame in the ImageSet. /// public int FrameOffset; /// /// How many times to loop the ImageSet animation during the Particle's lifespan. /// public int Loops; /// /// Determines if the ScaleY will always be locked to the ScaleX. /// public bool LockScaleRatio; /// /// Determines if the Image will have a centered origin. /// public bool CenterOrigin = true; /// /// Flip the ImageSet horizontally. /// public bool FlipX = false; /// /// Flip the ImageSet vertically. /// public bool FlipY = false; /// /// Determines if the Particle should animate the ImageSet. /// public bool Animate; /// /// The shader to use on the ImageSet. /// public Shader Shader; /// /// The specific frames to display for the ImageSet. If set it will override the default FrameCount. /// public List Frames; #endregion #region Public Properties /// /// The Color of the Particle. Overrides ColorR, ColorG, ColorB. /// public Color Color { set { color = value; hasColor = true; } get { return color; } } /// /// The magnitude of the particle's movement. Overrides SpeedX and SpeedY. /// public float SpeedLen { set { speedLen = value; hasSpeedLen = true; } get { return speedLen; } } /// /// The direction of the particle's movement. Overrides SpeedX and SpeedY. /// public float SpeedDir { set { speedDir = value; hasSpeedDir = true; } get { return speedDir; } } /// /// How many frames are present in the ImageSet for the Particle to animate through. /// public int FrameCount { set { frameCount = value; hasFrameCount = true; } get { return frameCount; } } /// /// The final horizontal speed of the Particle. /// public float FinalSpeedX { set { finalSpeedX = value; hasFinalSpeedX = true; } get { return finalSpeedX; } } /// /// The final vertical speed of the Particle. /// public float FinalSpeedY { set { finalSpeedY = value; hasFinalSpeedY = true; } get { return finalSpeedY; } } /// /// The final X scale of the rendered Image. /// public float FinalScaleX { set { finalScaleX = value; hasFinalScaleX = true; } get { return finalScaleX; } } /// /// The final Y scale of the rendered Image. /// public float FinalScaleY { set { finalScaleY = value; hasFinalScaleY = true; } get { return finalScaleY; } } /// /// The final angle of the rendered Image. /// public float FinalAngle { set { finalAngle = value; hasFinalAngle = true; } get { return finalAngle; } } /// /// The final X position of the Particle. If set this overrides speed values. /// public float FinalX { set { finalX = value; hasFinalX = true; } get { return finalX; } } /// /// The final Y position of the Particle. If set this overrides speed values. /// public float FinalY { set { finalY = value; hasFinalY = true; } get { return finalY; } } /// /// The final Alpha of the rendered Image. /// public float FinalAlpha { set { finalAlpha = value; hasFinalAlpha = true; } get { return finalAlpha; } } /// /// The final Color R component of the rendered Image. /// public float FinalColorR { set { finalColorR = value; hasFinalColorR = true; } get { return finalColorR; } } /// /// The final Color G component of the rendered Image. /// public float FinalColorG { set { finalColorG = value; hasFinalColorG = true; } get { return finalColorG; } } /// /// The final Color B component of the rendered Image. /// public float FinalColorB { set { finalColorB = value; hasFinalColorB = true; } get { return finalColorB; } } /// /// The final Color of the rendered Image. If set will override the R G B components. /// public Color FinalColor { set { finalColor = value; hasFinalColor = true; } get { return finalColor; } } /// /// The final speed length of the Particle. If set will override SpeedX and SpeedY. /// public float FinalSpeedLen { set { finalSpeedLen = value; hasFinalSpeedLen = true; } get { return finalSpeedLen; } } /// /// The final speed direction of the Particle. If set will override SpeedX and SpeedY. /// public float FinalSpeedDir { set { finalSpeedDir = value; hasFinalSpeedDir = true; } get { return finalSpeedDir; } } /// /// The X origin of the rendered Image. /// public float OriginX { get { return originX; } set { originX = value; useOrigin = true; } } /// /// The Y origin of the rendered Image. /// public float OriginY { get { return originY; } set { originY = value; useOrigin = true; } } #endregion #region Constructors /// /// Create a new Particle. /// /// The x position. /// The y position. /// The source file path of the ImageSet. /// The width of the ImageSet cell. /// The height of the ImageSet cell. public Particle(float x, float y, string source = null, int width = 0, int height = 0) : base(x, y) { Image = new ImageSet(source, width, height); } /// /// Create a new Particle. /// /// The x position. /// The y position. /// The Texture to use for the ImageSet. /// The width of the ImageSet cell. /// The height of the ImageSet cell. public Particle(float x, float y, Texture texture, int width = 0, int height = 0) : base(x, y) { Image = new ImageSet(texture, width, height); } /// /// Create a new Particle. /// /// The x position. /// The y position. /// /// /// public Particle(float x, float y, AtlasTexture texture, int width = 0, int height = 0) : base(x, y) { Image = new ImageSet(texture, width, height); } /// /// Create a blank Particle. /// /// The x position. /// The y position. public Particle(float x, float y) : base(x, y) { } /// /// Create a new Particle. /// /// The x position. /// The y position. /// The ImageSet to use for the Particle. public Particle(float x, float y, ImageSet imageSet) : base(x, y) { Image = imageSet; } #endregion #region Public Methods /// /// Added to the Scene. /// public override void Added() { base.Added(); if (Delay == 0) Start(); } /// /// Start the Particle. /// public void Start() { if (LifeSpan == 0) { LifeSpan = DefaultLifeSpan; } if (Frames != null) { useFrameList = true; FrameCount = Frames.Count; } if (FrameCount > 0) { Animate = true; } if (hasSpeedLen || hasSpeedDir) { useSpeedXY = false; } if (hasFinalX || hasFinalY) { tweenPosition = true; } if (!tweenPosition) { if (useSpeedXY) { if (!hasFinalSpeedX) { FinalSpeedX = SpeedX; } if (!hasFinalSpeedY) { FinalSpeedY = SpeedY; } SpeedLen = 0; FinalSpeedLen = 0; SpeedDir = 0; FinalSpeedDir = 0; } else { FinalSpeedX = 0; FinalSpeedY = 0; if (!hasFinalSpeedLen) { FinalSpeedLen = SpeedLen; } if (!hasFinalSpeedDir) { FinalSpeedDir = SpeedDir; } } FinalX = 0; FinalY = 0; } else { xpos = X; ypos = Y; if (!hasFinalX) { FinalX = X; } if (!hasFinalY) { FinalY = Y; } } if (!hasFinalScaleX) { FinalScaleX = ScaleX; } if (!hasFinalScaleY) { FinalScaleY = ScaleY; } if (!hasFinalAngle) { FinalAngle = Angle; } if (!hasFinalAlpha) { FinalAlpha = Alpha; } if (!hasFinalColorR) { FinalColorR = ColorR; } if (!hasFinalColorG) { FinalColorG = ColorG; } if (!hasFinalColorB) { FinalColorB = ColorB; } initSpeedX = SpeedX; initSpeedY = SpeedY; initScaleX = ScaleX; initScaleY = ScaleY; initAngle = Angle; initAlpha = Alpha; initColorR = ColorR; initColorG = ColorG; initColorB = ColorB; initSpeedLen = SpeedLen; initSpeedDir = SpeedDir; initX = X; initY = Y; initColorLerp = colorLerp; initColor = Color; X += OffsetX; Y += OffsetY; AddGraphic(Image); if (useOrigin) { Image.OriginX = originX; Image.OriginY = originY; } else { if (CenterOrigin) Image.CenterOrigin(); } for (var i = 0; i < AdvanceSteps; i++) { if (!useSpeedXY) { SpeedX = Util.PolarX(SpeedDir, SpeedLen); SpeedY = Util.PolarY(SpeedDir, SpeedLen); } X += SpeedX; Y += SpeedY; } if (Ease == null) { Ease = Otter.Ease.Linear; } if (Shader != null) { Image.Shader = Shader; } Image.Visible = false; ActiveCount++; } /// /// Update the Particle. /// public override void Update() { base.Update(); // Handle delay if (delayTimer < Delay) { delayTimer += Game.DeltaTime; Timer = 0; if (delayTimer == Delay) { Start(); } return; } // Update values float lerp = Ease((float)Timer / LifeSpan); SpeedX = Util.Lerp(initSpeedX, finalSpeedX, lerp); SpeedY = Util.Lerp(initSpeedY, finalSpeedY, lerp); ScaleX = Util.Lerp(initScaleX, finalScaleX, lerp); ScaleY = Util.Lerp(initScaleY, finalScaleY, lerp); Angle = Util.Lerp(initAngle, finalAngle, lerp); Alpha = Util.Lerp(initAlpha, finalAlpha, lerp); ColorR = Util.Lerp(initColorR, finalColorR, lerp); ColorG = Util.Lerp(initColorG, finalColorG, lerp); ColorB = Util.Lerp(initColorB, finalColorB, lerp); SpeedLen = Util.Lerp(initSpeedLen, finalSpeedLen, lerp); SpeedDir = Util.Lerp(initSpeedDir, finalSpeedDir, lerp); xpos = Util.Lerp(initX, finalX, lerp); ypos = Util.Lerp(initY, finalY, lerp); colorLerp = Util.Lerp(0, 1, lerp); Image.Visible = true; // If the positions are not controlled by tweens. if (!tweenPosition) { // if SpeedDir and Len are being used if (!useSpeedXY) { SpeedX = Util.PolarX(SpeedDir, SpeedLen); SpeedY = Util.PolarY(SpeedDir, SpeedLen); } // Control the position with the particle speed. X += SpeedX; Y += SpeedY; } else { // Control the position with the interpolated/tweened position. X = xpos; Y = ypos; } // Set up animation int endFrame; if (hasFrameCount) { endFrame = FrameOffset + FrameCount; } else { endFrame = Image.Frames; } // Animate the particle if (Animate) { var playCount = Loops + 1; var frameIndex = (int)Util.ScaleClamp(Timer, 0, LifeSpan, 0, FrameCount * playCount); frameIndex %= FrameCount; if (useFrameList) { Image.Frame = Frames[frameIndex]; } else { Image.Frame = frameIndex + FrameOffset; } } else { Image.Frame = 0; } // Control the image scale. Image.ScaleX = ScaleX; if (LockScaleRatio) { Image.ScaleY = ScaleX; } else { Image.ScaleY = ScaleY; } // Determines if the particle faces the direction of its speed vector. if (MotionAngle) { Image.Angle = Util.Angle(SpeedX, SpeedY); } else { Image.Angle = Angle; } // Control the blend mode. Image.Blend = Blend; // Color the color. if (hasColor) { if (hasFinalColor) { Image.Color = Util.LerpColor(initColor, finalColor, colorLerp); } else { Image.Color = Color; } Image.Alpha = Alpha; } else { Image.Color = new Color(ColorR, ColorG, ColorB, Alpha); } // Flip the image. Image.FlippedX = FlipX; Image.FlippedY = FlipY; } /// /// Removed from the Scene. /// public override void Removed() { base.Removed(); Timer = 0; ClearGraphics(); ActiveCount--; } public override void SceneEnd() { base.SceneEnd(); RemoveSelf(); } #endregion } }