GameEngine.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800
  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. m_pISystem->GetIMovieSystem()->SetCallback(nullptr);
  205. delete m_pISystem;
  206. m_pISystem = nullptr;
  207. m_hSystemHandle.reset(nullptr);
  208. delete m_pSystemUserCallback;
  209. }
  210. static int ed_killmemory_size;
  211. static int ed_indexfiles;
  212. void KillMemory(IConsoleCmdArgs* /* pArgs */)
  213. {
  214. while (true)
  215. {
  216. const int kLimit = 10000000;
  217. int size;
  218. if (ed_killmemory_size > 0)
  219. {
  220. size = ed_killmemory_size;
  221. }
  222. else
  223. {
  224. size = rand() * rand();
  225. size = size > kLimit ? kLimit : size;
  226. }
  227. new uint8[size];
  228. }
  229. }
  230. static void CmdGotoEditor(IConsoleCmdArgs* pArgs)
  231. {
  232. // Console commands are assumed to be in the culture invariant locale since they can come from data files.
  233. AZ::Locale::ScopedSerializationLocale scopedLocale;
  234. // feature is mostly useful for QA purposes, this works with the game "goto" command
  235. // this console command actually is used by the game command, the editor command shouldn't be used by the user
  236. int iArgCount = pArgs->GetArgCount();
  237. CViewManager* pViewManager = GetIEditor()->GetViewManager();
  238. CViewport* pRenderViewport = pViewManager->GetGameViewport();
  239. if (!pRenderViewport)
  240. {
  241. return;
  242. }
  243. float x, y, z, wx, wy, wz;
  244. if (iArgCount == 7
  245. && azsscanf(pArgs->GetArg(1), "%f", &x) == 1
  246. && azsscanf(pArgs->GetArg(2), "%f", &y) == 1
  247. && azsscanf(pArgs->GetArg(3), "%f", &z) == 1
  248. && azsscanf(pArgs->GetArg(4), "%f", &wx) == 1
  249. && azsscanf(pArgs->GetArg(5), "%f", &wy) == 1
  250. && azsscanf(pArgs->GetArg(6), "%f", &wz) == 1)
  251. {
  252. Matrix34 tm = pRenderViewport->GetViewTM();
  253. tm.SetTranslation(Vec3(x, y, z));
  254. tm.SetRotation33(Matrix33::CreateRotationXYZ(DEG2RAD(Ang3(wx, wy, wz))));
  255. pRenderViewport->SetViewTM(tm);
  256. }
  257. }
  258. AZ::Outcome<void, AZStd::string> CGameEngine::Init(
  259. bool bPreviewMode,
  260. bool bTestMode,
  261. const char* sInCmdLine,
  262. IInitializeUIInfo* logo,
  263. HWND hwndForInputSystem)
  264. {
  265. m_pSystemUserCallback = new SSystemUserCallback(logo);
  266. constexpr const char* crySystemLibraryName = AZ_TRAIT_OS_DYNAMIC_LIBRARY_PREFIX "CrySystem" AZ_TRAIT_OS_DYNAMIC_LIBRARY_EXTENSION;
  267. m_hSystemHandle = AZ::DynamicModuleHandle::Create(crySystemLibraryName);
  268. if (!m_hSystemHandle->Load(AZ::DynamicModuleHandle::LoadFlags::InitFuncRequired))
  269. {
  270. auto errorMessage = AZStd::string::format("%s Loading Failed", crySystemLibraryName);
  271. Error(errorMessage.c_str());
  272. return AZ::Failure(errorMessage);
  273. }
  274. PFNCREATESYSTEMINTERFACE pfnCreateSystemInterface =
  275. m_hSystemHandle->GetFunction<PFNCREATESYSTEMINTERFACE>("CreateSystemInterface");
  276. SSystemInitParams sip;
  277. sip.bEditor = true;
  278. sip.bDedicatedServer = false;
  279. sip.bPreview = bPreviewMode;
  280. sip.bTestMode = bTestMode;
  281. sip.hInstance = nullptr;
  282. #ifdef AZ_PLATFORM_MAC
  283. // Create a hidden QWidget. Would show a black window on macOS otherwise.
  284. auto window = new QWidget();
  285. QObject::connect(qApp, &QApplication::lastWindowClosed, window, &QWidget::deleteLater);
  286. sip.hWnd = (HWND)window->winId();
  287. #else
  288. sip.hWnd = hwndForInputSystem;
  289. #endif
  290. sip.pLogCallback = &m_logFile;
  291. sip.sLogFileName = "@log@/Editor.log";
  292. sip.pUserCallback = m_pSystemUserCallback;
  293. if (sInCmdLine)
  294. {
  295. azstrncpy(sip.szSystemCmdLine, AZ_COMMAND_LINE_LEN, sInCmdLine, AZ_COMMAND_LINE_LEN);
  296. if (strstr(sInCmdLine, "-export") || strstr(sInCmdLine, "/export") || strstr(sInCmdLine, "-autotest_mode"))
  297. {
  298. sip.bUnattendedMode = true;
  299. }
  300. }
  301. if (sip.bUnattendedMode)
  302. {
  303. m_modalWindowDismisser = AZStd::make_unique<ModalWindowDismisser>();
  304. }
  305. AssetProcessConnectionStatus apConnectionStatus;
  306. m_pISystem = pfnCreateSystemInterface(sip);
  307. if (!gEnv)
  308. {
  309. gEnv = m_pISystem->GetGlobalEnvironment();
  310. }
  311. if (!m_pISystem)
  312. {
  313. AZStd::string errorMessage = "Could not initialize CSystem. View the logs for more details.";
  314. gEnv = nullptr;
  315. Error("CreateSystemInterface Failed");
  316. return AZ::Failure(errorMessage);
  317. }
  318. if (apConnectionStatus.CheckNegotiationFailed())
  319. {
  320. auto errorMessage = AZStd::string::format("Negotiation with Asset Processor failed.\n"
  321. "Please ensure the Asset Processor is running on the same branch and try again.");
  322. gEnv = nullptr;
  323. return AZ::Failure(errorMessage);
  324. }
  325. if (apConnectionStatus.CheckConnectionFailed())
  326. {
  327. AzFramework::AssetSystem::ConnectionSettings connectionSettings;
  328. AzFramework::AssetSystem::ReadConnectionSettingsFromSettingsRegistry(connectionSettings);
  329. auto errorMessage = AZStd::string::format("Unable to connect to the local Asset Processor.\n\n"
  330. "The Asset Processor is either not running locally or not accepting connections on port %hu. "
  331. "Check your remote_port settings in bootstrap.cfg or view the Asset Processor's \"Logs\" tab "
  332. "for any errors.", connectionSettings.m_assetProcessorPort);
  333. gEnv = nullptr;
  334. return AZ::Failure(errorMessage);
  335. }
  336. SetEditorCoreEnvironment(gEnv);
  337. if (gEnv && gEnv->pMovieSystem)
  338. {
  339. gEnv->pMovieSystem->EnablePhysicsEvents(m_bSimulationMode);
  340. }
  341. CLogFile::AboutSystem();
  342. REGISTER_CVAR(ed_killmemory_size, -1, VF_DUMPTODISK, "Sets the testing allocation size. -1 for random");
  343. REGISTER_CVAR(ed_indexfiles, 1, VF_DUMPTODISK, "Index game resource files, 0 - inactive, 1 - active");
  344. REGISTER_COMMAND("ed_killmemory", KillMemory, VF_NULL, "");
  345. REGISTER_COMMAND("ed_goto", CmdGotoEditor, VF_CHEAT, "Internal command, used by the 'GOTO' console command\n");
  346. // The editor needs to handle the quit command differently
  347. gEnv->pConsole->RemoveCommand("quit");
  348. REGISTER_COMMAND("quit", CGameEngine::HandleQuitRequest, VF_RESTRICTEDMODE, "Quit/Shutdown the engine");
  349. CrySystemEventBus::Broadcast(&CrySystemEventBus::Events::OnCryEditorInitialized);
  350. return AZ::Success();
  351. }
  352. bool CGameEngine::InitGame(const char*)
  353. {
  354. m_pISystem->ExecuteCommandLine();
  355. return true;
  356. }
  357. void CGameEngine::SetLevelPath(const QString& path)
  358. {
  359. QByteArray levelPath;
  360. levelPath.reserve(AZ_MAX_PATH_LEN);
  361. levelPath = Path::ToUnixPath(Path::RemoveBackslash(path)).toUtf8();
  362. m_levelPath = levelPath;
  363. m_levelName = m_levelPath.mid(m_levelPath.lastIndexOf('/') + 1);
  364. const char* oldExtension = EditorUtils::LevelFile::GetOldCryFileExtension();
  365. const char* defaultExtension = EditorUtils::LevelFile::GetDefaultFileExtension();
  366. // Store off if
  367. if (QFileInfo(path + oldExtension).exists())
  368. {
  369. m_levelExtension = oldExtension;
  370. }
  371. else
  372. {
  373. m_levelExtension = defaultExtension;
  374. }
  375. }
  376. bool CGameEngine::LoadLevel(
  377. [[maybe_unused]] bool bDeleteAIGraph,
  378. [[maybe_unused]] bool bReleaseResources)
  379. {
  380. m_bLevelLoaded = false;
  381. CLogFile::FormatLine("Loading map '%s' into engine...", m_levelPath.toUtf8().data());
  382. // Switch the current directory back to the Primary CD folder first.
  383. // The engine might have trouble to find some files when the current
  384. // directory is wrong
  385. QDir::setCurrent(GetIEditor()->GetPrimaryCDFolder());
  386. m_bLevelLoaded = true;
  387. return true;
  388. }
  389. bool CGameEngine::ReloadLevel()
  390. {
  391. if (!LoadLevel(false, false))
  392. {
  393. return false;
  394. }
  395. return true;
  396. }
  397. void CGameEngine::SwitchToInGame()
  398. {
  399. auto streamer = AZ::Interface<AZ::IO::IStreamer>::Get();
  400. if (streamer)
  401. {
  402. AZStd::binary_semaphore wait;
  403. AZ::IO::FileRequestPtr flush = streamer->FlushCaches();
  404. streamer->SetRequestCompleteCallback(flush, [&wait](AZ::IO::FileRequestHandle) { wait.release(); });
  405. streamer->QueueRequest(flush);
  406. wait.acquire();
  407. }
  408. GetIEditor()->Notify(eNotify_OnBeginGameMode);
  409. m_pISystem->GetIMovieSystem()->EnablePhysicsEvents(true);
  410. m_bInGameMode = true;
  411. m_pISystem->GetIMovieSystem()->Reset(true, false);
  412. // Transition to runtime entity context.
  413. AzToolsFramework::EditorEntityContextRequestBus::Broadcast(&AzToolsFramework::EditorEntityContextRequestBus::Events::StartPlayInEditor);
  414. if (!CCryEditApp::instance()->IsInAutotestMode())
  415. {
  416. // Constrain and hide the system cursor (important to do this last)
  417. AzFramework::InputSystemCursorRequestBus::Event(AzFramework::InputDeviceMouse::Id,
  418. &AzFramework::InputSystemCursorRequests::SetSystemCursorState,
  419. AzFramework::SystemCursorState::ConstrainedAndHidden);
  420. }
  421. Log("Entered game mode");
  422. }
  423. void CGameEngine::SwitchToInEditor()
  424. {
  425. // Transition to editor entity context.
  426. AzToolsFramework::EditorEntityContextRequestBus::Broadcast(&AzToolsFramework::EditorEntityContextRequestBus::Events::StopPlayInEditor);
  427. // Reset movie system
  428. for (int i = m_pISystem->GetIMovieSystem()->GetNumPlayingSequences(); --i >= 0;)
  429. {
  430. m_pISystem->GetIMovieSystem()->GetPlayingSequence(i)->Deactivate();
  431. }
  432. m_pISystem->GetIMovieSystem()->Reset(false, false);
  433. CViewport* pGameViewport = GetIEditor()->GetViewManager()->GetGameViewport();
  434. m_pISystem->GetIMovieSystem()->EnablePhysicsEvents(m_bSimulationMode);
  435. m_bInGameMode = false;
  436. // Out of game in Editor mode.
  437. if (pGameViewport)
  438. {
  439. pGameViewport->SetViewTM(m_playerViewTM);
  440. }
  441. GetIEditor()->Notify(eNotify_OnEndGameMode);
  442. // Unconstrain the system cursor and make it visible (important to do this last)
  443. AzFramework::InputSystemCursorRequestBus::Event(AzFramework::InputDeviceMouse::Id,
  444. &AzFramework::InputSystemCursorRequests::SetSystemCursorState,
  445. AzFramework::SystemCursorState::UnconstrainedAndVisible);
  446. Log("Exited game mode");
  447. }
  448. void CGameEngine::HandleQuitRequest(IConsoleCmdArgs* /*args*/)
  449. {
  450. if (GetIEditor()->GetGameEngine()->IsInGameMode())
  451. {
  452. GetIEditor()->GetGameEngine()->RequestSetGameMode(false);
  453. gEnv->pConsole->ShowConsole(false);
  454. }
  455. else
  456. {
  457. MainWindow::instance()->window()->close();
  458. }
  459. }
  460. void CGameEngine::RequestSetGameMode(bool inGame)
  461. {
  462. m_ePendingGameMode = inGame ? ePGM_SwitchToInGame : ePGM_SwitchToInEditor;
  463. if (m_ePendingGameMode == ePGM_SwitchToInGame)
  464. {
  465. AzToolsFramework::EditorLegacyGameModeNotificationBus::Broadcast(
  466. &AzToolsFramework::EditorLegacyGameModeNotificationBus::Events::OnStartGameModeRequest);
  467. }
  468. else if (m_ePendingGameMode == ePGM_SwitchToInEditor)
  469. {
  470. AzToolsFramework::EditorLegacyGameModeNotificationBus::Broadcast(
  471. &AzToolsFramework::EditorLegacyGameModeNotificationBus::Events::OnStopGameModeRequest);
  472. }
  473. }
  474. void CGameEngine::SetGameMode(bool bInGame)
  475. {
  476. if (m_bInGameMode == bInGame)
  477. {
  478. return;
  479. }
  480. if (!GetIEditor()->GetDocument())
  481. {
  482. return;
  483. }
  484. GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_GAME_MODE_SWITCH_START, bInGame, 0);
  485. // Enables engine to know about that.
  486. gEnv->SetIsEditorGameMode(bInGame);
  487. // Ignore updates while changing in and out of game mode
  488. m_bIgnoreUpdates = true;
  489. // Switching modes will destroy the current AzFramework::EntityConext which may contain
  490. // data the queued events hold on to, so execute all queued events before switching.
  491. ExecuteQueuedEvents();
  492. if (bInGame)
  493. {
  494. SwitchToInGame();
  495. }
  496. else
  497. {
  498. SwitchToInEditor();
  499. }
  500. // Enables engine to know about that.
  501. if (MainWindow::instance())
  502. {
  503. AzFramework::InputChannelRequestBus::Broadcast(&AzFramework::InputChannelRequests::ResetState);
  504. MainWindow::instance()->setFocus();
  505. }
  506. GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_EDITOR_GAME_MODE_CHANGED, bInGame, 0);
  507. m_bIgnoreUpdates = false;
  508. GetISystem()->GetISystemEventDispatcher()->OnSystemEvent(ESYSTEM_EVENT_GAME_MODE_SWITCH_END, bInGame, 0);
  509. }
  510. void CGameEngine::SetSimulationMode(bool enabled, bool bOnlyPhysics)
  511. {
  512. if (m_bSimulationMode == enabled)
  513. {
  514. return;
  515. }
  516. m_pISystem->GetIMovieSystem()->EnablePhysicsEvents(enabled);
  517. if (enabled)
  518. {
  519. GetIEditor()->Notify(eNotify_OnBeginSimulationMode);
  520. }
  521. else
  522. {
  523. GetIEditor()->Notify(eNotify_OnEndSimulationMode);
  524. }
  525. m_bSimulationMode = enabled;
  526. // Enables engine to know about simulation mode.
  527. gEnv->SetIsEditorSimulationMode(enabled);
  528. // Execute all queued events before switching modes.
  529. ExecuteQueuedEvents();
  530. // Transition back to editor entity context.
  531. // Symmetry is not critical. It's okay to call this even if we never called StartPlayInEditor
  532. // (bOnlyPhysics was true when we entered simulation mode).
  533. AzToolsFramework::EditorEntityContextRequestBus::Broadcast(&AzToolsFramework::EditorEntityContextRequestBus::Events::StopPlayInEditor);
  534. if (m_bSimulationMode && !bOnlyPhysics)
  535. {
  536. // Transition to runtime entity context.
  537. AzToolsFramework::EditorEntityContextRequestBus::Broadcast(&AzToolsFramework::EditorEntityContextRequestBus::Events::StartPlayInEditor);
  538. }
  539. AzFramework::InputChannelRequestBus::Broadcast(&AzFramework::InputChannelRequests::ResetState);
  540. }
  541. void CGameEngine::SetPlayerViewMatrix(const Matrix34& tm, [[maybe_unused]] bool bEyePos)
  542. {
  543. m_playerViewTM = tm;
  544. }
  545. void CGameEngine::SyncPlayerPosition(bool bEnable)
  546. {
  547. m_bSyncPlayerPosition = bEnable;
  548. if (m_bSyncPlayerPosition)
  549. {
  550. SetPlayerViewMatrix(m_playerViewTM);
  551. }
  552. }
  553. void CGameEngine::SetCurrentMOD(const char* sMod)
  554. {
  555. m_MOD = sMod;
  556. }
  557. QString CGameEngine::GetCurrentMOD() const
  558. {
  559. return m_MOD;
  560. }
  561. void CGameEngine::Update()
  562. {
  563. if (m_bIgnoreUpdates)
  564. {
  565. return;
  566. }
  567. switch (m_ePendingGameMode)
  568. {
  569. case ePGM_SwitchToInGame:
  570. {
  571. SetGameMode(true);
  572. m_ePendingGameMode = ePGM_NotPending;
  573. break;
  574. }
  575. case ePGM_SwitchToInEditor:
  576. {
  577. bool wasInSimulationMode = GetIEditor()->GetGameEngine()->GetSimulationMode();
  578. if (wasInSimulationMode)
  579. {
  580. GetIEditor()->GetGameEngine()->SetSimulationMode(false);
  581. }
  582. SetGameMode(false);
  583. if (wasInSimulationMode)
  584. {
  585. GetIEditor()->GetGameEngine()->SetSimulationMode(true);
  586. }
  587. m_ePendingGameMode = ePGM_NotPending;
  588. break;
  589. }
  590. }
  591. AZ::ComponentApplication* componentApplication = nullptr;
  592. AZ::ComponentApplicationBus::BroadcastResult(componentApplication, &AZ::ComponentApplicationBus::Events::GetApplication);
  593. if (m_bInGameMode)
  594. {
  595. if (gEnv->pSystem)
  596. {
  597. gEnv->pSystem->UpdatePreTickBus();
  598. componentApplication->Tick();
  599. gEnv->pSystem->UpdatePostTickBus();
  600. }
  601. if (CViewport* pRenderViewport = GetIEditor()->GetViewManager()->GetGameViewport())
  602. {
  603. pRenderViewport->Update();
  604. }
  605. // Check for the Escape key to exit game mode here rather than in Qt,
  606. // because all Qt events are usually filtered out in game mode in
  607. // QtEditorApplication_<platform>.cpp nativeEventFilter() to prevent
  608. // using Editor menu actions and shortcuts that shouldn't trigger while
  609. // playing the game.
  610. // When the user opens the console, Qt events will be allowed
  611. // so the user can interact with limited Editor content like the console.
  612. const AzFramework::InputChannel* inputChannel = nullptr;
  613. const AzFramework::InputChannelId channelId(AzFramework::InputDeviceKeyboard::Key::Escape);
  614. AzFramework::InputChannelRequestBus::EventResult(inputChannel, channelId, &AzFramework::InputChannelRequests::GetInputChannel);
  615. if(inputChannel && inputChannel->GetState() == AzFramework::InputChannel::State::Began)
  616. {
  617. // leave game mode
  618. RequestSetGameMode(false);
  619. }
  620. }
  621. else
  622. {
  623. // [marco] check current sound and vis areas for music etc.
  624. // but if in game mode, 'cos is already done in the above call to game->update()
  625. unsigned int updateFlags = ESYSUPDATE_EDITOR;
  626. GetIEditor()->GetAnimation()->Update();
  627. GetIEditor()->GetSystem()->UpdatePreTickBus(updateFlags);
  628. componentApplication->Tick();
  629. GetIEditor()->GetSystem()->UpdatePostTickBus(updateFlags);
  630. }
  631. }
  632. void CGameEngine::OnEditorNotifyEvent(EEditorNotifyEvent event)
  633. {
  634. switch (event)
  635. {
  636. case eNotify_OnSplashScreenDestroyed:
  637. {
  638. if (m_pSystemUserCallback != nullptr)
  639. {
  640. m_pSystemUserCallback->OnSplashScreenDone();
  641. }
  642. }
  643. break;
  644. }
  645. }
  646. void CGameEngine::OnAreaModified([[maybe_unused]] const AABB& modifiedArea)
  647. {
  648. }
  649. void CGameEngine::ExecuteQueuedEvents()
  650. {
  651. AZ::Data::AssetBus::ExecuteQueuedEvents();
  652. AZ::TickBus::ExecuteQueuedEvents();
  653. AZ::MainThreadRenderRequestBus::ExecuteQueuedEvents();
  654. }
  655. #include <moc_GameEngine.cpp>