├── icons └── Azteroids.icns ├── img └── screenshot-1.png ├── .gitmodules ├── src ├── components │ ├── appearance │ │ ├── laser.h │ │ ├── ship.h │ │ ├── appearance.h │ │ ├── asteroid.h │ │ ├── laser.cpp │ │ ├── particle.h │ │ ├── asteroid.cpp │ │ ├── ship.cpp │ │ └── particle.cpp │ ├── mass.h │ ├── geometry.h │ ├── particle.h │ ├── attrition.h │ ├── identity.h │ ├── momentum.h │ └── position.h ├── systems │ ├── collision.h │ ├── particle.h │ ├── render.h │ ├── movement.h │ ├── particle.cpp │ ├── render.cpp │ ├── movement.cpp │ └── collision.cpp ├── managers │ ├── level.h │ └── level.cpp ├── app.h ├── entities │ ├── usership.h │ └── usership.cpp ├── app.cpp └── main.cpp ├── .gitignore ├── README.md └── CMakeLists.txt /icons/Azteroids.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rodrigosetti/azteroids/HEAD/icons/Azteroids.icns -------------------------------------------------------------------------------- /img/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rodrigosetti/azteroids/HEAD/img/screenshot-1.png -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/glfw"] 2 | path = lib/glfw 3 | url = git@github.com:glfw/glfw.git 4 | [submodule "lib/entityx"] 5 | path = lib/entityx 6 | url = git@github.com:alecthomas/entityx.git 7 | -------------------------------------------------------------------------------- /src/components/appearance/laser.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "appearance.h" 4 | 5 | class Laser : public Appearance { 6 | public: 7 | void render(float dt); 8 | }; 9 | 10 | -------------------------------------------------------------------------------- /src/components/mass.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct Mass : entityx::Component { 6 | Mass(float mass = 0.0f) : mass(mass) {} 7 | 8 | float mass; 9 | }; 10 | 11 | -------------------------------------------------------------------------------- /src/components/geometry.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct Geometry : entityx::Component { 6 | Geometry(float radius = 0.0f) : radius(radius) {} 7 | 8 | float radius; 9 | }; 10 | 11 | -------------------------------------------------------------------------------- /src/components/appearance/ship.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "appearance.h" 4 | 5 | struct Ship : Appearance { 6 | void render(float dt); 7 | void throttle(); 8 | 9 | private: 10 | 11 | float throttle_level = 0; 12 | }; 13 | 14 | -------------------------------------------------------------------------------- /src/components/particle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct Particle : entityx::Component { 6 | Particle(float duration = 1.0f) : duration(duration) {} 7 | 8 | float age = 0; 9 | float duration; 10 | }; 11 | 12 | -------------------------------------------------------------------------------- /src/components/appearance/appearance.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct Appearance : entityx::Component { 6 | 7 | virtual void render(float dt) {} 8 | virtual void render(entityx::Entity entity, float dt) { render(dt); } 9 | }; 10 | 11 | -------------------------------------------------------------------------------- /src/components/appearance/asteroid.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "appearance.h" 4 | 5 | class Asteroid : public Appearance { 6 | public: 7 | Asteroid(float size) : size(size) {} 8 | 9 | void render(float dt); 10 | 11 | private: 12 | float size; 13 | }; 14 | 15 | -------------------------------------------------------------------------------- /src/components/attrition.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct Attrition : entityx::Component { 6 | Attrition(float linear = 1.0f, float angular = 1.0f) : 7 | linear(linear), angular(angular) {} 8 | 9 | float linear, angular; 10 | }; 11 | 12 | -------------------------------------------------------------------------------- /src/components/identity.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | enum class EntityIdentity { ASTEROID, SHIP, LASER, PARTICLE }; 6 | 7 | struct Identity : entityx::Component { 8 | Identity(EntityIdentity identity) : identity(identity) {} 9 | 10 | EntityIdentity identity; 11 | }; 12 | 13 | -------------------------------------------------------------------------------- /src/systems/collision.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class CollisionSystem : public entityx::System { 6 | public: 7 | void update(entityx::ptr entities, 8 | entityx::ptr events, 9 | double dt) override; 10 | }; 11 | -------------------------------------------------------------------------------- /src/systems/particle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class ParticleSystem : public entityx::System { 6 | public: 7 | void update(entityx::ptr entities, 8 | entityx::ptr events, 9 | double dt) override; 10 | }; 11 | 12 | -------------------------------------------------------------------------------- /src/components/appearance/laser.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "laser.h" 3 | 4 | void Laser::render(float dt) { 5 | const GLfloat mat_emission[] = { 0.5f, 1.0f, 1.0f, 1.0f }; 6 | 7 | glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission); 8 | 9 | glBegin(GL_LINES); 10 | glVertex3f(0, 0, 0); 11 | glVertex3f(0, 30, 0); 12 | glEnd(); 13 | } 14 | 15 | -------------------------------------------------------------------------------- /src/systems/render.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class RenderSystem : public entityx::System { 6 | public: 7 | void update(entityx::ptr entities, 8 | entityx::ptr events, 9 | double dt) override; 10 | 11 | int width; 12 | int height; 13 | }; 14 | 15 | -------------------------------------------------------------------------------- /src/components/momentum.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct Momentum : entityx::Component { 6 | Momentum(float x = 0.0f, float y = 0.0f, 7 | float angular = 0.0f) : 8 | x(x), y(y), angular(angular) {} 9 | 10 | float normal() { 11 | return sqrt(pow(x, 2) + pow(y, 2)); 12 | } 13 | 14 | float x, y, angular; 15 | }; 16 | 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Compiled Dynamic libraries 8 | *.so 9 | *.dylib 10 | *.dll 11 | 12 | # Compiled Static libraries 13 | *.lai 14 | *.la 15 | *.a 16 | *.lib 17 | 18 | # Executables 19 | *.exe 20 | *.out 21 | *.app 22 | 23 | # CMake related files 24 | CMakeCache.txt 25 | CMakeFiles 26 | Makefile 27 | cmake_install.cmake 28 | install_manifest.txt 29 | 30 | # Output dir 31 | bin/ 32 | 33 | # OS ignores 34 | .DS_Store 35 | -------------------------------------------------------------------------------- /src/managers/level.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | // foward declaration of App 8 | class App; 9 | 10 | class Level : public entityx::Manager { 11 | public: 12 | Level(App *application); 13 | 14 | protected: 15 | void configure(); 16 | 17 | void initialize(); 18 | 19 | void update(double dt); 20 | 21 | private: 22 | App *application; 23 | UserShip user_ship; 24 | }; 25 | 26 | -------------------------------------------------------------------------------- /src/systems/movement.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class MovementSystem : public entityx::System { 6 | public: 7 | MovementSystem(int width, int height) : width(width), height(height) {} 8 | 9 | void update(entityx::ptr entities, 10 | entityx::ptr events, 11 | double dt) override; 12 | 13 | private: 14 | int width, height; 15 | }; 16 | 17 | -------------------------------------------------------------------------------- /src/app.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | struct Keys { 6 | bool up = false; 7 | bool down = false; 8 | bool left = false; 9 | bool right = false; 10 | bool space = false; 11 | }; 12 | 13 | class App { 14 | 15 | public: 16 | 17 | App(int width, int height); 18 | 19 | void initialize(); 20 | 21 | void step(double dt); 22 | 23 | Keys keys_pressed; 24 | 25 | int width, height; 26 | 27 | private: 28 | 29 | Level level; 30 | }; 31 | 32 | -------------------------------------------------------------------------------- /src/components/appearance/particle.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "appearance.h" 4 | 5 | class Spark : public Appearance { 6 | public: 7 | Spark(const float color[]) : color(color) {}; 8 | 9 | void render(entityx::Entity entity, float dt); 10 | 11 | private: 12 | const float *color; 13 | }; 14 | 15 | class Cloud : public Appearance { 16 | public: 17 | Cloud(const float color[], const float grow_speed) : 18 | color(color), grow_speed(grow_speed) {} 19 | 20 | void render(entityx::Entity entity, float dt); 21 | 22 | private: 23 | const float *color; 24 | const float grow_speed; 25 | }; 26 | 27 | -------------------------------------------------------------------------------- /src/systems/particle.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include "particle.h" 6 | 7 | void ParticleSystem::update(entityx::ptr entities, 8 | entityx::ptr events, 9 | double dt) { 10 | for (auto entity : entities->entities_with_components()) { 11 | if (entity.valid()) { 12 | entityx::ptr particle = entity.component(); 13 | 14 | particle->age += dt; 15 | if (particle->age > particle->duration) { 16 | entity.destroy(); 17 | } 18 | } 19 | } 20 | }; 21 | 22 | -------------------------------------------------------------------------------- /src/components/appearance/asteroid.cpp: -------------------------------------------------------------------------------- 1 | #ifdef __APPLE__ 2 | #include 3 | #else 4 | #include 5 | #endif 6 | 7 | #include "asteroid.h" 8 | 9 | void Asteroid::render(float dt) { 10 | const GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 }; 11 | const GLfloat mat_diffuse[] = { .5f, .5f, .5f, 1.0f }; 12 | const GLfloat mat_emission[] = { .1f, .1f, .0f, 1.0f }; 13 | const GLfloat mat_shininess[] = { 50.0 }; 14 | 15 | glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission); 16 | glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); 17 | glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess); 18 | glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mat_diffuse); 19 | 20 | glPushMatrix(); 21 | glScalef(size, size, size); 22 | glutSolidSphere(1, 5, 5); 23 | glPopMatrix(); 24 | } 25 | 26 | -------------------------------------------------------------------------------- /src/entities/usership.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class UserShip { 6 | 7 | public: 8 | UserShip(float x, float y) : x(x), y(y) {} 9 | 10 | void initialize(entityx::ptr entities, 11 | entityx::ptr events); 12 | 13 | void update(double dt); 14 | 15 | void move_forward(double dt); 16 | void move_backwards(double dt); 17 | void rotate_left(double dt); 18 | void rotate_right(double dt); 19 | void fire(double dt); 20 | 21 | private: 22 | float x, y; 23 | double cool_down = 0; 24 | 25 | entityx::Entity entity; 26 | entityx::ptr entity_manager; 27 | 28 | void change_linear_momentum(float delta); 29 | void change_angular_momemtum(float delta); 30 | }; 31 | 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Azteroids 2 | 3 | Simple asteroids game made in C++ and OpenGL. 4 | 5 | ![Screen Shot](img/screenshot-1.png?raw=true) 6 | 7 | ## Dependencies 8 | 9 | * OpenGL and GLUT. 10 | * Cmake. 11 | * C++ compiler with C++11 support. 12 | * Other dependencies are git submodules (entityx and glfw). 13 | 14 | ## Build 15 | 16 | Clone this repository and update the git submodules (`git submodule update`). 17 | 18 | Just run `cmake` to build the Makefile, and then `make`. The executable will be 19 | placed inside `bin` folder. 20 | 21 | ## Commands 22 | 23 | * Arrow keys - move the ship. 24 | * Space - shoot. 25 | 26 | ## Acknowledgments 27 | 28 | * Alec Thomas' [entityx](https://github.com/alecthomas/entityx) - 29 | Entity-Component system. 30 | * [CMake](http://cmake.org) - cross-platform open-source build system. 31 | * [GLFW](http://www.glfw.org) - library for creating windows with OpenGL. 32 | 33 | -------------------------------------------------------------------------------- /src/components/position.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | enum class OffLimitBehavior { DESTROY, LOOP, BOUNCE }; 6 | 7 | struct Position : entityx::Component { 8 | Position(float x, float y, 9 | float rotation = 0.0f, 10 | float rotation_axis_x = 0.0f, 11 | float rotation_axis_y = 0.0f, 12 | float rotation_axis_z = 1.0f, 13 | OffLimitBehavior offLimitBehavior = OffLimitBehavior::LOOP) : 14 | x(x), y(y), rotation(rotation), 15 | rotation_axis_x(rotation_axis_x), 16 | rotation_axis_y(rotation_axis_y), 17 | rotation_axis_z(rotation_axis_z), 18 | offLimitBehavior(offLimitBehavior) {} 19 | 20 | float distance(entityx::ptr other) { 21 | return sqrt(pow(other->x - x, 2) + pow(other->y - y, 2)); 22 | } 23 | 24 | float x, y, 25 | rotation, 26 | rotation_axis_x, rotation_axis_y, rotation_axis_z; 27 | OffLimitBehavior offLimitBehavior; 28 | }; 29 | 30 | -------------------------------------------------------------------------------- /src/systems/render.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "render.h" 8 | 9 | void RenderSystem::update(entityx::ptr entities, 10 | entityx::ptr events, 11 | double dt) { 12 | glMatrixMode(GL_MODELVIEW); 13 | glLoadIdentity(); 14 | 15 | for (auto entity : entities->entities_with_components()) { 16 | if (entity.valid()) { 17 | entityx::ptr position = entity.component(); 18 | entityx::ptr appearance = entity.component(); 19 | 20 | glPushMatrix(); 21 | if (position) { 22 | glTranslatef(position->x, position->y, -500); 23 | glRotatef(position->rotation, 24 | position->rotation_axis_x, 25 | position->rotation_axis_y, 26 | position->rotation_axis_z); 27 | } 28 | appearance->render(entity, dt); 29 | glPopMatrix(); 30 | } 31 | } 32 | }; 33 | 34 | -------------------------------------------------------------------------------- /src/app.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "app.h" 4 | 5 | App::App(int width, int height) : 6 | width(width), height(height), level(Level(this)) { 7 | level.start(); 8 | } 9 | 10 | void App::initialize() { 11 | const GLfloat light_position0[] = { 0.0, 0.0, 1.0, 0.0 }; 12 | const GLfloat light_diffuse0[] = { 10.0, 10.0, 10.0, 1.0 }; 13 | 14 | const GLfloat light_position1[] = { 0.0, -1.0, 0.0, 0.0 }; 15 | const GLfloat light_diffuse1[] = { 10.0, 10.0, 50.0, 1.0 }; 16 | 17 | glClearColor (0.0, 0.0, 0.0, 0.0); 18 | glShadeModel (GL_FLAT); 19 | glEnable(GL_LIGHTING); 20 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 21 | glEnable(GL_BLEND); 22 | 23 | glLightfv(GL_LIGHT0, GL_POSITION, light_position0); 24 | glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse0); 25 | glEnable(GL_LIGHT0); 26 | 27 | glLightfv(GL_LIGHT1, GL_POSITION, light_position1); 28 | glLightfv(GL_LIGHT1, GL_DIFFUSE, light_diffuse1); 29 | glEnable(GL_LIGHT1); 30 | 31 | glEnable(GL_DEPTH_TEST); 32 | 33 | glMatrixMode(GL_PROJECTION); 34 | glLoadIdentity(); 35 | glOrtho(0, width, 0, height, 0.1f, 1000); 36 | } 37 | 38 | void App::step(double dt) { 39 | level.step(dt); 40 | } 41 | 42 | -------------------------------------------------------------------------------- /src/components/appearance/ship.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "ship.h" 3 | 4 | void Ship::render(float dt) { 5 | const GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 }; 6 | const GLfloat mat_shininess[] = { 100.0 }; 7 | const GLfloat mat_diffuse_left[] = {1.f, 0.0f, 0.5f}; 8 | const GLfloat mat_diffuse_right[] = {0.5f, 0.0f, 1.f}; 9 | 10 | GLfloat mat_diffuse_throttle[] = {0.5f, throttle_level, throttle_level}; 11 | 12 | glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); 13 | glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess); 14 | 15 | glPushMatrix(); 16 | glScalef(30, 30, 30); 17 | 18 | glBegin(GL_TRIANGLES); 19 | glMaterialfv(GL_FRONT, GL_EMISSION, mat_diffuse_left); 20 | glVertex3f(-0.6f, -0.4f, 0.f); 21 | glVertex3f(0.f, -0.2f, -0.5f); 22 | glVertex3f(0.f, 0.6f, -0.5f); 23 | 24 | glMaterialfv(GL_FRONT, GL_EMISSION, mat_diffuse_right); 25 | glVertex3f(0.6f, -0.4f, 0.f); 26 | glVertex3f(0.f, -0.2f, -0.5f); 27 | glVertex3f(0.f, 0.6f, -0.5f); 28 | 29 | glMaterialfv(GL_FRONT, GL_EMISSION, mat_diffuse_throttle); 30 | glVertex3f(-0.6f, -0.4f, 0.f); 31 | glVertex3f(0.f, -0.2f, -0.5f); 32 | glVertex3f(0.6f, -0.4f, 0.f); 33 | glEnd(); 34 | glPopMatrix(); 35 | 36 | // decay throttle level 37 | throttle_level *= pow(0.5f, dt); 38 | } 39 | 40 | void Ship::throttle() { 41 | throttle_level = 1; 42 | } 43 | 44 | -------------------------------------------------------------------------------- /src/components/appearance/particle.cpp: -------------------------------------------------------------------------------- 1 | #ifdef __APPLE__ 2 | #include 3 | #else 4 | #include 5 | #endif 6 | 7 | #include 8 | 9 | #include "particle.h" 10 | 11 | void Spark::render(entityx::Entity entity, float dt) { 12 | entityx::ptr particle = entity.component(); 13 | GLfloat fade = particle? 1.0f - (particle->age / particle->duration) : 1.0f; 14 | 15 | GLfloat mat_emission[] = { color[0], color[1], color[2], fade }; 16 | 17 | glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission); 18 | 19 | glBegin(GL_POINTS); 20 | glVertex3f(0, 0, 0); 21 | glEnd(); 22 | } 23 | 24 | void Cloud::render(entityx::Entity entity, float dt) { 25 | entityx::ptr particle = entity.component(); 26 | GLfloat fade = particle? 1.0f - (particle->age / particle->duration) : 1.0f; 27 | GLfloat size = (particle? particle->age : 1.0f) * grow_speed; 28 | 29 | GLfloat mat_emission[] = { color[0] * fade, 30 | color[1] * fade, 31 | color[2] * fade, 32 | fade }; 33 | 34 | glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, mat_emission); 35 | glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_emission); 36 | 37 | glPushMatrix(); 38 | glScalef(size, size, 0); 39 | glutSolidSphere(1, 50, 50); 40 | glPopMatrix(); 41 | } 42 | 43 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.8) 2 | project (AzteroidsGame) 3 | set(VERSION "0.1.0") 4 | 5 | # Required libraries in this source dir 6 | add_subdirectory(lib/entityx EXCLUDE_FROM_ALL) 7 | add_subdirectory(lib/glfw EXCLUDE_FROM_ALL) 8 | 9 | # use GLUT 10 | find_package(GLUT REQUIRED) 11 | include_directories(${GLUT_INCLUDE_DIRS}) 12 | 13 | # include paths 14 | include_directories(lib/glfw/include lib/entityx src) 15 | 16 | if (NOT APPLE) 17 | # HACK: This is NOTFOUND on OS X 10.8 18 | find_package(OpenGL REQUIRED) 19 | include_directories(${OPENGL_INCLUDE_DIR}) 20 | endif() 21 | 22 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "bin/") 23 | file(GLOB_RECURSE azteroids_SOURCES "src/*.cpp") 24 | 25 | if (APPLE) 26 | set(OSX_ICON_FILES ${CMAKE_CURRENT_SOURCE_DIR}/icons/Azteroids.icns) 27 | # set where in the bundle to put the icns files 28 | set_source_files_properties(${OSX_ICON_FILES} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") 29 | # include the icns files in the target 30 | set( azteroids_SOURCES ${azteroids_SOURCES} ${OSX_ICON_FILES} ) 31 | endif() 32 | 33 | add_executable(Azteroids WIN32 MACOSX_BUNDLE ${azteroids_SOURCES}) 34 | 35 | if (APPLE) 36 | set_target_properties(Azteroids PROPERTIES 37 | MACOSX_BUNDLE_BUNDLE_NAME "Azteroids" 38 | MACOSX_BUNDLE_ICON_FILE "Azteroids" 39 | MACOSX_BUNDLE_SHORT_VERSION_STRING ${VERSION} 40 | MACOSX_BUNDLE_LONG_VERSION_STRING "Azteroids - Version ${VERSION}") 41 | endif() 42 | 43 | if (MSVC) 44 | # Tell MSVC to use main instead of WinMain for Windows subsystem executables 45 | set_target_properties(Azteroids PROPERTIES 46 | LINK_FLAGS "/ENTRY:mainCRTStartup") 47 | endif() 48 | 49 | set(CMAKE_CXX_FLAGS "-std=c++11 -stdlib=libc++") 50 | target_link_libraries(Azteroids entityx glfw ${GLFW_LIBRARIES} ${GLUT_LIBRARIES}) 51 | 52 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #ifdef __APPLE__ 4 | #include 5 | #else 6 | #include 7 | #endif 8 | 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | const int WINDOW_WIDTH = 800; 15 | const int WINDOW_HEIGHT = 600; 16 | 17 | const int SPACE_WIDTH = 800; 18 | const int SPACE_HEIGHT = 600; 19 | 20 | App application(SPACE_WIDTH, SPACE_HEIGHT); 21 | 22 | static void error_callback (int error, const char* description) { 23 | fputs(description, stderr); 24 | } 25 | 26 | static void key_callback (GLFWwindow* window, int key, int scancode, int action, int mods) { 27 | if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { 28 | glfwSetWindowShouldClose(window, GL_TRUE); 29 | return; 30 | } 31 | 32 | // set keys 33 | if (action == GLFW_PRESS || action == GLFW_RELEASE) { 34 | bool key_pressed = action == GLFW_PRESS; 35 | switch (key) { 36 | case GLFW_KEY_UP: 37 | application.keys_pressed.up = key_pressed; 38 | break; 39 | case GLFW_KEY_DOWN: 40 | application.keys_pressed.down = key_pressed; 41 | break; 42 | case GLFW_KEY_LEFT: 43 | application.keys_pressed.left = key_pressed; 44 | break; 45 | case GLFW_KEY_RIGHT: 46 | application.keys_pressed.right = key_pressed; 47 | break; 48 | case GLFW_KEY_SPACE: 49 | application.keys_pressed.space = key_pressed; 50 | break; 51 | } 52 | } 53 | } 54 | 55 | int main (void) { 56 | GLFWwindow* window; 57 | float lastTime = 0; 58 | 59 | glfwSetErrorCallback(error_callback); 60 | if (!glfwInit()) { 61 | exit(EXIT_FAILURE); 62 | } 63 | window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT, "Azteroids", NULL, NULL); 64 | if (!window) { 65 | glfwTerminate(); 66 | exit(EXIT_FAILURE); 67 | } 68 | glfwMakeContextCurrent(window); 69 | glfwSetKeyCallback(window, key_callback); 70 | 71 | application.initialize(); 72 | 73 | while (!glfwWindowShouldClose(window)) { 74 | int width, height; 75 | glfwGetFramebufferSize(window, &width, &height); 76 | glViewport(0, 0, width, height); 77 | 78 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 79 | 80 | double time = glfwGetTime(); 81 | application.step(time - lastTime); 82 | lastTime = time; 83 | 84 | glfwSwapBuffers(window); 85 | glfwPollEvents(); 86 | } 87 | glfwDestroyWindow(window); 88 | glfwTerminate(); 89 | exit(EXIT_SUCCESS); 90 | } 91 | 92 | -------------------------------------------------------------------------------- /src/managers/level.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "level.h" 14 | 15 | const int AZTEROIDS_NUM = 20; 16 | 17 | Level::Level(App *application) : application(application), 18 | user_ship(application->width/2, 19 | application->height/2) {} 20 | 21 | void Level::configure() { 22 | system_manager->add(); 23 | system_manager->add(application->width, 24 | application->height); 25 | system_manager->add(); 26 | system_manager->add(); 27 | }; 28 | 29 | void Level::initialize() { 30 | 31 | // Create asteroids: 32 | for (int i = 0; i < AZTEROIDS_NUM; i++) { 33 | float mass = rand() % 20 + 20; 34 | entityx::Entity asteroid = entity_manager->create(); 35 | asteroid.assign(mass); 36 | asteroid.assign(EntityIdentity::ASTEROID); 37 | asteroid.assign(mass); 38 | asteroid.assign(mass); 39 | asteroid.assign(rand() % 1000 - 500, rand() % 1000 - 500, 40 | rand() % 1200 - 600); 41 | asteroid.assign(rand() % application->width, 42 | rand() % application->height, 43 | rand() % 360, 44 | rand() % 10, rand() % 10, rand() % 10); 45 | } 46 | 47 | // Initialize user-controllable ship: 48 | user_ship.initialize(entity_manager, event_manager); 49 | } 50 | 51 | void Level::update(double dt) { 52 | system_manager->update(dt); 53 | system_manager->update(dt); 54 | system_manager->update(dt); 55 | system_manager->update(dt); 56 | 57 | user_ship.update(dt); 58 | 59 | // update user ship based on the keys state 60 | if (application->keys_pressed.up) { 61 | user_ship.move_forward(dt); 62 | } 63 | if (application->keys_pressed.down) { 64 | user_ship.move_backwards(dt); 65 | } 66 | if (application->keys_pressed.left) { 67 | user_ship.rotate_left(dt); 68 | } 69 | if (application->keys_pressed.right) { 70 | user_ship.rotate_right(dt); 71 | } 72 | if (application->keys_pressed.space) { 73 | user_ship.fire(dt); 74 | } 75 | } 76 | 77 | -------------------------------------------------------------------------------- /src/entities/usership.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "usership.h" 11 | 12 | const float LINEAR_MOMENTUM_STEP = 2000; 13 | const float ANGULAR_MOMENTUM_STEP = 5000; 14 | const double FIRE_COOLDOWN = .3; 15 | 16 | const float PI = 3.14159265359f; 17 | const float DPI = 2 * PI; 18 | const float HPI = 0.5f * PI; 19 | 20 | void UserShip::initialize(entityx::ptr entities, 21 | entityx::ptr events) { 22 | entity_manager = entities; 23 | 24 | // create entity's components 25 | entity = entities->create(); 26 | entity.assign(EntityIdentity::SHIP); 27 | entity.assign(30); 28 | entity.assign(); 29 | entity.assign(10); 30 | entity.assign(0, 0, 0); 31 | entity.assign(0.6f, 0.5f); 32 | entity.assign(x, y); 33 | } 34 | 35 | void UserShip::update(double dt) { 36 | cool_down += dt; 37 | } 38 | 39 | void UserShip::move_forward(double dt) { 40 | change_linear_momentum(dt * LINEAR_MOMENTUM_STEP); 41 | } 42 | 43 | void UserShip::move_backwards(double dt) { 44 | change_linear_momentum(dt * -LINEAR_MOMENTUM_STEP); 45 | } 46 | 47 | void UserShip::rotate_left(double dt) { 48 | change_angular_momemtum(dt * ANGULAR_MOMENTUM_STEP); 49 | } 50 | 51 | void UserShip::rotate_right(double dt) { 52 | change_angular_momemtum(dt * -ANGULAR_MOMENTUM_STEP); 53 | } 54 | 55 | void UserShip::fire(double dt) { 56 | if (cool_down > FIRE_COOLDOWN) { 57 | cool_down = 0; // reset cool down 58 | 59 | entityx::ptr position = entity.component(); 60 | entityx::ptr momentum = entity.component(); 61 | 62 | // laser beam! 63 | float angle = (position->rotation * DPI / 360.f) + HPI; 64 | entityx::Entity laser = entity_manager->create(); 65 | laser.assign(); 66 | laser.assign(EntityIdentity::LASER); 67 | laser.assign(10); 68 | laser.assign(cos(angle) * 500, 69 | sin(angle) * 500); 70 | laser.assign(position->x + cos(angle) * 20, 71 | position->y + sin(angle) * 20, 72 | position->rotation, 73 | 0, 0, 1, 74 | OffLimitBehavior::DESTROY); 75 | 76 | } 77 | } 78 | 79 | void UserShip::change_angular_momemtum(float delta) { 80 | entityx::ptr momentum = entity.component(); 81 | 82 | momentum->angular += delta; 83 | } 84 | 85 | void UserShip::change_linear_momentum(float delta) { 86 | entityx::ptr position = entity.component(); 87 | entityx::ptr momentum = entity.component(); 88 | entityx::ptr ship = entity.component(); 89 | 90 | float angle = (position->rotation * DPI / 360.f) + HPI; 91 | momentum->x += cos(angle) * delta; 92 | momentum->y += sin(angle) * delta; 93 | 94 | ship->throttle(); 95 | } 96 | 97 | -------------------------------------------------------------------------------- /src/systems/movement.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "movement.h" 8 | 9 | void MovementSystem::update(entityx::ptr entities, 10 | entityx::ptr events, 11 | double dt) { 12 | for (auto entity : entities->entities_with_components()) { 13 | if (entity.valid()) { 14 | entityx::ptr position = entity.component(); 15 | entityx::ptr momentum = entity.component(); 16 | entityx::ptr mass = entity.component(); 17 | entityx::ptr geometry = entity.component(); 18 | float m = mass? mass->mass : 1; 19 | float radius = geometry? geometry->radius : 0; 20 | 21 | // linear momentum 22 | position->x += momentum->x / m * dt; 23 | position->y += momentum->y / m * dt; 24 | 25 | // angular momentum 26 | position->rotation += momentum->angular / m * dt; 27 | 28 | // Bound rotation to 0-360 deg 29 | while (position->rotation < 0) { position->rotation += 360; } 30 | while (position->rotation > 360) { position->rotation -= 360; } 31 | 32 | // Bound linear position 33 | switch (position->offLimitBehavior) { 34 | case OffLimitBehavior::DESTROY: 35 | if (position->x < -radius || 36 | position->x > width + radius || 37 | position->y < -radius || 38 | position->y > height + radius) { 39 | entity.destroy(); 40 | } 41 | break; 42 | case OffLimitBehavior::LOOP: 43 | while (position->x < -radius) { position->x += width + radius*2; } 44 | while (position->x > width + radius) { position->x -= width + radius*2; } 45 | while (position->y < -radius) { position->y += height + radius*2; } 46 | while (position->y > height + radius) { position->y -= height + radius*2; } 47 | break; 48 | case OffLimitBehavior::BOUNCE: 49 | if (position->x < radius) { 50 | position->x = radius; 51 | momentum->x *= -1; 52 | } else if (position->x > width - radius) { 53 | position->x = width - radius; 54 | momentum->x *= -1; 55 | } 56 | if (position->y < radius) { 57 | position->y = radius; 58 | momentum->y *= -1; 59 | } else if (position->y > height - radius) { 60 | position->y = height - radius; 61 | momentum->y *= -1; 62 | } 63 | break; 64 | } 65 | } 66 | } 67 | 68 | for (auto entity : entities->entities_with_components()) { 69 | entityx::ptr attrition = entity.component(); 70 | entityx::ptr momentum = entity.component(); 71 | 72 | momentum->x *= pow(attrition->linear, dt); 73 | momentum->y *= pow(attrition->linear, dt); 74 | momentum->angular *= pow(attrition->angular, dt); 75 | } 76 | } 77 | 78 | -------------------------------------------------------------------------------- /src/systems/collision.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "collision.h" 11 | 12 | const float MINIMUM_MASS = 10; 13 | 14 | void handle_cases(entityx::ptr entity_manager, 15 | entityx::Entity entity1, entityx::Entity entity2); 16 | void bounce(entityx::Entity entity1, entityx::Entity entity2); 17 | 18 | void CollisionSystem::update(entityx::ptr entities, 19 | entityx::ptr events, 20 | double dt) { 21 | for (auto entity1 : entities->entities_with_components()) { 22 | entityx::ptr pos1 = entity1.component(); 23 | entityx::ptr geo1 = entity1.component(); 24 | 25 | for (auto entity2 : entities->entities_with_components()) { 26 | if (entity1.valid() && entity2.valid() && entity1 != entity2) { 27 | entityx::ptr pos2 = entity2.component(); 28 | entityx::ptr geo2 = entity2.component(); 29 | 30 | double distance = pos1->distance(pos2); 31 | double min_distance = geo1->radius + geo2->radius; 32 | 33 | if (distance < min_distance) { 34 | // Collision! 35 | handle_cases(entities, entity1, entity2); 36 | } 37 | } 38 | } 39 | } 40 | } 41 | 42 | void handle_cases(entityx::ptr entity_manager, 43 | entityx::Entity entity1, entityx::Entity entity2) { 44 | const float spark_color[] = {0.5f, 1.0f, 1.0f}; 45 | 46 | entityx::ptr id1 = entity1.component(); 47 | entityx::ptr id2 = entity2.component(); 48 | 49 | // handle cases... 50 | if (id1->identity == EntityIdentity::ASTEROID && 51 | id2->identity == EntityIdentity::ASTEROID) { 52 | bounce(entity1, entity2); 53 | 54 | } else if (id1->identity == EntityIdentity::LASER && 55 | id2->identity == EntityIdentity::ASTEROID) { 56 | entityx::ptr mass = entity2.component(); 57 | entityx::ptr position = entity2.component(); 58 | 59 | entity1.destroy(); 60 | entity2.destroy(); 61 | 62 | if (mass->mass > MINIMUM_MASS * 2) { 63 | int parts = fmin(4, mass->mass / MINIMUM_MASS); 64 | float new_mass = mass->mass * 1.5 / parts; 65 | for (int i = 0; i < parts; i++) { 66 | entityx::Entity asteroid = entity_manager->create(); 67 | asteroid.assign(new_mass); 68 | asteroid.assign(EntityIdentity::ASTEROID); 69 | asteroid.assign(new_mass); 70 | asteroid.assign(new_mass); 71 | asteroid.assign(rand() % 1000 - 500, rand() % 1000 - 500, 72 | rand() % 1200 - 600); 73 | asteroid.assign(position->x + (rand() % int(new_mass)) - new_mass/2, 74 | position->y + (rand() % int(new_mass)) - new_mass/2, 75 | rand() % 360, 76 | rand() % 10, rand() % 10, rand() % 10); 77 | } 78 | } 79 | 80 | // Create some particle 81 | for (int i = 0; i < 500; i++) { 82 | float angle = rand() % 360; 83 | float speed = rand() % 100 + 50; 84 | float duration = (rand() % 1000) / 1000.0f; 85 | entityx::Entity spark = entity_manager->create(); 86 | spark.assign(spark_color); 87 | spark.assign(duration); 88 | spark.assign(EntityIdentity::PARTICLE); 89 | spark.assign(cos(angle) * speed, sin(angle) * speed); 90 | spark.assign(position->x, position->y, 91 | 0, 0, 0, 1, 92 | OffLimitBehavior::DESTROY); 93 | } 94 | entityx::Entity cloud = entity_manager->create(); 95 | cloud.assign(spark_color, 500); 96 | cloud.assign(0.2f); 97 | cloud.assign(EntityIdentity::PARTICLE); 98 | cloud.assign(position->x, position->y); 99 | 100 | } else if (id2->identity == EntityIdentity::LASER && 101 | id1->identity == EntityIdentity::ASTEROID) { 102 | // just swap... 103 | handle_cases(entity_manager, entity2, entity1); 104 | } 105 | } 106 | 107 | void bounce(entityx::Entity entity1, entityx::Entity entity2) { 108 | entityx::ptr pos1 = entity1.component(); 109 | entityx::ptr geo1 = entity1.component(); 110 | entityx::ptr mom1 = entity1.component(); 111 | 112 | entityx::ptr pos2 = entity2.component(); 113 | entityx::ptr geo2 = entity2.component(); 114 | 115 | double distance = pos1->distance(pos2); 116 | double min_distance = geo1->radius + geo2->radius; 117 | 118 | if (distance < min_distance) { 119 | // Collision! 120 | double dx = (pos2->x - pos1->x) / distance; 121 | double dy = (pos2->y - pos1->y) / distance; 122 | double adjust = (min_distance - distance) / 2.0; 123 | 124 | // correct position to the minimum distance 125 | pos1->x -= dx * adjust; 126 | pos1->y -= dy * adjust; 127 | pos2->x += dx * adjust; 128 | pos2->y += dy * adjust; 129 | 130 | entityx::ptr mom2 = entity2.component(); 131 | 132 | if (mom1 && mom2) { 133 | // Exchange momentum 134 | double contrib1 = (mom1->x * dx) + (mom1->y * dy); 135 | double contrib2 = (mom2->x * dx) + (mom2->y * dy); 136 | 137 | mom1->x += contrib2 * dx; 138 | mom1->y += contrib2 * dy; 139 | mom1->x -= contrib1 * dx; 140 | mom1->y -= contrib1 * dy; 141 | 142 | mom2->x += contrib1 * dx; 143 | mom2->y += contrib1 * dy; 144 | mom2->x -= contrib2 * dx; 145 | mom2->y -= contrib2 * dy; 146 | } 147 | } 148 | } 149 | 150 | --------------------------------------------------------------------------------