diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.cpp b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.cpp index 473e9534cb..6fa69c7a24 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.cpp +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.cpp @@ -73,7 +73,10 @@ namespace PhysX { } - CharacterControllerComponent::~CharacterControllerComponent() = default; + CharacterControllerComponent::~CharacterControllerComponent() + { + DisableController(); + } // AZ::Component void CharacterControllerComponent::Init() @@ -92,7 +95,7 @@ namespace PhysX void CharacterControllerComponent::Deactivate() { - DestroyController(); + DisableController(); Physics::CollisionFilteringRequestBus::Handler::BusDisconnect(); AzPhysics::SimulatedBodyComponentRequestsBus::Handler::BusDisconnect(); @@ -198,7 +201,7 @@ namespace PhysX void CharacterControllerComponent::DisablePhysics() { - DestroyController(); + DisableController(); } bool CharacterControllerComponent::IsPhysicsEnabled() const @@ -421,17 +424,32 @@ namespace PhysX AZ::TransformBus::EventResult(entityTranslation, GetEntityId(), &AZ::TransformBus::Events::GetWorldTranslation); m_characterConfig->m_position = entityTranslation; - if (auto* sceneInterface = AZ::Interface::Get()) + auto* sceneInterface = AZ::Interface::Get(); + if (sceneInterface != nullptr) { - AzPhysics::SimulatedBodyHandle bodyHandle = sceneInterface->AddSimulatedBody(defaultSceneHandle, m_characterConfig.get()); - m_controller = azdynamic_cast(sceneInterface->GetSimulatedBodyFromHandle(defaultSceneHandle, bodyHandle)); + m_controllerBodyHandle = sceneInterface->AddSimulatedBody(defaultSceneHandle, m_characterConfig.get()); + m_controller = azdynamic_cast( + sceneInterface->GetSimulatedBodyFromHandle(defaultSceneHandle, m_controllerBodyHandle)); } if (m_controller == nullptr) { AZ_Error("PhysX Character Controller Component", false, "Failed to create character controller."); return; } - + + if (sceneInterface != nullptr) + { + // if the scene removes this controller body, we should also clean up our resources. + m_onSimulatedBodyRemovedHandler = AzPhysics::SceneEvents::OnSimulationBodyRemoved::Handler( + [this]([[maybe_unused]] AzPhysics::SceneHandle sceneHandle, AzPhysics::SimulatedBodyHandle bodyHandle) { + if (bodyHandle == m_controllerBodyHandle) + { + DestroyController(); + } + }); + sceneInterface->RegisterSimulationBodyRemovedHandler(defaultSceneHandle, m_onSimulatedBodyRemovedHandler); + } + CharacterControllerRequestBus::Handler::BusConnect(GetEntityId()); m_preSimulateHandler = AzPhysics::SystemEvents::OnPresimulateEvent::Handler( @@ -447,7 +465,7 @@ namespace PhysX } } - void CharacterControllerComponent::DestroyController() + void CharacterControllerComponent::DisableController() { if (!IsPhysicsEnabled()) { @@ -460,10 +478,15 @@ namespace PhysX { sceneInterface->RemoveSimulatedBody(m_controller->m_sceneOwner, m_controller->m_bodyHandle); } + + DestroyController(); + } + + void CharacterControllerComponent::DestroyController() + { m_controller = nullptr; - m_preSimulateHandler.Disconnect(); - + m_onSimulatedBodyRemovedHandler.Disconnect(); CharacterControllerRequestBus::Handler::BusDisconnect(); } } // namespace PhysX diff --git a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.h b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.h index a7a1a92ad2..7c25312b72 100644 --- a/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.h +++ b/Gems/PhysX/Code/Source/PhysXCharacters/Components/CharacterControllerComponent.h @@ -131,7 +131,12 @@ namespace PhysX void ToggleCollisionLayer(const AZStd::string& layerName, AZ::Crc32 colliderTag, bool enabled) override; private: + // Creates the physics character controller in the current default physics scene. + // This will do nothing if the controller is already created. void CreateController(); + // Removes the physics character controller from the scene and will call DestroyController for clean up. + void DisableController(); + // Cleans up all references and events used with the physics character controller. void DestroyController(); void OnPreSimulate(float deltaTime); @@ -139,6 +144,8 @@ namespace PhysX AZStd::unique_ptr m_characterConfig; AZStd::shared_ptr m_shapeConfig; PhysX::CharacterController* m_controller = nullptr; + AzPhysics::SimulatedBodyHandle m_controllerBodyHandle = AzPhysics::InvalidSimulatedBodyHandle; AzPhysics::SystemEvents::OnPresimulateEvent::Handler m_preSimulateHandler; + AzPhysics::SceneEvents::OnSimulationBodyRemoved::Handler m_onSimulatedBodyRemovedHandler; }; } // namespace PhysX diff --git a/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp b/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp index 79aa767959..689ea47be7 100644 --- a/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp +++ b/Gems/PhysX/Code/Source/Scene/PhysXScene.cpp @@ -489,6 +489,7 @@ namespace PhysX // Disable simulation on body (not signaling OnSimulationBodySimulationDisabled event) DisableSimulationOfBodyInternal(*simulatedBody.second); } + m_simulatedBodyRemovedEvent.Signal(m_sceneHandle, simulatedBody.second->m_bodyHandle); delete simulatedBody.second; } }