NetworkPrefabSpawnerComponent.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project
  3. *
  4. * SPDX-License-Identifier: Apache-2.0 OR MIT
  5. *
  6. */
  7. #include "NetworkPrefabSpawnerComponent.h"
  8. #include <AzCore/Asset/AssetManagerBus.h>
  9. #include <AzCore/Asset/AssetSerializer.h>
  10. #include <AzCore/Serialization/EditContext.h>
  11. #include <AzCore/Serialization/SerializeContext.h>
  12. #include <AzCore/std/smart_ptr/make_shared.h>
  13. #include <AzFramework/Components/TransformComponent.h>
  14. #include <AzFramework/Spawnable/SpawnableEntitiesInterface.h>
  15. namespace MultiplayerSample
  16. {
  17. void NetworkPrefabSpawnerComponent::Reflect(AZ::ReflectContext* reflection)
  18. {
  19. if (const auto serializationContext = azrtti_cast<AZ::SerializeContext*>(reflection))
  20. {
  21. serializationContext->Class<NetworkPrefabSpawnerComponent, Component>()
  22. ->Field("Default Prefab", &NetworkPrefabSpawnerComponent::m_defaultSpawnableAsset)
  23. ->Version(1);
  24. if (const auto editContext = serializationContext->GetEditContext())
  25. {
  26. editContext->Class<NetworkPrefabSpawnerComponent>("Network Prefab Spawner",
  27. "Handles spawning of prefabs")
  28. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  29. ->Attribute(AZ::Edit::Attributes::Category, "MultiplayerSample")
  30. ->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/NetworkPrefabSpawner.svg")
  31. ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/NetworkPrefabSpawner.svg")
  32. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
  33. ->DataElement(nullptr, &NetworkPrefabSpawnerComponent::m_defaultSpawnableAsset, "Default Prefab", "Default prefab to spawn upon request.")
  34. ;
  35. }
  36. }
  37. }
  38. void NetworkPrefabSpawnerComponent::Activate()
  39. {
  40. if (NetworkPrefabSpawnerInterface::Get() == nullptr)
  41. {
  42. NetworkPrefabSpawnerInterface::Register(this);
  43. }
  44. NetworkPrefabSpawnerRequestBus::Handler::BusConnect(GetEntityId());
  45. // preload
  46. if (m_defaultSpawnableAsset.GetId().IsValid())
  47. {
  48. m_defaultSpawnableAsset.QueueLoad();
  49. }
  50. }
  51. void NetworkPrefabSpawnerComponent::Deactivate()
  52. {
  53. if (NetworkPrefabSpawnerInterface::Get() == this)
  54. {
  55. NetworkPrefabSpawnerInterface::Unregister(this);
  56. }
  57. NetworkPrefabSpawnerRequestBus::Handler::BusDisconnect();
  58. AZ::Data::AssetBus::MultiHandler::BusDisconnect();
  59. }
  60. void NetworkPrefabSpawnerComponent::SpawnDefaultPrefab(const AZ::Transform& worldTm, PrefabCallbacks callbacks)
  61. {
  62. AssetItem newAsset;
  63. newAsset.m_pathToAsset = m_defaultSpawnableAsset.GetHint().c_str();
  64. newAsset.m_spawnableAsset = m_defaultSpawnableAsset;
  65. AZ::Data::AssetBus::MultiHandler::BusConnect(m_defaultSpawnableAsset.GetId());
  66. if (newAsset.m_spawnableAsset.IsReady() == false)
  67. {
  68. newAsset.m_spawnableAsset.QueueLoad();
  69. }
  70. m_assetMap.emplace(newAsset.m_spawnableAsset.GetId(), newAsset);
  71. const SpawnRequest request{ newAsset.m_spawnableAsset.GetId(), worldTm, AZStd::move(callbacks) };
  72. if (newAsset.m_spawnableAsset.IsReady())
  73. {
  74. CreateInstance(request, &newAsset);
  75. }
  76. else
  77. {
  78. m_requests.push_back(request);
  79. }
  80. }
  81. void NetworkPrefabSpawnerComponent::SpawnPrefab(const AZ::Transform& worldTm, const char* assetPath, PrefabCallbacks callbacks)
  82. {
  83. const AZ::Data::AssetId assetId = GetSpawnableAssetId(assetPath);
  84. const SpawnRequest request{ assetId, worldTm, AZStd::move(callbacks) };
  85. auto foundAsset = m_assetMap.find(assetId);
  86. if (foundAsset != m_assetMap.end())
  87. {
  88. if (foundAsset->second.m_spawnableAsset.IsReady())
  89. {
  90. CreateInstance(request, &foundAsset->second);
  91. }
  92. else
  93. {
  94. m_requests.push_back(request);
  95. }
  96. }
  97. else
  98. {
  99. AssetItem newAsset;
  100. newAsset.m_pathToAsset = assetPath;
  101. newAsset.m_spawnableAsset.Create(assetId, false);
  102. AZ::Data::AssetBus::MultiHandler::BusConnect(assetId);
  103. newAsset.m_spawnableAsset.QueueLoad();
  104. m_assetMap.emplace(assetId, newAsset);
  105. if (newAsset.m_spawnableAsset.IsReady())
  106. {
  107. CreateInstance(request, &newAsset);
  108. }
  109. else
  110. {
  111. m_requests.push_back(request);
  112. }
  113. }
  114. }
  115. void NetworkPrefabSpawnerComponent::SpawnPrefabAsset(const AZ::Transform& worldTm,
  116. const AZ::Data::Asset<AzFramework::Spawnable>& asset, PrefabCallbacks callbacks)
  117. {
  118. AssetItem newAsset;
  119. newAsset.m_pathToAsset = asset.GetHint().c_str();
  120. newAsset.m_spawnableAsset = asset;
  121. AZ::Data::AssetBus::MultiHandler::BusConnect(asset.GetId());
  122. if (newAsset.m_spawnableAsset.IsReady() == false)
  123. {
  124. newAsset.m_spawnableAsset.QueueLoad();
  125. }
  126. m_assetMap.emplace(newAsset.m_spawnableAsset.GetId(), newAsset);
  127. const SpawnRequest request{ newAsset.m_spawnableAsset.GetId(), worldTm, AZStd::move(callbacks) };
  128. if (newAsset.m_spawnableAsset.IsReady())
  129. {
  130. CreateInstance(request, &newAsset);
  131. }
  132. else
  133. {
  134. m_requests.push_back(request);
  135. }
  136. }
  137. AZ::Data::AssetId NetworkPrefabSpawnerComponent::GetSpawnableAssetId(const char* assetPath) const
  138. {
  139. if (assetPath)
  140. {
  141. AZ::Data::AssetId assetId;
  142. AZ::Data::AssetCatalogRequestBus::BroadcastResult(
  143. assetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, assetPath,
  144. AZ::Data::s_invalidAssetType, false);
  145. if (assetId.IsValid())
  146. {
  147. return assetId;
  148. }
  149. }
  150. return {};
  151. }
  152. void NetworkPrefabSpawnerComponent::CreateInstance(const SpawnRequest& request, const AssetItem* asset)
  153. {
  154. AZ_Assert(asset, "AssetMap didn't contain the asset id for prefab spawning");
  155. AZ::Transform world = request.m_whereToSpawn;
  156. if (asset)
  157. {
  158. auto ticket = AZStd::make_shared<AzFramework::EntitySpawnTicket>(asset->m_spawnableAsset);
  159. auto preSpawnCallback = [world, request, ticket]([[maybe_unused]] AzFramework::EntitySpawnTicket::Id ticketId, AzFramework::SpawnableEntityContainerView view)
  160. {
  161. const AZ::Entity* rootEntity = *view.begin();
  162. if (AzFramework::TransformComponent* entityTransform = rootEntity->FindComponent<AzFramework::TransformComponent>())
  163. {
  164. entityTransform->SetWorldTM(world);
  165. }
  166. if (request.m_callbacks.m_beforeActivateCallback)
  167. {
  168. request.m_callbacks.m_beforeActivateCallback(ticket, view);
  169. }
  170. };
  171. auto onSpawnedCallback = [request, ticket]([[maybe_unused]] AzFramework::EntitySpawnTicket::Id ticketId, AzFramework::SpawnableConstEntityContainerView view)
  172. {
  173. if (request.m_callbacks.m_onActivateCallback)
  174. {
  175. request.m_callbacks.m_onActivateCallback(ticket, view);
  176. }
  177. };
  178. AZ_Assert(ticket->IsValid(), "Unable to instantiate spawnable asset");
  179. if (ticket->IsValid())
  180. {
  181. AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs;
  182. optionalArgs.m_preInsertionCallback = AZStd::move(preSpawnCallback);
  183. optionalArgs.m_completionCallback = AZStd::move(onSpawnedCallback);
  184. AzFramework::SpawnableEntitiesInterface::Get()->SpawnAllEntities(*ticket, AZStd::move(optionalArgs));
  185. }
  186. }
  187. }
  188. void NetworkPrefabSpawnerComponent::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
  189. {
  190. const AZ::Data::AssetId assetId = asset.GetId();
  191. AZ::Data::AssetBus::MultiHandler::BusDisconnect(assetId);
  192. const auto foundAsset = m_assetMap.find(assetId);
  193. if (foundAsset != m_assetMap.end())
  194. {
  195. for (auto requestIterator = m_requests.begin(); requestIterator < m_requests.end(); /*iterating inside the loop body*/)
  196. {
  197. const SpawnRequest& request = *requestIterator;
  198. if (request.m_assetIdToSpawn == assetId)
  199. {
  200. CreateInstance(request, &foundAsset->second);
  201. requestIterator = m_requests.erase(requestIterator);
  202. }
  203. else
  204. {
  205. ++requestIterator;
  206. }
  207. }
  208. }
  209. }
  210. }