0

I am making a top-down shooter game and I don't know how to make the picture box rotate to a specific point.

private void Game_MouseMove(object sender, MouseEventArgs e) // this is to handle all the tracking of the mouse
        {
            this.Cursor = System.Windows.Forms.Cursors.Cross; // changes the mouse to a crosshair
            int RotationAngle = 0;

            Point PositionOfMouse = Cursor.Position; // this finds the position of the mouse in the form 
            DisplayForCoOrds.Text = PositionOfMouse.ToString(); // this displays the co ords of the mouse in the form 
            

        }   

I have found the coordinates of the mouse pointer, but I need to find the angle from which the picture box is to the mouse cursor.

I think I need to do some trigonometry to get this value, but I'm not sure if there is a way in which you can use the RotatateFlip method to rotate to a specific angle.

  • [RotatateFlip](https://learn.microsoft.com/en-us/dotnet/api/system.drawing.image.rotateflip?view=dotnet-plat-ext-3.1) only allows the [RotateFlipType Enum](https://learn.microsoft.com/en-us/dotnet/api/system.drawing.rotatefliptype?view=dotnet-plat-ext-3.1) as the parameter. Maybe [this](https://stackoverflow.com/a/36871433/8335151) can help you. – 大陸北方網友 Sep 15 '20 at 08:40
  • 1
    Why are you using WinForms? use something like [Unity3D](https://unity.com/), it's free and it's c#. WinForms for games is just not the right tool for the job. You get loads of libraries that cover the scenarios you need in games. – CobyC Sep 15 '20 at 08:41
  • @CobyC I am doing this for my coursework, I would not get as many marks for just using unity and it's pre made libaries. – winston Sep 15 '20 at 08:48

1 Answers1

1

You can make use of the MathF.Atan2(..) MathF function.

Wiki on Atan2 explains what atan2 is used for.

The function atan2 (y,x) or arctan2(y,x) (from "2-argument arctangent") is defined as the angle in the Euclidean plane, given in radians, between the positive x axis and the ray to the point (x, y) ≠ (0, 0).

Calculate the Euclidean plane in relation to the picture box. Vector2 direction = new Vector2(e.X - pictureBox2.Location.X, e.Y - pictureBox2.Location.Y);

Then calculate the angle using Atan2 and convert the radiant value returned by Atan2 to degrees by multiplying it with the constant 57.2978. See wiki on Radian

then using the code linked in this answer to change the bitmap angle.

I used a simple Form with 2 picture boxes on it.

  • Assigned picture box 1 a picture as source.
  • Leave picture Box 2 blank.
  • Implement the code in the Form1_MouseMove Event.

EDIT: I changed to code to display a calculated method using the TranslateTransform(..) method and another method using the Matrix.RotateAt(...) method.

Also implemented the Dispose of the image as suggested by @Jimi

Code:

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    Image _Image = Image.FromFile(@"S:\Images\next.png");

    private void Form1_MouseMove(object sender, MouseEventArgs e)
    {

        //get the direction of the mouse by looking at the position of the picture box in relation to the mouse pointer
        Vector2 pictureBoxMtxPlane = new Vector2(e.X - pictureBoxMtx.Location.X, e.Y - pictureBoxMtx.Location.Y);
        Vector2 pictureBoxCalcPlane = new Vector2(e.X - pictureBoxCalc.Location.X, e.Y - pictureBoxCalc.Location.Y);

        //https://en.wikipedia.org/wiki/Atan2
        //atan2 - calculates the angle between the x axis and the ray line to a point. gt 0
        //57.2978 is a constant used for radians to degrees conversion (One radian is equal 57.295779513 degrees)
        //https://en.wikipedia.org/wiki/Radian
        float convertToDeg = (180 / MathF.PI);

        float angleMtx = MathF.Atan2(pictureBoxMtxPlane.Y, pictureBoxMtxPlane.X) * convertToDeg;
        float angleCalc = MathF.Atan2(pictureBoxCalcPlane.Y, pictureBoxCalcPlane.X) * convertToDeg;

        //show the angle values on the label controls
        lblMtxDeg.Text = angleMtx.ToString();
        lblCalcDeg.Text = angleCalc.ToString();
        
        //dispose the previously drawn image if there was an image (? - null conditional)
        pictureBoxMtx.Image?.Dispose();
        Bitmap mtxBitmap = new Bitmap(_Image, pictureBoxMtx.Size.Width, pictureBoxMtx.Size.Height);

        pictureBoxCalc.Image?.Dispose();
        Bitmap calcBitmap = new Bitmap(_Image, pictureBoxCalc.Size.Width, pictureBoxCalc.Size.Height);

        //set picture box 2 to the rotated source image.
        pictureBoxMtx.Image = RotateImageMatrix(mtxBitmap, angleMtx);
        pictureBoxCalc.Image = RotateImage(calcBitmap, angleCalc);

    }

    public Bitmap RotateImageMatrix(Bitmap mtxBitmap, float angle)
    {
        using (var g = Graphics.FromImage(mtxBitmap))
        using (var matrix = new Matrix()) {
            //rotate at image mid point
            matrix.RotateAt(angle, new PointF(mtxBitmap.Width / 2, mtxBitmap.Height / 2));
            g.Transform = matrix;
            //draw passed in image onto graphics object
            g.DrawImage(mtxBitmap, new PointF(0, 0));
        }
        return mtxBitmap;
    }

    public Bitmap RotateImage(Bitmap calcBitMap, float angle)
    {
        using (Graphics g = Graphics.FromImage(calcBitMap))
        {
            //move rotation point to center of image
            g.TranslateTransform((float)calcBitMap.Width / 2, (float)calcBitMap.Height / 2);
            //rotate
            g.RotateTransform(angle);
            //move image back
            g.TranslateTransform(-(float)calcBitMap.Width / 2, -(float)calcBitMap.Height / 2);
            //draw passed in image onto graphics object
            g.DrawImage(calcBitMap, new PointF(0, 0));
        }
        return calcBitMap;
    }

}

It looks like this.

Follow Mouse Cursor

CobyC
  • 2,058
  • 1
  • 18
  • 24
  • `57.2978f` is (`180 / Math.PI`, it's usually written like this, it's more clear what kind of conversion is applied). Note that you're leaking a Bitmap on every rotation. You can simply dispose of the previous: `pictureBox2.Image?.Dispose(); pictureBox2.Image = RotateImage(b, angle);`. But, you don't really need to create a new Bitmap, you can rotate the World coordinates of the Graphics Context (`Matrix.RotateAt()` is really simple to use) and draw the same Bitmap on a Control's surface, without assigning the Bitmap to a property. It's also quite faster. – Jimi Sep 15 '20 at 10:22
  • I agree with Jimi, no need to create a new bitmap each time. Load that bitmap once and store it at form level. Now handle the PAINT event of the PictureBox and draw that rotated bitmap using the supplied `e.Graphics`. In the forms mouse move event you'd simply Invalidate() the picturebox causing it to repaint itself. Do all of the calculations in the Paint() event. – Idle_Mind Sep 15 '20 at 14:40
  • Thank you for the help, but I don't know how MathF works, I thought I would do 'using MathF' but it's a class? – winston Sep 17 '20 at 14:36
  • `MathF` is the *`float`* implementation of the standard `Math` *integer* static class. It contains loads of static methods that is useful in mathematical calculations. You don't have to know how `Math` or `MathF` works. The [documentation](https://docs.microsoft.com/en-us/dotnet/api/system.mathf?view=netcore-3.1) explains the methods. If for some reason you don't want to use the classes built into .Net you will have to calculate *tan* and *Radials / Degrees* to get the same result. You can just do `MathF.TheMethodYouWant(x,y,)` it is static not disposable so you can't use `using(...)` on it. – CobyC Sep 17 '20 at 18:03
  • In this example you can also use a `Point` or `PointF` like `PointF pictureBoxMtxPlane = new PointF(e.X - pictureBoxMtx.Location.X, e.Y - pictureBoxMtx.Location.Y);` Vectors aren't something that belongs to Unity. It's a scientific / mathematical concept. see [what is a vector](https://www.dummies.com/education/science/physics/what-is-a-vector/) – CobyC Sep 18 '20 at 11:18
  • Thank you so much for your help. I'm still not sure what "lblMtxDeg.Text = angleMtx.ToString();" is doing and "using (var matrix = new Matrix())" could you explain or link me to something that would help? – winston Sep 23 '20 at 08:28
  • `lblMtxDeg` is just a label control that I added to the form to show the value of the angle, it is not needed. To understand the `using(...)` statement you can look at [this article explaining IDisposable](https://coding.abel.nu/2011/12/idisposable-and-using-in-c/#:~:text=NET%20way%20to%20signal%20that,and%20SqlCommand%20do%20implement%20IDisposable.). The [Microsoft documentation on Matrix](https://docs.microsoft.com/en-us/dotnet/api/system.drawing.drawing2d.matrix?view=dotnet-plat-ext-3.1) covers what a matrix is. [Matrices in C#](https://www.youtube.com/watch?v=fyknpOat01w) on YouTube. – CobyC Sep 23 '20 at 09:45