0
votes

So I have a turret type gun, with a base sprite (Turret Base) and a separate sprite for the shaft (Turret Shaft). The shaft is drawn behind the base, and rotates to point towards the mouse position. The angle of the shaft is stored as _turretAngle.

My issue comes in when I try to position the bullet correctly. The bullet in question (Bullet) needs to always fire from the tip of the shaft, angled correctly, and of course, in the proper direction.

To angle the bullet, I simply use _turretAngle, and the direction is new Vector2(-Math.Cos(_turretAngle), -Math.Sin(_turretAngle)); but I can't get the initial position correct.

I currently have it as follows:

int i = _bullets.Count - 1;
Bullet b = _bullets[i];

b.SetAngle(_turretAngle);

float cos = (float)Math.Cos(_turretAngle);
float sin = (float)Math.Sin(_turretAngle);

b.SetDirection(new Vector2(-cos, -sin));

var posX = (Position.X - (Shaft.Width * cos) - (b.Width * cos)) + (Width / 2) - ((b.Width / 2) * cos);
var posY = Position.Y - (Shaft.Height * sin) - (b.Height * sin);

b.SetPosition((int)posX, (int)posY);

b.BulletState = Bullet.State.Active;

The bullet is so close to being position properly, but it is not position at the shaft, instead (depending on the angle of the turret) positioned anywhere from 5-15 pixels ahead of the turret.

How it appears currently:

Current

How I'd like it to appear:

Ideal

I know I might be being nit-picky here, but I'd really like to know how the math is wrong.

I realize there might be some information missing, so let me know if I need to add anything.

1
I would suggest to just spawn this bullet on the same position as turet is, then apply the same rotation matrix and then apply translation matrix which should move the object in y direction equal to height of the turret + height of the bullet. Reason being that you're applying rotation matrix first and then move the object. It should align perfectly with not even one pixel gap.Mateusz

1 Answers

2
votes

Extending my comment with some code samples and more descriptive explanation.
You did not specified if your angle is in degrees or radians so I'm assuming you're using degrees which has to be converted. Next thing would be to create a rotation and translation matricies which you'll then apply to your bullet object.

My suggestion is to create an extension class like such :

public static class TransformEx
{
    // method to convert degree into radians
    public static double ToRadians(this double degree)
    {
        return ( ( degree % 360.0D ) / 180.0D ) * Math.PI;
    }

    // get the rotation matrix
    public static Matrix GetRotationMatrix(this double angle, Vector2 rotationCenter)
    {
        double radians = angle.ToRadians();
        double cos = Math.Cos(radians);
        double sin = Math.Sin(radians);

        return new Matrix
        {
            M11 = cos,
            M12 = sin,
            M21 = -sin,
            M22 = cos,
            M41 = ( rotationCenter.X * ( 1.0 - cos ) ) + ( rotationCenter.Y * sin ),
            M42 = ( rotationCenter.Y * ( 1.0 - cos ) ) - ( rotationCenter.X * sin )
        };
    }

    // get translation matrix
    public static Matrix GetTranslationMatrix(this Vector2 position)
    {
        return new Matrix
        {
            M41 = position.X,
            M42 = position.Y,
            M43 = 0
        };
    }
}

Having this you can now proceed with the calculations and set up your bullet. Keep in mind though that you have to calculate translation on X axis for the bullet to be displayed correctly.

// spawn the bullet
Bullet b = _bullets[i];

// get size of bullet and turret for further calculations
Vector2 bulletSize = new Vector2(b.Width, b.Height);
Vector2 turretSize = new Vector2(turret.Width, turret.Height);

// move bullet to the same position as turret
b.Position = turret.Position;

// calculate new translation depending on the size difference
double deltaX = (turretSize.X - bulletSize.X) / 2;
Vector2 bulletNewPos = new Vector2(b.Position.X + deltaX, b.Position.Y + turretSize.Height);

// using extension methods get rotation matrix
// this will rotate referencing the middle point of your bullet
Matrix mtxRotation = _turretAngle.GetRotationMatrix(new Vector2(bulletSize.X / 2, bulletSize.Y / 2));

// now you have to create translation matrix
// but remember to calculate position correctly
Matrix mtxTranslation = bulletNewPos.GetTranslationMatrix();

// now all you have to do is to rotate and then translate your bullet
Matrix transformMatrix = mtxTranslation * mtxRotation;

Now the transformMatrix contains all of the necessary transformations details you need. You can use overload method for SpriteBatch.Begin to use this transformation matrix.

But if you still want to use your SetPosition and SetAngle methods. You can simply use this matrix to extract new position and apply this using :

b.SetAngle(_turretAngle);
// M41 >> position.X
// M42 >> position.Y
// M43 >> position.Z
b.SetPosition((int)transformMatrix.M41, (int)transformMatrix.M42);

EDIT :
This assumes that your axis values are defined as such :

   Y +
   ^
   |
   |
   |           
-X +------------> X +
   Y-