added my simple simulation implementation with trivial graphics
This commit is contained in:
parent
ea0aa01b2d
commit
183a26412a
28 changed files with 950 additions and 36 deletions
22
game/state/commands.cpp
Normal file
22
game/state/commands.cpp
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#include "commands.hpp"
|
||||
|
||||
namespace game {
|
||||
void ShootCommand::apply(Player *player, State *state) const
|
||||
{
|
||||
(void) state;
|
||||
// TODO spawn missile if alive and enough energy
|
||||
//std::cout<<"apply command " << name() << std::endl;
|
||||
|
||||
player->energy -= m_speed;
|
||||
player->missiles.push_back(new Missile(player->id, player->ship->position, m_angle, m_speed));
|
||||
}
|
||||
|
||||
bool ShootCommand::allowed(const Player *player, const State *state) const
|
||||
{
|
||||
(void) state;
|
||||
|
||||
// TODO
|
||||
return player->alive && player->energy >= m_speed;
|
||||
//return player->alive;
|
||||
}
|
||||
}
|
||||
56
game/state/commands.hpp
Normal file
56
game/state/commands.hpp
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#pragma once
|
||||
|
||||
#include "state.hpp"
|
||||
#include "player.hpp"
|
||||
#include "ship.hpp"
|
||||
#include "planet.hpp"
|
||||
#include "missile.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace game {
|
||||
class Command {
|
||||
public:
|
||||
Command()
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~Command()
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool allowed(const Player *player, const State *state) const
|
||||
{
|
||||
(void) player;
|
||||
(void) state;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void apply(Player *player, State *state) const
|
||||
{
|
||||
(void) state;
|
||||
(void) player;
|
||||
|
||||
std::cout<<"Command '" << name() << "' not yet implemented!" << std::endl;
|
||||
}
|
||||
|
||||
virtual std::string name() const { return "<unnamed>"; }
|
||||
};
|
||||
|
||||
class ShootCommand : public Command {
|
||||
public:
|
||||
ShootCommand(float angle, float speed) : m_angle(angle), m_speed(speed)
|
||||
{
|
||||
}
|
||||
|
||||
std::string name() const { return "<shoot>"; }
|
||||
|
||||
void apply( Player *player, State *state) const;
|
||||
bool allowed(const Player *player, const State *state) const;
|
||||
|
||||
private:
|
||||
float m_angle;
|
||||
float m_speed;
|
||||
};
|
||||
}
|
||||
0
game/state/explosion.cpp
Normal file
0
game/state/explosion.cpp
Normal file
17
game/state/explosion.hpp
Normal file
17
game/state/explosion.hpp
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#if 0
|
||||
class Explosion {
|
||||
public:
|
||||
enum class Kind {
|
||||
Normal,
|
||||
MissileWithShip
|
||||
};
|
||||
|
||||
glm::vec2 position;
|
||||
|
||||
Explosion(Kind kind);
|
||||
|
||||
public:
|
||||
};
|
||||
#endif
|
||||
82
game/state/missile.cpp
Normal file
82
game/state/missile.cpp
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
#include "missile.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "state.hpp"
|
||||
#include "player.hpp"
|
||||
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
||||
namespace game {
|
||||
Missile::Missile(int playerId, const glm::vec2 &pos, float angle, float speed)
|
||||
: playerId(playerId)
|
||||
, position(pos)
|
||||
{
|
||||
velocity = speed * glm::vec2(std::sin(angle), std::cos(angle));
|
||||
|
||||
//std::cout << "spawned missile for player " << playerId << " @ ("
|
||||
// << pos.x << ", " << pos.y << ") speed = " << speed << std::endl;
|
||||
}
|
||||
|
||||
Missile::~Missile()
|
||||
{
|
||||
//std::cout<<"destroyed missile" << std::endl;
|
||||
}
|
||||
|
||||
Missile::Event Missile::advance(const game::State *state, float dt)
|
||||
{
|
||||
glm::vec2 gravityForce = glm::vec2(0.0, 0.0);
|
||||
|
||||
for (const Player *other : state->players) {
|
||||
if (other->ship != nullptr && other->id != playerId) {
|
||||
glm::vec2 diff = other->ship->position - position;
|
||||
float dist = glm::length(diff);
|
||||
if (dist <= other->ship->radius) {
|
||||
// TODO: collect all hits and return the first one only
|
||||
// TODO: find exact hit position!
|
||||
return Missile::Event(position, playerId, other->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// get forces
|
||||
for (const Planet *planet : state->planets) {
|
||||
glm::vec2 diff = planet->position - position;
|
||||
float dist = glm::length(diff);
|
||||
if (dist <= planet->radius) {
|
||||
// TODO: collect all hits and return the first one only
|
||||
// TODO: find exact hit position!
|
||||
return Missile::Event(position, Missile::HitObject::HitPlanet);
|
||||
}
|
||||
|
||||
dist *= 20.0;
|
||||
if (dist > 0.001) {
|
||||
float G = 0.1;
|
||||
gravityForce += G * diff / (1.0f + dist*dist);
|
||||
}
|
||||
}
|
||||
|
||||
// simple (and bad) euler integration.
|
||||
position += 0.5f * gravityForce*dt*dt + velocity*dt;
|
||||
velocity += gravityForce * dt;
|
||||
|
||||
//const float thr = 0.01;
|
||||
//float s = glm::length(velocity);
|
||||
//if (s > thr) {
|
||||
// velocity = glm::normalize(velocity) * thr;
|
||||
//}
|
||||
|
||||
glm::vec2 nextPos = position + velocity;
|
||||
|
||||
// TODO: collision checks
|
||||
position = nextPos;
|
||||
|
||||
// check if distance to center of the universe is getting too big
|
||||
float distToCenter = glm::length(position);
|
||||
if (distToCenter > state->maxMissileDistance()) {
|
||||
return Missile::Event(position, Missile::HitObject::LeftUniverse);
|
||||
}
|
||||
|
||||
return Missile::Event(position);
|
||||
}
|
||||
}
|
||||
68
game/state/missile.hpp
Normal file
68
game/state/missile.hpp
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
#pragma once
|
||||
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
#include "state.hpp"
|
||||
|
||||
#include "ship.hpp"
|
||||
#include "planet.hpp"
|
||||
|
||||
namespace game {
|
||||
class State;
|
||||
|
||||
// missile belongs to a player and optionally fills a trace behind it.
|
||||
// trace then belongs to the player.
|
||||
class Missile {
|
||||
public:
|
||||
enum class HitObject {
|
||||
Nothing,
|
||||
HitPlayer,
|
||||
HitPlanet,
|
||||
// HitMissile,
|
||||
LeftUniverse
|
||||
};
|
||||
|
||||
// missile advances to pos. if hit != Nothing, it hits something and
|
||||
// stops existing afterwards.
|
||||
class Event {
|
||||
public:
|
||||
Event(const glm::vec2 &pos) : Event(pos, HitObject::Nothing)
|
||||
{
|
||||
}
|
||||
|
||||
Event(const glm::vec2 &pos, HitObject hit)
|
||||
: hit(hit), pos(pos)
|
||||
{
|
||||
}
|
||||
|
||||
Event(const glm::vec2 &pos, int playerIdKiller, int playerIdVictim)
|
||||
: Event(pos, HitObject::HitPlayer)
|
||||
{
|
||||
this->playerIdKiller = playerIdKiller;
|
||||
this->playerIdVictim = playerIdVictim;
|
||||
}
|
||||
|
||||
HitObject hit;
|
||||
glm::vec2 pos;
|
||||
|
||||
int playerIdKiller;
|
||||
int playerIdVictim;
|
||||
};
|
||||
|
||||
// XXX
|
||||
int playerId; // owner won't be hit by own missiles
|
||||
glm::vec2 position;
|
||||
glm::vec2 velocity;
|
||||
//Trace *trace;
|
||||
|
||||
Missile(int playerId, const glm::vec2 &pos, float angle, float speed);
|
||||
|
||||
~Missile();
|
||||
|
||||
// try to advance. if something will be hit, return the first hit in
|
||||
// time.
|
||||
Missile::Event advance(const game::State *state, float dt);
|
||||
};
|
||||
}
|
||||
|
||||
0
game/state/object.cpp
Normal file
0
game/state/object.cpp
Normal file
15
game/state/object.hpp
Normal file
15
game/state/object.hpp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#pragma once
|
||||
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
class Object {
|
||||
public:
|
||||
Object(const glm::vec2 &pos, float r) : position(pos), radius(r)
|
||||
{
|
||||
}
|
||||
|
||||
const glm::vec2 position;
|
||||
const float radius;
|
||||
};
|
||||
|
||||
0
game/state/planet.cpp
Normal file
0
game/state/planet.cpp
Normal file
12
game/state/planet.hpp
Normal file
12
game/state/planet.hpp
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "object.hpp"
|
||||
|
||||
namespace game {
|
||||
class Planet : public Object {
|
||||
public:
|
||||
Planet(const glm::vec2 &pos, float r) : Object(pos, r)
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
33
game/state/player.cpp
Normal file
33
game/state/player.cpp
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#include "player.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "commands.hpp"
|
||||
|
||||
#include "ship.hpp"
|
||||
|
||||
namespace game {
|
||||
void Player::popCommand()
|
||||
{
|
||||
//std::cout<<"popCommand()" << std::endl;
|
||||
|
||||
Command *cmd = commands.front();
|
||||
commands.erase(commands.begin());
|
||||
delete(cmd);
|
||||
}
|
||||
|
||||
Command *Player::peekCommand()
|
||||
{
|
||||
return commands.front();
|
||||
}
|
||||
|
||||
bool Player::hasCommandInQueue()
|
||||
{
|
||||
return !commands.empty();
|
||||
}
|
||||
|
||||
void Player::addCommand(Command *cmd)
|
||||
{
|
||||
commands.push_back(cmd);
|
||||
}
|
||||
}
|
||||
36
game/state/player.hpp
Normal file
36
game/state/player.hpp
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
#pragma once
|
||||
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
namespace game {
|
||||
class Ship;
|
||||
class Command;
|
||||
class Missile;
|
||||
|
||||
class Player {
|
||||
public:
|
||||
int id;
|
||||
bool alive;
|
||||
float energy;
|
||||
float deadTimeCounter;
|
||||
Ship *ship;
|
||||
std::vector<Missile*> missiles;
|
||||
|
||||
Player(int id) : id(id), alive(true), energy(0.0), ship(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
void addCommand(Command *cmd);
|
||||
bool hasCommandInQueue();
|
||||
|
||||
Command *peekCommand();
|
||||
void popCommand();
|
||||
|
||||
private:
|
||||
std::deque<Command*> commands;
|
||||
};
|
||||
}
|
||||
0
game/state/ship.cpp
Normal file
0
game/state/ship.cpp
Normal file
12
game/state/ship.hpp
Normal file
12
game/state/ship.hpp
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
|
||||
#include "object.hpp"
|
||||
|
||||
namespace game {
|
||||
class Ship : public Object {
|
||||
public:
|
||||
Ship(const glm::vec2 &pos, float r) : Object(pos, r)
|
||||
{
|
||||
}
|
||||
};
|
||||
}
|
||||
262
game/state/state.cpp
Normal file
262
game/state/state.cpp
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
#include "state.hpp"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <glm/vec2.hpp>
|
||||
#include <glm/vec3.hpp>
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
||||
#include "object.hpp"
|
||||
#include "missile.hpp"
|
||||
#include "player.hpp"
|
||||
#include "planet.hpp"
|
||||
#include "ship.hpp"
|
||||
#include "commands.hpp"
|
||||
|
||||
#include "util.hpp"
|
||||
|
||||
namespace game {
|
||||
void State::init()
|
||||
{
|
||||
m_shipRadius = 0.05;
|
||||
m_maxMissileDistance = 2.0;
|
||||
m_playerRespawnTime = 2.0;
|
||||
m_defaultEnergy = 10.0;
|
||||
|
||||
bool planetsOnCircle = false;
|
||||
|
||||
int numPlanets = 10;
|
||||
for (int i=0; i<numPlanets; i++) {
|
||||
float t = i / static_cast<float>(numPlanets);
|
||||
t *= 2.0*M_PI;
|
||||
|
||||
// distribute but not in the center
|
||||
int tries = 0;
|
||||
glm::vec2 pos;
|
||||
do {
|
||||
float cr = 0.7;
|
||||
if (planetsOnCircle) {
|
||||
pos = cr * glm::vec2(std::sin(t), std::cos(t));
|
||||
} else {
|
||||
pos = cr * util::randv2_m1_1();
|
||||
}
|
||||
} while(glm::length(pos) < 0.2 && tries++ < 1000);
|
||||
|
||||
planets.push_back(new Planet(pos, 0.03 + 0.07*util::randf_0_1()));
|
||||
}
|
||||
}
|
||||
|
||||
bool State::spawnShipForPlayer(Player *player)
|
||||
{
|
||||
glm::vec2 spawnPos;
|
||||
if (!findFreePositionWithRadius(5.0 * m_shipRadius, spawnPos)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
player->ship = new Ship(spawnPos, m_shipRadius);
|
||||
ships.push_back(player->ship);
|
||||
|
||||
player->energy = m_defaultEnergy;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void State::addPlayer(int playerId)
|
||||
{
|
||||
Player *player = new Player(playerId);
|
||||
players.push_back(player);
|
||||
}
|
||||
|
||||
void State::playerLeft(int playerId)
|
||||
{
|
||||
(void) playerId;
|
||||
}
|
||||
|
||||
void State::advancePlayerShipSpawns(float dt)
|
||||
{
|
||||
(void) dt;
|
||||
|
||||
for (Player *player : players) {
|
||||
if (!player->alive) {
|
||||
player->deadTimeCounter += dt;
|
||||
if (player->deadTimeCounter >= m_playerRespawnTime) {
|
||||
player->deadTimeCounter = 0;
|
||||
player->alive = true;
|
||||
std::cout<<"respawning player " << player->id << " now!" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (player->alive && player->ship == NULL) {
|
||||
if (!spawnShipForPlayer(player)) {
|
||||
std::cout<<"could not spawn ship for player!" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void State::advancePlayerCommands(float dt)
|
||||
{
|
||||
(void) dt;
|
||||
for (Player *player : players) {
|
||||
if (player->alive) {
|
||||
player->energy += dt;
|
||||
}
|
||||
|
||||
// try to execute as much queued commands as possible.
|
||||
while (player->hasCommandInQueue()) {
|
||||
Command *command = player->peekCommand();
|
||||
if (!command->allowed(player, this)) {
|
||||
break;
|
||||
}
|
||||
|
||||
command->apply(player, this);
|
||||
player->popCommand();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void State::playerKillsPlayer(Player *killer, Player *victim)
|
||||
{
|
||||
if (killer == nullptr || victim == nullptr) {
|
||||
std::cerr <<"error: killer / victim is NULL!" << std::endl;
|
||||
exit(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
std::cout<<"player " << killer->id << " killed " << victim->id << std::endl;
|
||||
|
||||
// destroy ship
|
||||
size_t i;
|
||||
for (i=0; i<ships.size(); i++) {
|
||||
if (ships[i] == victim->ship) {
|
||||
ships.erase(ships.begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
delete(victim->ship);
|
||||
victim->ship = nullptr;
|
||||
|
||||
victim->alive = false;
|
||||
victim->deadTimeCounter = 0.0;
|
||||
|
||||
// TODO
|
||||
// add points
|
||||
//
|
||||
// TODO
|
||||
// message
|
||||
//
|
||||
// TODO
|
||||
// respawn timer
|
||||
}
|
||||
|
||||
void State::advancePlayerMissiles(float dt)
|
||||
{
|
||||
// advance missiles
|
||||
for (Player *player : players) {
|
||||
size_t i = 0;
|
||||
while (i<player->missiles.size()) {
|
||||
Missile *missile = player->missiles[i];
|
||||
|
||||
//std::cout<<"missile: " << (long unsigned int) missile << std::endl;
|
||||
const Missile::Event evt = missile->advance(this, dt);
|
||||
|
||||
if (evt.hit == Missile::HitObject::Nothing) {
|
||||
i++;
|
||||
|
||||
} else {
|
||||
switch(evt.hit) {
|
||||
case Missile::HitObject::HitPlanet:
|
||||
//std::cout<<"hit Planet" << std::endl;
|
||||
break;
|
||||
|
||||
case Missile::HitObject::HitPlayer:
|
||||
//std::cout<<"hit Player" << std::endl;
|
||||
playerKillsPlayer(playerForId(evt.playerIdKiller), playerForId(evt.playerIdVictim));
|
||||
break;
|
||||
|
||||
case Missile::HitObject::LeftUniverse:
|
||||
//std::cout<<"missile left the universe." << std::endl;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
player->missiles.erase(player->missiles.begin() + i);
|
||||
delete(missile);
|
||||
//std::cout<<std::endl;
|
||||
}
|
||||
|
||||
// TODO
|
||||
// add trace to the missile
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void State::advance(float dt)
|
||||
{
|
||||
//std::cout<<"advance ship spawns" << std::endl;
|
||||
advancePlayerShipSpawns(dt);
|
||||
|
||||
//std::cout<<"advance commands" << std::endl;
|
||||
advancePlayerCommands(dt);
|
||||
|
||||
//std::cout<<"advance missiles" << std::endl;
|
||||
advancePlayerMissiles(dt);
|
||||
}
|
||||
|
||||
Player *State::playerForId(int playerId)
|
||||
{
|
||||
for (Player *p : players) {
|
||||
if (p->id == playerId) {
|
||||
return p;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool State::findFreePositionWithRadius(float radius, glm::vec2 &pos)
|
||||
{
|
||||
bool first = ships.size() == 0;
|
||||
|
||||
int tries = 0;
|
||||
while(true) {
|
||||
bool noCollision = true;
|
||||
|
||||
//pos = glm::linearRand2(-1.0, 1.0);
|
||||
if (first) {
|
||||
first = false;
|
||||
pos = glm::vec2(0.0, 0.0);
|
||||
} else {
|
||||
pos = glm::vec2(util::randf_0_1(), util::randf_m1_1());
|
||||
//glm::linearRand(-1.0, 1.0), glm::linearRand(-1.0, 1.0));
|
||||
}
|
||||
|
||||
for (Planet *p : planets) {
|
||||
if (glm::distance(p->position, pos) <= radius) {
|
||||
noCollision = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (noCollision) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (tries++ >= 1000) {
|
||||
pos = glm::vec2(0.0, 0.0);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void State::commandForPlayer(int playerId, game::Command *cmd)
|
||||
{
|
||||
Player *player = playerForId(playerId);
|
||||
if (player != nullptr) {
|
||||
player->addCommand(cmd);
|
||||
}
|
||||
}
|
||||
}
|
||||
64
game/state/state.hpp
Normal file
64
game/state/state.hpp
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <glm/vec2.hpp>
|
||||
|
||||
namespace game {
|
||||
class Command;
|
||||
class Missile;
|
||||
class Player;
|
||||
class Planet;
|
||||
class Ship;
|
||||
|
||||
// trace of a missile. exists without a missile at player.
|
||||
//class Trace {
|
||||
//public:
|
||||
// std::vector<glm::vec2> points;
|
||||
//};
|
||||
|
||||
class State {
|
||||
public:
|
||||
void init();
|
||||
void advance(float dt);
|
||||
|
||||
// the (network) layer calling these three functions should keep id's
|
||||
// unique and give one (network) input an id.
|
||||
void addPlayer(int playerId);
|
||||
void playerLeft(int playerId);
|
||||
void commandForPlayer(int playerId, Command *cmd);
|
||||
|
||||
// lookup. return nullptr on invalid playerId
|
||||
Player *playerForId(int playerId);
|
||||
|
||||
float maxMissileDistance() const { return m_maxMissileDistance; }
|
||||
float shipRadius() const { return m_shipRadius; }
|
||||
|
||||
std::vector<Planet*> planets;
|
||||
std::vector<Ship*> ships;
|
||||
std::vector<Player*> players;
|
||||
|
||||
private:
|
||||
void playerKillsPlayer(Player *killer, Player *victim);
|
||||
|
||||
void advancePlayerShipSpawns(float dt);
|
||||
void advancePlayerCommands(float dt);
|
||||
void advancePlayerMissiles(float dt);
|
||||
|
||||
// try to spawn a ship for this player.
|
||||
// return true on success, false on failure to find a spot.
|
||||
bool spawnShipForPlayer(Player *player);
|
||||
|
||||
// find some place where nothing is placed nearby (ships/planets).
|
||||
// usefule for spanwing things
|
||||
bool findFreePositionWithRadius(float r, glm::vec2 &pos);
|
||||
|
||||
float m_maxMissileDistance;
|
||||
float m_playerRespawnTime;
|
||||
float m_shipRadius;
|
||||
float m_defaultEnergy;
|
||||
};
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue