Roids version 1.5

(1/1)

Richard Marks:
An update to Roids source. Now we've got the ability to shoot! 8)

Code:

/*
Roids.cpp
'Roids! 1.5
A simple Asteroids clone in C/C++
(C) 2010, Richard Marks

Features in this version:
* Ship Flight Physics
* Ship Flight Physics
* Animated Ship
* Graphics created procedurally (no external resources)
* Player can shoot bullets
*/

// link to allegro on MSVC
#if defined(_WIN32) || defined(WIN32)
#pragma comment(lib, "alleg.lib")
#endif

#include <allegro.h>
#include <math.h>

BITMAP* backBuffer;

/*
player variables

prefix with "player"

X, Y - the position of the upperleft corner of the sprite
XV, YV - the velocity of the ship
R - the rotation angle in radians
Image - the current graphic for the ship to draw

FrameNoThrust - the graphic template for the ship with no thrusters
FrameThrust - the graphic template for the ship with thrusters
*/
float playerX, playerY, playerXV, playerYV, playerR;
BITMAP* playerImage;
BITMAP* playerFrameNoThrust;
BITMAP* playerFrameThrust;

/*
player bullet variables

prefix with "pBullet"

X, Y - the position of the bullet
XV, YV - the velocity of the bullet
Alive - when false, the bullet will be recycled

Each variable is an array since we want to be able
to shoot many bullets and not just a single bullet.
*/

// our maximum number of bullets that we can fire at any one time.
const int MAX_BULLETS = 100;

float pBulletX[MAX_BULLETS];
float pBulletY[MAX_BULLETS];
float pBulletXV[MAX_BULLETS];
float pBulletYV[MAX_BULLETS];
bool pBulletAlive[MAX_BULLETS];

bool LoadContent()
{
const int WHITE = makecol(0xFF, 0xFF, 0xFF);
const int TRANSPARENT = makecol(0xFF, 0x00, 0xFF);

// create the player graphics
const int playerWidth = 48;
const int playerHeight = 24;

playerFrameNoThrust = create_bitmap(playerWidth, playerHeight);
playerFrameThrust = create_bitmap(playerWidth, playerHeight);
clear_to_color(playerFrameNoThrust, TRANSPARENT);
clear_to_color(playerFrameThrust, TRANSPARENT);

// draw the ship with no thruster
const int shipWidth = (playerWidth / 5) * 4;
const int thrustOffset = (playerHeight / 4) * 3;
line(playerFrameNoThrust, playerWidth - shipWidth, 0, playerWidth - 1, playerHeight / 2, WHITE);
line(playerFrameNoThrust, playerWidth - shipWidth, 0, playerWidth - shipWidth, playerHeight - 1, WHITE);
line(playerFrameNoThrust, playerWidth - shipWidth, playerHeight - 1, playerWidth - 1, playerHeight / 2, WHITE);

// copy the ship image to the shp with thruster and draw the thruster
blit(playerFrameNoThrust, playerFrameThrust, 0, 0, 0, 0, playerWidth, playerHeight);
triangle(playerFrameThrust,
0, playerHeight / 2,
playerWidth - shipWidth, thrustOffset,
playerWidth - shipWidth, playerHeight - (thrustOffset + 1), WHITE);

// start the player with the no thruster image
playerImage = playerFrameNoThrust;

// start the player in the center of the screen
playerX = backBuffer->w / 2;
playerY = backBuffer->h / 2;
playerX -= playerImage->w / 2;
playerY -= playerImage->h / 2;

// no rotation to start
playerR = 0.0f;

// not moving
playerXV = 0.0f;
playerYV = 0.0f;

// make sure all bullets are dead at startup
for (int i = 0; i < MAX_BULLETS; i++)
{
pBulletAlive[i] = false;
}

return true;
}

void FireBullet()
{
const float bulletAcceleration = 4.0f;
const float bulletShift = 1.75f;

// find an available bullet
for (int i = 0; i < MAX_BULLETS; i++)
{
// if the bullet is alive, continue
if (pBulletAlive[i])
{
continue;
}

// set the bullet velocity based on the player's ship rotation
pBulletXV[i] = (float)cos(playerR) * bulletAcceleration;
pBulletYV[i] = (float)sin(playerR) * bulletAcceleration;

// apply the player ship's velocity to the bullet velocity
pBulletXV[i] += playerXV;
pBulletYV[i] += playerYV;

// start the bullet from the center of the player's ship
pBulletX[i] = (playerX + (playerImage->w / 2)) + pBulletXV[i] * bulletShift;
pBulletY[i] = (playerY + (playerImage->h / 2)) + pBulletYV[i] * bulletShift;

// turn "on" the bullet so it will be updated and drawn
pBulletAlive[i] = true;

// bail out the function now that we've fired the bullet
return;
}
}


void UpdateFrame()
{
const float maxRotation = 6.28318531f;
const float minRotation = -maxRotation;
const float rotationSpeed = 0.05f;
const float accelerationSpeed = 0.13f;
const float maxVelocity = 10.0f;
const float minVelocity = -maxVelocity;
const float dragForce = 0.02f;


// pressing and releasing the spacebar will fire a bullet
static bool fireButton = false;
if (key[KEY_SPACE])
{
if (!fireButton)
{
fireButton = true;
}
}
else
{
if (fireButton)
{
fireButton = false;
FireBullet();
}
}

// if the up arrow is held down
if (key[KEY_UP])
{
// change the player image to show the thruster
playerImage = playerFrameThrust;

// accelerate in the direction of rotation
playerXV += (float)(cos(playerR) * accelerationSpeed);
playerYV += (float)(sin(playerR) * accelerationSpeed);

// make sure we don't accelerate too much
if (playerXV > maxVelocity) { playerXV = maxVelocity; }
if (playerXV < minVelocity) { playerXV = minVelocity; }
if (playerYV > maxVelocity) { playerYV = maxVelocity; }
if (playerYV < minVelocity) { playerYV = minVelocity; }
}
else // if the up arrow is not held down
{
// change the player image to not show the thruster
playerImage = playerFrameNoThrust;

// apply some dragging force to slow the ship down gradually
if (playerXV < 0.0f) { playerXV += dragForce; }
if (playerXV > 0.0f) { playerXV -= dragForce; }
if (playerYV < 0.0f) { playerYV += dragForce; }
if (playerYV > 0.0f) { playerYV -= dragForce; }
}

// if the left arrow is held down
if (key[KEY_LEFT])
{
// rotate the player counter clockwise
playerR -= rotationSpeed;
if (playerR < minRotation)
{
playerR = maxRotation;
}
}
else if (key[KEY_RIGHT]) // if the right arrow is held down
{
// rotate the player clockwise
playerR += rotationSpeed;
if (playerR > maxRotation)
{
playerR = minRotation;
}
}

// apply the player velocity to the position to make the ship move
playerX += playerXV;
playerY += playerYV;

// warp the player to opposing edges of the screen
if (playerX < -playerImage->w) { playerX = backBuffer->w; }
if (playerX > backBuffer->w) { playerX = -playerImage->w; }
if (playerY < -playerImage->h) { playerY = backBuffer->h; }
if (playerY > backBuffer->h) { playerY = -playerImage->h; }

// update the bullets
for (int i = 0; i < MAX_BULLETS; i++)
{
// only update bullets that are alive
if (!pBulletAlive[i])
{
continue;
}

// apply the velocity to the bullet
pBulletX[i] += pBulletXV[i];
pBulletY[i] += pBulletYV[i];

// if the bullet goes off the screen
if (
pBulletX[i] < 0 ||
pBulletX[i] > backBuffer->w ||
pBulletY[i] < 0 ||
pBulletY[i] > backBuffer->h)
{
// kill the bullet so we can reuse it
pBulletAlive[i] = false;
}
}
}

// draws a sprite rotated at the spefied angle at the specified position
// angle needs to be in degrees
void DrawSpriteRotated(BITMAP* sprite, BITMAP* target, int x, int y, int angle)
{
if (!sprite || !target) { return; }
fixed rotation = ftofix((0.711 * (float)angle));
BITMAP* t = create_bitmap(sprite->w, sprite->h);
blit(sprite, t, 0, 0, 0, 0, t->w, t->h);
rotate_sprite(target, t, x, y, rotation);
destroy_bitmap(t);
}

void RenderFrame()
{
const int bulletColor = makecol(0xFF, 0xFF, 0xFF);

clear_bitmap(backBuffer);

// draw all bullets that are alive
for (int i = 0; i < MAX_BULLETS; i++)
{
if (!pBulletAlive[i])
{
continue;
}

circlefill(backBuffer, pBulletX[i], pBulletY[i], 1, bulletColor);
}

DrawSpriteRotated(playerImage, backBuffer, playerX, playerY, playerR * 57.2957795f);

blit(backBuffer, screen, 0, 0, 0, 0, backBuffer->w, backBuffer->h);
}

void UnloadContent()
{
destroy_bitmap(playerFrameNoThrust);
destroy_bitmap(playerFrameThrust);
}

// everything below is just a small allegro framework app to host the game
static volatile int timerTickCount = 0;
static volatile bool mainThreadIsRunning = true;

static void OnTimerTickEvent()
{
timerTickCount++;
}
END_OF_FUNCTION(OnTimerTickEvent)

static void OnCloseButtonClickEvent()
{
mainThreadIsRunning = false;
}
END_OF_FUNCTION(OnCloseButtonClickEvent)

int main(int argc, char* argv[])
{
allegro_init();
install_keyboard();
install_timer();
set_color_depth(24);
set_gfx_mode(GFX_AUTODETECT_WINDOWED, 800, 600, 0, 0);
backBuffer = create_bitmap(SCREEN_W, SCREEN_H);
set_window_title("Roids v1.5 (C) 2010, Richard Marks");
LOCK_FUNCTION(OnCloseButtonClickEvent);
LOCK_FUNCTION(OnTimerTickEvent);
LOCK_VARIABLE(timerTickCount);
set_close_button_callback(OnCloseButtonClickEvent);
install_int_ex(OnTimerTickEvent, BPS_TO_TIMER(30));
srand(time(0));
if (!LoadContent())
{
return 1;
}
while(mainThreadIsRunning)
{
if(timerTickCount > 0)
{
while(timerTickCount > 0)
{
if (keyboard_needs_poll())
{
poll_keyboard();
}
if (key[KEY_ESC]) { mainThreadIsRunning = false; }
UpdateFrame();
timerTickCount--;
}
RenderFrame();
}
else
{
rest(1);
}
}
UnloadContent();
destroy_bitmap(backBuffer);
return 0;
}
END_OF_MAIN()

Navigation

[0] Message Index