Commit 82bd42e5 authored by Georg Schaefer's avatar Georg Schaefer
Browse files

Merge branch 'feature/22-level-collision-detection' into 'develop'

Resolve "Level: Collision-Detection"

Closes #22

See merge request !7
parents 57107559 d6ef9ae9
Pipeline #3269 passed with stage
in 2 minutes and 38 seconds
{
"Transform":{
"scale": {"x": 0.5, "y": 0.5, "z": 0.5}
"Transform": {
"scale": {
"x": 0.5,
"y": 0.5,
"z": 0.5
}
},
"Model": {
"aid": "model:cube"
},
"Shadowcaster": {
},
"Shadowcaster": {},
"Movement": {
"beats_per_step": 1,
"distance_per_step": 5,
......@@ -15,5 +18,8 @@
"overshoot": 4
},
"Input_controller": {},
"Player": {}
"Player": {},
"Rigid_body": {
"radius": 1.0
}
}
#include "rigid_body_comp.hpp"
#pragma once
#include <mirrage/ecs/ecs.hpp>
namespace phase_shifter::gameplay {
struct Rigid_body_comp : public mirrage::ecs::Component<Rigid_body_comp> {
static constexpr const char* name() { return "Rigid_body"; }
using Component::Component;
float radius;
};
sf2_structDef(Rigid_body_comp, radius);
} // namespace phase_shifter::gameplay
......@@ -3,7 +3,7 @@
#include <sstream>
namespace phase_shifter::level {
Level::Level(Tileset_ptr tileset, vector_2d<char> tiles) : _tileset(tileset), _tiles(tiles) {}
Level::Level(Tileset_ptr tileset, vector_2d<char> tiles) : tileset(tileset), tiles(tiles) {}
} // namespace phase_shifter::level
namespace mirrage::asset {
......
......@@ -25,19 +25,14 @@ namespace phase_shifter::level {
using Tileset_ptr = mirrage::asset::Ptr<Tileset>;
class Level {
friend class Level_system;
public:
struct Level {
template <typename T>
using vector_2d = std::vector<std::vector<T>>;
Level(Tileset_ptr tileset, vector_2d<char> tiles);
private:
Tileset_ptr _tileset;
vector_2d<char> _tiles;
Tileset_ptr tileset;
vector_2d<char> tiles;
};
using Level_ptr = mirrage::asset::Ptr<Level>;
......
#include "level_system.hpp"
#include "../gameplay/movement_comp.hpp"
#include "../gameplay/player_comp.hpp"
#include "../gameplay/rigid_body_comp.hpp"
#include "../gameplay/viewtarget_comp.hpp"
#include <mirrage/ecs/components/transform_comp.hpp>
......@@ -12,6 +15,8 @@ namespace phase_shifter::level {
Level_system::Level_system(mirrage::ecs::Entity_manager& entities, mirrage::asset::Asset_manager& assets)
: _entities(entities), _assets(assets)
{
_entities.register_component_type<gameplay::Rigid_body_comp>();
_spawners.emplace("p", [&](const Tile& tile, const glm::vec3& position) {
mirrage::ecs::Entity_facet player =
_entities.entity_builder(tile.spawns).position(position).create();
......@@ -36,28 +41,74 @@ namespace phase_shifter::level {
});
}
auto Level_system::load(const std::string& name) -> void
{
_current_level = _assets.load<Level>(mirrage::asset::AID("level:" + name));
auto& level = _current_level.get_blocking();
auto& tiles = level._tileset->tiles;
auto offset = level._tileset->tile_size;
glm::vec3 position{0.f};
for(auto&& row : level._tiles) {
position.x = 0.f;
for(auto&& tile_key : row) {
std::string key(1, tile_key);
if(auto tile_it = tiles.find(key); tile_it != tiles.end()) {
auto& tile = tile_it->second;
_entities.entity_builder(tile.blueprint).position(position).create();
if(tile.spawn) {
_spawn_function(key)(tile, position);
auto Level_system::update(const mirrage::util::Time& time) -> void {}
namespace {
using action = std::function<auto(const Tileset& tileset,
const std::string& key,
const Tile& tile,
const glm::vec3& position)
->void>;
auto for_each(const Level& level, const action& action)
{
auto& tiles = level.tileset->tiles;
auto offset = level.tileset->tile_size;
glm::vec3 position{0.f};
for(auto&& row : level.tiles) {
position.x = 0.f;
for(auto&& tile_key : row) {
std::string key(1, tile_key);
if(auto tile_it = tiles.find(key); tile_it != tiles.end()) {
auto& tile = tile_it->second;
action(level.tileset.get_blocking(), key, tile, position);
position.x += offset;
}
position.x += offset;
}
position.z += offset;
}
position.z += offset;
}
} // namespace
auto Level_system::load(const std::string& name) -> void
{
_current_level = _assets.load<Level>(mirrage::asset::AID("level:" + name));
auto& level = _current_level.get_blocking();
for_each(level, [&](auto&, const std::string& key, const Tile& tile, const glm::vec3& position) {
_entities.entity_builder(tile.blueprint).position(position).create();
if(tile.spawn) {
_spawn_function(key)(tile, position);
}
});
}
auto Level_system::ray_cast(const glm::vec2& origin, const glm::vec2& direction)
-> std::pair<bool, util::Contact>
{
auto& level = _current_level.get_blocking();
std::pair<bool, util::Contact> result{false, {}};
for_each(level,
[&](const Tileset& tileset,
const std::string& key,
const Tile& tile,
const glm::vec3& position) {
if(!tile.solid) {
return;
}
glm::vec2 tile_origin{position.x, position.z};
auto half_size = tileset.tile_size / 2.f;
glm::vec2 half_vector{half_size, half_size};
auto [hit, contact] = util::intersect({origin, direction}, {tile_origin, half_vector});
if(!hit) {
return;
}
if(contact.distance2 < result.second.distance2) {
result.first = true;
result.second = contact;
}
});
return result;
}
auto Level_system::_spawn_function(const std::string& key)
......
#pragma once
#include "../util/collision.hpp"
#include "level.hpp"
#include <mirrage/utils/units.hpp>
#include <functional>
#include <unordered_map>
......@@ -20,7 +23,9 @@ namespace phase_shifter::level {
public:
Level_system(mirrage::ecs::Entity_manager& entities, mirrage::asset::Asset_manager& assets);
auto update(const mirrage::util::Time& time) -> void;
auto load(const std::string& name) -> void;
auto ray_cast(const glm::vec2& origin, const glm::vec2& direction) -> std::pair<bool, util::Contact>;
private:
auto _spawn_function(const std::string& key)
......
......@@ -110,6 +110,7 @@ namespace phase_shifter {
_beat_system->update(dt);
_input_system->update(dt);
_level_system->update(dt);
_movement_system->update(dt);
_combat_system->update(dt);
_camera_system->update(dt);
......
#include "collision.hpp"
#include <glm/gtc/epsilon.hpp>
#include <glm/gtx/intersect.hpp>
#include <glm/gtx/norm.hpp>
namespace phase_shifter::util {
auto intersect(const Ray& ray, const Aabb& aabb) -> std::pair<bool, Contact>
{
auto min = aabb.origin - aabb.half_vec;
auto max = aabb.origin + aabb.half_vec;
float tmin = (min.x - ray.origin.x) / ray.direction.x;
float tmax = (max.x - ray.origin.x) / ray.direction.x;
if(tmin > tmax) {
std::swap(tmin, tmax);
}
float tymin = (min.y - ray.origin.y) / ray.direction.y;
float tymax = (max.y - ray.origin.y) / ray.direction.y;
if(tymin > tymax) {
std::swap(tymin, tymax);
}
if((tmin > tymax) || (tymin > tmax)) {
return {false, {}};
}
glm::vec2 normal{-ray.direction.x, 0.f};
if(tymin > tmin) {
tmin = tymin;
normal.x = 0.f;
normal.y = -ray.direction.y;
}
glm::vec2 point{ray.origin + tmin * ray.direction};
return {true, {point, glm::normalize(normal), glm::distance2(point, ray.origin)}};
}
auto intersect(const Ray& ray, const Circle& circle) -> std::pair<bool, Contact>
{
glm::vec3 point;
glm::vec3 normal;
if(glm::intersectRaySphere(glm::vec3{ray.origin.x, 0.f, ray.origin.y},
glm::vec3{ray.direction.x, 0.f, ray.direction.y},
glm::vec3{circle.origin.x, 0.f, circle.origin.y},
circle.radius,
point,
normal)) {
return {true,
{glm::vec2{point.x, point.y},
glm::vec2{normal.x, normal.y},
glm::distance2(glm::vec3{ray.origin.x, 0.f, ray.origin.y}, point)}};
}
return {false, {}};
}
auto intersect(const Circle& circle1, const Circle& circle2) -> std::pair<bool, Contact>
{
auto c2_to_c1 = circle1.origin - circle2.origin;
auto distance2 = glm::length2(c2_to_c1);
auto combined_radius = circle1.radius + circle2.radius;
auto distance = distance2 - combined_radius * combined_radius;
if(glm::abs(distance) <= glm::epsilon<float>()) {
auto normal = glm::normalize(c2_to_c1);
return {true, {circle2.origin + circle2.radius * normal, normal, distance}};
}
return {false, {}};
}
} // namespace phase_shifter::util
#pragma once
#include <tuple>
#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
namespace phase_shifter::util {
struct Contact {
glm::vec2 point{0.f};
glm::vec2 normal{0.f};
float distance2 = std::numeric_limits<float>::max();
};
struct Ray {
glm::vec2 origin;
glm::vec2 direction;
};
struct Aabb {
glm::vec2 origin;
glm::vec2 half_vec;
};
struct Circle {
glm::vec2 origin;
float radius;
};
auto intersect(const Ray& ray, const Aabb& aabb) -> std::pair<bool, Contact>;
auto intersect(const Ray& ray, const Circle& circle) -> std::pair<bool, Contact>;
auto intersect(const Circle& circle1, const Circle& circle2) -> std::pair<bool, Contact>;
} // namespace phase_shifter::util
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment