2
votes

I'm making a game in C# XNA 4.0 and I want the game to have guns that rotate to face the player and shoot at them. I have attached a picture below to summarise this.

enter image description here

I have the shooting AI implemented, but I want to know how I can make the gun rotate so that it faces the player. The player class uses a Vector2 to represent its position and the gun class has a float value to represent its rotation, so how do I make the gun rotate to point toward the vector? (I'm pretty sure I know how to draw the rotated gun, I just need to know how to alter the gun's rotation.)

Edit:

This is my Gun class in its entirety. The constructor takes a position (where it will be placed in the level), a fire rate (how many times it fires every second or so) and a bullet speed (how fast the bullets travel). There is a seperate bullet class, but that should not be necessary for explaining the gun class.

Vector2 m_position;
decimal m_fireRate;
decimal timer;
double m_bulletSpeed;
double rotation;
List<Bullet> bullets;

public Gun(Vector2 position, decimal fireRate, double bulletSpeed)
{
    m_position = position;
    m_fireRate = fireRate;
    timer = 0.0m;
    m_bulletSpeed = bulletSpeed;
    bullets = new List<Bullet>();
    rotation = 0.0;
}

public void Update()
{
    timer += 0.025m;

    if (timer % m_fireRate == 0)
    {
        //Create a new bullet based on the rate of fire (Obtain the gun texture from the main game class)
        bullets.Add(new Bullet(m_bulletSpeed, rotation, new Vector2(m_position.X + (MyGame.GunTex.Width / 3), m_position.Y + MyGame.GunTex.Height)));
    }

    foreach (Bullet bullet in bullets.ToList())
    {
        bullet.Update();

        //Delete the bullet if it is offscreen
        if (bullet.Position.X >= MyGame.WindowWidth || bullet.Position.X <= 0 || bullet.Position.Y >= MyGame.WindowHeight)
        {
            bullets.Remove(bullet);
        }
    }

    //ROTATE TO FOLLOW PLAYER POSITION
}

public void Draw(SpriteBatch spriteBatch)
{
    spriteBatch.Draw(MyGame.GunTex, m_position, Color.White);

    foreach(Bullet bullet in bullets)
    {
        bullet.Draw(spriteBatch);
    }
}
1
Can you post your Gun class?MickyD
Yep. The gun class has been posted.user4721723
@MickyDuncan - OP is doing bullets.ToList() because he's doing bullets.Remove(bullet) inside the loop. So OP would need to do something like for (var i = bullets.Count - 1; i >= 0; i--) and then later bullets.RemoveAt(i);dbc
@dbc Ah good spot, my bad.MickyD

1 Answers

2
votes

If you just want the angle between the center of rotation of the gun turret, it's given by Math.Atan2() (in radians):

        var diffVec = playerPos - m_position;
        var angle = (float)Math.Atan2(diffVec.Y, diffVec.X);

        float newRotation;

        // To make the gun point at the player:
        newRotation = MathHelper.WrapAngle(angle); // Adjust to between -PI and PI.  Actually Atan2() returns in this range anyway.

If you want to limit the rotation of the turret to some maximum angular velocity you could do:

    const double maxAngularVelocity = Math.PI / 18.0; // 10 degrees per second.  Or whatever, your choice

    void UpdateRotation(Vector2 playerPos, decimal timeStep)
    {
        UpdateRotation(playerPos, timeStep, maxAngularVelocity);
    }

    void UpdateRotation(Vector2 playerPos, decimal timeStep, double maxAngularVelocity)
    {
        var diffVec = playerPos - m_position;
        var angle = (float)Math.Atan2(diffVec.Y, diffVec.X);

        float newRotation;

        // To make the gun point at the player:
        newRotation = MathHelper.WrapAngle(angle); // Adjust to between -PI and PI.  Actually Atan2() returns in this range anyway.

        // To make rotate towards the player with a maximum angular rotation, using the smallest direction
        var timeStepf = (float)timeStep;
        var diffAng = MathHelper.WrapAngle(newRotation - (float)rotation); // Adjust difference to between -PI and PI.
        diffAng = timeStepf * MathHelper.Clamp(diffAng / timeStepf, (float)-maxAngularVelocity, (float)maxAngularVelocity); // Clamp the difference by the maximum angular velocity.
        newRotation = MathHelper.WrapAngle((float)rotation + diffAng); // Adjust to between -PI and PI.

        rotation = newRotation;
    }

Note that, with this scheme, if the player is roughly behind the gunner and dances back and forth between less than 180 degrees and more than 180 degrees behind the gunner, the gun can vibrate between directions. You could solve this by remembering the previous rotational velocity and continuing in that direction if the player is close to 180 degrees behind the gun.

Another possibility would be to

  • Define a reaction time for the gunner (0.25 seconds, say)
  • Have the gunner aim at the position of the player 0.25 seconds ago plus a linear extrapolation of where they will be when the bullet arrives, given the distance to the player at that time (0.25 seconds ago) and the velocity of the player at that time, possibly clamped by the maximum angular velocity of the turret.
  • To make the game easier or harder, introduce larger or smaller errors into the gunner's estimate of the distance.