Help with Missile Command

Started by
43 comments, last by Glydion 6 years, 8 months ago

You should absolutely not be inverting any x coordinates in this case.

You only need to invert the mouse's y-coordinate because the game is set up to measure the angle between the bottom of the screen and the mouse cursor. Since SDL coordinates use the top-left as the default origin, you need to compensate for that by "flipping" the cursor's y-position, so that it represents vertical distance from the bottom of the screen rather than from the top.

Advertisement

Also, using cross product to determine rotation direction is great and useful, but not necessary in this case since the cannon is constrained between 0 and 180 degrees. If it were using a full circle of rotation, cross product would be the best way to determine which direction to rotate.

P.S. when you get the angle of a single vector, you're asking for the angle between it and the world x-axis.

EDIT: Aerodactyl's explanation is for gradual rotation, as in the cannon rotates towards the desired angle each frame rather than snapping to it. Everything I've written in this post assumes snapping, but if you do want gradual turning then you will need to use cross product to determine which direction to rotate.

Hey cmac,

Yes, for this basic version of Missile Command, you're right I'm okay with snapping the cannon to the desired angle.

When I've completed the game, I would like to tackle gradual rotation for more 'realism'.

@Aerodactyl55 Hey Aerodactyl, I had a question about one of your last posts.  

I know understand the importance of having 2 vectors so I can get the angle between them.  

I know how to get the vector from cannon to cursor now, but how would I get the cannon's orientation for the second vector?  

When I initialized the cannon, I set it to a starting x and y and it pretty much just faces 'straight up' upon creation.

Would this 'facing up' be its orientation in this context?

 

I did a lookup of the question and there's a nice explanation on sin/cos here when looking for direction vector.

http://www.cplusplus.com/forum/beginner/49639/

Sorry I should have looked it up first before asking.

You don't need to compare the cannon's orientation with the desired orientation (cannon->mouse angle), because you're hard setting it to whatever (cannon->mouse)'s angle is every frame.

Orientation is basically just whatever the value of your cannon's angle variable is, which as I said you're overwriting each frame to the desired value with no smoothing. In most coordinate systems, 0 degrees is facing straight right. For this reason, any sprites you rotate such as the cannon should by default face right. If they do not, you'll need to apply a mathematical offset to compensate for it, or just rotate the sprite in PhotoShop or something.

You're right when you said you need two vectors to check and angle, but it seems like you're overthinking it and confusing yourself a bit. Atan2 does compare two vectors: the vector you pass in against the world x-axis. If you pass in a vector pointing straight right into atan2, the resulting angle will be 0. In this case, that is all you need.

If you did want gradual rotation, then you would need to test the angle between the current orientation (cos(cannon.angle), sin(cannon.angle)) and the desired orientation (cannon->mouse), so that you can interpolate current towards desired without hard-setting it to the value. Like you said though, this isn't necessary for this game.

Ahhh, thanks for the great explanation cmac, I believe I'm beginning to get the hang of this.

Yes I think I need to re-work my code as when the cannon angle is at 0, it actually is pointing straight up instead of the right.

@MarcusAseth  Hi Marcus, for some reason, once you discovered that my cannon's center was set to 16,48, I decided to change it to its position in the world coordinates, which is 401, 553, as per the following code:


// Set the cannon pipe's rotation pivot point
center.x = 401;
center.y = 553;

When I entered my center coordinates as per the above, the 'rotating' of the texture got messed up and the cannon actually rotated (in an arc) and flew away from the cannon base (lol) and only came back when I moved my mouse cursor to an arbitrary position, which wasn't right.

I then decided to switch the cannon's center position to my original value here:


// Set the cannon pipe's rotation pivot point
center.x = (width / 2);
center.y = height;

When I switched the center back to my original values, the cannon stayed on the center and pivoted sub-normally (as per the original post) as it was taking the cannon pipe's x and then adding (center.x) to it and then moving down (center.y) to reach the bottom-middle of the cannon, though the angling was still not the one I wanted.

I did some digging into my render function with rotate, SDL_RenderCopyEX, and I came to the conclusion that when rendering in this function, the parameter for center (SDL_Point) takes into account the cannon x,y coordinates as the origin and then locates 16,48 based off of the texture's destination rect of the texture being displayed, not the world coordinates, as per the wiki page for the function.

 


int SDL_RenderCopyEx(SDL_Renderer*          renderer,
                     SDL_Texture*           texture,
                     const SDL_Rect*        srcrect,
                     const SDL_Rect*        dstrect,
                     const double           angle,
                     const SDL_Point*       center,
                     const SDL_RendererFlip flip)

center - a pointer to a point indicating the point around which dstrect will be rotated (if NULL, rotation will be done around dstrect.w/2, dstrect.h/2)

It must have been how SDL programmed this function.  This is what I think.

I'm getting closer to a solution to this issue so I'm pretty excited about that, but it's now 1am and my eyes are falling out, so I'll hit the hay for now.  

 

Go with that then, as long as it works :)

I would say that the important thing is to make sure that your expectation of what your code is doing consistently matches with what the code is actually doing, meaning: spam cout all over the place :P

Don't know if you know already, but if you're in a Win32 App project, then you need this code to get a console and have the cout work as expected:


//Create Console
FILE* stream;
AllocConsole();
freopen_s(&stream, "conout$", "w", stdout);

 

Hey Marcus, sorry it took a while to respond, it's been crazy the past few days.

It's good to just have some graph paper and some pencils to start sketching this logic out, it's not as foreign as it was before thanks to all of the helpful responses so far.

I'm getting closer, as cmac mentioned, I'm now inverting the y-coordinate for the mouse and I think I have to do the same for the cannon's center-y as well.

Tonight when I get home, I'll try implementing this and see what happens.

Hey guys, here's an update on my Missile Command game:

I did some sketches on graph paper to flesh out my thoughts so the logic wasn't so abstract for me, and after 2-3 days of just working through it, I finally got the cannon to follow the mouse! :D Yay!

It's not perfect but it's a start.  

Here's a screenshot GIF of it:

http://i.imgur.com/sEV0XKU.gifv

Man that took me over 2 weeks to figure out, and I wanted to thank MarcusAseth, Aerodactyl55 and cmac for their wonderful feedback in helping me out.

What finally got it to work was actually my center point, it was in the wrong position! :o 

Instead of the "center bottom" of the cannon pipe, I changed it to the dead center of the pipe (width / 2, height / 2), relative to the drawing rectangle of the cannon itself, called atan2 with the displacement between the mouse x and y and the cannon's actual x and y and voila it worked!

Here's the updated code:

Game.cpp


void Game::Update(float delta)
{
    // Game logic

    // Input, get mouse position to determine
    // the cannon pipe's angle for rendering
    int mX, mY;
    SDL_GetMouseState(&mX,&mY);

    // "Snap" the cannon angle to the angle between
    // cannon and mouse
    SetCannonAngle( mX - cannon->x, mY - cannon->y );

    // If there are no more silos,
    // start a new game
    if(GetSiloCount() == 0)
    {
        NewGame();
    }

    // Update the cannon's angle
    cannon->Update(delta);

    /*
    Test Data
    */

    // Stream the X, Y, Angle and Center values
    xValue.str("");     // Clear the stream before piping the xValue
    xValue << "X: " << mX;

    yValue.str("");     // Clear the stream before piping the yValue
    yValue << "Y: " << mY;

    angleValue.str(""); // Clear the stream before piping the angleValue
    angleValue << "Angle: " << cannon->angle;

    // Set font color to WHITE
    SDL_Color textColor = {255,255,255};

    //***************************************
    // DEBUG - Prepare the fonts for testing
    //***************************************

    // Render the X-coordinate text
    SDL_Surface* temp = TTF_RenderText_Solid( fontMouseX, xValue.str().c_str(), textColor );
    xTexture = SDL_CreateTextureFromSurface( renderer, temp );
    xWidth = temp->w;
    xHeight = temp->h;
    SDL_FreeSurface(temp);

    // Render the Y-coordinate text
    temp = TTF_RenderText_Solid( fontMouseY, yValue.str().c_str(), textColor );
    yTexture = SDL_CreateTextureFromSurface( renderer, temp );
    yWidth = temp->w;
    yHeight = temp->h;
    SDL_FreeSurface(temp);

    // Render the angle text
    temp = TTF_RenderText_Solid( fontCannonAngle, angleValue.str().c_str(), textColor );
    aTexture = SDL_CreateTextureFromSurface( renderer, temp );
    aWidth = temp->w;
    aHeight = temp->h;
    SDL_FreeSurface(temp);
}

 

This was the game changer - I initialized the center coordinates to the very center of the texture:

Cannon.cpp


// Set the cannon pipe's rotation pivot point,
// relative to the cannon pipe
center.x = (width / 2);
center.y = (height / 2);

 

After this, the angling worked.  Now I have to figure out how to get the cannon to not angle beyond 0 and 180 degrees and stay above ground.

Oh and I'm not sure if these coordinate values are correct or not, but when the angle is 45 degrees, it is facing bottom-right, straight down at 90, to the left at 180, top left at 225, straight up at 270 and top-right at 315 degrees respectively.

Is this correct?

EDIT: The above degrees were from my test data and not actually shown in the animated GIF.  For some reason the angle value shows negative values for the angles, and right now I'm not sure how to convert that so it shows values from 0 to 360 degrees.

 

As my game is not done, is it proper convention to continue the post?  

Or do I open a new post regarding the next logic challenge of my Missile Command game when I run into trouble?

Once again, I appreciate everybody's help.  I'll go to sleep now, goodnight.

I think you just need to pass -y  to atan2 in order to flip the y and have 45° top right and 315° bottom right.

and then 


if(mouseAngle < minRotation || mouseAngle > maxRotation)){
  cannonAngle = mouseAngle < minRotation ? minRotation : maxRotation;
else{
  cannonAngle = mouseAngle;
}

to have it constrained, but I'm sure you would have figured that out :P

 

Also to convert range -180°|180° in range 0°|360°:


if(mouseAngle < 0) {mouseAngle += 360;}

this means that in range 0-180 degree nothings happens, but when you get a negative angle (since after 180° atan2 gives you the negative range) like -179 instead of  181°,    -179+360 = 181 and you are good :)

This topic is closed to new replies.

Advertisement