mirror of
https://github.com/zhenyan121/Cubed.git
synced 2026-08-08 17:57:02 +08:00
refactor(ecs): move entity physics to tick-based systems
Update PhysicalSystem and SpeedSystem to operate on entt::registry instead of individual components. Add TickVelocity for server creatures so movement is calculated per tick without frame delta time. LocalPlayer now implements its own client-side physics with collision detection.
This commit is contained in:
@@ -3,10 +3,17 @@
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
namespace Cubed {
|
||||
struct TickVelocity {
|
||||
|
||||
glm::vec3 value{0.0f};
|
||||
// blocks/tick!!! -1 for in
|
||||
glm::vec3 max{1.0f, -1.0f, 1.0f};
|
||||
};
|
||||
|
||||
struct Velocity {
|
||||
|
||||
glm::vec3 value{0.0f};
|
||||
|
||||
// blocks/second!!!
|
||||
glm::vec3 max{4.5f, 7.5f, 7.5f};
|
||||
};
|
||||
|
||||
@@ -23,4 +30,5 @@ struct Gravity {
|
||||
|
||||
float value = DEFAULT_G;
|
||||
};
|
||||
|
||||
} // namespace Cubed
|
||||
@@ -8,7 +8,7 @@ namespace Cubed {
|
||||
struct BaseServerCreature {
|
||||
Transform transform{};
|
||||
|
||||
Velocity velocity{};
|
||||
TickVelocity velocity{};
|
||||
|
||||
Movement movement{};
|
||||
|
||||
|
||||
@@ -149,12 +149,14 @@ private:
|
||||
|
||||
void update_direction();
|
||||
void update_lookup_block();
|
||||
|
||||
void update_move(float delta_time);
|
||||
|
||||
void update_move(float dt);
|
||||
void update_player_chunk();
|
||||
|
||||
void play_walk_sound(float dt);
|
||||
Gait compute_gait() const;
|
||||
|
||||
void update_speed(float dt);
|
||||
std::tuple<bool, bool, bool> update_physical(float dt);
|
||||
glm::vec3 get_move_distance(float dt);
|
||||
};
|
||||
} // namespace Cubed
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
#pragma once
|
||||
#include "Cubed/gameplay/ecs/movement.hpp"
|
||||
#include "Cubed/gameplay/ecs/state.hpp"
|
||||
#include "Cubed/gameplay/ecs/transform.hpp"
|
||||
#include "Cubed/gameplay/world.hpp"
|
||||
namespace Cubed {
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
namespace Cubed {
|
||||
class ServerWorld;
|
||||
class PhysicalSystem {
|
||||
public:
|
||||
static glm::vec3 get_move_distance(float dt, const Direction& d,
|
||||
const Velocity& v);
|
||||
static glm::vec3 get_move_distance(const Direction& d,
|
||||
const TickVelocity& v);
|
||||
|
||||
static std::tuple<bool, bool, bool>
|
||||
update(float dt, World& world, glm::vec3& moved_pos, Velocity& v,
|
||||
Direction& direction, MoveState& move_state, HitboxID hitbox);
|
||||
static void update(ServerWorld& world, entt::registry& registry);
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "Cubed/gameplay/ecs/movement.hpp"
|
||||
#include "Cubed/gameplay/ecs/state.hpp"
|
||||
#include "Cubed/gameplay/ecs/transform.hpp"
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
namespace Cubed {
|
||||
class SpeedSystem {
|
||||
public:
|
||||
static void update(float dt, Velocity& v, MoveState& move_state,
|
||||
Movement& movement, Direction& direction,
|
||||
const Gravity& g);
|
||||
static void update(float dt, entt::registry& registry);
|
||||
|
||||
private:
|
||||
};
|
||||
|
||||
@@ -5,12 +5,110 @@
|
||||
#include "Cubed/debug_collector.hpp"
|
||||
#include "Cubed/gameplay/client_world.hpp"
|
||||
#include "Cubed/gameplay/hitbox_manager.hpp"
|
||||
#include "Cubed/gameplay/systems/physical_system.hpp"
|
||||
#include "Cubed/gameplay/systems/speed_system.hpp"
|
||||
namespace {} // namespace
|
||||
|
||||
namespace Cubed {
|
||||
LocalPlayer::LocalPlayer(ClientWorld& world) : m_world(world) {}
|
||||
|
||||
namespace {
|
||||
|
||||
bool update_x(const glm::vec3& pos, const glm::vec3& distance,
|
||||
ClientWorld& world, const Hitbox& box) {
|
||||
glm::vec3 p = pos;
|
||||
p.x += distance.x;
|
||||
Hitbox b = box;
|
||||
b.center += p;
|
||||
glm::vec3 min = b.min();
|
||||
glm::vec3 max = b.max();
|
||||
int minx = std::floor(min.x);
|
||||
int maxx = std::floor(max.x);
|
||||
int miny = std::floor(min.y);
|
||||
int maxy = std::floor(max.y);
|
||||
int minz = std::floor(min.z);
|
||||
int maxz = std::floor(max.z);
|
||||
|
||||
for (int x = minx; x <= maxx; ++x) {
|
||||
for (int y = miny; y <= maxy; ++y) {
|
||||
for (int z = minz; z <= maxz; ++z) {
|
||||
glm::ivec3 block_pos{x, y, z};
|
||||
if (!world.can_pass_block(block_pos)) {
|
||||
Hitbox block_box = World::get_block_aabb(block_pos);
|
||||
if (b.intersects(block_box)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool update_y(const glm::vec3& pos, const glm::vec3& distance,
|
||||
ClientWorld& world, const Hitbox& box) {
|
||||
glm::vec3 p = pos;
|
||||
p.y += distance.y;
|
||||
Hitbox b = box;
|
||||
b.center += p;
|
||||
glm::vec3 min = b.min();
|
||||
glm::vec3 max = b.max();
|
||||
int minx = std::floor(min.x);
|
||||
int maxx = std::floor(max.x);
|
||||
int miny = std::floor(min.y);
|
||||
int maxy = std::floor(max.y);
|
||||
int minz = std::floor(min.z);
|
||||
int maxz = std::floor(max.z);
|
||||
|
||||
for (int x = minx; x <= maxx; ++x) {
|
||||
for (int y = miny; y <= maxy; ++y) {
|
||||
for (int z = minz; z <= maxz; ++z) {
|
||||
glm::ivec3 block_pos{x, y, z};
|
||||
if (!world.can_pass_block(block_pos)) {
|
||||
Hitbox block_box = World::get_block_aabb(block_pos);
|
||||
if (b.intersects(block_box)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool update_z(const glm::vec3& pos, const glm::vec3& distance,
|
||||
ClientWorld& world, const Hitbox& box) {
|
||||
glm::vec3 p = pos;
|
||||
p.z += distance.z;
|
||||
Hitbox b = box;
|
||||
b.center += p;
|
||||
glm::vec3 min = b.min();
|
||||
glm::vec3 max = b.max();
|
||||
int minx = std::floor(min.x);
|
||||
int maxx = std::floor(max.x);
|
||||
int miny = std::floor(min.y);
|
||||
int maxy = std::floor(max.y);
|
||||
int minz = std::floor(min.z);
|
||||
int maxz = std::floor(max.z);
|
||||
|
||||
for (int x = minx; x <= maxx; ++x) {
|
||||
for (int y = miny; y <= maxy; ++y) {
|
||||
for (int z = minz; z <= maxz; ++z) {
|
||||
glm::ivec3 block_pos{x, y, z};
|
||||
if (!world.can_pass_block(block_pos)) {
|
||||
Hitbox block_box = World::get_block_aabb(block_pos);
|
||||
if (b.intersects(block_box)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
LocalPlayer::LocalPlayer(ClientWorld& world) : m_world(world) {
|
||||
|
||||
m_hitbox = HitboxManager::instance().get_hitbox_id("cubed:player");
|
||||
}
|
||||
LocalPlayer::~LocalPlayer() {}
|
||||
|
||||
const glm::vec3& LocalPlayer::get_front() const { return m_front; }
|
||||
@@ -333,9 +431,9 @@ LocalPlayer::get_hotbar() const {
|
||||
return m_hotbar;
|
||||
}
|
||||
|
||||
void LocalPlayer::update_move(float delta_time) {
|
||||
void LocalPlayer::update_move(float dt) {
|
||||
// if frame rate less than 1 frame per second, don't update
|
||||
if (delta_time > 1.0f) {
|
||||
if (dt > 1.0f) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -343,7 +441,7 @@ void LocalPlayer::update_move(float delta_time) {
|
||||
m_sprinting = false;
|
||||
}
|
||||
if (space_on) {
|
||||
space_on_time += delta_time;
|
||||
space_on_time += dt;
|
||||
if (space_on_time >= MAX_SPACE_ON_TIME) {
|
||||
space_on = false;
|
||||
space_on_time = 0.0f;
|
||||
@@ -360,8 +458,7 @@ void LocalPlayer::update_move(float delta_time) {
|
||||
glm::vec3{m_max_run_speed, m_max_y_speed, m_max_run_speed};
|
||||
}
|
||||
|
||||
SpeedSystem::update(delta_time, m_velocity, m_move_state, m_movement,
|
||||
m_direction, m_gravity);
|
||||
update_speed(dt);
|
||||
|
||||
update_direction();
|
||||
|
||||
@@ -374,12 +471,9 @@ void LocalPlayer::update_move(float delta_time) {
|
||||
}
|
||||
|
||||
if (m_game_mode == SPECTATOR) {
|
||||
player_pos += PhysicalSystem::get_move_distance(delta_time, m_direction,
|
||||
m_velocity);
|
||||
player_pos += get_move_distance(dt);
|
||||
} else {
|
||||
auto [x, y, z] =
|
||||
PhysicalSystem::update(delta_time, m_world, player_pos, m_velocity,
|
||||
m_direction, m_move_state, m_hitbox);
|
||||
auto [x, y, z] = update_physical(dt);
|
||||
if (!x || !z) {
|
||||
m_sprinting = false;
|
||||
}
|
||||
@@ -406,7 +500,7 @@ void LocalPlayer::update_move(float delta_time) {
|
||||
}
|
||||
|
||||
for (auto& [key, timer] : m_timers) {
|
||||
timer.update(delta_time);
|
||||
timer.update(dt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -593,6 +687,96 @@ void LocalPlayer::init(std::string_view name) {
|
||||
}
|
||||
}
|
||||
|
||||
void LocalPlayer::update_speed(float dt) {
|
||||
// calculate speed
|
||||
auto& v = m_velocity;
|
||||
if (m_move_state.forward || m_move_state.back || m_move_state.left ||
|
||||
m_move_state.right) {
|
||||
m_direction.value = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
v.value.x += m_movement.acceleration * dt;
|
||||
v.value.z += m_movement.acceleration * dt;
|
||||
if (v.value.x > v.max.x) {
|
||||
v.value.x = v.max.x;
|
||||
}
|
||||
if (v.value.z > v.max.z) {
|
||||
v.value.z = v.max.z;
|
||||
}
|
||||
} else {
|
||||
v.value.x += -m_movement.deceleration * dt;
|
||||
v.value.z += -m_movement.deceleration * dt;
|
||||
if (v.value.z < 0.0f) {
|
||||
v.value.z = 0.0f;
|
||||
}
|
||||
if (v.value.x < 0.0f) {
|
||||
v.value.x = 0.0f;
|
||||
}
|
||||
if (v.value.z < 0.0f && v.value.x < 0.0f) {
|
||||
m_direction.value = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
if (m_move_state.is_fly) {
|
||||
if (m_move_state.up) {
|
||||
v.value.y = v.max.y;
|
||||
}
|
||||
|
||||
if (m_move_state.down) {
|
||||
v.value.y = -v.max.y;
|
||||
}
|
||||
|
||||
if (!m_move_state.down && !m_move_state.up) {
|
||||
v.value.y = 0.0f;
|
||||
}
|
||||
} else {
|
||||
if (m_move_state.up && m_move_state.can_up) {
|
||||
v.value.y = m_movement.jump_power;
|
||||
m_move_state.can_up = false;
|
||||
}
|
||||
|
||||
v.value.y += -m_gravity.value * dt;
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<bool, bool, bool> LocalPlayer::update_physical(float dt) {
|
||||
|
||||
auto distance = get_move_distance(dt);
|
||||
auto box = HitboxManager::hitbox(m_hitbox);
|
||||
bool x = false;
|
||||
bool y = false;
|
||||
bool z = false;
|
||||
if (update_x(m_pos.value, distance, m_world, box.box)) {
|
||||
m_pos.value.x += distance.x;
|
||||
x = true;
|
||||
} else {
|
||||
m_velocity.value.x = 0.0f;
|
||||
}
|
||||
|
||||
if (update_y(m_pos.value, distance, m_world, box.box)) {
|
||||
m_pos.value.y += distance.y;
|
||||
y = true;
|
||||
} else {
|
||||
m_velocity.value.y = 0.0f;
|
||||
if (distance.y < 0) {
|
||||
m_move_state.can_up = true;
|
||||
m_move_state.is_fly = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (update_z(m_pos.value, distance, m_world, box.box)) {
|
||||
m_pos.value.z += distance.z;
|
||||
z = true;
|
||||
} else {
|
||||
m_velocity.value.z = 0.0f;
|
||||
}
|
||||
return {x, y, z};
|
||||
}
|
||||
|
||||
glm::vec3 LocalPlayer::get_move_distance(float dt) {
|
||||
const auto& d = m_direction;
|
||||
const auto& v = m_velocity;
|
||||
return glm::vec3{d.value.x * v.value.x * dt, v.value.y * dt,
|
||||
d.value.z * v.value.z * dt};
|
||||
}
|
||||
|
||||
bool LocalPlayer::is_underwater() const { return m_underwater; }
|
||||
void LocalPlayer::set_underwater(bool u) { m_underwater = u; }
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
#include "Cubed/gameplay/systems/physical_system.hpp"
|
||||
|
||||
#include "Cubed/gameplay/ecs/server_entity.hpp"
|
||||
#include "Cubed/gameplay/hitbox_manager.hpp"
|
||||
#include "Cubed/gameplay/server_world.hpp"
|
||||
namespace Cubed {
|
||||
|
||||
namespace {
|
||||
|
||||
bool update_x(const glm::vec3& pos, const glm::vec3& distance, World& world,
|
||||
const Hitbox& box) {
|
||||
bool update_x(const glm::vec3& pos, const glm::vec3& distance,
|
||||
ServerWorld& world, const Hitbox& box) {
|
||||
glm::vec3 p = pos;
|
||||
p.x += distance.x;
|
||||
Hitbox b = box;
|
||||
@@ -36,8 +38,8 @@ bool update_x(const glm::vec3& pos, const glm::vec3& distance, World& world,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool update_y(const glm::vec3& pos, const glm::vec3& distance, World& world,
|
||||
const Hitbox& box) {
|
||||
bool update_y(const glm::vec3& pos, const glm::vec3& distance,
|
||||
ServerWorld& world, const Hitbox& box) {
|
||||
glm::vec3 p = pos;
|
||||
p.y += distance.y;
|
||||
Hitbox b = box;
|
||||
@@ -67,8 +69,8 @@ bool update_y(const glm::vec3& pos, const glm::vec3& distance, World& world,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool update_z(const glm::vec3& pos, const glm::vec3& distance, World& world,
|
||||
const Hitbox& box) {
|
||||
bool update_z(const glm::vec3& pos, const glm::vec3& distance,
|
||||
ServerWorld& world, const Hitbox& box) {
|
||||
glm::vec3 p = pos;
|
||||
p.z += distance.z;
|
||||
Hitbox b = box;
|
||||
@@ -100,45 +102,41 @@ bool update_z(const glm::vec3& pos, const glm::vec3& distance, World& world,
|
||||
|
||||
} // namespace
|
||||
|
||||
std::tuple<bool, bool, bool>
|
||||
PhysicalSystem::update(float dt, World& world, glm::vec3& moved_pos,
|
||||
Velocity& v, Direction& direction, MoveState& move_state,
|
||||
HitboxID hitbox) {
|
||||
auto distance = get_move_distance(dt, direction, v);
|
||||
auto box = HitboxManager::hitbox(hitbox);
|
||||
bool x = false;
|
||||
bool y = false;
|
||||
bool z = false;
|
||||
if (update_x(moved_pos, distance, world, box.box)) {
|
||||
moved_pos.x += distance.x;
|
||||
x = true;
|
||||
} else {
|
||||
v.value.x = 0.0f;
|
||||
}
|
||||
void PhysicalSystem::update(ServerWorld& world, entt::registry& registry) {
|
||||
|
||||
if (update_y(moved_pos, distance, world, box.box)) {
|
||||
moved_pos.y += distance.y;
|
||||
y = true;
|
||||
} else {
|
||||
v.value.y = 0.0f;
|
||||
if (distance.y < 0) {
|
||||
move_state.can_up = true;
|
||||
move_state.is_fly = false;
|
||||
auto view = registry.view<BaseServerCreature>();
|
||||
for (auto e : view) {
|
||||
auto& creature = view.get<BaseServerCreature>(e);
|
||||
auto distance =
|
||||
get_move_distance(creature.direction, creature.velocity);
|
||||
auto box = HitboxManager::hitbox(creature.hitbox);
|
||||
auto& pos = creature.transform.position.value;
|
||||
auto& v = creature.velocity;
|
||||
if (update_x(pos, distance, world, box.box)) {
|
||||
pos.x += distance.x;
|
||||
|
||||
} else {
|
||||
v.value.x = 0.0f;
|
||||
}
|
||||
|
||||
if (update_y(pos, distance, world, box.box)) {
|
||||
pos.y += distance.y;
|
||||
|
||||
} else {
|
||||
v.value.y = 0.0f;
|
||||
}
|
||||
|
||||
if (update_z(pos, distance, world, box.box)) {
|
||||
pos.z += distance.z;
|
||||
|
||||
} else {
|
||||
v.value.z = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
if (update_z(moved_pos, distance, world, box.box)) {
|
||||
moved_pos.z += distance.z;
|
||||
z = true;
|
||||
} else {
|
||||
v.value.z = 0.0f;
|
||||
}
|
||||
return {x, y, z};
|
||||
}
|
||||
|
||||
glm::vec3 PhysicalSystem::get_move_distance(float dt, const Direction& d,
|
||||
const Velocity& v) {
|
||||
return glm::vec3{d.value.x * v.value.x * dt, v.value.y * dt,
|
||||
d.value.z * v.value.z * dt};
|
||||
glm::vec3 PhysicalSystem::get_move_distance(const Direction& d,
|
||||
const TickVelocity& v) {
|
||||
return glm::vec3{d.value.x * v.value.x, v.value.y, d.value.z * v.value.z};
|
||||
}
|
||||
} // namespace Cubed
|
||||
@@ -1,54 +1,24 @@
|
||||
#include "Cubed/gameplay/systems/speed_system.hpp"
|
||||
|
||||
#include "Cubed/gameplay/ecs/ai_struct.hpp"
|
||||
#include "Cubed/gameplay/ecs/server_entity.hpp"
|
||||
|
||||
namespace Cubed {
|
||||
|
||||
void SpeedSystem::update(float dt, Velocity& v, MoveState& move_state,
|
||||
Movement& movement, Direction& direction,
|
||||
const Gravity& g) {
|
||||
// calculate speed
|
||||
if (move_state.forward || move_state.back || move_state.left ||
|
||||
move_state.right) {
|
||||
direction.value = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
v.value.x += movement.acceleration * dt;
|
||||
v.value.z += movement.acceleration * dt;
|
||||
if (v.value.x > v.max.x) {
|
||||
v.value.x = v.max.x;
|
||||
}
|
||||
if (v.value.z > v.max.z) {
|
||||
v.value.z = v.max.z;
|
||||
}
|
||||
} else {
|
||||
v.value.x += -movement.deceleration * dt;
|
||||
v.value.z += -movement.deceleration * dt;
|
||||
if (v.value.z < 0.0f) {
|
||||
v.value.z = 0.0f;
|
||||
}
|
||||
if (v.value.x < 0.0f) {
|
||||
v.value.x = 0.0f;
|
||||
}
|
||||
if (v.value.z < 0.0f && v.value.x < 0.0f) {
|
||||
direction.value = glm::vec3(0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
}
|
||||
if (move_state.is_fly) {
|
||||
if (move_state.up) {
|
||||
v.value.y = v.max.y;
|
||||
}
|
||||
void SpeedSystem::update(float dt, entt::registry& registry) {
|
||||
auto view = registry.view<BaseServerCreature, MoveBoost>();
|
||||
|
||||
if (move_state.down) {
|
||||
v.value.y = -v.max.y;
|
||||
for (auto e : view) {
|
||||
auto [creature, moveboost] = view.get<BaseServerCreature, MoveBoost>(e);
|
||||
if (moveboost.count <= moveboost.duration) {
|
||||
++moveboost.count;
|
||||
creature.velocity.value +=
|
||||
creature.direction.value * creature.movement.acceleration;
|
||||
} else {
|
||||
creature.velocity.value -=
|
||||
creature.direction.value * creature.movement.deceleration;
|
||||
}
|
||||
|
||||
if (!move_state.down && !move_state.up) {
|
||||
v.value.y = 0.0f;
|
||||
}
|
||||
} else {
|
||||
if (move_state.up && move_state.can_up) {
|
||||
v.value.y = movement.jump_power;
|
||||
move_state.can_up = false;
|
||||
}
|
||||
|
||||
v.value.y += -g.value * dt;
|
||||
creature.velocity.value.y = creature.gravity.value * dt;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user