Implement initial test of entity physics, and entity vs terrain collision with high-density of colliders. (Decent performance w/o optimizations!)

This commit is contained in:
2024-12-27 14:38:22 -05:00
parent 2418ddfc46
commit 3fb2db4fd4
8 changed files with 151 additions and 41 deletions

View File

@@ -0,0 +1,27 @@
#include <Client/AssetService.hpp>
#include <Core/Entity.hpp>
#include <JGL/JGL.h>
#include <rewindow/inputservice.hpp>
void CaveGame::Core::Player::Draw() {
auto myAsset = Client::AssetService::Get()->player_sprite;
JGL::J2D::DrawPartialSprite(myAsset, position, {0,0}, {16, 24});
JGL::J2D::OutlineRect(Colors::Red, position, bounding_box);
}
void CaveGame::Core::Player::Update(float elapsed) {
Humanoid::Update(elapsed);
if (InputService::IsKeyDown(Keys::A))
WalkLeft();
if (InputService::IsKeyDown(Keys::D))
WalkRight();
if (InputService::IsKeyDown(Keys::W))
Jump();
if (InputService::IsKeyDown(Keys::S))
Accelerate({0, -1});
}

View File

@@ -18,9 +18,12 @@
#include <Core/Loggers.hpp>
#include <rewindow/logger/logger.h>
#include <Core/Tile.hpp>
#include <JGL/logger/logger.h>
int main(int argc, char** argv) {
JGL::Logger::Warning.EnableConsole(false);
JGL::Logger::Debug.EnableConsole(false);
ReWindow::Logger::Debug.EnableConsole(false);
ReWindow::Logger::Debug.EnableFile(false);
CaveGame::Logs::Info.IncludeLocation(false);
@@ -32,12 +35,6 @@ int main(int argc, char** argv) {
bool steam_success = SteamAPI_Init();
#endif
//srand(0);
CaveGame::Core::DefineTiles();
//for (auto& tile : result)
//std:: cout << tile << std::endl;
auto* window = new CaveGame::ClientApp::CaveGameWindow("Re-CaveGame", 800, 600);
//window->SetResizable(true);
@@ -52,4 +49,4 @@ int main(int argc, char** argv) {
CaveGame::Logs::Info.Log("Exiting client program.");
return 0;
}
}

View File

@@ -3,6 +3,9 @@
#include <J3ML/LinearAlgebra.hpp>
#include <vector>
#include "Color4.hpp"
#include "Interfaces.hpp"
#include "Loggers.hpp"
#include "SimpleAABBSolver.hpp"
namespace CaveGame::Core {
class StatusEffect {
@@ -13,14 +16,17 @@ namespace CaveGame::Core {
explicit Entity(const Vector2& spawnPoint);
int Health() const { return health; }
int Health() const;
int MaxHealth() const { return max_health; }
int MaxHealth() const;
virtual void Draw() = 0;
virtual void Update(float elapsed) = 0;
Vector2 Position() const { return position;}
Vector2 Size() const { return bounding_box;}
virtual void CollisionTest(ITileMap* map, float step) {}
protected:
int health;
int max_health;
@@ -37,19 +43,76 @@ namespace CaveGame::Core {
class PhysicsEntity : public Entity {
public:
explicit PhysicsEntity(const Vector2& spawnPoint) : Entity(spawnPoint) {}
explicit PhysicsEntity(const Vector2& spawnPoint) : Entity(spawnPoint) {
next_position = spawnPoint;
}
[[nodiscard]] Vector2 Velocity() const { return velocity; }
[[nodiscard]] Vector2 Velocity() const;
[[nodiscard]] Vector2 EstimatedNextPosition() const { return next_position; }
[[nodiscard]] Vector2 EstimatedNextPosition() const;
void Accelerate(const Vector2& vector) {
velocity += vector;
}
virtual void PhysicsUpdate(float elapsed) {
next_position += velocity * elapsed;
float friction = 2.45f;
velocity *= (1 - (elapsed*friction));
position = next_position;
}
void CollisionTest(ITileMap* map, float elapsed) override
{
for (int x = -1; x <= bounding_box.x; x++) {
for (int y = -1; y <= bounding_box.y; y++) {
Vector2 tileBoxPos = Vector2(Math::Floor(position.x), Math::Floor(position.y)) + Vector2(x,y);
TileID id = map->GetTile(tileBoxPos.x, tileBoxPos.y);
if (id == TileID::VOID || id == TileID::AIR)
continue;
Vector2 entity_centroid = position + (bounding_box / 2.f);
Vector2 entity_halfbox = bounding_box / 2.f;
Vector2 tile_centroid = tileBoxPos + Vector2{0.5f, 0.5f};
Vector2 tile_halfbox = Vector2{0.5f, 0.5f};
if (Solver::AABB2Dvs(entity_centroid, entity_halfbox, tile_centroid, tile_halfbox)) {
Vector2 separation = Solver::SolveAABB(entity_centroid, entity_halfbox, tile_centroid, tile_halfbox);
Vector2 normal = Solver::GetNormalForAABB(separation, velocity);
next_position += separation;
//if (normal.y != 0)
// velocity = {velocity.x, -velocity.y};
//if (normal.x != 0)
// velocity = {-velocity.x, velocity.y};
}
}
}
}
void Update(float elapsed) override {
// TODO: Sophisticated mechanism to maintain locked-timestep, multiple-iteration physics steps.
PhysicsUpdate(elapsed);
}
protected:
Vector2 velocity;
Vector2 next_position;
float mass;
private:
};
@@ -68,6 +131,14 @@ namespace CaveGame::Core {
{
return 0;
}
void Update(float elapsed) override {
PhysicsEntity::Update(elapsed);
}
void PhysicsUpdate(float elapsed) override {
PhysicsEntity::PhysicsUpdate(elapsed);
}
protected:
};
@@ -83,16 +154,24 @@ namespace CaveGame::Core {
class Player : public Humanoid {
public:
explicit Player(const Vector2& spawnPoint) : Humanoid(spawnPoint) { }
explicit Player(const Vector2& spawnPoint);
void Draw() override;
void Update(float elapsed) override;
void Jump();
void PhysicsUpdate(float elapsed) override;
void Jump() {
Accelerate({0, 1});
}
void Climb();
void Descend();
void Crouch();
void WalkLeft();
void WalkRight();
void WalkLeft() {
Accelerate({-1, 0});
}
void WalkRight() {
Accelerate({1, 0});
}
protected:
};
@@ -108,4 +187,4 @@ namespace CaveGame::Core {
class PhysicalParticle {};
}
}

View File

@@ -71,16 +71,14 @@ namespace CaveGame::Core
static LineSegTestResult LineSegment2Dvs(LineSegment2D s1, LineSegment2D s2);
bool AABB2Dvs(const Vector2& posA, const Vector2& sizeA, const Vector2& posB, const Vector2& sizeB);
bool AABB2Dvs(const AABB2D& a, const AABB2D& b);
Vector2 SolveAABB(const Vector2& posA, const Vector2& sizeA, const Vector2& posB, const Vector2& sizeB);
static Vector2 SolveAABB(const Vector2& posA, const Vector2& sizeA, const Vector2& posB, const Vector2& sizeB);
Vector2 GetNormalForAABB(const Vector2& separation, const Vector2& velocity);
static Vector2 GetNormalForAABB(const Vector2& separation, const Vector2& velocity);
static Vector2 SolveAABB(const AABB2D& a, const AABB2D& b);
Vector2 SolveAABB(const AABB2D& a, const AABB2D& b);
}
}

View File

@@ -366,8 +366,4 @@ namespace CaveGame::Core
}
void DefineTiles();
}

View File

@@ -1,16 +1,27 @@
#include <Core/Entity.hpp>
CaveGame::Core::Entity::Entity(const Vector2 &spawnPoint) {
position = spawnPoint;
}
void CaveGame::Core::Player::Draw() {
#ifdef CAVE_CLIENT
JGL::J2D::DrawPartialSprite();
#endif
CaveGame::Core::Player::Player(const Vector2 &spawnPoint): Humanoid(spawnPoint) {
bounding_box = {16, 24};
}
int CaveGame::Core::Entity::Health() const { return health; }
int CaveGame::Core::Entity::MaxHealth() const { return max_health; }
Vector2 CaveGame::Core::PhysicsEntity::Velocity() const { return velocity; }
Vector2 CaveGame::Core::PhysicsEntity::EstimatedNextPosition() const { return next_position; }
void CaveGame::Core::Player::Update(float elapsed) {
Humanoid::Update(elapsed);
}
void CaveGame::Core::Player::PhysicsUpdate(float elapsed) {
Humanoid::PhysicsUpdate(elapsed);
}

View File

@@ -1,3 +1,4 @@
#include <Core/Loggers.hpp>
#include <Core/Tile.hpp>
namespace CaveGame::Core
@@ -210,14 +211,7 @@ namespace CaveGame::Core
}
void DefineTiles() {
/*RegisterTile(new Tile(TileID::AIR, "air", {0,0,0,0}));
RegisterTile(new SoilTile(TileID::DIRT, "dirt", Colors::Green, TileID::DIRT));
RegisterTile(new SoilTile(TileID::MUD, "mud", Colors::Green, TileID::MUD));
RegisterTile(new Tile(TileID::STONE, "stone", Colors::Green));
RegisterTile(new GrassyTile(TileID::GRASS, "grass", Colors::Green));*/
}
Tile *GetByName(const std::string &name) {
// TODO: Optimize with additional mapping!!

View File

@@ -25,6 +25,14 @@ namespace CaveGame::Core
}
DoRandomTileTicks();
for (auto* entity : entities) {
entity->Update(elapsed);
entity->CollisionTest(this, elapsed);
}
}
TileID World::GetTile(int x, int y) const {