3
\$\begingroup\$

The game basically has a player (PlayerCharacter class) that can move around the world. It has a 2D world made of tiles managed by TileEngine class. The player can shoot projectiles and can collide with the boundary of the map and any solid tiles.

Entity.h:

#ifndef ENTITY_H
#define ENTITY_H

#include <cmath>

class Entity
{
public:
    Entity();
    Entity(float px, float py) : mPosX(px), mPosY(py) {}
    ~Entity();

    //Getters
    float GetPosX() { return mPosX; }
    float GetPosY() { return mPosY; }

    //Setters
    void SetPosX(float val) { mPosX = val; }
    void SetPosY(float val) { mPosY = val; }

protected:
    float mPosX;
    float mPosY;
};

#endif //ENTITY_H

Entity.cpp:

#include "Entity.h"

Entity::Entity()
{

}

Entity::~Entity()
{

}

PlayerCharacter.h:

#ifndef PLAYERCHARACTER_H
#define PLAYERCHARACTER_H

#include "Entity.h"
#include "WeaponStats.h"
#include "Projectile.h"
#include <SFML/Graphics.hpp>
#include <vector>

struct  KeyState{
    bool UpPressed;
    bool DownPressed;
    bool LeftPressed;
    bool RightPressed;
    bool LMBPressed;
};

class PlayerCharacter : public Entity
{
public:
    //Constructor and Destructor
    PlayerCharacter();
    PlayerCharacter(float pPosX, float pPosY, float pSpeed = 0, float pHealth = 0);
    PlayerCharacter(float pPosX, float pPosY, sf::Texture pTexture, float pSpeed = 0, float pHealth = 0);
    ~PlayerCharacter();

    //Getters
    float GetSpeed() { return mSpeed; }
    float GetHealth() {return mHealth; }
    sf::Sprite GetSprite() { return mSprite; }
    sf::Texture GetTexture() { return mTexture; }
    float GetDirection() { return mDirection; }
    WeaponStats GetWeapon() { return mWeapon; }
    std::vector<Projectile>& GetProjectiles() { return mProjectiles; }

    //Setters
    void SetSpeed(float val) { mSpeed = val; }
    void SetHealth(float val);
    void SetSprite(sf::Sprite val) { mSprite = val; }
    void SetTexture(sf::Texture val);
    void SetDiretion(float val) { mDirection = val; }
    void SetWeapon(WeaponStats pWeapon) { mWeapon = pWeapon; }
    void SetProjectiles(std::vector<Projectile>& pProjectiles) { mProjectiles = pProjectiles; }

    //Member Functions
    void Render(sf::RenderWindow* pTarget);     //Draw the player sprite and the projectiles
    void Update(float TimeStep, KeyState val, bool WillCollide);  //Update the projectiles positions and the players sprite
    void GenerateProjectile();

private:
    void UpdateProjectiles(float TimeStep);
    void RenderProjectiles(sf::RenderWindow* pTarget);
    void UpdateSprite();
    void Move(KeyState val, float TimeStep);    //Move the player in the pressed directions

    sf::Sprite mSprite;
    sf::Texture mTexture;
    float mSpeed;
    float mHealth;
    float mDirection;
    WeaponStats mWeapon;
    std::vector<Projectile> mProjectiles;
    sf::Clock mWeaponClock;
};

#endif // PLAYERCHARACTER_H

PlayerCharacter.cpp:

#include "PlayerCharacter.h"

PlayerCharacter::PlayerCharacter()
{

}

PlayerCharacter::~PlayerCharacter()
{

}

PlayerCharacter::PlayerCharacter(float pPosX, float pPosY, sf::Texture pTexture, float pSpeed, float pHealth)
{
    SetPosX(pPosX);
    SetPosY(pPosY);
    SetSpeed(pSpeed);
    SetHealth(pHealth);
    SetTexture(pTexture);
}

PlayerCharacter::PlayerCharacter(float pPosX, float pPosY, float pSpeed, float pHealth)
{
    SetPosX(pPosX);
    SetPosY(pPosY);
    SetSpeed(pSpeed);
    SetHealth(pHealth);
}

void PlayerCharacter::SetHealth(float val)
{
    mHealth = val;

    if (mHealth < 0)    //Health should not be less than zero
    {
        mHealth = 0;
    }
}

void PlayerCharacter::SetTexture(sf::Texture val)
{
    mTexture = val;
    mSprite.setTexture(mTexture);
    mSprite.setOrigin(mTexture.getSize().x / 2, mTexture.getSize().y / 2);  //Set the origin to be the center of the texture so it rotates around the center
    //mSprite.setOrigin(0, 0);
}

void PlayerCharacter::Move(KeyState var, float TimeStep)
{
    if (var.UpPressed)
        SetPosY(mPosY -= mSpeed * TimeStep);
    if (var.DownPressed)
        SetPosY(mPosY += mSpeed * TimeStep);
    if (var.LeftPressed)
        SetPosX(mPosX -= mSpeed * TimeStep);
    if (var.RightPressed)
        SetPosX(mPosX += mSpeed * TimeStep);
}

void PlayerCharacter::Render(sf::RenderWindow* pTarget)
{
    pTarget->draw(mSprite);
    RenderProjectiles(pTarget);
}

void PlayerCharacter::UpdateSprite()
{
    mSprite.setPosition(mPosX, mPosY);
    mSprite.setRotation(mDirection + 90);
}

void PlayerCharacter::UpdateProjectiles(float TimeStep)
{
    for (unsigned int i = 0; i < mProjectiles.size(); i++)
    {
        mProjectiles[i].Update(TimeStep);
    }
}

void PlayerCharacter::RenderProjectiles(sf::RenderWindow* pTarget)
{
    for (unsigned int i = 0; i < mProjectiles.size(); i++)
    {
        mProjectiles[i].Render(pTarget);
    }
}

void PlayerCharacter::Update(float TimeStep, KeyState val, bool WillCollide)
{
    if (!WillCollide)   //LevelEntityManager will pass a true of false wether it is allowed to move
        Move(val, TimeStep);

    UpdateSprite();

    if (val.LMBPressed && mWeaponClock.getElapsedTime().asSeconds() > mWeapon.mFireRate)
    {
        GenerateProjectile();
        mWeaponClock.restart();
    }

    UpdateProjectiles(TimeStep);
}

void PlayerCharacter::GenerateProjectile()
{
    float RandDir = mDirection + (-1 + static_cast <float> (rand()) /( static_cast <float> (RAND_MAX/(2)))) * mWeapon.mBulletSpred;         //Random number between 0 and 1 * Spread

    Projectile Temp(mPosX, mPosY, RandDir, mWeapon.mBulletSpeed, mWeapon.mDamage, mWeapon.mBulletTexture);

    mProjectiles.push_back(Temp);
}

FunctionLib.h:

#ifndef FUNCTIONLIB_H
#define FUNCTIONLIB_H

//required headers
#include "Entity.h"
#include "SFML/Graphics.hpp"

//required preprocessor macros
#define _PI 3.14159265

//member functions
float DistanceBetween(float px1, float py1, float px2, float py2);
float DistanceBetween(Entity* pEntity1, Entity* pEntity2);
float ToDegrees(float radians);
float ToRadians(float degrees);
float DirectionToPoint(float ax, float ay, float bx, float by); //returns in radians the angle from a(x, y) to b(x, y)
float DirectionToPoint(Entity* a, Entity* b);                   //returns in radians the angle from a to b

std::vector<sf::Vector2f> GenerateBoxFromSprite(sf::Sprite pSprite);
std::vector<sf::Vector2f> GenerateBoxFromDimentions(float px, float py, float width, float height);

#endif // FUNCTIONLIB_H

FunctionLib.cpp:

#include "FunctionLib.h"

float DistanceBetween(float px1, float py1, float px2, float py2)
{
    float DiffX = px1 - px2;
    float DiffY = py1 - py2;

    float DistSqr = DiffX * DiffX + DiffY * DiffY;

    return sqrt(DistSqr);
}

float DistanceBetween(Entity* pEntity1, Entity* pEntity2)
{
    float DiffX = pEntity1->GetPosX() - pEntity2->GetPosX();
    float DiffY = pEntity1->GetPosY() - pEntity2->GetPosY();

    float DistSqr = DiffX * DiffX + DiffY * DiffY;

    return sqrt(DistSqr);
}

float ToDegrees(float radians)
{
    return radians * (180 / _PI);
}

float ToRadians(float degrees)
{
    return degrees * (_PI / 180);
}

float DirectionToPoint(float ax, float ay, float bx, float by)
{
    float DiffX = bx - ax;
    float DiffY = by - ay;

    return atan2(DiffY, DiffX);
}

float DirectionToPoint(Entity* a, Entity* b)
{
    float DiffX = b->GetPosX() - a->GetPosX();
    float DiffY = b->GetPosY() - a->GetPosY();

    return atan2(DiffY, DiffX);
}

std::vector<sf::Vector2f> GenerateBoxFromSprite(sf::Sprite pSprite)
{
    sf::Vector2f P1((pSprite.getPosition().x - pSprite.getOrigin().x), (pSprite.getPosition().y - pSprite.getOrigin().y)); //Top right corner
    sf::Vector2f P2((pSprite.getPosition().x + pSprite.getOrigin().x), (pSprite.getPosition().y - pSprite.getOrigin().y)); //Top left
    sf::Vector2f P3((pSprite.getPosition().x - pSprite.getOrigin().x), (pSprite.getPosition().y + pSprite.getOrigin().y)); //Bottom Right
    sf::Vector2f P4((pSprite.getPosition().x + pSprite.getOrigin().x), (pSprite.getPosition().y + pSprite.getOrigin().y)); //Bottom left

    std::vector<sf::Vector2f> CornerPoints;

    CornerPoints.push_back(P1);
    CornerPoints.push_back(P2);
    CornerPoints.push_back(P3);
    CornerPoints.push_back(P4);

    return CornerPoints;
}

std::vector<sf::Vector2f> GenerateBoxFromDimentions(float px, float py, float width, float height)
{
    sf::Vector2f P1(px, py); //Top right corner
    sf::Vector2f P2(px + width, py); //Top left
    sf::Vector2f P3(px, py + height); //Bottom Right
    sf::Vector2f P4(px + width, py + height); //Bottom left

    std::vector<sf::Vector2f> CornerPoints;

    CornerPoints.push_back(P1);
    CornerPoints.push_back(P2);
    CornerPoints.push_back(P3);
    CornerPoints.push_back(P4);

    return CornerPoints;
}

WeaponStats.h:

#ifndef WEAPONSTATS_H_INCLUDED
#define WEAPONSTATS_H_INCLUDED

#include <SFML/Graphics.hpp>

struct WeaponStats
{
    float mDamage;
    float mBulletSpeed;
    float mFireRate;
    float mBulletSpred;
    sf::Texture mBulletTexture;
};

enum WeaponTypes
{
    RifleWeapon,
    SMGWeapon,
    ShotGunWeapon,
};

WeaponStats GetWeaponStat(WeaponTypes WeaponID);


#endif // WEAPONSTATS_H_INCLUDED

WeaponStats.cpp:

#include "WeaponStats.h"

WeaponStats GetWeaponStat(WeaponTypes WeaponID)
{
    WeaponStats WeaponIDStats;
    sf::Texture WeaponIDTexture;

    switch (WeaponID)
    {
        case RifleWeapon:
            WeaponIDTexture.loadFromFile("Bullet.png");
            WeaponIDStats.mBulletSpeed = 25;
            WeaponIDStats.mDamage = 10;
            WeaponIDStats.mFireRate = 0.1;
            WeaponIDStats.mBulletSpred = 2;
            break;
        case SMGWeapon:
            WeaponIDTexture.loadFromFile("Bullet.png");
            WeaponIDStats.mBulletSpeed = 20;
            WeaponIDStats.mDamage = 7;
            WeaponIDStats.mFireRate = 0.05;
            WeaponIDStats.mBulletSpred = 1;
            break;
        case ShotGunWeapon:
            WeaponIDTexture.loadFromFile("Bullet.png");
            WeaponIDStats.mBulletSpeed = 20;
            WeaponIDStats.mDamage = 20;
            WeaponIDStats.mFireRate = 2;
            WeaponIDStats.mBulletSpred = 5;
            break;
        default:
            break;
    }

    WeaponIDStats.mBulletTexture = WeaponIDTexture;

    return WeaponIDStats;
}

Projectile.h:

#ifndef PROJECTILE_H_INCLUDED
#define PROJECTILE_H_INCLUDED

#include "Entity.h"
#include "FunctionLib.h"
#include <cmath>
#include <SFML/Graphics.hpp>

class Projectile : public Entity
{
public:
    //Construtor and Destructor
    Projectile();
    Projectile(float px, float py, float pDirection, float pVelocity, float pDamage, sf::Texture mSpriteTexture);    //You can give it a direction and a speed
    ~Projectile();

    //getters
    float GetVelX() { return VelX; }
    float GetVelY() { return VelY; }
    float GetDamage() { return mDamage; }
    sf::Sprite GetSprite() { return mSprite; }
    float GetDirection() { return mDirection; }

    //setters
    void SetVelX(float pVelX) { VelX = pVelX; }
    void SetVelY(float pVelY) { VelY = pVelY; }
    void SetDamage(float pDamage) { mDamage = pDamage; }
    void SetSprite(sf::Sprite pSprite) { mSprite = pSprite; }
    void SetDirection(float pDirection);
    void SetTexture(sf::Texture pTexture);

    //public member functions
    void Render(sf::RenderWindow* pTarget) {pTarget->draw(mSprite);}                //draw mSprite to screen
    void Update(float TimeStep);

private:
    //private member functions
    void UpdateSprite();
    void CalculateVelocities(float pDirection, float pVelocity);                    //Based on a direction and a velocity calculate the velocity x and y

    //private member variables
    float mDirection;
    float VelX, VelY;
    float mDamage;
    sf::Texture mSpriteTexture;
    sf::Sprite mSprite;
};

#endif // PROJECTILE_H_INCLUDED

Projectile.cpp:

#include "Projectile.h"

Projectile::Projectile()
{

}

Projectile::~Projectile()
{

}

Projectile::Projectile(float px, float py, float pDirection, float pVelocity, float pDamage, sf::Texture mSpriteTexture)
{
    CalculateVelocities(pDirection, pVelocity);

    SetDamage(pDamage);
    SetTexture(mSpriteTexture);
    SetDirection(pDirection);
    SetPosX(px);
    SetPosY(py);
}

void Projectile::Update(float TimeStep)
{
    mPosX += VelX * TimeStep;
    mPosY += VelY * TimeStep;

    UpdateSprite();
}

void Projectile::SetDirection(float pDirection)
{
    mDirection = pDirection;
    mSprite.setRotation(mDirection + 90);
}

void Projectile::SetTexture(sf::Texture pTexture)
{
    mSpriteTexture = pTexture;
    mSprite.setTexture(mSpriteTexture);
    mSprite.setOrigin(mSpriteTexture.getSize().x / 2, mSpriteTexture.getSize().y / 2);
}

void Projectile::UpdateSprite()
{
    mSprite.setTexture(mSpriteTexture);
    mSprite.setPosition(mPosX, mPosY);
}

void Projectile::CalculateVelocities(float pDirection, float pVelocity)
{
    VelX = ToDegrees(cos(ToRadians(pDirection))) * pVelocity;
    VelY = ToDegrees(sin(ToRadians(pDirection))) * pVelocity;
}

TileEngine.h:

#include "SFML/Graphics.hpp"

struct Tile
{
    sf::Sprite mTileSprite;
    bool mSolidState;
};

class TileEngine
{
public:
    TileEngine();                               //Defult ctor
    TileEngine(std::string pFileLocation);      //Generate from a file
    TileEngine(float pTileWidth, float pTileHeight, unsigned int pMapSizeX, unsigned int pMapSizeY, sf::Texture pTileSet, std::vector<std::vector<int> >& pTileIDVec, std::vector<std::vector<bool> >& pSolidStateVec, float pPosX = 0, float pPosY = 0);   //Generate from paramiters
    TileEngine(float pTileWidth, float pTileHeight, unsigned int pMapSizeX, unsigned int pMapSizeY, sf::Texture pTileSet, std::vector<std::vector<Tile> > pTiles, float pPosX = 0, float pPosY = 0);    //Generate from already generated Tile vector
    ~TileEngine();

    //Getters
    float GetPosX() { return mPosX; }
    float GetPosY() { return mPosY; }
    float GetTileWidth() { return mTileWidth; }
    float GetTileHeight() { return mTileHeight; }
    float GetSizeX() { return mMapSizeX; }
    float GetSizeY() { return mMapSizeY; }
    std::vector<std::vector<Tile> >& GetTiles() { return mTiles; }

    //Setters
    void SetPosX(float pPosX) { mPosX = pPosX; }
    void SetPosY(float pPosY) { mPosY = pPosY; }
    void SetTiles(std::vector<std::vector<Tile> >& pTiles) { mTiles = pTiles; }

    //Public member functions
    void LoadFromFile(std::string pFileLocation);
    void LoadFromParam(float pTileWidth, float pTileHeight, unsigned int pMapSizeX, unsigned int pMapSizeY, sf::Texture pTileSet, std::vector<std::vector<int> >& pTileIDVec, std::vector<std::vector<bool> >& pSolidStateVec, float pPosX = 0, float pPosY = 0);
    void LoadFromTiles(float pTileWidth, float pTileHeight, unsigned int pMapSizeX, unsigned int pMapSizeY, sf::Texture pTileSet, std::vector<std::vector<Tile> >& pTiles, float pPosX = 0, float pPosY = 0);
    void Render(sf::RenderWindow* pTarget);
    bool CheckSolid(float px, float py);

private:
    void UpdateTileSpritePos();

    //Private member variables
    float mPosX, mPosY;                 //Position x and y
    float mTileWidth, mTileHeight;      //Tile width and height in pixles
    unsigned int mMapSizeX, mMapSizeY;  //Map size, in tiles
    sf::Texture mTileSet;
    std::vector<std::vector<Tile> > mTiles;
};

#endif // TILEENGINE_H_INCLUDED

TileEngine.cpp:

#include "TileEngine.h"

TileEngine::TileEngine()
{

}

TileEngine::~TileEngine()
{

}

TileEngine::TileEngine(std::string pFileLocation)
{
    LoadFromFile(pFileLocation);
}

TileEngine::TileEngine(float pTileWidth, float pTileHeight, unsigned int pMapSizeX, unsigned int pMapSizeY, sf::Texture pTileSet, std::vector<std::vector<int> >& pTileIDVec, std::vector<std::vector<bool> >& pSolidStateVec, float pPosX, float pPosY)
{
    LoadFromParam(pTileWidth, pTileHeight, pMapSizeX, pMapSizeY, pTileSet, pTileIDVec, pSolidStateVec, pPosX, pPosY);
}

TileEngine::TileEngine(float pTileWidth, float pTileHeight, unsigned int pMapSizeX, unsigned int pMapSizeY, sf::Texture pTileSet, std::vector<std::vector<Tile> > pTiles, float pPosX, float pPosY)
{
    LoadFromTiles(pTileWidth, pTileHeight, pMapSizeX, pMapSizeY, pTileSet, pTiles, pPosX, pPosY);
}

void TileEngine::LoadFromFile(std::string pFileLocation)
{
    //Load from File
}

void TileEngine::LoadFromParam(float pTileWidth, float pTileHeight, unsigned int pMapSizeX, unsigned int pMapSizeY, sf::Texture pTileSet, std::vector<std::vector<int> >& pTileIDVec, std::vector<std::vector<bool> >& pSolidStateVec, float pPosX, float pPosY)
{
    mPosX = pPosX;
    mPosY = pPosY;
    mTileWidth = pTileWidth;
    mTileHeight = pTileHeight;
    mMapSizeX = pMapSizeX;
    mMapSizeY = pMapSizeY;
    mTileSet = pTileSet;

    for (unsigned int i = 0; i < mMapSizeY; i++)
    {
        std::vector<Tile> Row;

        for (unsigned int j = 0; j < mMapSizeX; j++)
        {
            Tile TempTile;

            TempTile.mSolidState = pSolidStateVec[i][j];
            TempTile.mTileSprite.setTexture(mTileSet);
            TempTile.mTileSprite.setTextureRect(sf::IntRect((pTileIDVec[i][j] % static_cast<int>(mTileSet.getSize().x / mTileWidth)) * mTileWidth, static_cast<int>(pTileIDVec[i][j] / (mTileSet.getSize().x / mTileWidth)) * mTileHeight, mTileWidth, mTileHeight));

            Row.push_back(TempTile);
        }

        mTiles.push_back(Row);
    }

    UpdateTileSpritePos();
}

void TileEngine::LoadFromTiles(float pTileWidth, float pTileHeight, unsigned int pMapSizeX, unsigned int pMapSizeY, sf::Texture pTileSet, std::vector<std::vector<Tile> >& pTiles, float pPosX, float pPosY)
{
    mPosX = pPosX;
    mPosY = pPosY;
    mTileWidth = pTileWidth;
    mTileHeight = pTileHeight;
    mMapSizeX = pMapSizeX;
    mMapSizeY = pMapSizeY;
    mTiles = pTiles;
    mTileSet = pTileSet;
}

void TileEngine::Render(sf::RenderWindow* pTarget)
{
    for (unsigned int i = 0; i < mMapSizeY; i++)
    {
        for (unsigned int j = 0; j < mMapSizeX; j++)
        {
            pTarget->draw(mTiles[i][j].mTileSprite);
        }
    }
}

void TileEngine::UpdateTileSpritePos()
{
    for (unsigned int i = 0; i < mMapSizeY; i++)
    {
        for (unsigned int j = 0; j < mMapSizeX; j++)
        {
            mTiles[i][j].mTileSprite.setPosition(mPosX + j * mTileWidth, mPosY + i * mTileHeight);
        }
    }
}

bool TileEngine::CheckSolid(float px, float py)
{
    float RelX = px - mPosX;
    float RelY = py - mPosY;

    if (RelX < 0 || RelY < 0 || RelX > mMapSizeX * mTileWidth ||  RelY > mMapSizeY * mTileHeight)  //If out of the world, colision = true
        return true;

    int TilesX = static_cast<int>(RelX / mTileWidth);
    int TilesY = static_cast<int>(RelY / mTileHeight);

    if (mTiles[TilesY][TilesX].mSolidState)  //guarenteed not to throw out of range exeption because of previous if statement exclusing out of bounds values
        return true;

    return false;
}

LevelEntityManager.h:

#ifndef LEVELENTITYMANAGER_H
#define LEVELENTITYMANAGER_H

#include "PlayerCharacter.h"
#include "TileEngine.h"
#include <SFML/Graphics.hpp>

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

    sf::RenderWindow* GetTarget() { return mpTarget; }
    TileEngine GetTileEngine() { return mTileEngine; }
    PlayerCharacter GetPlayer() { return mPlayer; }

    void SetTarget(sf::RenderWindow* val) { mpTarget = val; }
    void SetTileEngine(TileEngine val) { mTileEngine = val; }
    void SetPlayer(PlayerCharacter val) { mPlayer = val; }

    void Render();
    void Update(KeyState pKeyState);

private:
    bool CheckTileSolidColision(std::vector<sf::Vector2f> CornerPoints);
    sf::Vector2f GetPlayerNewPosition(PlayerCharacter pPlayer, KeyState pKeyState);

    sf::RenderWindow* mpTarget;
    TileEngine mTileEngine;
    PlayerCharacter mPlayer;

    sf::Clock mFrameClock;
    float mFrameTime;
};

#endif // LEVELENTITYMANAGER_H

LevelEntityManager.cpp:

#include "LevelEntityManager.h"

LevelEntityManager::LevelEntityManager()
{
    //ctor
}

LevelEntityManager::~LevelEntityManager()
{
    //dtor
}

void LevelEntityManager::Update(KeyState pKeyState)
{
    mFrameTime = mFrameClock.restart().asSeconds();

    sf::Vector2f NewPlayerPos = GetPlayerNewPosition(mPlayer, pKeyState);
    sf::Vector2f NewPlayerSpritePos = sf::Vector2f(NewPlayerPos.x - mPlayer.GetSprite().getOrigin().x, NewPlayerPos.y - mPlayer.GetSprite().getOrigin().y);

    mPlayer.SetDiretion(ToDegrees(DirectionToPoint(mPlayer.GetPosX(), mPlayer.GetPosY(), sf::Mouse::getPosition(*mpTarget).x, sf::Mouse::getPosition(*mpTarget).y)));
    mPlayer.Update(mFrameTime, pKeyState, CheckTileSolidColision(GenerateBoxFromDimentions(NewPlayerSpritePos.x, NewPlayerSpritePos.y, mPlayer.GetTexture().getSize().x, mPlayer.GetTexture().getSize().y)));
}

sf::Vector2f LevelEntityManager::GetPlayerNewPosition(PlayerCharacter pPlayer, KeyState pKeyState)
{
    float NewPlayerX = pPlayer.GetPosX();
    float NewPlayerY = pPlayer.GetPosY();

    if (pKeyState.UpPressed)
        NewPlayerY -= pPlayer.GetSpeed() * mFrameTime;
    if (pKeyState.DownPressed)
        NewPlayerY += pPlayer.GetSpeed() * mFrameTime;
    if (pKeyState.LeftPressed)
        NewPlayerX -= pPlayer.GetSpeed() * mFrameTime;
    if (pKeyState.RightPressed)
        NewPlayerX += pPlayer.GetSpeed() * mFrameTime;

    sf::Vector2f NewPlayerPos(NewPlayerX, NewPlayerY);

    return NewPlayerPos;
}

void LevelEntityManager::Render()
{
    mTileEngine.Render(mpTarget);   //Order of rendering here is important!
    mPlayer.Render(mpTarget);
}

bool LevelEntityManager::CheckTileSolidColision(std::vector<sf::Vector2f> CornerPoints)
{
    for (int i = 0; i < CornerPoints.size(); i++)
    {
        if (mTileEngine.CheckSolid(CornerPoints[i].x, CornerPoints[i].y))
            return true;
    }

    return false;
}

Main.cpp:

#include "PlayerCharacter.h"
#include "FunctionLib.h"
#include "TileEngine.h"
#include "LevelEntityManager.h"

#include <stdlib.h>     /* srand, rand */
#include <time.h>       /* time */

void PollEvent();
void Render();
void Update();
void GenerateTestLevel();

unsigned int const MapWidth = 20;
unsigned int const MapHeight = 20;

sf::ContextSettings settings;
sf::RenderWindow window;
LevelEntityManager TestLevel;
KeyState KeysPressed;

int main()
{
    srand (time(NULL));

    settings.antialiasingLevel = 8;
    window.create(sf::VideoMode(sf::VideoMode::getDesktopMode().width, sf::VideoMode::getDesktopMode().height), "Heist", sf::Style::Default, settings);

    GenerateTestLevel();

    PlayerCharacter MyPlayer(512, 512, 150, 100);
    TileEngine MyEngine;
    sf::Texture MyTexture;
    sf::Texture TileSet;

    MyTexture.loadFromFile("PlaceHolderPlayer.png");
    TileSet.loadFromFile("TileSet.png");

    MyPlayer.SetTexture(MyTexture);
    MyPlayer.SetWeapon(GetWeaponStat(SMGWeapon));

    std::vector<std::vector<int> > TileIDVec;
    std::vector<std::vector<bool> > SolidStateVec;

    for (unsigned int i = 0; i < MapHeight; i++)
    {
        std::vector<bool> boolRow;
        std::vector<int> intRow;

        for (unsigned int j = 0; j < MapWidth; j++)
        {
            intRow.push_back(rand() % 10);   //random between 0 and 0 (always 0) and then +1 to always be 1

            if (intRow[j] > 1)
                intRow[j] = 1;

            if (intRow[j] == 0)
                boolRow.push_back(true);       //random between 0 and 0
            else
                boolRow.push_back(false);
        }

        SolidStateVec.push_back(boolRow);
        TileIDVec.push_back(intRow);
    }

    MyEngine.LoadFromParam(32, 32, MapWidth, MapHeight, TileSet, TileIDVec, SolidStateVec, 64, 64);

    TestLevel.SetPlayer(MyPlayer);
    TestLevel.SetTileEngine(MyEngine);
    TestLevel.SetTarget(&window);

    while (window.isOpen())
    {
        PollEvent();
        Render();
    }
}

void GenerateTestLevel()
{

}

void PollEvent()
{
    sf::Event event;

    while (window.pollEvent(event))
    {
        if (event.type == sf::Event::Closed)
            window.close();
    }

    Update();
}

void Update()
{
    KeysPressed.LMBPressed = sf::Mouse::isButtonPressed(sf::Mouse::Left);
    KeysPressed.LeftPressed = sf::Keyboard::isKeyPressed(sf::Keyboard::A);
    KeysPressed.RightPressed = sf::Keyboard::isKeyPressed(sf::Keyboard::D);
    KeysPressed.UpPressed = sf::Keyboard::isKeyPressed(sf::Keyboard::W);
    KeysPressed.DownPressed = sf::Keyboard::isKeyPressed(sf::Keyboard::S);

    TestLevel.Update(KeysPressed);
}

void Render()
{
    window.clear();
    TestLevel.Render();
    window.display();
}
\$\endgroup\$
  • \$\begingroup\$ Wow, that's a wall of code to review :o Without reaing the code, it looks clean though. That's already a good point :) \$\endgroup\$ – Morwenn Aug 13 '15 at 10:26
4
\$\begingroup\$

Remove unused headers

For example, in Entity.h you include #include <cmath> but it's not used. If you would only use it in your cpp, move the include there to save some compile time. If compile time is really important you can even predeclare in your header.

Use initializer lists for all constructors

For example, your zero-argument constructor in the Entity class (the one defined in the .cpp) doesn't initialize it's arguments, and you might up reading garbage memory.

Mark const methods as such

For example all getters can be marked as const, as well as all other functions that do not logically alter the state of the object.

float GetPosX() const { return mPosX; }
CheckTileSolidColision(const std::vector<sf::Vector2f>& CornerPoints) const;

Use virtual destructor's for classes you've inherited from

virtual ~Entity();

If you don't, the destructor of the subclass may not get executed if you're working on the interface.

Be consistent in naming conventions

It seems that you use the m prefix for members and p prefix for parameters. That's probably a good idea but be consistent

Projectile::Projectile(float px, float py, float pDirection, float pVelocity, float pDamage, sf::Texture mSpriteTexture) // Change mSpriteTexture in pSpriteTexture

Another tip is to use lowerCamelCase for variable names.

Try to avoid useless comments

Comments such as //ctor inside of a constructor don't say much - the syntax of the language and/or name of the function should be good enough to describe simple actions. Comments are very valuable, so feel free them on places where they are useful (in complex methods), but don't add any that add nothing.

Use nullptr over NULL

NULL is defined as a zero, which can also be considered as an integer by the compiler. This can get some weird issues when you have a pointer and integer overloaded function.

Pass arguments by const reference where possible

If an argument has a complicated copy constructor (e.g. if it has to copy memory (such as std::vector) or has to do atomic operations (such as increasing a refcount))

bool CheckTileSolidColision(std::vector<sf::Vector2f> pCornerPoints); // Change the argument to const std::vector<sf::Vector2f>& pCornerPoints 

Work directly on an object instead of a temporary

in LevelEntityManager::GetPlayerNewPosition you use two temporary variables NewPlayerX and NewPlayerY just to copy them over into NewPlayerPos. You can the following code and work on NewPlayerPos directly:

 sf::Vector2f NewPlayerPos(pPlayerPos);
 if (pKeyState.UpPressed)
    NewPlayerPos.y -= pPlayer.GetSpeed() * mFrameTime;
 ...

The same happens in your main.cpp where you create a temporary vector boolRow and then push them on SolidStateVec. Why not add them directly to SolidStateVec? You know the size beforehand, so you can call resize on SolidStateVec and acces the elements using the [] operator. (This way, the vector has to reallocate only once)

Use namespaces

You have some places with generic names, such as ToDegrees(), which might collide later on when you include some math library. Use namespaces from the beginning to prevent issues later on.

Prefer the compiler over the preprocessor

If you can, prefer a const variable over a preprocessor define.

#define _PI 3.14159265
const float _PI = 3.14159265.f;

The compiler can complain if you double declare, is strongly typed, ...

Cache sizes of containers while iterating

Depending on the container, calling GetSize() and operator[] can be expensive. Cache the size up front or use a range based for.

const size_t cornerPointsSize = pCornerPoints.size();
for (int i = 0; i < cornerPointsSize; ++i)
{
    const sf::Vector2f& currentPoint = CornerPoints[i];
    if (mTileEngine.CheckSolid(currentPoint.x, currentPoint.y))
        return true;
}

for (const sf::Vector2f& currentPoint : cornerPointsSize)
{
    if (mTileEngine.CheckSolid(currentPoint.x, currentPoint.y))
        return true;
}

Do nullptr checks

In some functions (e.g. Render(sf::RenderWindow* pTarget)) you pass a pointer while you're never checking if that pointer is actually valid. If the pointer cannot be invalid, pass a reference instead. If the pointer can be invalid, get some logging (use some form of asserts).

Watch out for integer divisions

return degrees * (_PI / 180);

180 is an integer, not a float. So you'll get a whole devision instead of a floating point devision if _PI is an integer as well. This is not the case, but it is dangerous so I recommend you change it to _PI / 180.0f

Clean up a little

Remove empty functions, don't pollute your main function. So either remove GenerateTestLevel() or transfer some of the logic that's currently in main.

Polling versus events

In your Update function you're polling the state. This means that every single frame, you're requesting state. In this case, it's about a few keys but if you have support for multiple players and controllers, this method is going to get computationally expensive. Responding on events is a little more complex, but worth considering for larger projects. Have a look at events in sfml.

Use parenthesis

They're free, so why not use a few extra. Take a look at this piece of code:

RelY > mMapSizeY * mTileHeight

Operator > has lower precedence than operator *, so in this case it is correct, but it may become wrong if you use another operator (such as bitwise operators) and it is confusing to read. So just add them!

RelY > (mMapSizeY * mTileHeight)
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.