Public Group

# Help with Missile Command

## Recommended Posts

Hello everyone.

I'm fairly new to game programming and I've started the beginner's path to learning how to do game programming incrementally, as suggested by Alpha_ProgDes (hope I spelled his name right) in his fantastic article about Game Programming for Beginners and the 10 requisite games that you absolutely need to know and finish in order to get real game programming experience.

After about 4 or so months of trial and error and growing pains, I was able to complete a working (but not perfect with lots of bad practice, global variables, etc.) copy of Pong, Snakes and Breakout.  Right now I'm just doing enough to get the games to work (passable) and then later, I will go and re-do them after getting better at this.

Currently, I'm on the fourth game of this list, which is Missile Command, and I'm only a little bit into the game and I'm now stuck on the first 'logic' aspect of just getting the cannon to rotate and change angle based on the position of the mouse.

No matter what I've tried, I just can't seem to get the cannon to angle properly as per the precise location of the mouse.

When I run the game, the cannon will move arbitrarily towards the right side, but never turn and angle to the left, even if my mouse is on the left-hand side of the game screen.

I've created some test output for the x and y coordinate of the mouse position as well as the cannon's angle but it hasn't helped me that much yet.

If it is allowed in these forums, I can record a small video that shows the output and paste it here as a link.  But I'm not sure.

Here is my code for main as well as the Game, Vector2 and Cannon classes (I have other classes but I told myself I would only work on them once I've solved this particular logic problem):

main.h

#ifndef MAIN_H_
#define MAIN_H_

#include "game.h"

int main(int argc, char* argv[]);

#endif // MAIN_H_

main.cpp

//******************************************
// Missile Command - v1.0.0
// By: Pedro Miranda Jr. (Glydion)
//
// Start Date: Wednesday May 31, 2017
// Finish Date:
//******************************************

#include "main.h"

int main(int argc, char* argv[])
{
// Create the game object
Game* game = new Game();

// Initialize and run the game
if(game->Init())
{
game->Run();
}

// Clean up
delete game;

return 0;
}

Vector2.h

#ifndef VECTOR2_H_INCLUDED
#define VECTOR2_H_INCLUDED

#include <math.h>

class Vector2
{
public:
Vector2();
Vector2(float x, float y);
~Vector2();

float x, y;

};

#endif // VECTOR2_H_INCLUDED

Vector2.cpp


#include "Vector2.h"

Vector2::Vector2()
{

}

Vector2::Vector2(float x, float y)
{
this->x = x;
this->y = y;
}

Vector2::~Vector2()
{

}

Cannon.h

#ifndef CANNON_H_INCLUDED
#define CANNON_H_INCLUDED

#include "Entity.h"

#include <math.h>

// Define a missile speed in pixels per second
/*const float MISSILE_SPEED = 550;

class Missile : public Entity
{
public:
Missile(SDL_Renderer* renderer);
~Missile();

void Update(float delta);
void Render(float delta);

void SetDirection(float dirX, float dirY);

float dirX, dirY;

bool state;         // Check if the missile is alive

private:
SDL_Texture* texture;
};
*/

class Cannon : public Entity
{
public:
Cannon(SDL_Renderer* renderer);
~Cannon();

// x, y, width and height for base piece
float baseX, baseY, baseWidth, baseHeight;

// cannon pipe angle
double angle;

// Missiles that the cannon fires
//Missile missile;

// Fire a missile
//void FireMissile();

void Update(float delta);
void Render(float delta);

float GetCenterX();
float GetCenterY();

private:
SDL_Texture* cannonTexture;     // Texture for cannon pipe
SDL_Texture* baseTexture;       // Texture for cannon base
SDL_Point center;
};

#endif // CANNON_H_INCLUDED

Cannon.cpp


#include "Cannon.h"

//******************************
// Missile function definitions
//******************************

//******************************
// Cannon function definitions
//******************************

Cannon::Cannon(SDL_Renderer* renderer) : Entity(renderer)
{
// Create cannon pipe texture
cannonTexture = SDL_CreateTextureFromSurface(renderer, surface);
SDL_FreeSurface(surface);

// Create cannon base texture
baseTexture = SDL_CreateTextureFromSurface(renderer, surface);
SDL_FreeSurface(surface);

// Set the cannon pipe's dimensions
width = 32;
height = 48;

// Set the cannon base's dimensions
baseWidth = 64;
baseHeight = 24;

// Set the cannon pipe's vertical position
x = 417 - width;
y = 505;

// Set the cannon base's vertical position
baseX = 432 - baseWidth;
baseY = 545;

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

// Set the cannon pipe's angle
angle = 0.0f;
}

Cannon::~Cannon()
{
// Clean resources
SDL_DestroyTexture(cannonTexture);
SDL_DestroyTexture(baseTexture);
}

void Cannon::Update(float delta)
{

}

void Cannon::Render(float delta)
{
// Set source rectangle for cannon pipe
SDL_Rect rect;
rect.x = (int)(x + 0.5f);
rect.y = (int)(y + 0.5f);
rect.w = width;
rect.h = height;

// Render the cannon pipe
SDL_RenderCopyEx(renderer,cannonTexture,0,&rect,angle,&center,SDL_FLIP_NONE);

// Set source rectangle for cannon base
rect.x = (int)(baseX + 0.5f);
rect.y = (int)(baseY + 0.5f);
rect.w = baseWidth;
rect.h = baseHeight;

// Render the cannon base
SDL_RenderCopy(renderer,baseTexture,0,&rect);
}

float Cannon::GetCenterX()
{
return center.x;
}

float Cannon::GetCenterY()
{
return center.y;
}

Game.h

#ifndef GAME_H_
#define GAME_H_

// Preprocessors
#include "SDL.h"
#include "SDL_image.h"
#include "SDL_ttf.h"
#include "SDL_mixer.h"
#include <iostream>
#include <sstream>
#include <stdio.h>
#include <math.h>
#include <cmath>

// Inclusions for game objects
#include "Cannon.h"
#include "Vector2.h"
#include "Ground.h"
#include "Silo.h"

#define FPS_DELAY 500

class Game
{
public:
Game();
~Game();

bool Init();
void Run();

private:
SDL_Window* window;
SDL_Renderer* renderer;

SDL_Texture* texture;

// Timing
unsigned int lastTick, fpsTick, fps, frameCount;

// Test
float testX, testY;

/*
Fonts for testing
*/
TTF_Font* fontMouseX;
TTF_Font* fontMouseY;
TTF_Font* fontCannonAngle;

std::stringstream xValue;
std::stringstream yValue;
std::stringstream angleValue;

SDL_Texture* xTexture;
SDL_Texture* yTexture;
SDL_Texture* aTexture;

int xWidth, xHeight, yWidth, yHeight, aWidth, aHeight;

/*
END
*/

// Game objects
Cannon* cannon;                                 // The cannon, both the pipe and the base
Silo* silo;
Ground* ground;                                 // The ground

SDL_Cursor* cursor;                             // The crosshair cursor

Vector2* target;                                // The vector of the cursor

void Clean();                                   // Cleanup function
void Update(float delta);                       // Update game elements
void Render(float delta);                       // Render game elements

void NewGame();                                 // Start a new game

void SetCannonAngle(Vector2* theVector);        // Set the angle of the cannon pipe
void ExplodeMissile();                          // Explode the missile once it
// has reached the target

void CheckAsteroidCollisions();                 // See if missile hits asteroid
void CheckSiloCollisions();                     // See if asteroid hits silo
int GetSiloCount();                             // Check if there are silos remaining

};

#endif // GAME_H_

Game.cpp


#include "Game.h"

Game::Game()
{
window = 0;
renderer = 0;
}

Game::~Game()
{

}

bool Game::Init()
{
// Initialize the SDL video and audio subsystems
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO);

// Create window
window = SDL_CreateWindow("Missile Command v1.0", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
800, 600, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL);

if(!window)
{
std::cout << "Error creating window: " << SDL_GetError() << std::endl;
return false;
}

// Create renderer
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
if(!renderer)
{
std::cout << "Error creating renderer: " << SDL_GetError() << std::endl;
return false;
}

TTF_Init();

fontMouseX = TTF_OpenFont("lato.ttf", 14);
fontMouseY = TTF_OpenFont("lato.ttf", 14);
fontCannonAngle = TTF_OpenFont("lato.ttf", 14);

// Initialize resources
texture = SDL_CreateTextureFromSurface(renderer,surface);
SDL_FreeSurface(surface);

// Set mouse cursor to crosshair
cursor = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR);
SDL_SetCursor(cursor);

// Initialize timing
lastTick = SDL_GetTicks();
fpsTick = lastTick;
fps = 0;                // Set starting FPS value
frameCount = 0;         // Set starting frame count

testX = 0;
testY = 0;

return true;
}

void Game::Clean()
{
// Clean resources
SDL_DestroyTexture(texture);
SDL_FreeCursor(cursor);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);

// Clean test data
SDL_DestroyTexture(xTexture);
SDL_DestroyTexture(yTexture);
SDL_DestroyTexture(aTexture);
}

void Game::Run()
{
// Create game objects
cannon = new Cannon(renderer);
ground = new Ground(renderer);
silo = new Silo(renderer);

// Start a new game
NewGame();

// Main loop
while(1)
{
// Event handler
SDL_Event e;

// If event is a QUIT event, stop the program
if(SDL_PollEvent(&e))
{
if(e.type == SDL_QUIT)
{
break;
}
}

// Calculate delta and fps
unsigned int curTick = SDL_GetTicks();
float delta = (curTick - lastTick) / 1000.0f;

// Cap FPS delay to specific amount
if(curTick - fpsTick >= FPS_DELAY)
{
fps = frameCount * (1000.0f / (curTick - fpsTick));
fpsTick = curTick;
frameCount = 0;
//std::cout << "FPS: " << fps << std::endl;
char buf[100];
snprintf(buf,100,"Missile Command v1.0 (fps: %u)", fps);
SDL_SetWindowTitle(window,buf);
}
else
{
frameCount++;
}

lastTick = curTick;

// Update and render the game
Update(delta);
Render(delta);
}

delete cannon;
delete ground;
delete silo;

Clean();

// Close the fonts
TTF_CloseFont(fontMouseX);
TTF_CloseFont(fontMouseY);
TTF_CloseFont(fontCannonAngle);

TTF_Quit();

SDL_Quit();
}

void Game::NewGame()
{

}

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);

// Create the target vector, normalize it
// and pass it to the cannon to set the
// current angle
target = new Vector2(mX,mY);
SetCannonAngle( target );

// 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 and Angle 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
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
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
temp = TTF_RenderText_Solid( fontCannonAngle, angleValue.str().c_str(), textColor );
aTexture = SDL_CreateTextureFromSurface( renderer, temp );
aWidth = temp->w;
aHeight = temp->h;
SDL_FreeSurface(temp);
}

void Game::SetCannonAngle( Vector2* theVector )
{
// Calculate arc tangent between the
// the mouse's position and the center
// axis of the cannon and convert to degrees

double theAngle = atan2( theVector->x, theVector->y );
theAngle = ConvertDegrees(theAngle);
cannon->angle = theAngle;
}

{
// Convert the angle from radians to
// degrees and return the value
return radians * ( 180 / 3.141592653589793238 );
}

int Game::GetSiloCount()
{
int siloCount = 0;

// If the current silo is still alive,
// the game is not over
if(silo->state)
{
siloCount = 1;
}

return siloCount;
}

void Game::Render(float delta)
{
// Clear the renderer
SDL_RenderClear(renderer);

// Render the game objects
cannon->Render(delta);
ground->Render(delta);
silo->Render(delta);

/*
Setting source rectangles and then
rendering the x, y and angle fonts
*/

// For x
SDL_Rect rect;
rect.x = 20;
rect.y = 20;
rect.w = xWidth;
rect.h = xHeight;
SDL_RenderCopy(renderer, xTexture, 0, &rect);

// For y
rect.x = 20;
rect.y = 40;
rect.w = yWidth;
rect.h = yHeight;
SDL_RenderCopy(renderer, yTexture, 0, &rect);

// For angle
rect.x = 20;
rect.y = 60;
rect.w = aWidth;
rect.h = aHeight;
SDL_RenderCopy(renderer, aTexture, 0, &rect);

// Present the renderer to display
SDL_RenderPresent(renderer);
}

That's the code.

Attached to this post is a set of stills that show that the cannon is not angling properly towards the crosshair cursor, either on the left side or the right side of the video screen.

Any help would be appreciated, folks.

Edited by Glydion
Forgot to bold some words.

##### Share on other sites

I don't have SDL so can't try, but watching at the code I was wondering, where you write

target = new Vector2(mX,mY);
SetCannonAngle( target );

What is this angle set relative to? I mean, are you taking into consideration the origin of the cannon or you're taking the mouse input angle against the 0,0 coordinates of the screen?

Ihave no idea of what I'm talking about though probably

but I mean,

void Game::SetCannonAngle( Vector2* theVector )
{
// Calculate arc tangent between the
// the mouse's position and the center
// axis of the cannon and convert to degrees

double theAngle = atan2( theVector->x, theVector->y );
theAngle = ConvertDegrees(theAngle);
cannon->angle = theAngle;
}

I may be totally wrong but in that comment you mention the center axis of the cannon but I don't see it inside the function, unless I'm missing it

Edited by MarcusAseth

##### Share on other sites

I also checked the function atan2, it says

Quote
Compute arc tangent with two parameters

Returns the principal value of the arc tangent of y/x, expressed in radians.

In your code though, the x comes before the y so the inputs are reversed

Edited by MarcusAseth

##### Share on other sites

Hmm yes I noticed that too.  I use the bottom-center of the cannon when rendering the cannon itself, but not in the calculation of the angle.

So far I'm just passing the mouse X and Y and just making a Vector2 out of it.

Whoops, my bad.  Yes atan2 is supposed to have y before x, I'd better fix that, thanks for pointing that out.

I'm just thinking about how to take the cannon's center into account for the atan2 function.  Hmm, perhaps I subtract them?  I'll give that a shot and I'll post my update.

Oh one other question, do I need a second Vector2 to get the angle to work properly?

Edited by Glydion

##### Share on other sites

I suggest always sketch something that helps you reasoning   My sketch below

If your mouse has 350 then is effectively only 50 in the X, compared to the origin, so I suggest trying to pass atan2((mY-originY, mX - originX)) and see if it works

Edit: it won't work for the Y,  maybe ((originY) - (originY - mY)) or something like that for the Y

Edited by MarcusAseth

##### Share on other sites

Yeah I didn't think to sketch it out actually lol.

Pong and Snakes were pretty easy to just keep in my head (as they're simple in concept) but this Missile Command is the first game that's really challenged me.

I should know better.  Yes that makes so much more sense when it is drawn out in a sketch.

I'll give that a try in a bit, just got to do some laundry  I appreciate your help Marcus.

##### Share on other sites

Okay, just made those changes to the atan2 function and it appears that the cannon still behaves the same way.

I think we're getting closer, but I'll try some more tomorrow.  Thanks again for the assistance.

I'm going to sketch my thoughts out.

##### Share on other sites

Well, could be also this

 // Set the cannon pipe's dimensions
width = 32;
height = 48;

// Set the cannon base's dimensions
baseWidth = 64;
baseHeight = 24;

// Set the cannon pipe's vertical position
x = 417 - width;
y = 505;

// Set the cannon base's vertical position
baseX = 432 - baseWidth;
baseY = 545;

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

width is 32. You set the x position around 400, but then the center.x for the rotation pivot is just width/2, which means the center of the cannon is 16 while your cannon X position is near 400 ...doesn't looks legit

Edited by MarcusAseth

##### Share on other sites

Hey Marcus,

Wow, I really should have thought these through.

Initially, when I set those values, I was just correlating them to where they would be on the screen.  Man I was way off.

I'm at work now but I'll redo that code when I get home later.  Great find, thanks for pointing that out.

##### Share on other sites

Hello again,

I'm still working on getting it to do what I want, including grabbing graph paper and just plotting points.

I skipped over this part (when I really shouldn't have) and now I'm paying for it because of results that I can't seem to track.

I'm going to research arc tangents more because I feel like I'm missing something very important in this logic for the angle rotations.

So tonight, it's just me with pen and paper working through my logic some more (for the next game, I won't even touch a keyboard until I got this hammered down, better habit in the long run).

I appreciate your help from last night Marcus.  I'll keep at this and update later.

Edited by Glydion

1. 1
Rutin
26
2. 2
3. 3
4. 4
JoeJ
18
5. 5

• 14
• 14
• 11
• 11
• 9
• ### Forum Statistics

• Total Topics
631757
• Total Posts
3002135
×