1
\$\begingroup\$

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

\$\endgroup\$
4
  • \$\begingroup\$ 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. \$\endgroup\$
    – House
    Commented Apr 11, 2012 at 21:53
  • 1
    \$\begingroup\$ And add an example screenshot. \$\endgroup\$ Commented Apr 11, 2012 at 21:55
  • \$\begingroup\$ Added screenshot for clarity. The code is there just for reference, it isn't necessary to read it to understand my question. \$\endgroup\$ Commented Apr 11, 2012 at 22:10
  • \$\begingroup\$ here's some code that does this: obviam.net/index.php/particle-explosion-with-android \$\endgroup\$
    – Ray Tayek
    Commented Apr 12, 2012 at 18:12

2 Answers 2

6
\$\begingroup\$

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.

\$\endgroup\$
2
  • \$\begingroup\$ 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 \$\endgroup\$ Commented Apr 11, 2012 at 22:35
  • \$\begingroup\$ 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. \$\endgroup\$ Commented Apr 11, 2012 at 22:40
0
\$\begingroup\$
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.

\$\endgroup\$
2
  • \$\begingroup\$ 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? \$\endgroup\$ Commented Apr 11, 2012 at 22:28
  • \$\begingroup\$ 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. \$\endgroup\$ Commented Apr 11, 2012 at 22:38

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .