From 47b764aff356bcc8bdda3f5b398efdf1de100023 Mon Sep 17 00:00:00 2001 From: Cadecraft Date: Sat, 23 Mar 2024 09:42:40 -0700 Subject: [PATCH] collision system --- CMakeLists.txt | 3 ++ src/gamestate.cpp | 31 ++++++++++++++++++ src/gamestate.h | 25 ++++++++++++++ src/main.cpp | 51 +++++++++++++++++------------ src/renderer.cpp | 4 +-- src/renderer.h | 4 +-- src/worldobject.cpp | 22 ++++++++++++- src/worldobject.h | 17 ++++++---- src/worldobjects/bluecube.h | 2 +- src/worldobjects/player.h | 52 ++++++++++++++++++++++++++---- src/worldobjects/redcube.h | 2 +- src/worldobjects/staticcollider.h | 35 ++++++++++++++++++++ src/worldobjects/stationaryimage.h | 2 +- src/worldstate.cpp | 30 +++-------------- src/worldstate.h | 22 ++++--------- 15 files changed, 221 insertions(+), 81 deletions(-) create mode 100644 src/gamestate.cpp create mode 100644 src/gamestate.h create mode 100644 src/worldobjects/staticcollider.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 554fc2d..db10fe7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,10 +23,13 @@ add_executable(PlatScifi src/renderer.h src/worldstate.cpp src/worldstate.h + src/gamestate.cpp + src/gamestate.h src/worldobjects/redcube.h src/worldobjects/bluecube.h src/worldobjects/stationaryimage.h src/worldobjects/player.h + src/worldobjects/staticcollider.h ) target_link_libraries(PlatScifi PRIVATE sfml-graphics) target_compile_features(PlatScifi PRIVATE cxx_std_17) diff --git a/src/gamestate.cpp b/src/gamestate.cpp new file mode 100644 index 0000000..5005a41 --- /dev/null +++ b/src/gamestate.cpp @@ -0,0 +1,31 @@ +#include "gamestate.h" + +void GameState::spawnObject(WorldObject* object) { + objects.push_back(object); +} + +void GameState::update() { + for (WorldObject* object : objects) { + object->update(worldState, objects); + } +} + +std::vector::iterator GameState::objectsBegin() { + return objects.begin(); +} + +std::vector::iterator GameState::objectsEnd() { + return objects.end(); +} + +void GameState::clear() { + while (!objects.empty()) { + // TODO: good memory freeing? + delete objects.back(); + objects.pop_back(); + } +} + +GameState::~GameState() { + clear(); +} diff --git a/src/gamestate.h b/src/gamestate.h new file mode 100644 index 0000000..618282f --- /dev/null +++ b/src/gamestate.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include "worldobject.h" +#include "worldstate.h" + +// Store the full state of the world and all objects +class GameState { +private: + WorldState worldState; + std::vector objects; + +public: + // Spawn an object + void spawnObject(WorldObject* object); + // Update + void update(); + // Get all objects + std::vector::iterator objectsBegin(); + std::vector::iterator objectsEnd(); + // Clear all items + void clear(); + // Destructor to free memory + ~GameState(); +}; diff --git a/src/main.cpp b/src/main.cpp index 7b362cb..411917b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,12 +4,13 @@ #include "assethandler.h" #include "renderer.h" #include "renderdata.h" -#include "worldstate.h" +#include "gamestate.h" #include "worldobject.h" #include "worldobjects/redcube.h" // Debugging #include "worldobjects/bluecube.h" #include "worldobjects/stationaryimage.h" #include "worldobjects/player.h" +#include "worldobjects/staticcollider.h" // SFML Demo int main() { @@ -21,17 +22,24 @@ int main() { // Main data and objects AssetHandler assetHandler; Renderer renderer(window, assetHandler); - WorldState worldState; + GameState gameState; std::set keysPressed; // Main world objects - Player* player = new Player(4, 10); - worldState.spawnObject(player); + Player* player = new Player(5, 10); + gameState.spawnObject(player); - // Debug: create a red cube, blue cube, and image - worldState.spawnObject(new RedCube(0, 0)); - worldState.spawnObject(new BlueCube(0, 0)); - worldState.spawnObject(new StationaryImage(6, 6, 2, 2, "assets/tiv_logo.png")); + // Debug: create a red cube, blue cube, image, etc. + gameState.spawnObject(new RedCube(0, 0)); + gameState.spawnObject(new BlueCube(0, 0)); + gameState.spawnObject(new StationaryImage(6, 6, 2, 2, "assets/tiv_logo.png")); + // Bottom colliders + gameState.spawnObject(new StaticCollider(3, 25, 6, 2)); + gameState.spawnObject(new StaticCollider(9, 27, 10, 2)); + gameState.spawnObject(new StaticCollider(19, 25, 6, 2)); + // Vertical colliders + gameState.spawnObject(new StaticCollider(0, 0, 2, 25)); + gameState.spawnObject(new StaticCollider(25, 0, 2, 19)); // Main game loop while (window.isOpen()) { @@ -49,28 +57,31 @@ int main() { // Use input if (keysPressed.find(sf::Keyboard::A) != keysPressed.end()) { - player->move(-0.2, 0); + player->accelerate(-0.05, 0); } if (keysPressed.find(sf::Keyboard::D) != keysPressed.end()) { - player->move(0.2, 0); + player->accelerate(0.05, 0); } - if (keysPressed.find(sf::Keyboard::W) != keysPressed.end()) { - player->move(0, -0.2); + if ( + keysPressed.find(sf::Keyboard::W) != keysPressed.end() + || keysPressed.find(sf::Keyboard::Space) != keysPressed.end() + ) { + player->jump(); } if (keysPressed.find(sf::Keyboard::S) != keysPressed.end()) { - player->move(0, 0.2); + player->accelerate(0, 0.05); } - if (keysPressed.find(sf::Keyboard::Space) != keysPressed.end()) { - worldState.spawnObject(new BlueCube(player->getLocx(), player->getLocy())); + /*if () { + gameState.spawnObject(new BlueCube(player->getLocx(), player->getLocy())); keysPressed.erase(sf::Keyboard::Space); - } + }*/ - // Update the world - worldState.update(); + // Update the game and world + gameState.update(); - // Render the world + // Render the game and world window.clear(); - renderer.renderWorld(worldState); + renderer.renderWorldObjects(gameState); window.display(); } } diff --git a/src/renderer.cpp b/src/renderer.cpp index 2b30753..0eae301 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -66,9 +66,9 @@ void Renderer::renderFromData(RenderData& data) { } } -void Renderer::renderWorld(WorldState& worldState) { +void Renderer::renderWorldObjects(GameState& gameState) { // todo: fix on mac/linux - for (std::vector::iterator it = worldState.objectsBegin(); it != worldState.objectsEnd(); it++) { + for (std::vector::iterator it = gameState.objectsBegin(); it != gameState.objectsEnd(); it++) { renderFromData((*it)->getRenderData()); } } diff --git a/src/renderer.h b/src/renderer.h index c6d18bf..a208520 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -1,7 +1,7 @@ #pragma once #include -#include "worldstate.h" +#include "gamestate.h" #include "renderdata.h" #include "assethandler.h" @@ -19,5 +19,5 @@ class Renderer { void renderFromData(RenderData& data); // Render the entire world - void renderWorld(WorldState& worldState); + void renderWorldObjects(GameState& worldState); }; diff --git a/src/worldobject.cpp b/src/worldobject.cpp index 2cd52bf..1d34532 100644 --- a/src/worldobject.cpp +++ b/src/worldobject.cpp @@ -8,11 +8,31 @@ WorldObject::WorldObject() { height = 1.0; } -UpdateResult WorldObject::update() { +UpdateResult WorldObject::update(WorldState& worldState, std::vector& objects) { // Update return UpdateResult::None; } +double WorldObject::isPointInside(float x, float y) { + return (x >= locx && x < locx + width && y > locy && y < locy + height); +} + +double WorldObject::getLocx() { + return locx; +} + +double WorldObject::getLocy() { + return locy; +} + +double WorldObject::getWidth() { + return width; +} + +double WorldObject::getHeight() { + return height; +} + RenderData WorldObject::getRenderData() { return { RenderType::Rectangle, diff --git a/src/worldobject.h b/src/worldobject.h index c2c7e4c..2f45b16 100644 --- a/src/worldobject.h +++ b/src/worldobject.h @@ -2,7 +2,7 @@ #include #include "renderdata.h" -//#include "worldstate.h" +#include "worldstate.h" // The result of an update function enum class UpdateResult { @@ -24,12 +24,17 @@ class WorldObject { WorldObject(); // Update each frame - virtual UpdateResult update(/*WorldState& worldState*/); + virtual UpdateResult update(WorldState& worldState, std::vector& objects); - // TODO: delete this function - /*void tester(std::vector& v) { - // Do stuff with v - }*/ + // Get whether a point is inside of this object + double isPointInside(float x, float y); + + double getLocx(); + double getLocy(); + double getWidth(); + double getHeight(); + + // TODO: get whether a rectangle is inside of this object // Return the results to render virtual RenderData getRenderData(); diff --git a/src/worldobjects/bluecube.h b/src/worldobjects/bluecube.h index 6020344..7b84caa 100644 --- a/src/worldobjects/bluecube.h +++ b/src/worldobjects/bluecube.h @@ -15,7 +15,7 @@ class BlueCube : public WorldObject { } // Override the update function - UpdateResult update() { + UpdateResult update(WorldState& worldState, std::vector& objects) { locx += velx; if (locx > 64 || locx < 0) velx *= -1; locy += vely; diff --git a/src/worldobjects/player.h b/src/worldobjects/player.h index 97350cf..f08ead6 100644 --- a/src/worldobjects/player.h +++ b/src/worldobjects/player.h @@ -4,6 +4,10 @@ // The data for the player class Player : public WorldObject { +private: + float velx = 0; + float vely = 0; + public: // Constructor Player(double spawnx, double spawny) : WorldObject() { @@ -11,11 +15,16 @@ class Player : public WorldObject { locy = spawny; } - // Input: movement + // Input: acceleration // TODO: accelerate instead of move - void move(double deltax, double deltay) { - locx += deltax; - locy += deltay; + void accelerate(double deltax, double deltay) { + velx += deltax; + vely += deltay; + } + + // Input: jump + void jump() { + vely = -0.4; } double getLocx() { @@ -26,8 +35,39 @@ class Player : public WorldObject { return locy; } - // Override update: do nothing (yet) - UpdateResult update() { + // Override update: gravity and acceleration + UpdateResult update(WorldState& worldState, std::vector& objects) { + vely += worldState.getGravityStrength(); + velx *= 0.96; + vely *= 0.96; + locx += velx; + locy += vely; + //if (velx < 0.01 && velx >= -0.01) velx = 0; + // Check collision + // todo: refactor + // todo: only check with types enabling collision + // todo: when hitting multiple? + for (WorldObject* object : objects) { + if (object == this) continue; + // X movement pushout + // todo: better way of doing the 0.1 thing + if (locy + height - 0.1 >= object->getLocy() && locy + 0.1 < object->getLocy() + object->getHeight()) { + // Within the y + if (locx + width > object->getLocx() && locx < object->getLocx() + object->getWidth()) { + // Side wall + locx -= velx; + velx = 0; + } + } + if (locx + width >= object->getLocx() && locx < object->getLocx() + object->getWidth()) { + // Within the x + if (locy + height > object->getLocy() && locy < object->getLocy() + object->getHeight()) { + // Floor/ceiling + locy -= vely; + vely = 0; + } + } + } return UpdateResult::None; } diff --git a/src/worldobjects/redcube.h b/src/worldobjects/redcube.h index 2af49bc..9cc7440 100644 --- a/src/worldobjects/redcube.h +++ b/src/worldobjects/redcube.h @@ -12,7 +12,7 @@ class RedCube : public WorldObject { } // Override update: move right over time - UpdateResult update() { + UpdateResult update(WorldState& worldState, std::vector& objects) { locx += 0.1; if (locx > 30) { locy += 1; diff --git a/src/worldobjects/staticcollider.h b/src/worldobjects/staticcollider.h new file mode 100644 index 0000000..4c70a5b --- /dev/null +++ b/src/worldobjects/staticcollider.h @@ -0,0 +1,35 @@ +#pragma once + +#include "../worldobject.h" + +// An object that does not move, but that other objects can collide with +class StaticCollider : public WorldObject { +public: + // Constructor + StaticCollider(double spawnx, double spawny, double width, double height) : WorldObject() { + locx = spawnx; + locy = spawny; + this->width = width; + this->height = height; + } + + // Override update: gravity + UpdateResult update(WorldState& worldState, std::vector& objects) { + return UpdateResult::None; + } + + // Override rendering + RenderData getRenderData() { + // TODO: image/animations + return { + RenderType::Rectangle, + coordType, + locx, + locy, + width, + height, + { 14, 92, 81 }, + "" + }; + } +}; diff --git a/src/worldobjects/stationaryimage.h b/src/worldobjects/stationaryimage.h index 8c356f1..a7e231e 100644 --- a/src/worldobjects/stationaryimage.h +++ b/src/worldobjects/stationaryimage.h @@ -18,7 +18,7 @@ class StationaryImage : public WorldObject { } // Override update: do nothing - UpdateResult update() { + UpdateResult update(WorldState& worldState, std::vector& objects) { return UpdateResult::None; } diff --git a/src/worldstate.cpp b/src/worldstate.cpp index 5cae5b3..b93032e 100644 --- a/src/worldstate.cpp +++ b/src/worldstate.cpp @@ -1,31 +1,9 @@ #include "worldstate.h" -void WorldState::spawnObject(WorldObject* object) { - objects.push_back(object); +std::string WorldState::getLevelName() { + return levelName; } -void WorldState::update() { - for (WorldObject* object : objects) { - object->update(); - } -} - -std::vector::iterator WorldState::objectsBegin() { - return objects.begin(); -} - -std::vector::iterator WorldState::objectsEnd() { - return objects.end(); -} - -void WorldState::clear() { - while (!objects.empty()) { - // TODO: good memory freeing? - delete objects.back(); - objects.pop_back(); - } -} - -WorldState::~WorldState() { - clear(); +float WorldState::getGravityStrength() { + return gravityStrength; } diff --git a/src/worldstate.h b/src/worldstate.h index 4d0f4a4..579cebf 100644 --- a/src/worldstate.h +++ b/src/worldstate.h @@ -1,23 +1,15 @@ #pragma once -#include -#include "worldobject.h" +#include -// Store the full state of the world and all objects class WorldState { private: - std::vector objects; + std::string levelName; + float gravityStrength = 0.01; public: - // Spawn an object - void spawnObject(WorldObject* object); - // Update - void update(); - // Get all objects - std::vector::iterator objectsBegin(); - std::vector::iterator objectsEnd(); - // Clear all items - void clear(); - // Destructor to free memory - ~WorldState(); + // Get the current level name + std::string getLevelName(); + // Get the current gravity field strength + float getGravityStrength(); };