-
Notifications
You must be signed in to change notification settings - Fork 3
[2409] Csv Spawner Notification Bus #109
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: o3de-2409
Are you sure you want to change the base?
Changes from 47 commits
6470d8a
6ccf97e
915d029
1b9affd
57322f8
ee5c565
399c2d1
5b7dd26
3fc175d
9cf218a
4651355
45bd34c
ffa7f34
23e9891
7049997
c91a03a
f70295a
5373024
09e44bf
ee39e1c
2fbc807
002c35b
139bb38
318d19f
0c45656
bb29e40
5a24a36
54e6f12
22c3628
51e1941
476f2b4
26cb4bd
62358ef
9e3cf1a
95b70d1
b30aac2
75e3739
61fc9aa
13671a4
2c17d49
896f067
687aff9
b30dda3
3694333
2e4d01a
3345918
48cd8d2
24c4e8d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
/** | ||
* Copyright (C) Robotec AI - All Rights Reserved | ||
* | ||
* This source code is protected under international copyright law. All rights | ||
* reserved and protected by the copyright holders. | ||
* This file is confidential and only available to authorized individuals with | ||
* the permission of the copyright holders. If you encounter this file and do | ||
* not have permission, please contact the copyright holders and delete this | ||
* file. | ||
*/ | ||
|
||
#pragma once | ||
|
||
#include <CsvSpawner/CsvSpawnerUtils.h> | ||
|
||
#include <AzCore/EBus/EBus.h> | ||
#include <AzCore/RTTI/BehaviorContext.h> | ||
|
||
namespace CsvSpawner | ||
{ | ||
/** | ||
* @brief Interface for handling entity spawn events for Csv Spawner. | ||
* | ||
* CsvSpawnerInterface is an Event Bus interface that notifies multiple | ||
* listeners when entity spawning begins and finishes. | ||
*/ | ||
class CsvSpawnerInterface : public AZ::EBusTraits | ||
w-czerski marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{ | ||
public: | ||
AZ_RTTI(CsvSpawnerInterface, CsvSpawnerInterfaceTypeId); | ||
virtual ~CsvSpawnerInterface() = default; | ||
|
||
/** | ||
* @brief Called when entity spawning begins. | ||
* @param m_spawnInfo Struct holding information about entities to be spawned. | ||
*/ | ||
virtual void OnEntitiesSpawnBegin(CsvSpawnerUtils::SpawnInfo m_spawnInfo) = 0; | ||
|
||
/** | ||
* @brief Called when entity spawning finishes. | ||
* @param m_spawnInfo Struct holding information about entities to be spawned. | ||
* @param m_statusCode Status code indicating success, failure and warnings of the spawn. | ||
*/ | ||
virtual void OnEntitiesSpawnFinished(CsvSpawnerUtils::SpawnInfo m_spawnInfo, CsvSpawnerUtils::SpawnStatus m_statusCode) = 0; | ||
|
||
/// EBus Configuration - Allows multiple listeners to handle events. | ||
static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple; | ||
}; | ||
|
||
// Create an EBus using the notification interface | ||
using CsvSpawnerNotificationBus = AZ::EBus<CsvSpawnerInterface>; | ||
|
||
class CsvSpawnerNotificationBusHandler | ||
: public CsvSpawnerNotificationBus::Handler | ||
, public AZ::BehaviorEBusHandler | ||
{ | ||
public: | ||
AZ_EBUS_BEHAVIOR_BINDER( | ||
CsvSpawnerNotificationBusHandler, | ||
CsvSpawnerNotificationBusHandlerTypeId, | ||
AZ::SystemAllocator, | ||
OnEntitiesSpawnBegin, | ||
OnEntitiesSpawnFinished); | ||
|
||
void OnEntitiesSpawnBegin(CsvSpawnerUtils::SpawnInfo m_spawnInfo) override | ||
{ | ||
Call(FN_OnEntitiesSpawnBegin, m_spawnInfo); | ||
} | ||
|
||
void OnEntitiesSpawnFinished(CsvSpawnerUtils::SpawnInfo m_spawnInfo, CsvSpawnerUtils::SpawnStatus m_statusCode) override | ||
{ | ||
Call(FN_OnEntitiesSpawnFinished, m_spawnInfo, m_statusCode); | ||
} | ||
}; | ||
} // namespace CsvSpawner |
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -11,11 +11,15 @@ | |||||||||||
|
||||||||||||
#include "CsvSpawnerUtils.h" | ||||||||||||
|
||||||||||||
#include "AzCore/std/smart_ptr/make_shared.h" | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||
|
||||||||||||
#include <AzFramework/Physics/CollisionBus.h> | ||||||||||||
norbertprokopiuk marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||
#include <CsvSpawner/CsvSpawnerInterface.h> | ||||||||||||
|
||||||||||||
#include <AzCore/Asset/AssetSerializer.h> | ||||||||||||
#include <AzCore/Serialization/EditContext.h> | ||||||||||||
#include <AzCore/Serialization/SerializeContext.h> | ||||||||||||
#include <AzFramework/Components/TransformComponent.h> | ||||||||||||
#include <AzFramework/Physics/CollisionBus.h> | ||||||||||||
#include <AzFramework/Physics/Common/PhysicsSceneQueries.h> | ||||||||||||
#include <AzFramework/Physics/PhysicsScene.h> | ||||||||||||
#include <AzFramework/Physics/PhysicsSystem.h> | ||||||||||||
|
@@ -37,6 +41,32 @@ namespace CsvSpawner::CsvSpawnerUtils | |||||||||||
->Field("Transform", &CsvSpawnableEntityInfo::m_transform) | ||||||||||||
->Field("Name", &CsvSpawnableEntityInfo::m_name) | ||||||||||||
->Field("Seed", &CsvSpawnableEntityInfo::m_seed); | ||||||||||||
|
||||||||||||
if (AZ::EditContext* editContext = serializeContext->GetEditContext()) | ||||||||||||
{ | ||||||||||||
editContext->Class<CsvSpawnableEntityInfo>("Csv Spawnable Entity Info", "An entity configuration for spawning") | ||||||||||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "") | ||||||||||||
->DataElement(AZ::Edit::UIHandlers::Default, &CsvSpawnableEntityInfo::m_id, "ID", "Optional ID for the entity") | ||||||||||||
->DataElement( | ||||||||||||
AZ::Edit::UIHandlers::Default, &CsvSpawnableEntityInfo::m_transform, "Transform", "Transform of the entity") | ||||||||||||
->DataElement( | ||||||||||||
AZ::Edit::UIHandlers::Default, | ||||||||||||
&CsvSpawnableEntityInfo::m_name, | ||||||||||||
"Name", | ||||||||||||
"Name of the spawnable entity configuration") | ||||||||||||
->DataElement( | ||||||||||||
AZ::Edit::UIHandlers::Default, &CsvSpawnableEntityInfo::m_seed, "Seed", "Optional seed value for randomization"); | ||||||||||||
} | ||||||||||||
} | ||||||||||||
|
||||||||||||
if (auto* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context)) | ||||||||||||
{ | ||||||||||||
behaviorContext->Class<CsvSpawnableEntityInfo>("CsvSpawnableEntityInfo") | ||||||||||||
->Constructor<>() | ||||||||||||
->Property("Id", BehaviorValueProperty(&CsvSpawnableEntityInfo::m_id)) | ||||||||||||
->Property("Transform", BehaviorValueProperty(&CsvSpawnableEntityInfo::m_transform)) | ||||||||||||
->Property("Name", BehaviorValueProperty(&CsvSpawnableEntityInfo::m_name)) | ||||||||||||
->Property("Seed", BehaviorValueProperty(&CsvSpawnableEntityInfo::m_seed)); | ||||||||||||
} | ||||||||||||
} | ||||||||||||
void CsvSpawnableAssetConfiguration::Reflect(AZ::ReflectContext* context) | ||||||||||||
|
@@ -193,6 +223,24 @@ namespace CsvSpawner::CsvSpawnerUtils | |||||||||||
const AZStd::string& physicsSceneName, | ||||||||||||
AZ::EntityId parentId) | ||||||||||||
{ | ||||||||||||
SpawnInfo broadcastSpawnInfo = | ||||||||||||
SpawnInfo{ entitiesToSpawn, physicsSceneName, parentId }; // Spawn Info used in CsvSpawner EBus notify. | ||||||||||||
// SpawnStatus spawnStatusCode = SpawnStatus::Success; // Spawn Status Code used for CsvSpawner EBus notify - | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I recommend to remove this code if it is commented |
||||||||||||
// OnEntitiesSpawnFinished. | ||||||||||||
auto spawnStatusCode = AZStd::make_shared<SpawnStatus>( | ||||||||||||
SpawnStatus::Success); // Spawn Status Code used for CsvSpawner EBus notify - OnEntitiesSpawnFinished. | ||||||||||||
|
||||||||||||
// Call CsvSpawner EBus notification - Begin | ||||||||||||
CsvSpawnerNotificationBus::Broadcast(&CsvSpawnerInterface::OnEntitiesSpawnBegin, broadcastSpawnInfo); | ||||||||||||
|
||||||||||||
// Check if there are no entities to spawn | ||||||||||||
if (entitiesToSpawn.empty()) | ||||||||||||
{ | ||||||||||||
*spawnStatusCode |= SpawnStatus::Fail; | ||||||||||||
CsvSpawnerNotificationBus::Broadcast(&CsvSpawnerInterface::OnEntitiesSpawnFinished, broadcastSpawnInfo, *spawnStatusCode); | ||||||||||||
return {}; | ||||||||||||
} | ||||||||||||
|
||||||||||||
auto sceneInterface = AZ::Interface<AzPhysics::SceneInterface>::Get(); | ||||||||||||
AZ_Assert(sceneInterface, "Unable to get physics scene interface"); | ||||||||||||
const auto sceneHandle = sceneInterface->GetSceneHandle(physicsSceneName); | ||||||||||||
|
@@ -201,6 +249,8 @@ namespace CsvSpawner::CsvSpawnerUtils | |||||||||||
auto spawner = AZ::Interface<AzFramework::SpawnableEntitiesDefinition>::Get(); | ||||||||||||
AZ_Assert(spawner, "Unable to get spawnable entities definition"); | ||||||||||||
|
||||||||||||
auto pendingSpawns = AZStd::make_shared<AZStd::atomic_int>(static_cast<int>(entitiesToSpawn.size())); | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Inside for loop through |
||||||||||||
|
||||||||||||
// get parent transform | ||||||||||||
AZ::Transform parentTransform = AZ::Transform::CreateIdentity(); | ||||||||||||
if (parentId.IsValid()) | ||||||||||||
|
@@ -221,6 +271,9 @@ namespace CsvSpawner::CsvSpawnerUtils | |||||||||||
if (!spawnableAssetConfiguration.contains(entityConfig.m_name)) | ||||||||||||
{ | ||||||||||||
AZ_Error("CsvSpawner", false, "SpawnableAssetConfiguration %s not found", entityConfig.m_name.c_str()); | ||||||||||||
|
||||||||||||
// Add notify code status | ||||||||||||
*spawnStatusCode |= SpawnStatus::Warning; | ||||||||||||
continue; | ||||||||||||
} | ||||||||||||
|
||||||||||||
|
@@ -250,6 +303,9 @@ namespace CsvSpawner::CsvSpawnerUtils | |||||||||||
} | ||||||||||||
else | ||||||||||||
{ | ||||||||||||
// Add notify code status | ||||||||||||
*spawnStatusCode |= SpawnStatus::Warning; | ||||||||||||
|
||||||||||||
continue; // Skip this entity if we can't find a valid position and | ||||||||||||
// place on terrain is enabled. | ||||||||||||
} | ||||||||||||
|
@@ -259,10 +315,13 @@ namespace CsvSpawner::CsvSpawnerUtils | |||||||||||
AzFramework::EntitySpawnTicket ticket(spawnable); | ||||||||||||
// Set the pre-spawn callback to set the name of the root entity to the name | ||||||||||||
// of the spawnable | ||||||||||||
optionalArgs.m_preInsertionCallback = [transform](auto id, auto view) | ||||||||||||
optionalArgs.m_preInsertionCallback = [transform, &spawnStatusCode](auto id, auto view) | ||||||||||||
{ | ||||||||||||
if (view.empty()) | ||||||||||||
{ | ||||||||||||
// Add notify code status | ||||||||||||
*spawnStatusCode |= SpawnStatus::Warning | SpawnStatus::Stopped; | ||||||||||||
|
||||||||||||
return; | ||||||||||||
} | ||||||||||||
AZ::Entity* root = *view.begin(); | ||||||||||||
|
@@ -272,23 +331,94 @@ namespace CsvSpawner::CsvSpawnerUtils | |||||||||||
transformInterface->SetWorldTM(transform); | ||||||||||||
}; | ||||||||||||
optionalArgs.m_completionCallback = | ||||||||||||
[parentId]( | ||||||||||||
[parentId, spawnStatusCode, pendingSpawns, broadcastSpawnInfo]( | ||||||||||||
[[maybe_unused]] AzFramework::EntitySpawnTicket::Id ticketId, AzFramework::SpawnableConstEntityContainerView view) | ||||||||||||
{ | ||||||||||||
if (view.empty()) | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The code within this if statement exhibits undefined behavior because |
||||||||||||
{ | ||||||||||||
return; | ||||||||||||
*spawnStatusCode |= SpawnStatus::Warning | SpawnStatus::Stopped; | ||||||||||||
} | ||||||||||||
else | ||||||||||||
{ | ||||||||||||
const AZ::Entity* root = *view.begin(); | ||||||||||||
AZ::TransformBus::Event(root->GetId(), &AZ::TransformBus::Events::SetParent, parentId); | ||||||||||||
Comment on lines
+346
to
+347
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||
} | ||||||||||||
|
||||||||||||
// Decrement the pending counter | ||||||||||||
const int remaining = --(*pendingSpawns); | ||||||||||||
if (remaining == 0) | ||||||||||||
{ | ||||||||||||
// All spawns are finished, now broadcast finished notification | ||||||||||||
CsvSpawnerNotificationBus::Broadcast( | ||||||||||||
&CsvSpawnerInterface::OnEntitiesSpawnFinished, broadcastSpawnInfo, *spawnStatusCode); | ||||||||||||
} | ||||||||||||
const AZ::Entity* root = *view.begin(); | ||||||||||||
AZ::TransformBus::Event(root->GetId(), &AZ::TransformBus::Events::SetParent, parentId); | ||||||||||||
}; | ||||||||||||
optionalArgs.m_priority = AzFramework::SpawnablePriority_Lowest; | ||||||||||||
spawner->SpawnAllEntities(ticket, optionalArgs); | ||||||||||||
tickets[entityConfig.m_id] = AZStd::move(ticket); | ||||||||||||
} | ||||||||||||
|
||||||||||||
return tickets; | ||||||||||||
} | ||||||||||||
|
||||||||||||
void SpawnInfo::Reflect(AZ::ReflectContext* context) | ||||||||||||
{ | ||||||||||||
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context)) | ||||||||||||
{ | ||||||||||||
// Reflect SpawnStatus enum | ||||||||||||
serializeContext->Enum<SpawnStatus>() | ||||||||||||
->Version(0) | ||||||||||||
->Value("Success", SpawnStatus::Success) | ||||||||||||
->Value("Fail", SpawnStatus::Fail) | ||||||||||||
->Value("Stopped", SpawnStatus::Stopped) | ||||||||||||
->Value("Warning", SpawnStatus::Warning); | ||||||||||||
|
||||||||||||
// Reflect SpawnInfo struct | ||||||||||||
serializeContext->Class<SpawnInfo>() | ||||||||||||
->Version(0) | ||||||||||||
->Field("EntitiesToSpawn", &SpawnInfo::m_entitiesToSpawn) | ||||||||||||
->Field("PhysicsSceneName", &SpawnInfo::m_physicsSceneName) | ||||||||||||
->Field("SpawnerParentEntityId", &SpawnInfo::m_spawnerParentEntityId); | ||||||||||||
|
||||||||||||
if (auto* editContext = serializeContext->GetEditContext()) | ||||||||||||
{ | ||||||||||||
editContext->Class<SpawnInfo>("Spawn Info", "Information about entities being spawned") | ||||||||||||
->ClassElement(AZ::Edit::ClassElements::EditorData, "") | ||||||||||||
->DataElement( | ||||||||||||
AZ::Edit::UIHandlers::Default, | ||||||||||||
&SpawnInfo::m_entitiesToSpawn, | ||||||||||||
"Entities to Spawn", | ||||||||||||
"List of entities to be spawned.") | ||||||||||||
->DataElement( | ||||||||||||
AZ::Edit::UIHandlers::Default, | ||||||||||||
&SpawnInfo::m_physicsSceneName, | ||||||||||||
"Physics Scene", | ||||||||||||
"Name of the physics scene where entities will be spawned.") | ||||||||||||
->DataElement( | ||||||||||||
AZ::Edit::UIHandlers::Default, | ||||||||||||
&SpawnInfo::m_spawnerParentEntityId, | ||||||||||||
"Parent Entity", | ||||||||||||
"Parent entity ID responsible for spawning."); | ||||||||||||
} | ||||||||||||
} | ||||||||||||
|
||||||||||||
if (auto* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context)) | ||||||||||||
{ | ||||||||||||
behaviorContext->EnumProperty<static_cast<int>(CsvSpawnerUtils::SpawnStatus::Success)>("SpawnStatus_Success"); | ||||||||||||
behaviorContext->EnumProperty<static_cast<int>(CsvSpawnerUtils::SpawnStatus::Fail)>("SpawnStatus_Fail"); | ||||||||||||
behaviorContext->EnumProperty<static_cast<int>(CsvSpawnerUtils::SpawnStatus::Stopped)>("SpawnStatus_Stopped"); | ||||||||||||
behaviorContext->EnumProperty<static_cast<int>(CsvSpawnerUtils::SpawnStatus::Warning)>("SpawnStatus_Warning"); | ||||||||||||
|
||||||||||||
behaviorContext->Class<SpawnInfo>("SpawnInfo") | ||||||||||||
->Constructor() | ||||||||||||
->Attribute(AZ::Script::Attributes::Category, "CsvSpawner") | ||||||||||||
->Attribute(AZ::Script::Attributes::Module, "editor") | ||||||||||||
->Property("m_entitiesToSpawn", BehaviorValueProperty(&SpawnInfo::m_entitiesToSpawn)) | ||||||||||||
->Property("m_physicsSceneName", BehaviorValueProperty(&SpawnInfo::m_physicsSceneName)) | ||||||||||||
->Property("m_spawnerParentEntityId", BehaviorValueProperty(&SpawnInfo::m_spawnerParentEntityId)); | ||||||||||||
} | ||||||||||||
} | ||||||||||||
|
||||||||||||
bool IsTerrainAvailable() | ||||||||||||
{ | ||||||||||||
return AzFramework::Terrain::TerrainDataRequestBus::HasHandlers(); | ||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that created notification methods are correct, but I've noticed that you set
AddressPolicy
asSingle
. We can have multiple CSVSpawner components on the scene. It means that we should be send notification either when all components finishes their jobs (you would need system component for that), or you can change this notification busAddressPolicy
toById
to allow each component to send their own notification. Then it would be client call to decide on which entity/component notification they want to connect with.