My biggest issue in this project was the structure of the code and using the necessary data structures. I organized it to the best of my ability but I am sure there much more room improvement and I would love some advice on this. The way I have my enemies firing feels a bit hacky and I'm not sure as to how to have that in a better way.
Also, I am not entirely sure why I have to have #include <vector>
in a header or .cpp file at times. This is dependent in certain classes and I am unsure why.
main.cpp
#include "Display.h"
#include "Game.h"
#include "Media.h"
#include "Player.h"
#include <SDL.h>
#include <iostream>
int main(int argc, char* args[])
{
Display display;
Game game;
Media media;
Player player;
game.start(display, media, player);
printf("\n\n\nYou Win!");
SDL_Delay(3000);
return 0;
}
Display.h
#pragma once
#include <SDL.h>
#include <vector>
struct Laser;
struct Enemy;
class Player;
class Media;
class Display
{
public:
Display();
~Display();
bool init();
void render(Media& media, Player& player, std::vector<Enemy>& enemies, std::vector<Laser>& playerLasers, std::vector<Laser>& enemyLasers);
SDL_Surface* getWindowSurface() { return m_windowSurface; }
SDL_Renderer* getRenderer() { return m_renderer; }
protected:
private:
SDL_Window* m_window = nullptr;
SDL_Surface* m_windowSurface = nullptr;
SDL_Renderer* m_renderer = nullptr;
//Screen resolution
const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 800;
};
Display.cpp
#include "Display.h"
#include "Media.h"
#include "Player.h"
#include "Enemy.h"
#include "Laser.h"
#include <iostream>
#include <vector>
Display::Display()
{
//init();
}
Display::~Display()
{
}
bool Display::init()
{
bool success = true;
//Initialize SDL Video
if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
success = false;
printf("Video failed to initialize.", SDL_GetError());
}
else
{
//Create Window
m_window = SDL_CreateWindow("Space Invaders", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (m_window == nullptr)
{
success = false;
printf("Failed to create window.", SDL_GetError());
}
else
{
//Get the window surface
m_windowSurface = SDL_GetWindowSurface(m_window);
if (m_windowSurface == nullptr)
{
success = false;
printf("Failed to get the window surface.", SDL_GetError());
}
else
{
//Create the renderer
m_renderer = SDL_CreateRenderer(m_window, -1, SDL_RENDERER_ACCELERATED);
if (m_renderer == nullptr)
{
success = false;
printf("Failed to create the renderer.", SDL_GetError());
}
}
}
}
return success;
}
void Display::render(Media & media, Player & player, std::vector<Enemy>& enemies, std::vector<Laser>& playerLasers, std::vector<Laser>& enemyLasers)
{
SDL_RenderClear(m_renderer);
SDL_SetRenderDrawColor(m_renderer, 255, 255, 255, 0);
//Render player
SDL_RenderCopy(m_renderer, media.getPlayerTexture(), nullptr, &player.getPosition());
//Render enemies
for (auto &i : enemies)
{
SDL_RenderCopy(m_renderer, media.getEnemyTexture(), nullptr, &i.m_pos);
}
//Render Player Lasers
for (auto &i : playerLasers)
{
SDL_RenderCopy(m_renderer, media.getLaserTexture(), nullptr, &i.m_pos);
}
for (auto &i : enemyLasers)
{
SDL_RenderCopy(m_renderer, media.getLaserTexture(), nullptr, &i.m_pos);
}
SDL_RenderPresent(m_renderer);
}
Media.h
#pragma once
#include <SDL.h>
#include <string>
class Display;
class Media
{
public:
Media();
~Media();
bool load(Display& display);
SDL_Texture* getPlayerTexture() { return m_player; }
SDL_Texture* getEnemyTexture() { return m_enemy; }
SDL_Texture* getLaserTexture() { return m_laser; }
protected:
private:
SDL_Surface* loadSurface(Display& display, std::string path);
SDL_Texture* loadTexture(Display& display, std::string path);
SDL_Texture* m_laser = nullptr;
SDL_Texture* m_player = nullptr;
SDL_Texture* m_enemy = nullptr;
};
Media.cpp
#include "Media.h"
#include "Display.h"
Media::Media()
{
}
Media::~Media()
{
}
bool Media::load(Display & display)
{
bool success = true;
m_laser = loadTexture(display, "laser.bmp");
if (m_laser == nullptr)
{
printf("Failed to load laser.", SDL_GetError());
success = false;
}
m_player = loadTexture(display, "player.bmp");
if (m_player == nullptr)
{
printf("Failed to load player", SDL_GetError());
success = false;
}
m_enemy = loadTexture(display, "enemy.bmp");
if (m_enemy == nullptr)
{
printf("Failed to load enemy", SDL_GetError());
success = false;
}
return success;
}
SDL_Surface * Media::loadSurface(Display & display, std::string path)
{
SDL_Surface* optimizedSurface = nullptr;
SDL_Surface* surface = SDL_LoadBMP(path.c_str());
if(surface == nullptr)
{
printf("Unable to load", path.c_str(), SDL_GetError());
}
else
{
optimizedSurface = SDL_ConvertSurface(surface, display.getWindowSurface()->format, 0);
}
SDL_FreeSurface(surface);
return optimizedSurface;
}
SDL_Texture * Media::loadTexture(Display& display, std::string path)
{
SDL_Texture* texture = SDL_CreateTextureFromSurface(display.getRenderer(), loadSurface(display, path.c_str()));
if (texture == nullptr)
{
printf("Failed to get texture.", SDL_GetError());
}
return texture;
}
Game.h
#pragma once
#include <SDL.h>
#include <vector>
struct Laser;
struct Enemy;
class Player;
class Media;
class Display;
class Game
{
public:
Game();
~Game();
void start(Display& display, Media& media, Player& player);
private:
bool m_quit = false;
void addEnemy(std::vector<Enemy>& enemies);
void moveEnemies(std::vector<Enemy>& enemies);
void moveLasers(std::vector<Laser>& playerLasers, std::vector<Laser>& enemyLasers);
void checkCollisions(Player& player, std::vector<Enemy>& enemies, std::vector<Laser>& playerLasers, std::vector<Laser>& enemyLasers);
bool findPlayerLaserCollision(Laser& laser, Enemy& enemy);
bool findEnemyLaserCollisions(Laser& laser, Player& player);
bool checkGameOver(std::vector<Enemy>& enemies);
void enemyShoot(std::vector<Enemy>& enemies, std::vector<Laser>& enemyLasers);
};
Game.cpp
#include "Game.h"
#include "Display.h"
#include "Media.h"
#include "Player.h"
#include "Enemy.h"
#include "MoveDirection.h"
#include "Laser.h"
#include <iostream>
Game::Game()
{
}
Game::~Game()
{
}
void Game::start(Display& display, Media& media, Player& player)
{
//Initialize SDL
if (!display.init())
{
printf("Failed to initailize.", SDL_GetError());
}
else
{
//Load all media
if (!media.load(display))
{
printf("Failed to load media", SDL_GetError());
}
else
{
SDL_Event e;
std::vector<Enemy> enemies; //Holds all enemies
std::vector<Laser> playerLasers; //Holds all of player lasers
std::vector<Laser> enemyLasers;
addEnemy(enemies); //Adds enemies to the game
//Begin game loop
while (!m_quit)
{
//Event management
while (SDL_PollEvent(&e) != 0)
{
if(e.type == SDL_KEYDOWN)
{
player.movement(e);
}
if (e.type == SDL_MOUSEBUTTONDOWN)
{
player.shoot(playerLasers);
}
}
//Shoot
enemyShoot(enemies, enemyLasers);
//Movement
player.movementBounds();
moveLasers(playerLasers, enemyLasers);
moveEnemies(enemies);
//Collision detection
checkCollisions(player, enemies, playerLasers, enemyLasers);
//Check game over
m_quit = checkGameOver(enemies);
//Render
display.render(media, player, enemies, playerLasers, enemyLasers);
//Simulate 60 fps - Read on tutorial, not entirely sure if this is ok.
SDL_Delay(16);
}
}
}
}
void Game::addEnemy(std::vector<Enemy>& enemies)
{
int spawnXPos = 100;
int newSpawnXPos = 100;
int spawnYPos = 150;
int enemyWidth = 50;
int enemyHeight = 50;
int numbOfEnemies = 6;
int movementSpeed = 3;
for (int i = 0; i < numbOfEnemies; i++)
{
enemies.push_back(Enemy({ spawnXPos, spawnYPos, enemyWidth, enemyHeight }, movementSpeed, MoveDirection::LEFT));
spawnXPos += newSpawnXPos; //Spawns enemies along a row
}
}
void Game::moveEnemies(std::vector<Enemy>& enemies)
{
int newYPos = 20; //Move enemies down everytime they hit a wall
for (auto &enemy : enemies)
{
//Move Left
if (enemy.m_dir == MoveDirection::LEFT)
{
enemy.m_pos.x -= enemy.m_movementSpeed;
//If an enemy has exceeded allowed bounds, change direction
if (enemy.m_pos.x < enemy.MIN_X_POS)
{
for (auto &i : enemies)
{
i.m_dir = MoveDirection::RIGHT;
i.m_pos.y += newYPos;
}
}
}
//Move Right
if (enemy.m_dir == MoveDirection::RIGHT)
{
enemy.m_pos.x += enemy.m_movementSpeed;
//If enemy exceeds allowed bounds, change direction
if (enemy.m_pos.x > enemy.MAX_X_POS)
{
for(auto &i : enemies)
{
i.m_dir = MoveDirection::LEFT;
i.m_pos.y += newYPos;
}
}
}
}
}
void Game::moveLasers(std::vector<Laser>& playerLasers, std::vector<Laser>& enemyLasers)
{
//Movement of player lasers
for (auto &playerLaser : playerLasers)
{
if (playerLaser.m_dir == MoveDirection::UP)
{
playerLaser.m_pos.y -= playerLaser.m_movementSpeed;
}
}
//Movement of enemy lasers
for (auto &enemyLaser : enemyLasers)
{
if (enemyLaser.m_dir == MoveDirection::DOWN)
{
enemyLaser.m_pos.y += enemyLaser.m_movementSpeed;
}
}
}
void Game::checkCollisions(Player& player, std::vector<Enemy>& enemies, std::vector<Laser>& playerLasers, std::vector<Laser>& enemyLasers)
{
//checkCollisions(player, enemies, playerLasers, enemyLasers);
//Check for playerLaser/Enemy collision
for (int playerLaser = 0; playerLaser < playerLasers.size(); playerLaser++)
{
for (int enemy = 0; enemy < enemies.size(); enemy++)
{
//If collision has been detected, delete both playerLaser and Enemy
if (findPlayerLaserCollision(playerLasers[playerLaser], enemies[enemy]))
{
printf("Collision");
playerLasers.erase(playerLasers.begin() + playerLaser);
enemies.erase(enemies.begin() + enemy);
return;
}
}
}
for (int enemyLaser = 0; enemyLaser < enemyLasers.size(); enemyLaser++)
{
if (findEnemyLaserCollisions(enemyLasers[enemyLaser], player))
{
printf("Player collision");
enemyLasers.erase(enemyLasers.begin() + enemyLaser);
return;
}
}
//No collisions detected
}
bool Game::findPlayerLaserCollision(Laser& playerLaser, Enemy& enemy)
{
//Player laser collisions
int playerLaserLeft = playerLaser.m_pos.x;
int playerLaserRight = playerLaser.m_pos.x + playerLaser.m_pos.w;
int playerLaserTop = playerLaser.m_pos.y;
int playerLaserBot = playerLaser.m_pos.y + playerLaser.m_pos.h;
//Enemy collisions
int enemyLeft = enemy.m_pos.x;
int enemyRight = enemy.m_pos.x + enemy.m_pos.w;
int enemyTop = enemy.m_pos.y;
int enemyBot = enemy.m_pos.y + enemy.m_pos.h;
//Check for collisions
if (playerLaserRight < enemyLeft) {
return false;
}
if (playerLaserLeft > enemyRight) {
return false;
}
if (playerLaserTop > enemyBot) {
return false;
}
if (playerLaserBot < enemyTop) {
return false;
}
//If collision
return true;
}
bool Game::findEnemyLaserCollisions(Laser & laser, Player & player)
{
//Player Collisions
int playerLeft = player.getPositionX();
int playerRight = player.getPositionX() + player.getWidth();
int playerTop = player.getPositionY();
int playerBot = player.getPositionY() + player.getHeight();
//EnemyLaser Collision
int laserLeft = laser.m_pos.x;
int laserRight = laser.m_pos.x + laser.m_pos.w;
int laserTop = laser.m_pos.y;
int laserBot = laser.m_pos.y + laser.m_pos.h;
//Detect collisions
if (playerLeft > laserRight) {
return false;
}
if (playerRight < laserLeft) {
return false;
}
if (playerTop > laserBot) {
return false;
}
if (playerBot < laserTop) {
return false;
}
//Collision
return true;
}
bool Game::checkGameOver(std::vector<Enemy>& enemies)
{
if (enemies.size() == 0)
{
printf("Game Over!");
return true;
}
return false;
}
void Game::enemyShoot(std::vector<Enemy>& enemies, std::vector<Laser>& enemyLasers)
{
//If any enemies are alive.
if (enemies.size() > 0)
{
Uint32 startTime = SDL_GetTicks();
int randEnemy = rand() % enemies.size();
//Starting position of laser
int startXPos = enemies[randEnemy].m_pos.x;
int startYPos = enemies[randEnemy].m_pos.y;
//Size of laser
int sizeWidth = 25;
int sizeHeight = 50;
//Movement speed of laser
int movementSpeed = 8;
//Direction of laser
MoveDirection dir = MoveDirection::DOWN;
if (startTime % 125 == 0)
{
enemyLasers.push_back(Laser({ startXPos, startYPos, sizeWidth, sizeHeight }, movementSpeed, dir));
}
}
}
Player.h
#pragma once
#include <SDL.h>
#include <vector>
struct Enemy;
struct Laser;
class Player
{
public:
Player();
~Player();
void movement(SDL_Event& e);
void movementBounds();
void init(); //Initialize
SDL_Rect getPosition() { return m_pos; }
int getPositionY() { return m_pos.y; }
int getPositionX() { return m_pos.x; }
int getWidth() { return SIZE_WIDTH; }
int getHeight() { return SIZE_HEIGHT; }
void shoot(std::vector<Laser>& playerLasers);
private:
//position of player
SDL_Rect m_pos;
//Size of player
const int SIZE_WIDTH = 50;
const int SIZE_HEIGHT = 50;
//Starting position of player
const int START_XPOS = 350;
const int START_YPOS = 700;
//Movement speed of player
const int MOVEMENT_SPEED = 10;
//MovementBoundaries
const int xMinDistance = 0;
const int xMaxDistance = 775;
};
Player.cpp
#include "Player.h"
#include "Laser.h"
Player::Player()
{
init();
}
Player::~Player()
{
}
void Player::movement(SDL_Event& e)
{
switch (e.key.keysym.sym)
{
//Move right
case SDLK_d: m_pos.x += MOVEMENT_SPEED;
break;
//Move left
case SDLK_a: m_pos.x -= MOVEMENT_SPEED;
break;
}
}
void Player::movementBounds()
{
if (m_pos.x <= xMinDistance) {
m_pos.x = xMinDistance;
}
if (m_pos.x >= xMaxDistance) {
m_pos.x = xMaxDistance;
}
}
void Player::init()
{
//Size of player
m_pos.h = SIZE_HEIGHT;
m_pos.w = SIZE_WIDTH;
//starting position
m_pos.x = START_XPOS;
m_pos.y = START_YPOS;
}
void Player::shoot(std::vector<Laser>& playerLasers)
{
int startXPos = m_pos.x + 10;
int startYPos = m_pos.y - 35;
int movementSpeed = 8;
int sizeWidth = 25;
int sizeHeight = 50;
MoveDirection dir = MoveDirection::UP;
playerLasers.push_back(Laser({ startXPos, startYPos, sizeWidth, sizeHeight }, movementSpeed, dir));
}
Enemy.h
#pragma once
#include <SDL.h>
#include "MoveDirection.h"
struct Enemy
{
Enemy(SDL_Rect pos, int movementSpeed, MoveDirection dir)
{
m_pos = pos;
m_movementSpeed = movementSpeed;
m_dir = dir;
}
SDL_Rect m_pos; //Position of enemy
int m_movementSpeed = 0; //Movement speed of enemy
MoveDirection m_dir; //Movement direction of enemy
int MIN_X_POS = 0;
int MAX_X_POS = 750;
};
MoveDirection.h
#pragma once
//Movement directions of players, enemies and lasers
enum class MoveDirection
{
LEFT,
RIGHT,
UP,
DOWN
};
Laser.h
#pragma once
#include <SDL.h>
#include "MoveDirection.h"
struct Laser
{
Laser(SDL_Rect pos, int movementSpeed, MoveDirection dir)
{
m_pos = pos;
m_movementSpeed = movementSpeed;
m_dir = dir;
}
SDL_Rect m_pos; //Position of enemy
int m_movementSpeed = 0; //Movement speed of enemy
MoveDirection m_dir; //Movement direction of enemy
};