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
|
@ -8,10 +8,20 @@ set(GAME_SRC
|
||||||
main.cpp
|
main.cpp
|
||||||
opengl.cpp
|
opengl.cpp
|
||||||
glclasses.cpp
|
glclasses.cpp
|
||||||
config.cpp
|
|
||||||
simulation.cpp
|
|
||||||
renderer.cpp
|
renderer.cpp
|
||||||
)
|
game_window.cpp
|
||||||
|
triangle_window.cpp
|
||||||
|
|
||||||
|
util.cpp
|
||||||
|
game.cpp
|
||||||
|
state/object.cpp
|
||||||
|
state/state.cpp
|
||||||
|
state/player.cpp
|
||||||
|
state/planet.cpp
|
||||||
|
state/missile.cpp
|
||||||
|
state/commands.cpp
|
||||||
|
state/state.cpp
|
||||||
|
)
|
||||||
|
|
||||||
set(GAME_HEADERS
|
set(GAME_HEADERS
|
||||||
opengl.h
|
opengl.h
|
||||||
|
@ -20,7 +30,7 @@ set(GAME_HEADERS
|
||||||
config.h
|
config.h
|
||||||
simulation.h
|
simulation.h
|
||||||
renderer.h
|
renderer.h
|
||||||
)
|
)
|
||||||
|
|
||||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
67
game/game.cpp
Normal file
67
game/game.cpp
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
#include "game.hpp"
|
||||||
|
|
||||||
|
#include "state/commands.hpp"
|
||||||
|
|
||||||
|
#include "util.hpp"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
Game::Game()
|
||||||
|
{
|
||||||
|
// advance simulation with 100 Hz
|
||||||
|
m_time_step = 1.0 / 100.0;
|
||||||
|
m_time_for_next_step = 0.0;
|
||||||
|
|
||||||
|
m_state = new game::State();
|
||||||
|
m_state->init();
|
||||||
|
|
||||||
|
m_state->addPlayer(0);
|
||||||
|
m_state->addPlayer(1);
|
||||||
|
m_state->addPlayer(2);
|
||||||
|
m_state->addPlayer(3);
|
||||||
|
//m_state->addPlayer(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Game::cycle(float dt)
|
||||||
|
{
|
||||||
|
static float acc = 0.0;
|
||||||
|
acc += dt;
|
||||||
|
|
||||||
|
float spawnInterval = 0.1;
|
||||||
|
while(acc > spawnInterval) {
|
||||||
|
acc -= spawnInterval;
|
||||||
|
|
||||||
|
float a = 2.0 * M_PI * util::randf_0_1();
|
||||||
|
float speed = 0.002;
|
||||||
|
m_state->players[0]->addCommand(new game::ShootCommand(a, speed));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
if (dt >= 10.0) {
|
||||||
|
//std::cout<<"time to big: " << dt << std::endl;
|
||||||
|
dt = m_time_step;
|
||||||
|
}
|
||||||
|
|
||||||
|
//std::cout<<"adding dt: " << dt << std::endl;
|
||||||
|
m_time_for_next_step += dt;
|
||||||
|
|
||||||
|
int steps = 0;
|
||||||
|
while(m_time_for_next_step >= m_time_step) {
|
||||||
|
//std::cout<<"time now: " << m_time_for_next_step << std::endl;
|
||||||
|
m_time_for_next_step -= m_time_step;
|
||||||
|
|
||||||
|
m_state->advance(m_time_step);
|
||||||
|
steps++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//std::cout << m_time_for_next_step << " s remaining time, " << steps << " steps taken." << std::endl;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
(void) dt;
|
||||||
|
|
||||||
|
m_state->advance(dt);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
21
game/game.hpp
Normal file
21
game/game.hpp
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "state/state.hpp"
|
||||||
|
|
||||||
|
class Game {
|
||||||
|
public:
|
||||||
|
Game();
|
||||||
|
|
||||||
|
// main method of the game. run this regulary
|
||||||
|
// return false if want to exit.
|
||||||
|
bool cycle(float dt);
|
||||||
|
|
||||||
|
// for rendering
|
||||||
|
const game::State *state() const { return m_state; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
game::State *m_state;
|
||||||
|
|
||||||
|
float m_time_for_next_step;
|
||||||
|
float m_time_step;
|
||||||
|
};
|
0
game/game_window.cpp
Normal file
0
game/game_window.cpp
Normal file
89
game/game_window.h
Normal file
89
game/game_window.h
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "opengl.h"
|
||||||
|
#include "renderer.h"
|
||||||
|
|
||||||
|
#include "game.hpp"
|
||||||
|
|
||||||
|
#include "state/object.hpp"
|
||||||
|
#include "state/missile.hpp"
|
||||||
|
#include "state/player.hpp"
|
||||||
|
#include "state/planet.hpp"
|
||||||
|
#include "state/ship.hpp"
|
||||||
|
|
||||||
|
class GameWindow : public endofthejedi::GLWindow {
|
||||||
|
private:
|
||||||
|
protected:
|
||||||
|
void init() override {
|
||||||
|
glClearColor(0.5f, 0.6f, 0.7f, 1.0f);
|
||||||
|
resize();
|
||||||
|
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
void render(double time) override {
|
||||||
|
//static bool once = false;
|
||||||
|
//if (!once) {
|
||||||
|
// once = true;
|
||||||
|
// for (int i=0; i<1000; i++) {
|
||||||
|
// m_game.cycle(time);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
if (!m_game.cycle(static_cast<float>(time/1000000000.0))) {
|
||||||
|
std::cout<<"stopping the game..." << std::endl;
|
||||||
|
stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
for (const game::Planet *planet : m_game.state()->planets) {
|
||||||
|
drawPlanet(planet->position, planet->radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const game::Ship *ship : m_game.state()->ships) {
|
||||||
|
drawShip(ship->position);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const game::Player *player : m_game.state()->players) {
|
||||||
|
for (const game::Missile *missile : player->missiles) {
|
||||||
|
drawMissile(missile->position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void resize() override { glViewport(0, 0, getwidth(), getheight()); }
|
||||||
|
|
||||||
|
void drawShip(const glm::vec2 &pos)
|
||||||
|
{
|
||||||
|
//std::cout<<"draw ship @ " << pos.x << ", " << pos.y << std::endl;
|
||||||
|
glm::vec3 color = glm::vec3(0.2, 1.0, 0.3);
|
||||||
|
|
||||||
|
float radius = m_game.state()->shipRadius();
|
||||||
|
|
||||||
|
m_renderer.drawCircle(pos.x, pos.y, radius, color.x, color.y, color.z, 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawPlanet(const glm::vec2 &pos, float radius)
|
||||||
|
{
|
||||||
|
glm::vec3 color = glm::vec3(0.7, 0.1, 0.2);
|
||||||
|
|
||||||
|
//std::cout<<"draw planet @ " << pos.x << ", " << pos.y << std::endl;
|
||||||
|
m_renderer.drawCircle(pos.x, pos.y, radius, color.x, color.y, color.z, 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
void drawMissile(const glm::vec2 &pos)
|
||||||
|
{
|
||||||
|
glm::vec3 color = glm::vec3(1.0, 1.0, 0.3);
|
||||||
|
m_renderer.drawCircle(pos.x, pos.y, 0.005, color.x, color.y, color.z, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
GameWindow(unsigned int width, unsigned int height)
|
||||||
|
: endofthejedi::GLWindow(width, height) {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Game m_game;
|
||||||
|
endofthejedi::Renderer m_renderer;
|
||||||
|
};
|
||||||
|
|
|
@ -1,36 +1,11 @@
|
||||||
#include "opengl.h"
|
#include "opengl.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
class MainWindow : public endofthejedi::GLWindow {
|
//#include "triangle_window.h"
|
||||||
private:
|
#include "game_window.h"
|
||||||
protected:
|
|
||||||
void init() override {
|
|
||||||
glClearColor(0.5f, 0.6f, 0.7f, 1.0f);
|
|
||||||
resize();
|
|
||||||
|
|
||||||
glEnable(GL_DEPTH_TEST);
|
|
||||||
}
|
|
||||||
void render(double time) override {
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
||||||
|
|
||||||
glBegin(GL_TRIANGLES);
|
|
||||||
glColor3f(1.0f, 0.0f, 0.0f);
|
|
||||||
glVertex3f(0.0f, -1.0f, 0.0f);
|
|
||||||
glColor3f(0.0f, 1.0f, 0.0f);
|
|
||||||
glVertex3f(-1.0f, 1.0f, 0.0f);
|
|
||||||
glColor3f(0.0f, 0.0f, 1.0f);
|
|
||||||
glVertex3f(1.0f, 1.0f, 0.0f);
|
|
||||||
glEnd();
|
|
||||||
}
|
|
||||||
void resize() override { glViewport(0, 0, getwidth(), getheight()); }
|
|
||||||
|
|
||||||
public:
|
|
||||||
MainWindow(unsigned int width, unsigned int height)
|
|
||||||
: endofthejedi::GLWindow(width, height) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
int main(int argc, const char *argv[]) {
|
int main(int argc, const char *argv[]) {
|
||||||
MainWindow window(500, 500);
|
GameWindow window(500, 500);
|
||||||
window.set_maxfps(60.0);
|
window.set_maxfps(60.0);
|
||||||
window.loop();
|
window.loop();
|
||||||
window.stop();
|
window.stop();
|
||||||
|
|
|
@ -7,12 +7,12 @@ endofthejedi::Renderer::Renderer() {
|
||||||
endofthejedi::Renderer::~Renderer() {}
|
endofthejedi::Renderer::~Renderer() {}
|
||||||
|
|
||||||
void endofthejedi::Renderer::drawCircle(float x, float y, float radius, float r,
|
void endofthejedi::Renderer::drawCircle(float x, float y, float radius, float r,
|
||||||
float g, float b) {
|
float g, float b, int numSides) {
|
||||||
glBegin(GL_TRIANGLE_FAN);
|
glBegin(GL_TRIANGLE_FAN);
|
||||||
glVertex2f(x, y); // center of circle
|
glVertex2f(x, y); // center of circle
|
||||||
for (int i = 0; i <= 64; i++) {
|
for (int i = 0; i <= numSides; i++) {
|
||||||
glColor3f(r,g,b);
|
glColor3f(r,g,b);
|
||||||
glVertex2f(x + (radius * cos(i * 2 * M_PI / 64)), y + (radius * sin(i * 2 * M_PI / 64)));
|
glVertex2f(x + (radius * cos(i * 2 * M_PI / numSides)), y + (radius * sin(i * 2 * M_PI / numSides)));
|
||||||
}
|
}
|
||||||
glEnd();
|
glEnd();
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,7 @@ class Renderer {
|
||||||
Renderer();
|
Renderer();
|
||||||
~Renderer();
|
~Renderer();
|
||||||
void drawCircle(float x, float y, float radius, float r,
|
void drawCircle(float x, float y, float radius, float r,
|
||||||
float g, float b);
|
float g, float b, int numSides=12);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
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;
|
||||||
|
};
|
||||||
|
};
|
0
game/triangle_window.cpp
Normal file
0
game/triangle_window.cpp
Normal file
36
game/triangle_window.h
Normal file
36
game/triangle_window.h
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "opengl.h"
|
||||||
|
|
||||||
|
class TriangleWindow : public endofthejedi::GLWindow {
|
||||||
|
private:
|
||||||
|
protected:
|
||||||
|
void init() override {
|
||||||
|
glClearColor(0.5f, 0.6f, 0.7f, 1.0f);
|
||||||
|
resize();
|
||||||
|
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
}
|
||||||
|
void render(double time) override {
|
||||||
|
(void) time;
|
||||||
|
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
glBegin(GL_TRIANGLES);
|
||||||
|
glColor3f(1.0f, 0.0f, 0.0f);
|
||||||
|
glVertex3f(0.0f, -1.0f, 0.0f);
|
||||||
|
glColor3f(0.0f, 1.0f, 0.0f);
|
||||||
|
glVertex3f(-1.0f, 1.0f, 0.0f);
|
||||||
|
glColor3f(0.0f, 0.0f, 1.0f);
|
||||||
|
glVertex3f(1.0f, 1.0f, 0.0f);
|
||||||
|
glEnd();
|
||||||
|
}
|
||||||
|
void resize() override { glViewport(0, 0, getwidth(), getheight()); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
TriangleWindow(unsigned int width, unsigned int height)
|
||||||
|
: endofthejedi::GLWindow(width, height)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
25
game/util.cpp
Normal file
25
game/util.cpp
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
#include "util.hpp"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
namespace util {
|
||||||
|
float randf_m1_1()
|
||||||
|
{
|
||||||
|
return 2.0f*randf_0_1() - 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
float randf_0_1()
|
||||||
|
{
|
||||||
|
return static_cast<float>(static_cast<double>(rand()) / (double) RAND_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec2 randv2_m1_1()
|
||||||
|
{
|
||||||
|
return glm::vec2(randf_m1_1(), randf_m1_1());
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec2 randv2_0_1()
|
||||||
|
{
|
||||||
|
return glm::vec2(randf_0_1(), randf_0_1());
|
||||||
|
}
|
||||||
|
}
|
12
game/util.hpp
Normal file
12
game/util.hpp
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glm/vec2.hpp>
|
||||||
|
|
||||||
|
namespace util {
|
||||||
|
|
||||||
|
float randf_0_1();
|
||||||
|
float randf_m1_1();
|
||||||
|
|
||||||
|
glm::vec2 randv2_m1_1();
|
||||||
|
glm::vec2 randv2_0_1();
|
||||||
|
}
|
Loading…
Reference in a new issue