Hello,
My Background:
I'm learning SDL and C++ with LazyFoo's Tutorial and Accelerated C++ by Andrew Koening and Barbara E. Moo.
I've read most of LazyFoo's early tutorials on rendering shapes and textures but not the multithreading yet. From the C++ book, I've learned about basic containers like vectors, lists, and queues but nothing in depth yet because I'm still working through it.
Stuff You Might Like:
I've finished a playable version of pong and I'm making progress on a bullet hell engine.
Here's a ZIP of things that are playable (hopefully) on Windows.
It has:
pong.exe - It's pong.
game.exe - A level I've made with my engine so far. WASD to move and LSHIFT to slow down.
superpong.exe - A goofy version of pong that I never finished with way too many balls and no player 2.
projectiles.exe - Test version of my bullet engine that makes nice patterns if you press 1-7.
Helping Me:
I'm posting some code snips from my engine. I'd like some tips on using the C++ containers because they are a new tool for me. I would also appreciate any style tips.
JGame.h:
#include <SDL.h>
#include <SDL_image.h>
#include <SDL_ttf.h>
#include <list>
#include <cmath>
#include <string>
#include <cstdlib>
#include "LProjectile.h"
#include "LBullet.h"
#include "LEntity.h"
#include "..\LTimer.h"
#include "..\LCircle.h"
#include "..\LPlayer.h"
#include "..\LTexture.h"
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
const int FPS = 60;
class JGame {
public:
JGame();
~JGame();
void play();
LEntity* ent;
private:
// initialize SDL window & renderer, SDL_image, SDL_font
bool init();
void reset();
bool loadMedia();
// triggers corresponding game element functions
void handleEvents();
void handleKeystates();
void act();
void render();
// clean up
void close();
// creates a bullet on the player and shoots it to the
// top of the screen
void shootFromPlayer();
void spawnOrbittingEnemy(bool fromLeft);
bool initSuccess, loadSuccess;
bool playing;
SDL_Window* gameWindow;
SDL_Renderer* gameRenderer;
LTimer* gameTimer;
// Container for each wave of enemies to spawn
std::queue<std::queue<EntityInfo> > entityWaves;
std::list<LEntity*> enemyEntities;
std::list<LEntity*> allyEntities;
Circle centerMarker;
Circle hLinear;
int hLinLimit;
int hLinModifier;
int frames;
int score;
int maxScore;
bool gameJustEnded;
LPlayer* player;
LTexture background;
// # of invincibility frames
int iFrames;
// Functions that add a wave of enemies to entityWaves
void wave1();
void RLRL_Wave();
void level1();
void stall(int frames);
void alternateSliderPair();
void clumpSpawn();
};
JGame.cpp:
#include "JGame.h"
// functions of interest, the rest removed to keep file shorter
// act(), render(), wave1()
// I use the containers in act and render
// in wave1 I do some memory stuff that might be really bad but I don't know
JGame::JGame(){
// not important
}
JGame::~JGame(){
close();
}
void JGame::play(){
playing = initSuccess;
while(playing){
handleEvents();
handleKeystates();
act();
render();
}
}
bool JGame::init(){
// not important
}
void JGame::reset(){
// not important
}
bool JGame::loadMedia(){
// not important
}
void JGame::handleEvents(){
// not important
}
void JGame::handleKeystates(){
// not important
}
void JGame::act(){
// if there is another wave of enemies, spawn them
// else the game is over and print out player score
if(!entityWaves.empty()){
if(!entityWaves.front().empty()){
std::queue<EntityInfo> *currentWave = &entityWaves.front();
if(currentWave->front().framesBeforeSpawning <= 0){
enemyEntities.push_back(spawnEntity(currentWave->front()));
currentWave->pop();
} else {
currentWave->front().framesBeforeSpawning--;
}
} else {
entityWaves.pop();
}
} else {
if(enemyEntities.empty() && gameJustEnded){
SDL_Log("final score: %d/%d\n", score, maxScore);
if(!score) score = 1;
int performance = maxScore * 10 / score - 10;
switch(performance){
case 0:{
if(score == maxScore){
SDL_Log("PERFECT! YOU HAVE ACHIEVED EXCELLENCE!\n");
break;
}
}
case 1:{
SDL_Log("GREAT! YOU CAN STILL DO BETTER!\n");
break;
}
case 2:{
SDL_Log("YOU'RE GETTING THE HANG OF IT!\n");
break;
}
case 3:{
SDL_Log("YOU CAN DO THIS! I BELIEVE IN YOU!\n");
break;
}
default: {
SDL_Log("KEEP TRYING! DON'T GIVE UP!\n");
break;
}
}
SDL_Log("R to retry\n");
gameJustEnded = false;
}
}
if(hLinear.x > hLinLimit || hLinear.x < 64){
hLinModifier *= -1;
}
hLinear.x += hLinModifier;
// do player actions
player->act();
// do friendly entity actions
std::list<LEntity*>::iterator i = allyEntities.begin();
for(;i != allyEntities.end();i++){
if(!(*i)->isDead()){
(*i)->act();
} else {
i = allyEntities.erase(i);
}
}
Circle* pC = player->getHitCircle();
// do enemy collisions with player or bullet
// on hit with player add 2 seconds of iFrames
// on hit with bullet take damage, 'hurt' the bullet back so it is erased in the next loop
// if enemy has a bullet to spawn, in its subqueue mark it to be spawned to the game
std::list<EntityInfo> enemiesToSpawn;
std::list<LEntity*>::iterator j = enemyEntities.begin();
for(;j != enemyEntities.end();j++){
if(!(*j)->isDead()){
if((*j)->getType() == enemy){
i = allyEntities.begin();
for(;i != allyEntities.end();i++){
if((*i)->getType()== player_bullet){
if(checkCircles((*j)->getCircle(), (*i)->getCircle())){
(*j)->takeDamage(1);
(*i)->takeDamage(1);
}
}
}
}
if(!(*j)->isDead()){
(*j)->act();
Circle* eC = (*j)->getCircle();
if(checkCircles(eC,pC) && iFrames == 0){
iFrames = FPS * 2;
}
if((*j)->spawnFromSubQueue()){
EntityInfo* frontInfo = (*j)->getFrontSubEntityInfo();
frontInfo->position = (*j)->getPosition();
enemiesToSpawn.push_back(*frontInfo);
}
} else {
SDL_Log("enemy down! +50!\n");
score += 50;
if(!iFrames){
SDL_Log("NO BLINKING BONUS +50!");
score += 50;
}
}
} else {
j = enemyEntities.erase(j);
}
}
// spawn enemy bullets to the field and lerp them at the player
std::list<EntityInfo>::iterator k = enemiesToSpawn.begin();
for(;k != enemiesToSpawn.end();k++){
EntityInfo info = (*k);
LEntity* spawn = new LEntity(info.type, info.renderer,
info.position.x, info.position.y);
MotionInfo* lerpToPlayer = new MotionInfo();
MotionInfo* end = new MotionInfo();
lerpToPlayer->motion = lerp;
lerpToPlayer->frames = 4 * FPS;
lerpToPlayer->goForward = true;
lerpToPlayer->params.push_back(player->getCenter().x);
lerpToPlayer->params.push_back(player->getCenter().y);
lerpToPlayer->params.push_back(lerpToPlayer->frames / 2 * 1000/FPS);
lerpToPlayer->params.push_back(false);
end->motion = kill;
end->frames = 1;
spawn->pushMotion(*lerpToPlayer);
spawn->pushMotion(*end);
enemyEntities.push_back(spawn);
}
}
void JGame::render(){
SDL_SetRenderDrawColor(gameRenderer, 0,0,0,0xFF);
SDL_RenderClear(gameRenderer);
background.render(gameRenderer, 0, 0, NULL, 0, NULL, SDL_FLIP_NONE);
//run render functions below
SDL_Color green = {0,0xFF,0x55,0xFF};
renderCircle(&hLinear, green, gameRenderer);
player->render(0,0);
SDL_Color red = {0xFF,0,0,0xFF};
renderCircle(¢erMarker, red, gameRenderer);
SDL_Color cyan = {0,0xFF,0xFF,0xFF};
if(!iFrames){
renderCircle(player->getHitCircle(), cyan, gameRenderer);
} else {
if(iFrames % 6 < 2){
// make player hitcircle blink
renderCircle(player->getHitCircle(), cyan, gameRenderer);
}
}
std::list<LEntity*>::iterator i = allyEntities.begin();
for(;i != allyEntities.end();i++){
(*i)->render();
}
std::list<LEntity*>::iterator j = enemyEntities.begin();
for(;j != enemyEntities.end();j++){
(*j)->render();
}
SDL_RenderPresent(gameRenderer);
frames++;
if(iFrames) iFrames--;
}
void JGame::close(){
// not important
}
void JGame::shootFromPlayer(){
// not important
}
void addBullet(EntityInfo *super, int frameDelay){
// not important
}
void JGame::wave1(){
std::queue<EntityInfo> theWave;
EntityInfo* rightSlider = new EntityInfo();
rightSlider->type = enemy;
rightSlider->position.x = 0;
rightSlider->position.y = 100;
rightSlider->renderer = gameRenderer;
MotionInfo* moveRight = new MotionInfo();
MotionInfo* end = new MotionInfo();
moveRight->motion = horizontal;
moveRight->frames = 6 * FPS;
moveRight->goForward = true;
moveRight->params.push_back(3);
end->motion = kill;
end->frames = 1;
pushMotion(rightSlider->motionQueue, *moveRight);
pushMotion(rightSlider->motionQueue, *end);
EntityInfo* sliders[4];
int waveSize = 4;
for(int i = 0; i<4; i++){
sliders[i] = new EntityInfo();
memcpy(sliders[i], rightSlider, sizeof(EntityInfo));
sliders[i]->position.y += 100 * i;
sliders[i]->framesBeforeSpawning = FPS;
addBullet(sliders[i], 0);
addBullet(sliders[i], FPS);
addBullet(sliders[i], FPS);
addBullet(sliders[i], FPS);
theWave.push(*sliders[i]);
}
entityWaves.push(theWave);
maxScore += waveSize * 100;
delete rightSlider;
delete moveRight;
delete end;
}
void JGame::RLRL_Wave(){
// not important
}
// stalls by waiting <frames> and spawning an entity that instantly dies
void JGame::stall(int frames){
// not important
}
void JGame::alternateSliderPair(){
// not important
}
void JGame::clumpSpawn(){
// not important
}
void JGame::level1(){
RLRL_Wave();
RLRL_Wave();
stall(FPS);
wave1();
wave1();
stall(FPS);
int ASPCount = 10;
for(int i=0; i<ASPCount; i++){
alternateSliderPair();
stall(FPS/2);
}
stall(4*FPS);
clumpSpawn();
stall(6*FPS);
clumpSpawn();
stall(6*FPS);
}
If you need to see the whole code base I've attached it.
Thank you for your time.