refactor(character): 使用整数坐标优化角色位置同步

- 将CharacterWorldPosition改为使用整数坐标,避免浮点精度问题
- 添加位置余数处理,确保移动平滑性
- 统一角色位置同步逻辑到SyncActorPositionFromWorld方法
- 修改地图移动检测使用整数坐标判断
This commit is contained in:
2026-04-05 11:42:39 +08:00
parent c4eefab70c
commit 2b0cfc6ce5
5 changed files with 122 additions and 37 deletions

View File

@@ -54,13 +54,17 @@ enum class CharacterStateId {
/// 角色的逻辑世界坐标。x/y 是地面平面z 是高度轴。
struct CharacterWorldPosition {
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
int32 x = 0;
int32 y = 0;
int32 z = 0;
/// 当前项目的 2.5D 投影:地面 y 决定站位,高度 z 决定视觉抬升。
Vec2 ToScreenPosition() const { return Vec2(x, y - z); }
Vec2 ToGroundPosition() const { return Vec2(x, y); }
Vec2 ToScreenPosition() const {
return Vec2(static_cast<float>(x), static_cast<float>(y - z));
}
Vec2 ToGroundPosition() const {
return Vec2(static_cast<float>(x), static_cast<float>(y));
}
};
/// 运动器只负责推进坐标和速度,不直接决定状态流转。
@@ -77,8 +81,14 @@ struct CharacterMotor {
float gravity = 1600.0f;
void SetGroundPosition(const Vec2& groundPosition) {
position.x = groundPosition.x;
position.y = groundPosition.y;
position.x = RoundWorldCoordinate(groundPosition.x);
position.y = RoundWorldCoordinate(groundPosition.y);
positionRemainder_ = Vec2::Zero();
}
void SetWorldPosition(const CharacterWorldPosition& worldPosition) {
position = worldPosition;
ClearPositionRemainders();
}
void ApplyGroundInput(float moveX, float moveY, float speedScale = 1.0f) {
@@ -113,42 +123,75 @@ struct CharacterMotor {
void Jump() {
grounded = false;
verticalVelocity = jumpSpeed;
if (position.z < 0.0f) {
position.z = 0.0f;
if (position.z < 0) {
position.z = 0;
verticalPositionRemainder_ = 0.0f;
}
}
void Update(float deltaTime) {
position.x += groundVelocity.x * deltaTime;
position.y += groundVelocity.y * deltaTime;
positionRemainder_.x += groundVelocity.x * deltaTime;
positionRemainder_.y += groundVelocity.y * deltaTime;
if (forcedSlideRemainingTime > 0.0f && forcedSlideVelocity.lengthSquared() > 0.0f) {
float slideDeltaTime = deltaTime < forcedSlideRemainingTime
? deltaTime
: forcedSlideRemainingTime;
position.x += forcedSlideVelocity.x * slideDeltaTime;
position.y += forcedSlideVelocity.y * slideDeltaTime;
positionRemainder_.x += forcedSlideVelocity.x * slideDeltaTime;
positionRemainder_.y += forcedSlideVelocity.y * slideDeltaTime;
forcedSlideRemainingTime -= slideDeltaTime;
if (forcedSlideRemainingTime <= 0.0f) {
ClearForcedSlide();
}
}
position.x += ConsumeWholeUnits(positionRemainder_.x);
position.y += ConsumeWholeUnits(positionRemainder_.y);
if (!grounded || position.z > 0.0f || verticalVelocity > 0.0f) {
position.z += verticalVelocity * deltaTime;
if (!grounded || position.z > 0 || verticalVelocity > 0.0f) {
verticalPositionRemainder_ += verticalVelocity * deltaTime;
verticalVelocity -= gravity * deltaTime;
grounded = false;
position.z += ConsumeWholeUnits(verticalPositionRemainder_);
if (position.z <= 0.0f) {
position.z = 0.0f;
if (position.z <= 0) {
position.z = 0;
verticalVelocity = 0.0f;
verticalPositionRemainder_ = 0.0f;
grounded = true;
}
} else {
position.z = 0.0f;
position.z = 0;
verticalVelocity = 0.0f;
verticalPositionRemainder_ = 0.0f;
grounded = true;
}
}
private:
static int32 RoundWorldCoordinate(float value) {
return static_cast<int32>(std::lround(value));
}
static int32 ConsumeWholeUnits(float& remainder) {
if (remainder >= 1.0f) {
int32 wholeUnits = static_cast<int32>(std::floor(remainder));
remainder -= static_cast<float>(wholeUnits);
return wholeUnits;
}
if (remainder <= -1.0f) {
int32 wholeUnits = static_cast<int32>(std::ceil(remainder));
remainder -= static_cast<float>(wholeUnits);
return wholeUnits;
}
return 0;
}
void ClearPositionRemainders() {
positionRemainder_ = Vec2::Zero();
verticalPositionRemainder_ = 0.0f;
}
Vec2 positionRemainder_ = Vec2::Zero();
float verticalPositionRemainder_ = 0.0f;
};
/// 逻辑动作定义。这里只保留显式注册的逻辑入口名。

View File

@@ -170,6 +170,7 @@ private:
void CommitPendingActionContext(const std::string& defaultRequestedActionId,
const std::string& defaultSourceActionId,
CharacterStateId defaultSourceStateId);
void SyncActorPositionFromWorld();
bool SetActionStrict(const std::string& actionName,
const char* phase,
const std::string& requestedActionId);