feat: add moving platform support + fix jolt
This commit is contained in:
@@ -27,6 +27,16 @@ namespace {
|
||||
}
|
||||
return current + delta / distance * maxDelta;
|
||||
}
|
||||
|
||||
glm::vec3 ProjectOntoSlope(const glm::vec3& vector, const glm::vec3& slopeNormal)
|
||||
{
|
||||
const glm::vec3 projected = vector - slopeNormal * glm::dot(vector, slopeNormal);
|
||||
const float projectedLength = glm::length(projected);
|
||||
if (projectedLength < 1e-6f) {
|
||||
return glm::vec3{0.0f};
|
||||
}
|
||||
return projected * (glm::length(vector) / projectedLength);
|
||||
}
|
||||
}
|
||||
|
||||
FirstPersonController::FirstPersonController(GameObject& parent)
|
||||
@@ -130,22 +140,38 @@ void FirstPersonController::FixedUpdate(float fixedDt)
|
||||
m_PreviousPosition = GetTransform().GetWorldPosition();
|
||||
m_HasPreviousPosition = true;
|
||||
|
||||
glm::vec3 velocity = m_Rigidbody->GetLinearVelocity();
|
||||
const std::optional<GroundContact> ground = FindGround();
|
||||
const glm::vec3 groundVelocity = ground ? ground->velocity : glm::vec3{0.0f};
|
||||
|
||||
m_Grounded = velocity.y <= 0.1f && CheckGrounded();
|
||||
// Movement happens relative to whatever is underneath, so moving platforms carry the player
|
||||
glm::vec3 velocity = m_Rigidbody->GetLinearVelocity() - groundVelocity;
|
||||
|
||||
m_Grounded = ground.has_value() && glm::dot(velocity, ground->normal) <= 0.1f;
|
||||
m_GroundNormal = m_Grounded ? ground->normal : glm::vec3{0.0f, 1.0f, 0.0f};
|
||||
m_CoyoteTimer = m_Grounded ? m_CoyoteTime : std::max(m_CoyoteTimer - fixedDt, 0.0f);
|
||||
|
||||
const auto& input = InputManager::GetInstance();
|
||||
const bool sprinting = m_Possessed && input.IsKeyDown(SDL_SCANCODE_LSHIFT);
|
||||
const glm::vec3 wishVelocity = GetWishDirection() * (sprinting ? m_SprintSpeed : m_WalkSpeed);
|
||||
|
||||
const float acceleration = m_Grounded ? m_GroundAcceleration : m_AirAcceleration;
|
||||
const glm::vec3 horizontal = MoveTowards(
|
||||
glm::vec3{velocity.x, 0.0f, velocity.z},
|
||||
wishVelocity,
|
||||
acceleration * fixedDt);
|
||||
velocity.x = horizontal.x;
|
||||
velocity.z = horizontal.z;
|
||||
if (m_Grounded)
|
||||
{
|
||||
const glm::vec3 velocityAlongSlope = velocity - m_GroundNormal * glm::dot(velocity, m_GroundNormal);
|
||||
velocity = MoveTowards(
|
||||
velocityAlongSlope,
|
||||
ProjectOntoSlope(wishVelocity, m_GroundNormal),
|
||||
m_GroundAcceleration * fixedDt);
|
||||
velocity -= m_GroundNormal * m_GroundStickSpeed;
|
||||
}
|
||||
else
|
||||
{
|
||||
const glm::vec3 horizontal = MoveTowards(
|
||||
glm::vec3{velocity.x, 0.0f, velocity.z},
|
||||
wishVelocity,
|
||||
m_AirAcceleration * fixedDt);
|
||||
velocity.x = horizontal.x;
|
||||
velocity.z = horizontal.z;
|
||||
}
|
||||
|
||||
if (m_JumpBufferTimer > 0.0f && m_CoyoteTimer > 0.0f)
|
||||
{
|
||||
@@ -155,22 +181,25 @@ void FirstPersonController::FixedUpdate(float fixedDt)
|
||||
m_Grounded = false;
|
||||
}
|
||||
|
||||
// Falling faster than rising, and letting go of jump early cuts the jump short
|
||||
float gravity = m_Gravity;
|
||||
if (velocity.y < 0.0f || !m_JumpHeld)
|
||||
if (!m_Grounded)
|
||||
{
|
||||
gravity *= m_FallGravityMultiplier;
|
||||
// Falling faster than rising, and letting go of jump early cuts the jump short
|
||||
float gravity = m_Gravity;
|
||||
if (velocity.y < 0.0f || !m_JumpHeld)
|
||||
{
|
||||
gravity *= m_FallGravityMultiplier;
|
||||
}
|
||||
velocity.y -= gravity * fixedDt;
|
||||
}
|
||||
velocity.y -= gravity * fixedDt;
|
||||
|
||||
m_Rigidbody->SetLinearVelocity(velocity);
|
||||
m_Rigidbody->SetLinearVelocity(velocity + groundVelocity);
|
||||
}
|
||||
|
||||
bool FirstPersonController::CheckGrounded() const
|
||||
std::optional<FirstPersonController::GroundContact> FirstPersonController::FindGround() const
|
||||
{
|
||||
const auto* world = m_Rigidbody->GetPhysicsWorld();
|
||||
if (world == nullptr || m_Capsule == nullptr)
|
||||
return false;
|
||||
return std::nullopt;
|
||||
|
||||
const glm::vec3 center = GetTransform().GetWorldPosition() + m_Capsule->GetCenterOffset();
|
||||
const float radius = m_Capsule->GetRadius();
|
||||
@@ -187,6 +216,7 @@ bool FirstPersonController::CheckGrounded() const
|
||||
|
||||
constexpr float startHeight = 0.2f;
|
||||
constexpr float probeDistance = 0.1f;
|
||||
const float minWalkableNormalY = std::cos(m_MaxSlopeAngle);
|
||||
for (const glm::vec2& offset : offsets)
|
||||
{
|
||||
const glm::vec3 origin{center.x + offset.x, bottom + startHeight, center.z + offset.y};
|
||||
@@ -194,12 +224,15 @@ bool FirstPersonController::CheckGrounded() const
|
||||
PhysicsRaycastHit hit{};
|
||||
if (world->Raycast(origin, glm::vec3{0.0f, -1.0f, 0.0f}, startHeight + probeDistance, hit,
|
||||
m_Rigidbody->GetBody()) &&
|
||||
hit.normal.y > 0.6f)
|
||||
hit.normal.y >= minWalkableNormalY)
|
||||
{
|
||||
return true;
|
||||
return GroundContact{
|
||||
.normal = glm::normalize(hit.normal),
|
||||
.velocity = hit.body.IsValid() ? world->GetLinearVelocity(hit.body) : glm::vec3{0.0f},
|
||||
};
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
glm::vec3 FirstPersonController::GetWishDirection() const
|
||||
@@ -243,9 +276,12 @@ void FirstPersonController::ImGuiInspector()
|
||||
ImGui::DragFloat("Coyote Time", &m_CoyoteTime, 0.005f, 0.0f, 1.0f);
|
||||
ImGui::DragFloat("Jump Buffer Time", &m_JumpBufferTime, 0.005f, 0.0f, 1.0f);
|
||||
ImGui::DragFloat("Interact Distance", &m_InteractDistance, 0.05f, 0.0f, 20.0f);
|
||||
ImGui::SliderAngle("Max Slope Angle", &m_MaxSlopeAngle, 0.0f, 89.0f);
|
||||
ImGui::DragFloat("Ground Stick Speed", &m_GroundStickSpeed, 0.05f, 0.0f, 20.0f);
|
||||
ImGui::DragFloat("Mouse Sensitivity", &m_MouseSensitivity, 0.0001f, 0.0f, 0.1f);
|
||||
ImGui::DragFloat("Eye Height", &m_EyeHeight, 0.01f, -2.0f, 2.0f);
|
||||
ImGui::Text("Grounded: %s", m_Grounded ? "yes" : "no");
|
||||
ImGui::Text("Ground slope: %.1f deg", glm::degrees(std::acos(glm::clamp(m_GroundNormal.y, -1.0f, 1.0f))));
|
||||
ImGui::Text("Yaw: %.1f deg", glm::degrees(m_Yaw));
|
||||
ImGui::Text("Pitch: %.1f deg", glm::degrees(m_Pitch));
|
||||
if (bool possessed = m_Possessed; ImGui::Checkbox("Possessed", &possessed)) {
|
||||
@@ -266,6 +302,8 @@ nlohmann::json FirstPersonController::Serialize() const
|
||||
{"coyoteTime", m_CoyoteTime},
|
||||
{"jumpBufferTime", m_JumpBufferTime},
|
||||
{"interactDistance", m_InteractDistance},
|
||||
{"maxSlopeAngle", m_MaxSlopeAngle},
|
||||
{"groundStickSpeed", m_GroundStickSpeed},
|
||||
{"mouseSensitivity", m_MouseSensitivity},
|
||||
{"eyeHeight", m_EyeHeight},
|
||||
{"yaw", m_Yaw},
|
||||
@@ -285,6 +323,8 @@ void FirstPersonController::Deserialize(const nlohmann::json& data)
|
||||
m_CoyoteTime = data.value("coyoteTime", m_CoyoteTime);
|
||||
m_JumpBufferTime = data.value("jumpBufferTime", m_JumpBufferTime);
|
||||
m_InteractDistance = data.value("interactDistance", m_InteractDistance);
|
||||
m_MaxSlopeAngle = data.value("maxSlopeAngle", m_MaxSlopeAngle);
|
||||
m_GroundStickSpeed = data.value("groundStickSpeed", m_GroundStickSpeed);
|
||||
m_MouseSensitivity = data.value("mouseSensitivity", m_MouseSensitivity);
|
||||
m_EyeHeight = data.value("eyeHeight", m_EyeHeight);
|
||||
m_Yaw = data.value("yaw", m_Yaw);
|
||||
|
||||
Reference in New Issue
Block a user