libertylocked
libertylocked

Reputation: 912

Questions about drawing 2D bullet paths in XNA

So basically it's a 2D platform shooter game. And I want to draw the path of each individual bullet. There should be a white line after every bullet, and the alpha value should be decreasing from the bullet to the position that it was fired from.

How can I create such effect?

EDIT: I'm just asking for a basic method instead of detailed code. I think I might need to use particle effects to do it?

EDIT 2: Thanks! But can I change the source rectangle of gradline texture instead of scaling it?

Please correct me if I got it wrong somewhere, thanks!

Texture2D gradline should look like this: enter image description here

(As far as I remember when using Math.Atan2 to find the angle of a vector, the 0 deg should be at the positive Y axis)

I'm using this method to find the degree of a vector. Since the positive Y goes down the window in the window coordinate system, I changed to -vector.Y here: (which makes more sense to me)

protected float VectorToRadians(Vector2 vector)
{
    return (float)Math.Atan2(vector.X, -vector.Y);
}

And I also need to find the distance between the bullet and the position that it was fired, to determine the height of source rectangle. So I have a method like this

protected int GetRectangleHeight(Vector2 startingPos, Vector2 currentPos, int lineTextureLength)
{
    float distance = (startingPos - currentPos).Length();
    if (distance < lineTextureLength)  // if distance is smaller than the texture length, only draw part of it
        return distance;
    else                        // if distance is larger or equal to the texture length, draw the entire texture
        return lineTextureLength;
}

And finally in draw

spriteBatch.Draw(
  gradline,
  bulletCurrentPos,
  new Rectangle(0, 0, gradline.Width, GetRectangleHeight(bulletStartingPos, bulletCurrentPos),
  Color.White,
  VectorToRadians(bulletCurrentPos - bulletStartingPos),
  Vector2.Zero,     // actually i think setting the top-center as origin might look better
  1f,                  // full scale
  SpriteEffects.None,
  0f);

And when the bullet hits something and is gone, the path should still be drawn onto screen. During that time the source rectangle's y (and also height, I think, to make sure that the rectangle doesn't go out of the texture) should be changed to draw the end part of the trail only, until y reaches the height of the texture.

Upvotes: 1

Views: 870

Answers (1)

user1306322
user1306322

Reputation: 8721

You can do this with an image of a line fading to transparent color. Like this:

enter image description here

Here is a version that will draw a trace from the center of the screen to the cursor (laser sight):

(If you want it to be drawn from someplace else, you can replace centerScreen and cursor_v2 with your preferred Vector2, like current position of a bullet and the position from which that bullet was originally shot. For that, the code piece in the end of my answer might be best.)

Texture2D gradline; // set this var to your fading line texture in LoadContent 

Vector2 centerScreen = new Vector2(
  GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height);
Vector2 cursor_v2 = new Vector2(currentMouseState.X, currentMouseState.Y);

spriteBatch.Draw(           // this is one of many Draw overloads
  gradline,                // your line texture
  centerScreen,           // we just set this to be at your game window center
  null,                  // this means entire texture will be drawn
  Color.White,                        // no tinting
  GetAngle(centerScreen, cursor_v2), // this rotates texture, see my code below
  Vector2.Zero,      // texture will be drawn from screen center without offset
  new Vector2(GetDistance(centerScreen, cursor_v2) / gradline.Width, 1),
    // ↑ This is scaling vector, X scales texture to distance from screen center
   //  to cursor and then divides by the texture's length.
  //  See how Y is set to 1, which means texture height will not be changed.

  SpriteEffects.None,  // this can mirror the texture, we don't do this now
  0f);                // everybody leaves this zero :p

One more time without comments:

spriteBatch.Draw(gradline, centerScreen, null, Color.White, GetAngle(centerScreen, cursor_v2), Vector2.Zero, new Vector2(GetDistance(centerScreen, cursor_v2) / gradline.Width, 1), SpriteEffects.None, 0f);

My code for getting angle value: (just place it somewhere in your Game class)

public float GetAngle(Vector2 v1, Vector2 v2)
{
    Vector2 v = Vector2.Normalize(Vector2.Subtract(v2, v1));

    return (float)Math.Atan2(v.Y, v.X);
}

Alternatively, you can make a texture of constant length and just rotate it with this code:

spriteBatch.Draw(gradline, centerScreen, null, Color.White,
  GetAngle(centerScreen, cursor_v2), Vector2.Zero, 1, SpriteEffects.None, 0f);
                                      // look here ↑
  // this Draw overload takes float value instead of Vector2 for scaling textures
  //   so with 1 the texture size will not be scaled

Another way is to create particles every half-second or so at positions of every bullet in the game, and after particle expires, delete it. I suspect your bullets move in lines, not in parabolas, so it's probably not worth it performance wise.

If your bullets change their direction often, you could draw their full path, but that would require drawing a list of VertexPositionColor, which is more complex, but may give nice results.

Upvotes: 1

Related Questions