SimulationManager.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "SimulationManager.h"
  9. #include "SimulationInterfaces/SimulationMangerRequestBus.h"
  10. #include <AzCore/Component/ComponentApplicationBus.h>
  11. #include <AzCore/Serialization/SerializeContext.h>
  12. #include <AzCore/Settings/SettingsRegistry.h>
  13. #include <AzFramework/Components/ConsoleBus.h>
  14. #include <AzFramework/Physics/PhysicsSystem.h>
  15. #include <DebugDraw/DebugDrawBus.h>
  16. #include <SimulationInterfaces/SimulationFeaturesAggregatorRequestBus.h>
  17. #include <SimulationInterfaces/SimulationInterfacesTypeIds.h>
  18. #include <simulation_interfaces/msg/simulator_features.hpp>
  19. namespace SimulationInterfaces
  20. {
  21. namespace
  22. {
  23. //! Convert string like : keyboard_key_alphanumeric_O to a pretty name like "Key 'O'"
  24. AZStd::string MakePrettyKeyboardName(const AzFramework::InputChannelId& inputChannelId)
  25. {
  26. // Convert the input channel name to a pretty format for display
  27. // split the name by underscores
  28. AZStd::vector<AZStd::string> parts;
  29. AZ::StringFunc::Tokenize(inputChannelId.GetName(), parts, "_");
  30. if (parts.size() < 4)
  31. {
  32. return inputChannelId.GetName(); // return original if no parts found
  33. }
  34. const AZStd::string& deviceType = parts[0];
  35. const AZStd::string& keyType = parts[1];
  36. const AZStd::string& keyRegion = parts[2];
  37. const AZStd::string& keyName = parts[3];
  38. if (deviceType != "keyboard" || keyType != "key")
  39. {
  40. return inputChannelId.GetName(); // return original if not a keyboard alphanumeric key
  41. }
  42. // Create a pretty name based on the key region and key name
  43. if (keyRegion == "alphanumeric" || keyRegion == "function")
  44. {
  45. // For alphanumeric keys, we can return the key name directly
  46. return AZStd::string::format("Key '%s'", keyName.c_str());
  47. }
  48. return AZStd::string::format("Key '%s_%s'", keyRegion.c_str(), keyName.c_str());
  49. }
  50. const AZStd::unordered_map<SimulationState, AZStd::string> SimulationStateToString = {
  51. { simulation_interfaces::msg::SimulationState::STATE_PAUSED, "STATE_PAUSED" },
  52. { simulation_interfaces::msg::SimulationState::STATE_PLAYING, "STATE_PLAYING" },
  53. { simulation_interfaces::msg::SimulationState::STATE_QUITTING, "STATE_QUITTING" },
  54. { simulation_interfaces::msg::SimulationState::STATE_STOPPED, "STATE_STOPPED" }
  55. };
  56. constexpr AZStd::string_view PrintStateName = "/SimulationInterfaces/PrintStateNameInGui";
  57. constexpr AZStd::string_view StartInStoppedStateKey = "/SimulationInterfaces/StartInStoppedState";
  58. constexpr AZStd::string_view KeyboardTransitionStoppedToPlaying = "/SimulationInterfaces/KeyboardTransitions/StoppedToPlaying";
  59. constexpr AZStd::string_view KeyboardTransitionPausedToPlaying = "/SimulationInterfaces/KeyboardTransitions/PausedToPlaying";
  60. constexpr AZStd::string_view KeyboardTransitionPlayingToPaused = "/SimulationInterfaces/KeyboardTransitions/PlayingToPaused";
  61. AZStd::string GetStateName(SimulationState state)
  62. {
  63. auto it = SimulationStateToString.find(state);
  64. if (it != SimulationStateToString.end())
  65. {
  66. return it->second;
  67. }
  68. return AZStd::string::format("Unknown state: %d", aznumeric_cast<int>(state));
  69. }
  70. bool StartInStoppedState()
  71. {
  72. AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get();
  73. AZ_Assert(settingsRegistry, "Settings Registry is not available");
  74. bool output = true;
  75. settingsRegistry->Get(output, StartInStoppedStateKey);
  76. return output;
  77. }
  78. bool PrintStateNameInGui()
  79. {
  80. AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get();
  81. AZ_Assert(settingsRegistry, "Settings Registry is not available");
  82. bool output = true;
  83. settingsRegistry->Get(output, PrintStateName);
  84. return output;
  85. }
  86. AZStd::optional<AzFramework::InputChannelId> GetKeyboardTransitionKey(const AZStd::string& registryKeyName)
  87. {
  88. AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get();
  89. AZ_Assert(settingsRegistry, "Settings Registry is not available");
  90. AZStd::string channelIdName;
  91. settingsRegistry->Get(channelIdName, registryKeyName);
  92. if (channelIdName.empty())
  93. {
  94. AZ_Error("SimulationManager", false, "Failed to get keyboard transition key from registry: %s", registryKeyName.c_str());
  95. return AZStd::nullopt;
  96. }
  97. AZ::Crc32 channelIdCrc32 = AZ::Crc32(channelIdName.c_str());
  98. for (const auto& inputChannel : AzFramework::InputDeviceKeyboard::Key::All)
  99. {
  100. if (inputChannel.GetNameCrc32() == channelIdCrc32)
  101. {
  102. return inputChannel;
  103. }
  104. }
  105. AZ_Error("SimulationManager", false, "Failed to find input channel with name: %s", channelIdName.c_str());
  106. return AZStd::nullopt;
  107. }
  108. } // namespace
  109. AZ_COMPONENT_IMPL(SimulationManager, "SimulationManager", SimulationManagerTypeId);
  110. void SimulationManager::Reflect(AZ::ReflectContext* context)
  111. {
  112. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  113. {
  114. serializeContext->Class<SimulationManager, AZ::Component>()->Version(0);
  115. }
  116. }
  117. void SimulationManager::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
  118. {
  119. provided.push_back(AZ_CRC_CE("SimulationManagerService"));
  120. }
  121. void SimulationManager::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
  122. {
  123. incompatible.push_back(AZ_CRC_CE("SimulationManagerService"));
  124. }
  125. void SimulationManager::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required)
  126. {
  127. required.push_back(AZ_CRC_CE("PhysicsService"));
  128. required.push_back(AZ_CRC_CE("SimulationFeaturesAggregator"));
  129. }
  130. void SimulationManager::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent)
  131. {
  132. dependent.push_back(AZ_CRC_CE("SimulationFeaturesAggregator"));
  133. dependent.push_back(AZ_CRC_CE("DebugDrawTextService"));
  134. }
  135. SimulationManager::SimulationManager()
  136. {
  137. if (SimulationManagerRequestBusInterface::Get() == nullptr)
  138. {
  139. SimulationManagerRequestBusInterface::Register(this);
  140. }
  141. InputChannelEventListener::BusConnect();
  142. }
  143. SimulationManager::~SimulationManager()
  144. {
  145. InputChannelEventListener::BusDisconnect();
  146. if (SimulationManagerRequestBusInterface::Get() == this)
  147. {
  148. SimulationManagerRequestBusInterface::Unregister(this);
  149. }
  150. }
  151. void SimulationManager::InitializeSimulationState()
  152. {
  153. // if start in stopped state, pause simulation. Default state for simulation by the standard is STOPPED and
  154. // SetSimulationState has logic to prevent transition to the same state.
  155. if (StartInStoppedState())
  156. {
  157. m_simulationState = simulation_interfaces::msg::SimulationState::STATE_STOPPED;
  158. SetSimulationPaused(true);
  159. }
  160. else
  161. {
  162. SetSimulationState(simulation_interfaces::msg::SimulationState::STATE_PLAYING);
  163. }
  164. }
  165. void SimulationManager::Activate()
  166. {
  167. AzFramework::LevelSystemLifecycleNotificationBus::Handler::BusDisconnect();
  168. SimulationManagerRequestBus::Handler::BusConnect();
  169. SimulationFeaturesAggregatorRequestBus::Broadcast(
  170. &SimulationFeaturesAggregatorRequests::AddSimulationFeatures,
  171. AZStd::unordered_set<SimulationFeatureType>{ simulation_interfaces::msg::SimulatorFeatures::SIMULATION_RESET,
  172. simulation_interfaces::msg::SimulatorFeatures::SIMULATION_RESET_TIME,
  173. simulation_interfaces::msg::SimulatorFeatures::SIMULATION_RESET_STATE,
  174. simulation_interfaces::msg::SimulatorFeatures::SIMULATION_RESET_SPAWNED,
  175. simulation_interfaces::msg::SimulatorFeatures::SIMULATION_STATE_PAUSE,
  176. simulation_interfaces::msg::SimulatorFeatures::STEP_SIMULATION_SINGLE,
  177. simulation_interfaces::msg::SimulatorFeatures::STEP_SIMULATION_MULTIPLE,
  178. simulation_interfaces::msg::SimulatorFeatures::STEP_SIMULATION_ACTION,
  179. simulation_interfaces::msg::SimulatorFeatures::SIMULATION_STATE_SETTING,
  180. simulation_interfaces::msg::SimulatorFeatures::SIMULATION_STATE_GETTING });
  181. if (PrintStateNameInGui())
  182. {
  183. AZ::TickBus::Handler::BusConnect();
  184. }
  185. AZ::SystemTickBus::QueueFunction(
  186. [this]()
  187. {
  188. InitializeSimulationState();
  189. });
  190. // Query registry for keyboard transition keys
  191. if (const auto stoppedToPlayingKey = GetKeyboardTransitionKey(KeyboardTransitionStoppedToPlaying))
  192. {
  193. RegisterTransitionsKey(
  194. *stoppedToPlayingKey,
  195. simulation_interfaces::msg::SimulationState::STATE_STOPPED,
  196. simulation_interfaces::msg::SimulationState::STATE_PLAYING);
  197. }
  198. if (const auto pausedToPlayingKey = GetKeyboardTransitionKey(KeyboardTransitionPausedToPlaying))
  199. {
  200. RegisterTransitionsKey(
  201. *pausedToPlayingKey,
  202. simulation_interfaces::msg::SimulationState::STATE_PAUSED,
  203. simulation_interfaces::msg::SimulationState::STATE_PLAYING);
  204. }
  205. if (const auto playingToPausedKey = GetKeyboardTransitionKey(KeyboardTransitionPlayingToPaused))
  206. {
  207. RegisterTransitionsKey(
  208. *playingToPausedKey,
  209. simulation_interfaces::msg::SimulationState::STATE_PLAYING,
  210. simulation_interfaces::msg::SimulationState::STATE_PAUSED);
  211. }
  212. }
  213. void SimulationManager::Deactivate()
  214. {
  215. AZ::TickBus::Handler::BusDisconnect();
  216. SimulationManagerRequestBus::Handler::BusDisconnect();
  217. }
  218. bool SimulationManager::IsSimulationPaused() const
  219. {
  220. return m_isSimulationPaused;
  221. }
  222. bool SimulationManager::IsSimulationStepsActive() const
  223. {
  224. return m_simulationFinishEvent.IsConnected();
  225. }
  226. void SimulationManager::CancelStepSimulation()
  227. {
  228. if (m_simulationFinishEvent.IsConnected())
  229. {
  230. m_simulationFinishEvent.Disconnect();
  231. SetSimulationPaused(true);
  232. m_numberOfPhysicsSteps = 0;
  233. }
  234. }
  235. void SimulationManager::SetSimulationPaused(bool paused)
  236. {
  237. // get az physics system
  238. auto* physicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get();
  239. AZ_Assert(physicsSystem, "Physics system is not available");
  240. const auto& sceneHandlers = physicsSystem->GetAllScenes();
  241. [[maybe_unused]] auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get();
  242. AZ_Assert(sceneInterface, "Physics scene interface is not available");
  243. for (auto& scene : sceneHandlers)
  244. {
  245. AZ_Assert(scene, "Physics scene is not available");
  246. scene->SetEnabled(!paused);
  247. m_isSimulationPaused = paused;
  248. }
  249. }
  250. void SimulationManager::StepSimulation(AZ::u64 steps)
  251. {
  252. if (steps == 0)
  253. {
  254. return;
  255. }
  256. m_numberOfPhysicsSteps = steps;
  257. // install handler
  258. m_simulationFinishEvent = AzPhysics::SceneEvents::OnSceneSimulationFinishHandler(
  259. [this](AzPhysics::SceneHandle sceneHandle, float)
  260. {
  261. m_numberOfPhysicsSteps--;
  262. AZ_Printf("SimulationManager", "Physics simulation step finished. Remaining steps: %d", m_numberOfPhysicsSteps);
  263. SimulationManagerNotificationsBus::Broadcast(
  264. &SimulationManagerNotifications::OnSimulationStepFinish, m_numberOfPhysicsSteps);
  265. if (m_numberOfPhysicsSteps <= 0)
  266. {
  267. SetSimulationPaused(true);
  268. // remove handler
  269. m_simulationFinishEvent.Disconnect();
  270. }
  271. });
  272. // get default scene
  273. [[maybe_unused]] auto* physicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get();
  274. AZ_Assert(physicsSystem, "Physics system is not available");
  275. auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get();
  276. AZ_Assert(sceneInterface, "Physics scene interface is not available");
  277. AzPhysics::SceneHandle defaultScene = sceneInterface->GetSceneHandle(AzPhysics::DefaultPhysicsSceneName);
  278. auto scene = sceneInterface->GetScene(defaultScene);
  279. AZ_Assert(scene, "Default physics scene is not available");
  280. // install handler
  281. scene->RegisterSceneSimulationFinishHandler(m_simulationFinishEvent);
  282. SetSimulationPaused(false);
  283. }
  284. void SimulationManager::ReloadLevel(SimulationManagerRequests::ReloadLevelCallback completionCallback)
  285. {
  286. AzFramework::LevelSystemLifecycleNotificationBus::Handler::BusConnect();
  287. m_reloadLevelCallback = completionCallback;
  288. // We need to delete all entities before reloading the level
  289. DeletionCompletedCb deleteAllCompletion = [](const AZ::Outcome<void, FailedResult>& result)
  290. {
  291. AZ_Trace("SimulationManager", "Delete all entities completed: %s, reload level", result.IsSuccess() ? "true" : "false");
  292. const char* levelName = AZ::Interface<AzFramework::ILevelSystemLifecycle>::Get()->GetCurrentLevelName();
  293. AzFramework::ConsoleRequestBus::Broadcast(&AzFramework::ConsoleRequests::ExecuteConsoleCommand, "UnloadLevel");
  294. AZStd::string command = AZStd::string::format("LoadLevel %s", levelName);
  295. AzFramework::ConsoleRequestBus::Broadcast(&AzFramework::ConsoleRequests::ExecuteConsoleCommand, command.c_str());
  296. };
  297. // delete spawned entities
  298. SimulationEntityManagerRequestBus::Broadcast(&SimulationEntityManagerRequests::DeleteAllEntities, deleteAllCompletion);
  299. }
  300. void SimulationManager::OnLoadingComplete(const char* levelName)
  301. {
  302. AZ_Printf("SimulationManager", "Level loading started: %s", levelName);
  303. if (m_reloadLevelCallback)
  304. {
  305. m_reloadLevelCallback();
  306. m_reloadLevelCallback = nullptr;
  307. }
  308. // reset of the simulation, assign the same state as at the beginning
  309. InitializeSimulationState();
  310. AzFramework::LevelSystemLifecycleNotificationBus::Handler::BusDisconnect();
  311. }
  312. SimulationState SimulationManager::GetSimulationState() const
  313. {
  314. return m_simulationState;
  315. }
  316. AZ::Outcome<void, FailedResult> SimulationManager::SetSimulationState(SimulationState stateToSet)
  317. {
  318. // check if simulation is in desire state
  319. if (m_simulationState == stateToSet)
  320. {
  321. return AZ::Failure(FailedResult(
  322. simulation_interfaces::srv::SetSimulationState::Response::ALREADY_IN_TARGET_STATE,
  323. "Simulation is already in requested state, transition unecessary"));
  324. }
  325. if (IsTransitionForbiddenInEditor(stateToSet))
  326. {
  327. const auto stateToSetName = GetStateName(stateToSet);
  328. const auto currentStateName = GetStateName(m_simulationState);
  329. return AZ::Failure(FailedResult(
  330. simulation_interfaces::srv::SetSimulationState::Response::INCORRECT_TRANSITION,
  331. AZStd::string::format(
  332. "Requested transition (%s -> %s) is forbidden in the Editor. It is available in GameLauncher.",
  333. currentStateName.c_str(),
  334. stateToSetName.c_str())));
  335. }
  336. if (IsTransitionForbidden(stateToSet))
  337. {
  338. const auto stateToSetName = GetStateName(stateToSet);
  339. const auto currentStateName = GetStateName(m_simulationState);
  340. return AZ::Failure(FailedResult(
  341. simulation_interfaces::srv::SetSimulationState::Response::INCORRECT_TRANSITION,
  342. AZStd::string::format("Requested transition (%s -> %s) is forbidden", currentStateName.c_str(), stateToSetName.c_str())));
  343. }
  344. switch (stateToSet)
  345. {
  346. case simulation_interfaces::msg::SimulationState::STATE_STOPPED:
  347. {
  348. SimulationManagerRequests::ReloadLevelCallback cb = []()
  349. {
  350. SimulationInterfaces::SimulationManagerRequestBus::Broadcast(
  351. &SimulationInterfaces::SimulationManagerRequests::SetSimulationPaused, true);
  352. };
  353. ReloadLevel(cb);
  354. break;
  355. }
  356. case simulation_interfaces::msg::SimulationState::STATE_PLAYING:
  357. {
  358. SetSimulationPaused(false);
  359. break;
  360. }
  361. case simulation_interfaces::msg::SimulationState::STATE_PAUSED:
  362. {
  363. SetSimulationPaused(true);
  364. break;
  365. }
  366. case simulation_interfaces::msg::SimulationState::STATE_QUITTING:
  367. {
  368. // stop simulation -> kill the simulator.
  369. SetSimulationPaused(true);
  370. // queue to allow status of this method to be returned, then start quitting
  371. AZ::SystemTickBus::QueueFunction(
  372. []()
  373. {
  374. AzFramework::ConsoleRequestBus::Broadcast(&AzFramework::ConsoleRequests::ExecuteConsoleCommand, "quit");
  375. });
  376. break;
  377. }
  378. default:
  379. {
  380. return AZ::Failure(FailedResult(
  381. simulation_interfaces::srv::SetSimulationState::Response::INCORRECT_TRANSITION, "Requested state doesn't exists"));
  382. break;
  383. }
  384. }
  385. m_simulationState = stateToSet;
  386. return AZ::Success();
  387. }
  388. bool SimulationManager::IsTransitionForbiddenInEditor(SimulationState requestedState)
  389. {
  390. // in the Editor we cannot reload level, so going to STOPPED state is forbidden, we cannot quit the editor so going to QUITTING
  391. // state is forbidden
  392. AZ::ApplicationTypeQuery appType;
  393. AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationBus::Events::QueryApplicationType, appType);
  394. if (appType.IsValid() && !appType.IsGame())
  395. {
  396. if (requestedState == simulation_interfaces::msg::SimulationState::STATE_STOPPED ||
  397. requestedState == simulation_interfaces::msg::SimulationState::STATE_QUITTING)
  398. {
  399. return true;
  400. }
  401. }
  402. return false;
  403. }
  404. bool SimulationManager::IsTransitionForbidden(SimulationState requestedState)
  405. {
  406. AZStd::pair<SimulationState, SimulationState> desireTransition{ m_simulationState, requestedState };
  407. auto it = AZStd::find(m_forbiddenStatesTransitions.begin(), m_forbiddenStatesTransitions.end(), desireTransition);
  408. return it != m_forbiddenStatesTransitions.end();
  409. }
  410. void SimulationManager::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
  411. {
  412. // get if we have available keyboard transition
  413. AZStd::string keyboardHint;
  414. const auto maybeKeyboardTransition = m_keyboardTransitions.find(m_simulationState);
  415. if (maybeKeyboardTransition != m_keyboardTransitions.end())
  416. {
  417. keyboardHint = maybeKeyboardTransition->second.m_uiDescription;
  418. }
  419. DebugDraw::DebugDrawRequestBus::Broadcast(
  420. &DebugDraw::DebugDrawRequests::DrawTextOnScreen,
  421. AZStd::string::format("Simulation state: %s %s", GetStateName(m_simulationState).c_str(), keyboardHint.c_str()),
  422. AZ::Color(1.0f, 1.0f, 1.0f, 1.0f),
  423. 0.f);
  424. }
  425. void SimulationManager::RegisterTransitionsKey(
  426. const AzFramework::InputChannelId& key, SimulationState sourceState, SimulationState desiredState)
  427. {
  428. const auto uiKeyName = MakePrettyKeyboardName(key);
  429. const auto uiHint =
  430. AZStd::string::format("Press %s to change simulation state to %s", uiKeyName.c_str(), GetStateName(desiredState).c_str());
  431. m_keyboardTransitions[sourceState] = { key, desiredState, uiHint };
  432. }
  433. bool SimulationManager::OnInputChannelEventFiltered(const AzFramework::InputChannel& inputChannel)
  434. {
  435. const AzFramework::InputDeviceId& deviceId = inputChannel.GetInputDevice().GetInputDeviceId();
  436. if (AzFramework::InputDeviceKeyboard::IsKeyboardDevice(deviceId) && inputChannel.IsStateBegan())
  437. {
  438. const auto maybeKeyboardTransition = m_keyboardTransitions.find(m_simulationState);
  439. if (maybeKeyboardTransition == m_keyboardTransitions.end())
  440. {
  441. return false;
  442. }
  443. if (maybeKeyboardTransition->second.m_inputChannelId == inputChannel.GetInputChannelId())
  444. {
  445. // if we have transition, set the state
  446. auto result = SetSimulationState(maybeKeyboardTransition->second.m_desiredState);
  447. AZ_Error(
  448. "SimulationManager",
  449. result.IsSuccess(),
  450. "Failed to change simulation state: %d %s",
  451. result.GetError().m_errorCode,
  452. result.GetError().m_errorString.c_str());
  453. return true;
  454. }
  455. }
  456. return false;
  457. }
  458. } // namespace SimulationInterfaces