UiGameEntityContext.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  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 "UiGameEntityContext.h"
  9. #include <AzCore/Serialization/Utils.h>
  10. #include <LyShine/Bus/UiCanvasBus.h>
  11. #include <LyShine/Bus/UiElementBus.h>
  12. #include <LyShine/Bus/UiTransformBus.h>
  13. #include <LyShine/Bus/UiTransform2dBus.h>
  14. #include <LyShine/UiComponentTypes.h>
  15. ////////////////////////////////////////////////////////////////////////////////////////////////////
  16. UiGameEntityContext::UiGameEntityContext(AZ::EntityId canvasEntityId)
  17. : m_canvasEntityId(canvasEntityId)
  18. {
  19. }
  20. ////////////////////////////////////////////////////////////////////////////////////////////////////
  21. UiGameEntityContext::~UiGameEntityContext()
  22. {
  23. }
  24. ////////////////////////////////////////////////////////////////////////////////////////////////////
  25. bool UiGameEntityContext::HandleLoadedRootSliceEntity(AZ::Entity* rootEntity, bool remapIds, AZ::SliceComponent::EntityIdToEntityIdMap* idRemapTable)
  26. {
  27. AZ_Assert(m_entityOwnershipService->IsInitialized(), "The context has not been initialized.");
  28. bool rootEntityReloadSuccessful = false;
  29. AzFramework::SliceEntityOwnershipServiceRequestBus::EventResult(rootEntityReloadSuccessful, GetContextId(),
  30. &AzFramework::SliceEntityOwnershipServiceRequestBus::Events::HandleRootEntityReloadedFromStream, rootEntity, remapIds, idRemapTable);
  31. if (!rootEntityReloadSuccessful)
  32. {
  33. return false;
  34. }
  35. AZ::SliceComponent::EntityList entities;
  36. m_entityOwnershipService->GetAllEntities(entities);
  37. AzFramework::SliceEntityOwnershipServiceRequestBus::Event(GetContextId(),
  38. &AzFramework::SliceEntityOwnershipServiceRequests::SetIsDynamic, true);
  39. InitializeEntities(entities);
  40. return true;
  41. }
  42. ////////////////////////////////////////////////////////////////////////////////////////////////////
  43. AZ::Entity* UiGameEntityContext::CreateUiEntity(const char* name)
  44. {
  45. AZ::Entity* entity = CreateEntity(name);
  46. if (entity)
  47. {
  48. // we don't currently do anything extra here, UI entities are not automatically
  49. // Init'ed and Activate'd when they are created. We wait until the required components
  50. // are added before Init and Activate
  51. }
  52. return entity;
  53. }
  54. ////////////////////////////////////////////////////////////////////////////////////////////////////
  55. void UiGameEntityContext::AddUiEntity(AZ::Entity* entity)
  56. {
  57. AZ_Assert(entity, "Supplied entity is invalid.");
  58. AddEntity(entity);
  59. }
  60. ////////////////////////////////////////////////////////////////////////////////////////////////////
  61. void UiGameEntityContext::AddUiEntities(const AzFramework::EntityList& entities)
  62. {
  63. for (AZ::Entity* entity : entities)
  64. {
  65. AZ_Assert(!AzFramework::EntityIdContextQueryBus::MultiHandler::BusIsConnectedId(entity->GetId()), "Entity already in context.");
  66. AzFramework::RootSliceAsset rootSliceAsset;
  67. AzFramework::SliceEntityOwnershipServiceRequestBus::EventResult(rootSliceAsset, GetContextId(),
  68. &AzFramework::SliceEntityOwnershipServiceRequestBus::Events::GetRootAsset);
  69. rootSliceAsset->GetComponent()->AddEntity(entity);
  70. }
  71. m_entityOwnershipService->HandleEntitiesAdded(entities);
  72. }
  73. ////////////////////////////////////////////////////////////////////////////////////////////////////
  74. bool UiGameEntityContext::CloneUiEntities(const AZStd::vector<AZ::EntityId>& sourceEntities, AzFramework::EntityList& resultEntities)
  75. {
  76. resultEntities.clear();
  77. AZ::SliceComponent::InstantiatedContainer sourceObjects(false);
  78. for (const AZ::EntityId& id : sourceEntities)
  79. {
  80. AZ::Entity* entity = nullptr;
  81. AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, id);
  82. if (entity)
  83. {
  84. sourceObjects.m_entities.push_back(entity);
  85. }
  86. }
  87. AZ::SliceComponent::EntityIdToEntityIdMap idMap;
  88. AZ::SliceComponent::InstantiatedContainer* clonedObjects =
  89. AZ::IdUtils::Remapper<AZ::EntityId>::CloneObjectAndGenerateNewIdsAndFixRefs(&sourceObjects, idMap);
  90. if (!clonedObjects)
  91. {
  92. AZ_Error("UiEntityContext", false, "Failed to clone source entities.");
  93. return false;
  94. }
  95. resultEntities = clonedObjects->m_entities;
  96. AddUiEntities(resultEntities);
  97. clonedObjects->m_deleteEntitiesOnDestruction = false;
  98. delete clonedObjects;
  99. return true;
  100. }
  101. ////////////////////////////////////////////////////////////////////////////////////////////////////
  102. bool UiGameEntityContext::DestroyUiEntity(AZ::EntityId entityId)
  103. {
  104. return EntityContext::DestroyEntityById(entityId);
  105. }
  106. ////////////////////////////////////////////////////////////////////////////////////////////////////
  107. bool UiGameEntityContext::DestroyEntity(AZ::Entity* entity)
  108. {
  109. AZ_Assert(entity, "Invalid entity passed to DestroyEntity");
  110. AZ_Assert(m_entityOwnershipService->IsInitialized(), "The context has not been initialized.");
  111. AzFramework::EntityContextId owningContextId = AzFramework::EntityContextId::CreateNull();
  112. AzFramework::EntityIdContextQueryBus::EventResult(
  113. owningContextId, entity->GetId(), &AzFramework::EntityIdContextQueryBus::Events::GetOwningContextId);
  114. AZ_Assert(owningContextId == m_contextId, "Entity does not belong to this context, and therefore can not be safely destroyed by this context.");
  115. if (owningContextId == m_contextId)
  116. {
  117. HandleEntitiesRemoved({ entity->GetId() });
  118. AzFramework::RootSliceAsset rootSliceAsset;
  119. AzFramework::SliceEntityOwnershipServiceRequestBus::EventResult(rootSliceAsset, GetContextId(),
  120. &AzFramework::SliceEntityOwnershipServiceRequestBus::Events::GetRootAsset);
  121. rootSliceAsset->GetComponent()->RemoveEntity(entity);
  122. return true;
  123. }
  124. return false;
  125. }
  126. ////////////////////////////////////////////////////////////////////////////////////////////////////
  127. void UiGameEntityContext::InitUiContext()
  128. {
  129. m_entityOwnershipService = AZStd::make_unique<AzFramework::SliceEntityOwnershipService>(GetContextId(), GetSerializeContext());
  130. InitContext();
  131. m_entityOwnershipService->InstantiateAllPrefabs();
  132. UiEntityContextRequestBus::Handler::BusConnect(GetContextId());
  133. UiGameEntityContextBus::Handler::BusConnect(GetContextId());
  134. }
  135. ////////////////////////////////////////////////////////////////////////////////////////////////////
  136. void UiGameEntityContext::DestroyUiContext()
  137. {
  138. UiEntityContextRequestBus::Handler::BusDisconnect();
  139. UiGameEntityContextBus::Handler::BusDisconnect();
  140. DestroyContext();
  141. }
  142. ////////////////////////////////////////////////////////////////////////////////////////////////////
  143. bool UiGameEntityContext::SaveToStreamForGame(AZ::IO::GenericStream& stream, AZ::DataStream::StreamType streamType)
  144. {
  145. AzFramework::RootSliceAsset rootSliceAsset;
  146. AzFramework::SliceEntityOwnershipServiceRequestBus::EventResult(rootSliceAsset, GetContextId(),
  147. &AzFramework::SliceEntityOwnershipServiceRequestBus::Events::GetRootAsset);
  148. if (!rootSliceAsset)
  149. {
  150. return false;
  151. }
  152. AZ::Entity* rootSliceEntity = rootSliceAsset->GetEntity();
  153. return AZ::Utils::SaveObjectToStream<AZ::Entity>(stream, streamType, rootSliceEntity);
  154. }
  155. ////////////////////////////////////////////////////////////////////////////////////////////////////
  156. bool UiGameEntityContext::SaveCanvasEntityToStreamForGame(AZ::Entity* canvasEntity, AZ::IO::GenericStream& stream, AZ::DataStream::StreamType streamType)
  157. {
  158. if (!canvasEntity)
  159. {
  160. return false;
  161. }
  162. return AZ::Utils::SaveObjectToStream<AZ::Entity>(stream, streamType, canvasEntity);
  163. }
  164. ////////////////////////////////////////////////////////////////////////////////////////////////////
  165. void UiGameEntityContext::OnContextEntitiesAdded(const AzFramework::EntityList& entities)
  166. {
  167. EntityContext::OnContextEntitiesAdded(entities);
  168. InitializeEntities(entities);
  169. }
  170. ////////////////////////////////////////////////////////////////////////////////////////////////////
  171. void UiGameEntityContext::InitializeEntities(const AzFramework::EntityList& entities)
  172. {
  173. // UI entities are now automatically activated on creation
  174. for (AZ::Entity* entity : entities)
  175. {
  176. if (entity->GetState() == AZ::Entity::State::Constructed)
  177. {
  178. entity->Init();
  179. }
  180. }
  181. for (AZ::Entity* entity : entities)
  182. {
  183. if (entity->GetState() == AZ::Entity::State::Init)
  184. {
  185. entity->Activate();
  186. }
  187. }
  188. }
  189. //////////////////////////////////////////////////////////////////////////
  190. bool UiGameEntityContext::ValidateEntitiesAreValidForContext(const AzFramework::EntityList& entities)
  191. {
  192. // All entities in a slice being instantiated in the UI editor should
  193. // have the UiElementComponent on them.
  194. for (AZ::Entity* entity : entities)
  195. {
  196. if (!entity->FindComponent(LyShine::UiElementComponentUuid))
  197. {
  198. return false;
  199. }
  200. }
  201. return true;
  202. }
  203. ////////////////////////////////////////////////////////////////////////////////////////////////////
  204. AzFramework::SliceInstantiationTicket UiGameEntityContext::InstantiateDynamicSlice(
  205. const AZ::Data::Asset<AZ::Data::AssetData>& sliceAsset, const AZ::Vector2& position, bool isViewportPosition,
  206. AZ::Entity* parent, const AZ::IdUtils::Remapper<AZ::EntityId>::IdMapper& customIdMapper)
  207. {
  208. if (sliceAsset.GetId().IsValid())
  209. {
  210. AzFramework::SliceInstantiationTicket ticket;
  211. AzFramework::SliceEntityOwnershipServiceRequestBus::EventResult(ticket, GetContextId(),
  212. &AzFramework::SliceEntityOwnershipServiceRequests::InstantiateSlice, sliceAsset, customIdMapper, nullptr);
  213. if (ticket.IsValid())
  214. {
  215. auto it = m_instantiatingDynamicSlices.emplace(ticket, InstantiatingDynamicSlice(sliceAsset, position, isViewportPosition, parent));
  216. bool inserted = it.second;
  217. AZ_Error("UiEntityContext", inserted, "InstantiateDynamicSlice failed because the key already exists.");
  218. if (inserted)
  219. {
  220. AzFramework::SliceInstantiationResultBus::MultiHandler::BusConnect(ticket);
  221. return ticket;
  222. }
  223. }
  224. }
  225. return AzFramework::SliceInstantiationTicket();
  226. }
  227. ////////////////////////////////////////////////////////////////////////////////////////////////////
  228. void UiGameEntityContext::OnSlicePreInstantiate(const AZ::Data::AssetId& sliceAssetId, const AZ::SliceComponent::SliceInstanceAddress& sliceAddress)
  229. {
  230. const AzFramework::SliceInstantiationTicket ticket = *AzFramework::SliceInstantiationResultBus::GetCurrentBusId();
  231. auto instantiatingIter = m_instantiatingDynamicSlices.find(ticket);
  232. if (instantiatingIter != m_instantiatingDynamicSlices.end())
  233. {
  234. const AZ::SliceComponent::EntityList& entities = sliceAddress.GetInstance()->GetInstantiated()->m_entities;
  235. // If the context was loaded from a stream and Ids were remapped, fix up entity Ids in that slice that
  236. // point to entities in the stream (i.e. level entities).
  237. AZ::SliceComponent::EntityIdToEntityIdMap loadedEntityIdMap;
  238. AzFramework::SliceEntityOwnershipServiceRequestBus::EventResult(loadedEntityIdMap, GetContextId(),
  239. &AzFramework::SliceEntityOwnershipServiceRequestBus::Events::GetLoadedEntityIdMap);
  240. if (!loadedEntityIdMap.empty())
  241. {
  242. AZ::SliceComponent::InstantiatedContainer instanceEntities(false);
  243. instanceEntities.m_entities = entities;
  244. AZ::IdUtils::Remapper<AZ::EntityId>::RemapIds(&instanceEntities,
  245. [this](const AZ::EntityId& originalId, bool isEntityId, const AZStd::function<AZ::EntityId()>&) -> AZ::EntityId
  246. {
  247. if (!isEntityId)
  248. {
  249. AZ::SliceComponent::EntityIdToEntityIdMap loadedEntityIdMap;
  250. AzFramework::SliceEntityOwnershipServiceRequestBus::EventResult(loadedEntityIdMap, GetContextId(),
  251. &AzFramework::SliceEntityOwnershipServiceRequestBus::Events::GetLoadedEntityIdMap);
  252. auto iter = loadedEntityIdMap.find(originalId);
  253. if (iter != loadedEntityIdMap.end())
  254. {
  255. return iter->second;
  256. }
  257. }
  258. return originalId;
  259. }, m_serializeContext, false);
  260. }
  261. UiGameEntityContextSliceInstantiationResultsBus::Event(
  262. ticket,
  263. &UiGameEntityContextSliceInstantiationResultsBus::Events::OnEntityContextSlicePreInstantiate,
  264. sliceAssetId,
  265. sliceAddress);
  266. }
  267. }
  268. ////////////////////////////////////////////////////////////////////////////////////////////////////
  269. void UiGameEntityContext::OnSliceInstantiated(const AZ::Data::AssetId& sliceAssetId, const AZ::SliceComponent::SliceInstanceAddress& instance)
  270. {
  271. const AzFramework::SliceInstantiationTicket ticket = *AzFramework::SliceInstantiationResultBus::GetCurrentBusId();
  272. AzFramework::SliceInstantiationResultBus::MultiHandler::BusDisconnect(ticket);
  273. auto instantiatingIter = m_instantiatingDynamicSlices.find(ticket);
  274. if (instantiatingIter != m_instantiatingDynamicSlices.end())
  275. {
  276. InstantiatingDynamicSlice& instantiating = instantiatingIter->second;
  277. const AZ::SliceComponent::EntityList& entities = instance.GetInstance()->GetInstantiated()->m_entities;
  278. // It's possible that this dynamic slice only contains editor-only elements
  279. if (entities.empty())
  280. {
  281. return;
  282. }
  283. // Create a set of all the top-level entities.
  284. AZStd::unordered_set<AZ::Entity*> topLevelEntities;
  285. for (AZ::Entity* entity : entities)
  286. {
  287. topLevelEntities.insert(entity);
  288. }
  289. // remove anything from the topLevelEntities set that is referenced as the child of another element in the list
  290. for (AZ::Entity* entity : entities)
  291. {
  292. LyShine::EntityArray children;
  293. UiElementBus::EventResult(children, entity->GetId(), &UiElementBus::Events::GetChildElements);
  294. for (auto child : children)
  295. {
  296. topLevelEntities.erase(child);
  297. }
  298. }
  299. // This can be null is nothing is selected. That is OK, the usage of it below treats that as meaning
  300. // add as a child of the root element.
  301. AZ::Entity* parent = instantiating.m_parent;
  302. // Now topLevelElements contains all of the top-level elements in the set of newly instantiated entities
  303. // Copy the topLevelEntities set into a list
  304. LyShine::EntityArray entitiesToInit;
  305. for (auto entity : topLevelEntities)
  306. {
  307. entitiesToInit.push_back(entity);
  308. }
  309. // There must be at least one element
  310. AZ_Assert(entitiesToInit.size() >= 1, "There must be at least one top-level entity in a UI slice.");
  311. // Initialize the internal parent pointers and the canvas pointer in the elements
  312. // We do this before adding the elements, otherwise the GetUniqueChildName code in FixupCreatedEntities will
  313. // already see the new elements and think the names are not unique
  314. UiCanvasBus::Event(m_canvasEntityId, &UiCanvasBus::Events::FixupCreatedEntities, entitiesToInit, true, parent);
  315. // Add all of the top-level entities as children of the parent
  316. for (auto entity : topLevelEntities)
  317. {
  318. UiCanvasBus::Event(m_canvasEntityId, &UiCanvasBus::Events::AddElement, entity, parent, nullptr);
  319. }
  320. // Here we adjust the position of the instantiated entities. Depending on how the dynamic slice
  321. // was spawned we position it at a viewport position or a relative position.
  322. if (instantiating.m_isViewportPosition)
  323. {
  324. const AZ::Vector2& desiredViewportPosition = instantiating.m_position;
  325. AZ::Entity* rootElement = entitiesToInit[0];
  326. // Transform pivot position to canvas space
  327. AZ::Vector2 pivotPos;
  328. UiTransformBus::EventResult(pivotPos, rootElement->GetId(), &UiTransformBus::Events::GetCanvasSpacePivotNoScaleRotate);
  329. // Transform destination position to canvas space
  330. AZ::Matrix4x4 transformFromViewport;
  331. UiTransformBus::Event(rootElement->GetId(), &UiTransformBus::Events::GetTransformFromViewport, transformFromViewport);
  332. AZ::Vector3 destPos3 = transformFromViewport * AZ::Vector3(desiredViewportPosition.GetX(), desiredViewportPosition.GetY(), 0.0f);
  333. AZ::Vector2 destPos(destPos3.GetX(), destPos3.GetY());
  334. AZ::Vector2 offsetDelta = destPos - pivotPos;
  335. // Adjust offsets on all top level elements
  336. for (auto entity : entitiesToInit)
  337. {
  338. UiTransform2dInterface::Offsets offsets;
  339. UiTransform2dBus::EventResult(offsets, entity->GetId(), &UiTransform2dBus::Events::GetOffsets);
  340. UiTransform2dBus::Event(entity->GetId(), &UiTransform2dBus::Events::SetOffsets, offsets + offsetDelta);
  341. }
  342. }
  343. else if (!instantiating.m_position.IsZero())
  344. {
  345. AZ::Entity* rootElement = entitiesToInit[0];
  346. UiTransformBus::Event(rootElement->GetId(), &UiTransformBus::Events::MoveLocalPositionBy, instantiating.m_position);
  347. }
  348. // must erase this in case our instantiate calls trigger a slice spawn which would invalid this iterator.
  349. m_instantiatingDynamicSlices.erase(instantiatingIter);
  350. // This allows the UiSpawnerComponent to respond after the entities have been activated and fixed up
  351. UiGameEntityContextSliceInstantiationResultsBus::Event(
  352. ticket, &UiGameEntityContextSliceInstantiationResultsBus::Events::OnEntityContextSliceInstantiated, sliceAssetId, instance);
  353. UiGameEntityContextNotificationBus::Broadcast(
  354. &UiGameEntityContextNotificationBus::Events::OnSliceInstantiated, sliceAssetId, instance, ticket);
  355. }
  356. }
  357. ////////////////////////////////////////////////////////////////////////////////////////////////////
  358. void UiGameEntityContext::OnSliceInstantiationFailed(const AZ::Data::AssetId& sliceAssetId)
  359. {
  360. const AzFramework::SliceInstantiationTicket ticket = *AzFramework::SliceInstantiationResultBus::GetCurrentBusId();
  361. AzFramework::SliceInstantiationResultBus::MultiHandler::BusDisconnect(ticket);
  362. if (m_instantiatingDynamicSlices.erase(ticket) > 0)
  363. {
  364. UiGameEntityContextSliceInstantiationResultsBus::Event(
  365. ticket, &UiGameEntityContextSliceInstantiationResultsBus::Events::OnEntityContextSliceInstantiationFailed, sliceAssetId);
  366. UiGameEntityContextNotificationBus::Broadcast(
  367. &UiGameEntityContextNotificationBus::Events::OnSliceInstantiationFailed, sliceAssetId, ticket);
  368. }
  369. }