5 Commits

Author SHA1 Message Date
1e106d75a3 refactor(gameplay): extract physics and collision from ClientPlayer into PhysicalSystem
Move per-axis collision detection and move distance calculation to new
PhysicalSystem. Move SpeedSystem implementation from inline header to
separate .cpp file. Add const accessors to Entity. Replace inline AABB
helper with HitboxManager registration of player hitbox using new
PLAYER_SIZE constant. Remove obsolete members and functions from
ClientPlayer.
2026-07-28 11:43:10 +08:00
bd1a9557a5 refactor: move movement logic into SpeedSystem and Entity components 2026-07-28 10:54:13 +08:00
831569cec6 feat(player): split max speed into horizontal/vertical, set spawn pos 2026-07-28 10:10:01 +08:00
a5c1c7d4aa refactor(gameplay): extract Entity base class from ClientPlayer
Move position, walk pose, velocity, orientation, movement, and gravity
fields and their accessors into a new Entity base class. ClientPlayer now
inherits from Entity, removing duplicated members. Also update velocity
handling to use 3D vector per axis and adjust related logic.
2026-07-28 09:27:28 +08:00
802d2ecb5a refactor(gameplay): extract movement, gravity, orientation, and walk pose into structs 2026-07-28 08:53:51 +08:00
15 changed files with 465 additions and 274 deletions

View File

@@ -1,8 +1,8 @@
#pragma once #pragma once
#include "Cubed/AABB.hpp"
#include "Cubed/constants.hpp" #include "Cubed/constants.hpp"
#include "Cubed/gameplay/block.hpp" #include "Cubed/gameplay/block.hpp"
#include "Cubed/gameplay/chunk_pos.hpp" #include "Cubed/gameplay/chunk_pos.hpp"
#include "Cubed/gameplay/entity.hpp"
#include "Cubed/gameplay/game_mode.hpp" #include "Cubed/gameplay/game_mode.hpp"
#include "Cubed/gameplay/game_time.hpp" #include "Cubed/gameplay/game_time.hpp"
#include "Cubed/gameplay/item_stack.hpp" #include "Cubed/gameplay/item_stack.hpp"
@@ -17,7 +17,7 @@
namespace Cubed { namespace Cubed {
class ClientWorld; class ClientWorld;
class ClientPlayer { class ClientPlayer : public Entity {
public: public:
static constexpr size_t HOTBAR_SUM = 10; static constexpr size_t HOTBAR_SUM = 10;
static constexpr float WALK_SOUND_INTERVAL = 0.45f; static constexpr float WALK_SOUND_INTERVAL = 0.45f;
@@ -38,9 +38,8 @@ public:
const ChunkPosSet& get_chunk_pos_set() const; const ChunkPosSet& get_chunk_pos_set() const;
ChunkPosSet get_chunk_pos_set(); ChunkPosSet get_chunk_pos_set();
static AABB get_aabb(const glm::vec3& pos);
const glm::vec3& get_front() const; const glm::vec3& get_front() const;
Gait get_gait() const;
const std::optional<LookBlock>& get_look_block_pos() const; const std::optional<LookBlock>& get_look_block_pos() const;
// thread safe // thread safe
glm::vec3 get_player_pos() const; glm::vec3 get_player_pos() const;
@@ -53,15 +52,10 @@ public:
float& max_walk_speed(); float& max_walk_speed();
float& max_run_speed(); float& max_run_speed();
float& max_speed();
float& acceleration();
float& deceleration();
float& g();
float& fly_y_speed(); float& fly_y_speed();
const ItemStack& get_current_itemstack() const; const ItemStack& get_current_itemstack() const;
void set_gait(Gait gait);
GameMode& game_mode(); GameMode& game_mode();
ClientWorld& get_world(); ClientWorld& get_world();
@@ -69,13 +63,9 @@ public:
void set_uuid(std::string_view uuid); void set_uuid(std::string_view uuid);
std::string get_uuid() const; std::string get_uuid() const;
const std::string& get_name() const; const std::string& get_name() const;
void reset_key_status(); void reset_input_status();
void init(std::string_view name); void init(std::string_view name);
float yaw() const;
float pitch() const;
float& angle();
float& walk_time();
bool ray_cast(const glm::vec3& start, const glm::vec3& dir, bool ray_cast(const glm::vec3& start, const glm::vec3& dir,
glm::ivec3& block_pos, glm::vec3& normal, glm::ivec3& block_pos, glm::vec3& normal,
float distance = 4.0f); float distance = 4.0f);
@@ -91,28 +81,18 @@ private:
using enum GameMode; using enum GameMode;
float m_max_walk_speed = DEFAULT_MAX_WALK_SPEED; float m_max_walk_speed = DEFAULT_MAX_WALK_SPEED;
float m_max_run_speed = DEFAULT_MAX_RUN_SPEED; float m_max_run_speed = DEFAULT_MAX_RUN_SPEED;
float m_acceleration = DEFAULT_ACCELERATION; float m_max_y_speed = 7.5f;
float m_deceleration = DEFAULT_DECELERATION;
float m_g = DEFAULT_G;
static constexpr float MAX_SPACE_ON_TIME = 0.3f; static constexpr float MAX_SPACE_ON_TIME = 0.3f;
static constexpr float PLACE_BLOCK_INTERVAL = 0.2f; static constexpr float PLACE_BLOCK_INTERVAL = 0.2f;
Movement m_movement{};
Gravity m_gravity{};
float m_place_time = PLACE_BLOCK_INTERVAL; float m_place_time = PLACE_BLOCK_INTERVAL;
std::atomic<float> m_yaw = 0.0f;
std::atomic<float> m_pitch = 0.0f;
std::array<ItemStack, HOTBAR_SUM> m_hotbar; std::array<ItemStack, HOTBAR_SUM> m_hotbar;
float m_sensitivity = 0.15f; float m_sensitivity = 0.15f;
float m_max_speed = m_max_walk_speed;
float m_y_speed = 0.0f;
float m_fly_y_speed = 7.5f;
bool can_up = true;
float space_on_time = 0.0f; float space_on_time = 0.0f;
bool space_on = false; bool space_on = false;
bool is_fly = false;
float m_xz_speed = 0.0f;
int m_selected_hotbar = 0; int m_selected_hotbar = 0;
@@ -120,19 +100,12 @@ private:
bool m_sprinting = false; bool m_sprinting = false;
bool m_underwater = false; bool m_underwater = false;
glm::vec3 direction = glm::vec3(0.0f, 0.0f, 0.0f);
glm::vec3 move_distance{0.0f, 0.0f, 0.0f};
// player is tow block tall, the pos is the lower pos // player is tow block tall, the pos is the lower pos
glm::vec3 m_player_pos{0.0f, 255.0f, 0.0f};
ChunkPos m_last_chunk_pos{0, 0}; ChunkPos m_last_chunk_pos{0, 0};
glm::vec3 m_front{0, 0, -1}; glm::vec3 m_front{0, 0, -1};
glm::vec3 m_right{0, 0, 0}; glm::vec3 m_right{0, 0, 0};
static constexpr glm::vec3 M_SIZE{0.6f, 1.8f, 0.6f};
std::atomic<Gait> m_gait = Gait::STOP;
MoveState m_move_state{};
MouseState m_mouse_state{}; MouseState m_mouse_state{};
GameMode m_game_mode = CREATIVE; GameMode m_game_mode = CREATIVE;
std::optional<LookBlock> m_look_block = std::nullopt; std::optional<LookBlock> m_look_block = std::nullopt;
@@ -141,9 +114,6 @@ private:
std::string m_uuid; std::string m_uuid;
ClientWorld& m_world; ClientWorld& m_world;
float m_angle{0.0f};
float m_walk_time{0.0f};
std::unordered_map<std::string, Timer> m_timers; std::unordered_map<std::string, Timer> m_timers;
mutable std::shared_mutex m_player_pos_mutex; mutable std::shared_mutex m_player_pos_mutex;
@@ -155,10 +125,6 @@ private:
void update_move(float delta_time); void update_move(float delta_time);
void update_x_move(glm::vec3& player_pos);
void update_y_move(glm::vec3& player_pos);
void update_z_move(glm::vec3& player_pos);
void update_player_chunk(); void update_player_chunk();
void play_walk_sound(float dt); void play_walk_sound(float dt);

View File

@@ -1,5 +1,6 @@
#pragma once #pragma once
#include "Cubed/AABB.hpp" #include "Cubed/AABB.hpp"
#include "Cubed/constants.hpp"
#include "Cubed/gameplay/player.hpp" #include "Cubed/gameplay/player.hpp"
#include <glm/glm.hpp> #include <glm/glm.hpp>
@@ -36,10 +37,79 @@ struct Orientation {
struct Velocity { struct Velocity {
glm::vec3 value{0.0f}; glm::vec3 value{0.0f};
glm::vec3 max{4.5f, 7.5f, 7.5f};
}; };
struct HitBoxes { struct HitBoxes {
std::vector<AABB> boxex; std::vector<AABB> boxex;
}; };
struct Movement {
float acceleration = DEFAULT_ACCELERATION;
float deceleration = DEFAULT_DECELERATION;
float jump_power = 7.5f;
};
struct Gravity {
float value = DEFAULT_G;
};
struct MoveState {
bool forward = false;
bool back = false;
bool left = false;
bool right = false;
bool down = false;
bool up = false;
bool is_fly = false;
bool can_up = true;
};
struct Direction {
glm::vec3 value{0.0f};
};
class Entity {
public:
glm::vec3& max_speed();
float& acceleration();
float& deceleration();
float& g();
void set_gait(Gait gait);
float yaw() const;
float pitch() const;
float& roll();
float& walk_time();
Gait get_gait() const;
Velocity& velocity();
Position& pos();
WalkPose& walk_pose();
Orientation& angle();
Movement& movement();
Gravity& gravity();
MoveState& move_state();
Direction& direction();
const Velocity& velocity() const;
const Position& pos() const;
const WalkPose& walk_pose() const;
const Orientation& angle() const;
const Movement& movement() const;
const Gravity& gravity() const;
const MoveState& move_state() const;
const Direction& direction() const;
protected:
Position m_pos;
WalkPose m_walk_pose;
Velocity m_velocity;
Orientation m_angle;
Movement m_movement;
Gravity m_gravity;
MoveState m_move_state;
Direction m_direction;
};
} // namespace Cubed } // namespace Cubed

View File

@@ -1,4 +1,6 @@
#pragma once #pragma once
#include "glm/ext/vector_float3.hpp"
#include <stdexcept> #include <stdexcept>
#include <utility> #include <utility>
namespace Cubed { namespace Cubed {
@@ -17,5 +19,5 @@ inline Gait get_gait_from_id(int id) {
throw std::runtime_error("Unknown Gait"); throw std::runtime_error("Unknown Gait");
} }
} }
static constexpr glm::vec3 PLAYER_SIZE{0.6f, 1.8f, 0.6f};
} // namespace Cubed } // namespace Cubed

View File

@@ -0,0 +1,16 @@
#pragma once
#include "Cubed/gameplay/entity.hpp"
#include "Cubed/gameplay/world.hpp"
namespace Cubed {
class PhysicalSystem {
public:
static glm::vec3 get_move_distance(float dt, const Entity& e);
static std::tuple<bool, bool, bool> update(float dt, Entity& e,
World& world);
static std::tuple<bool, bool, bool>
update(float dt, Entity& e, World& world, glm::vec3& moved_pos);
private:
};
} // namespace Cubed

View File

@@ -0,0 +1,12 @@
#pragma once
#include "Cubed/gameplay/entity.hpp"
namespace Cubed {
class SpeedSystem {
public:
static void update(float dt, Entity& e);
private:
};
} // namespace Cubed

View File

@@ -2,15 +2,6 @@
namespace Cubed { namespace Cubed {
struct MoveState {
bool forward = false;
bool back = false;
bool left = false;
bool right = false;
bool down = false;
bool up = false;
};
struct MouseState { struct MouseState {
bool left = false; bool left = false;
bool right = false; bool right = false;

View File

@@ -97,4 +97,7 @@ target_sources(${PROJECT_NAME}
render/model_renderer.cpp render/model_renderer.cpp
gameplay/chunk.cpp gameplay/chunk.cpp
gameplay/hitbox_manager.cpp gameplay/hitbox_manager.cpp
gameplay/entity.cpp
gameplay/systems/physical_system.cpp
gameplay/systems/speed_system.cpp
) )

View File

@@ -502,7 +502,11 @@ void DevPanel::show_player_tab_item() {
} else if (m_player->game_mode() == GameMode::SPECTATOR) { } else if (m_player->game_mode() == GameMode::SPECTATOR) {
m_player_profile.game_mode = 1; m_player_profile.game_mode = 1;
ImGui::SliderFloat("MaxSpeed", &m_player->max_speed(), 1.0f, ImGui::SliderFloat("MaxSpeed x", &m_player->max_speed().x, 1.0f,
500.0f);
ImGui::SliderFloat("MaxSpeed y", &m_player->max_speed().y, 1.0f,
500.0f);
ImGui::SliderFloat("MaxSpeed z", &m_player->max_speed().z, 1.0f,
500.0f); 500.0f);
} }
if (ImGui::Button("reset")) { if (ImGui::Button("reset")) {
@@ -512,6 +516,7 @@ void DevPanel::show_player_tab_item() {
m_player->deceleration() = DEFAULT_DECELERATION; m_player->deceleration() = DEFAULT_DECELERATION;
m_player->g() = DEFAULT_G; m_player->g() = DEFAULT_G;
m_player->change_mode(GameMode::CREATIVE); m_player->change_mode(GameMode::CREATIVE);
m_player->fly_y_speed() = 7.5f;
m_player->set_gait(Gait::WALK); m_player->set_gait(Gait::WALK);
m_player_profile.game_mode = 0; m_player_profile.game_mode = 0;
m_player_profile.gait = 0; m_player_profile.gait = 0;

View File

@@ -4,31 +4,24 @@
#include "Cubed/config.hpp" #include "Cubed/config.hpp"
#include "Cubed/debug_collector.hpp" #include "Cubed/debug_collector.hpp"
#include "Cubed/gameplay/client_world.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 {} // namespace
namespace Cubed { namespace Cubed {
ClientPlayer::ClientPlayer(ClientWorld& world) : m_world(world) {} ClientPlayer::ClientPlayer(ClientWorld& world) : m_world(world) {}
ClientPlayer::~ClientPlayer() {} ClientPlayer::~ClientPlayer() {}
AABB ClientPlayer::get_aabb(const glm::vec3& pos) {
glm::vec3 half = M_SIZE * 0.5f;
glm::vec3 center{pos.x, pos.y + half.y, pos.z};
return AABB{center, half};
}
const glm::vec3& ClientPlayer::get_front() const { return m_front; } const glm::vec3& ClientPlayer::get_front() const { return m_front; }
Gait ClientPlayer::get_gait() const { return m_gait.load(); }
const std::optional<LookBlock>& ClientPlayer::get_look_block_pos() const { const std::optional<LookBlock>& ClientPlayer::get_look_block_pos() const {
return m_look_block; return m_look_block;
} }
glm::vec3 ClientPlayer::get_player_pos() const { glm::vec3 ClientPlayer::get_player_pos() const {
std::shared_lock lock(m_player_pos_mutex); std::shared_lock lock(m_player_pos_mutex);
return m_player_pos; return m_pos.value;
} }
const MoveState& ClientPlayer::get_move_state() const { return m_move_state; } const MoveState& ClientPlayer::get_move_state() const { return m_move_state; }
@@ -110,27 +103,39 @@ void ClientPlayer::change_mode(GameMode mode) {
m_game_mode = mode; m_game_mode = mode;
Logger::info("Change GameMode to {}", to_str(mode)); Logger::info("Change GameMode to {}", to_str(mode));
if (mode == CREATIVE) { if (mode == CREATIVE) {
is_fly = false; m_move_state.is_fly = false;
m_max_speed = m_max_walk_speed; m_max_run_speed = DEFAULT_MAX_RUN_SPEED;
m_velocity.max =
glm::vec3{m_max_walk_speed, m_max_y_speed, m_max_walk_speed};
} else if (mode == SPECTATOR) { } else if (mode == SPECTATOR) {
is_fly = true; m_move_state.is_fly = true;
m_max_speed = m_max_run_speed; m_walk_pose.gait = Gait::RUN;
m_velocity.max =
glm::vec3{m_max_run_speed, m_max_y_speed, m_max_run_speed};
} }
} }
void ClientPlayer::reload_config() { void ClientPlayer::reload_config() {
auto& config = m_world.get_config(); auto& config = m_world.get_config();
m_sensitivity = config.get("player.mouse_sensitivity", 0.15f); m_sensitivity = config.get("player.mouse_sensitivity", 0.15f);
} }
void ClientPlayer::set_player_pos(const glm::vec3& pos) { m_player_pos = pos; } void ClientPlayer::set_player_pos(const glm::vec3& pos) {
std::lock_guard lock(m_player_pos_mutex);
m_pos.value = pos;
}
void ClientPlayer::update(float delta_time) { void ClientPlayer::update(float delta_time) {
m_gait = compute_gait(); WalkPose pos = m_walk_pose;
pos.gait = compute_gait();
m_walk_pose = pos;
update_move(delta_time); update_move(delta_time);
update_lookup_block(); update_lookup_block();
place_block(delta_time); place_block(delta_time);
d_rep("player_pos", "x: {:.2f} y: {:.2f} z: {:.2f}", m_player_pos.x, std::shared_lock lock(m_player_pos_mutex);
m_player_pos.y, m_player_pos.z); d_rep("player_pos", "x: {:.2f} y: {:.2f} z: {:.2f}", m_pos.value.x,
d_rep("speed", "Speed: {:.2} m/s", m_xz_speed); m_pos.value.y, m_pos.value.z);
d_rep("speed", "Speed(x, y, z): {:.2} m/s {:.2} m/s {:.2} m/s",
m_velocity.value.x, m_velocity.value.y, m_velocity.value.z);
} }
bool ClientPlayer::update_player_move_state(Key key, KeyAction action) { bool ClientPlayer::update_player_move_state(Key key, KeyAction action) {
if (key == Key::W) { if (key == Key::W) {
@@ -167,8 +172,8 @@ bool ClientPlayer::update_player_move_state(Key key, KeyAction action) {
m_move_state.up = true; m_move_state.up = true;
if (space_on) { if (space_on) {
if (m_game_mode == CREATIVE) { if (m_game_mode == CREATIVE) {
is_fly = !is_fly; m_move_state.is_fly = !m_move_state.is_fly;
m_y_speed = 0.0f; m_velocity.value.y = 0.0f;
} }
space_on = false; space_on = false;
space_on_time = 0.0f; space_on_time = 0.0f;
@@ -233,18 +238,17 @@ bool ClientPlayer::update_player_move_state(Key key, KeyAction action) {
} }
void ClientPlayer::update_front_vec(float offset_x, float offset_y) { void ClientPlayer::update_front_vec(float offset_x, float offset_y) {
m_yaw += offset_x * m_sensitivity; Orientation& angle = m_angle;
m_pitch += offset_y * m_sensitivity; angle.yaw += offset_x * m_sensitivity;
angle.pitch += offset_y * m_sensitivity;
// m_yaw = std::fmod(m_yaw.load(), 360.0); // m_yaw = std::fmod(m_yaw.load(), 360.0);
m_pitch = std::clamp(m_pitch.load(), -89.0f, 89.0f); angle.pitch = std::clamp(angle.pitch, -89.0f, 89.0f);
m_front.x = m_front.x = sin(glm::radians(angle.yaw)) * cos(glm::radians(angle.pitch));
sin(glm::radians(m_yaw.load())) * cos(glm::radians(m_pitch.load())); m_front.y = sin(glm::radians(angle.pitch));
m_front.y = sin(glm::radians(m_pitch.load())); m_front.z = -cos(glm::radians(angle.yaw)) * cos(glm::radians(angle.pitch));
m_front.z =
-cos(glm::radians(m_yaw.load())) * cos(glm::radians(m_pitch.load()));
m_front = glm::normalize(m_front); m_front = glm::normalize(m_front);
} }
@@ -270,7 +274,7 @@ void ClientPlayer::update_direction() {
move_dir = move_dir_front + move_dir_right; move_dir = move_dir_front + move_dir_right;
if (glm::length(move_dir) > 0.001f) { if (glm::length(move_dir) > 0.001f) {
direction = glm::normalize(move_dir); m_direction.value = glm::normalize(move_dir);
} }
} }
@@ -278,8 +282,9 @@ void ClientPlayer::update_lookup_block() {
// calculate the block that is looked // calculate the block that is looked
glm::ivec3 block_pos; glm::ivec3 block_pos;
glm::vec3 block_normal; glm::vec3 block_normal;
std::shared_lock lock(m_player_pos_mutex);
if (ray_cast( if (ray_cast(
glm::vec3(m_player_pos.x, (m_player_pos.y + 1.6f), m_player_pos.z), glm::vec3(m_pos.value.x, (m_pos.value.y + 1.6f), m_pos.value.z),
m_front, block_pos, block_normal)) { m_front, block_pos, block_normal)) {
m_look_block = LookBlock{block_pos, glm::floor(block_normal)}; m_look_block = LookBlock{block_pos, glm::floor(block_normal)};
} else { } else {
@@ -308,7 +313,8 @@ void ClientPlayer::place_block(float dt) {
glm::ivec3 near_pos = m_look_block->pos + m_look_block->normal; glm::ivec3 near_pos = m_look_block->pos + m_look_block->normal;
if (!m_world.is_solid(near_pos)) { if (!m_world.is_solid(near_pos)) {
AABB block_box = ClientWorld::get_block_aabb(near_pos); AABB block_box = ClientWorld::get_block_aabb(near_pos);
AABB player_box = get_aabb(get_player_pos()); AABB player_box = HitboxManager::aabb("cubed:player");
player_box.center += get_player_pos();
if (!player_box.intersects(block_box)) { if (!player_box.intersects(block_box)) {
m_world.report_block_change(near_pos, type); m_world.report_block_change(near_pos, type);
} }
@@ -332,24 +338,10 @@ void ClientPlayer::update_move(float delta_time) {
if (delta_time > 1.0f) { if (delta_time > 1.0f) {
return; return;
} }
if (m_xz_speed < 0.01f) {
if (m_velocity.value.x < 0.01f || m_velocity.value.z < 0.01f) {
m_sprinting = false; m_sprinting = false;
} }
// ensure the thread safe
glm::vec3 player_pos;
{
std::shared_lock lock(m_player_pos_mutex);
player_pos = m_player_pos;
}
if (m_game_mode != SPECTATOR) {
m_max_speed =
(m_gait == Gait::RUN) ? m_max_run_speed : m_max_walk_speed;
} else {
m_max_speed = m_max_run_speed;
}
if (space_on) { if (space_on) {
space_on_time += delta_time; space_on_time += delta_time;
if (space_on_time >= MAX_SPACE_ON_TIME) { if (space_on_time >= MAX_SPACE_ON_TIME) {
@@ -358,56 +350,38 @@ void ClientPlayer::update_move(float delta_time) {
} }
} }
// calculate speed if (m_game_mode != SPECTATOR) {
if (m_move_state.forward || m_move_state.back || m_move_state.left || m_velocity.max =
m_move_state.right) { (m_walk_pose.gait == Gait::RUN)
direction = glm::vec3(0.0f, 0.0f, 0.0f); ? glm::vec3{m_max_run_speed, m_max_y_speed, m_max_run_speed}
m_xz_speed += m_acceleration * delta_time; : glm::vec3{m_max_walk_speed, m_max_y_speed, m_max_walk_speed};
if (m_xz_speed > m_max_speed) {
m_xz_speed = m_max_speed;
}
} else { } else {
m_xz_speed += -m_deceleration * delta_time; m_velocity.max =
if (m_xz_speed < 0) { glm::vec3{m_max_run_speed, m_max_y_speed, m_max_run_speed};
m_xz_speed = 0;
direction = glm::vec3(0.0f, 0.0f, 0.0f);
}
} }
SpeedSystem::update(delta_time, *this);
update_direction(); update_direction();
move_distance = {direction.x * m_xz_speed * delta_time, 0.0f, // ensure the thread safe
direction.z * m_xz_speed * delta_time}; glm::vec3 player_pos;
if (is_fly) { {
if (m_move_state.up) { std::shared_lock lock(m_player_pos_mutex);
m_y_speed = m_fly_y_speed; player_pos = m_pos.value;
} }
if (m_move_state.down) { if (m_game_mode == SPECTATOR) {
m_y_speed = -m_fly_y_speed; player_pos += PhysicalSystem::get_move_distance(delta_time, *this);
}
if (!m_move_state.down && !m_move_state.up) {
m_y_speed = 0.0f;
}
} else { } else {
if (m_move_state.up && can_up) { auto [x, y, z] =
m_y_speed = 7.5; PhysicalSystem::update(delta_time, *this, m_world, player_pos);
can_up = false; if (!x || !z) {
m_sprinting = false;
} }
m_y_speed += -m_g * delta_time;
} }
move_distance.y = m_y_speed * delta_time;
// y
update_y_move(player_pos);
// x
update_x_move(player_pos);
update_z_move(player_pos);
if (player_pos.y < -15.0f) { if (player_pos.y < -15.0f) {
Logger::warn("y is tow low"); Logger::warn("y is tow low");
player_pos += glm::vec3(1.0f, 100.0f, 1.0f); player_pos += glm::vec3(1.0f, 100.0f, 1.0f);
@@ -415,7 +389,7 @@ void ClientPlayer::update_move(float delta_time) {
{ {
std::lock_guard lock(m_player_pos_mutex); std::lock_guard lock(m_player_pos_mutex);
m_player_pos = player_pos; m_pos.value = player_pos;
} }
update_player_chunk(); update_player_chunk();
auto it = m_timers.find("Player Walk Sound"); auto it = m_timers.find("Player Walk Sound");
@@ -433,115 +407,12 @@ void ClientPlayer::update_move(float delta_time) {
} }
} }
void ClientPlayer::update_x_move(glm::vec3& player_pos) {
player_pos.x += move_distance.x;
if (m_game_mode == SPECTATOR) {
return;
}
AABB player_box = get_aabb(player_pos);
glm::vec3 min = player_box.min();
glm::vec3 max = player_box.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 (!m_world.can_pass_block(block_pos)) {
AABB block_box = ClientWorld::get_block_aabb(block_pos);
if (player_box.intersects(block_box)) {
m_sprinting = false;
player_pos.x -= move_distance.x;
return;
}
}
}
}
}
}
void ClientPlayer::update_y_move(glm::vec3& player_pos) {
player_pos.y += move_distance.y;
if (m_game_mode == SPECTATOR) {
return;
}
AABB player_box = get_aabb(player_pos);
glm::vec3 min = player_box.min();
glm::vec3 max = player_box.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 (!m_world.can_pass_block(block_pos)) {
AABB block_box = ClientWorld::get_block_aabb(block_pos);
if (player_box.intersects(block_box)) {
player_pos.y -= move_distance.y;
m_y_speed = 0.0f;
if (move_distance.y < 0) {
can_up = true;
is_fly = false;
}
return;
}
}
}
}
}
}
void ClientPlayer::update_z_move(glm::vec3& player_pos) {
player_pos.z += move_distance.z;
if (m_game_mode == SPECTATOR) {
return;
}
AABB player_box = get_aabb(player_pos);
glm::vec3 min = player_box.min();
glm::vec3 max = player_box.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 (!m_world.can_pass_block(block_pos)) {
AABB block_box = ClientWorld::get_block_aabb(block_pos);
if (player_box.intersects(block_box)) {
m_sprinting = false;
player_pos.z -= move_distance.z;
return;
}
}
}
}
}
}
void ClientPlayer::update_player_chunk() { void ClientPlayer::update_player_chunk() {
float x, z; float x, z;
{ {
std::shared_lock lock(m_player_pos_mutex); std::shared_lock lock(m_player_pos_mutex);
x = m_player_pos.x; x = m_pos.value.x;
z = m_player_pos.z; z = m_pos.value.z;
} }
ChunkPos chunk_pos = get_chunk_pos(x, z); ChunkPos chunk_pos = get_chunk_pos(x, z);
float dist = distance2(chunk_pos, m_last_chunk_pos); float dist = distance2(chunk_pos, m_last_chunk_pos);
@@ -553,7 +424,7 @@ void ClientPlayer::update_player_chunk() {
} }
Gait ClientPlayer::compute_gait() const { Gait ClientPlayer::compute_gait() const {
if (m_xz_speed < 0.01f) if (m_velocity.value.x < 0.01f && m_velocity.value.z < 0.01f)
return Gait::STOP; return Gait::STOP;
if (m_sprinting) if (m_sprinting)
@@ -565,12 +436,12 @@ Gait ClientPlayer::compute_gait() const {
bool ClientPlayer::update_scroll(float yoffset) { bool ClientPlayer::update_scroll(float yoffset) {
if (m_game_mode == SPECTATOR) { if (m_game_mode == SPECTATOR) {
if (yoffset > 0) { if (yoffset > 0) {
if (m_max_speed < 500.0f) { if (m_max_run_speed < 500.0f) {
m_max_speed += 1.0f; m_max_run_speed += 1.0f;
} }
} else { } else {
if (m_max_speed > 1.0f) { if (m_max_run_speed > 1.0f) {
m_max_speed -= 1.0f; m_max_run_speed -= 1.0f;
} }
} }
} }
@@ -649,15 +520,11 @@ ClientPlayer::ChunkPosSet ClientPlayer::get_chunk_pos_set() {
float& ClientPlayer::max_walk_speed() { return m_max_walk_speed; } float& ClientPlayer::max_walk_speed() { return m_max_walk_speed; }
float& ClientPlayer::max_run_speed() { return m_max_run_speed; } float& ClientPlayer::max_run_speed() { return m_max_run_speed; }
float& ClientPlayer::max_speed() { return m_max_speed; } float& ClientPlayer::fly_y_speed() { return m_max_y_speed; }
float& ClientPlayer::acceleration() { return m_acceleration; }
float& ClientPlayer::deceleration() { return m_deceleration; }
float& ClientPlayer::g() { return m_g; }
float& ClientPlayer::fly_y_speed() { return m_fly_y_speed; }
const ItemStack& ClientPlayer::get_current_itemstack() const { const ItemStack& ClientPlayer::get_current_itemstack() const {
return m_hotbar[m_selected_hotbar]; return m_hotbar[m_selected_hotbar];
}; };
void ClientPlayer::set_gait(Gait gait) { m_gait = gait; }
GameMode& ClientPlayer::game_mode() { return m_game_mode; } GameMode& ClientPlayer::game_mode() { return m_game_mode; }
ClientWorld& ClientPlayer::get_world() { return m_world; } ClientWorld& ClientPlayer::get_world() { return m_world; }
@@ -672,7 +539,7 @@ std::string ClientPlayer::get_uuid() const {
} }
const std::string& ClientPlayer::get_name() const { return m_name; } const std::string& ClientPlayer::get_name() const { return m_name; }
void ClientPlayer::reset_key_status() { void ClientPlayer::reset_input_status() {
m_mouse_state.left = false; m_mouse_state.left = false;
m_mouse_state.right = false; m_mouse_state.right = false;
m_move_state.left = false; m_move_state.left = false;
@@ -684,14 +551,25 @@ void ClientPlayer::reset_key_status() {
} }
void ClientPlayer::init(std::string_view name) { void ClientPlayer::init(std::string_view name) {
{
std::lock_guard lock(m_player_pos_mutex);
m_pos.value = {0.0f, 255.0f, 0.0f};
}
m_name = name; m_name = name;
m_timers.try_emplace("Player Walk Sound", WALK_SOUND_INTERVAL, [this]() { m_timers.try_emplace("Player Walk Sound", WALK_SOUND_INTERVAL, [this]() {
if (!m_moving || is_fly) { if (!m_moving || m_move_state.is_fly) {
return; return;
} }
glm::ivec3 block = glm::floor(m_player_pos); glm::ivec3 block;
glm::vec3 pos;
{
std::shared_lock lock(m_player_pos_mutex);
block = glm::floor(m_pos.value);
pos = m_pos.value;
}
block.y -= 1; block.y -= 1;
BlockType id = m_world.get_block_tpye(block); BlockType id = m_world.get_block_tpye(block);
if (id == 0) { if (id == 0) {
@@ -700,7 +578,7 @@ void ClientPlayer::init(std::string_view name) {
std::string name = BlockManager::name_form_id(id); std::string name = BlockManager::name_form_id(id);
std::string sound = "block/" + name + "/walk.ogg"; std::string sound = "block/" + name + "/walk.ogg";
auto& audio = m_world.get_audio(); auto& audio = m_world.get_audio();
audio.play_3d(sound, m_player_pos); audio.play_3d(sound, pos);
Logger::debug("Player block {} walk sound", name); Logger::debug("Player block {} walk sound", name);
}); });
@@ -711,9 +589,4 @@ void ClientPlayer::init(std::string_view name) {
bool ClientPlayer::is_underwater() const { return m_underwater; } bool ClientPlayer::is_underwater() const { return m_underwater; }
void ClientPlayer::set_underwater(bool u) { m_underwater = u; } void ClientPlayer::set_underwater(bool u) { m_underwater = u; }
float ClientPlayer::yaw() const { return m_yaw; }
float ClientPlayer::pitch() const { return m_pitch; }
float& ClientPlayer::angle() { return m_angle; }
float& ClientPlayer::walk_time() { return m_walk_time; }
} // namespace Cubed } // namespace Cubed

View File

@@ -4,6 +4,7 @@
#include "Cubed/gameplay/chunk_generator.hpp" #include "Cubed/gameplay/chunk_generator.hpp"
#include "Cubed/gameplay/entity.hpp" #include "Cubed/gameplay/entity.hpp"
#include "Cubed/gameplay/game_time.hpp" #include "Cubed/gameplay/game_time.hpp"
#include "Cubed/gameplay/hitbox_manager.hpp"
#include "Cubed/gameplay/packet.hpp" #include "Cubed/gameplay/packet.hpp"
#include "Cubed/scene/world_scene.hpp" #include "Cubed/scene/world_scene.hpp"
#include "Cubed/tools/math_tools.hpp" #include "Cubed/tools/math_tools.hpp"
@@ -299,7 +300,8 @@ void ClientWorld::report_block_change(const glm::ivec3& pos,
for (const auto& player : m_players_data) { for (const auto& player : m_players_data) {
AABB box = ClientPlayer::get_aabb(player.pos.value); AABB box = HitboxManager::aabb("cubed:player");
box.center += player.pos.value;
if (box.intersects(block_box)) { if (box.intersects(block_box)) {
return; return;
} }
@@ -692,7 +694,7 @@ void ClientWorld::request_chunk() {
Logger::info("Send Chunk Request Success"); Logger::info("Send Chunk Request Success");
m_requesting_chunk = false; m_requesting_chunk = false;
} }
void ClientWorld::reset_key_status() { m_player.reset_key_status(); } void ClientWorld::reset_key_status() { m_player.reset_input_status(); }
void ClientWorld::receive_chunk(std::vector<uint8_t> raw_data, void ClientWorld::receive_chunk(std::vector<uint8_t> raw_data,
PacketHeader header) { PacketHeader header) {
@@ -956,7 +958,7 @@ void ClientWorld::update_players(float dt) {
{ {
auto gait = m_player.get_gait(); auto gait = m_player.get_gait();
auto& walk_time = m_player.walk_time(); auto& walk_time = m_player.walk_time();
auto& angle = m_player.angle(); auto& angle = m_player.roll();
if (gait == Gait::WALK || gait == Gait::RUN) { if (gait == Gait::WALK || gait == Gait::RUN) {
walk_time += dt; walk_time += dt;

33
src/gameplay/entity.cpp Normal file
View File

@@ -0,0 +1,33 @@
#include "Cubed/gameplay/entity.hpp"
namespace Cubed {
glm::vec3& Entity::max_speed() { return m_velocity.max; }
float& Entity::acceleration() { return m_movement.acceleration; }
float& Entity::deceleration() { return m_movement.deceleration; }
float& Entity::g() { return m_gravity.value; }
void Entity::set_gait(Gait gait) { m_walk_pose.gait = gait; }
float Entity::yaw() const { return m_angle.yaw; }
float Entity::pitch() const { return m_angle.pitch; }
float& Entity::roll() { return m_angle.roll; }
float& Entity::walk_time() { return m_walk_pose.walk_time; }
Gait Entity::get_gait() const { return m_walk_pose.gait; }
Velocity& Entity::velocity() { return m_velocity; }
Position& Entity::pos() { return m_pos; }
WalkPose& Entity::walk_pose() { return m_walk_pose; }
Orientation& Entity::angle() { return m_angle; }
Movement& Entity::movement() { return m_movement; }
Gravity& Entity::gravity() { return m_gravity; }
MoveState& Entity::move_state() { return m_move_state; }
Direction& Entity::direction() { return m_direction; }
const Velocity& Entity::velocity() const { return m_velocity; }
const Position& Entity::pos() const { return m_pos; }
const WalkPose& Entity::walk_pose() const { return m_walk_pose; }
const Orientation& Entity::angle() const { return m_angle; }
const Movement& Entity::movement() const { return m_movement; }
const Gravity& Entity::gravity() const { return m_gravity; }
const MoveState& Entity::move_state() const { return m_move_state; }
const Direction& Entity::direction() const { return m_direction; }
} // namespace Cubed

View File

@@ -1,12 +1,20 @@
#include "Cubed/gameplay/hitbox_manager.hpp" #include "Cubed/gameplay/hitbox_manager.hpp"
#include "Cubed/gameplay/player.hpp"
#include "Cubed/tools/log.hpp" #include "Cubed/tools/log.hpp"
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
namespace fs = std::filesystem; namespace fs = std::filesystem;
using nlohmann::json; using nlohmann::json;
namespace Cubed { namespace Cubed {
HitboxManager::HitboxManager() {} HitboxManager::HitboxManager() {
glm::vec3 half = PLAYER_SIZE * 0.5f;
glm::vec3 center{0.0f, half.y, 0.0f};
m_hitboxes.emplace("cubed:player", AABB{center, half});
}
HitboxManager::~HitboxManager() {} HitboxManager::~HitboxManager() {}
HitboxManager& HitboxManager::instance() { HitboxManager& HitboxManager::instance() {

View File

@@ -0,0 +1,154 @@
#include "Cubed/gameplay/systems/physical_system.hpp"
#include "Cubed/gameplay/hitbox_manager.hpp"
namespace Cubed {
namespace {
bool update_x(const glm::vec3& pos, const glm::vec3& distance, World& world,
const AABB& box) {
glm::vec3 p = pos;
p.x += distance.x;
AABB 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)) {
AABB 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, World& world,
const AABB& box) {
glm::vec3 p = pos;
p.y += distance.y;
AABB 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)) {
AABB 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, World& world,
const AABB& box) {
glm::vec3 p = pos;
p.z += distance.z;
AABB 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)) {
AABB block_box = World::get_block_aabb(block_pos);
if (b.intersects(block_box)) {
return false;
}
}
}
}
}
return true;
}
} // namespace
std::tuple<bool, bool, bool> PhysicalSystem::update(float dt, Entity& e,
World& world) {
glm::vec3 pos = e.pos().value;
auto ans = update(dt, e, world, pos);
e.pos().value = pos;
return ans;
}
std::tuple<bool, bool, bool> PhysicalSystem::update(float dt, Entity& e,
World& world,
glm::vec3& moved_pos) {
auto distance = get_move_distance(dt, e);
auto& m_velocity = e.velocity();
auto& m_move_state = e.move_state();
AABB box = HitboxManager::aabb("cubed:player");
bool x = false;
bool y = false;
bool z = false;
if (update_x(moved_pos, distance, world, box)) {
moved_pos.x += distance.x;
x = true;
} else {
m_velocity.value.x = 0.0f;
}
if (update_y(moved_pos, distance, world, box)) {
moved_pos.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(moved_pos, distance, world, box)) {
moved_pos.z += distance.z;
z = true;
} else {
m_velocity.value.z = 0.0f;
}
return {x, y, z};
}
glm::vec3 PhysicalSystem::get_move_distance(float dt, const Entity& e) {
auto& m_direction = e.direction();
auto& m_velocity = e.velocity();
return glm::vec3{m_direction.value.x * m_velocity.value.x * dt,
m_velocity.value.y * dt,
m_direction.value.z * m_velocity.value.z * dt};
}
} // namespace Cubed

View File

@@ -0,0 +1,56 @@
#include "Cubed/gameplay/systems/speed_system.hpp"
namespace Cubed {
void SpeedSystem::update(float dt, Entity& e) {
auto& m_velocity = e.velocity();
auto& m_move_state = e.move_state();
auto& m_movement = e.movement();
auto& direction = e.direction();
auto& m_gravity = e.gravity();
// calculate speed
if (m_move_state.forward || m_move_state.back || m_move_state.left ||
m_move_state.right) {
direction.value = glm::vec3(0.0f, 0.0f, 0.0f);
m_velocity.value.x += m_movement.acceleration * dt;
m_velocity.value.z += m_movement.acceleration * dt;
if (m_velocity.value.x > m_velocity.max.x) {
m_velocity.value.x = m_velocity.max.x;
}
if (m_velocity.value.z > m_velocity.max.z) {
m_velocity.value.z = m_velocity.max.z;
}
} else {
m_velocity.value.x += -m_movement.deceleration * dt;
m_velocity.value.z += -m_movement.deceleration * dt;
if (m_velocity.value.z < 0.0f) {
m_velocity.value.z = 0.0f;
}
if (m_velocity.value.x < 0.0f) {
m_velocity.value.x = 0.0f;
}
if (m_velocity.value.z < 0.0f && m_velocity.value.x < 0.0f) {
direction.value = glm::vec3(0.0f, 0.0f, 0.0f);
}
}
if (m_move_state.is_fly) {
if (m_move_state.up) {
m_velocity.value.y = m_velocity.max.y;
}
if (m_move_state.down) {
m_velocity.value.y = -m_velocity.max.y;
}
if (!m_move_state.down && !m_move_state.up) {
m_velocity.value.y = 0.0f;
}
} else {
if (m_move_state.up && m_move_state.can_up) {
m_velocity.value.y = m_movement.jump_power;
m_move_state.can_up = false;
}
m_velocity.value.y += -m_gravity.value * dt;
}
}
} // namespace Cubed

View File

@@ -330,7 +330,7 @@ void Window::set_imgui_enabled(bool enable) {
if (m_camera) { if (m_camera) {
auto player = m_camera->player(); auto player = m_camera->player();
if (player) { if (player) {
player->reset_key_status(); player->reset_input_status();
} }
} }
} }