From b53826079429939cdfbda073608cb85be8ba0738 Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Sat, 7 Mar 2015 11:29:57 -0500 Subject: Created entity-component system Also tweaked the bloom flicker, tweaked the scanline texture, created a second test map, and created some currently unused sound effects. --- CMakeLists.txt | 2 +- maps/second.txt | 25 +++ res/Randomize27.wav | Bin 0 -> 48628 bytes res/Randomize87.wav | Bin 0 -> 47368 bytes res/ground.wav | 1 + res/scanlines_333.bmp | Bin 0 -> 216 bytes res/tiles.png | Bin 504 -> 688 bytes shaders/bloom2.fragment | 8 +- src/components.cpp | 537 ++++++++++++++++++++++++++++++++++++++++++++++++ src/components.h | 93 +++++++++ src/entity.cpp | 38 ++++ src/entity.h | 72 +++++++ src/game.cpp | 124 +++++++++++ src/game.h | 43 ++++ src/main.cpp | 67 +----- src/map.cpp | 2 +- src/mapview.cpp | 393 ----------------------------------- src/mapview.h | 65 ------ src/mob.h | 12 -- src/renderer.cpp | 6 +- src/renderer.h | 2 +- src/state.h | 13 -- src/world.cpp | 32 +++ src/world.h | 27 +++ 24 files changed, 1004 insertions(+), 558 deletions(-) create mode 100644 maps/second.txt create mode 100644 res/Randomize27.wav create mode 100644 res/Randomize87.wav create mode 100644 res/ground.wav create mode 100644 res/scanlines_333.bmp create mode 100644 src/components.cpp create mode 100644 src/components.h create mode 100644 src/entity.cpp create mode 100644 src/entity.h create mode 100644 src/game.cpp create mode 100644 src/game.h delete mode 100644 src/mapview.cpp delete mode 100644 src/mapview.h delete mode 100644 src/mob.h delete mode 100644 src/state.h create mode 100644 src/world.cpp create mode 100644 src/world.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a006f67..0937493 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,6 +34,6 @@ set(ALL_LIBS # include_directories(${SDL2_INCLUDE_DIR}) set(CMAKE_BUILD_TYPE Debug) -add_executable(Aromatherapy src/main.cpp src/map.cpp src/mapview.cpp src/renderer.cpp) +add_executable(Aromatherapy src/main.cpp src/map.cpp src/renderer.cpp src/world.cpp src/entity.cpp src/components.cpp src/game.cpp) target_link_libraries(Aromatherapy ${ALL_LIBS}) install(TARGETS Aromatherapy RUNTIME DESTINATION ${BIN_DIR}) diff --git a/maps/second.txt b/maps/second.txt new file mode 100644 index 0000000..386af10 --- /dev/null +++ b/maps/second.txt @@ -0,0 +1,25 @@ +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,0,0,0,0,0,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,0, +0,0,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,19,0,0,0,0,0,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,19,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,10,10,10,10,10,10,10,10,10,21,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,18,9,9,9,9,9,9,9,9,9,9,17,0,0,0,0,0,0,0,0,0,22,8,8,8,8,8,8,8,8,8,8,21,0,0,0,0, +20,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,13,0,0,0,0, +0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,8,8,8,8, +0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,20,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,28,29,-1,30,31,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,33,34,35,36,37,38,39,40,41,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,27,2,2,3,3,3,4,26,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,20,0,0,0,0,0,19,0,0,27,1,1,1,2,2,2,26,0,0,0,0,0,0,0,0,0,0,24,4,2,2,1,3,2,2,1,2,2,3, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,0,0,0,0,12,0,0,0,0,0,19,0,0,0,0,0, +It's A Trap! \ No newline at end of file diff --git a/res/Randomize27.wav b/res/Randomize27.wav new file mode 100644 index 0000000..020b0ad Binary files /dev/null and b/res/Randomize27.wav differ diff --git a/res/Randomize87.wav b/res/Randomize87.wav new file mode 100644 index 0000000..6338faf Binary files /dev/null and b/res/Randomize87.wav differ diff --git a/res/ground.wav b/res/ground.wav new file mode 100644 index 0000000..16db27f --- /dev/null +++ b/res/ground.wav @@ -0,0 +1 @@ +-1>1>-1||0|1|false>-1||0|1|false>-1||0|1|false>-1||0|1|false>-1||0|1|false \ No newline at end of file diff --git a/res/scanlines_333.bmp b/res/scanlines_333.bmp new file mode 100644 index 0000000..a5575b1 Binary files /dev/null and b/res/scanlines_333.bmp differ diff --git a/res/tiles.png b/res/tiles.png index 905221f..270f557 100644 Binary files a/res/tiles.png and b/res/tiles.png differ diff --git a/shaders/bloom2.fragment b/shaders/bloom2.fragment index eca56ea..da44ea1 100644 --- a/shaders/bloom2.fragment +++ b/shaders/bloom2.fragment @@ -18,14 +18,14 @@ void main() color = vec3(0.0); //color += texture(tex2, UV).rgb / 2.0; color += texture(blurTex, UV).rgb; - color = max(vec3(0.0), color - 0.25); + color = max(vec3(0.0), color - 0.5); //color *= color; - float flicker = 0.5 + nrand(vec2(iGlobalTime)); + //float flicker = 0.5 + nrand(vec2(iGlobalTime)); //flicker *= (flicker); - flicker = sqrt(flicker); + //flicker = sqrt(flicker); //flicker = pow(flicker, 1.0/8.0); - color *= flicker; + //color *= flicker; //color *= mix(vec3(0.0), color, flicker); color += texture(clearTex, UV).rgb; diff --git a/src/components.cpp b/src/components.cpp new file mode 100644 index 0000000..f9c7c10 --- /dev/null +++ b/src/components.cpp @@ -0,0 +1,537 @@ +#include "components.h" +#include "game.h" + +// User movement component + +void UserMovementComponent::input(int key, int action) +{ + if (action == GLFW_PRESS) + { + if (key == GLFW_KEY_LEFT) + { + holdingLeft = true; + + message_t msg; + msg.type = CM_WALK_LEFT; + + entity.send(msg); + } else if (key == GLFW_KEY_RIGHT) + { + holdingRight = true; + + message_t msg; + msg.type = CM_WALK_RIGHT; + + entity.send(msg); + } else if (key == GLFW_KEY_UP) + { + message_t msg; + msg.type = CM_JUMP; + + entity.send(msg); + } else if (key == GLFW_KEY_DOWN) + { + message_t msg; + msg.type = CM_CAN_DROP; + + entity.send(msg); + } + } else if (action == GLFW_RELEASE) + { + if (key == GLFW_KEY_LEFT) + { + holdingLeft = false; + + if (holdingRight) + { + message_t msg; + msg.type = CM_WALK_RIGHT; + + entity.send(msg); + } else { + message_t msg; + msg.type = CM_STOP_WALKING; + + entity.send(msg); + } + } else if (key == GLFW_KEY_RIGHT) + { + holdingRight = false; + + if (holdingLeft) + { + message_t msg; + msg.type = CM_WALK_LEFT; + + entity.send(msg); + } else { + message_t msg; + msg.type = CM_STOP_WALKING; + + entity.send(msg); + } + } else if (key == GLFW_KEY_DOWN) + { + message_t msg; + msg.type = CM_CANT_DROP; + + entity.send(msg); + } else if (key == GLFW_KEY_UP) + { + message_t msg; + msg.type = CM_STOP_JUMP; + + entity.send(msg); + } + } +} + +// Physics component + +void PhysicsBodyComponent::receive(message_t msg) +{ + if (msg.type == CM_WALK_LEFT) + { + velocity.first = -1.5; + } else if (msg.type == CM_WALK_RIGHT) + { + velocity.first = 1.5; + } else if (msg.type == CM_STOP_WALKING) + { + velocity.first = 0.0; + } +} + +void PhysicsBodyComponent::tick() +{ + velocity.first += accel.first; + velocity.second += accel.second; + + position.first += velocity.first; + position.second += velocity.second; +} + +void PhysicsBodyComponent::detectCollision(Entity& player, Locatable& physics, std::pair old_position) +{ + // If already colliding, do nothing! + if ((old_position.first + physics.size.first > this->position.first) + && (old_position.first < this->position.first + this->size.first) + && (old_position.second + physics.size.second > this->position.second) + && (old_position.second < this->position.second + this->size.second)) + { + return; + } + + // If newly colliding, SHOCK AND HORROR! + if ((physics.position.first + physics.size.first > this->position.first) + && (physics.position.first < this->position.first + this->size.first) + && (physics.position.second + physics.size.second > this->position.second) + && (physics.position.second < this->position.second + this->size.second)) + { + message_t msg; + msg.type = CM_COLLISION; + msg.collisionEntity = &player; + + entity.send(msg); + } +} + +// Render player + +PlayerSpriteComponent::PlayerSpriteComponent(Entity& entity, Locatable& physics) : Component(entity), physics(physics) +{ + sprite = loadTextureFromFile("../res/Starla.png"); +} + +PlayerSpriteComponent::~PlayerSpriteComponent() +{ + destroyTexture(sprite); +} + +void PlayerSpriteComponent::render(Texture* buffer) +{ + int frame = 0; + if (isMoving) + { + frame += 2; + + if (animFrame < 10) + { + frame += 2; + } + } + if (facingLeft) frame++; + + Rectangle src_rect(frame*10, 0, 10, 12); + Rectangle dst_rect((int) physics.position.first, (int) physics.position.second, physics.size.first, physics.size.second); + blitTexture(sprite, buffer, &src_rect, &dst_rect); +} + +void PlayerSpriteComponent::receive(message_t msg) +{ + if (msg.type == CM_WALK_LEFT) + { + facingLeft = true; + isMoving = true; + } else if (msg.type == CM_WALK_RIGHT) + { + facingLeft = false; + isMoving = true; + } else if (msg.type == CM_STOP_WALKING) + { + isMoving = false; + } +} + +void PlayerSpriteComponent::tick() +{ + animFrame++; + animFrame %= 20; +} + +// Player physics + +#define JUMP_VELOCITY(h, l) (-2 * (h) / (l)) +#define JUMP_GRAVITY(h, l) (2 * ((h) / (l)) / (l)) + +PlayerPhysicsComponent::PlayerPhysicsComponent(Entity& entity) : Component(entity) +{ + jump_velocity = JUMP_VELOCITY(TILE_HEIGHT*4.5, 0.3*FRAMES_PER_SECOND); + jump_gravity = JUMP_GRAVITY(TILE_HEIGHT*4.5, 0.3*FRAMES_PER_SECOND); + jump_gravity_short = JUMP_GRAVITY(TILE_HEIGHT*3.5, 0.233*FRAMES_PER_SECOND); + + accel.second = jump_gravity_short; +} + +void PlayerPhysicsComponent::receive(message_t msg) +{ + if (msg.type == CM_WALK_LEFT) + { + velocity.first = -1.5; + direction = -1; + } else if (msg.type == CM_WALK_RIGHT) + { + velocity.first = 1.5; + direction = 1; + } else if (msg.type == CM_STOP_WALKING) + { + velocity.first = 0.0; + direction = 0; + } else if (msg.type == CM_JUMP) + { + velocity.second = jump_velocity; + accel.second = jump_gravity; + } else if (msg.type == CM_STOP_JUMP) + { + accel.second = jump_gravity_short; + } else if (msg.type == CM_CAN_DROP) + { + canDrop = true; + } else if (msg.type == CM_CANT_DROP) + { + canDrop = false; + } else if (msg.type == CM_DROP) + { + if (canDrop) + { + canDrop = false; + } else { + position.second = msg.dropAxis - size.second; + velocity.second = 0; + } + } +} + +void PlayerPhysicsComponent::tick() +{ + // Continue walking even if blocked earlier + if (velocity.first == 0) + { + if (direction < 0) + { + velocity.first = -1.5; + } else if (direction > 0) + { + velocity.first = 1.5; + } + } + + // Increase gravity at the height of jump + if ((accel.second == jump_gravity) && (velocity.second >= 0)) + { + accel.second = jump_gravity_short; + } + + // Apply acceleration + velocity.first += accel.first; + velocity.second += accel.second; + + // Terminal velocity + if (velocity.first < -16) velocity.first = -16; + if (velocity.first > 16) velocity.first = 16; + if (velocity.second < -16) velocity.second = -16; + if (velocity.second > 16) velocity.second = 16; + + // Do the movement + std::pair old_position = std::make_pair(position.first, position.second); + position.first += velocity.first; + position.second += velocity.second; + + // Check for collisions + for (auto it = entity.world->bodies.begin(); it != entity.world->bodies.end(); it++) + { + auto poop = *it; + poop->detectCollision(entity, *this, old_position); + } +} + +// Map rendering + +MapRenderComponent::MapRenderComponent(Entity& entity, Map* map) : Component(entity) +{ + screen = createTexture(GAME_WIDTH, GAME_HEIGHT); + fillTexture(screen, NULL, 0, 0, 0); + + Texture* tiles = loadTextureFromFile("../res/tiles.png"); + + for (int i=0; imapdata()[i]; + int x = i % MAP_WIDTH; + int y = i / MAP_WIDTH; + Rectangle dst(x*TILE_WIDTH, y*TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT); + Rectangle src(tile%8*TILE_WIDTH, tile/8*TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT); + + if (tile > 0) + { + blitTexture(tiles, screen, &src, &dst); + } + } + + destroyTexture(tiles); + + Texture* font = loadTextureFromFile("../res/font.bmp"); + const char* map_name = map->title(); + int start_x = (40/2) - (strlen(map_name)/2); + for (size_t i=0; imap = map; + + add_collision(-6, 0, GAME_WIDTH, left, (map->getLeftMap() == NULL) ? 1 : 2); + add_collision(GAME_WIDTH+6, 0, GAME_WIDTH, right, (map->getRightMap() == NULL) ? 3 : 2); + + for (int i=0; imapdata()[i]; + + if ((tile > 0) && (!((tile >= 5) && (tile <= 7)))) + { + add_collision(x*TILE_WIDTH, y*TILE_HEIGHT, (y+1)*TILE_HEIGHT, right, 0); + add_collision((x+1)*TILE_WIDTH, y*TILE_HEIGHT, (y+1)*TILE_HEIGHT, left, 0); + add_collision(y*TILE_HEIGHT, x*TILE_WIDTH, (x+1)*TILE_WIDTH, down, 0); + add_collision((y+1)*TILE_HEIGHT, x*TILE_WIDTH, (x+1)*TILE_WIDTH, up, 0); + } else if ((tile >= 5) && (tile <= 7)) + { + add_collision(y*TILE_HEIGHT, x*TILE_WIDTH, (x+1)*TILE_WIDTH, down, 3); + } + } +} + +void MapCollisionComponent::add_collision(int axis, int lower, int upper, direction_t dir, int type) +{ + std::list::iterator it; + + switch (dir) + { + case up: + it = up_collisions.begin(); + for (; it!=up_collisions.end(); it++) + { + if (it->axis < axis) break; + } + + up_collisions.insert(it, {axis, lower, upper, type}); + + break; + case down: + it = down_collisions.begin(); + for (; it!=down_collisions.end(); it++) + { + if (it->axis > axis) break; + } + + down_collisions.insert(it, {axis, lower, upper, type}); + + break; + case left: + it = left_collisions.begin(); + for (; it!=left_collisions.end(); it++) + { + if (it->axis < axis) break; + } + + left_collisions.insert(it, {axis, lower, upper, type}); + + break; + case right: + it = right_collisions.begin(); + for (; it!=right_collisions.end(); it++) + { + if (it->axis > axis) break; + } + + right_collisions.insert(it, {axis, lower, upper, type}); + + break; + } +} + +void MapCollisionComponent::detectCollision(Entity& player, Locatable& physics, std::pair old_position) +{ + int fixed_x = (int) physics.position.first; + int fixed_y = (int) physics.position.second; + int fixed_ox = (int) old_position.first; + int fixed_oy = (int) old_position.second; + + if (fixed_x < fixed_ox) + { + for (auto it=left_collisions.begin(); it!=left_collisions.end(); it++) + { + if (it->axis > fixed_ox) continue; + if (it->axis < fixed_x) break; + + if ((fixed_oy+physics.size.second > it->lower) && (fixed_oy < it->upper)) + { + // We have a collision! + if (it->type == 0) + { + physics.position.first = it->axis; + physics.velocity.first = 0; + } else if (it->type == 1) + { + physics.position.first = GAME_WIDTH-physics.size.first/2; + } else if (it->type == 2) + { + physics.position.first = GAME_WIDTH-physics.size.first/2; + Game::getInstance().loadMap(map->getLeftMap()); + } + + break; + } + } + } else if (fixed_x > fixed_ox) + { + for (auto it=right_collisions.begin(); it!=right_collisions.end(); it++) + { + if (it->axis < fixed_ox+physics.size.first) continue; + if (it->axis > fixed_x+physics.size.first) break; + + if ((fixed_oy+physics.size.second > it->lower) && (fixed_oy < it->upper)) + { + // We have a collision! + if (it->type == 0) + { + physics.position.first = it->axis - physics.size.first; + physics.velocity.first = 0; + } else if (it->type == 1) + { + physics.position.first = -physics.size.first/2; + } else if (it->type == 2) + { + physics.position.first = -physics.size.first/2; + Game::getInstance().loadMap(map->getRightMap()); + } else if (it->type == 3) + { + physics.position.first = it->axis - physics.size.first; + + message_t msg; + msg.type = CM_WALK_LEFT; + player.send(msg); + } + + break; + } + } + } + + fixed_x = (int) physics.position.first; + fixed_y = (int) physics.position.second; + + if (fixed_y < fixed_oy) + { + for (auto it=up_collisions.begin(); it!=up_collisions.end(); it++) + { + if (it->axis > fixed_oy) continue; + if (it->axis < fixed_y) break; + + if ((fixed_x+physics.size.first > it->lower) && (fixed_x < it->upper)) + { + // We have a collision! + if (it->type == 0) + { + physics.position.second = it->axis; + physics.velocity.second = 0; + } else if (it->type == 1) + { + physics.position.second = GAME_HEIGHT-physics.size.second/2-1; + } + + break; + } + } + } else if (fixed_y > fixed_oy) + { + for (auto it=down_collisions.begin(); it!=down_collisions.end(); it++) + { + if (it->axis < fixed_oy+physics.size.second) continue; + if (it->axis > fixed_y+physics.size.second) break; + + if ((fixed_x+physics.size.first > it->lower) && (fixed_x < it->upper)) + { + // We have a collision! + if (it->type == 0) + { + physics.position.second = it->axis - physics.size.second; + physics.velocity.second = 0; + //mob->onGround = true; + } else if (it->type == 1) + { + physics.position.second = -physics.size.second/2; + } else if (it->type == 3) + { + message_t msg; + msg.type = CM_DROP; + msg.dropAxis = it->axis; + + player.send(msg); + } + + break; + } + } + } +} diff --git a/src/components.h b/src/components.h new file mode 100644 index 0000000..687eab1 --- /dev/null +++ b/src/components.h @@ -0,0 +1,93 @@ +#ifndef COMPONENTS_H +#define COMPONENTS_H + +#include "entity.h" +#include +#include +#include "map.h" + +class UserMovementComponent : public Component { + public: + UserMovementComponent(Entity& parent) : Component(parent) {}; + void input(int key, int action); + + private: + bool holdingLeft = false; + bool holdingRight = false; +}; + +class PhysicsBodyComponent : public Component, public Collidable, public Locatable { + public: + PhysicsBodyComponent(Entity& parent) : Component(parent) {}; + void receive(message_t msg); + void tick(); + void detectCollision(Entity& player, Locatable& physics, std::pair old_position); +}; + +class PlayerSpriteComponent : public Component { + public: + PlayerSpriteComponent(Entity& parent, Locatable& physics); + ~PlayerSpriteComponent(); + void render(Texture* buffer); + void receive(message_t msg); + void tick(); + + private: + Locatable& physics; + Texture* sprite; + int animFrame = 0; + bool facingLeft = false; + bool isMoving = false; +}; + +class PlayerPhysicsComponent : public Component, public Locatable { + public: + PlayerPhysicsComponent(Entity& parent); + void tick(); + void receive(message_t msg); + + private: + double jump_velocity; + double jump_gravity; + double jump_gravity_short; + int direction = 0; + bool canDrop = false; +}; + +class MapRenderComponent : public Component { + public: + MapRenderComponent(Entity& parent, Map* map); + ~MapRenderComponent(); + void render(Texture* buffer); + + private: + Texture* screen; +}; + +enum direction_t { + up, left, down, right +}; + +typedef struct { + int axis; + int lower; + int upper; + int type; +} collision_t; + +class MapCollisionComponent : public Component, public Collidable { + public: + MapCollisionComponent(Entity& parent, Map* map); + void detectCollision(Entity& player, Locatable& physics, std::pair old_position); + + private: + void add_collision(int axis, int lower, int upper, direction_t dir, int type); + + std::list left_collisions; + std::list right_collisions; + std::list up_collisions; + std::list down_collisions; + Map* map; +}; + +#endif diff --git a/src/entity.cpp b/src/entity.cpp new file mode 100644 index 0000000..405de24 --- /dev/null +++ b/src/entity.cpp @@ -0,0 +1,38 @@ +#include "entity.h" + +void Entity::addComponent(std::shared_ptr c) +{ + components.push_back(c); +} + +void Entity::send(message_t msg) +{ + for (auto it = components.begin(); it != components.end(); it++) + { + (*it)->receive(msg); + } +} + +void Entity::tick() +{ + for (auto it = components.begin(); it != components.end(); it++) + { + (*it)->tick(); + } +} + +void Entity::input(int key, int action) +{ + for (auto it = components.begin(); it != components.end(); it++) + { + (*it)->input(key, action); + } +} + +void Entity::render(Texture* buffer) +{ + for (auto it = components.begin(); it != components.end(); it++) + { + (*it)->render(buffer); + } +} diff --git a/src/entity.h b/src/entity.h new file mode 100644 index 0000000..5a37147 --- /dev/null +++ b/src/entity.h @@ -0,0 +1,72 @@ +#ifndef ENTITY_H +#define ENTITY_H + +class Entity; +class Component; +class Locatable; +class Collidable; + +#include +#include "renderer.h" +#include "world.h" + +enum message_type { + CM_WALK_LEFT, + CM_WALK_RIGHT, + CM_STOP_WALKING, + CM_COLLISION, + CM_JUMP, + CM_STOP_JUMP, + CM_DROP, + CM_CAN_DROP, + CM_CANT_DROP +}; + +typedef struct { + message_type type; + Entity* collisionEntity; + int dropAxis; +} message_t; + +class Entity { + public: + Entity(World* world) : world(world) {} + ~Entity() {}; + void addComponent(std::shared_ptr c); + void send(message_t msg); + void tick(); + void input(int key, int action); + void render(Texture* buffer); + + World* world; + + private: + std::list> components; +}; + +class Component { + public: + Component(Entity& entity) : entity(entity) {} + virtual ~Component() {}; + virtual void receive(message_t msg) {(void)msg;} + virtual void render(Texture* tex) {(void)tex;} + virtual void tick() {} + virtual void input(int key, int action) {(void)key; (void)action;} + + Entity& entity; +}; + +class Locatable { + public: + std::pair position; + std::pair size; + std::pair velocity; + std::pair accel; +}; + +class Collidable { + public: + virtual void detectCollision(Entity& player, Locatable& physics, std::pair old_position) = 0; +}; + +#endif diff --git a/src/game.cpp b/src/game.cpp new file mode 100644 index 0000000..2db0a2c --- /dev/null +++ b/src/game.cpp @@ -0,0 +1,124 @@ +#include "game.h" +#include "renderer.h" + +Game::Game() +{ + window = initRenderer(); + glfwSwapInterval(1); + + m = new Map("../maps/embarass.txt"); + m2 = new Map("../maps/second.txt"); + + m->setLeftMap(m2); + m2->setRightMap(m); + + world = new World(); + + auto player = std::make_shared(world); + + auto player_input = std::make_shared(*player); + player->addComponent(player_input); + + auto player_physics = std::make_shared(*player); + player_physics->position = std::make_pair(100.0,100.0); + player_physics->size = std::make_pair(10.0,12.0); + player->addComponent(player_physics); + + auto player_anim = std::make_shared(*player, *player_physics); + player->addComponent(player_anim); + + world->addEntity(player); + world->player = player; + + loadMap(m); +} + +Game::~Game() +{ + if (world != 0) + { + delete world; + } + + if (nextWorld != 0) + { + delete nextWorld; + } + + delete m; + delete m2; + + destroyRenderer(); +} + +void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + (void)window; + (void)scancode; + (void)mods; + + if ((key == GLFW_KEY_ESCAPE) && (action == GLFW_PRESS)) + { + Game::getInstance().shouldQuit = true; + } + + Game::getInstance().input(key, action); +} + +void Game::execute() +{ + glfwSetKeyCallback(window, key_callback); + + Texture* buffer = createTexture(GAME_WIDTH, GAME_HEIGHT); + + while (!(shouldQuit || glfwWindowShouldClose(window))) + { + // Should we load a new world? + if (nextWorld != 0) + { + delete world; + world = nextWorld; + world->player->world = world; + nextWorld = 0; + } + + // Handle input + glfwPollEvents(); + + // Tick! + world->tick(); + + // Do rendering + world->render(buffer); + renderScreen(buffer); + } + + destroyTexture(buffer); +} + +void Game::input(int key, int action) +{ + if (world != NULL) + { + world->input(key, action); + } +} + +void Game::loadMap(Map* map) +{ + nextWorld = new World(); + + nextWorld->player = world->player; + + auto mapEn = std::make_shared(nextWorld); + + auto map_render = std::make_shared(*mapEn, map); + mapEn->addComponent(map_render); + + auto map_collision = std::make_shared(*mapEn, map); + mapEn->addComponent(map_collision); + nextWorld->bodies.push_back(map_collision.get()); + + nextWorld->addEntity(mapEn); + nextWorld->addEntity(nextWorld->player); +} diff --git a/src/game.h b/src/game.h new file mode 100644 index 0000000..69224dc --- /dev/null +++ b/src/game.h @@ -0,0 +1,43 @@ +#ifndef GAME_H +#define GAME_H + +#include "components.h" + +const int TILE_WIDTH = 8; +const int TILE_HEIGHT = 8; +const int GAME_WIDTH = 320; +const int GAME_HEIGHT = 200; +const int MAP_WIDTH = GAME_WIDTH/TILE_WIDTH; +const int MAP_HEIGHT = GAME_HEIGHT/TILE_HEIGHT; + +const int FRAMES_PER_SECOND = 60; +const double SECONDS_PER_FRAME = 1.0 / FRAMES_PER_SECOND; + +class Game { + public: + static Game& getInstance() + { + static Game instance; + + return instance; + } + + ~Game(); + void execute(); + void loadMap(Map* map); + void input(int key, int action); + + bool shouldQuit = false; + private: + Game(); + Game(Game const&); + void operator=(Game const&); + + GLFWwindow* window; + World* world; + World* nextWorld; + Map* m; + Map* m2; +}; + +#endif diff --git a/src/main.cpp b/src/main.cpp index bc7832d..c552d2a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,77 +1,14 @@ #include #include -#include "map.h" -#include "state.h" -#include "mapview.h" #include "renderer.h" #include - -using namespace::std; - -bool quit = false; - -State* curGameState; - -void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) -{ - if ((key == GLFW_KEY_ESCAPE) && (action == GLFW_PRESS)) - { - quit = true; - } - - if (curGameState != NULL) - { - curGameState->input(key, action); - } -} +#include "game.h" int main() { srand(time(NULL)); - GLFWwindow* window = initRenderer(); - glfwSwapInterval(1); - glfwSetKeyCallback(window, key_callback); - - Map* m = new Map("../maps/embarass.txt"); - //Map* m2 = new Map("../maps/cozy.txt"); - - //m->setLeftMap(m2); - //m2->setRightMap(m); - - curGameState = new MapView(m, 100, 100); - - Texture* buffer = createTexture(GAME_WIDTH, GAME_HEIGHT); - //Texture* buffer = loadTextureFromBMP("../res/title.png"); - - double lastTime = glfwGetTime(); - int nbFrames = 0; - - while (!(quit || glfwWindowShouldClose(window))) - { - double currentTime = glfwGetTime(); - nbFrames++; - if (currentTime - lastTime >= 1.0) - { - // printf and reset timer - printf("%f ms/frame\n", 1000.0/double(nbFrames)); - nbFrames = 0; - lastTime += 1.0; - } - - curGameState->tick(); - - // Do rendering - curGameState->render(buffer); - renderScreen(buffer); - - glfwPollEvents(); - } - - delete curGameState; - delete m; - - destroyRenderer(); + Game::getInstance().execute(); return 0; } diff --git a/src/map.cpp b/src/map.cpp index 5f9e7bd..fda8009 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -1,5 +1,5 @@ #include "map.h" -#include "mapview.h" +#include "game.h" Map::Map(char* filename) { diff --git a/src/mapview.cpp b/src/mapview.cpp deleted file mode 100644 index a85bb31..0000000 --- a/src/mapview.cpp +++ /dev/null @@ -1,393 +0,0 @@ -#include "mapview.h" - -#define JUMP_VELOCITY(h, l) (-2 * (h) / (l)) -#define JUMP_GRAVITY(h, l) (2 * ((h) / (l)) / (l)) - -// Initialize jump physics -double jump_height = TILE_HEIGHT*4; -double jump_length = 0.25 * FRAMES_PER_SECOND; -//double jump_velocity = -2 * jump_height / jump_length; -//double jump_gravity = -1 * jump_velocity / jump_length; -double jump_velocity = JUMP_VELOCITY(TILE_HEIGHT*4, 0.30*FRAMES_PER_SECOND); -double jump_gravity = JUMP_GRAVITY(TILE_HEIGHT*4, 0.30*FRAMES_PER_SECOND); -double jump_gravity_short = JUMP_GRAVITY(TILE_HEIGHT*3, 0.20*FRAMES_PER_SECOND); - -MapView::MapView(Map* first, int x, int y) -{ - // Initialize player data - player = new mob_t(); - player->x = x; - player->y = y; - player->x_vel = 0; - player->y_vel = 0; - player->y_accel = jump_gravity_short; - player->x_accel = 0; - player->w = 10; - player->h = 12; - player->onGround = false; - player->animFrame = 0; - - bg = createTexture(GAME_WIDTH, GAME_HEIGHT); - chara = loadTextureFromBMP("../res/Starla.png"); - tiles = loadTextureFromBMP("../res/tiles.png"); - - loadMap(first); -} - -MapView::~MapView() -{ - destroyTexture(bg); - destroyTexture(chara); - destroyTexture(tiles); - - delete player; -} - -void MapView::loadMap(Map* m) -{ - curMap = m; - - left_collisions.clear(); - right_collisions.clear(); - up_collisions.clear(); - down_collisions.clear(); - - add_collision(-6, 0, GAME_WIDTH, left, (m->getLeftMap() == NULL) ? 1 : 2); - add_collision(GAME_WIDTH+6, 0, GAME_WIDTH, right, (m->getRightMap() == NULL) ? 1 : 2); - - fillTexture(bg, NULL, 0, 0, 0); - - const int* mapbuf = m->mapdata(); - - for (int i=0; i 0) - { - blitTexture(tiles, bg, &src, &dst); - } - //blitTexture(tiles, bg, &src, &dst); - - if ((mapbuf[i] > 0) && (!((mapbuf[i] >= 5) && (mapbuf[i] <= 7)))) - { - //if ((x != 0) && (mapbuf[i-1] != 'X')) - { - add_collision(x*TILE_WIDTH, y*TILE_HEIGHT, (y+1)*TILE_HEIGHT, right, 0); - } - - //if ((x != 39) && (mapbuf[i+1] != 'X')) - { - add_collision((x+1)*TILE_WIDTH, y*TILE_HEIGHT, (y+1)*TILE_HEIGHT, left, 0); - } - - //if ((y != 0) && (mapbuf[i-MAP_WIDTH] != 'X')) - { - add_collision(y*TILE_HEIGHT, x*TILE_WIDTH, (x+1)*TILE_WIDTH, down, 0); - } - - //if ((y != 23) && (mapbuf[i+MAP_WIDTH] != 'X')) - { - add_collision((y+1)*TILE_HEIGHT, x*TILE_WIDTH, (x+1)*TILE_WIDTH, up, 0); - } - } else if ((mapbuf[i] >= 5) && (mapbuf[i] <= 7)) - { - add_collision(y*TILE_HEIGHT, x*TILE_WIDTH, (x+1)*TILE_WIDTH, down, 3); - } - } - - Texture* font = loadTextureFromBMP("../res/font.bmp"); - const char* map_name = m->title(); - int start_x = (40/2) - (strlen(map_name)/2); - for (size_t i=0; ionGround) - { - player->y_vel = jump_velocity; - player->onGround = false; - holding_up = true; - } - break; - case GLFW_KEY_DOWN: - holding_down = true; - break; - } - } else if (action == GLFW_RELEASE) - { - switch (key) - { - case GLFW_KEY_LEFT: - holding_left = false; - if (!holding_right) player->animFrame = 1; - break; - case GLFW_KEY_RIGHT: - holding_right = false; - if (!holding_left) player->animFrame = 0; - break; - case GLFW_KEY_DOWN: - holding_down = false; - break; - case GLFW_KEY_UP: - holding_up = false; - break; - } - } -} - -void MapView::tick() -{ - if (holding_left && player->x_vel >= 0) - { - player->x_vel = -2; - } else if (holding_right && player->x_vel <= 0) - { - player->x_vel = 2; - } else if (!holding_left && !holding_right) { - player->x_vel = 0; - } - - player->x_vel += player->x_accel; - if (player->x_vel < -16) player->x_vel = -16; - if (player->x_vel > 16) player->x_vel = 16; - int playerx_next = player->x + player->x_vel; - - if (holding_up) - { - player->y_vel += jump_gravity; - } else { - player->y_vel += jump_gravity_short; - } - - if (player->y_vel > 16) player->y_vel = 16; // Terminal velocity - if (player->y_vel < -16) player->y_vel = -16; - int playery_next = player->y + player->y_vel; - - check_collisions(player, playerx_next, playery_next); -} - -void MapView::render(Texture* tex) -{ - if (animFrame == 0) - { - if (holding_left) - { - if (player->animFrame == 3) - { - player->animFrame = 5; - } else { - player->animFrame = 3; - } - } else if (holding_right) - { - if (player->animFrame == 2) - { - player->animFrame = 4; - } else { - player->animFrame = 2; - } - } - } - - animFrame++; - animFrame %= 10; - - // Draw the background - blitTexture(bg, tex, NULL, NULL); - - // Draw the player - Rectangle src_rect(player->animFrame * 10, 0, 10, 12); - Rectangle dst_rect(player->x, player->y, player->w, player->h); - blitTexture(chara, tex, &src_rect, &dst_rect); -} - -void MapView::add_collision(int axis, int lower, int upper, direction_t dir, int type) -{ - //printf("added collision\n"); - list::iterator it; - - switch (dir) - { - case up: - it = up_collisions.begin(); - for (; it!=up_collisions.end(); it++) - { - if (it->axis < axis) break; - } - - up_collisions.insert(it, {axis, lower, upper, type}); - - break; - case down: - it = down_collisions.begin(); - for (; it!=down_collisions.end(); it++) - { - if (it->axis > axis) break; - } - - down_collisions.insert(it, {axis, lower, upper, type}); - - break; - case left: - it = left_collisions.begin(); - for (; it!=left_collisions.end(); it++) - { - if (it->axis < axis) break; - } - - left_collisions.insert(it, {axis, lower, upper, type}); - - break; - case right: - it = right_collisions.begin(); - for (; it!=right_collisions.end(); it++) - { - if (it->axis > axis) break; - } - - right_collisions.insert(it, {axis, lower, upper, type}); - - break; - } -} - -void MapView::check_collisions(mob_t* mob, int x_next, int y_next) -{ - if (x_next < mob->x) - { - for (list::iterator it=left_collisions.begin(); it!=left_collisions.end(); it++) - { - if (it->axis > mob->x) continue; - if (it->axis < x_next) break; - - if ((mob->y+mob->h > it->lower) && (mob->y < it->upper)) - { - // We have a collision! - if (it->type == 0) - { - x_next = it->axis; - mob->x_vel = 0; - } else if (it->type == 1) - { - x_next = GAME_WIDTH-mob->w/2; - } else if (it->type == 2) - { - x_next = GAME_WIDTH-mob->w/2; - loadMap(curMap->getLeftMap()); - } - - break; - } - } - } else if (x_next > mob->x) - { - for (list::iterator it=right_collisions.begin(); it!=right_collisions.end(); it++) - { - if (it->axis < mob->x+mob->w) continue; - if (it->axis > x_next+mob->w) break; - - if ((mob->y+mob->h > it->lower) && (mob->y < it->upper)) - { - // We have a collision! - if (it->type == 0) - { - x_next = it->axis - mob->w; - mob->x_vel = 0; - } else if (it->type == 1) - { - x_next = -mob->w/2; - } else if (it->type == 2) - { - x_next = -mob->w/2; - loadMap(curMap->getRightMap()); - } - - break; - } - } - } - - mob->x = x_next; - - if (y_next < mob->y) - { - for (list::iterator it=up_collisions.begin(); it!=up_collisions.end(); it++) - { - if (it->axis > mob->y) continue; - if (it->axis < y_next) break; - - if ((mob->x+mob->w > it->lower) && (mob->x < it->upper)) - { - // We have a collision! - if (it->type == 0) - { - y_next = it->axis; - mob->y_vel = 0; - } else if (it->type == 1) - { - y_next = GAME_HEIGHT-mob->h/2-1; - } - - break; - } - } - } else if (y_next > mob->y) - { - for (list::iterator it=down_collisions.begin(); it!=down_collisions.end(); it++) - { - if (it->axis < mob->y+mob->h) continue; - if (it->axis > y_next+mob->h) break; - - if ((mob->x+mob->w > it->lower) && (mob->x < it->upper)) - { - // We have a collision! - if (it->type == 0) - { - y_next = it->axis - mob->h; - mob->y_vel = 0; - mob->onGround = true; - } else if (it->type == 1) - { - y_next = 1 - mob->h/2; - } else if (it->type == 3) - { - if (holding_down) - { - holding_down = false; - } else { - y_next = it->axis - mob->h; - mob->y_vel = 0; - mob->onGround = true; - } - } - - break; - } - } - } - - mob->y = y_next; -} diff --git a/src/mapview.h b/src/mapview.h deleted file mode 100644 index 505ab25..0000000 --- a/src/mapview.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef MAPVIEW_H -#define MAPVIEW_H - -#include -#include "state.h" -#include "mob.h" -#include "map.h" - -using namespace::std; - -const int TILE_WIDTH = 8; -const int TILE_HEIGHT = 8; -const int GAME_WIDTH = 320; -const int GAME_HEIGHT = 200; -const int MAP_WIDTH = GAME_WIDTH/TILE_WIDTH; -const int MAP_HEIGHT = GAME_HEIGHT/TILE_HEIGHT; - -const int FRAMES_PER_SECOND = 60; -const double SECONDS_PER_FRAME = 1.0 / FRAMES_PER_SECOND; - -enum direction_t { - up, left, down, right -}; - -typedef struct { - int axis; - int lower; - int upper; - int type; -} collision_t; - -class MapView : public State { - public: - MapView(Map* start, int x, int y); - ~MapView(); - void loadMap(Map* m); - void input(int key, int action); - void tick(); - void render(Texture* tex); - - private: - void add_collision(int axis, int lower, int upper, direction_t dir, int type); - void check_collisions(mob_t* mob, int x_next, int y_next); - - list left_collisions; - list right_collisions; - list up_collisions; - list down_collisions; - - Texture* bg = NULL; - Texture* chara; - Texture* tiles; - - bool holding_left = false; - bool holding_right = false; - bool holding_down = false; - bool holding_up = false; - mob_t* player; - - Map* curMap; - - int animFrame = 0; -}; - -#endif diff --git a/src/mob.h b/src/mob.h deleted file mode 100644 index 213b062..0000000 --- a/src/mob.h +++ /dev/null @@ -1,12 +0,0 @@ -typedef struct { - int x; - int y; - double x_vel; - double y_vel; - double x_accel; - double y_accel; - int w; - int h; - bool onGround; - int animFrame; -} mob_t; diff --git a/src/renderer.cpp b/src/renderer.cpp index 72de8a9..e7db069 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -4,8 +4,8 @@ #include #include #include -#include "mapview.h" #include +#include "game.h" // include stb_image #define STB_IMAGE_IMPLEMENTATION @@ -405,7 +405,7 @@ GLFWwindow* initRenderer() glGenTextures(1, &scanlinesTex); glBindTexture(GL_TEXTURE_2D, scanlinesTex); int stdw, stdh; - unsigned char* scanlinesTex_data = stbi_load("../res/scanlines_222.bmp", &stdw, &stdh, 0, 3); + unsigned char* scanlinesTex_data = stbi_load("../res/scanlines_333.bmp", &stdw, &stdh, 0, 3); flipImageData(scanlinesTex_data, stdw, stdh, 3); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, atdw, atdh, 0, GL_RGB, GL_UNSIGNED_BYTE, scanlinesTex_data); stbi_image_free(scanlinesTex_data); @@ -507,7 +507,7 @@ void destroyTexture(Texture* tex) delete tex; } -Texture* loadTextureFromBMP(char* filename) +Texture* loadTextureFromFile(char* filename) { if (!rendererInitialized) { diff --git a/src/renderer.h b/src/renderer.h index 144956a..a4e21f7 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -35,7 +35,7 @@ GLFWwindow* initRenderer(); void destroyRenderer(); Texture* createTexture(int width, int height); void destroyTexture(Texture* tex); -Texture* loadTextureFromBMP(char* filename); +Texture* loadTextureFromFile(char* filename); void saveTextureToBMP(Texture* tex, char* filename); void fillTexture(Texture* tex, Rectangle* loc, int r, int g, int b); void blitTexture(Texture* srctex, Texture* dsttex, Rectangle* srcrect, Rectangle* dstrect); diff --git a/src/state.h b/src/state.h deleted file mode 100644 index e7962ec..0000000 --- a/src/state.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef STATE_H -#define STATE_H - -#include "renderer.h" - -class State { -public: - virtual void input(int key, int action) = 0; - virtual void tick() = 0; - virtual void render(Texture* tex) = 0; -}; - -#endif diff --git a/src/world.cpp b/src/world.cpp new file mode 100644 index 0000000..90d9ab8 --- /dev/null +++ b/src/world.cpp @@ -0,0 +1,32 @@ +#include "world.h" + +void World::tick() +{ + for (auto it = entities.begin(); it != entities.end(); it++) + { + (*it)->tick(); + } +} + +void World::input(int key, int action) +{ + for (auto it = entities.begin(); it != entities.end(); it++) + { + (*it)->input(key, action); + } +} + +void World::render(Texture* buffer) +{ + fillTexture(buffer, NULL, 0, 0, 0); + + for (auto it = entities.begin(); it != entities.end(); it++) + { + (*it)->render(buffer); + } +} + +void World::addEntity(std::shared_ptr e) +{ + entities.push_back(e); +} diff --git a/src/world.h b/src/world.h new file mode 100644 index 0000000..ad6c788 --- /dev/null +++ b/src/world.h @@ -0,0 +1,27 @@ +#ifndef WORLD_H +#define WORLD_H + +class World; + +#include +#include "renderer.h" +#include "entity.h" +#include + +class World { + public: + World() {}; + ~World() {}; + void tick(); + void input(int key, int action); + void render(Texture* buffer); + void addEntity(std::shared_ptr e); + + std::list bodies; + std::shared_ptr player; + + private: + std::list> entities; +}; + +#endif -- cgit 1.4.1