@@ -1,19 +1,17 @@
#include <vector>
#include <memory>
#include <algorithm>
#include <SFML/Graphics.hpp>

#include "Game.h"
#include "Errors.h"
#include "Entitys.h"
#include "StopWatch.h"

#include <iostream>

si::model::Game::Game () {
// TODO MOVE TO XML
this->addEntity(
std::make_shared<Entity>(
si::Ship("./assets/images/enemy.png")
)
);

};

si::model::Game::Game (int x, int y) {
@@ -25,60 +23,123 @@ si::model::Game::Game (int x, int y) {

void si::model::Game::update () {
int frames = this->stopWatch.getFrames();
int framesCopy = frames;

// TODO WHAT IF UPDATE TAKES LONGER THAN DELTATIME
int calculatedFrames = frames;

// This is safe because getFrames returns a number above or equal to 0
while (frames--) {
// Add the entitys
for (auto& entityPtr : this->addObjects) {
// using vector for better readability
auto vec = this->addObjects;
if (std::find(vec.begin(), vec.end(), entityPtr) != vec.end()) {
this->objectPointers.push_back(entityPtr);
}
}
// ===== Add the entitys =====
this->_handleAddObjects();

this->addObjects.clear();
// ===== Handle collisions =====
this->_handleCollisions();

// ===== Update all entitys =====
for (auto& entity : this->objectPointers) {
// ===== Handle collisions =====


// ===== Update all entitys =====
entity->update(this->stopWatch.getDeltaTime(), *this);
}

// Remove objects
for (auto& entityPtr : this->deleteObjects) {
// using vector for better readability
auto vec = this->addObjects;
this->objectPointers.erase(
std::remove(vec.begin(), vec.end(), entityPtr), vec.end()
);
// ===== Remove objects =====
this->_handleRemoveObjects();
}

// If in the time we calculated the frames we now have to do more frames
// than we calculated then we are starting to lagg behind, it this gets
// too bad we will have to adjust the deltaTime
if (this->stopWatch.getFramesWithoutReset() > calculatedFrames) {
this->_laggTimes++;
} else {
this->_laggTimes--;
if (this->_laggTimes < 0) this->_laggTimes = 0;
}

// If we lagg behind 5 times, we will raise the deltaTime
if (this->_laggTimes > 5) {
this->stopWatch.setDeltaTime(this->stopWatch.getDeltaTime() * 2);
}
}

void si::model::Game::_handleAddObjects () {
for (auto entityPtr : this->addObjects) {
// using vector for better readability
auto& vec = this->objectPointers;

if (std::find(vec.begin(), vec.end(), entityPtr) == vec.end()) {
vec.push_back(entityPtr);
}
}

this->addObjects.clear();
}

this->deleteObjects.clear();
void si::model::Game::_handleRemoveObjects () {
for (auto& entityPtr : this->deleteObjects) {
// Delete it from the list

auto& vec = this->objectPointers;
vec.erase(
std::remove(vec.begin(),
vec.end(),
this->getEntity(entityPtr)),
vec.end()
);
}

this->deleteObjects.clear();
}

void si::model::Game::_handleCollisions () {
// If in the future the performance is a problem it is possible to
// first contruct a spatial hashmap to reduce the amount of objects
// we need to compare
for (auto& entity1 : this->objectPointers) {
sf::FloatRect bounds1 = entity1->sprite.getGlobalBounds();

for (auto& entity2 : this->objectPointers) {
// Don't compare with ourselves
if (entity1 == entity2) break;

sf::FloatRect bounds2 = entity2->sprite.getGlobalBounds();
if (bounds1.intersects(bounds2)) {
entity1->executeCollisionUsingMeOn(*entity2, *this);
}
}
}
}

void si::model::Game::addEntity (std::shared_ptr< Entity > entityPtr) {
this->addObjects.push_back(entityPtr);
}

void si::model::Game::removeEntity (std::shared_ptr< Entity > entityPtr) {
void si::model::Game::removeEntity (Entity* entityPtr) {
this->deleteObjects.push_back(entityPtr);
}

std::vector< std::shared_ptr< si::Entity >> si::model::Game::getObjects () {
return this->objectPointers;
}

std::shared_ptr<si::Entity> si::model::Game::getEntity (si::Entity* entityPtr) {
// Find the entity we want to remove
for (auto& sharedEntityPtr : this->objectPointers) {

// If they are the same
if (sharedEntityPtr.get() == entityPtr) {

return sharedEntityPtr;
}
}

return std::shared_ptr<si::Entity>();
}

void si::model::Game::notifyObservers () {

};

void si::model::Game::clear () {

};

std::vector<int> si::model::Game::getWorldSize () {
return {
this->worldSize.at(0),
@@ -8,12 +8,7 @@ si::model::StopWatch::StopWatch () {
}

si::model::StopWatch::StopWatch (int delta) {
if (delta <= 0) {
throw new ArgumentShouldBeStrictPositive();
}

this->_delta = delta;
this->_lastTime = std::chrono::system_clock::now();
this->setDeltaTime(delta);
}

void si::model::StopWatch::Reset () {
@@ -30,6 +25,24 @@ int si::model::StopWatch::getFrames () {
return frames;
}

int si::model::StopWatch::getFramesWithoutReset () {
clocktime currentTime = std::chrono::system_clock::now();
milliDuration elapsed = currentTime - this->_lastTime;
double elapsedMillis = elapsed.count();

int frames = elapsedMillis / this->_delta;
return frames;
}

int si::model::StopWatch::getDeltaTime () {
return this->_delta;
}

void si::model::StopWatch::setDeltaTime (int delta) {
if (delta <= 0) {
throw new ArgumentShouldBeStrictPositive();
}

this->_delta = delta;
this->_lastTime = std::chrono::system_clock::now();
}
@@ -3,6 +3,8 @@
#include "Errors.h"
#include "Game.h"

#include <iostream>

si::view::Screen::Screen () {
// Trying to make a screen without a game is not defined
throw NotEnoughArgumentsError();
@@ -3,27 +3,55 @@

#include <SFML/Graphics.hpp>
#include <string>
#include <memory>

#include <iostream>

namespace si {
namespace model {
// Forward declaration of Game to break cyclic dependency
class Game;
}

class Entity {
class Bullet;
class Ship;
class EnemyShip;
class Wall;

class EntityBase {
public:
// This function will be called by the game, let the other know you
// collided with it, this is called double dispatch because you know
// what type you are but the game itself does not keep track
// This one has to be implemented on every entitytype
void virtual executeCollisionUsingMeOn(EntityBase& other, si::model::Game& game) = 0;

// This is the collision function that will be called
// The parameter is an entity with the most specific type implemented
// When two objects collide both their collision function will be
// called, This base class should implement every collision function
void virtual collision(EntityBase& with, si::model::Game& game) {};
void virtual collision (Bullet& with, si::model::Game& game) {};
void virtual collision (Ship& with, si::model::Game& game) {};
void virtual collision (EnemyShip& with, si::model::Game& game) {};
void virtual collision (Wall& with, si::model::Game& game) {};
};

class Entity : public EntityBase {
private:
std::vector<float> position = {0, 0}; // Pixels
std::vector<float> speed = {0, 0}; // Pixels per ms
int rotation = 0; // Degrees

sf::Texture texture;
std::string textureFileName;
sf::Sprite sprite;
float rotation = 0; // Degrees

public:
friend class Bullet;
friend class Ship;
friend class Wall;
friend class EnemyShip;

sf::Texture texture;
std::string textureFileName;
sf::Sprite sprite;

Entity();
Entity(const Entity& from);
@@ -38,19 +66,39 @@ namespace si {
// Update the entity for 1 frame
void virtual update(int deltaTime, si::model::Game& game); // Deltatime = ms

// Notify the entity that it collided with another one, this function
// is called within one frame before update
void virtual collision(Entity& with);
// Has to be implemented on all entity types
void virtual executeCollisionUsingMeOn(EntityBase& other, si::model::Game& game);

// This function is called within one frame before update
// You should not keep the given reference without continiously
// checking if it still exists
void virtual collision(EntityBase& with, si::model::Game& game) {};

// Set the position and speed in pixels and pixels per ms respectively
// Set the position, speed and rotation in pixels,
// pixels per ms and degrees respectively
void virtual setPosition(float x, float y);
void virtual setSpeed(float x, float y);
void virtual setRotation(float rotation);
};

class Bullet : Entity {
class Bullet : public Entity {
private:
// You should never use this to access the owner unless you are sure
// that it is currently still in memory. It is possible that this does
// no longer exist
Entity* _owner;

public:
Bullet();
Bullet(std::string textureFileName);

// Has to be implemented on all entity types
void virtual executeCollisionUsingMeOn(EntityBase& other, si::model::Game& game);

// Set the owner or check if an object is the owner
// There is no get owner cause we cannot guarentee that it still exist
void setOwner(Entity* owner);
bool isOwner(Entity* owner);
};

class Ship : public Entity {
@@ -59,15 +107,15 @@ namespace si {
std::vector<float> thrust = {0, 0};

// Pixels per ms per ms that the thrusters can give in that direction
std::vector<float> thrustPower = {0.05, 0.05};
std::vector<float> thrustPower = {0.2, 0.2};

// FireSpeed = ms per bullet, if this is smaller than the deltaTime
// used in the updatefunction only one bullet will be fired per tick
int fireSpeed = 1000;
int msSinceLastBullet = 0;
bool fireing = false;
int _fireSpeed = 1000;
int _msSinceLastBullet = 0;

public:
friend class EnemyShip;
Ship();
Ship(std::string textureFileName);

@@ -79,12 +127,36 @@ namespace si {
void setThrustY(float y);

void virtual update(int deltaTime, si::model::Game& game); // Deltatime = ms

// Has to be implemented on all entity types
void virtual executeCollisionUsingMeOn(EntityBase& other, si::model::Game& game);

// Collision with bullets
void virtual collision (Bullet& with, si::model::Game& game);

bool fireing = false;
};

class EnemyShip : public Ship {
public:
EnemyShip();
EnemyShip(std::string textureFileName);

// Has to be implemented on all entity types
void virtual executeCollisionUsingMeOn(EntityBase& other, si::model::Game& game);

// Collision with bullets
void virtual collision (Bullet& with, si::model::Game& game);
void virtual update(int deltaTime, si::model::Game& game); // Deltatime = ms
};

class Wall : public Entity {
public:
Wall();
Wall(std::string textureFileName);

// Has to be implemented on all entity types
void virtual executeCollisionUsingMeOn(EntityBase& other, si::model::Game& game);
};

}
Empty file.
@@ -10,10 +10,10 @@ namespace si {

class PlayerController : public Controller {
public:
std::shared_ptr<si::Ship> target;
std::shared_ptr< si::Ship > target;

PlayerController();
PlayerController(std::shared_ptr<Ship> target);
PlayerController(std::shared_ptr< si::Ship > target);

virtual void update();
};
@@ -22,7 +22,18 @@ namespace si {

// Objects to be deleted at the end of the current frame or if not
// currently inside a frame, at the end of the next frame
std::vector< std::shared_ptr< Entity > > deleteObjects;
std::vector< Entity* > deleteObjects;

// Used during updates
void _handleAddObjects ();
void _handleRemoveObjects();
void _handleCollisions();

// This is the amount of times we have used more times to calculate
// the frames than the frames were long, this means we are going
// to lagg behind more and more every frame, if this gets too big
// we have to consider increasing the deltaTime
int _laggTimes = 0;

public:

@@ -32,6 +43,9 @@ namespace si {
// Update the world
void update();

// Clear the world
void clear();

std::vector<int> getWorldSize();

// Add the given entity to the game at the beginning
@@ -40,11 +54,17 @@ namespace si {

// Remove the given entity from the game at the end
// of the current or next update frame
void removeEntity (std::shared_ptr< Entity > entityPtr);
void removeEntity (Entity* entityPtr);

// Returns a copy of the entitys that are within this
// frame in the vector
std::vector< std::shared_ptr< Entity >> getObjects();
std::vector< std::shared_ptr< Entity > > getObjects();

// Get the shared entity for a given normal entity pointer.
// This can be used if you want to have a pointer that will get
// memory managed for you. Entity will stay valid even if removed
// from the game
std::shared_ptr< Entity > getEntity (Entity* entityPtr);

// Notify the observers that something about the subject changed
virtual void notifyObservers();
@@ -40,10 +40,20 @@ namespace si {
*/
int getFrames();

/*
Same as above but will not reset the last time
*/
int getFramesWithoutReset();

/*
Get the amount of milliseconds we are using as delta
*/
int getDeltaTime();

/*
Set the delta time
*/
void setDeltaTime(int delta);
};
}
}