Archive Browser
Download Cyotek.ArcadeExplosionMaker.zip version 1.0.0.1, last updated 05/06/2012 (123.69 KB)
Download- md5: bd412009442f7923a555ba5b0f508fe0
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
namespace Cyotek.ArcadeExplosionMaker
{
// Cyotek Arcade Explosion Maker
// Copyright (c) 2012 Cyotek. All Rights Reserved.
// http://cyotek.com
// http://cyotek.com/blog/arcade-explosion-generator
// If you use this code or output in your applications, attribution or donations are welcome.
public class ExplosionGenerator : IDisposable
{
#region Private Member Declarations
private List<Image> _frames;
private Random _random;
private Bitmap _spriteSheet;
#endregion Private Member Declarations
#region Public Constructors
public ExplosionGenerator()
{
_random = new Random();
}
#endregion Public Constructors
#region Public Methods
public void Clear()
{
if (_frames != null)
{
while (_frames.Count != 0)
{
_frames[0].Dispose();
_frames.RemoveAt(0);
}
}
if (_spriteSheet != null)
_spriteSheet.Dispose();
}
public void Dispose()
{
this.Clear();
_frames = null;
_spriteSheet = null;
}
public void Generate()
{
this.Clear();
_frames = new List<Image>();
if (this.Parameters.Seed < 0)
this.Seed = new Random().Next(); // Could use Environment.Ticks but this produces a wider range of numbers
else
this.Seed = this.Parameters.Seed;
_random = new Random(this.Seed);
this.CreateFrames();
this.CreateSpriteSheet();
}
#endregion Public Methods
#region Public Properties
public ReadOnlyCollection<Image> Frames
{ get { return _frames != null ? _frames.AsReadOnly() : null; } }
public ExplosionParameters Parameters { get; set; }
public int Seed { get; protected set; }
public Bitmap SpriteSheet
{ get { return _spriteSheet; } }
public Size SpriteSheetLayoutSize { get; protected set; }
#endregion Public Properties
#region Private Methods
private ExplosionData CreateExplosion(Rectangle bounds)
{
int x;
int y;
int diameter;
int radius;
int growthSize;
int defaultRadius;
radius = _random.Next(this.Parameters.MinimumExplosionRadius, this.Parameters.MaximumExplosionRadius + 1);
diameter = radius * 2;
if (this.Parameters.FitToBounds && diameter < this.Parameters.FrameSize.Width && diameter < this.Parameters.FrameSize.Height)
{
x = radius + _random.Next(0, this.Parameters.FrameSize.Width - diameter);
y = radius + _random.Next(0, this.Parameters.FrameSize.Height - diameter);
}
else
{
x = _random.Next((this.Parameters.FrameSize.Width / 2), (this.Parameters.FrameSize.Width * 2) + 1);
y = _random.Next((this.Parameters.FrameSize.Height / 2), (this.Parameters.FrameSize.Height * 2) + 1);
}
if (this.Parameters.GrowthIncrement != 0)
growthSize = (int)Math.Max(2, (diameter / 100F) * this.Parameters.GrowthIncrement);
else
growthSize = 0;
if (this.Parameters.GrowthStages == 0)
defaultRadius = 0;
else
defaultRadius = Math.Max(0, radius - (growthSize * this.Parameters.GrowthStages));
return new ExplosionData()
{
Center = new Point(x, y),
Radius = defaultRadius,
RadiusGrowth = growthSize,
MaximumRadius = radius,
MinimumRadius = defaultRadius,
BorderGrowth = this.Parameters.BorderSize,
BorderSize = this.Parameters.GrowBorders ? 0 : this.Parameters.BorderSize,
GrowthSpeed = 1
};
}
private void CreateFrames()
{
List<ExplosionData> explosions;
explosions = new List<ExplosionData>();
for (int frameIndex = 0; frameIndex < this.Parameters.Frames; frameIndex++)
{
Bitmap image;
image = new Bitmap(this.Parameters.FrameSize.Width, this.Parameters.FrameSize.Height);
using (Graphics g = Graphics.FromImage(image))
{
Rectangle bounds;
g.SmoothingMode = this.Parameters.AntiAlias ? SmoothingMode.AntiAlias : SmoothingMode.None;
bounds = new Rectangle(Point.Empty, this.Parameters.FrameSize);
g.Clear(this.Parameters.BackColor);
if (explosions.Count < this.Parameters.MaximumExplosions)
{
ExplosionData explosion;
int zorder;
if (this.Parameters.RandomOrder)
zorder = _random.Next(0, explosions.Count);
else
zorder = 0;
explosion = this.CreateExplosion(bounds);
explosions.Insert(zorder, explosion);
}
this.RenderFrame(g, explosions);
}
_frames.Add(image);
}
}
private void CreateSpriteSheet()
{
int rows;
int columns;
int x;
int y;
columns = this.Parameters.SpriteSheetColumns;
if (columns < 1)
columns = (int)Math.Round(Math.Sqrt(_frames.Count));
rows = (int)Math.Round((float)_frames.Count / columns);
if (columns * rows < _frames.Count)
rows++;
this.SpriteSheetLayoutSize = new Size(columns, rows);
_spriteSheet = new Bitmap(columns * this.Parameters.FrameSize.Width, rows * this.Parameters.FrameSize.Height);
using (Graphics g = Graphics.FromImage(_spriteSheet))
{
for (int r = 0; r < rows; r++)
{
for (int c = 0; c < columns; c++)
{
int cell;
cell = r * columns + c;
if (cell < _frames.Count)
{
x = (c * this.Parameters.FrameSize.Width);
y = (r * this.Parameters.FrameSize.Height);
g.DrawImage(_frames[cell], new Point(x, y));
}
}
}
}
}
private Region FindCircleIntersections(PointF[] centers, float[] radii)
{
// http://blog.csharphelper.com/2010/08/23/find-the-area-where-two-or-more-circles-overlap-in-c.aspx
if (centers.Length < 1)
return null;
// Make a region.
Region result_region = new Region();
// Intersect the region with the circles.
for (int i = 0; i < centers.Length; i++)
{
using (GraphicsPath circle_path = new GraphicsPath())
{
circle_path.AddEllipse(
centers[i].X - radii[i], centers[i].Y - radii[i],
2 * radii[i], 2 * radii[i]);
result_region.Intersect(circle_path);
}
}
return result_region;
}
private void RenderCircle(Graphics g, PointF point, float radius, Color color)
{
this.RenderCircle(g, point, radius, color, 0, 0);
}
private void RenderCircle(Graphics g, PointF point, float radius, Color color, float offset, float lineWidth)
{
RectangleF bounds;
bounds = new RectangleF((point.X + offset) - radius, (point.Y + offset) - radius, (radius - offset) * 2, (radius - offset) * 2);
if (lineWidth == 0)
{
using (Brush brush = new SolidBrush(color))
g.FillEllipse(brush, bounds);
}
else
{
using (Pen pen = new Pen(color, lineWidth))
g.DrawEllipse(pen, bounds);
}
}
private void RenderExplosion(Graphics g, ExplosionData explosion)
{
if (explosion.Radius != 0)
{
float borderSize;
float diameter;
borderSize = explosion.BorderSize;
diameter = explosion.Radius * 2;
this.RenderCircle(g, explosion.Center, explosion.Radius, this.Parameters.Color3);
if (borderSize > 0)
{
if (diameter > borderSize)
this.RenderCircle(g, explosion.Center, explosion.Radius, this.Parameters.Color1, 0, borderSize + 1);
if (diameter > (borderSize * 2))
this.RenderCircle(g, explosion.Center, explosion.Radius, this.Parameters.Color2, borderSize, borderSize);
}
}
}
private void RenderExplosionBlackout(Graphics g, ExplosionData explosion)
{
if (explosion.MinimumRadius != 0)
{
this.RenderCircle(g, explosion.Center, explosion.MinimumRadius, this.Parameters.BlackoutColor);
this.RenderCircle(g, explosion.Center, explosion.MinimumRadius + (this.Parameters.BorderSize * 2), this.Parameters.BlackoutColor, 0, this.Parameters.BorderSize);
}
}
private void RenderExplosionMask(Graphics g, PointF[] centers, float[] radii)
{
using (Region region = this.FindCircleIntersections(centers, radii))
{
if (region != null)
{
using (Brush brush = new SolidBrush(this.Parameters.BlackoutColor))
g.FillRegion(brush, region);
}
}
}
private void RenderFrame(Graphics g, List<ExplosionData> explosions)
{
for (int explosionIndex = explosions.Count; explosionIndex > 0; explosionIndex--)
{
ExplosionData explosion;
explosion = explosions[explosionIndex - 1];
explosion.Radius += (explosion.RadiusGrowth * explosion.GrowthSpeed);
if (this.Parameters.GrowBorders)
explosion.BorderSize += explosion.BorderGrowth;
if (explosion.Radius >= explosion.MaximumRadius)
{
explosion.Radius = explosion.MaximumRadius;
explosion.RadiusGrowth = -explosion.RadiusGrowth;
explosion.BorderGrowth = -explosion.BorderGrowth;
}
if (explosion.Radius > explosion.MinimumRadius)
this.RenderExplosion(g, explosion);
else if (this.Parameters.RemoveExpiredExplosions)
explosions.Remove(explosion);
else if (this.Parameters.BlackOutExpiredExplosions)
this.RenderExplosionBlackout(g, explosion);
}
// mask out the overlapping area
if (explosions.Count > 1)
{
switch (this.Parameters.ExplosionMaskMode)
{
case ExplosionMaskMode.Previous:
this.RenderPreviousExplosionMask(g, explosions);
break;
case ExplosionMaskMode.All:
this.RenderExplosionMask(g, explosions.Select(e => e.Center).ToArray(), explosions.Select(e => e.Radius).ToArray());
break;
}
}
}
private void RenderPreviousExplosionMask(Graphics g, List<ExplosionData> explosions)
{
for (int i = 1; i < explosions.Count; i++)
{
ExplosionData explosion1;
ExplosionData explosion2;
explosion1 = explosions[i];
explosion2 = explosions[i - 1];
this.RenderExplosionMask(g, new PointF[] { explosion1.Center, explosion2.Center }, new float[] { explosion1.Radius, explosion2.Radius });
}
}
#endregion Private Methods
}
}
Donate
This software may be used free of charge, but as with all free software there are costs involved to develop and maintain.
If this site or its services have saved you time, please consider a donation to help with running costs and timely updates.
Donate