|
@@ -23,6 +23,7 @@ namespace PhysX
|
|
|
serializeContext->Class<CharacterGameplayConfiguration>()
|
|
|
->Version(1)
|
|
|
->Field("GravityMultiplier", &CharacterGameplayConfiguration::m_gravityMultiplier)
|
|
|
+ ->Field("GroundDetectionBoxHeight", &CharacterGameplayConfiguration::m_groundDetectionBoxHeight)
|
|
|
;
|
|
|
|
|
|
if (auto editContext = serializeContext->GetEditContext())
|
|
@@ -33,6 +34,13 @@ namespace PhysX
|
|
|
->DataElement(AZ::Edit::UIHandlers::Default, &CharacterGameplayConfiguration::m_gravityMultiplier,
|
|
|
"Gravity Multiplier", "Multiplier for global gravity value that applies only to this character entity.")
|
|
|
->Attribute(AZ::Edit::Attributes::Step, 0.1f)
|
|
|
+ ->DataElement(
|
|
|
+ AZ::Edit::UIHandlers::Default,
|
|
|
+ &CharacterGameplayConfiguration::m_groundDetectionBoxHeight,
|
|
|
+ "Ground Detection Box Height",
|
|
|
+ "Vertical size of box centered on the character's foot position used when testing for ground contact.")
|
|
|
+ ->Attribute(AZ::Edit::Attributes::Min, 0.0f)
|
|
|
+ ->Attribute(AZ::Edit::Attributes::Step, 0.001f)
|
|
|
;
|
|
|
}
|
|
|
}
|
|
@@ -60,6 +68,7 @@ namespace PhysX
|
|
|
|
|
|
CharacterGameplayComponent::CharacterGameplayComponent(const CharacterGameplayConfiguration& config)
|
|
|
: m_gravityMultiplier(config.m_gravityMultiplier)
|
|
|
+ , m_groundDetectionBoxHeight(config.m_groundDetectionBoxHeight)
|
|
|
{
|
|
|
}
|
|
|
|
|
@@ -72,6 +81,7 @@ namespace PhysX
|
|
|
serializeContext->Class<CharacterGameplayComponent, AZ::Component>()
|
|
|
->Version(1)
|
|
|
->Field("GravityMultiplier", &CharacterGameplayComponent::m_gravityMultiplier)
|
|
|
+ ->Field("GroundDetectionBoxHeight", &CharacterGameplayComponent::m_groundDetectionBoxHeight)
|
|
|
;
|
|
|
}
|
|
|
|
|
@@ -83,6 +93,14 @@ namespace PhysX
|
|
|
->Event("IsOnGround", &CharacterGameplayRequests::IsOnGround, "Is On Ground")
|
|
|
->Event("GetGravityMultiplier", &CharacterGameplayRequests::GetGravityMultiplier, "Get Gravity Multiplier")
|
|
|
->Event("SetGravityMultiplier", &CharacterGameplayRequests::SetGravityMultiplier, "Set Gravity Multiplier")
|
|
|
+ ->Event(
|
|
|
+ "GetGroundDetectionBoxHeight",
|
|
|
+ &CharacterGameplayRequests::GetGroundDetectionBoxHeight,
|
|
|
+ "Get Ground Detection Box Height")
|
|
|
+ ->Event(
|
|
|
+ "SetGroundDetectionBoxHeight",
|
|
|
+ &CharacterGameplayRequests::SetGroundDetectionBoxHeight,
|
|
|
+ "Set Ground Detection Box Height")
|
|
|
->Event("GetFallingVelocity", &CharacterGameplayRequests::GetFallingVelocity, "Get Falling Velocity")
|
|
|
->Event("SetFallingVelocity", &CharacterGameplayRequests::SetFallingVelocity, "Set Falling Velocity")
|
|
|
;
|
|
@@ -91,25 +109,83 @@ namespace PhysX
|
|
|
|
|
|
// CharacterGameplayRequestBus
|
|
|
bool CharacterGameplayComponent::IsOnGround() const
|
|
|
+ {
|
|
|
+ if (m_cachedGroundState == CharacterGroundState::NotYetDetermined)
|
|
|
+ {
|
|
|
+ DetermineCachedGroundState();
|
|
|
+ }
|
|
|
+
|
|
|
+ return m_cachedGroundState == CharacterGroundState::Touching;
|
|
|
+ }
|
|
|
+
|
|
|
+ void CharacterGameplayComponent::DetermineCachedGroundState() const
|
|
|
{
|
|
|
Physics::Character* character = nullptr;
|
|
|
Physics::CharacterRequestBus::EventResult(character, GetEntityId(), &Physics::CharacterRequests::GetCharacter);
|
|
|
if (!character)
|
|
|
{
|
|
|
- return true;
|
|
|
+ m_cachedGroundState = CharacterGroundState::Touching;
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
auto pxController = static_cast<physx::PxController*>(character->GetNativePointer());
|
|
|
if (!pxController)
|
|
|
{
|
|
|
- return true;
|
|
|
+ m_cachedGroundState = CharacterGroundState::Touching;
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
+ // first check if we can use the character controller state, which should be cheaper than doing a scene query
|
|
|
+
|
|
|
+ // if the controller is slightly above an object or has not been asked to move downwards, the PxController may
|
|
|
+ // not report a touched actor or downward collision, so this can give false negatives, but should not give
|
|
|
+ // false positives, so it's useful as an early out
|
|
|
physx::PxControllerState state;
|
|
|
pxController->getState(state);
|
|
|
- return
|
|
|
- state.touchedActor != nullptr ||
|
|
|
- (state.collisionFlags & physx::PxControllerCollisionFlag::eCOLLISION_DOWN) != 0;
|
|
|
+
|
|
|
+ if (state.touchedActor != nullptr || (state.collisionFlags & physx::PxControllerCollisionFlag::eCOLLISION_DOWN) != 0)
|
|
|
+ {
|
|
|
+ m_cachedGroundState = CharacterGroundState::Touching;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // if we get to this point it's still unclear whether the character is touching the ground so
|
|
|
+ // use an overlap query to see if there's any geometry immediately below the character's foot position
|
|
|
+ if (auto* scene = character->GetScene())
|
|
|
+ {
|
|
|
+ // use a box shape for the overlap, even if the character geometry is a capsule, to avoid difficulties with
|
|
|
+ // the curved base of the capsule
|
|
|
+ AZ::Vector3 footBoxDimensions(0.0f, 0.0f, m_groundDetectionBoxHeight);
|
|
|
+ if (pxController->getType() == physx::PxControllerShapeType::eCAPSULE)
|
|
|
+ {
|
|
|
+ const float radius = static_cast<physx::PxCapsuleController*>(pxController)->getRadius();
|
|
|
+ footBoxDimensions = AZ::Vector3(2.0f * radius, 2.0f * radius, m_groundDetectionBoxHeight);
|
|
|
+ }
|
|
|
+ else if (pxController->getType() == physx::PxControllerShapeType::eBOX)
|
|
|
+ {
|
|
|
+ const auto* boxController = static_cast<physx::PxBoxController*>(pxController);
|
|
|
+ footBoxDimensions = AZ::Vector3(
|
|
|
+ 2.0f * boxController->getHalfSideExtent(), 2.0f * boxController->getHalfForwardExtent(), m_groundDetectionBoxHeight);
|
|
|
+ }
|
|
|
+
|
|
|
+ AZ::Transform footBoxTransform = AZ::Transform::CreateFromQuaternionAndTranslation(
|
|
|
+ AZ::Quaternion::CreateShortestArc(AZ::Vector3::CreateAxisZ(), character->GetUpDirection()), character->GetBasePosition());
|
|
|
+ AzPhysics::OverlapRequest overlapRequest =
|
|
|
+ AzPhysics::OverlapRequestHelpers::CreateBoxOverlapRequest(footBoxDimensions, footBoxTransform);
|
|
|
+ overlapRequest.m_collisionGroup = character->GetCollisionGroup();
|
|
|
+ overlapRequest.m_maxResults = 2;
|
|
|
+ AZ::EntityId entityId = GetEntityId();
|
|
|
+ AzPhysics::SceneQueryHits sceneQueryHits = scene->QueryScene(&overlapRequest);
|
|
|
+ m_cachedGroundState = AZStd::any_of(
|
|
|
+ sceneQueryHits.m_hits.begin(),
|
|
|
+ sceneQueryHits.m_hits.end(),
|
|
|
+ [&entityId](const AzPhysics::SceneQueryHit& hit)
|
|
|
+ {
|
|
|
+ return hit.m_entityId != entityId;
|
|
|
+ })
|
|
|
+ ? CharacterGroundState::Touching
|
|
|
+ : CharacterGroundState::NotTouching;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
float CharacterGameplayComponent::GetGravityMultiplier() const
|
|
@@ -122,6 +198,16 @@ namespace PhysX
|
|
|
m_gravityMultiplier = gravityMultiplier;
|
|
|
}
|
|
|
|
|
|
+ float CharacterGameplayComponent::GetGroundDetectionBoxHeight() const
|
|
|
+ {
|
|
|
+ return m_groundDetectionBoxHeight;
|
|
|
+ }
|
|
|
+
|
|
|
+ void CharacterGameplayComponent::SetGroundDetectionBoxHeight(float groundDetectionBoxHeight)
|
|
|
+ {
|
|
|
+ m_groundDetectionBoxHeight = AZ::GetMax(0.0f, groundDetectionBoxHeight);
|
|
|
+ }
|
|
|
+
|
|
|
AZ::Vector3 CharacterGameplayComponent::GetFallingVelocity() const
|
|
|
{
|
|
|
return m_fallingVelocity;
|
|
@@ -142,12 +228,21 @@ namespace PhysX
|
|
|
OnGravityChanged(newGravity);
|
|
|
});
|
|
|
|
|
|
- m_preSimulateHandler = AzPhysics::SystemEvents::OnPresimulateEvent::Handler(
|
|
|
- [this](float deltaTime)
|
|
|
+ m_sceneSimulationStartHandler = AzPhysics::SceneEvents::OnSceneSimulationStartHandler(
|
|
|
+ [this](
|
|
|
+ [[maybe_unused]] AzPhysics::SceneHandle sceneHandle,
|
|
|
+ float fixedDeltaTime)
|
|
|
{
|
|
|
- OnPreSimulate(deltaTime);
|
|
|
- }
|
|
|
- );
|
|
|
+ OnSceneSimulationStart(fixedDeltaTime);
|
|
|
+ }, aznumeric_cast<int32_t>(AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority::Animation));
|
|
|
+
|
|
|
+ m_sceneSimulationFinishHandler = AzPhysics::SceneEvents::OnSceneSimulationStartHandler(
|
|
|
+ [this](
|
|
|
+ [[maybe_unused]] AzPhysics::SceneHandle sceneHandle,
|
|
|
+ [[maybe_unused]] float fixedDeltaTime)
|
|
|
+ {
|
|
|
+ OnSceneSimulationFinish();
|
|
|
+ }, aznumeric_cast<int32_t>(AzPhysics::SceneEvents::PhysicsStartFinishSimulationPriority::Default));
|
|
|
}
|
|
|
|
|
|
void CharacterGameplayComponent::Activate()
|
|
@@ -160,14 +255,18 @@ namespace PhysX
|
|
|
{
|
|
|
m_gravity = sceneInterface->GetGravity(worldBody->m_sceneOwner);
|
|
|
sceneInterface->RegisterSceneGravityChangedEvent(worldBody->m_sceneOwner, m_onGravityChangedHandler);
|
|
|
+ AzPhysics::SceneHandle attachedSceneHandle = AzPhysics::InvalidSceneHandle;
|
|
|
+ Physics::DefaultWorldBus::BroadcastResult(attachedSceneHandle, &Physics::DefaultWorldRequests::GetDefaultSceneHandle);
|
|
|
+ if (attachedSceneHandle == AzPhysics::InvalidSceneHandle)
|
|
|
+ {
|
|
|
+ AZ_Error("PhysX Character Controller Component", false, "Failed to retrieve default scene.");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ sceneInterface->RegisterSceneSimulationStartHandler(attachedSceneHandle, m_sceneSimulationStartHandler);
|
|
|
+ sceneInterface->RegisterSceneSimulationFinishHandler(attachedSceneHandle, m_sceneSimulationFinishHandler);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (auto* physXSystem = GetPhysXSystem())
|
|
|
- {
|
|
|
- physXSystem->RegisterPreSimulateEvent(m_preSimulateHandler);
|
|
|
- }
|
|
|
-
|
|
|
CharacterGameplayRequestBus::Handler::BusConnect(GetEntityId());
|
|
|
}
|
|
|
|
|
@@ -175,13 +274,19 @@ namespace PhysX
|
|
|
{
|
|
|
CharacterGameplayRequestBus::Handler::BusDisconnect();
|
|
|
m_onGravityChangedHandler.Disconnect();
|
|
|
- m_preSimulateHandler.Disconnect();
|
|
|
+ m_sceneSimulationStartHandler.Disconnect();
|
|
|
+ m_sceneSimulationFinishHandler.Disconnect();
|
|
|
}
|
|
|
|
|
|
// Physics::SystemEvent
|
|
|
- void CharacterGameplayComponent::OnPreSimulate(float deltaTime)
|
|
|
+ void CharacterGameplayComponent::OnSceneSimulationStart(float physicsTimestep)
|
|
|
+ {
|
|
|
+ ApplyGravity(physicsTimestep);
|
|
|
+ }
|
|
|
+
|
|
|
+ void CharacterGameplayComponent::OnSceneSimulationFinish()
|
|
|
{
|
|
|
- ApplyGravity(deltaTime);
|
|
|
+ m_cachedGroundState = CharacterGroundState::NotYetDetermined;
|
|
|
}
|
|
|
|
|
|
void CharacterGameplayComponent::OnGravityChanged(const AZ::Vector3& gravity)
|
|
@@ -205,6 +310,6 @@ namespace PhysX
|
|
|
}
|
|
|
|
|
|
m_fallingVelocity += m_gravityMultiplier * m_gravity * deltaTime;
|
|
|
- Physics::CharacterRequestBus::Event(GetEntityId(), &Physics::CharacterRequests::AddVelocity, m_fallingVelocity);
|
|
|
+ Physics::CharacterRequestBus::Event(GetEntityId(), &Physics::CharacterRequests::AddVelocityForPhysicsTimestep, m_fallingVelocity);
|
|
|
}
|
|
|
} // namespace PhysX
|