} | } | ||||
} | } | ||||
Swan::Entity &WGDefault::spawnPlayer(Swan::WorldPlane &plane) { | |||||
return plane.spawnEntity("core::player", Swan::SRFFloatArray{0, 0}); | |||||
Swan::BodyTrait::HasBody *WGDefault::spawnPlayer(Swan::WorldPlane &plane) { | |||||
return dynamic_cast<Swan::BodyTrait::HasBody *>( | |||||
plane.spawnEntity("core::player", Swan::SRFFloatArray{0, 0})); | |||||
} | } |
tStone_(world.getTileID("core::stone")), tAir_(world.getTileID("core::air")) {} | tStone_(world.getTileID("core::stone")), tAir_(world.getTileID("core::air")) {} | ||||
void genChunk(Swan::WorldPlane &plane, Swan::Chunk &chunk) override; | void genChunk(Swan::WorldPlane &plane, Swan::Chunk &chunk) override; | ||||
Swan::Entity &spawnPlayer(Swan::WorldPlane &plane) override; | |||||
Swan::BodyTrait::HasBody *spawnPlayer(Swan::WorldPlane &plane) override; | |||||
private: | private: | ||||
Swan::Tile::ID tGrass_, tDirt_, tStone_, tAir_; | Swan::Tile::ID tGrass_, tDirt_, tStone_, tAir_; |
add_library(libswan SHARED | add_library(libswan SHARED | ||||
src/traits/BodyTrait.cc | |||||
src/Animation.cc | src/Animation.cc | ||||
src/Body.cc | |||||
src/Chunk.cc | src/Chunk.cc | ||||
src/Clock.cc | src/Clock.cc | ||||
src/Game.cc | src/Game.cc |
#pragma once | |||||
#include "common.h" | |||||
#include "BoundingBox.h" | |||||
namespace Swan { | |||||
class WorldPlane; | |||||
class Body { | |||||
public: | |||||
Body(Vec2 size, float mass, Vec2 pos = Vec2::ZERO): | |||||
size_(size), mass_(mass), pos_(pos) {}; | |||||
void friction(Vec2 coef = Vec2(400, 50)); | |||||
void gravity(Vec2 g = Vec2(0, 20)); | |||||
void outline(Win &win); | |||||
void update(WorldPlane &plane, float dt); | |||||
void updateWithoutCollision(float dt); | |||||
BoundingBox getBounds() { return { pos_, size_ }; } | |||||
Vec2 force_ = { 0, 0 }; | |||||
Vec2 vel_ = { 0, 0 }; | |||||
bool on_ground_ = false; | |||||
Vec2 size_; | |||||
float mass_; | |||||
Vec2 pos_; | |||||
float bounciness_ = 0; | |||||
float mushyness_ = 2; | |||||
private: | |||||
void collideX(WorldPlane &plane); | |||||
void collideY(WorldPlane &plane); | |||||
}; | |||||
} |
#pragma once | |||||
#include "Vector2.h" | |||||
namespace Swan { | |||||
struct BoundingBox { | |||||
Vec2 pos; | |||||
Vec2 size; | |||||
double left() { return pos.x; } | |||||
double right() { return pos.x + size.x; } | |||||
double top() { return pos.y; } | |||||
double bottom() { return pos.y + size.y; } | |||||
}; | |||||
} |
#include "common.h" | #include "common.h" | ||||
#include "log.h" | #include "log.h" | ||||
#include "traits/BodyTrait.h" | |||||
#include "SRF.h" | #include "SRF.h" | ||||
#include "BoundingBox.h" | |||||
#include "Body.h" | |||||
namespace Swan { | namespace Swan { | ||||
virtual ~Entity() = default; | virtual ~Entity() = default; | ||||
virtual std::optional<BoundingBox> getBounds() { return std::nullopt; } | |||||
virtual void draw(const Context &ctx, Win &win) {} | virtual void draw(const Context &ctx, Win &win) {} | ||||
virtual void update(const Context &ctx, float dt) {} | virtual void update(const Context &ctx, float dt) {} | ||||
virtual void tick(const Context &ctx, float dt) {} | virtual void tick(const Context &ctx, float dt) {} | ||||
virtual void move(const Vec2 &pos) {} | |||||
virtual void moveTo(const Vec2 &pos) {} | |||||
virtual void despawn() {} | virtual void despawn() {} | ||||
virtual void readSRF(const Swan::Context &ctx, const SRF &srf) {} | virtual void readSRF(const Swan::Context &ctx, const SRF &srf) {} | ||||
virtual SRF *writeSRF(const Swan::Context &ctx) { return new SRFNone(); } | virtual SRF *writeSRF(const Swan::Context &ctx) { return new SRFNone(); } | ||||
}; | }; | ||||
class PhysicsEntity: public Entity { | |||||
class PhysicsEntity: public Entity, public BodyTrait::HasBody { | |||||
public: | public: | ||||
PhysicsEntity(Vec2 size, float mass): | PhysicsEntity(Vec2 size, float mass): | ||||
body_(size, mass) {} | body_(size, mass) {} | ||||
virtual std::optional<BoundingBox> getBounds() override { return body_.getBounds(); } | |||||
virtual BodyTrait::Body &getBody() override { return body_; } | |||||
virtual void update(const Context &ctx, float dt) override { | virtual void update(const Context &ctx, float dt) override { | ||||
body_.friction(); | body_.friction(); | ||||
body_.update(ctx.plane, dt); | body_.update(ctx.plane, dt); | ||||
} | } | ||||
virtual void move(const Vec2 &rel) override { body_.pos_ += rel; } | |||||
virtual void moveTo(const Vec2 &pos) override { body_.pos_ = pos; } | |||||
protected: | protected: | ||||
Body body_; | |||||
BodyTrait::PhysicsBody body_; | |||||
}; | }; | ||||
} | } |
std::unordered_map<std::string, WorldGen::Factory *> worldgens_; | std::unordered_map<std::string, WorldGen::Factory *> worldgens_; | ||||
std::unordered_map<std::string, Entity::Factory *> ents_; | std::unordered_map<std::string, Entity::Factory *> ents_; | ||||
Entity *player_; | |||||
BodyTrait::HasBody *player_; | |||||
Game *game_; | Game *game_; | ||||
std::mt19937 random_; | std::mt19937 random_; |
#include "Chunk.h" | #include "Chunk.h" | ||||
#include "Entity.h" | #include "Entity.h" | ||||
#include "traits/BodyTrait.h" | |||||
namespace Swan { | namespace Swan { | ||||
virtual ~WorldGen() = default; | virtual ~WorldGen() = default; | ||||
virtual void genChunk(WorldPlane &plane, Chunk &chunk) = 0; | virtual void genChunk(WorldPlane &plane, Chunk &chunk) = 0; | ||||
virtual Entity &spawnPlayer(WorldPlane &plane) = 0; | |||||
virtual BodyTrait::HasBody *spawnPlayer(WorldPlane &plane) = 0; | |||||
}; | }; | ||||
} | } |
#include <set> | #include <set> | ||||
#include "common.h" | #include "common.h" | ||||
#include "traits/BodyTrait.h" | |||||
#include "Chunk.h" | #include "Chunk.h" | ||||
#include "Tile.h" | #include "Tile.h" | ||||
#include "WorldGen.h" | #include "WorldGen.h" | ||||
WorldPlane(ID id, World *world, std::shared_ptr<WorldGen> gen): | WorldPlane(ID id, World *world, std::shared_ptr<WorldGen> gen): | ||||
id_(id), world_(world), gen_(std::move(gen)) {} | id_(id), world_(world), gen_(std::move(gen)) {} | ||||
Entity &spawnEntity(const std::string &name, const SRF ¶ms); | |||||
Entity *spawnEntity(const std::string &name, const SRF ¶ms); | |||||
void despawnEntity(Entity &ent); | void despawnEntity(Entity &ent); | ||||
Context getContext(); | Context getContext(); | ||||
Tile::ID getTileID(TilePos pos); | Tile::ID getTileID(TilePos pos); | ||||
Tile &getTile(TilePos pos); | Tile &getTile(TilePos pos); | ||||
Entity &spawnPlayer(); | |||||
BodyTrait::HasBody *spawnPlayer(); | |||||
void breakBlock(TilePos pos); | void breakBlock(TilePos pos); | ||||
void draw(Win &win); | void draw(Win &win); |
#pragma once | #pragma once | ||||
#include <swan/traits/BodyTrait.h> | |||||
#include <swan/Animation.h> | #include <swan/Animation.h> | ||||
#include <swan/Body.h> | |||||
#include <swan/BoundingBox.h> | |||||
#include <swan/Chunk.h> | #include <swan/Chunk.h> | ||||
#include <swan/Clock.h> | #include <swan/Clock.h> | ||||
#include <swan/Entity.h> | #include <swan/Entity.h> |
#pragma once | |||||
#include "../Vector2.h" | |||||
namespace Swan { | |||||
class WorldPlane; | |||||
class Win; | |||||
namespace BodyTrait { | |||||
class Body; | |||||
class HasBody { | |||||
public: | |||||
virtual Body &getBody() = 0; | |||||
}; | |||||
struct Bounds { | |||||
Vec2 pos; | |||||
Vec2 size; | |||||
double left() { return pos.x; } | |||||
double right() { return pos.x + size.x; } | |||||
double top() { return pos.y; } | |||||
double bottom() { return pos.y + size.y; } | |||||
}; | |||||
class Body { | |||||
public: | |||||
virtual Bounds getBounds() = 0; | |||||
virtual void move(Vec2 rel) = 0; | |||||
virtual void moveTo(Vec2 pos) = 0; | |||||
}; | |||||
// PhysicsBody is an implementation of BodyTrait::Body which implements | |||||
// a bunch of physics stuff. | |||||
class PhysicsBody: public Body { | |||||
public: | |||||
PhysicsBody(Vec2 size, float mass, Vec2 pos = Vec2::ZERO): | |||||
size_(size), mass_(mass), pos_(pos) {}; | |||||
BodyTrait::Bounds getBounds() override { return BodyTrait::Bounds{ pos_, size_ }; }; | |||||
void move(Vec2 rel) { pos_ += rel; } | |||||
void moveTo(Vec2 pos) { pos_ = pos; } | |||||
void friction(Vec2 coef = Vec2(400, 50)); | |||||
void gravity(Vec2 g = Vec2(0, 20)); | |||||
void outline(Win &win); | |||||
void update(WorldPlane &plane, float dt); | |||||
void updateWithoutCollision(float dt); | |||||
Vec2 force_ = { 0, 0 }; | |||||
Vec2 vel_ = { 0, 0 }; | |||||
bool on_ground_ = false; | |||||
Vec2 size_; | |||||
float mass_; | |||||
Vec2 pos_; | |||||
float bounciness_ = 0; | |||||
float mushyness_ = 2; | |||||
private: | |||||
void collideX(WorldPlane &plane); | |||||
void collideY(WorldPlane &plane); | |||||
}; | |||||
} | |||||
} |
} | } | ||||
void World::spawnPlayer() { | void World::spawnPlayer() { | ||||
player_ = &planes_[current_plane_].spawnPlayer(); | |||||
player_ = planes_[current_plane_].spawnPlayer(); | |||||
} | } | ||||
void World::setCurrentPlane(WorldPlane &plane) { | void World::setCurrentPlane(WorldPlane &plane) { | ||||
} | } | ||||
void World::draw(Win &win) { | void World::draw(Win &win) { | ||||
auto bounds = *player_->getBounds(); | |||||
auto bounds = player_->getBody().getBounds(); | |||||
win.cam_ = bounds.pos - (win.getSize() / 2) + (bounds.size / 2); | win.cam_ = bounds.pos - (win.getSize() / 2) + (bounds.size / 2); | ||||
planes_[current_plane_].draw(win); | planes_[current_plane_].draw(win); | ||||
} | } | ||||
for (auto &plane: planes_) | for (auto &plane: planes_) | ||||
plane.tick(dt); | plane.tick(dt); | ||||
auto bounds = *player_->getBounds(); | |||||
auto bounds = player_->getBody().getBounds(); | |||||
chunk_renderer_.tick( | chunk_renderer_.tick( | ||||
planes_[current_plane_], | planes_[current_plane_], | ||||
ChunkPos((int)bounds.pos.x / CHUNK_WIDTH, (int)bounds.pos.y / CHUNK_HEIGHT)); | ChunkPos((int)bounds.pos.x / CHUNK_WIDTH, (int)bounds.pos.y / CHUNK_HEIGHT)); |
return { .game = *world_->game_, .world = *world_, .plane = *this, .resources = world_->resources_ }; | return { .game = *world_->game_, .world = *world_, .plane = *this, .resources = world_->resources_ }; | ||||
} | } | ||||
Entity &WorldPlane::spawnEntity(const std::string &name, const SRF ¶ms) { | |||||
Entity *WorldPlane::spawnEntity(const std::string &name, const SRF ¶ms) { | |||||
if (world_->ents_.find(name) == world_->ents_.end()) { | if (world_->ents_.find(name) == world_->ents_.end()) { | ||||
panic << "Tried to spawn a non-existant entity " << name << "!"; | panic << "Tried to spawn a non-existant entity " << name << "!"; | ||||
abort(); | abort(); | ||||
} | } | ||||
Entity *ent = world_->ents_[name]->create(getContext(), params); | Entity *ent = world_->ents_[name]->create(getContext(), params); | ||||
if (auto bounds = ent->getBounds(); bounds) { | |||||
ent->move({ 0.5f - bounds->size.x / 2, 0 }); | |||||
if (auto has_body = dynamic_cast<BodyTrait::HasBody *>(ent); has_body) { | |||||
BodyTrait::Body &body = has_body->getBody(); | |||||
BodyTrait::Bounds bounds = body.getBounds(); | |||||
body.move({ 0.5f - bounds.size.x / 2, 0 }); | |||||
} | } | ||||
spawn_list_.push_back(std::unique_ptr<Entity>(ent)); | spawn_list_.push_back(std::unique_ptr<Entity>(ent)); | ||||
info << "Spawned " << name << ". SRF: " << params; | info << "Spawned " << name << ". SRF: " << params; | ||||
return *ent; | |||||
return ent; | |||||
} | } | ||||
void WorldPlane::despawnEntity(Entity &ent) { | void WorldPlane::despawnEntity(Entity &ent) { | ||||
return world_->getTileByID(getTileID(pos)); | return world_->getTileByID(getTileID(pos)); | ||||
} | } | ||||
Entity &WorldPlane::spawnPlayer() { | |||||
BodyTrait::HasBody *WorldPlane::spawnPlayer() { | |||||
return gen_->spawnPlayer(*this); | return gen_->spawnPlayer(*this); | ||||
} | } | ||||
} | } | ||||
void WorldPlane::draw(Win &win) { | void WorldPlane::draw(Win &win) { | ||||
auto pbounds = *world_->player_->getBounds(); | |||||
auto pbounds = world_->player_->getBody().getBounds(); | |||||
ChunkPos pcpos = ChunkPos( | ChunkPos pcpos = ChunkPos( | ||||
(int)floor(pbounds.pos.x / CHUNK_WIDTH), | (int)floor(pbounds.pos.x / CHUNK_WIDTH), | ||||
(int)floor(pbounds.pos.y / CHUNK_HEIGHT)); | (int)floor(pbounds.pos.y / CHUNK_HEIGHT)); |
#include "Body.h" | |||||
#include "traits/BodyTrait.h" | |||||
#include <math.h> | #include <math.h> | ||||
#include <array> | #include <array> | ||||
#include "Win.h" | #include "Win.h" | ||||
namespace Swan { | namespace Swan { | ||||
namespace BodyTrait { | |||||
static float epsilon = 0.0001; | static float epsilon = 0.0001; | ||||
void Body::friction(Vec2 coef) { | |||||
void PhysicsBody::friction(Vec2 coef) { | |||||
force_ += -vel_ * coef; | force_ += -vel_ * coef; | ||||
} | } | ||||
void Body::gravity(Vec2 g) { | |||||
void PhysicsBody::gravity(Vec2 g) { | |||||
force_ += g * mass_; | force_ += g * mass_; | ||||
} | } | ||||
void Body::collideX(WorldPlane &plane) { | |||||
void PhysicsBody::collideX(WorldPlane &plane) { | |||||
auto bounds = getBounds(); | auto bounds = getBounds(); | ||||
bool collided = false; | bool collided = false; | ||||
} | } | ||||
} | } | ||||
void Body::collideY(WorldPlane &plane) { | |||||
void PhysicsBody::collideY(WorldPlane &plane) { | |||||
auto bounds = getBounds(); | auto bounds = getBounds(); | ||||
bool collided = false; | bool collided = false; | ||||
on_ground_ = false; | on_ground_ = false; | ||||
} | } | ||||
} | } | ||||
void Body::outline(Win &win) { | |||||
void PhysicsBody::outline(Win &win) { | |||||
win.drawRect(pos_, size_); | win.drawRect(pos_, size_); | ||||
} | } | ||||
void Body::update(WorldPlane &plane, float dt) { | |||||
void PhysicsBody::update(WorldPlane &plane, float dt) { | |||||
vel_ += (force_ / mass_) * dt; | vel_ += (force_ / mass_) * dt; | ||||
force_ = { 0, 0 }; | force_ = { 0, 0 }; | ||||
collideY(plane); | collideY(plane); | ||||
} | } | ||||
void Body::updateWithoutCollision(float dt) { | |||||
void PhysicsBody::updateWithoutCollision(float dt) { | |||||
vel_ += (force_ / mass_) * dt; | vel_ += (force_ / mass_) * dt; | ||||
pos_ += vel_ * dt; | pos_ += vel_ * dt; | ||||
force_ = { 0, 0 }; | force_ = { 0, 0 }; | ||||
} | } | ||||
} | } | ||||
} |