GameEngine.cpp 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829
  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 "EditorDefs.h"
  9. #include "GameEngine.h"
  10. // Qt
  11. #include <QMessageBox>
  12. #include <QThread>
  13. // AzCore
  14. #include <AzCore/Component/ComponentApplication.h>
  15. #include <AzCore/IO/IStreamer.h>
  16. #include <AzCore/IO/Streamer/FileRequest.h>
  17. #include <AzCore/Serialization/Locale.h>
  18. #include <AzCore/std/parallel/binary_semaphore.h>
  19. #include <AzCore/Console/IConsole.h>
  20. // AzFramework
  21. #include <AzFramework/Asset/AssetSystemBus.h>
  22. #include <AzFramework/Input/Devices/Mouse/InputDeviceMouse.h>
  23. #include <AzFramework/Input/Devices/Keyboard/InputDeviceKeyboard.h>
  24. #include <AzFramework/Input/Buses/Requests/InputSystemCursorRequestBus.h>
  25. #include <AzFramework/Archive/IArchive.h>
  26. // Editor
  27. #include "IEditorImpl.h"
  28. #include "CryEditDoc.h"
  29. #include "Settings.h"
  30. // CryCommon
  31. #include <CryCommon/MainThreadRenderRequestBus.h>
  32. // Editor
  33. #include "CryEdit.h"
  34. #include "ViewManager.h"
  35. #include "AnimationContext.h"
  36. #include "MainWindow.h"
  37. // Implementation of System Callback structure.
  38. struct SSystemUserCallback
  39. : public ISystemUserCallback
  40. {
  41. SSystemUserCallback(IInitializeUIInfo* logo) : m_threadErrorHandler(this) { m_pLogo = logo; };
  42. void OnSystemConnect(ISystem* pSystem) override
  43. {
  44. ModuleInitISystem(pSystem, "Editor");
  45. }
  46. bool OnError(const char* szErrorString) override
  47. {
  48. // since we show a message box, we have to use the GUI thread
  49. if (QThread::currentThread() != qApp->thread())
  50. {
  51. bool result = false;
  52. QMetaObject::invokeMethod(&m_threadErrorHandler, "OnError", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, result), Q_ARG(const char*, szErrorString));
  53. return result;
  54. }
  55. if (szErrorString)
  56. {
  57. Log(szErrorString);
  58. }
  59. if (GetIEditor()->IsInTestMode())
  60. {
  61. exit(1);
  62. }
  63. char str[4096];
  64. if (szErrorString)
  65. {
  66. azsnprintf(str, 4096, "%s\r\nSave Level Before Exiting the Editor?", szErrorString);
  67. }
  68. else
  69. {
  70. azsnprintf(str, 4096, "Unknown Error\r\nSave Level Before Exiting the Editor?");
  71. }
  72. int res = IDNO;
  73. ICVar* pCVar = gEnv->pConsole ? gEnv->pConsole->GetCVar("sys_no_crash_dialog") : nullptr;
  74. if (!pCVar || pCVar->GetIVal() == 0)
  75. {
  76. res = QMessageBox::critical(QApplication::activeWindow(), QObject::tr("Engine Error"), str, QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
  77. }
  78. if (res == QMessageBox::Yes || res == QMessageBox::No)
  79. {
  80. if (res == QMessageBox::Yes)
  81. {
  82. if (GetIEditor()->SaveDocument())
  83. {
  84. QMessageBox::information(QApplication::activeWindow(), QObject::tr("Save"), QObject::tr("Level has been successfully saved!\r\nPress Ok to terminate Editor."));
  85. }
  86. }
  87. }
  88. return true;
  89. }
  90. bool OnSaveDocument() override
  91. {
  92. bool success = false;
  93. if (GetIEditor())
  94. {
  95. // Turn off save backup as we force a backup before reaching this point
  96. bool prevSaveBackup = gSettings.bBackupOnSave;
  97. gSettings.bBackupOnSave = false;
  98. success = GetIEditor()->SaveDocument();
  99. gSettings.bBackupOnSave = prevSaveBackup;
  100. }
  101. return success;
  102. }
  103. bool OnBackupDocument() override
  104. {
  105. CCryEditDoc* level = GetIEditor() ? GetIEditor()->GetDocument() : nullptr;
  106. if (level)
  107. {
  108. return level->BackupBeforeSave(true);
  109. }
  110. return false;
  111. }
  112. void OnProcessSwitch() override
  113. {
  114. if (GetIEditor()->IsInGameMode())
  115. {
  116. GetIEditor()->SetInGameMode(false);
  117. }
  118. }
  119. void OnInitProgress(const char* sProgressMsg) override
  120. {
  121. if (m_pLogo)
  122. {
  123. m_pLogo->SetInfoText(sProgressMsg);
  124. }
  125. }
  126. void OnSplashScreenDone()
  127. {
  128. m_pLogo = nullptr;
  129. }
  130. private:
  131. IInitializeUIInfo* m_pLogo;
  132. ThreadedOnErrorHandler m_threadErrorHandler;
  133. };
  134. ThreadedOnErrorHandler::ThreadedOnErrorHandler(ISystemUserCallback* callback)
  135. : m_userCallback(callback)
  136. {
  137. moveToThread(qApp->thread());
  138. }
  139. ThreadedOnErrorHandler::~ThreadedOnErrorHandler()
  140. {
  141. }
  142. bool ThreadedOnErrorHandler::OnError(const char* error)
  143. {
  144. return m_userCallback->OnError(error);
  145. }
  146. //! This class will be used by CSystem to find out whether the negotiation with the assetprocessor failed
  147. class AssetProcessConnectionStatus
  148. : public AzFramework::AssetSystemConnectionNotificationsBus::Handler
  149. {
  150. public:
  151. AssetProcessConnectionStatus()
  152. {
  153. AzFramework::AssetSystemConnectionNotificationsBus::Handler::BusConnect();
  154. };
  155. ~AssetProcessConnectionStatus() override
  156. {
  157. AzFramework::AssetSystemConnectionNotificationsBus::Handler::BusDisconnect();
  158. }
  159. //! Notifies listeners that connection to the Asset Processor failed
  160. void ConnectionFailed() override
  161. {
  162. m_connectionFailed = true;
  163. }
  164. void NegotiationFailed() override
  165. {
  166. m_negotiationFailed = true;
  167. }
  168. bool CheckConnectionFailed()
  169. {
  170. return m_connectionFailed;
  171. }
  172. bool CheckNegotiationFailed()
  173. {
  174. return m_negotiationFailed;
  175. }
  176. private:
  177. bool m_connectionFailed = false;
  178. bool m_negotiationFailed = false;
  179. };
  180. AZ_PUSH_DISABLE_WARNING(4273, "-Wunknown-warning-option")
  181. CGameEngine::CGameEngine()
  182. : m_bIgnoreUpdates(false)
  183. , m_ePendingGameMode(ePGM_NotPending)
  184. , m_modalWindowDismisser(nullptr)
  185. AZ_POP_DISABLE_WARNING
  186. {
  187. m_pISystem = nullptr;
  188. m_bLevelLoaded = false;
  189. m_bInGameMode = false;
  190. m_bSimulationMode = false;
  191. m_bSyncPlayerPosition = true;
  192. m_hSystemHandle.reset(nullptr);
  193. m_bJustCreated = false;
  194. m_levelName = "Untitled";
  195. m_levelExtension = EditorUtils::LevelFile::GetDefaultFileExtension();
  196. m_playerViewTM.SetIdentity();
  197. GetIEditor()->RegisterNotifyListener(this);
  198. }
  199. AZ_PUSH_DISABLE_WARNING(4273, "-Wunknown-warning-option")
  200. CGameEngine::~CGameEngine()
  201. {
  202. AZ_POP_DISABLE_WARNING
  203. GetIEditor()->UnregisterNotifyListener(this);
  204. IMovieSystem* movieSystem = AZ::Interface<IMovieSystem>::Get();
  205. if (movieSystem)
  206. {
  207. movieSystem->SetCallback(nullptr);
  208. }
  209. delete m_pISystem;
  210. m_pISystem = nullptr;
  211. m_hSystemHandle.reset(nullptr);
  212. delete m_pSystemUserCallback;
  213. }
  214. static int ed_killmemory_size;
  215. static int ed_indexfiles;
  216. void KillMemory(IConsoleCmdArgs* /* pArgs */)
  217. {
  218. while (true)
  219. {
  220. const int kLimit = 10000000;
  221. int size;
  222. if (ed_killmemory_size > 0)
  223. {
  224. size = ed_killmemory_size;
  225. }
  226. else
  227. {
  228. size = rand() * rand();
  229. size = size > kLimit ? kLimit : size;
  230. }
  231. new uint8[size];
  232. }
  233. }
  234. static void CmdGotoEditor(IConsoleCmdArgs* pArgs)
  235. {
  236. // Console commands are assumed to be in the culture invariant locale since they can come from data files.
  237. AZ::Locale::ScopedSerializationLocale scopedLocale;
  238. // feature is mostly useful for QA purposes, this works with the game "goto" command
  239. // this console command actually is used by the game command, the editor command shouldn't be used by the user
  240. int iArgCount = pArgs->GetArgCount();
  241. CViewManager* pViewManager = GetIEditor()->GetViewManager();
  242. CViewport* pRenderViewport = pViewManager->GetGameViewport();
  243. if (!pRenderViewport)
  244. {
  245. return;
  246. }
  247. float x, y, z, wx, wy, wz;
  248. if (iArgCount == 7
  249. && azsscanf(pArgs->GetArg(1), "%f", &x) == 1
  250. && azsscanf(pArgs->GetArg(2), "%f", &y) == 1
  251. && azsscanf(pArgs->GetArg(3), "%f", &z) == 1
  252. && azsscanf(pArgs->GetArg(4), "%f", &wx) == 1
  253. && azsscanf(pArgs->GetArg(5), "%f", &wy) == 1
  254. && azsscanf(pArgs->GetArg(6), "%f", &wz) == 1)
  255. {
  256. Matrix34 tm = pRenderViewport->GetViewTM();
  257. tm.SetTranslation(Vec3(x, y, z));
  258. tm.SetRotation33(Matrix33::CreateRotationXYZ(DEG2RAD(Ang3(wx, wy, wz))));
  259. pRenderViewport->SetViewTM(tm);
  260. }
  261. }
  262. AZ::Outcome<void, AZStd::string> CGameEngine::Init(
  263. bool bPreviewMode,
  264. bool bTestMode,
  265. const char* sInCmdLine,
  266. IInitializeUIInfo* logo,
  267. HWND hwndForInputSystem)
  268. {
  269. m_pSystemUserCallback = new SSystemUserCallback(logo);
  270. constexpr const char* crySystemLibraryName = AZ_TRAIT_OS_DYNAMIC_LIBRARY_PREFIX "CrySystem" AZ_TRAIT_OS_DYNAMIC_LIBRARY_EXTENSION;
  271. m_hSystemHandle = AZ::DynamicModuleHandle::Create(crySystemLibraryName);
  272. if (!m_hSystemHandle->Load(AZ::DynamicModuleHandle::LoadFlags::InitFuncRequired))
  273. {
  274. auto errorMessage = AZStd::string::format("%s Loading Failed", crySystemLibraryName);
  275. Error(errorMessage.c_str());
  276. return AZ::Failure(errorMessage);
  277. }
  278. PFNCREATESYSTEMINTERFACE pfnCreateSystemInterface =
  279. m_hSystemHandle->GetFunction<PFNCREATESYSTEMINTERFACE>("CreateSystemInterface");
  280. SSystemInitParams sip;
  281. sip.bEditor = true;
  282. sip.bDedicatedServer = false;
  283. sip.bPreview = bPreviewMode;
  284. sip.bTestMode = bTestMode;
  285. sip.hInstance = nullptr;
  286. #ifdef AZ_PLATFORM_MAC
  287. // Create a hidden QWidget. Would show a black window on macOS otherwise.
  288. auto window = new QWidget();
  289. QObject::connect(qApp, &QApplication::lastWindowClosed, window, &QWidget::deleteLater);
  290. sip.hWnd = (HWND)window->winId();
  291. #else
  292. sip.hWnd = hwndForInputSystem;
  293. #endif
  294. sip.pLogCallback = &m_logFile;
  295. sip.sLogFileName = "@log@/Editor.log";
  296. sip.pUserCallback = m_pSystemUserCallback;
  297. if (sInCmdLine)
  298. {
  299. azstrncpy(sip.szSystemCmdLine, AZ_COMMAND_LINE_LEN, sInCmdLine, AZ_COMMAND_LINE_LEN);
  300. if (strstr(sInCmdLine, "-export") || strstr(sInCmdLine, "/export") || strstr(sInCmdLine, "-autotest_mode"))
  301. {
  302. sip.bUnattendedMode = true;
  303. }
  304. }
  305. if (sip.bUnattendedMode)
  306. {
  307. m_modalWindowDismisser = AZStd::make_unique<ModalWindowDismisser>();
  308. }
  309. AssetProcessConnectionStatus apConnectionStatus;
  310. m_pISystem = pfnCreateSystemInterface(sip);
  311. if (!gEnv)
  312. {
  313. gEnv = m_pISystem->GetGlobalEnvironment();
  314. }
  315. if (!m_pISystem)
  316. {
  317. AZStd::string errorMessage = "Could not initialize CSystem. View the logs for more details.";
  318. gEnv = nullptr;
  319. Error("CreateSystemInterface Failed");
  320. return AZ::Failure(errorMessage);
  321. }
  322. if (apConnectionStatus.CheckNegotiationFailed())
  323. {
  324. auto errorMessage = AZStd::string::format("Negotiation with Asset Processor failed.\n"
  325. "Please ensure the Asset Processor is running on the same branch and try again.");
  326. gEnv = nullptr;
  327. return AZ::Failure(errorMessage);
  328. }
  329. if (apConnectionStatus.CheckConnectionFailed())
  330. {
  331. AzFramework::AssetSystem::ConnectionSettings connectionSettings;
  332. AzFramework::AssetSystem::ReadConnectionSettingsFromSettingsRegistry(connectionSettings);
  333. auto errorMessage = AZStd::string::format("Unable to connect to the local Asset Processor.\n\n"
  334. "The Asset Processor is either not running locally or not accepting connections on port %hu. "
  335. "Check your remote_port settings in bootstrap.cfg or view the Asset Processor's \"Logs\" tab "
  336. "for any errors.", connectionSettings.m_assetProcessorPort);
  337. gEnv = nullptr;
  338. return AZ::Failure(errorMessage);
  339. }
  340. SetEditorCoreEnvironment(gEnv);
  341. IMovieSystem* movieSystem = AZ::Interface<IMovieSystem>::Get();
  342. if (movieSystem)
  343. {
  344. movieSystem->EnablePhysicsEvents(m_bSimulationMode);
  345. }
  346. CLogFile::AboutSystem();
  347. REGISTER_CVAR(ed_killmemory_size, -1, VF_DUMPTODISK, "Sets the testing allocation size. -1 for random");
  348. REGISTER_CVAR(ed_indexfiles, 1, VF_DUMPTODISK, "Index game resource files, 0 - inactive, 1 - active");
  349. REGISTER_COMMAND("ed_killmemory", KillMemory, VF_NULL, "");
  350. REGISTER_COMMAND("ed_goto", CmdGotoEditor, VF_CHEAT, "Internal command, used by the 'GOTO' console command\n");
  351. // The editor needs to handle the quit command differently
  352. gEnv->pConsole->RemoveCommand("quit");
  353. REGISTER_COMMAND("quit", CGameEngine::HandleQuitRequest, VF_RESTRICTEDMODE, "Quit/Shutdown the engine");
  354. CrySystemEventBus::Broadcast(&CrySystemEventBus::Events::OnCryEditorInitialized);
  355. return AZ::Success();
  356. }
  357. bool CGameEngine::InitGame(const char*)
  358. {
  359. m_pISystem->ExecuteCommandLine();
  360. return true;
  361. }
  362. void CGameEngine::SetLevelPath(const QString& path)
  363. {
  364. QByteArray levelPath;
  365. levelPath.reserve(AZ_MAX_PATH_LEN);
  366. levelPath = Path::ToUnixPath(Path::RemoveBackslash(path)).toUtf8();
  367. m_levelPath = levelPath;
  368. m_levelName = m_levelPath.mid(m_levelPath.lastIndexOf('/') + 1);
  369. const char* oldExtension = EditorUtils::LevelFile::GetOldCryFileExtension();
  370. const char* defaultExtension = EditorUtils::LevelFile::GetDefaultFileExtension();
  371. // Store off if
  372. if (QFileInfo(path + oldExtension).exists())
  373. {
  374. m_levelExtension = oldExtension;
  375. }
  376. else
  377. {
  378. m_levelExtension = defaultExtension;
  379. }
  380. }
  381. bool CGameEngine::LoadLevel(
  382. [[maybe_unused]] bool bDeleteAIGraph,
  383. [[maybe_unused]] bool bReleaseResources)
  384. {
  385. m_bLevelLoaded = false;
  386. CLogFile::FormatLine("Loading map '%s' into engine...", m_levelPath.toUtf8().data());
  387. // Switch the current directory back to the Primary CD folder first.
  388. // The engine might have trouble to find some files when the current
  389. // directory is wrong
  390. QDir::setCurrent(GetIEditor()->GetPrimaryCDFolder());
  391. m_bLevelLoaded = true;
  392. return true;
  393. }
  394. bool CGameEngine::ReloadLevel()
  395. {
  396. if (!LoadLevel(false, false))
  397. {
  398. return false;
  399. }
  400. return true;
  401. }
  402. void CGameEngine::SwitchToInGame()
  403. {
  404. auto streamer = AZ::Interface<AZ::IO::IStreamer>::Get();
  405. if (streamer)
  406. {
  407. AZStd::binary_semaphore wait;
  408. AZ::IO::FileRequestPtr flush = streamer->FlushCaches();
  409. streamer->SetRequestCompleteCallback(flush, [&wait](AZ::IO::FileRequestHandle) { wait.release(); });
  410. streamer->QueueRequest(flush);
  411. wait.acquire();
  412. }
  413. GetIEditor()->Notify(eNotify_OnBeginGameMode);
  414. IMovieSystem* movieSystem = AZ::Interface<IMovieSystem>::Get();
  415. if (movieSystem)
  416. {
  417. movieSystem->EnablePhysicsEvents(true);
  418. }
  419. m_bInGameMode = true;
  420. if (movieSystem)
  421. {
  422. constexpr bool playOnReset = true;
  423. constexpr bool seekToStart = false;
  424. movieSystem->Reset(playOnReset, seekToStart);
  425. }
  426. // Transition to runtime entity context.
  427. AzToolsFramework::EditorEntityContextRequestBus::Broadcast(&AzToolsFramework::EditorEntityContextRequestBus::Events::StartPlayInEditor);
  428. if (!CCryEditApp::instance()->IsInAutotestMode())
  429. {
  430. // Constrain and hide the system cursor (important to do this last)
  431. AzFramework::InputSystemCursorRequestBus::Event(AzFramework::InputDeviceMouse::Id,
  432. &AzFramework::InputSystemCursorRequests::SetSystemCursorState,
  433. AzFramework::SystemCursorState::ConstrainedAndHidden);
  434. }
  435. Log("Entered game mode");
  436. }
  437. void CGameEngine::SwitchToInEditor()
  438. {
  439. // Transition to editor entity context.
  440. AzToolsFramework::EditorEntityContextRequestBus::Broadcast(&AzToolsFramework::EditorEntityContextRequestBus::Events::StopPlayInEditor);
  441. // Reset movie system
  442. IMovieSystem* movieSystem = AZ::Interface<IMovieSystem>::Get();
  443. if (movieSystem)
  444. {
  445. for (int i = movieSystem->GetNumPlayingSequences(); --i >= 0;)
  446. {
  447. movieSystem->GetPlayingSequence(i)->Deactivate();
  448. }
  449. constexpr bool playOnReset = false;
  450. constexpr bool seekToStart = false;
  451. movieSystem->Reset(playOnReset, seekToStart);
  452. }
  453. CViewport* pGameViewport = GetIEditor()->GetViewManager()->GetGameViewport();
  454. if (movieSystem)
  455. {
  456. movieSystem->EnablePhysicsEvents(m_bSimulationMode);
  457. }
  458. m_bInGameMode = false;
  459. // Out of game in Editor mode.
  460. if (pGameViewport)
  461. {
  462. pGameViewport->SetViewTM(m_playerViewTM);
  463. }
  464. GetIEditor()->Notify(eNotify_OnEndGameMode);
  465. // Unconstrain the system cursor and make it visible (important to do this last)
  466. AzFramework::InputSystemCursorRequestBus::Event(AzFramework::InputDeviceMouse::Id,
  467. &AzFramework::InputSystemCursorRequests::SetSystemCursorState,
  468. AzFramework::SystemCursorState::UnconstrainedAndVisible);
  469. Log("Exited game mode");
  470. }
  471. void CGameEngine::HandleQuitRequest(IConsoleCmdArgs* /*args*/)
  472. {
  473. if (GetIEditor()->GetGameEngine()->IsInGameMode())
  474. {
  475. GetIEditor()->GetGameEngine()->RequestSetGameMode(false);
  476. gEnv->pConsole->ShowConsole(false);
  477. }
  478. else
  479. {
  480. MainWindow::instance()->window()->close();
  481. }
  482. }
  483. void CGameEngine::RequestSetGameMode(bool inGame)
  484. {
  485. m_ePendingGameMode = inGame ? ePGM_SwitchToInGame : ePGM_SwitchToInEditor;
  486. if (m_ePendingGameMode == ePGM_SwitchToInGame)
  487. {
  488. AzToolsFramework::EditorLegacyGameModeNotificationBus::Broadcast(
  489. &AzToolsFramework::EditorLegacyGameModeNotificationBus::Events::OnStartGameModeRequest);
  490. }
  491. else if (m_ePendingGameMode == ePGM_SwitchToInEditor)
  492. {
  493. AzToolsFramework::EditorLegacyGameModeNotificationBus::Broadcast(
  494. &AzToolsFramework::EditorLegacyGameModeNotificationBus::Events::OnStopGameModeRequest);
  495. }
  496. }
  497. void CGameEngine::SetGameMode(bool bInGame)
  498. {
  499. if (m_bInGameMode == bInGame)
  500. {
  501. return;
  502. }
  503. if (!GetIEditor()->GetDocument())
  504. {
  505. return;
  506. }
  507. GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_GAME_MODE_SWITCH_START, bInGame, 0);
  508. // Enables engine to know about that.
  509. gEnv->SetIsEditorGameMode(bInGame);
  510. // Ignore updates while changing in and out of game mode
  511. m_bIgnoreUpdates = true;
  512. // Switching modes will destroy the current AzFramework::EntityConext which may contain
  513. // data the queued events hold on to, so execute all queued events before switching.
  514. ExecuteQueuedEvents();
  515. if (bInGame)
  516. {
  517. SwitchToInGame();
  518. }
  519. else
  520. {
  521. SwitchToInEditor();
  522. }
  523. // Enables engine to know about that.
  524. if (MainWindow::instance())
  525. {
  526. AzFramework::InputChannelRequestBus::Broadcast(&AzFramework::InputChannelRequests::ResetState);
  527. MainWindow::instance()->setFocus();
  528. }
  529. GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_EDITOR_GAME_MODE_CHANGED, bInGame, 0);
  530. m_bIgnoreUpdates = false;
  531. GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_GAME_MODE_SWITCH_END, bInGame, 0);
  532. }
  533. void CGameEngine::SetSimulationMode(bool enabled, bool bOnlyPhysics)
  534. {
  535. if (m_bSimulationMode == enabled)
  536. {
  537. return;
  538. }
  539. IMovieSystem* movieSystem = AZ::Interface<IMovieSystem>::Get();
  540. if (movieSystem)
  541. {
  542. movieSystem->EnablePhysicsEvents(enabled);
  543. }
  544. if (enabled)
  545. {
  546. GetIEditor()->Notify(eNotify_OnBeginSimulationMode);
  547. }
  548. else
  549. {
  550. GetIEditor()->Notify(eNotify_OnEndSimulationMode);
  551. }
  552. m_bSimulationMode = enabled;
  553. // Enables engine to know about simulation mode.
  554. gEnv->SetIsEditorSimulationMode(enabled);
  555. // Execute all queued events before switching modes.
  556. ExecuteQueuedEvents();
  557. // Transition back to editor entity context.
  558. // Symmetry is not critical. It's okay to call this even if we never called StartPlayInEditor
  559. // (bOnlyPhysics was true when we entered simulation mode).
  560. AzToolsFramework::EditorEntityContextRequestBus::Broadcast(&AzToolsFramework::EditorEntityContextRequestBus::Events::StopPlayInEditor);
  561. if (m_bSimulationMode && !bOnlyPhysics)
  562. {
  563. // Transition to runtime entity context.
  564. AzToolsFramework::EditorEntityContextRequestBus::Broadcast(&AzToolsFramework::EditorEntityContextRequestBus::Events::StartPlayInEditor);
  565. }
  566. AzFramework::InputChannelRequestBus::Broadcast(&AzFramework::InputChannelRequests::ResetState);
  567. }
  568. void CGameEngine::SetPlayerViewMatrix(const Matrix34& tm, [[maybe_unused]] bool bEyePos)
  569. {
  570. m_playerViewTM = tm;
  571. }
  572. void CGameEngine::SyncPlayerPosition(bool bEnable)
  573. {
  574. m_bSyncPlayerPosition = bEnable;
  575. if (m_bSyncPlayerPosition)
  576. {
  577. SetPlayerViewMatrix(m_playerViewTM);
  578. }
  579. }
  580. void CGameEngine::SetCurrentMOD(const char* sMod)
  581. {
  582. m_MOD = sMod;
  583. }
  584. QString CGameEngine::GetCurrentMOD() const
  585. {
  586. return m_MOD;
  587. }
  588. void CGameEngine::Update()
  589. {
  590. if (m_bIgnoreUpdates)
  591. {
  592. return;
  593. }
  594. switch (m_ePendingGameMode)
  595. {
  596. case ePGM_SwitchToInGame:
  597. {
  598. SetGameMode(true);
  599. m_ePendingGameMode = ePGM_NotPending;
  600. break;
  601. }
  602. case ePGM_SwitchToInEditor:
  603. {
  604. bool wasInSimulationMode = GetIEditor()->GetGameEngine()->GetSimulationMode();
  605. if (wasInSimulationMode)
  606. {
  607. GetIEditor()->GetGameEngine()->SetSimulationMode(false);
  608. }
  609. SetGameMode(false);
  610. if (wasInSimulationMode)
  611. {
  612. GetIEditor()->GetGameEngine()->SetSimulationMode(true);
  613. }
  614. m_ePendingGameMode = ePGM_NotPending;
  615. break;
  616. }
  617. }
  618. AZ::ComponentApplication* componentApplication = nullptr;
  619. AZ::ComponentApplicationBus::BroadcastResult(componentApplication, &AZ::ComponentApplicationBus::Events::GetApplication);
  620. if (m_bInGameMode)
  621. {
  622. if (gEnv->pSystem)
  623. {
  624. gEnv->pSystem->UpdatePreTickBus();
  625. componentApplication->Tick();
  626. gEnv->pSystem->UpdatePostTickBus();
  627. }
  628. if (CViewport* pRenderViewport = GetIEditor()->GetViewManager()->GetGameViewport())
  629. {
  630. pRenderViewport->Update();
  631. }
  632. // Check for the Escape key to exit game mode here rather than in Qt,
  633. // because all Qt events are usually filtered out in game mode in
  634. // QtEditorApplication_<platform>.cpp nativeEventFilter() to prevent
  635. // using Editor menu actions and shortcuts that shouldn't trigger while
  636. // playing the game.
  637. // When the user opens the console, Qt events will be allowed
  638. // so the user can interact with limited Editor content like the console.
  639. const AzFramework::InputChannel* inputChannel = nullptr;
  640. const AzFramework::InputChannelId channelId(AzFramework::InputDeviceKeyboard::Key::Escape);
  641. AzFramework::InputChannelRequestBus::EventResult(inputChannel, channelId, &AzFramework::InputChannelRequests::GetInputChannel);
  642. if(inputChannel && inputChannel->GetState() == AzFramework::InputChannel::State::Began)
  643. {
  644. // leave game mode
  645. RequestSetGameMode(false);
  646. }
  647. }
  648. else
  649. {
  650. // [marco] check current sound and vis areas for music etc.
  651. // but if in game mode, 'cos is already done in the above call to game->update()
  652. unsigned int updateFlags = ESYSUPDATE_EDITOR;
  653. GetIEditor()->GetAnimation()->Update();
  654. GetIEditor()->GetSystem()->UpdatePreTickBus(updateFlags);
  655. componentApplication->Tick();
  656. GetIEditor()->GetSystem()->UpdatePostTickBus(updateFlags);
  657. }
  658. }
  659. void CGameEngine::OnEditorNotifyEvent(EEditorNotifyEvent event)
  660. {
  661. switch (event)
  662. {
  663. case eNotify_OnSplashScreenDestroyed:
  664. {
  665. if (m_pSystemUserCallback != nullptr)
  666. {
  667. m_pSystemUserCallback->OnSplashScreenDone();
  668. }
  669. }
  670. break;
  671. }
  672. }
  673. void CGameEngine::OnAreaModified([[maybe_unused]] const AABB& modifiedArea)
  674. {
  675. }
  676. void CGameEngine::ExecuteQueuedEvents()
  677. {
  678. AZ::Data::AssetBus::ExecuteQueuedEvents();
  679. AZ::TickBus::ExecuteQueuedEvents();
  680. AZ::MainThreadRenderRequestBus::ExecuteQueuedEvents();
  681. }
  682. #include <moc_GameEngine.cpp>