SimulationManager.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618
  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 <AzCore/Component/ComponentApplicationBus.h>
  10. #include <AzCore/Component/TickBus.h>
  11. #include <AzCore/Outcome/Outcome.h>
  12. #include <AzCore/Serialization/SerializeContext.h>
  13. #include <AzCore/Settings/SettingsRegistry.h>
  14. #include <AzFramework/Components/ConsoleBus.h>
  15. #include <AzFramework/Physics/PhysicsSystem.h>
  16. #include <DebugDraw/DebugDrawBus.h>
  17. #include <SimulationInterfaces/LevelManagerRequestBus.h>
  18. #include <SimulationInterfaces/SimulationFeaturesAggregatorRequestBus.h>
  19. #include <SimulationInterfaces/SimulationInterfacesTypeIds.h>
  20. #include <SimulationInterfaces/SimulationMangerRequestBus.h>
  21. #include <SimulationInterfaces/WorldResource.h>
  22. #include <simulation_interfaces/msg/result.hpp>
  23. #include <simulation_interfaces/msg/simulation_state.hpp>
  24. #include <simulation_interfaces/msg/simulator_features.hpp>
  25. #include <simulation_interfaces/srv/get_current_world.hpp>
  26. namespace SimulationInterfaces
  27. {
  28. namespace
  29. {
  30. //! Convert string like : keyboard_key_alphanumeric_O to a pretty name like "Key 'O'"
  31. AZStd::string MakePrettyKeyboardName(const AzFramework::InputChannelId& inputChannelId)
  32. {
  33. // Convert the input channel name to a pretty format for display
  34. // split the name by underscores
  35. AZStd::vector<AZStd::string> parts;
  36. AZ::StringFunc::Tokenize(inputChannelId.GetName(), parts, "_");
  37. if (parts.size() < 4)
  38. {
  39. return inputChannelId.GetName(); // return original if no parts found
  40. }
  41. const AZStd::string& deviceType = parts[0];
  42. const AZStd::string& keyType = parts[1];
  43. const AZStd::string& keyRegion = parts[2];
  44. const AZStd::string& keyName = parts[3];
  45. if (deviceType != "keyboard" || keyType != "key")
  46. {
  47. return inputChannelId.GetName(); // return original if not a keyboard alphanumeric key
  48. }
  49. // Create a pretty name based on the key region and key name
  50. if (keyRegion == "alphanumeric" || keyRegion == "function")
  51. {
  52. // For alphanumeric keys, we can return the key name directly
  53. return AZStd::string::format("Key '%s'", keyName.c_str());
  54. }
  55. return AZStd::string::format("Key '%s_%s'", keyRegion.c_str(), keyName.c_str());
  56. }
  57. const AZStd::unordered_map<SimulationState, AZStd::string> SimulationStateToString = {
  58. { simulation_interfaces::msg::SimulationState::STATE_PAUSED, "STATE_PAUSED" },
  59. { simulation_interfaces::msg::SimulationState::STATE_PLAYING, "STATE_PLAYING" },
  60. { simulation_interfaces::msg::SimulationState::STATE_QUITTING, "STATE_QUITTING" },
  61. { simulation_interfaces::msg::SimulationState::STATE_STOPPED, "STATE_STOPPED" },
  62. { simulation_interfaces::msg::SimulationState::STATE_NO_WORLD, "STATE_NO_WORLD" },
  63. { simulation_interfaces::msg::SimulationState::STATE_LOADING_WORLD, "STATE_LOADING_WORLD" }
  64. };
  65. constexpr AZStd::string_view PrintStateName = "/SimulationInterfaces/PrintStateNameInGui";
  66. constexpr AZStd::string_view StartInStoppedStateKey = "/SimulationInterfaces/StartInStoppedState";
  67. constexpr AZStd::string_view KeyboardTransitionStoppedToPlaying = "/SimulationInterfaces/KeyboardTransitions/StoppedToPlaying";
  68. constexpr AZStd::string_view KeyboardTransitionPausedToPlaying = "/SimulationInterfaces/KeyboardTransitions/PausedToPlaying";
  69. constexpr AZStd::string_view KeyboardTransitionPlayingToPaused = "/SimulationInterfaces/KeyboardTransitions/PlayingToPaused";
  70. AZStd::string GetStateName(SimulationState state)
  71. {
  72. auto it = SimulationStateToString.find(state);
  73. if (it != SimulationStateToString.end())
  74. {
  75. return it->second;
  76. }
  77. return AZStd::string::format("Unknown state: %d", aznumeric_cast<int>(state));
  78. }
  79. bool StartInStoppedState()
  80. {
  81. AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get();
  82. AZ_Assert(settingsRegistry, "Settings Registry is not available");
  83. bool output = true;
  84. settingsRegistry->Get(output, StartInStoppedStateKey);
  85. return output;
  86. }
  87. bool PrintStateNameInGui()
  88. {
  89. AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get();
  90. AZ_Assert(settingsRegistry, "Settings Registry is not available");
  91. bool output = true;
  92. settingsRegistry->Get(output, PrintStateName);
  93. return output;
  94. }
  95. AZStd::optional<AzFramework::InputChannelId> GetKeyboardTransitionKey(const AZStd::string& registryKeyName)
  96. {
  97. AZ::SettingsRegistryInterface* settingsRegistry = AZ::SettingsRegistry::Get();
  98. AZ_Assert(settingsRegistry, "Settings Registry is not available");
  99. AZStd::string channelIdName;
  100. settingsRegistry->Get(channelIdName, registryKeyName);
  101. if (channelIdName.empty())
  102. {
  103. AZ_Error("SimulationManager", false, "Failed to get keyboard transition key from registry: %s", registryKeyName.c_str());
  104. return AZStd::nullopt;
  105. }
  106. AZ::Crc32 channelIdCrc32 = AZ::Crc32(channelIdName.c_str());
  107. for (const auto& inputChannel : AzFramework::InputDeviceKeyboard::Key::All)
  108. {
  109. if (inputChannel.GetNameCrc32() == channelIdCrc32)
  110. {
  111. return inputChannel;
  112. }
  113. }
  114. AZ_Error("SimulationManager", false, "Failed to find input channel with name: %s", channelIdName.c_str());
  115. return AZStd::nullopt;
  116. }
  117. } // namespace
  118. AZ_COMPONENT_IMPL(SimulationManager, "SimulationManager", SimulationManagerTypeId);
  119. void SimulationManager::Reflect(AZ::ReflectContext* context)
  120. {
  121. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  122. {
  123. serializeContext->Class<SimulationManager, AZ::Component>()->Version(0);
  124. }
  125. }
  126. void SimulationManager::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
  127. {
  128. provided.push_back(AZ_CRC_CE("SimulationManagerService"));
  129. }
  130. void SimulationManager::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
  131. {
  132. incompatible.push_back(AZ_CRC_CE("SimulationManagerService"));
  133. }
  134. void SimulationManager::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required)
  135. {
  136. required.push_back(AZ_CRC_CE("PhysicsService"));
  137. required.push_back(AZ_CRC_CE("SimulationFeaturesAggregator"));
  138. required.push_back(AZ_CRC_CE("LevelManagerService"));
  139. }
  140. void SimulationManager::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent)
  141. {
  142. dependent.push_back(AZ_CRC_CE("SimulationFeaturesAggregator"));
  143. dependent.push_back(AZ_CRC_CE("LevelManagerService"));
  144. dependent.push_back(AZ_CRC_CE("DebugDrawTextService"));
  145. }
  146. SimulationManager::SimulationManager()
  147. {
  148. if (SimulationManagerRequestBusInterface::Get() == nullptr)
  149. {
  150. SimulationManagerRequestBusInterface::Register(this);
  151. }
  152. }
  153. SimulationManager::~SimulationManager()
  154. {
  155. if (SimulationManagerRequestBusInterface::Get() == this)
  156. {
  157. SimulationManagerRequestBusInterface::Unregister(this);
  158. }
  159. }
  160. void SimulationManager::InitializeSimulationState()
  161. {
  162. // if start in stopped state, pause simulation. Default state for simulation by the standard is STOPPED and
  163. // SetSimulationState has logic to prevent transition to the same state.
  164. if (StartInStoppedState())
  165. {
  166. m_simulationState = simulation_interfaces::msg::SimulationState::STATE_STOPPED;
  167. SetSimulationPaused(true);
  168. }
  169. else
  170. {
  171. SetSimulationState(simulation_interfaces::msg::SimulationState::STATE_PLAYING);
  172. }
  173. }
  174. void SimulationManager::Activate()
  175. {
  176. SimulationManagerRequestBus::Handler::BusConnect();
  177. SimulationFeaturesAggregatorRequestBus::Broadcast(
  178. &SimulationFeaturesAggregatorRequests::AddSimulationFeatures,
  179. AZStd::unordered_set<SimulationFeatureType>{ simulation_interfaces::msg::SimulatorFeatures::SIMULATION_RESET,
  180. simulation_interfaces::msg::SimulatorFeatures::SIMULATION_RESET_TIME,
  181. simulation_interfaces::msg::SimulatorFeatures::SIMULATION_RESET_STATE,
  182. simulation_interfaces::msg::SimulatorFeatures::SIMULATION_RESET_SPAWNED,
  183. simulation_interfaces::msg::SimulatorFeatures::SIMULATION_STATE_PAUSE,
  184. simulation_interfaces::msg::SimulatorFeatures::STEP_SIMULATION_SINGLE,
  185. simulation_interfaces::msg::SimulatorFeatures::STEP_SIMULATION_MULTIPLE,
  186. simulation_interfaces::msg::SimulatorFeatures::STEP_SIMULATION_ACTION,
  187. simulation_interfaces::msg::SimulatorFeatures::SIMULATION_STATE_SETTING,
  188. simulation_interfaces::msg::SimulatorFeatures::SIMULATION_STATE_GETTING });
  189. if (PrintStateNameInGui())
  190. {
  191. AZ::TickBus::Handler::BusConnect();
  192. }
  193. // Query registry for keyboard transition keys
  194. if (const auto stoppedToPlayingKey = GetKeyboardTransitionKey(KeyboardTransitionStoppedToPlaying))
  195. {
  196. RegisterTransitionsKey(
  197. *stoppedToPlayingKey,
  198. simulation_interfaces::msg::SimulationState::STATE_STOPPED,
  199. simulation_interfaces::msg::SimulationState::STATE_PLAYING);
  200. }
  201. if (const auto pausedToPlayingKey = GetKeyboardTransitionKey(KeyboardTransitionPausedToPlaying))
  202. {
  203. RegisterTransitionsKey(
  204. *pausedToPlayingKey,
  205. simulation_interfaces::msg::SimulationState::STATE_PAUSED,
  206. simulation_interfaces::msg::SimulationState::STATE_PLAYING);
  207. }
  208. if (const auto playingToPausedKey = GetKeyboardTransitionKey(KeyboardTransitionPlayingToPaused))
  209. {
  210. RegisterTransitionsKey(
  211. *playingToPausedKey,
  212. simulation_interfaces::msg::SimulationState::STATE_PLAYING,
  213. simulation_interfaces::msg::SimulationState::STATE_PAUSED);
  214. }
  215. InputChannelEventListener::BusConnect();
  216. AZ::ApplicationTypeQuery appType;
  217. AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationBus::Events::QueryApplicationType, appType);
  218. // wait one tick to allow all system to start to ensure correct bus calls related to setting simulation state
  219. if (appType.IsEditor())
  220. {
  221. // if app is editor, buses for getting current world are inactive and return fail. In editor we need to simply initialize state
  222. AZ::SystemTickBus::QueueFunction(
  223. [this]()
  224. {
  225. InitializeSimulationState();
  226. });
  227. }
  228. else if (appType.IsGame())
  229. {
  230. AZ::SystemTickBus::QueueFunction(
  231. [this]()
  232. {
  233. // check if level is loaded
  234. AZ::Outcome<WorldResource, FailedResult> getCurrentWorldOutcome;
  235. LevelManagerRequestBus::BroadcastResult(getCurrentWorldOutcome, &LevelManagerRequestBus::Events::GetCurrentWorld);
  236. if (!getCurrentWorldOutcome.IsSuccess() &&
  237. getCurrentWorldOutcome.GetError().m_errorCode ==
  238. simulation_interfaces::srv::GetCurrentWorld::Response::NO_WORLD_LOADED)
  239. {
  240. SetSimulationState(simulation_interfaces::msg::SimulationState::STATE_NO_WORLD);
  241. }
  242. else if (getCurrentWorldOutcome.IsSuccess())
  243. {
  244. m_levelLoadedAtStartup = getCurrentWorldOutcome.GetValue().m_worldResource.m_uri;
  245. InitializeSimulationState();
  246. }
  247. });
  248. }
  249. }
  250. void SimulationManager::Deactivate()
  251. {
  252. InputChannelEventListener::BusDisconnect();
  253. UnregisterAllTransitionKeys();
  254. AZ::TickBus::Handler::BusDisconnect();
  255. SimulationManagerRequestBus::Handler::BusDisconnect();
  256. m_levelLoadedAtStartup.reset();
  257. }
  258. bool SimulationManager::IsSimulationPaused() const
  259. {
  260. return m_isSimulationPaused;
  261. }
  262. bool SimulationManager::IsSimulationStepsActive() const
  263. {
  264. return m_simulationFinishEvent.IsConnected();
  265. }
  266. void SimulationManager::CancelStepSimulation()
  267. {
  268. if (m_simulationFinishEvent.IsConnected())
  269. {
  270. m_simulationFinishEvent.Disconnect();
  271. SetSimulationPaused(true);
  272. m_numberOfPhysicsSteps = 0;
  273. }
  274. }
  275. void SimulationManager::SetSimulationPaused(bool paused)
  276. {
  277. // get az physics system
  278. auto* physicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get();
  279. AZ_Assert(physicsSystem, "Physics system is not available");
  280. const auto& sceneHandlers = physicsSystem->GetAllScenes();
  281. [[maybe_unused]] auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get();
  282. AZ_Assert(sceneInterface, "Physics scene interface is not available");
  283. for (auto& scene : sceneHandlers)
  284. {
  285. AZ_Assert(scene, "Physics scene is not available");
  286. scene->SetEnabled(!paused);
  287. m_isSimulationPaused = paused;
  288. }
  289. }
  290. void SimulationManager::StepSimulation(AZ::u64 steps)
  291. {
  292. if (steps == 0)
  293. {
  294. return;
  295. }
  296. m_numberOfPhysicsSteps = steps;
  297. // install handler
  298. m_simulationFinishEvent = AzPhysics::SceneEvents::OnSceneSimulationFinishHandler(
  299. [this](AzPhysics::SceneHandle sceneHandle, float)
  300. {
  301. m_numberOfPhysicsSteps--;
  302. AZ_Printf("SimulationManager", "Physics simulation step finished. Remaining steps: %d", m_numberOfPhysicsSteps);
  303. SimulationManagerNotificationsBus::Broadcast(
  304. &SimulationManagerNotifications::OnSimulationStepFinish, m_numberOfPhysicsSteps);
  305. if (m_numberOfPhysicsSteps <= 0)
  306. {
  307. SetSimulationPaused(true);
  308. // remove handler
  309. m_simulationFinishEvent.Disconnect();
  310. }
  311. });
  312. // get default scene
  313. [[maybe_unused]] auto* physicsSystem = AZ::Interface<AzPhysics::SystemInterface>::Get();
  314. AZ_Assert(physicsSystem, "Physics system is not available");
  315. auto* sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get();
  316. AZ_Assert(sceneInterface, "Physics scene interface is not available");
  317. AzPhysics::SceneHandle defaultScene = sceneInterface->GetSceneHandle(AzPhysics::DefaultPhysicsSceneName);
  318. auto scene = sceneInterface->GetScene(defaultScene);
  319. AZ_Assert(scene, "Default physics scene is not available");
  320. // install handler
  321. scene->RegisterSceneSimulationFinishHandler(m_simulationFinishEvent);
  322. SetSimulationPaused(false);
  323. }
  324. AZ::Outcome<void, FailedResult> SimulationManager::ResetSimulation(ReloadLevelCallback completionCallback)
  325. {
  326. // reset is possible only if simulation is already started - world is loaded, or simulation interfaces has in cache level loaded
  327. // during startup. Only in this case reseting makes sense. If there is no info about level loaded at startup and there is no world
  328. // loaded -> simulation is in the exactly same state as right after startup
  329. if ((m_simulationState == simulation_interfaces::msg::SimulationState::STATE_LOADING_WORLD ||
  330. m_simulationState == simulation_interfaces::msg::SimulationState::STATE_NO_WORLD) &&
  331. !m_levelLoadedAtStartup.has_value())
  332. {
  333. return AZ::Failure(FailedResult(
  334. simulation_interfaces::msg::Result::RESULT_OPERATION_FAILED,
  335. "Cannot reset simulation without loaded level and without knowledge about level loaded during startup. Simulator is "
  336. "already in the same state as after start up"));
  337. }
  338. m_reloadLevelCallback = completionCallback;
  339. // We need to delete all entities before reloading the level
  340. DeletionCompletedCb deleteAllCompletion =
  341. [levelLoadedAtStartup = m_levelLoadedAtStartup, completionCallback](const AZ::Outcome<void, FailedResult>& result)
  342. {
  343. AZ_Info("SimulationManager", "Delete all entities completed: %s, reload level", result.IsSuccess() ? "true" : "false");
  344. // queue required to allow all resources related to removed spawnables to be released, especially those related to level.pak
  345. AZ::SystemTickBus::QueueFunction(
  346. [levelLoadedAtStartup, completionCallback]()
  347. {
  348. // call level manager to reload the level
  349. if (levelLoadedAtStartup.has_value())
  350. {
  351. LoadWorldRequest request;
  352. request.levelResource.m_uri = levelLoadedAtStartup.value();
  353. LevelManagerRequestBus::Broadcast(&LevelManagerRequests::LoadWorld, request);
  354. }
  355. else
  356. {
  357. LevelManagerRequestBus::Broadcast(&LevelManagerRequests::UnloadWorld);
  358. if (completionCallback)
  359. {
  360. completionCallback();
  361. }
  362. }
  363. });
  364. };
  365. // delete spawned entities
  366. SimulationEntityManagerRequestBus::Broadcast(&SimulationEntityManagerRequests::DeleteAllEntities, deleteAllCompletion);
  367. return AZ::Success();
  368. }
  369. SimulationState SimulationManager::GetSimulationState() const
  370. {
  371. return m_simulationState;
  372. }
  373. AZ::Outcome<void, FailedResult> SimulationManager::SetSimulationState(SimulationState stateToSet)
  374. {
  375. // check if simulation is in desire state
  376. if (m_simulationState == stateToSet)
  377. {
  378. return AZ::Failure(FailedResult(
  379. simulation_interfaces::srv::SetSimulationState::Response::ALREADY_IN_TARGET_STATE,
  380. "Simulation is already in requested state, transition unecessary"));
  381. }
  382. if (IsTransitionForbiddenInEditor(stateToSet))
  383. {
  384. const auto stateToSetName = GetStateName(stateToSet);
  385. const auto currentStateName = GetStateName(m_simulationState);
  386. return AZ::Failure(FailedResult(
  387. simulation_interfaces::srv::SetSimulationState::Response::INCORRECT_TRANSITION,
  388. AZStd::string::format(
  389. "Requested transition (%s -> %s) is forbidden in the Editor. It is available in GameLauncher.",
  390. currentStateName.c_str(),
  391. stateToSetName.c_str())));
  392. }
  393. if (IsTransitionForbidden(stateToSet))
  394. {
  395. const auto stateToSetName = GetStateName(stateToSet);
  396. const auto currentStateName = GetStateName(m_simulationState);
  397. return AZ::Failure(FailedResult(
  398. simulation_interfaces::srv::SetSimulationState::Response::INCORRECT_TRANSITION,
  399. AZStd::string::format("Requested transition (%s -> %s) is forbidden", currentStateName.c_str(), stateToSetName.c_str())));
  400. }
  401. switch (stateToSet)
  402. {
  403. case simulation_interfaces::msg::SimulationState::STATE_STOPPED:
  404. {
  405. if (m_reloadLevelCallback)
  406. {
  407. m_reloadLevelCallback();
  408. m_reloadLevelCallback = nullptr;
  409. }
  410. if (m_levelLoaded)
  411. {
  412. InitializeSimulationState();
  413. m_levelLoaded = false;
  414. }
  415. else // transition from other state than load world
  416. {
  417. DeletionCompletedCb deleteAllCompletion = [this](const AZ::Outcome<void, FailedResult>& result)
  418. {
  419. AZ_Info("SimulationManager", "Delete all entities completed: %s", result.IsSuccess() ? "true" : "false");
  420. InitializeSimulationState();
  421. };
  422. // delete spawned entities
  423. SimulationEntityManagerRequestBus::Broadcast(&SimulationEntityManagerRequests::DeleteAllEntities, deleteAllCompletion);
  424. }
  425. break;
  426. }
  427. case simulation_interfaces::msg::SimulationState::STATE_PLAYING:
  428. {
  429. SetSimulationPaused(false);
  430. break;
  431. }
  432. case simulation_interfaces::msg::SimulationState::STATE_PAUSED:
  433. {
  434. SetSimulationPaused(true);
  435. break;
  436. }
  437. case simulation_interfaces::msg::SimulationState::STATE_QUITTING:
  438. {
  439. // stop simulation -> kill the simulator.
  440. SetSimulationPaused(true);
  441. // queue to allow status of this method to be returned, then start quitting
  442. AZ::SystemTickBus::QueueFunction(
  443. []()
  444. {
  445. AzFramework::ConsoleRequestBus::Broadcast(&AzFramework::ConsoleRequests::ExecuteConsoleCommand, "quit");
  446. });
  447. break;
  448. }
  449. case simulation_interfaces::msg::SimulationState::STATE_NO_WORLD:
  450. {
  451. // restore initial state for variables
  452. m_isSimulationPaused = false;
  453. m_levelLoaded = false;
  454. m_numberOfPhysicsSteps = 0;
  455. break;
  456. }
  457. case simulation_interfaces::msg::SimulationState::STATE_LOADING_WORLD:
  458. {
  459. m_levelLoaded = true;
  460. break;
  461. }
  462. default:
  463. {
  464. return AZ::Failure(FailedResult(
  465. simulation_interfaces::srv::SetSimulationState::Response::INCORRECT_TRANSITION, "Requested state doesn't exists"));
  466. break;
  467. }
  468. }
  469. m_simulationState = stateToSet;
  470. return AZ::Success();
  471. }
  472. bool SimulationManager::EntitiesOperationsPossible()
  473. {
  474. return (
  475. m_simulationState != simulation_interfaces::msg::SimulationState::STATE_LOADING_WORLD &&
  476. m_simulationState != simulation_interfaces::msg::SimulationState::STATE_NO_WORLD);
  477. }
  478. bool SimulationManager::IsTransitionForbiddenInEditor(SimulationState requestedState)
  479. {
  480. // in the Editor we cannot reload level, so going to STOPPED state is forbidden, we cannot quit the editor so going to QUITTING
  481. // state is forbidden
  482. AZ::ApplicationTypeQuery appType;
  483. AZ::ComponentApplicationBus::Broadcast(&AZ::ComponentApplicationBus::Events::QueryApplicationType, appType);
  484. if (appType.IsValid() && !appType.IsGame())
  485. {
  486. if (requestedState == simulation_interfaces::msg::SimulationState::STATE_STOPPED ||
  487. requestedState == simulation_interfaces::msg::SimulationState::STATE_QUITTING)
  488. {
  489. return true;
  490. }
  491. }
  492. return false;
  493. }
  494. bool SimulationManager::IsTransitionForbidden(SimulationState requestedState)
  495. {
  496. AZStd::pair<SimulationState, SimulationState> desireTransition{ m_simulationState, requestedState };
  497. auto it = AZStd::find(m_forbiddenStatesTransitions.begin(), m_forbiddenStatesTransitions.end(), desireTransition);
  498. return it != m_forbiddenStatesTransitions.end();
  499. }
  500. void SimulationManager::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time)
  501. {
  502. // get if we have available keyboard transition
  503. AZStd::string keyboardHint;
  504. const auto maybeKeyboardTransition = m_keyboardTransitions.find(m_simulationState);
  505. if (maybeKeyboardTransition != m_keyboardTransitions.end())
  506. {
  507. keyboardHint = maybeKeyboardTransition->second.m_uiDescription;
  508. }
  509. DebugDraw::DebugDrawRequestBus::Broadcast(
  510. &DebugDraw::DebugDrawRequests::DrawTextOnScreen,
  511. AZStd::string::format("Simulation state: %s \n %s", GetStateName(m_simulationState).c_str(), keyboardHint.c_str()),
  512. AZ::Color(1.0f, 1.0f, 1.0f, 1.0f),
  513. 0.f);
  514. }
  515. void SimulationManager::RegisterTransitionsKey(
  516. const AzFramework::InputChannelId& key, SimulationState sourceState, SimulationState desiredState)
  517. {
  518. const auto uiKeyName = MakePrettyKeyboardName(key);
  519. const auto uiHint =
  520. AZStd::string::format("Press %s to change simulation state to %s", uiKeyName.c_str(), GetStateName(desiredState).c_str());
  521. m_keyboardTransitions[sourceState] = { key, desiredState, uiHint };
  522. }
  523. void SimulationManager::UnregisterAllTransitionKeys()
  524. {
  525. m_keyboardTransitions.clear();
  526. }
  527. bool SimulationManager::OnInputChannelEventFiltered(const AzFramework::InputChannel& inputChannel)
  528. {
  529. const AzFramework::InputDeviceId& deviceId = inputChannel.GetInputDevice().GetInputDeviceId();
  530. if (AzFramework::InputDeviceKeyboard::IsKeyboardDevice(deviceId) && inputChannel.IsStateBegan())
  531. {
  532. const auto maybeKeyboardTransition = m_keyboardTransitions.find(m_simulationState);
  533. if (maybeKeyboardTransition == m_keyboardTransitions.end())
  534. {
  535. return false;
  536. }
  537. if (maybeKeyboardTransition->second.m_inputChannelId == inputChannel.GetInputChannelId())
  538. {
  539. // if we have transition, set the state
  540. auto result = SetSimulationState(maybeKeyboardTransition->second.m_desiredState);
  541. AZ_Error(
  542. "SimulationManager",
  543. result.IsSuccess(),
  544. "Failed to change simulation state: %d %s",
  545. result.GetError().m_errorCode,
  546. result.GetError().m_errorString.c_str());
  547. return true;
  548. }
  549. }
  550. return false;
  551. }
  552. } // namespace SimulationInterfaces