GameEngine.cpp 24 KB

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