feat: sync entity direction and rotate models accordingly

Move direction into the transform component and include it in server-to-client entity updates. Client now sets transform direction from the network message and uses it to compute yaw for model rotation, so entities visually face their movement direction. Refactor net_utils to support arbitrary Vec3 fields.
This commit is contained in:
2026-08-04 10:46:31 +08:00
parent 6b1f9d7354
commit e2bbc2d57f
12 changed files with 37 additions and 18 deletions

View File

@@ -49,6 +49,7 @@ void ClientEntityManager::handle_entity_update(UpdateInfo& info) {
auto creature = m_registry.try_get<BaseClientCreature>(e);
ASSERT(creature);
creature->transform.position.value = info.pos;
creature->transform.direction.value = info.direction;
}
void ClientEntityManager::receive_entity_create(S2CEntityCreate& s2c) {
@@ -66,7 +67,8 @@ void ClientEntityManager::receive_entity_destory(EntityID id) {
void ClientEntityManager::receive_entity_update(S2CEntityUpdate& msg) {
UpdateInfo e;
e.id = msg.id();
e.pos = Tools::get_net_pos(msg.pos());
e.pos = Tools::get_net_vec3(msg.pos());
e.direction = Tools::get_net_vec3(msg.direction());
m_tasks.emplace(Command::UPDATE, std::move(e));
}

View File

@@ -56,6 +56,9 @@ void ServerEntityManager::update_send() {
p->set_id(entity.id);
Tools::set_net_pos(p, creature.transform.position.value);
Tools::set_net_vec3(p->mutable_direction(),
creature.transform.direction.value);
for (auto& s : sessions) {
s->send(make_packet(p));
}

View File

@@ -875,7 +875,7 @@ void ServerWorld::handle_block_change(const BlockChangeReq& req) {
void ServerWorld::handle_entity_create(C2SEntityCreateRequest& req) {
m_entity_manager.add_entity(req.name(), Tools::get_net_pos(req.pos()));
m_entity_manager.add_entity(req.name(), Tools::get_net_vec3(req.pos()));
}
void ServerWorld::handle_entity_destory(C2SEntityDestoryRequest& req) {
m_entity_manager.destory(req.id());

View File

@@ -13,7 +13,8 @@ void SpeedSystem::update(float dt, entt::registry& registry) {
auto& v = creature.velocity.value;
if (moveboost.count <= moveboost.duration) {
++moveboost.count;
v += creature.direction.value * creature.movement.acceleration;
v += creature.transform.direction.value *
creature.movement.acceleration;
} else {
// Decelerated by friction in all directions
auto decay = [](float& c, float d) {

View File

@@ -29,7 +29,7 @@ void WanderAISystem::do_ai(BaseServerCreature& creature,
MoveBoost& move_boost) {
thread_local Random r{std::random_device()()};
if (r.random_bool(DIRECTION_PROBABILITY)) {
creature.direction.value = r.random_direction_horizontal();
creature.transform.direction.value = r.random_direction_horizontal();
}
if (r.random_bool(MOVE_PROBABILITY)) {

View File

@@ -26,4 +26,5 @@ message C2SEntityCreateRequest {
message S2CEntityUpdate {
uint64 id = 1;
Vec3 pos = 2;
Vec3 direction = 3;
}

View File

@@ -6,20 +6,23 @@
namespace Cubed {
ModelRender::ModelRender(Renderer& renderer) : m_renderer(renderer) {}
void ModelRender::render_model(ModelID id, const glm::vec3& pos,
void ModelRender::render_model(ModelID id, const glm::vec3& pos, float yaw,
Camera& camera) {
auto& root = ModelManager::model(id).node;
glm::mat4 transform = glm::translate(glm::mat4(1.0f), pos);
glm::mat4 transform = glm::translate(glm::mat4(1.0f), pos) *
glm::rotate(glm::mat4(1.0f), yaw, {0, 1, 0});
auto& shader = m_renderer.get_shader("model_shader");
glm::mat4 view = camera.get_camera_lookat();
shader.set_loc("proj_matrix", m_renderer.p_mat());
render_node(root, transform, view, shader, false);
}
void ModelRender::shadow_pass(ModelID id, const glm::vec3& pos,
void ModelRender::shadow_pass(ModelID id, const glm::vec3& pos, float yaw,
Camera& camera) {
auto& root = ModelManager::model(id).node;
glm::mat4 transform = glm::translate(glm::mat4(1.0f), pos);
glm::mat4 transform = glm::translate(glm::mat4(1.0f), pos) *
glm::rotate(glm::mat4(1.0f), yaw, {0, 1, 0});
auto& shader = m_renderer.get_shader("depth_model");
glm::mat4 view = camera.get_camera_lookat();

View File

@@ -300,8 +300,11 @@ void WorldRenderer::shadow_entity(ClientWorld& world,
auto view = registry.view<BaseClientCreature>();
for (auto entity : view) {
auto& creature = view.get<BaseClientCreature>(entity);
float yaw = std::atan2(creature.transform.direction.value.x,
creature.transform.direction.value.z);
m_renderer.model_renderer().shadow_pass(
creature.model, creature.transform.position.value,
creature.model, creature.transform.position.value, yaw,
world.world_scene().camera());
}
m_player_renderer.render(shader, world, true);
@@ -721,8 +724,10 @@ void WorldRenderer::render_entity(ClientWorld& world) {
auto view = registry.view<BaseClientCreature>();
for (auto entity : view) {
auto& creature = view.get<BaseClientCreature>(entity);
float yaw = std::atan2(creature.transform.direction.value.x,
creature.transform.direction.value.z);
m_renderer.model_renderer().render_model(
creature.model, creature.transform.position.value,
creature.model, creature.transform.position.value, yaw,
world.world_scene().camera());
}