Commit 14cccc95 authored by Georg Schäfer's avatar Georg Schäfer
Browse files

Merge branch 'develop' into feature/11-ui-calibration-screen

parents ce194bf1 a670493a
{
// Informationen zu dieser Datei finden Sie unter https://go.microsoft.com//fwlink//?linkid=834763.
"configurations": [
{
"name": "x64-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"inheritEnvironments": [ "msvc_x64_x64" ],
"buildRoot": "${projectDir}\\out\\build\\${name}",
"installRoot": "${projectDir}\\out\\install\\${name}",
"cmakeCommandArgs": "",
"buildCommandArgs": "-v",
"ctestCommandArgs": ""
}
]
}
\ No newline at end of file
......@@ -17,9 +17,19 @@
"step_time_percentage": 0.2,
"off_beat_threshold": 0.0
},
"FixedPath": {
"pause_between_steps": 1,
"wait_beats": 1
},
"Shooting": {
"default_orientation": 270,
"rotation_per_step": 15,
"max_rotation": 30,
"spawn_offset": 1,
"attack_radius": 5,
"pause_between_shots": 1
},
"Killable": {
"radius": 1
},
"FixedPath": {
}
}
}
{
"Transform": {
"scale": {
"x": 0.3,
"y": 0.3,
"z": 0.3
"x": 0.2,
"y": 0.2,
"z": 0.2
}
},
"Model": {
......@@ -19,7 +19,9 @@
},
"Fragile": {},
"ContinuousPath": {
"direction": 270,
"curvature": 0
}
},
"Rigid_body": {
"radius": 0.3
},
"Bullet": {}
}
......@@ -25,7 +25,8 @@
"Stationary_attack": {
"radius": 3
},
"Rigid_body": {
"radius": 0.5
}
"Rigid_body": {
"radius": 0.5
},
"Target": {}
}
......@@ -41,7 +41,7 @@ namespace phase_shifter::gameplay {
_beat_index++;
LOG(plog::debug) << "beat";
beats_left = size - 1 - _beat_index - _beat_offset;
beats_left = size - 1 - _beat_index + _beat_offset;
if(beats_left <= 0 || _beat_index + 1 >= size) {
_bus.send<Lose_msg>();
}
......
#pragma once
#include <mirrage/ecs/ecs.hpp>
#include <mirrage/utils/sf2_glm.hpp>
#include <mirrage/utils/units.hpp>
namespace phase_shifter::gameplay {
struct Bullet_comp : public mirrage::ecs::Stateless_tag_component<Bullet_comp> {
static constexpr const char* name() { return "Bullet"; }
using Stateless_tag_component::Stateless_tag_component;
};
} // namespace phase_shifter::gameplay
......@@ -12,8 +12,10 @@ namespace phase_shifter::gameplay {
float direction = 0; // angle of direction with 0° beeing North (-Z)
float curvature = 0; // angle of curvature in degrees
unsigned int pause_between_steps = 0; // number of beats the entity waits between steps
unsigned int wait_beats = 0; // number of beats the entity waits before taking the next step
};
sf2_structDef(Continuous_path_comp, direction, curvature);
sf2_structDef(Continuous_path_comp, direction, curvature, pause_between_steps, wait_beats);
} // namespace phase_shifter::gameplay
\ No newline at end of file
#include "enemy_system.hpp"
#include "beat_system.hpp"
#include "combat_system.hpp"
#include "movement_comp.hpp"
#include "player_comp.hpp"
#include "rigid_body_comp.hpp"
#include <mirrage/ecs/components/transform_comp.hpp>
namespace phase_shifter::gameplay {
Enemy_system::Enemy_system(mirrage::ecs::Entity_manager& entity_manager) : _entity_manager(entity_manager)
using namespace mirrage::ecs;
Enemy_system::Enemy_system(mirrage::util::Message_bus& bus,
Entity_manager& entity_manager,
const Beat_system& beat_system)
: _bus(bus), _entity_manager(entity_manager), _beat_system(beat_system)
{
_entity_manager.register_component_type<Fixed_path_comp>();
_entity_manager.register_component_type<Follow_target_comp>();
_entity_manager.register_component_type<Continuous_path_comp>();
_entity_manager.register_component_type<Shooting_comp>();
_entity_manager.register_component_type<Target_comp>();
_entity_manager.register_component_type<Bullet_comp>();
}
void Enemy_system::update(mirrage::util::Time dt)
{
for (auto&& [movement, fixed_path] : _entity_manager.list<Movement_comp, Fixed_path_comp>()) {
if (!movement.move) {
float rad_direction = fixed_path.next_direction() * mirrage::util::PI / 180.f;
if (rad_direction >= 0) {
movement.aim.x = std::sin(rad_direction);
movement.aim.y = -std::cos(rad_direction);
movement.move = true;
auto beat = _beat_system.beat_state();
do_movement(beat);
do_shooting(beat);
do_bullet_hit_detection();
}
void Enemy_system::do_movement(Beat_state& beat)
{
do_fixed_path_movement(beat);
do_cont_path_movement(beat);
do_hunting_movement(beat);
}
void Enemy_system::do_fixed_path_movement(Beat_state& beat)
{
for(auto&& [movement, fixed_path] : _entity_manager.list<Movement_comp, Fixed_path_comp>()) {
if(beat.beat) {
if(fixed_path.wait_beats == 0 && !movement.move) {
float direction = fixed_path.next_direction();
if(direction < 360) {
float rad_direction = direction * mirrage::util::PI / 180.f;
movement.aim.x = std::sin(rad_direction);
movement.aim.y = -std::cos(rad_direction);
movement.move = true;
}
fixed_path.wait_beats = fixed_path.pause_between_steps;
} else {
fixed_path.wait_beats--;
}
}
}
}
void Enemy_system::do_cont_path_movement(Beat_state& beat)
{
for(auto&& [movement, cont_path] : _entity_manager.list<Movement_comp, Continuous_path_comp>()) {
if(beat.beat) {
if(cont_path.wait_beats == 0 && !movement.move) {
float rad_direction = cont_path.next_direction() * mirrage::util::PI / 180.f;
movement.aim.x = std::sin(rad_direction);
movement.aim.y = -std::cos(rad_direction);
movement.move = true;
cont_path.wait_beats = cont_path.pause_between_steps;
} else {
cont_path.wait_beats--;
}
}
}
for (auto&&[movement, follow_target, transform] : _entity_manager.list<Movement_comp, Follow_target_comp, mirrage::ecs::components::Transform_comp>()) {
auto target_facet = _entity_manager.get(follow_target.target);
}
if(target_facet.is_some()) {
auto target_transform = target_facet.get_or_throw().get<mirrage::ecs::components::Transform_comp>();
void Enemy_system::do_hunting_movement(Beat_state& beat)
{
for(auto&& [movement, follow_target, transform] :
_entity_manager.list<Movement_comp, Follow_target_comp, components::Transform_comp>()) {
if(beat.beat) {
if(follow_target.wait_beats == 0 && !movement.move) {
auto target_facet = _entity_manager.get(follow_target.target);
if(target_transform.is_some()) {
auto& target_position = target_transform.get_or_throw().position;
auto& my_position = transform.position;
movement.aim = glm::vec2({target_position.x - my_position.x, target_position.z - my_position.z});
movement.move = true;
if(target_facet.is_some()) {
auto target_transform = target_facet.get_or_throw().get<components::Transform_comp>();
if(target_transform.is_some()) {
auto& target_position = target_transform.get_or_throw().position;
auto& my_position = transform.position;
movement.aim = glm::vec2(
{target_position.x - my_position.x, target_position.z - my_position.z});
movement.move = true;
}
}
follow_target.wait_beats = follow_target.pause_between_steps;
} else {
follow_target.wait_beats--;
}
}
}
}
for(auto&& [movement, cont_path] : _entity_manager.list<Movement_comp, Continuous_path_comp>()) {
if(!movement.move) {
float rad_direction = cont_path.next_direction() * mirrage::util::PI / 180.f;
movement.aim.x = std::sin(rad_direction);
movement.aim.y = -std::cos(rad_direction);
movement.move = true;
void Enemy_system::do_shooting(Beat_state& beat)
{
for(auto&& [entity, shooting, transform] :
_entity_manager.list<Entity_facet, Shooting_comp, components::Transform_comp>()) {
auto my_position = transform.position;
glm::vec3 closest_target_pos(0.f);
float closest_dist = 999999999.f;
for(auto&& [target, target_comp, target_transform] :
_entity_manager.list<Entity_facet, Target_comp, components::Transform_comp>()) {
auto target_position = target_transform.position;
auto dist = glm::length(glm::vec2(target_position.x, target_position.z)
- glm::vec2(my_position.x, my_position.z));
if(dist < closest_dist) {
closest_dist = dist;
closest_target_pos = target_position;
}
}
glm::vec2 t_direction{closest_target_pos.x - my_position.x, closest_target_pos.z - my_position.z};
shooting.target_direction =
std::acos(-t_direction.y / glm::length(t_direction)) * 180.f / mirrage::util::PI;
if(t_direction.x < 0) {
shooting.target_direction *= -1;
}
if(closest_dist <= shooting.attack_radius) {
shooting.idle = false;
} else {
shooting.idle = true;
}
auto orientation = shooting.target_direction;
if(shooting.idle) {
orientation = shooting.default_orientation + shooting.rotation;
}
auto rad_orientation = orientation * mirrage::util::PI / 180.f;
if(beat.beat) {
if(shooting.idle && shooting.rotate) {
shooting.do_rotation();
shooting.rotate = false;
}
if(shooting.wait_beats == 0) {
auto bullet_pattern = shooting.next_pattern();
auto spawn_position =
transform.position
+ shooting.spawn_offset
* glm::vec3(std::sin(rad_orientation), 0, -std::cos(rad_orientation));
for(auto bullet : bullet_pattern.bullets) {
auto bullet_direction = orientation + bullet.direction;
auto rad_bullet_direction = bullet_direction * mirrage::util::PI / 180.f;
_entity_manager.entity_builder("bullet")
.position(spawn_position)
.rotation(glm::rotation({0, 0, 1},
glm::vec3(std::sin(rad_bullet_direction),
0,
-std::cos(rad_bullet_direction))))
.post_create([=](auto entity) {
entity.process([=](Continuous_path_comp& cont_path) {
cont_path.direction = bullet_direction;
cont_path.curvature = bullet.curvature;
});
entity.process([=](Movement_comp& move, Continuous_path_comp& cont_path) {
float rad_direction = cont_path.direction * mirrage::util::PI / 180.f;
move.aim.x = std::sin(rad_direction);
move.aim.y = -std::cos(rad_direction);
});
})
.create();
}
shooting.wait_beats = shooting.pause_between_shots;
shooting.rotate = true;
} else {
shooting.wait_beats--;
}
}
transform.orientation = glm::rotation(
{0, 0, 1}, glm::vec3(std::sin(rad_orientation), 0, -std::cos(rad_orientation)));
entity.process([&](Movement_comp& movement) { movement.want_orientation = false; });
}
}
}
\ No newline at end of file
void Enemy_system::do_bullet_hit_detection()
{
for(auto&& [bullet_handle, bullet, bullet_transform, bullet_body, bullet_movement] :
_entity_manager.list<Entity_handle,
Bullet_comp,
components::Transform_comp,
Rigid_body_comp,
Movement_comp>()) {
for(auto&& [player_handle, player, player_transform, player_body, player_movement] :
_entity_manager.list<Entity_handle,
Player_comp,
components::Transform_comp,
Rigid_body_comp,
Movement_comp>()) {
glm::vec2 bullet_pos{bullet_transform.position.x, bullet_transform.position.z};
glm::vec2 last_bullet_pos = bullet_movement.last_position;
glm::vec2 player_pos{player_transform.position.x, player_transform.position.z};
glm::vec2 last_player_pos = player_movement.last_position;
if(!bullet_movement.moved) {
last_bullet_pos = bullet_pos;
}
if(!player_movement.moved) {
last_player_pos = player_pos;
}
if(intersect(Stadium{last_bullet_pos, bullet_pos, bullet_body.radius},
Stadium{last_player_pos, player_pos, player_body.radius})) {
_entity_manager.erase(bullet_handle);
_bus.send<Damaged_msg>(player_handle);
}
}
}
}
bool Enemy_system::intersect(const Stadium& stad1, const Stadium& stad2)
{
glm::vec2 direction1 = stad1.point2 - stad1.point1;
glm::vec2 direction2 = stad2.point2 - stad2.point1;
// check for intersection of the line segments
float temp = -direction2.x * direction1.y + direction1.x * direction2.y;
if (temp != 0) {
float s = (-direction1.y * (stad1.point1.x - stad2.point1.x)
+ direction1.x * (stad1.point1.y - stad2.point1.y))
/ temp;
float t = (-direction2.y * (stad1.point1.x - stad2.point1.x)
+ direction2.x * (stad1.point1.y - stad2.point1.y))
/ temp;
if (s >= 0 && s <= 1 && t >= 0 && t <= 1) {
return true;
}
}
// check the distance from each end point to the other line segment
float comb_radius = stad1.radius + stad2.radius;
float length2 = glm::length2(direction1);
if(length2 <= 0.001f) {
if(glm::distance(stad2.point1, stad1.point1) < comb_radius
|| glm::distance(stad2.point2, stad1.point1) < comb_radius) {
return true;
}
}
float lambda = std::clamp(dot(stad2.point1 - stad1.point1, direction1), 0.f, 1.f);
if(glm::distance(stad2.point1, stad1.point1 + lambda * direction1) < comb_radius) {
return true;
}
lambda = std::clamp(dot(stad2.point2 - stad1.point1, direction1), 0.f, 1.f);
if(glm::distance(stad2.point2, stad1.point1 + lambda * direction1) < comb_radius) {
return true;
}
length2 = glm::length2(direction2);
if(length2 <= 0.001f) {
if(glm::distance(stad1.point1, stad2.point1) < comb_radius
|| glm::distance(stad1.point2, stad2.point1) < comb_radius) {
return true;
}
}
lambda = std::clamp(dot(stad1.point1 - stad2.point1, direction2), 0.f, 1.f);
if(glm::distance(stad1.point1, stad2.point1 + lambda * direction2) < comb_radius) {
return true;
}
lambda = std::clamp(dot(stad1.point2 - stad2.point1, direction2), 0.f, 1.f);
if(glm::distance(stad1.point2, stad2.point1 + lambda * direction2) < comb_radius) {
return true;
}
return false;
}
float Enemy_system::dot(const glm::vec2& a, const glm::vec2& b) { return a.x * b.x + a.y * b.y; }
} // namespace phase_shifter::gameplay
#pragma once
#include <mirrage/utils/units.hpp>
#include <mirrage/ecs/entity_manager.hpp>
#include <mirrage/utils/units.hpp>
#include "bullet_comp.hpp"
#include "continuous_path_comp.hpp"
#include "fixed_path_comp.hpp"
#include "follow_target_comp.hpp"
#include "continuous_path_comp.hpp"
#include "shooting_comp.hpp"
#include "target_comp.hpp"
#include "beat_system.hpp"
namespace phase_shifter::gameplay {
struct Stadium {
glm::vec2 point1;
glm::vec2 point2;
float radius;
};
class Enemy_system {
public:
Enemy_system(mirrage::ecs::Entity_manager& entity_manager);
Enemy_system(mirrage::util::Message_bus& bus,
mirrage::ecs::Entity_manager& entity_manager,
const Beat_system& beat_system);
void update(mirrage::util::Time dt);
private:
mirrage::util::Message_bus& _bus;
mirrage::ecs::Entity_manager& _entity_manager;
const Beat_system& _beat_system;
void do_movement(Beat_state& beat);
void do_fixed_path_movement(Beat_state& beat);
void do_cont_path_movement(Beat_state& beat);
void do_hunting_movement(Beat_state& beat);
void do_shooting(Beat_state& beat);
void do_bullet_hit_detection();
bool intersect(const Stadium& stad1, const Stadium& stad2);
float dot(const glm::vec2& a, const glm::vec2& b);
};
}
\ No newline at end of file
} // namespace phase_shifter::gameplay
......@@ -4,15 +4,20 @@ namespace phase_shifter::gameplay {
void Fixed_path_comp::update_path(std::vector<float> directions)
{
next_step = 0;
reverse = false;
next_step = 0;
reverse = false;
this->directions = directions;
for (float angle : this->directions) {
while (angle < 0) {
angle += 360;
}
}
}
float Fixed_path_comp::next_direction()
{
if(directions.empty()) {
return -1;
return 1000;
}
float dir = directions[next_step];
......@@ -24,7 +29,7 @@ namespace phase_shifter::gameplay {
reverse = true;
}
} else {
if (dir >= 180) {
if (dir >= 180 && dir < 360) {
dir -= 180;
} else {
dir += 180;
......
......@@ -13,9 +13,15 @@ namespace phase_shifter::gameplay {
void update_path(std::vector<float> directions);
float next_direction();
unsigned int pause_between_steps = 0; // number of beats the entity waits between steps
unsigned int wait_beats = 0; // number of beats the entity waits before taking the next step
private:
unsigned int next_step = 0; //index of the next step
std::vector<float> directions; //vector containing the steps as angles (degrees) with 0° being North (-Z)
bool reverse = false; //flag wether the path is now traced backwards
};
sf2_structDef(Fixed_path_comp, pause_between_steps, wait_beats);
}
\ No newline at end of file
......@@ -8,6 +8,12 @@ namespace phase_shifter::gameplay {
static constexpr const char* name() { return "FollowTarget"; }
using Component::Component;
unsigned int pause_between_steps = 0; // number of beats the entity waits between steps
unsigned int wait_beats = 0; // number of beats the entity waits before taking the next step
mirrage::ecs::Entity_handle target;
};
sf2_structDef(Follow_target_comp, pause_between_steps, wait_beats);
} // namespace phase_shifter::gameplay
\ No newline at end of file
......@@ -24,9 +24,10 @@ namespace phase_shifter::gameplay {
float step_time = 0;
glm::vec2 last_step;
glm::vec2 last_aim;
glm::vec2 last_position;
bool attack = false;
glm::vec2 last_position; // position of the entity during the last frame
bool moved = false;
bool attack = false;
bool want_orientation = true; // flag whether the movement system shall orientate the entity
};
sf2_structDef(Movement_comp,
......
......@@ -45,10 +45,11 @@ namespace phase_shifter::gameplay {
auto beat = _beat_system.beat_state();
for(auto&& [entity, transform, move] : _ecs.list<Entity_facet, Transform_comp, Movement_comp>()) {
//save last position
move.last_position = {transform.position.x, transform.position.z};
move.moved = true;
if(beat.beat)
{
if(beat.beat) {
move.beats_since_move++;
}
......@@ -109,6 +110,7 @@ namespace phase_shifter::gameplay {
look_dir = offset;
}
transform.position.x = pos.x + offset.x;
transform.position.z = pos.y + offset.y;
}
......@@ -124,10 +126,12 @@ namespace phase_shifter::gameplay {
move.last_aim = look_dir;
look_dir /= look_dir_len;
transform.orientation = glm::normalize(glm::slerp(
transform.orientation,
glm::rotation(glm::vec3{0.f, 0.f, 1.f}, glm::vec3(look_dir.x, 0, look_dir.y)),