From 6b1dcc5df51df4a2d8b724187eb1bcdb4fd9df8b Mon Sep 17 00:00:00 2001 From: Kelly Rauchenberger Date: Sat, 14 Mar 2015 19:25:23 -0400 Subject: Added sound when you hit the ground Also split up components.cpp into files for each class, fixed a bug concerning falling off the screen when you change maps, and converted collision data into doubles. --- CMakeLists.txt | 19 +- src/components.cpp | 630 ------------------------------------- src/components.h | 125 -------- src/components/map_collision.cpp | 211 +++++++++++++ src/components/map_collision.h | 46 +++ src/components/map_render.cpp | 39 +++ src/components/map_render.h | 19 ++ src/components/physics_body.cpp | 65 ++++ src/components/physics_body.h | 20 ++ src/components/player_physics.cpp | 117 +++++++ src/components/player_physics.h | 25 ++ src/components/player_sprite.cpp | 60 ++++ src/components/player_sprite.h | 23 ++ src/components/simple_collider.cpp | 14 + src/components/simple_collider.h | 18 ++ src/components/static_image.cpp | 11 + src/components/static_image.h | 18 ++ src/components/user_movement.cpp | 100 ++++++ src/components/user_movement.h | 19 ++ src/entity.cpp | 3 - src/entity.h | 3 +- src/entityfactory.cpp | 5 +- src/game.cpp | 19 +- src/game.h | 3 +- 24 files changed, 844 insertions(+), 768 deletions(-) delete mode 100644 src/components.cpp delete mode 100644 src/components.h create mode 100644 src/components/map_collision.cpp create mode 100644 src/components/map_collision.h create mode 100644 src/components/map_render.cpp create mode 100644 src/components/map_render.h create mode 100644 src/components/physics_body.cpp create mode 100644 src/components/physics_body.h create mode 100644 src/components/player_physics.cpp create mode 100644 src/components/player_physics.h create mode 100644 src/components/player_sprite.cpp create mode 100644 src/components/player_sprite.h create mode 100644 src/components/simple_collider.cpp create mode 100644 src/components/simple_collider.h create mode 100644 src/components/static_image.cpp create mode 100644 src/components/static_image.h create mode 100644 src/components/user_movement.cpp create mode 100644 src/components/user_movement.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e86cd95..7c9084e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,10 +40,27 @@ set(ALL_LIBS include_directories( ${LIBXML2_INCLUDE_DIR} + src ) # include_directories(${SDL2_INCLUDE_DIR}) set(CMAKE_BUILD_TYPE Debug) -add_executable(Aromatherapy src/main.cpp src/map.cpp src/renderer.cpp src/entity.cpp src/components.cpp src/game.cpp src/muxer.cpp src/entityfactory.cpp) +add_executable(Aromatherapy + src/main.cpp + src/map.cpp + src/renderer.cpp + src/entity.cpp + src/game.cpp + src/muxer.cpp + src/entityfactory.cpp + src/components/map_collision.cpp + src/components/map_render.cpp + src/components/physics_body.cpp + src/components/player_physics.cpp + src/components/player_sprite.cpp + src/components/simple_collider.cpp + src/components/static_image.cpp + src/components/user_movement.cpp +) target_link_libraries(Aromatherapy ${ALL_LIBS}) install(TARGETS Aromatherapy RUNTIME DESTINATION ${BIN_DIR}) diff --git a/src/components.cpp b/src/components.cpp deleted file mode 100644 index 512fbed..0000000 --- a/src/components.cpp +++ /dev/null @@ -1,630 +0,0 @@ -#include "components.h" -#include "game.h" -#include "muxer.h" -#include "map.h" - -// User movement component - -void UserMovementComponent::input(Game& game, Entity& entity, int key, int action) -{ - if (action == GLFW_PRESS) - { - if (key == GLFW_KEY_LEFT) - { - holdingLeft = true; - - if (!frozen) - { - entity.send(game, Message::Type::walkLeft); - } - } else if (key == GLFW_KEY_RIGHT) - { - holdingRight = true; - - if (!frozen) - { - entity.send(game, Message::Type::walkRight); - } - } else if (key == GLFW_KEY_UP) - { - if (!frozen) - { - entity.send(game, Message::Type::jump); - } - } else if (key == GLFW_KEY_DOWN) - { - if (!frozen) - { - entity.send(game, Message::Type::canDrop); - } - } - } else if (action == GLFW_RELEASE) - { - if (key == GLFW_KEY_LEFT) - { - holdingLeft = false; - - if (!frozen) - { - if (holdingRight) - { - entity.send(game, Message::Type::walkRight); - } else { - entity.send(game, Message::Type::stopWalking); - } - } - } else if (key == GLFW_KEY_RIGHT) - { - holdingRight = false; - - if (!frozen) - { - if (holdingLeft) - { - entity.send(game, Message::Type::walkLeft); - } else { - entity.send(game, Message::Type::stopWalking); - } - } - } else if (key == GLFW_KEY_DOWN) - { - if (!frozen) - { - entity.send(game, Message::Type::cantDrop); - } - } else if (key == GLFW_KEY_UP) - { - if (!frozen) - { - entity.send(game, Message::Type::stopJump); - } - } - } -} - -void UserMovementComponent::receive(Game& game, Entity& entity, const Message& msg) -{ - if (msg.type == Message::Type::die) - { - frozen = true; - - entity.send(game, Message::Type::stopWalking); - } else if (msg.type == Message::Type::stopDying) - { - frozen = false; - - if (holdingLeft) - { - entity.send(game, Message::Type::walkLeft); - } else if (holdingRight) - { - entity.send(game, Message::Type::walkRight); - } - } -} - -// Physics component - -void PhysicsBodyComponent::receive(Game&, Entity&, const Message& msg) -{ - if (msg.type == Message::Type::walkLeft) - { - velocity.first = -90; - } else if (msg.type == Message::Type::walkRight) - { - velocity.first = 90; - } else if (msg.type == Message::Type::stopWalking) - { - velocity.first = 0.0; - } else if (msg.type == Message::Type::stopMovingHorizontally) - { - velocity.first = 0.0; - } else if (msg.type == Message::Type::stopMovingVertically) - { - velocity.second = 0.0; - } -} - -void PhysicsBodyComponent::tick(Game&, Entity& entity, double dt) -{ - // Accelerate - velocity.first += accel.first * dt; - velocity.second += accel.second * dt; - - // Terminal velocity -#define TERMINAL_VELOCITY_X (2 * TILE_WIDTH * FRAMES_PER_SECOND) -#define TERMINAL_VELOCITY_Y (2 * TILE_HEIGHT * FRAMES_PER_SECOND) - if (velocity.first < -TERMINAL_VELOCITY_X) velocity.first = -TERMINAL_VELOCITY_X; - if (velocity.first > TERMINAL_VELOCITY_X) velocity.first = TERMINAL_VELOCITY_X; - if (velocity.second < -TERMINAL_VELOCITY_Y) velocity.second = -TERMINAL_VELOCITY_Y; - if (velocity.second > TERMINAL_VELOCITY_Y) velocity.second = TERMINAL_VELOCITY_Y; - - // Do the movement - entity.position.first += velocity.first * dt; - entity.position.second += velocity.second * dt; -} - -void PhysicsBodyComponent::detectCollision(Game& game, Entity& entity, Entity& collider, std::pair old_position) -{ - // If already colliding, do nothing! - if ((old_position.first + collider.size.first > entity.position.first) - && (old_position.first < entity.position.first + entity.size.first) - && (old_position.second + collider.size.second > entity.position.second) - && (old_position.second < entity.position.second + entity.size.second)) - { - return; - } - - // If newly colliding, SHOCK AND HORROR! - if ((collider.position.first + collider.size.first > entity.position.first) - && (collider.position.first < entity.position.first + entity.size.first) - && (collider.position.second + collider.size.second > entity.position.second) - && (collider.position.second < entity.position.second + entity.size.second)) - { - Message msg(Message::Type::collision); - msg.collisionEntity = &collider; - - entity.send(game, msg); - } -} - -// Render player - -void PlayerSpriteComponent::render(Game&, Entity& entity, Texture& buffer) -{ - animFrame++; - - int frame = 0; - if (isMoving) - { - frame += 2; - - if (animFrame % 20 < 10) - { - frame += 2; - } - } - - if (facingLeft) - { - frame++; - } - - double alpha = 1.0; - if (dying && (animFrame % 4 < 2)) - { - alpha = 0.0; - } - - Rectangle src_rect {frame*10, 0, 10, 12}; - Rectangle dst_rect {(int) entity.position.first, (int) entity.position.second, entity.size.first, entity.size.second}; - buffer.blit(sprite, src_rect, dst_rect, alpha); -} - -void PlayerSpriteComponent::receive(Game&, Entity&, const Message& msg) -{ - if (msg.type == Message::Type::walkLeft) - { - facingLeft = true; - isMoving = true; - } else if (msg.type == Message::Type::walkRight) - { - facingLeft = false; - isMoving = true; - } else if (msg.type == Message::Type::stopWalking) - { - isMoving = false; - } else if (msg.type == Message::Type::die) - { - dying = true; - isMoving = false; - } else if (msg.type == Message::Type::stopDying) - { - dying = false; - } -} - -// Player physics - -#define JUMP_VELOCITY(h, l) (-2 * (h) / (l)) -#define JUMP_GRAVITY(h, l) (2 * ((h) / (l)) / (l)) - -PlayerPhysicsComponent::PlayerPhysicsComponent() -{ - jump_velocity = JUMP_VELOCITY(TILE_HEIGHT*4.5, 0.3); - jump_gravity = JUMP_GRAVITY(TILE_HEIGHT*4.5, 0.3); - jump_gravity_short = JUMP_GRAVITY(TILE_HEIGHT*3.5, 0.233); - - accel.second = jump_gravity_short; -} - -void PlayerPhysicsComponent::receive(Game&, Entity& entity, const Message& msg) -{ - if (msg.type == Message::Type::walkLeft) - { - velocity.first = -90; - direction = -1; - } else if (msg.type == Message::Type::walkRight) - { - velocity.first = 90; - direction = 1; - } else if (msg.type == Message::Type::stopWalking) - { - velocity.first = 0.0; - direction = 0; - } else if (msg.type == Message::Type::stopMovingHorizontally) - { - velocity.first = 0.0; - } else if (msg.type == Message::Type::stopMovingVertically) - { - velocity.second = 0.0; - } else if (msg.type == Message::Type::jump) - { - playSound("../res/Randomize87.wav", 0.25); - - velocity.second = jump_velocity; - accel.second = jump_gravity; - } else if (msg.type == Message::Type::stopJump) - { - accel.second = jump_gravity_short; - } else if (msg.type == Message::Type::canDrop) - { - canDrop = true; - } else if (msg.type == Message::Type::cantDrop) - { - canDrop = false; - } else if (msg.type == Message::Type::drop) - { - if (canDrop) - { - canDrop = false; - } else { - entity.position.second = msg.dropAxis - entity.size.second; - velocity.second = 0; - } - } else if (msg.type == Message::Type::die) - { - frozen = true; - } else if (msg.type == Message::Type::stopDying) - { - frozen = false; - } -} - -void PlayerPhysicsComponent::tick(Game& game, Entity& entity, double dt) -{ - // If frozen, do nothing - if (frozen) - { - return; - } - - // Continue walking even if blocked earlier - if (velocity.first == 0) - { - if (direction < 0) - { - velocity.first = -90; - } else if (direction > 0) - { - velocity.first = 90; - } - } - - // Increase gravity at the height of jump - if ((accel.second == jump_gravity) && (velocity.second >= 0)) - { - accel.second = jump_gravity_short; - } - - // Do the movement - std::pair old_position = entity.position; - PhysicsBodyComponent::tick(game, entity, dt); - - // Check for collisions - game.detectCollision(entity, old_position); -} - -// Map rendering - -MapRenderComponent::MapRenderComponent(const Map& map) -{ - screen.fill(screen.entirety(), 0, 0, 0); - - Texture tiles("../res/tiles.png"); - - for (int i=0; i 0) - { - screen.blit(tiles, src, dst); - } - } - - Texture font("../res/font.bmp"); - const char* map_name = map.getTitle(); - int start_x = (40/2) - (strlen(map_name)/2); - for (size_t i=0; i 0) && (tile < 28) && (!((tile >= 5) && (tile <= 7)))) - { - addCollision(x*TILE_WIDTH, y*TILE_HEIGHT, (y+1)*TILE_HEIGHT, Direction::right, Collision::Type::wall); - addCollision((x+1)*TILE_WIDTH, y*TILE_HEIGHT, (y+1)*TILE_HEIGHT, Direction::left, Collision::Type::wall); - addCollision(y*TILE_HEIGHT, x*TILE_WIDTH, (x+1)*TILE_WIDTH, Direction::down, Collision::Type::wall); - addCollision((y+1)*TILE_HEIGHT, x*TILE_WIDTH, (x+1)*TILE_WIDTH, Direction::up, Collision::Type::wall); - } else if ((tile >= 5) && (tile <= 7)) - { - addCollision(y*TILE_HEIGHT, x*TILE_WIDTH, (x+1)*TILE_WIDTH, Direction::down, Collision::Type::platform); - } else if (tile == 42) - { - addCollision(y*TILE_HEIGHT, x*TILE_WIDTH, (x+1)*TILE_WIDTH, Direction::down, Collision::Type::danger); - } - } -} - -void MapCollisionComponent::addCollision(int axis, int lower, int upper, Direction dir, Collision::Type type) -{ - std::list::iterator it; - - switch (dir) - { - case Direction::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 Direction::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 Direction::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 Direction::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(Game& game, Entity&, Entity& collider, std::pair old_position) -{ - int fixed_x = (int) collider.position.first; - int fixed_y = (int) collider.position.second; - int fixed_ox = (int) old_position.first; - int fixed_oy = (int) old_position.second; - - if (fixed_x < fixed_ox) - { - for (auto collision : left_collisions) - { - if (collision.axis > fixed_ox) continue; - if (collision.axis < fixed_x) break; - - if ((fixed_oy+collider.size.second > collision.lower) && (fixed_oy < collision.upper)) - { - // We have a collision! - if (processCollision(game, collider, collision, Direction::left)) - { - collider.position.second = old_position.second; - - return; - } - - break; - } - } - } else if (fixed_x > fixed_ox) - { - for (auto collision : right_collisions) - { - if (collision.axis < fixed_ox+collider.size.first) continue; - if (collision.axis > fixed_x+collider.size.first) break; - - if ((fixed_oy+collider.size.second > collision.lower) && (fixed_oy < collision.upper)) - { - // We have a collision! - if (processCollision(game, collider, collision, Direction::right)) - { - collider.position.second = old_position.second; - - return; - } - - break; - } - } - } - - fixed_x = (int) collider.position.first; - fixed_y = (int) collider.position.second; - - if (fixed_y < fixed_oy) - { - for (auto collision : up_collisions) - { - if (collision.axis > fixed_oy) continue; - if (collision.axis < fixed_y) break; - - if ((fixed_x+collider.size.first > collision.lower) && (fixed_x < collision.upper)) - { - // We have a collision! - if (processCollision(game, collider, collision, Direction::up)) - { - return; - } - - break; - } - } - } else if (fixed_y > fixed_oy) - { - for (auto collision : down_collisions) - { - if (collision.axis < fixed_oy+collider.size.second) continue; - if (collision.axis > fixed_y+collider.size.second) break; - - if ((fixed_x+collider.size.first > collision.lower) && (fixed_x < collision.upper)) - { - // We have a collision! - if (processCollision(game, collider, collision, Direction::down)) - { - return; - } - - break; - } - } - } -} - -bool MapCollisionComponent::processCollision(Game& game, Entity& collider, Collision collision, Direction dir) -{ - if (collision.type == Collision::Type::wall) - { - if (dir == Direction::left) - { - collider.position.first = collision.axis; - collider.send(game, Message::Type::stopMovingHorizontally); - } else if (dir == Direction::right) - { - collider.position.first = collision.axis - collider.size.first; - collider.send(game, Message::Type::stopMovingHorizontally); - } else if (dir == Direction::up) - { - collider.position.second = collision.axis; - collider.send(game, Message::Type::stopMovingVertically); - } else if (dir == Direction::down) - { - collider.position.second = collision.axis - collider.size.second; - collider.send(game, Message::Type::stopMovingVertically); - } - } else if (collision.type == Collision::Type::wrap) - { - if (dir == Direction::left) - { - collider.position.first = GAME_WIDTH-collider.size.first/2; - } else if (dir == Direction::right) - { - collider.position.first = -collider.size.first/2; - } else if (dir == Direction::up) - { - collider.position.second = GAME_HEIGHT-collider.size.second/2-1; - } else if (dir == Direction::down) - { - collider.position.second = -collider.size.second/2; - } - } else if (collision.type == Collision::Type::teleport) - { - if (dir == Direction::left) - { - collider.position.first = GAME_WIDTH-collider.size.first/2; - game.loadMap(*map.getLeftMap()); - } else if (dir == Direction::right) - { - collider.position.first = -collider.size.first/2; - game.loadMap(*map.getRightMap()); - } - - return true; - } else if (collision.type == Collision::Type::reverse) - { - if (dir == Direction::right) - { - collider.position.first = collision.axis - collider.size.first; - collider.send(game, Message::Type::walkLeft); - } - } else if (collision.type == Collision::Type::platform) - { - Message msg(Message::Type::drop); - msg.dropAxis = collision.axis; - - collider.send(game, msg); - } else if (collision.type == Collision::Type::danger) - { - game.playerDie(); - } - - return false; -} - -// Static image - -StaticImageComponent::StaticImageComponent(const char* filename) : sprite(Texture(filename)) -{ - -} - -void StaticImageComponent::render(Game&, Entity& entity, Texture& buffer) -{ - buffer.blit(sprite, sprite.entirety(), {(int) entity.position.first, (int) entity.position.second, entity.size.first, entity.size.second}); -} - -// Simple collision - -SimpleColliderComponent::SimpleColliderComponent(std::function callback) : callback(callback) -{ - -} - -void SimpleColliderComponent::receive(Game& game, Entity&, const Message& msg) -{ - if (msg.type == Message::Type::collision) - { - callback(game, *msg.collisionEntity); - } -} diff --git a/src/components.h b/src/components.h deleted file mode 100644 index e0c4a24..0000000 --- a/src/components.h +++ /dev/null @@ -1,125 +0,0 @@ -#ifndef COMPONENTS_H -#define COMPONENTS_H - -#include -#include -#include "renderer.h" -#include "entity.h" -#include "game.h" - -class Map; - -class UserMovementComponent : public Component { - public: - void input(Game& game, Entity& entity, int key, int action); - void receive(Game&, Entity&, const Message& msg); - - private: - bool holdingLeft = false; - bool holdingRight = false; - bool frozen = false; -}; - -class PhysicsBodyComponent : public Component { - public: - void receive(Game& game, Entity& entity, const Message& msg); - void tick(Game& game, Entity& entity, double dt); - void detectCollision(Game& game, Entity& entity, Entity& collider, std::pair old_position); - - protected: - std::pair velocity; - std::pair accel; -}; - -class PlayerSpriteComponent : public Component { - public: - void render(Game& game, Entity& entity, Texture& buffer); - void receive(Game& game, Entity& entity, const Message& msg); - - private: - Texture sprite {"../res/Starla.png"}; - int animFrame = 0; - bool facingLeft = false; - bool isMoving = false; - bool dying = false; -}; - -class PlayerPhysicsComponent : public PhysicsBodyComponent { - public: - PlayerPhysicsComponent(); - void tick(Game& game, Entity& entity, double dt); - void receive(Game& game, Entity& entity, const Message& msg); - - private: - double jump_velocity; - double jump_gravity; - double jump_gravity_short; - int direction = 0; - bool canDrop = false; - bool frozen = false; -}; - -class MapRenderComponent : public Component { - public: - MapRenderComponent(const Map& map); - void render(Game& game, Entity& entity, Texture& buffer); - - private: - Texture screen{GAME_WIDTH, GAME_HEIGHT}; -}; - -class MapCollisionComponent : public Component { - public: - MapCollisionComponent(const Map& map); - void detectCollision(Game& game, Entity& entity, Entity& collider, std::pair old_position); - - private: - enum class Direction { - up, left, down, right - }; - - struct Collision { - enum class Type { - wall, - wrap, - teleport, - reverse, - platform, - danger - }; - - int axis; - int lower; - int upper; - Type type; - }; - - void addCollision(int axis, int lower, int upper, Direction dir, Collision::Type type); - bool processCollision(Game& game, Entity& collider, Collision collision, Direction dir); - - std::list left_collisions; - std::list right_collisions; - std::list up_collisions; - std::list down_collisions; - const Map& map; -}; - -class StaticImageComponent : public Component { - public: - StaticImageComponent(const char* filename); - void render(Game& game, Entity& entity, Texture& buffer); - - private: - Texture sprite; -}; - -class SimpleColliderComponent : public Component { - public: - SimpleColliderComponent(std::function callback); - void receive(Game& game, Entity& entity, const Message& msg); - - private: - std::function callback; -}; - -#endif diff --git a/src/components/map_collision.cpp b/src/components/map_collision.cpp new file mode 100644 index 0000000..f385320 --- /dev/null +++ b/src/components/map_collision.cpp @@ -0,0 +1,211 @@ +#include "map_collision.h" +#include "map.h" +#include "game.h" + +MapCollisionComponent::MapCollisionComponent(const Map& map) : map(map) +{ + addCollision(-6, 0, GAME_WIDTH, Direction::left, (map.getLeftMap() == nullptr) ? Collision::Type::wrap : Collision::Type::teleport); + addCollision(GAME_WIDTH+6, 0, GAME_WIDTH, Direction::right, (map.getRightMap() == nullptr) ? Collision::Type::reverse : Collision::Type::teleport); + + for (int i=0; i 0) && (tile < 28) && (!((tile >= 5) && (tile <= 7)))) + { + addCollision(x*TILE_WIDTH, y*TILE_HEIGHT, (y+1)*TILE_HEIGHT, Direction::right, Collision::Type::wall); + addCollision((x+1)*TILE_WIDTH, y*TILE_HEIGHT, (y+1)*TILE_HEIGHT, Direction::left, Collision::Type::wall); + addCollision(y*TILE_HEIGHT, x*TILE_WIDTH, (x+1)*TILE_WIDTH, Direction::down, Collision::Type::wall); + addCollision((y+1)*TILE_HEIGHT, x*TILE_WIDTH, (x+1)*TILE_WIDTH, Direction::up, Collision::Type::wall); + } else if ((tile >= 5) && (tile <= 7)) + { + addCollision(y*TILE_HEIGHT, x*TILE_WIDTH, (x+1)*TILE_WIDTH, Direction::down, Collision::Type::platform); + } else if (tile == 42) + { + addCollision(y*TILE_HEIGHT, x*TILE_WIDTH, (x+1)*TILE_WIDTH, Direction::down, Collision::Type::danger); + } + } +} + +void MapCollisionComponent::addCollision(double axis, double lower, double + upper, Direction dir, Collision::Type type) +{ + std::list::iterator it; + + switch (dir) + { + case Direction::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 Direction::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 Direction::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 Direction::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(Game& game, Entity&, Entity& collider, std::pair old_position) +{ + if (collider.position.first < old_position.first) + { + for (auto collision : left_collisions) + { + if (collision.axis > old_position.first) continue; + if (collision.axis < collider.position.first) break; + + if ((old_position.second+collider.size.second > collision.lower) && (old_position.second < collision.upper)) + { + // We have a collision! + processCollision(game, collider, collision, Direction::left, old_position); + + break; + } + } + } else if (collider.position.first > old_position.first) + { + for (auto collision : right_collisions) + { + if (collision.axis < old_position.first+collider.size.first) continue; + if (collision.axis > collider.position.first+collider.size.first) break; + + if ((old_position.second+collider.size.second > collision.lower) && (old_position.second < collision.upper)) + { + // We have a collision! + processCollision(game, collider, collision, Direction::right, old_position); + + break; + } + } + } + + if (collider.position.second < old_position.second) + { + for (auto collision : up_collisions) + { + if (collision.axis > old_position.second) continue; + if (collision.axis < collider.position.second) break; + + if ((collider.position.first+collider.size.first > collision.lower) && (collider.position.first < collision.upper)) + { + // We have a collision! + processCollision(game, collider, collision, Direction::up, old_position); + + break; + } + } + } else if (collider.position.second > old_position.second) + { + for (auto collision : down_collisions) + { + if (collision.axis < old_position.second+collider.size.second) continue; + if (collision.axis > collider.position.second+collider.size.second) break; + + if ((collider.position.first+collider.size.first > collision.lower) && (collider.position.first < collision.upper)) + { + // We have a collision! + processCollision(game, collider, collision, Direction::down, old_position); + + break; + } + } + } +} + +void MapCollisionComponent::processCollision(Game& game, Entity& collider, Collision collision, Direction dir, std::pair old_position) +{ + if (collision.type == Collision::Type::wall) + { + if (dir == Direction::left) + { + collider.position.first = collision.axis; + collider.send(game, Message::Type::stopMovingHorizontally); + } else if (dir == Direction::right) + { + collider.position.first = collision.axis - collider.size.first; + collider.send(game, Message::Type::stopMovingHorizontally); + } else if (dir == Direction::up) + { + collider.position.second = collision.axis; + collider.send(game, Message::Type::stopMovingVertically); + } else if (dir == Direction::down) + { + collider.position.second = collision.axis - collider.size.second; + collider.send(game, Message::Type::hitTheGround); + } + } else if (collision.type == Collision::Type::wrap) + { + if (dir == Direction::left) + { + collider.position.first = GAME_WIDTH-collider.size.first/2; + } else if (dir == Direction::right) + { + collider.position.first = -collider.size.first/2; + } else if (dir == Direction::up) + { + collider.position.second = GAME_HEIGHT-collider.size.second/2-1; + } else if (dir == Direction::down) + { + collider.position.second = -collider.size.second/2; + } + } else if (collision.type == Collision::Type::teleport) + { + if (dir == Direction::left) + { + game.loadMap(*map.getLeftMap(), std::make_pair(GAME_WIDTH-collider.size.first/2, old_position.second)); + } else if (dir == Direction::right) + { + game.loadMap(*map.getRightMap(), std::make_pair(-collider.size.first/2, old_position.second)); + } + } else if (collision.type == Collision::Type::reverse) + { + if (dir == Direction::right) + { + collider.position.first = collision.axis - collider.size.first; + collider.send(game, Message::Type::walkLeft); + } + } else if (collision.type == Collision::Type::platform) + { + Message msg(Message::Type::drop); + msg.dropAxis = collision.axis; + + collider.send(game, msg); + } else if (collision.type == Collision::Type::danger) + { + game.playerDie(); + } +} diff --git a/src/components/map_collision.h b/src/components/map_collision.h new file mode 100644 index 0000000..3b718b6 --- /dev/null +++ b/src/components/map_collision.h @@ -0,0 +1,46 @@ +#ifndef MAP_COLLISION_H +#define MAP_COLLISION_H + +#include "entity.h" +#include + +class Map; +class Game; + +class MapCollisionComponent : public Component { + public: + MapCollisionComponent(const Map& map); + void detectCollision(Game& game, Entity& entity, Entity& collider, std::pair old_position); + + private: + enum class Direction { + up, left, down, right + }; + + struct Collision { + enum class Type { + wall, + wrap, + teleport, + reverse, + platform, + danger + }; + + double axis; + double lower; + double upper; + Type type; + }; + + void addCollision(double axis, double lower, double upper, Direction dir, Collision::Type type); + void processCollision(Game& game, Entity& collider, Collision collision, Direction dir, std::pair old_position); + + std::list left_collisions; + std::list right_collisions; + std::list up_collisions; + std::list down_collisions; + const Map& map; +}; + +#endif diff --git a/src/components/map_render.cpp b/src/components/map_render.cpp new file mode 100644 index 0000000..d93afe6 --- /dev/null +++ b/src/components/map_render.cpp @@ -0,0 +1,39 @@ +#include "map_render.h" +#include "map.h" +#include "game.h" + +MapRenderComponent::MapRenderComponent(const Map& map) : screen(GAME_WIDTH, GAME_HEIGHT) +{ + screen.fill(screen.entirety(), 0, 0, 0); + + Texture tiles("../res/tiles.png"); + + for (int i=0; i 0) + { + screen.blit(tiles, src, dst); + } + } + + Texture font("../res/font.bmp"); + const char* map_name = map.getTitle(); + int start_x = (40/2) - (strlen(map_name)/2); + for (size_t i=0; i TERMINAL_VELOCITY_X) velocity.first = TERMINAL_VELOCITY_X; + if (velocity.second < -TERMINAL_VELOCITY_Y) velocity.second = -TERMINAL_VELOCITY_Y; + if (velocity.second > TERMINAL_VELOCITY_Y) velocity.second = TERMINAL_VELOCITY_Y; + + // Do the movement + entity.position.first += velocity.first * dt; + entity.position.second += velocity.second * dt; +} + +void PhysicsBodyComponent::detectCollision(Game& game, Entity& entity, Entity& collider, std::pair old_position) +{ + // If already colliding, do nothing! + if ((old_position.first + collider.size.first > entity.position.first) + && (old_position.first < entity.position.first + entity.size.first) + && (old_position.second + collider.size.second > entity.position.second) + && (old_position.second < entity.position.second + entity.size.second)) + { + return; + } + + // If newly colliding, SHOCK AND HORROR! + if ((collider.position.first + collider.size.first > entity.position.first) + && (collider.position.first < entity.position.first + entity.size.first) + && (collider.position.second + collider.size.second > entity.position.second) + && (collider.position.second < entity.position.second + entity.size.second)) + { + Message msg(Message::Type::collision); + msg.collisionEntity = &collider; + + entity.send(game, msg); + } +} diff --git a/src/components/physics_body.h b/src/components/physics_body.h new file mode 100644 index 0000000..079cc51 --- /dev/null +++ b/src/components/physics_body.h @@ -0,0 +1,20 @@ +#ifndef PHYSICS_BODY_H +#define PHYSICS_BODY_H + +#include "entity.h" +#include + +class Game; + +class PhysicsBodyComponent : public Component { + public: + void receive(Game& game, Entity& entity, const Message& msg); + void tick(Game& game, Entity& entity, double dt); + void detectCollision(Game& game, Entity& entity, Entity& collider, std::pair old_position); + + protected: + std::pair velocity; + std::pair accel; +}; + +#endif diff --git a/src/components/player_physics.cpp b/src/components/player_physics.cpp new file mode 100644 index 0000000..1be43b6 --- /dev/null +++ b/src/components/player_physics.cpp @@ -0,0 +1,117 @@ +#include "player_physics.h" +#include "muxer.h" +#include "game.h" + +#define JUMP_VELOCITY(h, l) (-2 * (h) / (l)) +#define JUMP_GRAVITY(h, l) (2 * ((h) / (l)) / (l)) + +PlayerPhysicsComponent::PlayerPhysicsComponent() +{ + jump_velocity = JUMP_VELOCITY(TILE_HEIGHT*4.5, 0.3); + jump_gravity = JUMP_GRAVITY(TILE_HEIGHT*4.5, 0.3); + jump_gravity_short = JUMP_GRAVITY(TILE_HEIGHT*3.5, 0.233); + + accel.second = jump_gravity_short; +} + +void PlayerPhysicsComponent::receive(Game&, Entity& entity, const Message& msg) +{ + if (msg.type == Message::Type::walkLeft) + { + velocity.first = -90; + direction = -1; + } else if (msg.type == Message::Type::walkRight) + { + velocity.first = 90; + direction = 1; + } else if (msg.type == Message::Type::stopWalking) + { + velocity.first = 0.0; + direction = 0; + } else if (msg.type == Message::Type::stopMovingHorizontally) + { + velocity.first = 0.0; + } else if (msg.type == Message::Type::stopMovingVertically) + { + velocity.second = 0.0; + } else if (msg.type == Message::Type::hitTheGround) + { + if (isFalling) + { + playSound("../res/Randomize27.wav", 0.05); + isFalling = false; + } + + velocity.second = 0.0; + } else if (msg.type == Message::Type::jump) + { + playSound("../res/Randomize87.wav", 0.25); + + velocity.second = jump_velocity; + accel.second = jump_gravity; + } else if (msg.type == Message::Type::stopJump) + { + accel.second = jump_gravity_short; + } else if (msg.type == Message::Type::canDrop) + { + canDrop = true; + } else if (msg.type == Message::Type::cantDrop) + { + canDrop = false; + } else if (msg.type == Message::Type::drop) + { + if (canDrop) + { + canDrop = false; + } else { + entity.position.second = msg.dropAxis - entity.size.second; + velocity.second = 0; + } + } else if (msg.type == Message::Type::die) + { + frozen = true; + } else if (msg.type == Message::Type::stopDying) + { + frozen = false; + } +} + +void PlayerPhysicsComponent::tick(Game& game, Entity& entity, double dt) +{ + // If frozen, do nothing + if (frozen) + { + return; + } + + // Continue walking even if blocked earlier + if (velocity.first == 0) + { + if (direction < 0) + { + velocity.first = -90; + } else if (direction > 0) + { + velocity.first = 90; + } + } + + // Increase gravity at the height of jump + if ((accel.second == jump_gravity) && (velocity.second >= 0)) + { + accel.second = jump_gravity_short; + } + + // Do the movement + std::pair old_position = entity.position; + PhysicsBodyComponent::tick(game, entity, dt); + + // Check for collisions + game.detectCollision(entity, old_position); + + // Are we moving due to gravity? + if (velocity.second != 0.0) + { + isFalling = true; + } +} diff --git a/src/components/player_physics.h b/src/components/player_physics.h new file mode 100644 index 0000000..26f1fae --- /dev/null +++ b/src/components/player_physics.h @@ -0,0 +1,25 @@ +#ifndef PLAYER_PHYSICS_H +#define PLAYER_PHYSICS_H + +#include "entity.h" +#include "physics_body.h" + +class Game; + +class PlayerPhysicsComponent : public PhysicsBodyComponent { + public: + PlayerPhysicsComponent(); + void tick(Game& game, Entity& entity, double dt); + void receive(Game& game, Entity& entity, const Message& msg); + + private: + double jump_velocity; + double jump_gravity; + double jump_gravity_short; + int direction = 0; + bool canDrop = false; + bool frozen = false; + bool isFalling = false; +}; + +#endif diff --git a/src/components/player_sprite.cpp b/src/components/player_sprite.cpp new file mode 100644 index 0000000..2814852 --- /dev/null +++ b/src/components/player_sprite.cpp @@ -0,0 +1,60 @@ +#include "player_sprite.h" + +PlayerSpriteComponent::PlayerSpriteComponent() : sprite("../res/Starla.png") +{ + +} + +void PlayerSpriteComponent::render(Game&, Entity& entity, Texture& buffer) +{ + animFrame++; + + int frame = 0; + if (isMoving) + { + frame += 2; + + if (animFrame % 20 < 10) + { + frame += 2; + } + } + + if (facingLeft) + { + frame++; + } + + double alpha = 1.0; + if (dying && (animFrame % 4 < 2)) + { + alpha = 0.0; + } + + Rectangle src_rect {frame*10, 0, 10, 12}; + Rectangle dst_rect {(int) entity.position.first, (int) entity.position.second, entity.size.first, entity.size.second}; + buffer.blit(sprite, src_rect, dst_rect, alpha); +} + +void PlayerSpriteComponent::receive(Game&, Entity&, const Message& msg) +{ + if (msg.type == Message::Type::walkLeft) + { + facingLeft = true; + isMoving = true; + } else if (msg.type == Message::Type::walkRight) + { + facingLeft = false; + isMoving = true; + } else if (msg.type == Message::Type::stopWalking) + { + isMoving = false; + } else if (msg.type == Message::Type::die) + { + dying = true; + isMoving = false; + } else if (msg.type == Message::Type::stopDying) + { + dying = false; + } +} diff --git a/src/components/player_sprite.h b/src/components/player_sprite.h new file mode 100644 index 0000000..b1ac0af --- /dev/null +++ b/src/components/player_sprite.h @@ -0,0 +1,23 @@ +#ifndef PLAYER_SPRITE_H +#define PLAYER_SPRITE_H + +#include "entity.h" +#include "renderer.h" + +class Game; + +class PlayerSpriteComponent : public Component { + public: + PlayerSpriteComponent(); + void render(Game& game, Entity& entity, Texture& buffer); + void receive(Game& game, Entity& entity, const Message& msg); + + private: + Texture sprite; + int animFrame = 0; + bool facingLeft = false; + bool isMoving = false; + bool dying = false; +}; + +#endif diff --git a/src/components/simple_collider.cpp b/src/components/simple_collider.cpp new file mode 100644 index 0000000..f4b414e --- /dev/null +++ b/src/components/simple_collider.cpp @@ -0,0 +1,14 @@ +#include "simple_collider.h" + +SimpleColliderComponent::SimpleColliderComponent(std::function callback) : callback(callback) +{ + +} + +void SimpleColliderComponent::receive(Game& game, Entity&, const Message& msg) +{ + if (msg.type == Message::Type::collision) + { + callback(game, *msg.collisionEntity); + } +} diff --git a/src/components/simple_collider.h b/src/components/simple_collider.h new file mode 100644 index 0000000..15d78cf --- /dev/null +++ b/src/components/simple_collider.h @@ -0,0 +1,18 @@ +#ifndef SIMPLE_COLLIDER_H +#define SIMPLE_COLLIDER_H + +#include "entity.h" +#include + +class Game; + +class SimpleColliderComponent : public Component { + public: + SimpleColliderComponent(std::function callback); + void receive(Game& game, Entity& entity, const Message& msg); + + private: + std::function callback; +}; + +#endif diff --git a/src/components/static_image.cpp b/src/components/static_image.cpp new file mode 100644 index 0000000..9fa8dca --- /dev/null +++ b/src/components/static_image.cpp @@ -0,0 +1,11 @@ +#include "static_image.h" + +StaticImageComponent::StaticImageComponent(const char* filename) : sprite(Texture(filename)) +{ + +} + +void StaticImageComponent::render(Game&, Entity& entity, Texture& buffer) +{ + buffer.blit(sprite, sprite.entirety(), {(int) entity.position.first, (int) entity.position.second, entity.size.first, entity.size.second}); +} diff --git a/src/components/static_image.h b/src/components/static_image.h new file mode 100644 index 0000000..2dec78b --- /dev/null +++ b/src/components/static_image.h @@ -0,0 +1,18 @@ +#ifndef STATIC_IMAGE_H +#define STATIC_IMAGE_H + +#include "entity.h" +#include "renderer.h" + +class Game; + +class StaticImageComponent : public Component { + public: + StaticImageComponent(const char* filename); + void render(Game& game, Entity& entity, Texture& buffer); + + private: + Texture sprite; +}; + +#endif diff --git a/src/components/user_movement.cpp b/src/components/user_movement.cpp new file mode 100644 index 0000000..e499fee --- /dev/null +++ b/src/components/user_movement.cpp @@ -0,0 +1,100 @@ +#include "user_movement.h" +#include "renderer.h" + +void UserMovementComponent::input(Game& game, Entity& entity, int key, int action) +{ + if (action == GLFW_PRESS) + { + if (key == GLFW_KEY_LEFT) + { + holdingLeft = true; + + if (!frozen) + { + entity.send(game, Message::Type::walkLeft); + } + } else if (key == GLFW_KEY_RIGHT) + { + holdingRight = true; + + if (!frozen) + { + entity.send(game, Message::Type::walkRight); + } + } else if (key == GLFW_KEY_UP) + { + if (!frozen) + { + entity.send(game, Message::Type::jump); + } + } else if (key == GLFW_KEY_DOWN) + { + if (!frozen) + { + entity.send(game, Message::Type::canDrop); + } + } + } else if (action == GLFW_RELEASE) + { + if (key == GLFW_KEY_LEFT) + { + holdingLeft = false; + + if (!frozen) + { + if (holdingRight) + { + entity.send(game, Message::Type::walkRight); + } else { + entity.send(game, Message::Type::stopWalking); + } + } + } else if (key == GLFW_KEY_RIGHT) + { + holdingRight = false; + + if (!frozen) + { + if (holdingLeft) + { + entity.send(game, Message::Type::walkLeft); + } else { + entity.send(game, Message::Type::stopWalking); + } + } + } else if (key == GLFW_KEY_DOWN) + { + if (!frozen) + { + entity.send(game, Message::Type::cantDrop); + } + } else if (key == GLFW_KEY_UP) + { + if (!frozen) + { + entity.send(game, Message::Type::stopJump); + } + } + } +} + +void UserMovementComponent::receive(Game& game, Entity& entity, const Message& msg) +{ + if (msg.type == Message::Type::die) + { + frozen = true; + + entity.send(game, Message::Type::stopWalking); + } else if (msg.type == Message::Type::stopDying) + { + frozen = false; + + if (holdingLeft) + { + entity.send(game, Message::Type::walkLeft); + } else if (holdingRight) + { + entity.send(game, Message::Type::walkRight); + } + } +} diff --git a/src/components/user_movement.h b/src/components/user_movement.h new file mode 100644 index 0000000..1bcf05e --- /dev/null +++ b/src/components/user_movement.h @@ -0,0 +1,19 @@ +#ifndef USER_MOVEMENT_H +#define USER_MOVEMENT_H + +#include "entity.h" + +class Game; + +class UserMovementComponent : public Component { + public: + void input(Game& game, Entity& entity, int key, int action); + void receive(Game&, Entity&, const Message& msg); + + private: + bool holdingLeft = false; + bool holdingRight = false; + bool frozen = false; +}; + +#endif diff --git a/src/entity.cpp b/src/entity.cpp index 0ede567..2b6cd7f 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -1,7 +1,4 @@ #include "entity.h" -#include -#include "components.h" -#include "muxer.h" void Entity::addComponent(std::shared_ptr c) { diff --git a/src/entity.h b/src/entity.h index 26febfb..d09dbe5 100644 --- a/src/entity.h +++ b/src/entity.h @@ -24,7 +24,8 @@ class Message { canDrop, cantDrop, die, - stopDying + stopDying, + hitTheGround }; Message(Type type) : type(type) {} diff --git a/src/entityfactory.cpp b/src/entityfactory.cpp index 47a1463..bad72cf 100644 --- a/src/entityfactory.cpp +++ b/src/entityfactory.cpp @@ -1,9 +1,12 @@ #include "entityfactory.h" #include -#include "components.h" #include "muxer.h" #include #include +#include "components/static_image.h" +#include "components/simple_collider.h" +#include "components/physics_body.h" +#include "game.h" struct EntityData { char* sprite; diff --git a/src/game.cpp b/src/game.cpp index 6e79f75..20b8564 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1,9 +1,12 @@ #include "game.h" #include "renderer.h" -#include "components.h" #include "muxer.h" -#include "entityfactory.h" #include "map.h" +#include "components/user_movement.h" +#include "components/player_physics.h" +#include "components/player_sprite.h" +#include "components/map_render.h" +#include "components/map_collision.h" Game::Game() { @@ -23,7 +26,7 @@ Game::Game() Map& startingMap = Map::getNamedMap("embarass"); save = {&startingMap, player->position}; - loadMap(startingMap); + loadMap(startingMap, player->position); } void key_callback(GLFWwindow* window, int key, int, int action, int) @@ -65,6 +68,8 @@ void Game::execute(GLFWwindow* window) newWorld = false; entities.clear(); entities = std::move(nextEntities); + + player->position = nextPosition; } // Handle input @@ -106,7 +111,7 @@ void Game::execute(GLFWwindow* window) } } -void Game::loadMap(const Map& map) +void Game::loadMap(const Map& map, std::pair position) { auto mapEn = std::make_shared(); @@ -125,6 +130,7 @@ void Game::loadMap(const Map& map) newWorld = true; currentMap = ↦ + nextPosition = position; } void Game::detectCollision(Entity& collider, std::pair old_position) @@ -154,10 +160,11 @@ void Game::playerDie() schedule(0.75, [&] () { if (*currentMap != *save.map) { - loadMap(*save.map); + loadMap(*save.map, save.position); + } else { + player->position = save.position; } - player->position = save.position; player->send(*this, Message::Type::stopDying); }); } diff --git a/src/game.h b/src/game.h index bc31912..a4620d4 100644 --- a/src/game.h +++ b/src/game.h @@ -28,7 +28,7 @@ class Game { public: Game(); void execute(GLFWwindow* window); - void loadMap(const Map& map); + void loadMap(const Map& map, std::pair position); void detectCollision(Entity& collider, std::pair old_position); void saveGame(); void schedule(double time, std::function callback); @@ -39,6 +39,7 @@ class Game { std::list> entities; std::list> nextEntities; + std::pair nextPosition; bool newWorld; std::shared_ptr player; const Map* currentMap; -- cgit 1.4.1