SpawnerComponent.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  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 <AzCore/Asset/AssetSerializer.h>
  9. #include <AzCore/Component/TickBus.h>
  10. #include <AzCore/Component/TransformBus.h>
  11. #include <AzCore/Serialization/SerializeContext.h>
  12. #include <AzCore/RTTI/BehaviorContext.h>
  13. #include <AzCore/std/sort.h>
  14. #include <AzCore/Asset/AssetManager.h>
  15. #include <AzFramework/API/ApplicationAPI.h>
  16. #include <AzFramework/Entity/GameEntityContextBus.h>
  17. #include <AzFramework/Entity/SliceGameEntityOwnershipServiceBus.h>
  18. #include "SpawnerComponent.h"
  19. #ifdef LMBR_CENTRAL_EDITOR
  20. #include "EditorSpawnerComponent.h"
  21. #endif
  22. namespace LmbrCentral
  23. {
  24. // BehaviorContext SpawnerComponentNotificationBus forwarder
  25. class BehaviorSpawnerComponentNotificationBusHandler : public SpawnerComponentNotificationBus::Handler, public AZ::BehaviorEBusHandler
  26. {
  27. public:
  28. AZ_EBUS_BEHAVIOR_BINDER(BehaviorSpawnerComponentNotificationBusHandler, "{AC202871-2522-48A6-9B62-5FDAABB302CD}", AZ::SystemAllocator,
  29. OnSpawnBegin, OnSpawnEnd, OnEntitySpawned, OnSpawnedSliceDestroyed, OnEntitiesSpawned);
  30. void OnSpawnBegin(const AzFramework::SliceInstantiationTicket& ticket) override
  31. {
  32. Call(FN_OnSpawnBegin, ticket);
  33. }
  34. void OnSpawnEnd(const AzFramework::SliceInstantiationTicket& ticket) override
  35. {
  36. Call(FN_OnSpawnEnd, ticket);
  37. }
  38. void OnEntitySpawned(const AzFramework::SliceInstantiationTicket& ticket, const AZ::EntityId& id) override
  39. {
  40. Call(FN_OnEntitySpawned, ticket, id);
  41. }
  42. void OnSpawnedSliceDestroyed(const AzFramework::SliceInstantiationTicket& ticket) override
  43. {
  44. Call(FN_OnSpawnedSliceDestroyed, ticket);
  45. }
  46. //! Single event notification for an entire slice spawn, providing a list of all resulting entity Ids.
  47. void OnEntitiesSpawned(const AzFramework::SliceInstantiationTicket& ticket, const AZStd::vector<AZ::EntityId>& spawnedEntities) override
  48. {
  49. Call(FN_OnEntitiesSpawned, ticket, spawnedEntities);
  50. }
  51. };
  52. // Convert any instances of the old SampleComponent data into the appropriate
  53. // modern editor-component or game-component.
  54. bool ConvertLegacySpawnerComponent(AZ::SerializeContext& serializeContext, AZ::SerializeContext::DataElementNode& classNode)
  55. {
  56. #ifdef LMBR_CENTRAL_EDITOR
  57. // To determine whether we want an editor or runtime component, we check
  58. // if the old component was contained within GenericComponentWrapper::m_template.
  59. bool isEditorComponent = (classNode.GetName() == AZ::Crc32("m_template"));
  60. #endif
  61. // Get Component::m_id from the base class.
  62. AZ::u64 componentId = 0;
  63. if (auto baseClassNode = classNode.FindSubElement(AZ::Crc32("BaseClass1")))
  64. {
  65. baseClassNode->GetChildData(AZ::Crc32("Id"), componentId);
  66. }
  67. // Get data values.
  68. SpawnerConfig config;
  69. classNode.GetChildData(AZ::Crc32("Slice"), config.m_sliceAsset);
  70. classNode.GetChildData(AZ::Crc32("SpawnOnActivate"), config.m_spawnOnActivate);
  71. classNode.GetChildData(AZ::Crc32("DestroyOnDeactivate"), config.m_destroyOnDeactivate);
  72. // Convert this node into the appropriate component-type.
  73. // Note that converting the node will clear all child data nodes.
  74. #ifdef LMBR_CENTRAL_EDITOR
  75. if (isEditorComponent)
  76. {
  77. classNode.Convert(serializeContext, azrtti_typeid<EditorSpawnerComponent>());
  78. // Create a temporary editor-component and write its contents to this node
  79. EditorSpawnerComponent component;
  80. component.SetId(componentId);
  81. component.SetConfiguration(config);
  82. classNode.SetData(serializeContext, component);
  83. }
  84. else
  85. #endif // LMBR_CENTRAL_EDITOR
  86. {
  87. classNode.Convert(serializeContext, azrtti_typeid<SpawnerComponent>());
  88. // Create a temporary game-component and write its contents to this classNode
  89. SpawnerComponent component;
  90. component.SetId(componentId);
  91. component.SetConfiguration(config);
  92. classNode.SetData(serializeContext, component);
  93. }
  94. return true;
  95. }
  96. //=========================================================================
  97. void SpawnerComponent::Reflect(AZ::ReflectContext* context)
  98. {
  99. AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
  100. if (serializeContext)
  101. {
  102. serializeContext->ClassDeprecate("SpawnerComponent", DeprecatedSpawnerComponentTypeId, ConvertLegacySpawnerComponent);
  103. serializeContext->Class<SpawnerComponent, AZ::Component>()
  104. ->Version(1)
  105. ->Field("Slice", &SpawnerComponent::m_sliceAsset)
  106. ->Field("SpawnOnActivate", &SpawnerComponent::m_spawnOnActivate)
  107. ->Field("DestroyOnDeactivate", &SpawnerComponent::m_destroyOnDeactivate)
  108. ;
  109. }
  110. }
  111. //=========================================================================
  112. void SpawnerComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
  113. {
  114. provided.push_back(AZ_CRC_CE("SpawnerService"));
  115. }
  116. //=========================================================================
  117. void SpawnerComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType&)
  118. {
  119. }
  120. //=========================================================================
  121. void SpawnerComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
  122. {
  123. dependent.push_back(AZ_CRC_CE("TransformService"));
  124. }
  125. //=========================================================================
  126. SpawnerComponent::SpawnerComponent()
  127. {
  128. // Slice asset should load purely on-demand.
  129. m_sliceAsset.SetAutoLoadBehavior(AZ::Data::AssetLoadBehavior::NoLoad);
  130. }
  131. //=========================================================================
  132. void SpawnerComponent::Activate()
  133. {
  134. SpawnerComponentRequestBus::Handler::BusConnect(GetEntityId());
  135. if (m_spawnOnActivate)
  136. {
  137. SpawnSliceInternalRelative(m_sliceAsset, AZ::Transform::Identity());
  138. }
  139. }
  140. //=========================================================================
  141. void SpawnerComponent::Deactivate()
  142. {
  143. SpawnerComponentRequestBus::Handler::BusDisconnect();
  144. AzFramework::SliceInstantiationResultBus::MultiHandler::BusDisconnect();
  145. AZ::EntityBus::MultiHandler::BusDisconnect();
  146. AZ::Data::AssetBus::Handler::BusDisconnect();
  147. if (m_destroyOnDeactivate)
  148. {
  149. DestroyAllSpawnedSlices();
  150. }
  151. m_activeTickets.clear();
  152. m_entityToTicketMap.clear();
  153. m_ticketToEntitiesMap.clear();
  154. }
  155. bool SpawnerComponent::ReadInConfig(const AZ::ComponentConfig* spawnerConfig)
  156. {
  157. if (auto config = azrtti_cast<const SpawnerConfig*>(spawnerConfig))
  158. {
  159. m_sliceAsset = config->m_sliceAsset;
  160. m_spawnOnActivate = config->m_spawnOnActivate;
  161. m_destroyOnDeactivate = config->m_destroyOnDeactivate;
  162. return true;
  163. }
  164. return false;
  165. }
  166. bool SpawnerComponent::WriteOutConfig(AZ::ComponentConfig* outSpawnerConfig) const
  167. {
  168. if (auto config = azrtti_cast<SpawnerConfig*>(outSpawnerConfig))
  169. {
  170. config->m_sliceAsset = m_sliceAsset;
  171. config->m_spawnOnActivate = m_spawnOnActivate;
  172. config->m_destroyOnDeactivate = m_destroyOnDeactivate;
  173. return true;
  174. }
  175. return false;
  176. }
  177. //=========================================================================
  178. void SpawnerComponent::SetDynamicSlice(const AZ::Data::Asset<AZ::DynamicSliceAsset>& dynamicSliceAsset)
  179. {
  180. m_sliceAsset = dynamicSliceAsset;
  181. }
  182. //=========================================================================
  183. void SpawnerComponent::SetDynamicSliceByAssetId(AZ::Data::AssetId& assetId)
  184. {
  185. if (m_sliceAsset.GetId() == assetId)
  186. {
  187. return;
  188. }
  189. m_sliceAsset = AZ::Data::AssetManager::Instance().GetAsset(
  190. assetId, AZ::AzTypeInfo<AZ::DynamicSliceAsset>::Uuid(), m_sliceAsset.GetAutoLoadBehavior());
  191. AZ::Data::AssetBus::Handler::BusDisconnect();
  192. AZ::Data::AssetBus::Handler::BusConnect(assetId);
  193. }
  194. //=========================================================================
  195. void SpawnerComponent::SetSpawnOnActivate(bool spawnOnActivate)
  196. {
  197. m_spawnOnActivate = spawnOnActivate;
  198. }
  199. //=========================================================================
  200. bool SpawnerComponent::GetSpawnOnActivate()
  201. {
  202. return m_spawnOnActivate;
  203. }
  204. //=========================================================================
  205. AzFramework::SliceInstantiationTicket SpawnerComponent::Spawn()
  206. {
  207. return SpawnSliceInternalRelative(m_sliceAsset, AZ::Transform::Identity());
  208. }
  209. //=========================================================================
  210. AzFramework::SliceInstantiationTicket SpawnerComponent::SpawnRelative(const AZ::Transform& relative)
  211. {
  212. return SpawnSliceInternalRelative(m_sliceAsset, relative);
  213. }
  214. //=========================================================================
  215. AzFramework::SliceInstantiationTicket SpawnerComponent::SpawnAbsolute(const AZ::Transform& world)
  216. {
  217. return SpawnSliceInternalAbsolute(m_sliceAsset, world);
  218. }
  219. //=========================================================================
  220. AzFramework::SliceInstantiationTicket SpawnerComponent::SpawnSlice(const AZ::Data::Asset<AZ::Data::AssetData>& slice)
  221. {
  222. return SpawnSliceInternalRelative(slice, AZ::Transform::Identity());
  223. }
  224. //=========================================================================
  225. AzFramework::SliceInstantiationTicket SpawnerComponent::SpawnSliceRelative(const AZ::Data::Asset<AZ::Data::AssetData>& slice, const AZ::Transform& relative)
  226. {
  227. return SpawnSliceInternalRelative(slice, relative);
  228. }
  229. //=========================================================================
  230. AzFramework::SliceInstantiationTicket SpawnerComponent::SpawnSliceAbsolute(const AZ::Data::Asset<AZ::Data::AssetData>& slice, const AZ::Transform& world)
  231. {
  232. return SpawnSliceInternalAbsolute(slice, world);
  233. }
  234. //=========================================================================
  235. AzFramework::SliceInstantiationTicket SpawnerComponent::SpawnSliceInternalAbsolute(const AZ::Data::Asset<AZ::Data::AssetData>& slice, const AZ::Transform& world)
  236. {
  237. AzFramework::SliceInstantiationTicket ticket;
  238. AzFramework::SliceGameEntityOwnershipServiceRequestBus::BroadcastResult(ticket,
  239. &AzFramework::SliceGameEntityOwnershipServiceRequests::InstantiateDynamicSlice, slice, world, nullptr);
  240. if (ticket.IsValid())
  241. {
  242. m_activeTickets.emplace_back(ticket);
  243. m_ticketToEntitiesMap.emplace(ticket); // create entry for ticket, with no entities listed yet
  244. AzFramework::SliceInstantiationResultBus::MultiHandler::BusConnect(ticket);
  245. }
  246. return ticket;
  247. }
  248. //=========================================================================
  249. AzFramework::SliceInstantiationTicket SpawnerComponent::SpawnSliceInternalRelative(const AZ::Data::Asset<AZ::Data::AssetData>& slice, const AZ::Transform& relative)
  250. {
  251. AZ::Transform transform = AZ::Transform::Identity();
  252. AZ::TransformBus::EventResult(transform, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
  253. transform *= relative;
  254. return SpawnSliceInternalAbsolute(slice, transform);
  255. }
  256. //=========================================================================
  257. void SpawnerComponent::DestroySpawnedSlice(const AzFramework::SliceInstantiationTicket& sliceTicket)
  258. {
  259. auto foundTicketEntities = m_ticketToEntitiesMap.find(sliceTicket);
  260. if (foundTicketEntities != m_ticketToEntitiesMap.end())
  261. {
  262. AZStd::unordered_set<AZ::EntityId>& entitiesInSlice = foundTicketEntities->second;
  263. AzFramework::SliceInstantiationResultBus::MultiHandler::BusDisconnect(sliceTicket); // don't care anymore about events from this ticket
  264. if (entitiesInSlice.empty())
  265. {
  266. AzFramework::SliceGameEntityOwnershipServiceRequestBus::Broadcast(
  267. &AzFramework::SliceGameEntityOwnershipServiceRequestBus::Events::CancelDynamicSliceInstantiation, sliceTicket);
  268. }
  269. else
  270. {
  271. for (AZ::EntityId entity : entitiesInSlice)
  272. {
  273. AZ::EntityBus::MultiHandler::BusDisconnect(entity); // don't care anymore about events from this entity
  274. m_entityToTicketMap.erase(entity);
  275. }
  276. AzFramework::SliceGameEntityOwnershipServiceRequestBus::Broadcast(
  277. &AzFramework::SliceGameEntityOwnershipServiceRequests::DestroyDynamicSliceByEntity, *entitiesInSlice.begin());
  278. }
  279. m_ticketToEntitiesMap.erase(foundTicketEntities);
  280. m_activeTickets.erase(AZStd::remove(m_activeTickets.begin(), m_activeTickets.end(), sliceTicket), m_activeTickets.end());
  281. // slice destruction is queued, so queue the notification as well.
  282. AZ::EntityId entityId = GetEntityId();
  283. AZ::TickBus::QueueFunction(
  284. [entityId, sliceTicket]() // use copies, in case 'this' is destroyed
  285. {
  286. SpawnerComponentNotificationBus::Event(entityId, &SpawnerComponentNotificationBus::Events::OnSpawnedSliceDestroyed, sliceTicket);
  287. });
  288. }
  289. }
  290. //=========================================================================
  291. void SpawnerComponent::DestroyAllSpawnedSlices()
  292. {
  293. AZStd::vector<AzFramework::SliceInstantiationTicket> activeTickets = m_activeTickets; // iterate over a copy of the vector
  294. for (AzFramework::SliceInstantiationTicket& ticket : activeTickets)
  295. {
  296. DestroySpawnedSlice(ticket);
  297. }
  298. AZ_Assert(m_activeTickets.empty(), "SpawnerComponent::DestroyAllSpawnedSlices - tickets still listed");
  299. AZ_Assert(m_entityToTicketMap.empty(), "SpawnerComponent::DestroyAllSpawnedSlices - entities still listed");
  300. AZ_Assert(m_ticketToEntitiesMap.empty(), "SpawnerComponent::DestroyAllSpawnedSlices - ticket entities still listed");
  301. }
  302. //=========================================================================
  303. AZStd::vector<AzFramework::SliceInstantiationTicket> SpawnerComponent::GetCurrentlySpawnedSlices()
  304. {
  305. return m_activeTickets;
  306. }
  307. //=========================================================================
  308. bool SpawnerComponent::HasAnyCurrentlySpawnedSlices()
  309. {
  310. return !m_activeTickets.empty();
  311. }
  312. //=========================================================================
  313. AZStd::vector<AZ::EntityId> SpawnerComponent::GetCurrentEntitiesFromSpawnedSlice(const AzFramework::SliceInstantiationTicket& ticket)
  314. {
  315. AZStd::vector<AZ::EntityId> entities;
  316. auto foundTicketEntities = m_ticketToEntitiesMap.find(ticket);
  317. if (foundTicketEntities != m_ticketToEntitiesMap.end())
  318. {
  319. const AZStd::unordered_set<AZ::EntityId>& ticketEntities = foundTicketEntities->second;
  320. AZ_Warning("SpawnerComponent", !ticketEntities.empty(), "SpawnerComponent::GetCurrentEntitiesFromSpawnedSlice - Spawn has not completed, its entities are not available.");
  321. entities.reserve(ticketEntities.size());
  322. entities.insert(entities.end(), ticketEntities.begin(), ticketEntities.end());
  323. // Sort entities so that results are stable.
  324. AZStd::sort(entities.begin(), entities.end());
  325. }
  326. return entities;
  327. }
  328. //=========================================================================
  329. AZStd::vector<AZ::EntityId> SpawnerComponent::GetAllCurrentlySpawnedEntities()
  330. {
  331. AZStd::vector<AZ::EntityId> entities;
  332. entities.reserve(m_entityToTicketMap.size());
  333. // Return entities in the order their tickets spawned.
  334. // It's not a requirement, but it seems nice to do.
  335. for (const AzFramework::SliceInstantiationTicket& ticket : m_activeTickets)
  336. {
  337. const AZStd::unordered_set<AZ::EntityId>& ticketEntities = m_ticketToEntitiesMap[ticket];
  338. entities.insert(entities.end(), ticketEntities.begin(), ticketEntities.end());
  339. // Sort entities from a given ticket, so that results are stable.
  340. AZStd::sort(entities.end() - ticketEntities.size(), entities.end());
  341. }
  342. return entities;
  343. }
  344. //=========================================================================
  345. bool SpawnerComponent::IsReadyToSpawn()
  346. {
  347. return m_sliceAsset.IsReady();
  348. }
  349. //=========================================================================
  350. void SpawnerComponent::OnSlicePreInstantiate(const AZ::Data::AssetId& /*sliceAssetId*/, [[maybe_unused]] const AZ::SliceComponent::SliceInstanceAddress& sliceAddress)
  351. {
  352. const AzFramework::SliceInstantiationTicket ticket = (*AzFramework::SliceInstantiationResultBus::GetCurrentBusId());
  353. SpawnerComponentNotificationBus::Event(GetEntityId(), &SpawnerComponentNotificationBus::Events::OnSpawnBegin, ticket);
  354. }
  355. //=========================================================================
  356. void SpawnerComponent::OnSliceInstantiated([[maybe_unused]] const AZ::Data::AssetId& sliceAssetId, const AZ::SliceComponent::SliceInstanceAddress& sliceAddress)
  357. {
  358. const AzFramework::SliceInstantiationTicket ticket = (*AzFramework::SliceInstantiationResultBus::GetCurrentBusId());
  359. // Stop listening for this ticket (since it's done). We can have have multiple tickets in flight.
  360. AzFramework::SliceInstantiationResultBus::MultiHandler::BusDisconnect(ticket);
  361. const AZ::SliceComponent::EntityList& entities = sliceAddress.GetInstance()->GetInstantiated()->m_entities;
  362. AZStd::vector<AZ::EntityId> entityIds;
  363. entityIds.reserve(entities.size());
  364. AZStd::unordered_set<AZ::EntityId>& ticketEntities = m_ticketToEntitiesMap[ticket];
  365. for (AZ::Entity* currEntity : entities)
  366. {
  367. AZ::EntityId currEntityId = currEntity->GetId();
  368. entityIds.emplace_back(currEntityId);
  369. // update internal slice tracking data
  370. ticketEntities.emplace(currEntityId);
  371. m_entityToTicketMap.emplace(AZStd::make_pair(currEntityId, ticket));
  372. AZ::EntityBus::MultiHandler::BusConnect(currEntityId);
  373. SpawnerComponentNotificationBus::Event(
  374. GetEntityId(), &SpawnerComponentNotificationBus::Events::OnEntitySpawned, ticket, currEntityId);
  375. }
  376. SpawnerComponentNotificationBus::Event(GetEntityId(), &SpawnerComponentNotificationBus::Events::OnSpawnEnd, ticket);
  377. SpawnerComponentNotificationBus::Event(
  378. GetEntityId(), &SpawnerComponentNotificationBus::Events::OnEntitiesSpawned, ticket, entityIds);
  379. // If slice had no entities, clean it up
  380. if (entities.empty())
  381. {
  382. DestroySpawnedSlice(ticket);
  383. }
  384. }
  385. //=========================================================================
  386. void SpawnerComponent::OnSliceInstantiationFailedOrCanceled(const AZ::Data::AssetId& sliceAssetId, bool canceled)
  387. {
  388. const AzFramework::SliceInstantiationTicket ticket = *AzFramework::SliceInstantiationResultBus::GetCurrentBusId();
  389. AzFramework::SliceInstantiationResultBus::MultiHandler::BusDisconnect(ticket);
  390. // clean it up
  391. DestroySpawnedSlice(ticket);
  392. if (!canceled)
  393. {
  394. // error msg
  395. if (sliceAssetId == m_sliceAsset.GetId())
  396. {
  397. AZ_Error("SpawnerComponent", false, "Slice %s failed to instantiate", m_sliceAsset.ToString<AZStd::string>().c_str());
  398. }
  399. else
  400. {
  401. AZ_Error("SpawnerComponent", false, "Slice [id:'%s'] failed to instantiate", sliceAssetId.ToString<AZStd::string>().c_str());
  402. }
  403. }
  404. }
  405. //=========================================================================
  406. void SpawnerComponent::OnEntityDestruction(const AZ::EntityId& entityId)
  407. {
  408. AZ::EntityBus::MultiHandler::BusDisconnect(entityId);
  409. auto entityToTicketIter = m_entityToTicketMap.find(entityId);
  410. if (entityToTicketIter != m_entityToTicketMap.end())
  411. {
  412. AzFramework::SliceInstantiationTicket ticket = entityToTicketIter->second;
  413. m_entityToTicketMap.erase(entityToTicketIter);
  414. AZStd::unordered_set<AZ::EntityId>& ticketEntities = m_ticketToEntitiesMap[ticket];
  415. ticketEntities.erase(entityId);
  416. // If this was last entity in the spawn, clean it up
  417. if (ticketEntities.empty())
  418. {
  419. DestroySpawnedSlice(ticket);
  420. }
  421. }
  422. }
  423. void SpawnerComponent::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
  424. {
  425. AZ::Data::AssetBus::Handler::BusDisconnect();
  426. m_sliceAsset = asset;
  427. }
  428. } // namespace LmbrCentral