Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm developing a 2D action game, and in said game I've given my primary character an ability he can use to throw a fireball. I'm trying to design an effect so that when said fireball collides (be it with terrain or with an enemy) that the fireball will explode. For the explosion effect I've created a particle that once placed into game space will follow random, yet autonomic behavior based on random variables.

Here is my question: When I generate my explosion (essentially 90 of these particles) I get one of two behaviors,

1) They are all generated with the same random variables, and don't resemble an explosion at all, more like a large mass of clumped sprites that all follow the same randomly generated path.

2) If I assign each particle a unique seed to its random number generator, they are a little bit -more- spread out, yet clumping is still visible (they seem to fork out into 3 different directions)

Does anybody have any tips for producing particle-based 2D explosions? I'll include the code for my particle and the event I'm generating them in.

Fire particle class:

    public FireParticle(xTile.Dimensions.Location StartLocation, ContentManager content)
    {
        worldLocation = StartLocation;
        fireParticleAnimation = new FireParticleAnimation(content);
        random = new Random();

        int rightorleft = random.Next(0, 3);
        int upordown = random.Next(1, 3);
        int xVelocity = random.Next(0, 101);
        int yVelocity = random.Next(0, 101);
        Vector2 tempVector2 = new Vector2(0,0);

        if (rightorleft == 1)
        {
            tempVector2 = new Vector2(xVelocity, tempVector2.Y);
        }

        else if (rightorleft == 2)
        {
            tempVector2 = new Vector2(-xVelocity, tempVector2.Y);
        }

        if (upordown == 1)
        {
            tempVector2 = new Vector2(tempVector2.X, -yVelocity);
        }

        else if (upordown == 2)
        {
            tempVector2 = new Vector2(tempVector2.X, yVelocity);
        }

        velocity = tempVector2;
        scale = random.Next(1, 11);
        upwardForce = -10;
        dead = false;
    }

    public FireParticle(xTile.Dimensions.Location StartLocation, ContentManager content, int seed)
    {
        worldLocation = StartLocation;
        fireParticleAnimation = new FireParticleAnimation(content);
        random = new Random(seed);

        int rightorleft = random.Next(0, 3);
        int upordown = random.Next(1, 3);
        int xVelocity = random.Next(0, 101);
        int yVelocity = random.Next(0, 101);
        Vector2 tempVector2 = new Vector2(0, 0);

        if (rightorleft == 1)
        {
            tempVector2 = new Vector2(xVelocity, tempVector2.Y);
        }

        else if (rightorleft == 2)
        {
            tempVector2 = new Vector2(-xVelocity, tempVector2.Y);
        }

        if (upordown == 1)
        {
            tempVector2 = new Vector2(tempVector2.X, -yVelocity);
        }

        else if (upordown == 2)
        {
            tempVector2 = new Vector2(tempVector2.X, yVelocity);
        }

        velocity = tempVector2;
        scale = random.Next(1, 11);
        upwardForce = -10;
        dead = false;
    }

    #endregion

    #region Update and Draw

    public void Update(GameTime gameTime)
    {
        elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

        fireParticleAnimation.Update(gameTime);

        Vector2 moveAmount = velocity * elapsed;

        xTile.Dimensions.Location newPosition = new xTile.Dimensions.Location(worldLocation.X + (int)moveAmount.X, worldLocation.Y + (int)moveAmount.Y);

        worldLocation = newPosition;

        velocity.Y += upwardForce;

        if (fireParticleAnimation.finishedPlaying)
        {
            dead = true;
        }
    }

    public void Draw(SpriteBatch spriteBatch)
    {
        spriteBatch.Draw(
            fireParticleAnimation.image.Image,
            new Rectangle((int)drawLocation.X, (int)drawLocation.Y, scale, scale),
            fireParticleAnimation.image.SizeAndsource,
            Color.White * fireParticleAnimation.image.Alpha);
    }

Fireball explosion event:

    public override void Update(GameTime gameTime)
    {
        if (enabled)
        {
            float elapsed = (float)gameTime.ElapsedGameTime.TotalSeconds;

            foreach (Heart_of_Fire.World_Objects.Particles.FireParticle particle in explosionParticles.ToList())
            {
                particle.Update(gameTime);

                if (particle.Dead)
                {
                    explosionParticles.Remove(particle);
                }
            }

            collisionRectangle = new Microsoft.Xna.Framework.Rectangle((int)wrldPstn.X, (int)wrldPstn.Y, 5, 5);

            explosionCheck = exploded;

            if (!exploded)
            {
                coreGraphic.Update(gameTime);
                tailGraphic.Update(gameTime);

                Vector2 moveAmount = velocity * elapsed;

                moveAmount = horizontalCollision(moveAmount, layer);
                moveAmount = verticalCollision(moveAmount, layer);

                Vector2 newPosition = new Vector2(wrldPstn.X + moveAmount.X, wrldPstn.Y + moveAmount.Y);

                if (hasCollidedHorizontally || hasCollidedVertically)
                {
                    exploded = true;
                }

                wrldPstn = newPosition;
                worldLocation = new xTile.Dimensions.Location((int)wrldPstn.X, (int)wrldPstn.Y);
            }

            if (explosionCheck != exploded)
            {
                for (int i = 0; i < 90; i++)
                {
                    explosionParticles.Add(new World_Objects.Particles.FireParticle(
                        new Location(
                            collisionRectangle.X + random.Next(0, 6),
                            collisionRectangle.Y + random.Next(0, 6)),
                            contentMgr));
                }
            }

            if (exploded && explosionParticles.Count() == 0)
            {
                //enabled = false;
            }
        }
    }

enter image description here

share|improve this question
    
You'll be more likely to get an answer a quicker if you remove most of that code. Just include the relevant stuff. And screenshot. –  Byte56 Apr 11 '12 at 21:53
1  
And add an example screenshot. –  Daniel Apr 11 '12 at 21:55
    
Added screenshot for clarity. The code is there just for reference, it isn't necessary to read it to understand my question. –  TheBroodian Apr 11 '12 at 22:10
    
here's some code that does this: obviam.net/index.php/particle-explosion-with-android –  Ray Tayek Apr 12 '12 at 18:12

2 Answers 2

up vote 6 down vote accepted

Judging by your description of the problem, and from the following code, I bet you are storing the location of the particles as integers.

Vector2 moveAmount = velocity * elapsed;

xTile.Dimensions.Location newPosition = new xTile.Dimensions.Location(worldLocation.X + (int)moveAmount.X, worldLocation.Y + (int)moveAmount.Y);

If you change the position to be a Vector2 or floats, I believe your problem will go away.

share|improve this answer
    
You sir, are absolutely correct. Thank you for taking the time to point that out to me! Problem's solved now, my explosion is looking more organic now. :D –  TheBroodian Apr 11 '12 at 22:35
    
Glad I could help. It's a common mistake and causes weird movement behaviors, often forming lines along certain angles. Storing positions as floating point numbers, then converting to integrals when you draw prevents the issue. –  John McDonald Apr 11 '12 at 22:40
    
+1 That was a good catch. –  Byte56 Apr 11 '12 at 22:42
int rightorleft = random.Next(0, 3);
int upordown = random.Next(1, 3);

if (rightorleft == 1)
else if (rightorleft == 2)
if (upordown == 1)
else if (upordown == 2)

I am not sure if this is everything, but I do note that Right or Left have 0,3 as their range while up or down has 1,3.. both of them only do something on 1 or 2 however. Was this done on purpose to get some of the particles to not move? It may explain the clumping you are seeing.

share|improve this answer
    
I was wondering the same thing myself, however, it is intentional, if the random throws a 0, then the xVelocity will default to 0, and my particle will move neither left or right, which is intended. However, the xVelocity for all other particles which receive an xVelocity assignment should be given varying increments of an xVelocity, which should prevent them from clumping into the 3 forks in example 2... at least, that's how it would seem it would work in my head? –  TheBroodian Apr 11 '12 at 22:28
    
Oh, I should also note that the random number generated is never the max number set. So Random.Next(1, 3); will never return a 3. –  TheBroodian Apr 11 '12 at 22:38

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.