2
0
Эх сурвалжийг харах

Disable certain state transitions in Editor (#901)

* Disable certain state transitions in Editor
* Add optional debug draw for StateName.
* Add registry settings to Registry directory.

Signed-off-by: Michał Pełka <[email protected]>
Co-authored-by: Jan Hanca <[email protected]>
Michał Pełka 2 сар өмнө
parent
commit
8cf97e31a3

+ 1 - 0
Gems/SimulationInterfaces/Code/CMakeLists.txt

@@ -57,6 +57,7 @@ ly_add_target(
             AZ::AzFramework
         PRIVATE
             Gem::ROS2.Static
+            Gem::DebugDraw.API
 )
 target_depends_on_ros2_packages(${gem_name}.Private.Object rclcpp std_msgs geometry_msgs simulation_interfaces rclcpp_action)
 

+ 75 - 1
Gems/SimulationInterfaces/Code/Source/Clients/SimulationManager.cpp

@@ -14,6 +14,7 @@
 #include <AzCore/Settings/SettingsRegistry.h>
 #include <AzFramework/Components/ConsoleBus.h>
 #include <AzFramework/Physics/PhysicsSystem.h>
+#include <DebugDraw/DebugDrawBus.h>
 #include <SimulationInterfaces/SimulationFeaturesAggregatorRequestBus.h>
 #include <SimulationInterfaces/SimulationInterfacesTypeIds.h>
 #include <simulation_interfaces/msg/simulator_features.hpp>
@@ -22,8 +23,27 @@ namespace SimulationInterfaces
 {
     namespace
     {
+
+        const AZStd::unordered_map<SimulationState, AZStd::string> SimulationStateToString = {
+            { simulation_interfaces::msg::SimulationState::STATE_PAUSED, "STATE_PAUSED" },
+            { simulation_interfaces::msg::SimulationState::STATE_PLAYING, "STATE_PLAYING" },
+            { simulation_interfaces::msg::SimulationState::STATE_QUITTING, "STATE_QUITTING" },
+            { simulation_interfaces::msg::SimulationState::STATE_STOPPED, "STATE_STOPPED" }
+        };
+
+        constexpr AZStd::string_view PrintStateName = "/SimulationInterfaces/PrintStateNameInGui";
         constexpr AZStd::string_view StartInStoppedStateKey = "/SimulationInterfaces/StartInStoppedState";
 
+        AZStd::string GetStateName(SimulationState state)
+        {
+            auto it = SimulationStateToString.find(state);
+            if (it != SimulationStateToString.end())
+            {
+                return it->second;
+            }
+            return AZStd::string::format("Unknown state: %d", aznumeric_cast<int>(state));
+        }
+
         bool StartInStoppedState()
         {
             AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get();
@@ -32,6 +52,15 @@ namespace SimulationInterfaces
             settingsRegistry->Get(output, StartInStoppedStateKey);
             return output;
         }
+
+        bool PrintStateNameInGui()
+        {
+            AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get();
+            AZ_Assert(settingsRegistry, "Settings Registry is not available");
+            bool output = true;
+            settingsRegistry->Get(output, PrintStateName);
+            return output;
+        }
     } // namespace
 
     AZ_COMPONENT_IMPL(SimulationManager, "SimulationManager", SimulationManagerTypeId);
@@ -63,6 +92,7 @@ namespace SimulationInterfaces
     void SimulationManager::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent)
     {
         dependent.push_back(AZ_CRC_CE("SimulationFeaturesAggregator"));
+        dependent.push_back(AZ_CRC_CE("DebugDrawTextService"));
     }
 
     SimulationManager::SimulationManager()
@@ -112,6 +142,10 @@ namespace SimulationInterfaces
                                                          simulation_interfaces::msg::SimulatorFeatures::STEP_SIMULATION_ACTION,
                                                          simulation_interfaces::msg::SimulatorFeatures::SIMULATION_STATE_SETTING,
                                                          simulation_interfaces::msg::SimulatorFeatures::SIMULATION_STATE_GETTING });
+        if (PrintStateNameInGui())
+        {
+            AZ::TickBus::Handler::BusConnect();
+        }
         AZ::SystemTickBus::QueueFunction(
             [this]()
             {
@@ -121,6 +155,7 @@ namespace SimulationInterfaces
 
     void SimulationManager::Deactivate()
     {
+        AZ::TickBus::Handler::BusDisconnect();
         SimulationManagerRequestBus::Handler::BusDisconnect();
     }
 
@@ -246,11 +281,24 @@ namespace SimulationInterfaces
                 "Simulation is already in requested state, transition unecessary"));
         }
 
+        if (IsTransitionForbiddenInEditor(stateToSet))
+        {
+            const auto stateToSetName = GetStateName(stateToSet);
+            const auto currentStateName = GetStateName(m_simulationState);
+            return AZ::Failure(FailedResult(
+                simulation_interfaces::srv::SetSimulationState::Response::INCORRECT_TRANSITION,
+                AZStd::string::format(
+                    "Requested transition (%s -> %s) is forbidden in the Editor. It is available in GameLauncher.",
+                    currentStateName.c_str(),
+                    stateToSetName.c_str())));
+        }
         if (IsTransitionForbidden(stateToSet))
         {
+            const auto stateToSetName = GetStateName(stateToSet);
+            const auto currentStateName = GetStateName(m_simulationState);
             return AZ::Failure(FailedResult(
                 simulation_interfaces::srv::SetSimulationState::Response::INCORRECT_TRANSITION,
-                AZStd::string::format("Requested transition (%d -> %d) is forbidden", m_simulationState, stateToSet)));
+                AZStd::string::format("Requested transition (%s -> %s) is forbidden", currentStateName.c_str(), stateToSetName.c_str())));
         }
 
         switch (stateToSet)
@@ -299,6 +347,23 @@ namespace SimulationInterfaces
         return AZ::Success();
     }
 
+    bool SimulationManager::IsTransitionForbiddenInEditor(SimulationState requestedState)
+    {
+        // in the Editor we cannot reload level, so going to STOPPED state is forbidden, we cannot quit the editor so going to QUITTING
+        // state is forbidden
+        AZ::ApplicationTypeQuery appType;
+        AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationBus::Events::QueryApplicationType, appType);
+        if (appType.IsValid() && !appType.IsGame())
+        {
+            if (requestedState == simulation_interfaces::msg::SimulationState::STATE_STOPPED ||
+                requestedState == simulation_interfaces::msg::SimulationState::STATE_QUITTING)
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
     bool SimulationManager::IsTransitionForbidden(SimulationState requestedState)
     {
         AZStd::pair<SimulationState, SimulationState> desireTransition{ m_simulationState, requestedState };
@@ -306,4 +371,13 @@ namespace SimulationInterfaces
         return it != m_forbiddenStatesTransitions.end();
     }
 
+    void SimulationManager::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
+    {
+        DebugDraw::DebugDrawRequestBus::Broadcast(
+            &DebugDraw::DebugDrawRequests::DrawTextOnScreen,
+            AZStd::string::format("Simulation state: %s", GetStateName(m_simulationState).c_str()),
+            AZ::Color(1.0f, 1.0f, 1.0f, 1.0f),
+            0.f);
+    }
+
 } // namespace SimulationInterfaces

+ 7 - 0
Gems/SimulationInterfaces/Code/Source/Clients/SimulationManager.h

@@ -27,6 +27,7 @@ namespace SimulationInterfaces
         : public AZ::Component
         , protected SimulationManagerRequestBus::Handler
         , protected AzFramework::LevelSystemLifecycleNotificationBus::Handler
+        , protected AZ::TickBus::Handler
     {
     public:
         AZ_COMPONENT_DECL(SimulationManager);
@@ -59,7 +60,11 @@ namespace SimulationInterfaces
         // LevelSystemLifecycleNotificationBus interface implementation
         void OnLoadingComplete(const char* levelName) override;
 
+        // AZ::TickBus::Handler
+        void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
+
         bool m_isSimulationPaused = false;
+
         uint64_t m_numberOfPhysicsSteps = 0;
         AzPhysics::SceneEvents::OnSceneSimulationFinishHandler m_simulationFinishEvent;
         SimulationManagerRequests::ReloadLevelCallback m_reloadLevelCallback;
@@ -68,6 +73,8 @@ namespace SimulationInterfaces
         }; // default simulation state based on standard
         void InitializeSimulationState();
 
+        bool IsTransitionForbiddenInEditor(SimulationState requestedState);
+
         bool IsTransitionForbidden(SimulationState requestedState);
         // forbidden transition between state, first is current state, second is desire state
         const AZStd::array<AZStd::pair<SimulationState, SimulationState>, 4> m_forbiddenStatesTransitions{ {

+ 7 - 1
Gems/SimulationInterfaces/Code/Source/Tools/SimulationManagerEditor.cpp

@@ -57,14 +57,20 @@ namespace SimulationInterfaces
 
     void SimulationManagerEditor::Activate()
     {
-        BaseSystemComponent::Activate();
         AzToolsFramework::EditorEvents::Bus::Handler::BusConnect();
+        AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusConnect();
     }
 
     void SimulationManagerEditor::Deactivate()
     {
+        AzToolsFramework::EditorEntityContextNotificationBus::Handler::BusDisconnect();
         AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect();
         BaseSystemComponent::Deactivate();
     }
 
+    void SimulationManagerEditor::OnStartPlayInEditorBegin()
+    {
+        BaseSystemComponent::Activate();
+    }
+
 } // namespace SimulationInterfaces

+ 5 - 0
Gems/SimulationInterfaces/Code/Source/Tools/SimulationManagerEditor.h

@@ -9,6 +9,7 @@
 #pragma once
 
 #include <AzToolsFramework/API/ToolsApplicationAPI.h>
+#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
 
 #include <Clients/SimulationManager.h>
 
@@ -18,6 +19,7 @@ namespace SimulationInterfaces
     class SimulationManagerEditor
         : public SimulationManager
         , protected AzToolsFramework::EditorEvents::Bus::Handler
+        , private AzToolsFramework::EditorEntityContextNotificationBus::Handler
     {
         using BaseSystemComponent = SimulationManager;
 
@@ -39,5 +41,8 @@ namespace SimulationInterfaces
         void Init() override;
         void Activate() override;
         void Deactivate() override;
+
+        // EditorEntityContextNotificationBus
+        void OnStartPlayInEditorBegin() override;
     };
 } // namespace SimulationInterfaces

+ 2 - 1
Gems/SimulationInterfaces/Code/Tests/Tools/InterfacesTest.cpp

@@ -116,7 +116,8 @@ namespace UnitTest
             }
         }
 
-        template <typename Future>void SpinAppUntilFuture(Future &future)
+        template<typename Future>
+        void SpinAppUntilFuture(Future& future)
         {
             AZ::ComponentApplication* app = nullptr;
             AZ::ComponentApplicationBus::BroadcastResult(app, &AZ::ComponentApplicationBus::Events::GetApplication);

+ 6 - 0
Gems/SimulationInterfaces/Registry/simulationinterface_settings.setreg

@@ -0,0 +1,6 @@
+{
+    "SimulationInterfaces": {
+        "PrintStateNameInGui": true,
+        "StartInStoppedState": true
+    }
+}

+ 2 - 1
Gems/SimulationInterfaces/gem.json

@@ -23,7 +23,8 @@
     "requirements": "Requires ROS 2 Gem",
     "documentation_url": "",
     "dependencies": [
-        "ROS2>=3.3.0"
+        "ROS2>=3.3.0",
+        "DebugDraw"
     ],
     "repo_uri": "",
     "compatible_engines": [],