Implemented ground framework for collectables simulation

This commit is contained in:
Maximilian Giller 2023-07-03 01:16:44 +02:00
parent 949412df8a
commit a5ceecd2a4
15 changed files with 353 additions and 47 deletions

View file

@ -121,7 +121,12 @@ set(SOURCES
src/game/physics/holes/layouts/hole_layout.cpp
src/game/physics/holes/layouts/hole_layout.hpp
src/game/physics/holes/layouts/depth_hole_layout.hpp
src/game/physics/holes/layouts/depth_hole_description.hpp src/game/physics/holes/collectable_sim_ground_segment.hpp src/game/physics/holes/collectable_sim_ground_segment.cpp)
src/game/physics/holes/layouts/depth_hole_description.hpp
src/game/physics/holes/ground/collectable_sim_ground_segment.hpp
src/game/physics/holes/ground/collectable_sim_ground.cpp
src/game/physics/holes/ground/collectable_sim_ground.hpp
src/game/physics/body_adapter.cpp
src/game/physics/body_adapter.hpp)
set(PHYSICS_00_SOURCES
src/prototypes/physics_00.cpp)

View file

@ -11,6 +11,7 @@
#define DEFAULT_PLAYER_POINTS 0
#define PLAYER_MIN_RADIUS 0.5f // World units
#define PLAYER_RADIUS_PER_LEVEL 0.25f
#define DEFAULT_MAX_PLAYER_NUMBER 4
// World
#define WORLD_GRAVITY b2Vec2(0.f, -9.8f)

View file

@ -0,0 +1,82 @@
#include "body_adapter.hpp"
#include "../../config.h"
BodyAdapter::BodyAdapter(std::shared_ptr<b2World> world)
: world(std::move(world)), bodyObject(nullptr)
{}
BodyAdapter::~BodyAdapter()
{
destroyBody();
}
void BodyAdapter::destroyBody()
{
if (bodyObject != nullptr)
{
world->DestroyBody(bodyObject);
}
}
void BodyAdapter::createSquare(b2BodyType type, sf::Vector2f center, sf::Vector2f size)
{
destroyBody();
b2BodyDef bodyDef;
bodyDef.type = type;
bodyDef.angularDamping = COLLECTABLES_SIM_ANGULAR_DAMPING;
bodyDef.linearDamping = COLLECTABLES_SIM_LINEAR_DAMPING;
bodyDef.position.Set(center.x, center.y);
bodyObject = world->CreateBody(&bodyDef);
setBoxSize(size);
}
b2Body *BodyAdapter::body() const
{
return bodyObject;
}
sf::Vector2f BodyAdapter::getCenter() const
{
auto position = bodyObject->GetPosition();
auto velocity = bodyObject->GetLinearVelocity();
return sf::Vector2f(position.x, position.y) + sf::Vector2f(velocity.x, velocity.y);
}
sf::Vector2f BodyAdapter::getSize() const
{
return boundingBox;
}
void BodyAdapter::setCenter(sf::Vector2f center)
{
// Set via velocity
auto velocity = bodyObject->GetLinearVelocity();
velocity += b2Vec2(center.x, center.y) - bodyObject->GetPosition();
bodyObject->SetLinearVelocity(velocity);
}
void BodyAdapter::setBoxSize(sf::Vector2f size)
{
// Remove old fixture
if (bodyObject->GetFixtureList() != nullptr)
{
bodyObject->DestroyFixture(bodyObject->GetFixtureList());
}
// Recreates a square fixture
b2PolygonShape shape;
shape.SetAsBox(size.x / 2.f, size.y / 2.f);
boundingBox = size;
b2FixtureDef fixtureDef;
fixtureDef.shape = &shape;
fixtureDef.friction = COLLECTABLES_SIM_FRICTION;
fixtureDef.restitution = COLLECTABLES_SIM_RESTITUTION;
fixtureDef.density = COLLECTABLES_SIM_DENSITY;
bodyObject->CreateFixture(&fixtureDef);
}

View file

@ -0,0 +1,37 @@
#ifndef HOLESOME_BODY_ADAPTER_HPP
#define HOLESOME_BODY_ADAPTER_HPP
#include <box2d/box2d.h>
#include <memory>
#include <SFML/System/Vector2.hpp>
class BodyAdapter
{
public:
explicit BodyAdapter(std::shared_ptr<b2World> world);
~BodyAdapter();
[[nodiscard]] b2Body *body() const;
void createSquare(b2BodyType type, sf::Vector2f center, sf::Vector2f size);
[[nodiscard]] sf::Vector2f getCenter() const;
void setCenter(sf::Vector2f center);
[[nodiscard]] sf::Vector2f getSize() const;
void setBoxSize(sf::Vector2f size);
private:
std::shared_ptr<b2World> world;
b2Body *bodyObject;
sf::Vector2f boundingBox;
void destroyBody();
};
#endif //HOLESOME_BODY_ADAPTER_HPP

View file

@ -1 +0,0 @@
#include "collectable_sim_ground_segment.hpp"

View file

@ -1,14 +0,0 @@
#ifndef HOLESOME_COLLECTABLE_SIM_GROUND_SEGMENT_HPP
#define HOLESOME_COLLECTABLE_SIM_GROUND_SEGMENT_HPP
#include <box2d/box2d.h>
class CollectableSimGroundSegment
{
public:
int leftHoleId;
int rightHoleId;
b2Body *body;
};
#endif //HOLESOME_COLLECTABLE_SIM_GROUND_SEGMENT_HPP

View file

@ -11,9 +11,14 @@ CollectableSimulation::CollectableSimulation(const std::shared_ptr<Collectable>
world = std::make_shared<b2World>(WORLD_GRAVITY);
world->SetAllowSleeping(COLLECTABLES_SIM_SLEEPING);
// Create ground
simGround = std::make_shared<CollectableSimGround>(world);
// Todo: Set proper ground width
// Create body
collectableBody = std::make_shared<BodyAdapter>(world);
auto coordinates = collectable->coordinates->diagonalWorld();
body = addSquareBody(b2_dynamicBody, {coordinates.horizontal, coordinates.vertical}, sf::Vector2f(1, 1));
collectableBody->createSquare(b2_dynamicBody, {coordinates.horizontal, coordinates.vertical}, sf::Vector2f(1, 1));
}
void CollectableSimulation::physicsUpdate()
@ -25,28 +30,6 @@ void CollectableSimulation::physicsUpdate()
updateCollectable();
}
b2Body *CollectableSimulation::addSquareBody(b2BodyType type, sf::Vector2f center, sf::Vector2f size)
{
b2BodyDef bodyDef;
bodyDef.type = type;
bodyDef.angularDamping = COLLECTABLES_SIM_ANGULAR_DAMPING;
bodyDef.linearDamping = COLLECTABLES_SIM_LINEAR_DAMPING;
bodyDef.position.Set(center.x, center.y);
b2Body *body = world->CreateBody(&bodyDef);
b2PolygonShape shape;
shape.SetAsBox(size.x / 2.f, size.y / 2.f);
b2FixtureDef fixtureDef;
fixtureDef.shape = &shape;
fixtureDef.friction = COLLECTABLES_SIM_FRICTION;
fixtureDef.restitution = COLLECTABLES_SIM_RESTITUTION;
fixtureDef.density = COLLECTABLES_SIM_DENSITY;
body->CreateFixture(&fixtureDef);
return body;
}
std::shared_ptr<Collectable> CollectableSimulation::getCollectable() const
{
return collectable;
@ -56,15 +39,25 @@ void CollectableSimulation::updateGroundHole()
{
auto holeLayout = HoleLayout::getInstance()->atDepth(collectable->getDepth());
if (holeLayout.holes.empty())
{
simGround->closeAllHoles();
return;
}
// TODO: Remove assumption
// Assume only one hole
auto hole = holeLayout.holes[0];
// todo: Update ground hole
}
void CollectableSimulation::updateCollectable()
{
auto bodyPosition = body->GetPosition();
auto bodyPosition = collectableBody->body()->GetPosition();
collectable->coordinates->setDiagonal({bodyPosition.x, bodyPosition.y, collectable->getDepth()});
// Convert radians to degrees
float angle = body->GetAngle() * 180 / M_PI;
float angle = collectableBody->body()->GetAngle() * 180 / M_PI;
collectable->setRotation(angle);
}

View file

@ -3,8 +3,12 @@
#include <box2d/box2d.h>
#include <map>
#include "../../game_object.h"
#include "../../collectables/collectable.hpp"
#include "ground/collectable_sim_ground_segment.hpp"
#include "ground/collectable_sim_ground.hpp"
#include "../body_adapter.hpp"
class CollectableSimulation : public GameObject
{
@ -17,15 +21,15 @@ public:
private:
std::shared_ptr<b2World> world;
b2Body* body;
std::shared_ptr<BodyAdapter> collectableBody;
std::shared_ptr<Collectable> collectable;
std::shared_ptr<CollectableSimGround> simGround;
void updateGroundHole();
void updateCollectable();
b2Body *addSquareBody(b2BodyType type, sf::Vector2f center, sf::Vector2f size);
};

View file

@ -0,0 +1,102 @@
#include "collectable_sim_ground.hpp"
#include <utility>
#include "../../../../config.h"
void CollectableSimGround::closeAllHoles()
{
segments.clear();
// Create one segment for the ground
auto groundSegment = std::make_shared<CollectableSimGroundSegment>(world);
groundSegment->body->createSquare(b2_kinematicBody,
{0, -COLLECTABLES_SIM_GROUND_THICKNESS / 2.f},
{groundWidth, COLLECTABLES_SIM_GROUND_THICKNESS});
segments.push_back(groundSegment);
}
CollectableSimGround::CollectableSimGround(std::shared_ptr<b2World> world, float groundWidth)
: world(std::move(world))
{
setGroundWidth(groundWidth);
closeAllHoles();
}
void CollectableSimGround::setGroundWidth(float width)
{
if (width <= 0)
{
throw std::runtime_error("Ground width must be greater than 0");
}
if (width == groundWidth)
{
return;
}
groundWidth = width;
updateOuterSegmentsToWidth();
}
void CollectableSimGround::updateOuterSegmentsToWidth()
{
auto outerSegments = getOuterSegments();
auto borderPoints = groundWidth / 2.f;
// Left segment
if (outerSegments.left != nullptr)
{
auto leftSegment = outerSegments.left->body;
auto leftCenter = leftSegment->getCenter();
auto leftSize = leftSegment->getSize();
auto deltaToBorder = borderPoints - abs(leftCenter.x - leftSize.x / 2.f);
// Increase width if positive, decrease if negative
leftSize.x += deltaToBorder;
leftCenter.x -= deltaToBorder / 2.f;
leftSegment->setBoxSize(leftSize);
leftSegment->setCenter(leftCenter);
}
// Right segment
if (outerSegments.right != nullptr)
{
auto rightSegment = outerSegments.right->body;
auto rightCenter = rightSegment->getCenter();
auto rightSize = rightSegment->getSize();
auto deltaToBorder = borderPoints - abs(rightCenter.x + rightSize.x / 2.f);
// Increase width if positive, decrease if negative
rightSize.x += deltaToBorder;
rightCenter.x += deltaToBorder / 2.f;
rightSegment->setBoxSize(rightSize);
rightSegment->setCenter(rightCenter);
}
}
CollectableSimGround::SideSegments CollectableSimGround::getOuterSegments(int holeId)
{
SideSegments sideSegments;
for (const auto &segment: segments)
{
// If valid holeId, match left hole id
// If invalid holeId, get outer segment
if ((segment->leftHoleId == holeId && holeId >= 0)
|| (segment->rightHoleId < 0 && holeId < 0))
{
sideSegments.right = segment;
}
if ((segment->rightHoleId == holeId && holeId >= 0)
|| (segment->leftHoleId < 0 && holeId < 0))
{
sideSegments.left = segment;
}
}
return sideSegments;
}

View file

@ -0,0 +1,34 @@
#ifndef HOLESOME_COLLECTABLE_SIM_GROUND_HPP
#define HOLESOME_COLLECTABLE_SIM_GROUND_HPP
#include <vector>
#include <memory>
#include "collectable_sim_ground_segment.hpp"
class CollectableSimGround
{
public:
CollectableSimGround(std::shared_ptr<b2World> world, float groundWidth = 1000);
void closeAllHoles();
void setGroundWidth(float width);
private:
std::vector<std::shared_ptr<CollectableSimGroundSegment>> segments;
std::shared_ptr<b2World> world;
float groundWidth;
struct SideSegments {
std::shared_ptr<CollectableSimGroundSegment> left;
std::shared_ptr<CollectableSimGroundSegment> right;
};
SideSegments getOuterSegments(int holeId = -1);
void updateOuterSegmentsToWidth();
};
#endif //HOLESOME_COLLECTABLE_SIM_GROUND_HPP

View file

@ -0,0 +1,22 @@
#ifndef HOLESOME_COLLECTABLE_SIM_GROUND_SEGMENT_HPP
#define HOLESOME_COLLECTABLE_SIM_GROUND_SEGMENT_HPP
#include <box2d/box2d.h>
#include <memory>
#include "../layouts/depth_hole_description.hpp"
#include "../../body_adapter.hpp"
class CollectableSimGroundSegment
{
public:
explicit CollectableSimGroundSegment(const std::shared_ptr<b2World>& world)
{
body = std::make_shared<BodyAdapter>(world);
}
int leftHoleId = -1;
int rightHoleId = -1;
std::shared_ptr<BodyAdapter> body;
};
#endif //HOLESOME_COLLECTABLE_SIM_GROUND_SEGMENT_HPP

View file

@ -3,6 +3,7 @@
#include <vector>
#include "depth_hole_description.hpp"
#include "../../../../logging/easylogging++.h"
struct DepthHoleLayout
{
@ -12,6 +13,26 @@ struct DepthHoleLayout
DepthHoleLayout(float depth, std::vector<DepthHoleDescription> holes)
: depth(depth), holes(std::move(holes))
{}
[[nodiscard]] DepthHoleDescription getHole(int playerId) const
{
for (const auto &hole: holes)
{
if (hole.playerId == playerId)
{
return hole;
}
}
throw std::runtime_error(
"No hole for player " + std::to_string(playerId) + " at depth " + std::to_string(depth) +
" in layout.");
}
[[nodiscard]] bool hasHole(int playerId) const
{
return std::ranges::any_of(holes, [playerId](const auto &hole) { return hole.playerId == playerId; });
}
};
#endif //HOLESOME_DEPTH_HOLE_LAYOUT_HPP

View file

@ -4,7 +4,8 @@
#include "../input/input_mapper.h"
#include "../../texture_config.h"
PlayerCollection::PlayerCollection()
PlayerCollection::PlayerCollection(int maxPlayerCount)
: maxPlayerCount(maxPlayerCount)
{
// Set default spawn point
setSpawnPoints({{0, 0}});
@ -29,6 +30,7 @@ void PlayerCollection::addPlayer(const std::shared_ptr<Player> &player)
{
newPlayerBuffer.push_back(player);
addDetachedChild(player);
updateInputIdentityAllowance();
}
void PlayerCollection::clear()
@ -47,6 +49,7 @@ void PlayerCollection::clear()
}
clearChildren();
updateInputIdentityAllowance();
}
void PlayerCollection::lateUpdate()
@ -77,6 +80,7 @@ void PlayerCollection::removePlayer(const std::shared_ptr<Player> &player)
{
removedPlayerBuffer.push_back(player);
removeChild(player);
updateInputIdentityAllowance();
}
std::vector<std::shared_ptr<Player>> PlayerCollection::getNewPlayers() const
@ -130,3 +134,13 @@ std::shared_ptr<Player> PlayerCollection::getPlayerById(int playerId) const
}
throw std::runtime_error("Player with id " + std::to_string(playerId) + " not found");
}
int PlayerCollection::getMaxPlayerCount() const
{
return maxPlayerCount;
}
void PlayerCollection::updateInputIdentityAllowance() const
{
InputMapper::getInstance()->allowNewInputIdentities = getPlayers().size() < maxPlayerCount;
}

View file

@ -8,7 +8,7 @@
class PlayerCollection : public GameObject
{
public:
PlayerCollection();
explicit PlayerCollection(int maxPlayerCount = DEFAULT_MAX_PLAYER_NUMBER);
static std::shared_ptr<PlayerCollection> getInstance();
@ -26,6 +26,8 @@ public:
[[nodiscard]] std::vector<std::shared_ptr<Player>> getRemovedPlayers() const;
[[nodiscard]] int getMaxPlayerCount() const;
void lateUpdate() override;
void clear();
@ -40,6 +42,10 @@ private:
int nextSpawnPointIndex = 0;
void spawnPlayer(const std::shared_ptr<InputIdentity> &inputIdentity);
int maxPlayerCount;
void updateInputIdentityAllowance() const;
};

View file

@ -12,7 +12,7 @@ std::map<std::string, LevelConfig> const all_levels = {
{18, 18},
{0, 18},
{18, 0}}, {
CollectableInLevel("box", {0, 0})
CollectableInLevel("box", {3, 5})
},
{
// Blues