I recently finished programming a clone to the popular game Missile Command as part of the "getting started" article on gamedev.net. This project took me about two weeks to complete whilst working on it every now and then. The code contains over 1500 lines of C++ and SFML. This time I took a different design approach to my previous projects in order to simplify the code and make it more readable... hopefully.
Links to my other projects in this series:
Video of my Missile Command clone:
Download:
https://www.dropbox.com/s/07lq4wc5fsp1djb/Missile%20Command.zip
(Please tell me if the game doesn't work)
Features:
Crappy UI
Highscores
Explosions
Screenshots:
Files:
Last but not least: A huge chunk of code.
main.cpp
#include "init.h"
int main(){
Missile_Command game;
game.run();
return 0;
}
init.h
#pragma once
// C++
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <sstream>
// SFML
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>
#include <SFML/Network.hpp>
// Framework
#include "SoundEffect.h"
#include "Entity.h"
#include "Button.h"
#include "Math.h"
#include "Collision_Detection.h"
#include "Windows_Stuff.h"
// Game
#include "Missiles.h"
#include "Missile_Command.h"
// Globals
const float SECOND = 1000;
const float BLOCKSIZE = 20;
const int GRIDSIZE_X = 50;
const int GRIDSIZE_Y = 30;
const int SCRW = (int)BLOCKSIZE * GRIDSIZE_X;
const int SCRH = (int)BLOCKSIZE * GRIDSIZE_Y;
const double INTERVAL = 16.6; // 1000 / INTERVAL = FPS -> ~60
const sf::Color CLEARCOLOR(sf::Color(0, 0, 0));
Missile_Command.h
#pragma once
struct Particle;
enum class GameState{
Home,
Settings,
About,
Highscores,
Playing,
Paused,
GameOver,
Win
};
class Missile_Command{
sf::RenderWindow _window;
sf::Event _event;
GameState _gameState;
sf::Clock _clock;
sf::Font _font;
sf::Text _aboutText;
sf::Text _volumeText;
sf::Text _volumeInfoText;
sf::Text _scoreText;
sf::Text _inGameInfoText;
sf::Text _gameOverText;
sf::Text _winText;
sf::Text _highscoreNamesText;
sf::Text _highscoreScoresText;
sf::Text _ammunitionText;
sf::Texture _bgTex;
sf::Sprite _bgSprite1;
sf::Sprite _bgSprite2;
sf::Vector2f _wallSize;
sf::Vector2f _windowCenter;
sf::Vector2f _playerCenterLoc;
sf::Vector2f _missileSize;
std::vector<sf::Vector2f> _enemyVel;
int _score;
int _wave;
int _ammunition;
int _enemyCount; // wave limit
int _enemiesSpawned; // amount of enemies spawned in this wave
float _explosionExpandVel;
float _explosionFadeVel;
size_t _updates;
double _skippedTime;
double _secondSkipped;
float _canonAngle;
float _missileSpeed;
float _enemySpeed;
float _bgMoveVel;
float _bgOriginMoveVel;
float _floorHeight;
float _playerSize;
float _volume;
std::string _playerName;
std::vector<std::string> _highscores;
sf::Color _playerColor;
sf::Color _baseColor;
sf::Color _buttonColor;
sf::Color _buttonHoverColor;
sf::Color _missileColor;
sf::Color _baseExplodeColor;
sf::Color _baseFadeColor;
sf::Color _enemyExplodeColor;
sf::Color _enemyFadeColor;
sf::RectangleShape colBounds;
Missiles _missiles;
EntityR _canon;
EntityR _bunkerBase;
EntityC _bunkerTop;
EntityC _playerFloor;
std::vector<EntityR> _floor;
std::vector<EntityC> _bases;
std::vector<EntityC> _enemies;
Particles _menuEffect;
Particles _trails; // comet trails
SoundEffect _shootSound;
SoundEffect _explodeSound;
SoundEffect _loseSound;
SoundEffect _waveWinSound;
std::vector<SoundEffect> _hoverSounds;
ui::Button _quit;
ui::Button _start;
ui::Button _settings;
ui::Button _about;
ui::Button _highscoresBtn;
ui::Button _back;
ui::Button _continue;
ui::Button _pauseQuit;
ui::Button _home;
ui::Button _volumeDown;
ui::Button _volumeUp;
ui::Button _lastHovered; // used to detect the button that was last hovered over
void init();
void initFloor();
void initPlayer();
void initEnemy();
void initButtons();
void initText();
void initSounds();
void doEvents();
void reset();
// Load menus
void loadHome();
void loadSettings();
void loadAbout();
void loadHighscores();
void loadPlaying();
void loadPaused();
void loadGameOver();
void loadWin();
void update();
void updateBG();
void updateMenuEffect();
void updateEnemies();
void render();
void renderHome();
void renderSettings();
void renderAbout();
void renderHighScores();
void renderPlaying();
void renderPause();
void renderGameOver();
void renderWin();
void renderMenuEffect();
void renderBG();
void doHover(ui::Button & button);
void setVolume();
void spawnEnemy();
void removeEnemyAt(size_t index);
void doCollisions();
void removeBaseAt(size_t index);
void nextWave();
std::vector<std::string> split(std::string string, char splitParam);
void getHighscores();
bool addHighscore();
void writeHighscores();
void sortHighscores();
public:
Missile_Command();
~Missile_Command();
void run();
};
Missile_Command.cpp (~800 Lines)
#include "init.h"
Missile_Command::Missile_Command(){
srand((size_t)time(0));
init();
}
Missile_Command::~Missile_Command(){
// Destroy
}
void Missile_Command::init(){
_window.create(sf::VideoMode(SCRW, SCRH), "Missile Command");
_missileSize = sf::Vector2f(4, 4);
_explosionExpandVel = 0.8f;
_explosionFadeVel = 2.2f;
_missileSpeed = 12.0f;
_skippedTime = 0;
_updates = 0;
_secondSkipped = 0;
_bgOriginMoveVel = 1.0f;
_bgMoveVel = _bgOriginMoveVel;
_enemySpeed = 1.0f;
_volume = 30;
_baseExplodeColor = sf::Color(0, 100, 255);
_baseFadeColor = sf::Color(200, 50, 10);
_enemyExplodeColor = sf::Color::Yellow;
_enemyFadeColor = sf::Color::Magenta;
_playerColor = sf::Color::White;
_baseColor = sf::Color(56, 78, 90, 200);
_buttonColor = sf::Color(255, 255, 255, 128);
_buttonHoverColor = sf::Color((sf::Uint8)(_buttonColor.r / 1.5), (sf::Uint8)(_buttonColor.g / 1.5), (sf::Uint8)(_buttonColor.b / 1.5, 128));
_missileColor = sf::Color(255, 165, 0);
_floorHeight = 50;
_wallSize = sf::Vector2f(_floorHeight, _floorHeight * 2.0f);
_playerSize = _floorHeight;
_windowCenter = (sf::Vector2f)_window.getSize() / 2.0f;
if (!_bgTex.loadFromFile("sprites/bg.png"))
std::exit(-4);
_bgSprite1.setTexture(_bgTex);
_bgSprite2.setTexture(_bgTex);
_bgSprite1.setPosition(0, 0);
_bgSprite2.setPosition(_bgSprite1.getPosition().x - _bgTex.getSize().x, 0);
initFloor();
initButtons();
initText();
initSounds();
initPlayer();
_playerCenterLoc = _bunkerTop.getPosition() + sf::Vector2f(_bunkerTop.getRadius(), _bunkerTop.getRadius());
reset();
_gameState = GameState::Home; // Temporary -> Home
}
void Missile_Command::initFloor(){
_floor.resize(3);
_floor[0] = EntityR(sf::Vector2f((float)_window.getSize().x, _floorHeight), sf::Vector2f(0.0f, (float)_window.getSize().y - _floorHeight), _playerColor); // Main floor
_floor[1] = EntityR(_wallSize, sf::Vector2f(0, _window.getSize().y - (_floorHeight + _wallSize.y)), _playerColor); // Left wall
_floor[2] = EntityR(_wallSize, (sf::Vector2f)_window.getSize() - sf::Vector2f(_wallSize.x, _floorHeight + _wallSize.y), _playerColor); // Right wall
_playerFloor = EntityC(_playerSize * 2.0f, 9, sf::Vector2f(_windowCenter.x - _playerSize * 2.0f, _window.getSize().y - _floorHeight * 2.0f), _playerColor);
}
void Missile_Command::initPlayer(){
_bunkerBase = EntityR(sf::Vector2f(_playerSize, _playerSize / 2.1f), sf::Vector2f(_windowCenter.x - _playerSize / 2.0f, _playerFloor.getPosition().y - _playerSize / 4.2f), _playerColor);
_bunkerTop = EntityC(_playerSize / 2.0f, 40, sf::Vector2f(_windowCenter.x - _playerSize / 2.0f, _playerFloor.getPosition().y - _playerSize / 1.5f), _playerColor);
_canon = EntityR(sf::Vector2f(_playerSize / 5.0f, _playerSize / 1.2f), sf::Vector2f(_windowCenter.x, _bunkerTop.getPosition().y + _bunkerTop.getRadius()), _playerColor);
_canon.setOrigin(sf::Vector2f(_canon.getSize().x / 2.0f, _canon.getSize().y));
// Init bases
_bases.resize(6);
_bases[0] = EntityC(50, 10, sf::Vector2f(100, 520), _baseColor);
_bases[1] = EntityC(50, 10, sf::Vector2f(200, 520), _baseColor);
_bases[2] = EntityC(50, 10, sf::Vector2f(300, 520), _baseColor);
_bases[3] = EntityC(50, 10, sf::Vector2f(600, 520), _baseColor);
_bases[4] = EntityC(50, 10, sf::Vector2f(700, 520), _baseColor);
_bases[5] = EntityC(50, 10, sf::Vector2f(800, 520), _baseColor);
}
void Missile_Command::initEnemy(){
_enemies.clear();
_enemyVel.clear();
}
void Missile_Command::initButtons(){
_start = ui::Button("Start", 50, sf::Vector2f(15, 0), sf::Color::Black, _buttonColor, "fonts/segoeuil.ttf");
_settings = ui::Button("Settings", 50, sf::Vector2f(15, 80), sf::Color::Black, _buttonColor, "fonts/segoeuil.ttf");
_about = ui::Button("About", 50, sf::Vector2f(15, 160), sf::Color::Black, _buttonColor, "fonts/segoeuil.ttf");
_highscoresBtn = ui::Button("Highscores", 50, sf::Vector2f(15, 240), sf::Color::Black, _buttonColor, "fonts/segoeuil.ttf");
_quit = ui::Button("Quit", 50, sf::Vector2f(15, 320), sf::Color::Black, _buttonColor, "fonts/segoeuil.ttf");
_back = ui::Button("Back", 50, sf::Vector2f(15.0f, SCRH - 90.0f), sf::Color::Black, _buttonColor, "fonts/segoeuil.ttf");
_continue = ui::Button("Continue", 50, sf::Vector2f(400, 260), sf::Color::Black, _buttonColor, "fonts/segoeuil.ttf");
_pauseQuit = ui::Button("Home", 50, sf::Vector2f(250, 260), sf::Color::Black, _buttonColor, "fonts/segoeuil.ttf");
_volumeDown = ui::Button("<", 50, sf::Vector2f(15, 40), sf::Color::Black, _buttonColor, "fonts/segoeuil.ttf");
_volumeUp = ui::Button(">", 50, sf::Vector2f(130, 40), sf::Color::Black, _buttonColor, "fonts/segoeuil.ttf");
}
void Missile_Command::initText(){
_font.loadFromFile("fonts/segoeuil.ttf");
_aboutText.setFont(_font);
_volumeText.setFont(_font);
_volumeInfoText.setFont(_font);
_scoreText.setFont(_font);
_inGameInfoText.setFont(_font);
_gameOverText.setFont(_font);
_winText.setFont(_font);
_highscoreNamesText.setFont(_font);
_highscoreScoresText.setFont(_font);
_ammunitionText.setFont(_font);
_aboutText.setString("Game is controlled solely through mouse input.\n\n Programming: Gaius Baltar\n Sound effects: Gaius Baltar");
_volumeText.setString(std::to_string(_volume));
_volumeText.setCharacterSize(50);
_volumeText.setPosition(sf::Vector2f(65, 50));
_volumeInfoText.setString("Volume:");
_volumeInfoText.setCharacterSize(50);
_volumeInfoText.setPosition(sf::Vector2f(15, 0));
_scoreText.setCharacterSize(50);
_scoreText.setPosition(sf::Vector2f(250, 180));
_inGameInfoText.setCharacterSize(20);
_gameOverText.setCharacterSize(50);
_gameOverText.setPosition(sf::Vector2f(250, 100));
_gameOverText.setString("You lose...");
_winText.setCharacterSize(50);
_winText.setPosition(sf::Vector2f(250, 100));
_winText.setString("You Win!");
_highscoreNamesText.setCharacterSize(30);
_highscoreNamesText.setPosition(sf::Vector2f(50, 50));
_highscoreScoresText.setCharacterSize(30);
_highscoreScoresText.setPosition(sf::Vector2f(500, 50));
_ammunitionText.setCharacterSize(50);
_ammunitionText.setPosition(sf::Vector2f(475, 510));
_ammunitionText.setColor(sf::Color::Black);
}
void Missile_Command::initSounds(){
_shootSound = SoundEffect("sounds/shoot.wav", _volume);
_explodeSound = SoundEffect("sounds/explode.wav", _volume);
_loseSound = SoundEffect("sounds/lose.wav", _volume);
_waveWinSound = SoundEffect("sounds/win.wav", _volume);
_hoverSounds.resize(5);
for (size_t i = 0; i < _hoverSounds.size(); i++){
_hoverSounds[i] = SoundEffect("sounds/hover" + std::to_string(i + 1) + ".wav", _volume);
}
}
void Missile_Command::doEvents(){
while (_window.pollEvent(_event)){
if (_event.type == sf::Event::Closed){
_window.close();
}
switch (_gameState){
case GameState::Home:
if (_event.type == sf::Event::KeyPressed && _event.key.code == sf::Keyboard::Escape || _quit.isPressed(_event)){
_window.close();
}
if (_start.isPressed(_event))
loadPlaying();
if (_settings.isPressed(_event))
loadSettings();
if (_about.isPressed(_event))
loadAbout();
if (_highscoresBtn.isPressed(_event))
loadHighscores();
if (_event.type == sf::Event::MouseMoved){
doHover(_quit);
doHover(_start);
doHover(_settings);
doHover(_about);
doHover(_highscoresBtn);
}
break;
case GameState::Settings:
if (_event.type == sf::Event::KeyPressed && _event.key.code == sf::Keyboard::Escape){
loadHome();
}
if (_volumeDown.isPressed(_event) && _volume > 0){
_volume -= 1;
_volumeText.setString(std::to_string((int)_volume));
setVolume();
}
if (_volumeUp.isPressed(_event) && _volume < 100){
_volume += 1;
_volumeText.setString(std::to_string((int)_volume));
setVolume();
}
if (_back.isPressed(_event))
loadHome();
if (_event.type == sf::Event::MouseMoved){
doHover(_back);
doHover(_volumeDown);
doHover(_volumeUp);
}
break;
case GameState::About:
if (_event.type == sf::Event::KeyPressed && _event.key.code == sf::Keyboard::Escape){
loadHome();
}
if (_back.isPressed(_event))
loadHome();
if (_event.type == sf::Event::MouseMoved){
doHover(_back);
}
break;
case GameState::Highscores:
if (_event.type == sf::Event::KeyPressed && _event.key.code == sf::Keyboard::Escape){
loadHome();
}
if (_back.isPressed(_event))
loadHome();
if (_event.type == sf::Event::MouseMoved){
doHover(_back);
}
break;
case GameState::Playing:
if (_event.type == sf::Event::KeyPressed){
if (_event.key.code == sf::Keyboard::Space){
_missiles.explodeAtPosition(_playerFloor.getPosition() + sf::Vector2f(_playerFloor.getRadius(), _playerFloor.getRadius()), 0.5f, 7.0f, sf::Color(155, 115, 0), sf::Color(100, 0, 0));
}
if (_event.key.code == sf::Keyboard::Escape)
loadPaused();
}
if (_event.type == sf::Event::MouseButtonPressed){
if (_event.mouseButton.button == sf::Mouse::Left && _ammunition > 0){
_shootSound.play();
_canonAngle = (float)mth::getAngleInRadians(_playerCenterLoc, (sf::Vector2f)sf::Mouse::getPosition(_window));
_missiles.fireMissile(mth::getVelocity(_canonAngle, _missileSpeed), (sf::Vector2f)sf::Mouse::getPosition(_window));
_ammunition--;
}
}
break;
case GameState::Paused:
if (_event.type == sf::Event::KeyPressed && _event.key.code == sf::Keyboard::Escape){
loadPlaying();
}
if (_continue.isPressed(_event))
loadPlaying();
if (_pauseQuit.isPressed(_event))
loadHome();
if (_event.type == sf::Event::MouseMoved){
doHover(_continue);
doHover(_pauseQuit);
}
break;
case GameState::GameOver:
if (_event.type == sf::Event::KeyPressed && _event.key.code == sf::Keyboard::Escape){
loadHome();
}
if (_pauseQuit.isPressed(_event))
loadHome();
if (_continue.isPressed(_event)){
reset();
loadPlaying();
}
if (_event.type == sf::Event::MouseMoved){
doHover(_pauseQuit);
doHover(_continue);
}
break;
case GameState::Win:
if (_event.type == sf::Event::KeyPressed && _event.key.code == sf::Keyboard::Escape){
loadPlaying();
}
if (_pauseQuit.isPressed(_event))
loadHome();
if (_continue.isPressed(_event)){
loadPlaying();
}
if (_event.type == sf::Event::MouseMoved){
doHover(_pauseQuit);
doHover(_continue);
}
break;
default:
break;
}
}
}
void Missile_Command::reset(){
_wave = 0;
nextWave();
_ammunition = 40;
_score = 0;
_missiles = Missiles(_missileSize, _playerCenterLoc, _missileColor, sf::Color::Red, _explosionExpandVel, _explosionFadeVel);
initPlayer();
initEnemy();
_menuEffect.clear();
_trails.clear();
}
void Missile_Command::loadHome(){
_gameState = GameState::Home;
_bgMoveVel = _bgOriginMoveVel;
reset();
}
void Missile_Command::loadSettings(){
_gameState = GameState::Settings;
_volumeText.setString(std::to_string((int)_volume));
_bgMoveVel = _bgOriginMoveVel;
}
void Missile_Command::loadAbout(){
_gameState = GameState::About;
_bgMoveVel = _bgOriginMoveVel;
}
void Missile_Command::loadHighscores(){
_gameState = GameState::Highscores;
_bgMoveVel = _bgOriginMoveVel;
getHighscores();
sortHighscores();
std::string namesList;
std::string scoresList;
for (size_t i = 0; i < _highscores.size() - 1; i += 2){
namesList += std::to_string((i+2)/2) + ". " + _highscores[i] + "\n";
scoresList += _highscores[i + 1] + "\n";
}
_highscoreNamesText.setString(namesList);
_highscoreScoresText.setString(scoresList);
}
void Missile_Command::loadPlaying(){
_gameState = GameState::Playing;
_menuEffect.clear();
_bgMoveVel = _bgOriginMoveVel / 3.0f;
}
void Missile_Command::loadPaused(){
_continue.setString("Continue");
_gameState = GameState::Paused;
_bgMoveVel = _bgOriginMoveVel * 2.0f;
}
void Missile_Command::loadGameOver(){
_gameState = GameState::GameOver;
_continue.setString("Restart");
_bgMoveVel = _bgOriginMoveVel * 2.0f;
_enemies.clear();
_enemyVel.clear();
_missiles.clear();
_score += _ammunition * 10;
_score += _bases.size() * 100;
_scoreText.setString("Score: " + std::to_string(_score));
if (_playerName.size() <= 0){
std::cout << "\n\n\nPlease enter your name for the highscores list:\n\n\n";
std::getline(std::cin, _playerName);
}
addHighscore();
}
void Missile_Command::loadWin(){
_gameState = GameState::Win;
_continue.setString("Continue");
_bgMoveVel = _bgOriginMoveVel * 2.0f;
_enemies.clear();
_enemyVel.clear();
_missiles.clear();
_score += _ammunition * 10;
_score += _bases.size() * 100;
_ammunition = 40;
_scoreText.setString("Score: " + std::to_string(_score));
nextWave();
}
void Missile_Command::update(){
switch (_gameState){
case GameState::Home:
updateBG();
updateMenuEffect();
break;
case GameState::Settings:
updateBG();
updateMenuEffect();
break;
case GameState::About:
updateBG();
updateMenuEffect();
break;
case GameState::Highscores:
updateBG();
updateMenuEffect();
break;
case GameState::Playing:
updateBG();
_canon.setRotation(90 + (float)mth::getAngleInDegrees(_playerCenterLoc, (sf::Vector2f)sf::Mouse::getPosition(_window)));
_inGameInfoText.setString("Score: " + std::to_string(_score) + " Wave: " + std::to_string(_wave) + " Enemies remaining: " + std::to_string(_enemyCount - _enemiesSpawned));
_ammunitionText.setString(std::to_string(_ammunition));
_missiles.update(_explodeSound);
updateEnemies();
doCollisions();
if (rand() % 1000 <= _wave * 10 && _enemiesSpawned < _enemyCount){
spawnEnemy();
_enemiesSpawned++;
}
if (_bases.size() <= 0){
_loseSound.play();
loadGameOver();
}
if (_enemies.size() <= 0 && _enemiesSpawned >= _enemyCount){
_waveWinSound.play();
loadWin();
}
if (_wave > 10){
_waveWinSound.play();
loadGameOver();
loadHighscores();
}
break;
case GameState::Paused:
_bgMoveVel = _bgOriginMoveVel * 2.0f;
updateBG();
updateMenuEffect();
break;
case GameState::GameOver:
_bgMoveVel = _bgOriginMoveVel * 2.0f;
updateBG();
updateMenuEffect();
break;
case GameState::Win:
_bgMoveVel = _bgOriginMoveVel * 2.0f;
updateBG();
updateMenuEffect();
break;
}
}
void Missile_Command::updateBG(){
_bgSprite1.setPosition(_bgSprite1.getPosition() + sf::Vector2f(_bgMoveVel, 0));
_bgSprite2.setPosition(_bgSprite2.getPosition() + sf::Vector2f(_bgMoveVel, 0));
if (_bgSprite1.getPosition().x > _window.getSize().x)
_bgSprite1.setPosition(_bgSprite2.getPosition().x - _bgTex.getSize().x, 0);
if (_bgSprite2.getPosition().x > _window.getSize().x)
_bgSprite2.setPosition(_bgSprite1.getPosition().x - _bgTex.getSize().x, 0);
}
void Missile_Command::updateMenuEffect(){
if (rand() % 1000 <= 100){
_menuEffect.append(sf::Vector2f((float)(rand() % SCRW), (float)(rand() % SCRH)), 500, sf::Color(rand() % 256, rand() % 256, rand() % 256, 20));
}
if (_menuEffect.particles.size() > 1000)
_menuEffect.clear();
}
void Missile_Command::updateEnemies(){
for (size_t i = 0; i < _enemies.size(); i++){
_enemies[i].setPosition(_enemies[i].getPosition() + _enemyVel[i]);
if (rand() % 1000 < 200){
_trails.append(_enemies[i].getPosition() + sf::Vector2f((float)(rand() % 10), 0.0f), 500, sf::Color(200, rand() % 100 + 150, rand() % 100 + 150));
}
}
for (size_t i = 0; i < _trails.life.size(); i++){
_trails.life[i]--;
if (_trails.life[i] <= 0){
_trails.removeAt(i);
}
}
}
void Missile_Command::render(){
_window.clear(CLEARCOLOR);
switch (_gameState){
case GameState::Home:
renderBG();
renderMenuEffect();
renderHome();
break;
case GameState::Settings:
renderBG();
renderMenuEffect();
renderSettings();
break;
case GameState::About:
renderBG();
renderMenuEffect();
renderAbout();
break;
case GameState::Highscores:
renderBG();
renderMenuEffect();
renderHighScores();
break;
case GameState::Playing:
renderBG();
_window.draw(colBounds);
renderPlaying();
break;
case GameState::Paused:
renderBG();
renderMenuEffect();
renderPlaying();
renderPause();
break;
case GameState::GameOver:
renderBG();
renderMenuEffect();
renderPlaying();
renderGameOver();
break;
case GameState::Win:
renderBG();
renderMenuEffect();
renderPlaying();
renderWin();
break;
}
_window.display();
}
void Missile_Command::renderHome(){
_window.draw(_start);
_window.draw(_settings);
_window.draw(_about);
_window.draw(_highscoresBtn);
_window.draw(_quit);
}
void Missile_Command::renderSettings(){
_window.draw(_back);
_window.draw(_volumeDown);
_window.draw(_volumeUp);
_window.draw(_volumeText);
_window.draw(_volumeInfoText);
}
void Missile_Command::renderAbout(){
_window.draw(_aboutText);
_window.draw(_back);
}
void Missile_Command::renderHighScores(){
_window.draw(_back);
_window.draw(_highscoreNamesText);
_window.draw(_highscoreScoresText);
}
void Missile_Command::renderPlaying(){
for (auto item : _enemies)
_window.draw(item);
if (_trails.particles.size() > 0)
_window.draw(&_trails.particles[0], _trails.particles.size(), sf::PrimitiveType::Points);
_window.draw(_missiles);
for (auto item : _bases)
_window.draw(item);
for (auto item : _floor)
_window.draw(item);
_window.draw(_bunkerBase);
_window.draw(_bunkerTop);
_window.draw(_canon);
_window.draw(_playerFloor);
_window.draw(_ammunitionText);
_window.draw(_inGameInfoText);
}
void Missile_Command::renderPause(){
_window.draw(_continue);
_window.draw(_pauseQuit);
}
void Missile_Command::renderGameOver(){
_window.draw(_pauseQuit);
_window.draw(_gameOverText);
_window.draw(_scoreText);
}
void Missile_Command::renderWin(){
_window.draw(_pauseQuit);
_window.draw(_continue);
_window.draw(_winText);
_window.draw(_scoreText);
}
void Missile_Command::renderMenuEffect(){
if (_menuEffect.particles.size() > 0)
_window.draw(&_menuEffect.particles[0], _menuEffect.particles.size(), sf::PrimitiveType::Triangles);
}
void Missile_Command::renderBG(){
_window.draw(_bgSprite1);
_window.draw(_bgSprite2);
}
void Missile_Command::doHover(ui::Button & button){
if (button.isHovering(_event)){
if (_lastHovered.getString() != button.getString()){
button.setButtonFaceColor(_buttonHoverColor);
_hoverSounds[rand() % _hoverSounds.size()].play();
_lastHovered.setString(button.getString());
}
}
else{
if (_lastHovered.getString() == button.getString())
_lastHovered.setString(" ");
button.setButtonFaceColor(_buttonColor);
}
}
void Missile_Command::setVolume(){
_shootSound.setVolume(_volume);
_explodeSound.setVolume(_volume);
_loseSound.setVolume(_volume);
_waveWinSound.setVolume(_volume);
for (int i = 0; i < _hoverSounds.size();i++)
_hoverSounds[i].setVolume(_volume);
}
void Missile_Command::spawnEnemy(){
if (rand() % 2 == 0){
_enemies.push_back(EntityC(5, 5, sf::Vector2f((float)(rand() % 490), -10.0f), sf::Color::Red));
_enemyVel.push_back(sf::Vector2f(_enemySpeed / (rand() % 5 + 1), _enemySpeed));
}
else{
_enemies.push_back(EntityC(5, 5, sf::Vector2f((float)(rand() % 490 + 500), -10.0f), sf::Color::Red));
_enemyVel.push_back(sf::Vector2f((_enemySpeed / (rand() % 5 + 1)) * -1, _enemySpeed));
}
}
void Missile_Command::removeEnemyAt(size_t index){
_enemies.erase(_enemies.begin() + index);
_enemyVel.erase(_enemyVel.begin() + index);
}
void Missile_Command::doCollisions(){
// Missiles -> Enemies
for (auto missile : _missiles.getExplosions()){
for (size_t i = 0; i < _enemies.size(); i++){
if (cd::doesIntersect(missile, _enemies[i])){
_explodeSound.play();
_missiles.explodeAtPosition(_enemies[i].getPosition() + sf::Vector2f(_enemies[i].getRadius(), _enemies[i].getRadius()), 1.05f, 0.3f, _enemyExplodeColor, _enemyFadeColor);
_score += 10;
removeEnemyAt(i);
i--;
}
}
}
// Enemies -> Bases
for (size_t i = 0; i < _enemies.size(); i++){
if (_enemies[i].getPosition().y > _window.getSize().y - 200){
for (size_t j = 0; j < _bases.size(); j++){
if (_enemies.size() > 0 && cd::doesIntersect(_enemies[i], _bases[j])){
_explodeSound.play();
_missiles.explodeAtPosition(_bases[j].getPosition() + sf::Vector2f(_bases[j].getRadius(), _bases[j].getRadius()), 1.5f, 3.0f, _baseExplodeColor, _baseFadeColor);
removeBaseAt(j);
removeEnemyAt(i);
_score += 100 / (_bases.size() + 1);
std::cout << "sup\n";
i = i - 1 > 0 ? i - 1 : i;
break;
}
}
}
}
// Enemies -> Floor
for (size_t i = 0; i < _enemies.size(); i++){
if (_enemies[i].getPosition().y > _window.getSize().y - 200){
if (_enemies.size() > 0 && _enemies[i].getPosition().y > _window.getSize().y - 60){
_explodeSound.play();
_missiles.explodeAtPosition(_enemies[i].getPosition() + sf::Vector2f(_enemies[i].getRadius(), _enemies[i].getRadius()), 1.05f, 0.3f, _enemyExplodeColor, _enemyFadeColor);
removeEnemyAt(i);
_score -= 10;
i = i - 1 > 0 ? i - 1 : i;
continue;
}
else if (_enemies.size() > 0 && cd::doesIntersect(_enemies[i], _playerFloor)){
_explodeSound.play();
_missiles.explodeAtPosition(_enemies[i].getPosition() + sf::Vector2f(_enemies[i].getRadius(), _enemies[i].getRadius()), 1.05f, 0.3f, _enemyExplodeColor, _enemyFadeColor);
removeEnemyAt(i);
if (_ammunition >= 5)
_ammunition -= 5;
else
_ammunition = 0;
i = i - 1 > 0 ? i - 1 : i;
continue;
}
}
}
}
void Missile_Command::removeBaseAt(size_t index){
_bases.erase(_bases.begin() + index);
}
void Missile_Command::nextWave(){
_wave++;
_enemyCount = (_wave * _wave) + (_wave * _wave) + 10;
_enemySpeed = _wave / 10.0f + 0.5f;
_enemiesSpawned = 0;
}
std::vector<std::string> Missile_Command::split(std::string string, char splitParam){
std::vector<std::string> splitString;
int strBegin = 0;
for (size_t i = 0; i < string.size(); i++){
if (string[i] == splitParam && i != 0){
splitString.push_back(string.substr(strBegin, i));
if (string.size() > i + 1)
strBegin = i + 1;
}
if (string.size() <= i + 1){
splitString.push_back(string.substr(strBegin, i));
}
}
return splitString;
}
void Missile_Command::getHighscores(){
// read file into program
_highscores.clear();
std::string line;
std::ifstream inputStream("highscores.txt");
if (inputStream.is_open()){
while (std::getline(inputStream, line)){
std::vector<std::string> toIns{ split(line, '|') };
_highscores.insert(_highscores.end(), toIns.begin(), toIns.end());
}
inputStream.close();
}
for (size_t i = 0; i < _highscores.size() - 1; i+=2){
std::cout << _highscores[i] << " " << _highscores[i + 1] << "\n";
}
}
bool Missile_Command::addHighscore(){
bool isAdded;
getHighscores();
// Add score
_highscores.push_back(_playerName);
_highscores.push_back(std::to_string(_score));
// Sort all scores
sortHighscores();
if (atoi(_highscores[_highscores.size() - 1].c_str()) == _score)
isAdded = false;
else
isAdded = true;
// Remove lowest score
if (_highscores.size() > 20){
_highscores.resize(_highscores.size() - 2);
}
// Write highscores to file
writeHighscores();
return isAdded;
}
// write highscores into file
void Missile_Command::writeHighscores(){
std::ofstream outputStream("highscores.txt");
if (outputStream.is_open())
{
for (size_t i = 0; i < _highscores.size(); i += 2){
outputStream << _highscores[i] << '|' << _highscores[i + 1] << '\n';
}
outputStream.close();
}
}
void Missile_Command::sortHighscores(){
// sort highscores
int j;
std::string tmp1;
std::string tmp2;
for (size_t i = 3; i < _highscores.size(); i += 2){
j = i;
while (j > 1 && std::atoi(_highscores[j - 2].c_str()) < std::atoi(_highscores[j].c_str())) {
tmp1 = _highscores[j];
tmp2 = _highscores[j - 1];
_highscores[j] = _highscores[j - 2];
_highscores[j - 1] = _highscores[j - 3];
_highscores[j - 3] = tmp2;
_highscores[j - 2] = tmp1;
j -= 2;
}
}
}
void Missile_Command::run(){
while (_window.isOpen()){
const double elapsedTime = _clock.getElapsedTime().asMicroseconds() / 1000.0;
_skippedTime += elapsedTime;
_secondSkipped += elapsedTime;
_clock.restart();
while (_secondSkipped >= SECOND){
std::cout << _updates << " Updates at an average interval of " << _secondSkipped / _updates << "ms.\n";
_updates = 0;
_secondSkipped -= SECOND;
}
while (_skippedTime >= INTERVAL){
_updates++;
_skippedTime -= INTERVAL;
doEvents();
update();
}
render();
}
}
Entity.h
#pragma once
template<class T>
class Entity : public T
{
public:
//Constructors
Entity(sf::Vector2f size, sf::Vector2f pos, sf::Color color) : T(size){
setPosition(pos);
setFillColor(color);
}
Entity(float radius, size_t pointCount, sf::Vector2f pos, sf::Color color) : T(radius){
setPointCount(pointCount);
setPosition(pos);
setFillColor(color);
}
Entity(float width, float height, float x, float y) : T(sf::Vector2f(width, height)){
setPosition(x, y);
}
Entity() : T(){};
sf::Vector2i getGridPosition() const;
void setGridPosition(size_t x, size_t y);
void setOutline(float thickness, const sf::Color & color);
};
// Definitions ---------------------------------------------------------------------------- Definitions
enum class Side{
Top,
Bottom,
Left,
Right
};
struct Particles{
Particles(){}
std::vector<sf::Vertex> particles;
std::vector<float> life;
void append(sf::Vector2f pos, float life, sf::Color color){
sf::Vertex vertex(pos, color);
this->life.push_back(life);
particles.push_back(vertex);
}
void append(sf::Vector2f pos){
sf::Vertex vertex(pos);
this->life.push_back(100000.01f);
particles.push_back(vertex);
}
void removeAt(size_t index){
life.erase(life.begin() + index);
particles.erase(particles.begin() + index);
}
void clear(){
life.clear();
particles.clear();
}
};
typedef Entity<sf::RectangleShape> EntityR;
typedef Entity<sf::CircleShape> EntityC;
template<class T>
sf::Vector2i Entity<T>::getGridPosition() const{
return sf::Vector2i(getPosition() / BLOCKSIZE);
}
template<class T>
void Entity<T>::setGridPosition(size_t x, size_t y){
setPosition(x * BLOCKSIZE, y * BLOCKSIZE);
}
template<class T>
void Entity<T>::setOutline(float thickness, const sf::Color & color){
setOutlineThickness(thickness);
setOutlineColor(color);
}
Entity.cpp
#include "init.h"
Button.h
#pragma once
namespace ui{
class Button : public sf::Drawable
{
EntityR buttonFace;
sf::Font font;
sf::Text text;
public:
Button(std::string text, size_t textSize, sf::Vector2f pos, sf::Color textColor, sf::Color buttonFaceColor, std::string font);
Button& operator=(Button other){
font = other.font;
text = other.text;
buttonFace = other.buttonFace;
text.setFont(font);
return *this;
}
Button(){}
std::string getString() const;
EntityR getButtonFaceObj() const;
sf::Vector2f getPosition() const;
sf::Vector2f getButtonFaceSize() const;
size_t getTextSize() const;
sf::Color getTextColor() const;
sf::Color getButtonFaceColor() const;
void setString(std::string text);
void setPosition(const sf::Vector2f & pos);
void setTextSize(size_t textSize);
void setTextColor(const sf::Color & color);
void setButtonFaceColor(const sf::Color & color);
bool isPressed(const sf::Event & event);
bool isHovering(const sf::Event & event);
virtual void draw(sf::RenderTarget & target, sf::RenderStates states) const;
};
}
Button.cpp (Advice on how I could improve this class would be great)
#include "init.h"
ui::Button::Button(std::string text, size_t textSize, sf::Vector2f pos, sf::Color textColor, sf::Color buttonFaceColor, std::string font){
this->font.loadFromFile(font);
this->text.setFont(this->font);
setTextSize(textSize);
setString(text);
setPosition(pos);
setTextColor(textColor);
setButtonFaceColor(buttonFaceColor);
}
std::string ui::Button::getString() const{
return text.getString();
}
EntityR ui::Button::getButtonFaceObj() const{
return buttonFace;
}
sf::Vector2f ui::Button::getPosition() const{
return buttonFace.getPosition();
}
sf::Vector2f ui::Button::getButtonFaceSize() const{
return buttonFace.getSize();
}
size_t ui::Button::getTextSize() const{
return text.getCharacterSize();
}
sf::Color ui::Button::getTextColor() const{
return text.getColor();
}
sf::Color ui::Button::getButtonFaceColor() const{
return buttonFace.getFillColor();
}
void ui::Button::setString(std::string text){
this->text.setString(text);
setTextSize(getTextSize());
}
void ui::Button::setPosition(const sf::Vector2f & pos){
text.setPosition(pos + sf::Vector2f(10, 10));
float left = text.getGlobalBounds().left;
float top = text.getGlobalBounds().top;
buttonFace.setPosition(left - 10, top - 10);
}
void ui::Button::setTextSize(size_t textSize){
text.setCharacterSize(textSize);
float left = text.getGlobalBounds().left;
float top = text.getGlobalBounds().top;
float width = text.getGlobalBounds().width;
float height = text.getGlobalBounds().height;
buttonFace.setSize(sf::Vector2f(width + 20, height + 20));
buttonFace.setPosition(left - 10, top - 10);
}
void ui::Button::setTextColor(const sf::Color & color){
text.setColor(color);
}
void ui::Button::setButtonFaceColor(const sf::Color & color){
buttonFace.setFillColor(color);
}
bool ui::Button::isPressed(const sf::Event & event){
if (event.type == sf::Event::MouseButtonPressed){
if (event.mouseButton.button == sf::Mouse::Button::Left){
sf::Vector2f mousePos((float)event.mouseButton.x, (float)event.mouseButton.y);
if (cd::doesIntersect(mousePos, buttonFace))
return true;
}
}
return false;
}
bool ui::Button::isHovering(const sf::Event & event){
sf::Vector2f mousePos((float)event.mouseMove.x, (float)event.mouseMove.y);
if (cd::doesIntersect(mousePos, buttonFace))
return true;
return false;
}
void ui::Button::draw(sf::RenderTarget & target, sf::RenderStates states) const{
target.draw(buttonFace);
target.draw(text);
}
Missiles.h
#pragma once
class Missiles : public sf::Drawable
{
std::vector<sf::RectangleShape> missiles;
std::vector<EntityC> explosions;
std::vector<sf::Vector2f> missileVelocities;
std::vector<sf::Vector2f> explodePositions;
std::vector<float> expandVelocities;
std::vector<float> fadeVelocities;
std::vector<sf::Color> explodeColors;
std::vector<sf::Color> fadeColors;
sf::RectangleShape model;
sf::Color standardFadeColor;
float standardFadeVel;
float standardExpandVel;
public:
Missiles(const sf::Vector2f & size, const sf::Vector2f & origin, const sf::Color & color, const sf::Color & fadeColor, float expandVelocity, float fadeVelocity);
Missiles(){};
std::vector<EntityC> getExplosions() const{ return explosions; }
void update(SoundEffect & sound);
void clear();
void removeMissileAt(size_t index);
void removeExplosionAt(size_t index);
void explodeAtPosition(const sf::Vector2f & pos, float fadeVel, float expandVel, sf::Color color, sf::Color fadeColor);
void fireMissile(const sf::Vector2f & velocity, const sf::Vector2f & explodePos);
virtual void draw(sf::RenderTarget & target, sf::RenderStates states) const;
};
Missiles.cpp
#include "init.h"
Missiles::Missiles(const sf::Vector2f & size, const sf::Vector2f & origin, const sf::Color & color, const sf::Color & fadeColor, float expandVelocity, float fadeVelocity){
model.setSize(size);
model.setPosition(origin);
model.setFillColor(color);
standardFadeVel = fadeVelocity;
standardExpandVel = expandVelocity;
standardFadeColor = fadeColor;
clear();
}
void Missiles::update(SoundEffect & sound){
// Move missiles
for (size_t i = 0; i < missiles.size(); i++){
missiles[i].move(missileVelocities[i]);
}
// Test for boundary intersections
for (size_t i = 0; i < missiles.size(); i++){
if (cd::boundaryIntersect(missiles[i], EntityR((float)SCRW, (float)SCRH, 0, 0), Side::Top) ||
cd::boundaryIntersect(missiles[i], EntityR((float)SCRW, (float)SCRH, 0, 0), Side::Bottom) ||
cd::boundaryIntersect(missiles[i], EntityR((float)SCRW, (float)SCRH, 0, 0), Side::Left) ||
cd::boundaryIntersect(missiles[i], EntityR((float)SCRW, (float)SCRH, 0, 0), Side::Right))
{
removeMissileAt(i);
}
}
// Expand explosions and remove explosions that have completed their cycle
for (size_t i = 0; i < explosions.size(); i++){
explosions[i].setRadius(explosions[i].getRadius() + expandVelocities[i]);
explosions[i].setPosition(explosions[i].getPosition() - sf::Vector2f(expandVelocities[i], expandVelocities[i]));
if (explodeColors[i].a >= fadeVelocities[i]){
explodeColors[i].a -= fadeVelocities[i];
if (explodeColors[i].r > fadeColors[i].r)
explodeColors[i].r--;
else if (explodeColors[i].r < fadeColors[i].r)
explodeColors[i].r++;
if (explodeColors[i].g > fadeColors[i].g)
explodeColors[i].g--;
else if (explodeColors[i].g < fadeColors[i].g)
explodeColors[i].g++;
if (explodeColors[i].b > fadeColors[i].b)
explodeColors[i].b--;
else if (explodeColors[i].b < fadeColors[i].b)
explodeColors[i].b++;
explosions[i].setFillColor(explodeColors[i]);
}
else{
removeExplosionAt(i);
i = 0;
}
}
// Test if a missile should explode
bool willExplode = false;
size_t index = 0;
for (size_t i = 0; i < missiles.size(); i++){
sf::Vector2f missilePos = missiles[i].getPosition();
if (missileVelocities[i].x <= 0 && missileVelocities[i].y <= 0 && missilePos.x <= explodePositions[i].x && missilePos.y <= explodePositions[i].y ||
missileVelocities[i].x <= 0 && missileVelocities[i].y > 0 && missilePos.x <= explodePositions[i].x && missilePos.y > explodePositions[i].y ||
missileVelocities[i].x > 0 && missileVelocities[i].y <= 0 && missilePos.x > explodePositions[i].x && missilePos.y <= explodePositions[i].y ||
missileVelocities[i].x > 0 && missileVelocities[i].y > 0 && missilePos.x > explodePositions[i].x && missilePos.y > explodePositions[i].y){
willExplode = true;
index = i;
}
if (willExplode)
break;
}
if (willExplode){
sound.play();
fadeVelocities.push_back(standardFadeVel);
fadeColors.push_back(standardFadeColor);
expandVelocities.push_back(standardExpandVel);
explodeColors.push_back(model.getFillColor());
explosions.push_back(EntityC(model.getSize().x/2.0f, 30, explodePositions[index], explodeColors[explodeColors.size() - 1]));
removeMissileAt(index);
}
}
void Missiles::clear(){
missiles.clear();
explosions.clear();
missileVelocities.clear();
explodePositions.clear();
expandVelocities.clear();
fadeVelocities.clear();
explodeColors.clear();
fadeColors.clear();
}
void Missiles::removeMissileAt(size_t index){
missiles.erase(missiles.begin() + index);
missileVelocities.erase(missileVelocities.begin() + index);
explodePositions.erase(explodePositions.begin() + index);
}
void Missiles::removeExplosionAt(size_t index){
explosions.erase(explosions.begin() + index);
explodeColors.erase(explodeColors.begin() + index);
fadeVelocities.erase(fadeVelocities.begin() + index);
fadeColors.erase(fadeColors.begin() + index);
expandVelocities.erase(expandVelocities.begin() + index);
}
void Missiles::explodeAtPosition(const sf::Vector2f & pos, float fadeVel, float expandVel, sf::Color color, sf::Color fadeColor){
fadeVelocities.push_back(fadeVel);
fadeColors.push_back(fadeColor);
expandVelocities.push_back(expandVel);
explodeColors.push_back(color);
explosions.push_back(EntityC(model.getSize().x / 2.0f, 30, pos, explodeColors[explodeColors.size() - 1]));
}
void Missiles::fireMissile(const sf::Vector2f & velocity, const sf::Vector2f & explodePos){
explodePositions.push_back(explodePos);
missileVelocities.push_back(velocity);
missiles.push_back(model);
}
void Missiles::draw(sf::RenderTarget & target, sf::RenderStates states) const{
for (auto item : missiles){
target.draw(item);
}
for (auto item : explosions){
target.draw(item);
}
}
Collision_Detection.h
#pragma once
enum class Side;
namespace cd{
// A simple struct to make it easier to do stuff with lines
struct Line;
// A simple function to determine whether entity1 is within entity2
bool doesIntersect(EntityR entity1, EntityR entity2);
bool doesIntersect(EntityC entity1, EntityR entity2);
bool doesIntersect(EntityC entity1, EntityC entity2);
bool doesIntersect(sf::Vector2f point, EntityR entity);
// Test if an entity is outside the entity bounds
bool boundaryIntersect(sf::RectangleShape entity, sf::RectangleShape bounds, Side side);
bool boundaryIntersect(sf::CircleShape entity, sf::RectangleShape bounds, Side side);
// Test for an intersection between two lines
bool lineIntersect(Line line, sf::Vector2f pointMoving, float diam);
// Test if two entities will intersect with the current velocity vel
bool willIntercept(const EntityR & entityMoving, const EntityR & entityStatic, sf::Vector2f vel, Side side);
}
Collision_Detection.cpp
#include "init.h"
struct cd::Line{
Line(){}
Line(sf::Vector2f pointA, sf::Vector2f pointB) : A(pointA), B(pointB){}
sf::Vector2f A;
sf::Vector2f B;
};
bool cd::doesIntersect(EntityR entity1, EntityR entity2){
if (entity1.getPosition().x <= entity2.getPosition().x + entity2.getSize().x &&
entity1.getPosition().x + entity1.getSize().x >= entity2.getPosition().x &&
entity1.getPosition().y <= entity2.getPosition().y + entity2.getSize().y &&
entity1.getPosition().y + entity1.getSize().y >= entity2.getPosition().y)
return true;
return false;
}
bool cd::doesIntersect(EntityC entity1, EntityR entity2){
return false;
}
bool cd::doesIntersect(EntityC entity1, EntityC entity2){
sf::Vector2f origin1 = entity1.getPosition() + sf::Vector2f(entity1.getRadius(), entity1.getRadius());
sf::Vector2f origin2 = entity2.getPosition() + sf::Vector2f(entity2.getRadius(), entity2.getRadius());
float radius1 = entity1.getRadius();
float radius2 = entity2.getRadius();
if (sqrt((origin2.x - origin1.x) * (origin2.x - origin1.x) + (origin2.y - origin1.y) * (origin2.y - origin1.y)) < (radius1 + radius2)){
return true;
}
return false;
}
bool cd::doesIntersect(sf::Vector2f point, EntityR entity){
if (point.x >= entity.getPosition().x &&
point.x <= entity.getPosition().x + entity.getSize().x &&
point.y >= entity.getPosition().y &&
point.y <= entity.getPosition().y + entity.getSize().y)
return true;
return false;
}
bool cd::boundaryIntersect(sf::RectangleShape entity, sf::RectangleShape bounds, Side side){
if (side == Side::Top && entity.getPosition().y < bounds.getPosition().y)
return true;
else if (side == Side::Bottom && entity.getPosition().y + entity.getSize().y > bounds.getPosition().y + bounds.getSize().y)
return true;
else if (side == Side::Left && entity.getPosition().x < bounds.getPosition().x)
return true;
else if (side == Side::Right && entity.getPosition().x + entity.getSize().x > bounds.getPosition().x + bounds.getSize().x)
return true;
return false;
}
bool cd::boundaryIntersect(sf::CircleShape entity, sf::RectangleShape bounds, Side side){
if (side == Side::Top && entity.getPosition().y < bounds.getPosition().y)
return true;
else if (side == Side::Bottom && entity.getPosition().y + entity.getRadius() * 2.0f > bounds.getPosition().y + bounds.getSize().y)
return true;
else if (side == Side::Left && entity.getPosition().x < bounds.getPosition().x)
return true;
else if (side == Side::Right && entity.getPosition().x + entity.getRadius() * 2.0f > bounds.getPosition().x + bounds.getSize().x)
return true;
return false;
}
bool cd::lineIntersect(Line line, sf::Vector2f pointMoving, float diam){
if (line.A.x == line.B.x && pointMoving.x >= line.A.x - diam / 2.0f && pointMoving.x <= line.A.x + diam / 2.0f &&
pointMoving.y >= line.A.y && pointMoving.y <= line.B.y)
return true;
if (line.A.y == line.B.y && pointMoving.y >= line.A.y - diam / 2.0f && pointMoving.y <= line.A.y + diam / 2.0f &&
pointMoving.x >= line.A.x && pointMoving.x <= line.B.x)
return true;
return false;
}
bool cd::willIntercept(const EntityR & entityMoving, const EntityR & entityStatic, sf::Vector2f vel, Side side){
float m, b;
sf::Vector2f emCenter(entityMoving.getPosition() + (entityMoving.getSize() / 2.0f));
m = ((emCenter.y + vel.y) - emCenter.y) /
((emCenter.x + vel.x) - emCenter.x);
b = emCenter.y - m * emCenter.x;
Line line;
sf::Vector2f start(emCenter);
sf::Vector2f end;
if (side <= Side::Bottom){
line.A = entityStatic.getPosition() + sf::Vector2f(0, entityStatic.getSize().y * (int)side);
line.B = entityStatic.getPosition() + sf::Vector2f(entityStatic.getSize().x, entityStatic.getSize().y * (int)side);
end = sf::Vector2f((float)(int)((vel.y + emCenter.y - b) / m), vel.y + emCenter.y);
if (side == Side::Top){
for (float y = 0; y <= vel.y; y += 0.1f){
if (lineIntersect(line, sf::Vector2f((float)(int)((y + emCenter.y - b) / m), y + emCenter.y), entityMoving.getSize().y))
return true;
}
}
else if (side == Side::Bottom){
for (float y = 0; y >= vel.y; y -= 0.1f){
if (lineIntersect(line, sf::Vector2f((float)(int)((y + emCenter.y - b) / m), y + emCenter.y), entityMoving.getSize().y))
return true;
}
}
}
else if (side >= Side::Left){
int tmpSide = (int)side - (int)Side::Left;
line.A = entityStatic.getPosition() + sf::Vector2f(entityStatic.getSize().x * tmpSide, 0);
line.B = entityStatic.getPosition() + sf::Vector2f(entityStatic.getSize().x * tmpSide, entityStatic.getSize().y);
end = sf::Vector2f(vel.x + emCenter.x, (float)(int)(m * (vel.x + emCenter.x) + b));
if (side == Side::Left){
for (float x = 0; x <= vel.x; x += 0.1f){
if (lineIntersect(line, sf::Vector2f(x + emCenter.x, (float)(int)(m * (x + emCenter.x) + b)), entityMoving.getSize().x))
return true;
}
}
else if (side == Side::Right){
for (float x = 0; x >= vel.x; x -= 0.1f){
if (lineIntersect(line, sf::Vector2f(x + emCenter.x, (float)(int)(m * (x + emCenter.x) + b)), entityMoving.getSize().x))
return true;
}
}
}
return false;
}
Math.h
#pragma once
namespace mth{
// Basically pi
const double PI = 3.14159265358979323846264338327950288419716939937510582097494459;
// Get the angle between two vectors in degrees
double getAngleInDegrees(const sf::Vector2f & entityStaticCenter, const sf::Vector2f & entityMovingCenter);
// Get the angle between two vectors in radians
double getAngleInRadians(const sf::Vector2f & entityStaticCenter, const sf::Vector2f & entityMovingCenter);
// Calculate directional veloctiy, given radians and global velocity
sf::Vector2f getVelocity(double radians, double vel);
}
Math.cpp
#include "init.h"
double mth::getAngleInDegrees(const sf::Vector2f & entityStaticCenter, const sf::Vector2f & entityMovingCenter){
double winkel = atan2((entityMovingCenter.y - (entityStaticCenter.y)), (entityMovingCenter.x - (entityStaticCenter.x)));
winkel *= 180 / PI;
return winkel;
}
double mth::getAngleInRadians(const sf::Vector2f & entityStaticCenter, const sf::Vector2f & entityMovingCenter){
return atan2f((entityMovingCenter.y - (entityStaticCenter.y)), (entityMovingCenter.x - (entityStaticCenter.x)));
}
sf::Vector2f mth::getVelocity(double radians, double vel){
return sf::Vector2f((float)cos(radians) * (float)vel, (float)sin(radians) * (float)vel);
}
SoundEffect.h
#pragma once
class SoundEffect
{
sf::SoundBuffer buffer;
sf::Sound sound;
public:
SoundEffect(std::string filePath, float volume);
SoundEffect(std::string filePath, sf::Sound sound);
SoundEffect(){ }
SoundEffect& operator=(SoundEffect other){
buffer = other.buffer;
sound = other.sound;
sound.setBuffer(buffer);
return *this;
}
float getVolume() const;
void setVolume(float volume);
void play();
void pause();
void stop();
};
SoundEffect.cpp
#include "init.h"
SoundEffect::SoundEffect(std::string filePath, float volume){
if (!buffer.loadFromFile(filePath))
std::exit(-3);
sound.setBuffer(buffer);
sound.setVolume(volume);
}
SoundEffect::SoundEffect(std::string filePath, sf::Sound sound){
if (!buffer.loadFromFile(filePath))
std::exit(-3);
this->sound = sound;
this->sound.setBuffer(buffer);
}
float SoundEffect::getVolume() const{
return sound.getVolume();
}
void SoundEffect::setVolume(float volume){
sound.setVolume(volume);
}
void SoundEffect::play(){
sound.play();
}
void SoundEffect::pause(){
sound.pause();
}
void SoundEffect::stop(){
sound.stop();
}
Thank you for your time and for reading!