Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,193 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "LmbrCentral_precompiled.h"
#include "EditorLookAtComponent.h"
#include "LookAtComponent.h"
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Math/Transform.h>
namespace LmbrCentral
{
//=========================================================================
void EditorLookAtComponent::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<EditorLookAtComponent, AZ::Component>()
->Version(1)
->Field("Target", &EditorLookAtComponent::m_targetId)
->Field("ForwardAxis", &EditorLookAtComponent::m_forwardAxis)
;
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<EditorLookAtComponent>("Look At", "Force an entity to always look at a given target")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Gameplay")
->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/LookAt.png")
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/LookAt.png")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->DataElement(AZ::Edit::UIHandlers::Default, &EditorLookAtComponent::m_targetId, "Target", "The entity to look at")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorLookAtComponent::OnTargetChanged)
->DataElement(AZ::Edit::UIHandlers::ComboBox, &EditorLookAtComponent::m_forwardAxis, "Forward Axis", "The local axis that should point at the target")
->EnumAttribute(AZ::Transform::Axis::YPositive, "Y+")
->EnumAttribute(AZ::Transform::Axis::YNegative, "Y-")
->EnumAttribute(AZ::Transform::Axis::XPositive, "X+")
->EnumAttribute(AZ::Transform::Axis::XNegative, "X-")
->EnumAttribute(AZ::Transform::Axis::ZPositive, "Z+")
->EnumAttribute(AZ::Transform::Axis::ZNegative, "Z-")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorLookAtComponent::RecalculateTransform)
;
}
}
}
//=========================================================================
void EditorLookAtComponent::Activate()
{
if (m_targetId.IsValid())
{
AZ::EntityBus::Handler::BusConnect(m_targetId);
}
}
//=========================================================================
void EditorLookAtComponent::Deactivate()
{
AZ::TransformNotificationBus::MultiHandler::BusDisconnect();
AZ::EntityBus::Handler::BusDisconnect();
}
//=========================================================================
void EditorLookAtComponent::OnEntityActivated(const AZ::EntityId& /*entity*/)
{
AZ::TransformNotificationBus::MultiHandler::BusConnect(GetEntityId());
AZ::TransformNotificationBus::MultiHandler::BusConnect(m_targetId);
}
//=========================================================================
void EditorLookAtComponent::OnEntityDeactivated(const AZ::EntityId& /*entity*/)
{
AZ::TransformNotificationBus::MultiHandler::BusDisconnect(GetEntityId());
AZ::TransformNotificationBus::MultiHandler::BusDisconnect(m_targetId);
}
//=========================================================================
void EditorLookAtComponent::OnTransformChanged([[maybe_unused]] const AZ::Transform& local, [[maybe_unused]] const AZ::Transform& world)
{
// We need to defer the Look-At transform change. Can't flush it through here because
// that will cause a feedback loop and the originator of the transform change might
// not be finished broadcasting out to listeners. If we set look-at here, the look-at
// transform can be stomped later by the original data.
// Method 1: Connect to the Tick bus for a frame. In the next OnTick we set the
// Look-At transform and disconnect.
AZ::TickBus::Handler::BusConnect();
// Method 2: We may want to stay connected to the TickBus if the transform is constantly
// changing. Without a good heuristic to detect this case, we'll stick with Method 1.
// Here's a gist of this method:
// connect/disconnect to TickBus in OnEntityActivated/OnEntityDeactivated.
// set a 'shouldRecalc' flag here in OnTransformChanged to true;
// in OnTick do this:
// if (shouldRecalc)
// {
// RecalculateTransform();
// shouldRecalc = false;
// }
}
//=========================================================================
void EditorLookAtComponent::BuildGameEntity(AZ::Entity* gameEntity)
{
LookAtComponent* lookAtComponent = gameEntity->CreateComponent<LookAtComponent>();
if (lookAtComponent)
{
lookAtComponent->m_targetId = m_targetId;
lookAtComponent->m_forwardAxis = m_forwardAxis;
}
}
//=========================================================================
void EditorLookAtComponent::OnTargetChanged()
{
if (m_oldTargetId.IsValid())
{
// Disconnect from the old target entity
AZ::TransformNotificationBus::MultiHandler::BusDisconnect(m_oldTargetId);
AZ::EntityBus::Handler::BusDisconnect(m_oldTargetId);
m_oldTargetId = AZ::EntityId();
}
if (m_targetId.IsValid())
{
// Connect to the new target entity
// Won't connect to the new target's transform bus until we receive notification
// that target is activated via the EntityBus.
AZ::EntityBus::Handler::BusConnect(m_targetId);
m_oldTargetId = m_targetId;
RecalculateTransform();
}
else
{
// If the target is invalid (nothing to look at), stop listening to everything
AZ::TransformNotificationBus::MultiHandler::BusDisconnect();
AZ::EntityBus::Handler::BusDisconnect();
}
}
//=========================================================================
void EditorLookAtComponent::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/)
{
RecalculateTransform();
AZ::TickBus::Handler::BusDisconnect();
}
//=========================================================================
void EditorLookAtComponent::RecalculateTransform()
{
if (m_targetId.IsValid())
{
AZ::TransformNotificationBus::MultiHandler::BusDisconnect(GetEntityId());
{
AZ::Transform currentTM = AZ::Transform::CreateIdentity();
EBUS_EVENT_ID_RESULT(currentTM, GetEntityId(), AZ::TransformBus, GetWorldTM);
AZ::Vector3 currentScale = currentTM.ExtractScale();
AZ::Transform targetTM = AZ::Transform::CreateIdentity();
EBUS_EVENT_ID_RESULT(targetTM, m_targetId, AZ::TransformBus, GetWorldTM);
AZ::Transform lookAtTransform = AZ::Transform::CreateLookAt(
currentTM.GetTranslation(),
targetTM.GetTranslation(),
m_forwardAxis
);
lookAtTransform.MultiplyByScale(currentScale);
EBUS_EVENT_ID(GetEntityId(), AZ::TransformBus, SetWorldTM, lookAtTransform);
}
AZ::TransformNotificationBus::MultiHandler::BusConnect(GetEntityId());
}
}
}//namespace LmbrCentral
@@ -0,0 +1,94 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/EntityBus.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
namespace LmbrCentral
{
//=========================================================================
// EditorLookAtComponent
//=========================================================================
class EditorLookAtComponent
: public AzToolsFramework::Components::EditorComponentBase
, private AZ::TransformNotificationBus::MultiHandler
, private AZ::TickBus::Handler
, private AZ::EntityBus::Handler
{
public:
AZ_COMPONENT(EditorLookAtComponent, "{68D07AA1-49E9-4283-9697-7F887EB19C91}", AzToolsFramework::Components::EditorComponentBase);
//=====================================================================
// AZ::Component
void Activate() override;
void Deactivate() override;
//=====================================================================
//=====================================================================
// TransformBus
void OnTransformChanged(const AZ::Transform& /*local*/, const AZ::Transform& /*world*/) override;
//=====================================================================
//=====================================================================
// TickBus
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
//=====================================================================
//=====================================================================
// EntityBus
void OnEntityActivated(const AZ::EntityId& entityId) override;
void OnEntityDeactivated(const AZ::EntityId& entityId) override;
//=====================================================================
//=====================================================================
// EditorComponentBase
void BuildGameEntity(AZ::Entity* gameEntity) override;
//=====================================================================
protected:
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("LookAtService", 0x34230406));
}
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC("TransformService", 0x8ee22c50));
}
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("LookAtService", 0x34230406));
}
private:
void OnTargetChanged();
void RecalculateTransform();
// Serialized data
AZ::EntityId m_targetId;
AZ::Transform::Axis m_forwardAxis;
// Transient data
AZ::EntityId m_oldTargetId;
};
} // namespace LmbrCentral
@@ -0,0 +1,114 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "LmbrCentral_precompiled.h"
#include "EditorRandomTimedSpawnerComponent.h"
#include <AzCore/Serialization/EditContext.h>
namespace LmbrCentral
{
void EditorRandomTimedSpawnerConfiguration::Reflect(AZ::ReflectContext * context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<EditorRandomTimedSpawnerConfiguration, RandomTimedSpawnerConfiguration>()
->Version(1);
if (AZ::EditContext* editContext = serializeContext->GetEditContext())
{
editContext->Class<RandomTimedSpawnerConfiguration>("RandomTimedSpawner Configuration", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(AZ::Edit::UIHandlers::Default, &RandomTimedSpawnerConfiguration::m_enabled, "Enabled", "")
->DataElement(AZ::Edit::UIHandlers::ComboBox, &RandomTimedSpawnerConfiguration::m_randomDistribution, "Random Distribution", "")
->EnumAttribute(AZ::RandomDistributionType::Normal, "Normal")
->EnumAttribute(AZ::RandomDistributionType::UniformReal, "Uniform Real")
->ClassElement(AZ::Edit::ClassElements::Group, "Timing")
->Attribute(AZ::Edit::Attributes::AutoExpand, false)
->DataElement(AZ::Edit::UIHandlers::Default, &RandomTimedSpawnerConfiguration::m_spawnDelay, "Spawn Delay", "Time in seconds it takes to spawn")
->DataElement(AZ::Edit::UIHandlers::Default, &RandomTimedSpawnerConfiguration::m_spawnDelayVariation, "Spawn Delay Variation", "Variation applied to the spawn delay")
;
}
}
}
void EditorRandomTimedSpawnerComponent::Reflect(AZ::ReflectContext* context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<EditorRandomTimedSpawnerComponent, AzToolsFramework::Components::EditorComponentBase>()
->Version(1)
->Field("m_config", &EditorRandomTimedSpawnerComponent::m_config)
;
AZ::EditContext* editContext = serializeContext->GetEditContext();
if (editContext)
{
editContext->Class<EditorRandomTimedSpawnerComponent>("Random Timed Spawner", "")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Gameplay")
->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/RandomTimedSpawner.svg")
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/RandomTimedSpawner.png")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->Attribute(AZ::Edit::Attributes::HelpPageURL, "http://docs.aws.amazon.com/console/lumberyard/userguide/random-timed-spawner-component")
->DataElement(AZ::Edit::UIHandlers::Default, &EditorRandomTimedSpawnerComponent::m_config, "m_config", "No Description")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::ShowChildrenOnly)
;
}
}
EditorRandomTimedSpawnerConfiguration::Reflect(context);
}
void EditorRandomTimedSpawnerComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("RandomTimedSpawnerService", 0x56f2fa36));
}
void EditorRandomTimedSpawnerComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
//Only compatible with Box and Cylinder shapes
incompatible.push_back(AZ_CRC("CapsuleShapeService", 0x9bc1122c));
incompatible.push_back(AZ_CRC("SphereShapeService", 0x90c8dc80));
incompatible.push_back(AZ_CRC("CompoundShapeService", 0x4f7c640a));
incompatible.push_back(AZ_CRC("TubeShapeService", 0x3fe791b4));
incompatible.push_back(AZ_CRC("PrismShapeService", 0x8dbfb417));
incompatible.push_back(AZ_CRC("PolygonPrismShapeService", 0x1cbc4ed4));
}
void EditorRandomTimedSpawnerComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC("TransformService", 0x8ee22c50));
required.push_back(AZ_CRC("ShapeService", 0xe86aa5fe));
required.push_back(AZ_CRC("SpawnerService", 0xd2f1d7a3));
}
void EditorRandomTimedSpawnerComponent::Activate()
{
RandomTimedSpawnerComponentRequestBus::Handler::BusConnect(GetEntityId());
}
void EditorRandomTimedSpawnerComponent::Deactivate()
{
RandomTimedSpawnerComponentRequestBus::Handler::BusDisconnect(GetEntityId());
}
void EditorRandomTimedSpawnerComponent::BuildGameEntity(AZ::Entity* gameEntity)
{
RandomTimedSpawnerComponent* component = gameEntity->CreateComponent<RandomTimedSpawnerComponent>(&m_config);
}
} //namespace LmbrCentral
@@ -0,0 +1,71 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
#include <LmbrCentral/Scripting/RandomTimedSpawnerComponentBus.h>
#include "RandomTimedSpawnerComponent.h"
namespace LmbrCentral
{
class EditorRandomTimedSpawnerConfiguration
: public RandomTimedSpawnerConfiguration
{
public:
AZ_TYPE_INFO_LEGACY(EditorRandomTimedSpawnerConfiguration, "{AA68F544-917B-4F72-AEA7-3A906B9DEB2B}", RandomTimedSpawnerConfiguration);
AZ_CLASS_ALLOCATOR(EditorRandomTimedSpawnerConfiguration, AZ::SystemAllocator, 0);
static void Reflect(AZ::ReflectContext* context);
};
class EditorRandomTimedSpawnerComponent
: public AzToolsFramework::Components::EditorComponentBase
, public RandomTimedSpawnerComponentRequestBus::Handler
{
public:
AZ_COMPONENT(EditorRandomTimedSpawnerComponent, "{6D3E32F0-1971-416B-86DE-4B5EB6E2139E}", AzToolsFramework::Components::EditorComponentBase);
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
// AZ::Component
void Activate() override;
void Deactivate() override;
// RandomTimedSpawnerComponentRequestBus
void Enable() override { m_config.m_enabled = true; }
void Disable() override { m_config.m_enabled = false; }
void Toggle() override { m_config.m_enabled = !m_config.m_enabled; }
bool IsEnabled() override { return m_config.m_enabled; }
void SetRandomDistribution(AZ::RandomDistributionType randomDistribution) override { m_config.m_randomDistribution = randomDistribution; }
AZ::RandomDistributionType GetRandomDistribution() override { return m_config.m_randomDistribution; }
void SetSpawnDelay(double spawnDelay) override { m_config.m_spawnDelay = spawnDelay; }
double GetSpawnDelay() override { return m_config.m_spawnDelay; }
void SetSpawnDelayVariation(double spawnDelayVariation) override { m_config.m_spawnDelayVariation = spawnDelayVariation; }
double GetSpawnDelayVariation() override { return m_config.m_spawnDelayVariation; }
void BuildGameEntity(AZ::Entity* gameEntity);
private:
//Reflected members
EditorRandomTimedSpawnerConfiguration m_config;
};
} //namespace LmbrCentral
@@ -0,0 +1,163 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "LmbrCentral_precompiled.h"
#include "EditorSpawnerComponent.h"
#include "SpawnerComponent.h"
#include <QMessageBox>
#include <QApplication>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Slice/SliceComponent.h>
#include <AzFramework/Entity/EntityContextBus.h>
#include <AzFramework/Slice/SliceEntityBus.h>
namespace LmbrCentral
{
void EditorSpawnerComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<EditorSpawnerComponent, AZ::Component>()
->Version(1)
->Field("Slice", &EditorSpawnerComponent::m_sliceAsset)
->Field("SpawnOnActivate", &EditorSpawnerComponent::m_spawnOnActivate)
->Field("DestroyOnDeactivate", &EditorSpawnerComponent::m_destroyOnDeactivate)
;
AZ::EditContext* editContext = serializeContext->GetEditContext();
if (editContext)
{
editContext->Class<EditorSpawnerComponent>("Spawner", "The Spawner component allows an entity to spawn a design-time or run-time dynamic slice (*.dynamicslice) at the entity's location with an optional offset")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Gameplay")
->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/Spawner.svg")
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/Spawner.png")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://docs.aws.amazon.com/lumberyard/latest/userguide/component-spawner.html")
->DataElement(0, &EditorSpawnerComponent::m_sliceAsset, "Dynamic slice", "The slice to spawn")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorSpawnerComponent::SliceAssetChanged)
->DataElement(0, &EditorSpawnerComponent::m_spawnOnActivate, "Spawn on activate", "Should the component spawn the selected slice upon activation?")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorSpawnerComponent::SpawnOnActivateChanged)
->DataElement(0, &EditorSpawnerComponent::m_destroyOnDeactivate, "Destroy on deactivate", "Upon deactivation, should the component destroy any slices it spawned?")
;
}
}
}
void EditorSpawnerComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
SpawnerComponent::GetProvidedServices(services);
}
void EditorSpawnerComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
SpawnerComponent::GetRequiredServices(services);
}
void EditorSpawnerComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& services)
{
SpawnerComponent::GetDependentServices(services);
}
bool EditorSpawnerComponent::HasInfiniteLoop()
{
// If we are set to spawn on activate, then we need to make sure we don't point to ourself or we create an infinite spawn loop
AZ::SliceComponent::SliceInstanceAddress sliceInstanceAddress;
AzFramework::SliceEntityRequestBus::EventResult(sliceInstanceAddress, GetEntityId(),
&AzFramework::SliceEntityRequestBus::Events::GetOwningSlice);
if (m_spawnOnActivate && sliceInstanceAddress.GetReference())
{
// Compare the ids because one is source and the other is going to be the dynamic slice
return m_sliceAsset.GetId().m_guid == sliceInstanceAddress.GetReference()->GetSliceAsset().GetId().m_guid;
}
return false;
}
AZ::u32 EditorSpawnerComponent::SliceAssetChanged()
{
if (HasInfiniteLoop())
{
QMessageBox(QMessageBox::Warning,
"Input Error",
"Your spawner is set to Spawn on Activate. You cannot set the spawner to spawn a dynamic slice that contains this entity or it will spawn infinitely!",
QMessageBox::Ok, QApplication::activeWindow()).exec();
m_sliceAsset = AZ::Data::Asset<AZ::DynamicSliceAsset>();
// We have to refresh entire tree to update the asset control until the bug is fixed. Just refreshing values does not properly update the UI.
// Once LY-71192 (and the other variants) are fixed, this can be changed to ::ValuesOnly
return AZ::Edit::PropertyRefreshLevels::EntireTree;
}
return AZ::Edit::PropertyRefreshLevels::None;
}
AZ::u32 EditorSpawnerComponent::SpawnOnActivateChanged()
{
if (HasInfiniteLoop())
{
QMessageBox(QMessageBox::Warning,
"Input Error",
"Your spawner is set to spawn a dynamic slice that contains this entity. You cannot set the spawner to be Spawn on Activate or it will spawn infinitely!",
QMessageBox::Ok, QApplication::activeWindow()).exec();
m_spawnOnActivate = false;
return AZ::Edit::PropertyRefreshLevels::ValuesOnly;
}
return AZ::Edit::PropertyRefreshLevels::None;
}
bool EditorSpawnerComponent::ReadInConfig(const AZ::ComponentConfig* baseConfig)
{
if (auto config = azrtti_cast<const SpawnerConfig*>(baseConfig))
{
m_sliceAsset = config->m_sliceAsset;
m_spawnOnActivate = config->m_spawnOnActivate;
m_destroyOnDeactivate = config->m_destroyOnDeactivate;
return true;
}
return false;
}
bool EditorSpawnerComponent::WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const
{
if (auto config = azrtti_cast<SpawnerConfig*>(outBaseConfig))
{
config->m_sliceAsset = m_sliceAsset;
config->m_spawnOnActivate = m_spawnOnActivate;
config->m_destroyOnDeactivate = m_destroyOnDeactivate;
return true;
}
return false;
}
void EditorSpawnerComponent::BuildGameEntity(AZ::Entity* gameEntity)
{
// Add corresponding gameComponent to gameEntity.
auto gameComponent = gameEntity->CreateComponent<SpawnerComponent>();
SpawnerConfig config;
config.m_sliceAsset = m_sliceAsset;
config.m_spawnOnActivate = m_spawnOnActivate;
config.m_destroyOnDeactivate = m_destroyOnDeactivate;
gameComponent->SetConfiguration(config);
}
} // namespace LmbrCentral
@@ -0,0 +1,53 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
#include <AzCore/Slice/SliceAsset.h>
namespace LmbrCentral
{
/**
* Editor spawner component
* Spawns the entities from a ".dynamicslice" asset at runtime.
*/
class EditorSpawnerComponent
: public AzToolsFramework::Components::EditorComponentBase
{
public:
AZ_EDITOR_COMPONENT(EditorSpawnerComponent, "{77CDE991-EC1A-B7C1-B112-7456ABAC81A1}", EditorComponentBase);
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType&);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType&);
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType&);
bool ReadInConfig(const AZ::ComponentConfig* baseConfig) override;
bool WriteOutConfig(AZ::ComponentConfig* outBaseConfig) const override;
void BuildGameEntity(AZ::Entity* gameEntity) override;
protected:
//////////////////////////////////////////////////////////////////////////
// Data changed validation methods
AZ::u32 SliceAssetChanged();
AZ::u32 SpawnOnActivateChanged();
bool HasInfiniteLoop();
//////////////////////////////////////////////////////////////////////////
// Serialized members
AZ::Data::Asset<AZ::DynamicSliceAsset> m_sliceAsset{ AZ::Data::AssetLoadBehavior::PreLoad };
bool m_spawnOnActivate = false;
bool m_destroyOnDeactivate = false;
};
} // namespace LmbrCentral
@@ -0,0 +1,158 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "LmbrCentral_precompiled.h"
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include "EditorTagComponent.h"
#include <AzCore/Serialization/EditContextConstants.inl>
#include <AzCore/Serialization/EditContext.h>
namespace LmbrCentral
{
//=========================================================================
// Component Descriptor
//=========================================================================
void EditorTagComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<EditorTagComponent, AZ::Component>()
->Version(1)
->Field("Tags", &EditorTagComponent::m_tags);
AZ::EditContext* editContext = serialize->GetEditContext();
if (editContext)
{
editContext->Class<EditorTagComponent>("Tag", "The Tag component allows you to apply one or more labels to an entity")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->Attribute(AZ::Edit::Attributes::Category, "Gameplay")
->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/Tag.svg")
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/Tag.png")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://docs.aws.amazon.com/lumberyard/latest/userguide/component-tag.html")
->DataElement(AZ::Edit::UIHandlers::Default, &EditorTagComponent::m_tags, "Tags", "The tags that will be on this entity by default")
->Attribute(AZ::Edit::Attributes::ChangeNotify, &EditorTagComponent::OnTagChanged);
}
}
}
//=========================================================================
// EditorTagComponentRequestBus::Handler
//=========================================================================
bool EditorTagComponent::HasTag(const char* tag)
{
return AZStd::find(m_tags.begin(), m_tags.end(), tag) != m_tags.end();
}
void EditorTagComponent::AddTag(const char* tag)
{
if (!HasTag(tag))
{
m_tags.push_back(tag);
ActivateTag(tag);
}
}
void EditorTagComponent::RemoveTag(const char* tag)
{
AZStd::remove_if(m_tags.begin(), m_tags.end(), [&tag](const AZStd::string& target) { return target == tag; });
if (AZStd::find(m_activeTags.begin(), m_activeTags.end(), tag) != m_activeTags.end())
{
DeactivateTag(tag);
}
}
//////////////////////////////////////////////////////////////////////////
//=========================================================================
// AZ::Component
//=========================================================================
void EditorTagComponent::Activate()
{
EditorComponentBase::Activate();
ActivateTags();
EditorTagComponentRequestBus::Handler::BusConnect(GetEntityId());
}
void EditorTagComponent::Deactivate()
{
EditorTagComponentRequestBus::Handler::BusDisconnect();
DeactivateTags();
EditorComponentBase::Deactivate();
}
//=========================================================================
// AzToolsFramework::Components::EditorComponentBase
//=========================================================================
void EditorTagComponent::BuildGameEntity(AZ::Entity* gameEntity)
{
if (TagComponent* tagComponent = gameEntity->CreateComponent<TagComponent>())
{
Tags newTagList;
for (const AZStd::string& tag : m_tags)
{
newTagList.insert(Tag(tag.c_str()));
}
tagComponent->EditorSetTags(AZStd::move(newTagList));
}
}
void EditorTagComponent::ActivateTag(const char* tagName)
{
Tag tag(tagName);
const AZ::EntityId entityId = GetEntityId();
m_activeTags.push_back(tagName);
TagComponentNotificationsBus::Event(entityId, &TagComponentNotificationsBus::Events::OnTagAdded, tag);
TagGlobalNotificationBus::Event(tag, &TagGlobalNotificationBus::Events::OnEntityTagAdded, entityId);
TagGlobalRequestBus::MultiHandler::BusConnect(tag);
}
void EditorTagComponent::DeactivateTag(const char* tagName)
{
Tag tag(tagName);
const AZ::EntityId entityId = GetEntityId();
TagGlobalRequestBus::MultiHandler::BusDisconnect(tag);
TagGlobalNotificationBus::Event(tag, &TagGlobalNotificationBus::Events::OnEntityTagRemoved, entityId);
TagComponentNotificationsBus::Event(entityId, &TagComponentNotificationsBus::Events::OnTagRemoved, tag);
AZStd::remove_if(m_activeTags.begin(), m_activeTags.end(), [&tagName](const AZStd::string& target) { return target == tagName; });
}
void EditorTagComponent::ActivateTags()
{
for (const AZStd::string& tag : m_tags)
{
ActivateTag(tag.c_str());
}
}
void EditorTagComponent::DeactivateTags()
{
EditorTags tagsToDeactivate(AZStd::move(m_activeTags));
for (const AZStd::string& tag : tagsToDeactivate)
{
DeactivateTag(tag.c_str());
}
}
void EditorTagComponent::OnTagChanged()
{
DeactivateTags();
ActivateTags();
}
} // namespace LmbrCentral
@@ -0,0 +1,95 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
#include <AzCore/std/containers/vector.h>
#include <LmbrCentral/Scripting/TagComponentBus.h>
#include <AzCore/std/string/string.h>
#include "TagComponent.h"
#include <LmbrCentral/Scripting/EditorTagComponentBus.h>
namespace LmbrCentral
{
/**
* Tag Component
*
* Simple component that tags an entity with a list of filters or descriptors
*
*/
class EditorTagComponent
: public AzToolsFramework::Components::EditorComponentBase
, private LmbrCentral::EditorTagComponentRequestBus::Handler
, private LmbrCentral::TagGlobalRequestBus::MultiHandler
{
public:
AZ_COMPONENT(EditorTagComponent,
"{5272B56C-6CCC-4118-8539-D881F463ACD1}",
AzToolsFramework::Components::EditorComponentBase);
~EditorTagComponent() override = default;
//////////////////////////////////////////////////////////////////////////
// AZ::Component
void Activate() override;
void Deactivate() override;
//////////////////////////////////////////////////////////////////////////
private:
//////////////////////////////////////////////////////////////////////////
// AzToolsFramework::Components::EditorComponentBase
void BuildGameEntity(AZ::Entity* gameEntity) override;
//////////////////////////////////////////////////////////////////////////
// Component descriptor
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("TagService", 0xf1ef347d));
}
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("TagService", 0xf1ef347d));
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// TagGlobalRequestBus::MultiHandler
const AZ::EntityId RequestTaggedEntities() override { return GetEntityId(); }
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// EditorTagComponentRequestBus::Handler
bool HasTag(const char* tag) override;
void AddTag(const char* tag) override;
void RemoveTag(const char* tag) override;
const EditorTags& GetTags() override { return m_tags; }
//////////////////////////////////////////////////////////////////////////
void ActivateTag(const char* tagName);
void DeactivateTag(const char* tagName);
void ActivateTags();
void DeactivateTags();
void OnTagChanged();
EditorTags m_activeTags;
// Reflected Data
EditorTags m_tags;
};
} // namespace LmbrCentral
@@ -0,0 +1,185 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "LmbrCentral_precompiled.h"
#include "LookAtComponent.h"
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
namespace LmbrCentral
{
//////////////////////////////////////////////////////////////////////////
class BehaviorLookAtComponentNotificationBusHandler : public LookAtComponentNotificationBus::Handler, public AZ::BehaviorEBusHandler
{
public:
AZ_EBUS_BEHAVIOR_BINDER(BehaviorLookAtComponentNotificationBusHandler, "{2C171B89-CE6A-4C53-A286-0E1236A61FA0}", AZ::SystemAllocator,
OnTargetChanged);
// Sent when the light is turned on.
void OnTargetChanged(AZ::EntityId entityId) override
{
Call(FN_OnTargetChanged, entityId);
}
};
//=========================================================================
void LookAtComponent::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<LookAtComponent, AZ::Component>()
->Version(1)
->Field("Target", &LookAtComponent::m_targetId)
->Field("ForwardAxis", &LookAtComponent::m_forwardAxis)
;
}
if (auto behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<LookAtComponentRequestBus>("LookAt", "LookAtRequestBus")
->Attribute(AZ::Script::Attributes::Category, "Gameplay")
->Event("SetTarget", &LookAtComponentRequestBus::Events::SetTarget, "Set Target", { { { "Target", "The entity to look at" } } })
->Attribute(AZ::Script::Attributes::ToolTip, "Set the entity to look at")
->Event("SetTargetPosition", &LookAtComponentRequestBus::Events::SetTargetPosition, "Set Target Position", { { { "Position", "The position to look at" } } })
->Attribute(AZ::Script::Attributes::ToolTip, "Sets the target position to look at.")
->Event("SetAxis", &LookAtComponentRequestBus::Events::SetAxis, "Set Axis", { { { "Axis", "The forward axis to use as reference" } } })
->Attribute(AZ::Script::Attributes::ToolTip, "Specify the forward axis to use as reference for the look at")
;
behaviorContext->EBus<LookAtComponentNotificationBus>("LookAtNotification", "LookAtComponentNotificationBus", "Notifications for the Look At Component")
->Attribute(AZ::Script::Attributes::Category, "Gameplay")
->Handler<BehaviorLookAtComponentNotificationBusHandler>();
}
}
//=========================================================================
void LookAtComponent::Activate()
{
LookAtComponentRequestBus::Handler::BusConnect(GetEntityId());
if (m_targetId.IsValid())
{
AZ::EntityBus::Handler::BusConnect(m_targetId);
}
}
//=========================================================================
void LookAtComponent::Deactivate()
{
AZ::TransformNotificationBus::MultiHandler::BusDisconnect();
AZ::EntityBus::Handler::BusDisconnect();
LookAtComponentRequestBus::Handler::BusDisconnect();
}
//=========================================================================
void LookAtComponent::OnEntityActivated(const AZ::EntityId& /*entityId*/)
{
AZ::TransformNotificationBus::MultiHandler::BusConnect(GetEntityId());
AZ::TransformNotificationBus::MultiHandler::BusConnect(m_targetId);
}
//=========================================================================
void LookAtComponent::OnEntityDeactivated(const AZ::EntityId& /*entityId*/)
{
AZ::TransformNotificationBus::MultiHandler::BusDisconnect(GetEntityId());
AZ::TransformNotificationBus::MultiHandler::BusDisconnect(m_targetId);
}
void LookAtComponent::SetTarget(AZ::EntityId targetEntity)
{
if (m_targetId.IsValid())
{
AZ::TransformNotificationBus::MultiHandler::BusDisconnect(m_targetId);
}
m_targetPosition = AZ::Vector3(0, 0, 0);
m_targetId = targetEntity;
AZ::TransformNotificationBus::MultiHandler::BusConnect(m_targetId);
RecalculateTransform();
LookAtComponentNotificationBus::Broadcast(&LookAtComponentNotifications::OnTargetChanged, m_targetId);
}
void LookAtComponent::SetTargetPosition(const AZ::Vector3& targetPosition)
{
if (m_targetId.IsValid())
{
AZ::TransformNotificationBus::MultiHandler::BusDisconnect(m_targetId);
}
m_targetId.SetInvalid();
m_targetPosition = targetPosition;
RecalculateTransform();
LookAtComponentNotificationBus::Broadcast(&LookAtComponentNotifications::OnTargetChanged, m_targetId);
}
void LookAtComponent::SetAxis(AZ::Transform::Axis axis)
{
m_forwardAxis = axis;
RecalculateTransform();
}
//=========================================================================
void LookAtComponent::OnTransformChanged(const AZ::Transform& /*local*/, const AZ::Transform& /*world*/)
{
// See corresponding function in EditorLookAtComponent for comment.
AZ::TickBus::Handler::BusConnect();
}
//=========================================================================
void LookAtComponent::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/)
{
RecalculateTransform();
AZ::TickBus::Handler::BusDisconnect();
}
//=========================================================================
void LookAtComponent::RecalculateTransform()
{
AZ::Vector3 targetPosition = m_targetPosition;
if (m_targetId.IsValid())
{
AZ::Transform targetTM = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(targetTM, m_targetId, &AZ::TransformBus::Events::GetWorldTM);
targetPosition = targetTM.GetTranslation();
}
AZ::TransformNotificationBus::MultiHandler::BusDisconnect(GetEntityId());
{
AZ::Transform currentTM = AZ::Transform::CreateIdentity();
AZ::TransformBus::EventResult(currentTM, GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
AZ::Transform lookAtTransform = AZ::Transform::CreateLookAt(
currentTM.GetTranslation(),
targetPosition,
m_forwardAxis
);
AZ::TransformBus::Event(GetEntityId(), &AZ::TransformInterface::SetWorldTM, lookAtTransform);
}
AZ::TransformNotificationBus::MultiHandler::BusConnect(GetEntityId());
}
} // namespace LmbrCentral
@@ -0,0 +1,124 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Math/Transform.h>
#include <AzCore/Component/Component.h>
#include <AzCore/Component/EntityBus.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/Component/TransformBus.h>
namespace LmbrCentral
{
class LookAtComponentRequests
: public AZ::ComponentBus
{
public:
//! Set the target entity to look at
virtual void SetTarget([[maybe_unused]] AZ::EntityId targetEntity) {}
//! Set the target position to look at
virtual void SetTargetPosition([[maybe_unused]] const AZ::Vector3& position) {}
//! Set the reference forward axis
virtual void SetAxis([[maybe_unused]] AZ::Transform::Axis axis = AZ::Transform::Axis::ZPositive) {}
};
using LookAtComponentRequestBus = AZ::EBus<LookAtComponentRequests>;
class LookAtComponentNotifications
: public AZ::ComponentBus
{
public:
//! Notifies you that the target has changed
virtual void OnTargetChanged(AZ::EntityId) { }
};
using LookAtComponentNotificationBus = AZ::EBus<LookAtComponentNotifications>;
//=========================================================================
// LookAtComponent
//=========================================================================
class LookAtComponent
: public AZ::Component
, private AZ::TransformNotificationBus::MultiHandler
, private AZ::TickBus::Handler
, private AZ::EntityBus::Handler
, private LookAtComponentRequestBus::Handler
{
public:
friend class EditorLookAtComponent;
AZ_COMPONENT(LookAtComponent, "{11CDC627-25A9-4760-A61F-576CDB189B38}");
//=====================================================================
// AZ::Component
void Activate() override;
void Deactivate() override;
//=====================================================================
//=====================================================================
// TransformBus
void OnTransformChanged(const AZ::Transform& /*local*/, const AZ::Transform& /*world*/) override;
//=====================================================================
//=====================================================================
// TickBus
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
//=====================================================================
//=====================================================================
// EntityBus
void OnEntityActivated(const AZ::EntityId& entityId) override;
void OnEntityDeactivated(const AZ::EntityId& entityId) override;
//=====================================================================
//=====================================================================
// LookAtComponentRequestBus
void SetTarget(AZ::EntityId targetEntity) override;
void SetTargetPosition(const AZ::Vector3& targetPosition) override;
void SetAxis(AZ::Transform::Axis axis) override;
//=====================================================================
protected:
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("LookAtService", 0x34230406));
}
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC("TransformService", 0x8ee22c50));
}
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("LookAtService", 0x34230406));
}
private:
void RecalculateTransform();
// Serialized data
AZ::EntityId m_targetId;
AZ::Vector3 m_targetPosition;
AZ::Transform::Axis m_forwardAxis;
};
}//namespace LmbrCentral
@@ -0,0 +1,176 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "LmbrCentral_precompiled.h"
#include "RandomTimedSpawnerComponent.h"
#include <AzCore/RTTI/BehaviorContext.h>
#include <LmbrCentral/Shape/ShapeComponentBus.h>
#include <LmbrCentral/Scripting/SpawnerComponentBus.h>
#include <AzCore/Component/TransformBus.h>
namespace LmbrCentral
{
void RandomTimedSpawnerConfiguration::Reflect(AZ::ReflectContext * context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<RandomTimedSpawnerConfiguration>()
->Version(1)
->Field("Enabled", &RandomTimedSpawnerConfiguration::m_enabled)
->Field("RandomDistribution", &RandomTimedSpawnerConfiguration::m_randomDistribution)
->Field("SpawnDelay", &RandomTimedSpawnerConfiguration::m_spawnDelay)
->Field("SpawnDelayVariation", &RandomTimedSpawnerConfiguration::m_spawnDelayVariation)
;
}
if (AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context))
{
behaviorContext->EBus<RandomTimedSpawnerComponentRequestBus>("RandomTimedSpawnerRequestBus")
->Attribute(AZ::Script::Attributes::ExcludeFrom, AZ::Script::Attributes::ExcludeFlags::Preview)
->Event("Enable", &RandomTimedSpawnerComponentRequestBus::Events::Enable)
->Event("Disable", &RandomTimedSpawnerComponentRequestBus::Events::Disable)
->Event("Toggle", &RandomTimedSpawnerComponentRequestBus::Events::Toggle)
->Event("IsEnabled", &RandomTimedSpawnerComponentRequestBus::Events::IsEnabled)
->Event("SetRandomDistribution", &RandomTimedSpawnerComponentRequestBus::Events::SetRandomDistribution)
->Event("GetRandomDistribution", &RandomTimedSpawnerComponentRequestBus::Events::GetRandomDistribution)
->VirtualProperty("RandomDistribution", "GetRandomDistribution", "SetRandomDistribution")
->Event("SetSpawnDelay", &RandomTimedSpawnerComponentRequestBus::Events::SetSpawnDelay)
->Event("GetSpawnDelay", &RandomTimedSpawnerComponentRequestBus::Events::GetSpawnDelay)
->VirtualProperty("SpawnDelay", "GetSpawnDelay", "SetSpawnDelay")
->Event("SetSpawnDelayVariation", &RandomTimedSpawnerComponentRequestBus::Events::SetSpawnDelayVariation)
->Event("GetSpawnDelayVariation", &RandomTimedSpawnerComponentRequestBus::Events::GetSpawnDelayVariation)
->VirtualProperty("SpawnDelayVariation", "GetSpawnDelayVariation", "SetSpawnDelayVariation")
;
behaviorContext->Class<RandomTimedSpawnerComponent>()->RequestBus("RandomTimedSpawnerRequestBus");
}
}
void RandomTimedSpawnerComponent::Reflect(AZ::ReflectContext * context)
{
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<RandomTimedSpawnerComponent, AZ::Component>()
->Version(1)
->Field("m_config", &RandomTimedSpawnerComponent::m_config)
;
}
RandomTimedSpawnerConfiguration::Reflect(context);
}
void RandomTimedSpawnerComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("RandomTimedSpawnerService", 0x56f2fa36));
}
void RandomTimedSpawnerComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
//Only compatible with Box and Cylinder shapes
incompatible.push_back(AZ_CRC("CapsuleShapeService", 0x9bc1122c));
incompatible.push_back(AZ_CRC("SphereShapeService", 0x90c8dc80));
incompatible.push_back(AZ_CRC("CompoundShapeService", 0x4f7c640a));
}
void RandomTimedSpawnerComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
{
required.push_back(AZ_CRC("TransformService", 0x8ee22c50));
required.push_back(AZ_CRC("ShapeService", 0xe86aa5fe));
required.push_back(AZ_CRC("SpawnerService", 0xd2f1d7a3));
}
void RandomTimedSpawnerComponent::Activate()
{
AZStd::chrono::system_clock::time_point now = AZStd::chrono::system_clock::now();
m_currentTime = AZ::ScriptTimePoint(now).GetSeconds();
RandomTimedSpawnerComponentRequestBus::Handler::BusConnect(GetEntityId());
CalculateNextSpawnTime();
if (m_config.m_enabled)
{
AZ::TickBus::Handler::BusConnect();
}
}
void RandomTimedSpawnerComponent::Deactivate()
{
if (m_config.m_enabled)
{
AZ::TickBus::Handler::BusDisconnect();
}
RandomTimedSpawnerComponentRequestBus::Handler::BusDisconnect();
}
void RandomTimedSpawnerComponent::OnTick([[maybe_unused]] float deltaTime, AZ::ScriptTimePoint time)
{
m_currentTime = time.GetSeconds();
if (m_currentTime >= m_nextSpawnTime)
{
AZ::Transform spawnTransform = AZ::Transform::CreateIdentity();
spawnTransform.SetTranslation(CalculateNextSpawnPosition());
LmbrCentral::SpawnerComponentRequestBus::Event(GetEntityId(), &LmbrCentral::SpawnerComponentRequestBus::Events::SpawnAbsolute, spawnTransform);
CalculateNextSpawnTime();
}
}
void RandomTimedSpawnerComponent::Enable()
{
m_config.m_enabled = true;
AZ::TickBus::Handler::BusConnect();
}
void RandomTimedSpawnerComponent::Disable()
{
m_config.m_enabled = false;
AZ::TickBus::Handler::BusDisconnect();
}
void RandomTimedSpawnerComponent::Toggle()
{
m_config.m_enabled = !m_config.m_enabled;
if (m_config.m_enabled)
{
AZ::TickBus::Handler::BusConnect();
}
else
{
AZ::TickBus::Handler::BusDisconnect();
}
}
void RandomTimedSpawnerComponent::CalculateNextSpawnTime()
{
m_randomDistribution = std::uniform_real_distribution<double>(-m_config.m_spawnDelayVariation, m_config.m_spawnDelayVariation);
double variation = m_randomDistribution(m_randomEngine);
double spawnDelay = m_config.m_spawnDelay + variation;
m_nextSpawnTime = m_currentTime + spawnDelay;
}
AZ::Vector3 RandomTimedSpawnerComponent::CalculateNextSpawnPosition()
{
AZ::Vector3 spawnPos = AZ::Vector3::CreateZero();
//This spawnPos is untransformed
LmbrCentral::ShapeComponentRequestsBus::EventResult(spawnPos, GetEntityId(), &LmbrCentral::ShapeComponentRequestsBus::Events::GenerateRandomPointInside, m_config.m_randomDistribution);
return spawnPos;
}
} //namespace LmbrCentral
@@ -0,0 +1,106 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/Component.h>
#include <AzCore/Component/TickBus.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Math/Transform.h>
#include <AzCore/Slice/SliceAsset.h>
#include <LmbrCentral/Scripting/RandomTimedSpawnerComponentBus.h>
#include <AzCore/Math/Random.h>
#include <random>
namespace LmbrCentral
{
/**
* Configuration for the RandomTimedSpawnerComponent
*/
class RandomTimedSpawnerConfiguration
{
public:
AZ_TYPE_INFO(RandomTimedSpawnerConfiguration, "4133644F-FADA-4C82-A2A2-B587B20E81FA");
static void Reflect(AZ::ReflectContext* context);
bool m_enabled = true;
AZ::RandomDistributionType m_randomDistribution = AZ::RandomDistributionType::UniformReal;
double m_spawnDelay = 5.0;
double m_spawnDelayVariation = 0.0;
};
/**
* A component to spawn slices at regular intervals
* at random points inside of a volume.
*/
class RandomTimedSpawnerComponent
: public AZ::Component
, public AZ::TickBus::Handler
, public RandomTimedSpawnerComponentRequestBus::Handler
{
public:
AZ_COMPONENT(RandomTimedSpawnerComponent, RandomTimedSpawnerComponentTypeId);
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
RandomTimedSpawnerComponent() {}
explicit RandomTimedSpawnerComponent(RandomTimedSpawnerConfiguration *params)
{
m_config = *params;
}
~RandomTimedSpawnerComponent() {};
// AZ::Component
void Activate() override;
void Deactivate() override;
// TickBus
void OnTick(float deltaTime, AZ::ScriptTimePoint time) override;
// RandomTimedSpawnerRequestBus
void Enable() override;
void Disable() override;
void Toggle() override;
bool IsEnabled() override { return m_config.m_enabled; }
void SetRandomDistribution(AZ::RandomDistributionType randomDistribution) override { m_config.m_randomDistribution = randomDistribution; }
AZ::RandomDistributionType GetRandomDistribution() override { return m_config.m_randomDistribution; }
void SetSpawnDelay(double spawnDelay) override { m_config.m_spawnDelay = spawnDelay; }
double GetSpawnDelay() override { return m_config.m_spawnDelay; }
void SetSpawnDelayVariation(double spawnDelayVariation) override { m_config.m_spawnDelayVariation = spawnDelayVariation; }
double GetSpawnDelayVariation() override { return m_config.m_spawnDelayVariation; }
private:
//Reflected members
RandomTimedSpawnerConfiguration m_config;
//Unreflected members
double m_currentTime;
double m_nextSpawnTime;
std::default_random_engine m_randomEngine;
std::uniform_real_distribution<double> m_randomDistribution;
void CalculateNextSpawnTime();
AZ::Vector3 CalculateNextSpawnPosition();
};
} //namespace LmbrCentral
@@ -0,0 +1,476 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "LmbrCentral_precompiled.h"
#include <AzCore/Serialization/EditContext.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include "SimpleStateComponent.h"
namespace
{
const char * NullStateName = "<None>";
const char * NewStateName = "New State";
}
namespace LmbrCentral
{
// BehaviorContext SimpleStateComponentNotificationBus forwarder
class BehaviorSimpleStateComponentNotificationBusHandler : public SimpleStateComponentNotificationBus::Handler, public AZ::BehaviorEBusHandler
{
public:
AZ_EBUS_BEHAVIOR_BINDER(BehaviorSimpleStateComponentNotificationBusHandler, "{F935125C-AE4E-48C1-BB60-24A0559BC4D2}", AZ::SystemAllocator,
OnStateChanged);
void OnStateChanged(const char* oldState, const char* newState)
{
Call(FN_OnStateChanged, oldState, newState);
}
};
//=========================================================================
// ForEachEntity
//=========================================================================
template <class Func>
void ForEachEntity(AZStd::vector<AZ::EntityId>& entitiyIds, Func entityFunction)
{
for (const AZ::EntityId& currId : entitiyIds)
{
AZ::Entity* entity = nullptr;
EBUS_EVENT_RESULT(entity, AZ::ComponentApplicationBus, FindEntity, currId);
if (entity)
{
entityFunction(entity);
}
}
}
//=========================================================================
// State::State
//=========================================================================
State::State()
: m_name(NewStateName)
{
}
//=========================================================================
// State::Init
//=========================================================================
void State::Init()
{
UpdateNameCrc();
for (const AZ::EntityId& currId : m_entityIds)
{
// Listen for the entity's initialization so we can adjust initial activation state.
AZ::EntityBus::MultiHandler::BusConnect(currId);
}
}
//=========================================================================
// State::Activate
//=========================================================================
void State::Activate()
{
ForEachEntity(m_entityIds,
[](AZ::Entity* entity)
{
if (entity->GetState() != AZ::Entity::State::Active)
{
entity->Activate();
}
}
);
}
//=========================================================================
// State::Deactivate
//=========================================================================
void State::Deactivate()
{
ForEachEntity(m_entityIds,
[](AZ::Entity* entity)
{
if (entity->GetState() == AZ::Entity::State::Active)
{
entity->Deactivate();
}
}
);
}
//=========================================================================
// State::UpdateNameCrc
//=========================================================================
void State::UpdateNameCrc()
{
m_nameCrc = AZ::Crc32(m_name.c_str());
}
//=========================================================================
// GetStateFromName
//=========================================================================
State* State::FindWithName(AZStd::vector<State>& states, const char* stateName)
{
if (stateName)
{
const AZ::Crc32 stateNameCrc(stateName);
for (State& currState : states)
{
if ((currState.m_nameCrc == stateNameCrc) && (currState.m_name == stateName))
{
return &currState;
}
}
AZ_Error("SimpleStateComponent", false, "StateName '%s' does not map to any existing states", stateName);
}
return nullptr;
}
//=========================================================================
// OnEntityExists
//=========================================================================
void State::OnEntityExists(const AZ::EntityId& entityId)
{
AZ::EntityBus::MultiHandler::BusDisconnect(entityId);
// Mark the entity to not be activated by default.
AZ::Entity* entity = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, entityId);
if (entity && entity->GetState() <= AZ::Entity::State::Init)
{
entity->SetRuntimeActiveByDefault(false);
}
}
//=========================================================================
// SimpleStateComponent::Reflect
//=========================================================================
void State::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<State>()
->Version(1)
->Field("Name", &State::m_name)
->Field("EntityIds", &State::m_entityIds);
AZ::EditContext* editContext = serializeContext->GetEditContext();
if (editContext)
{
editContext->Class<State>("State", "A state includes a name and set of entities that will be activated when the state is entered and deactivated when the state is left.")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->DataElement(0, &State::m_name, "Name", "The name of this state")
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues", 0xcbc2147c))
->DataElement(0, &State::m_entityIds, "Entities", "The list of entities referenced by this state")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues", 0xcbc2147c));
}
}
}
//=========================================================================
// SimpleStateComponent::Reflect
//=========================================================================
void SimpleStateComponent::Reflect(AZ::ReflectContext* context)
{
State::Reflect(context);
if (AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<SimpleStateComponent, AZ::Component>()
->Version(1)
->Field("InitialStateName", &SimpleStateComponent::m_initialStateName)
->Field("ResetOnActivate", &SimpleStateComponent::m_resetStateOnActivate)
->Field("States", &SimpleStateComponent::m_states);
AZ::EditContext* editContext = serializeContext->GetEditContext();
if (editContext)
{
editContext->Class<SimpleStateComponent>("Simple State", "The Simple State component provides a simple state machine allowing activation and deactivation of associated entities")
->ClassElement(AZ::Edit::ClassElements::EditorData, "")
->Attribute(AZ::Edit::Attributes::Category, "Gameplay")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game", 0x232b318c))
->Attribute(AZ::Edit::Attributes::Icon, "Editor/Icons/Components/SimpleState.svg")
->Attribute(AZ::Edit::Attributes::ViewportIcon, "Editor/Icons/Components/Viewport/SimpleState.png")
->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://docs.aws.amazon.com/lumberyard/latest/userguide/component-simple-state.html")
->DataElement(AZ::Edit::UIHandlers::ComboBox, &SimpleStateComponent::m_initialStateName, "Initial state", "The initial active state")
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues", 0xcbc2147c))
->Attribute(AZ::Edit::Attributes::StringList, &SimpleStateComponent::GetStateNames)
->DataElement(0, &SimpleStateComponent::m_resetStateOnActivate, "Reset on activate", "If set, SimpleState will return to the configured initial state when activated, and not the state held prior to being deactivated.")
->DataElement(0, &SimpleStateComponent::m_states, "States", "The list of states")
->Attribute(AZ::Edit::Attributes::AutoExpand, true)
->Attribute(AZ::Edit::Attributes::ChangeNotify, AZ_CRC("RefreshAttributesAndValues", 0xcbc2147c));
}
}
AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
if (behaviorContext)
{
behaviorContext->EBus<SimpleStateComponentRequestBus>("SimpleStateComponentRequestBus")
->Event("SetState", &SimpleStateComponentRequestBus::Events::SetState)
->Event("SetStateByIndex", &SimpleStateComponentRequestBus::Events::SetStateByIndex)
->Event("SetToNextState", &SimpleStateComponentRequestBus::Events::SetToNextState)
->Event("SetToPreviousState", &SimpleStateComponentRequestBus::Events::SetToPreviousState)
->Event("SetToFirstState", &SimpleStateComponentRequestBus::Events::SetToFirstState)
->Event("SetToLastState", &SimpleStateComponentRequestBus::Events::SetToLastState)
->Event("GetNumStates", &SimpleStateComponentRequestBus::Events::GetNumStates)
->Event("GetCurrentState", &SimpleStateComponentRequestBus::Events::GetCurrentState)
;
behaviorContext->EBus<SimpleStateComponentNotificationBus>("SimpleStateComponentNotificationBus")
->Handler<BehaviorSimpleStateComponentNotificationBusHandler>()
;
}
}
//=========================================================================
// SimpleStateComponent::SimpleStateComponent
//=========================================================================
SimpleStateComponent::SimpleStateComponent()
: m_initialStateName(NullStateName)
{
}
//=========================================================================
// SimpleStateComponent::Init
//=========================================================================
void SimpleStateComponent::Init()
{
for (State& currState : m_states)
{
currState.Init();
}
// Prior revisions used the empty string as null state
const char * initialStateName = (m_initialStateName.empty() || (m_initialStateName == NullStateName)) ? nullptr : m_initialStateName.c_str();
m_initialState = State::FindWithName(m_states, initialStateName);
m_currentState = m_initialState;
}
//=========================================================================
// SimpleStateComponent::Activate
//=========================================================================
void SimpleStateComponent::Activate()
{
if (m_resetStateOnActivate)
{
m_currentState = m_initialState;
}
for (State& currState : m_states)
{
if (&currState != m_currentState)
{
currState.Deactivate();
}
}
if (m_currentState)
{
m_currentState->Activate();
}
SimpleStateComponentRequestBus::Handler::BusConnect(GetEntityId());
if (m_currentState)
{
// Notify newly activated State
// Note: Even if !m_resetStateOnActivate, the prior state to Activation is NullState
EBUS_EVENT_ID(GetEntityId(), SimpleStateComponentNotificationBus, OnStateChanged, nullptr, m_currentState->GetNameCStr());
}
}
//=========================================================================
// SimpleStateComponent::Deactivate
//=========================================================================
void SimpleStateComponent::Deactivate()
{
SimpleStateComponentRequestBus::Handler::BusDisconnect();
if (m_currentState)
{
m_currentState->Deactivate();
}
}
//=========================================================================
// SimpleStateComponent::SetStateInternal
//=========================================================================
void SimpleStateComponent::SetStateInternal(State* newState)
{
// Out with the old
const char* oldName = nullptr;
if (m_currentState)
{
oldName = m_currentState->GetNameCStr();
m_currentState->Deactivate();
}
// In with the new
const char* newName = nullptr;
if (newState)
{
newName = newState->GetNameCStr();
newState->Activate();
}
if (m_currentState != newState)
{
m_currentState = newState;
EBUS_EVENT_ID(GetEntityId(), SimpleStateComponentNotificationBus, OnStateChanged, oldName, newName);
}
}
//=========================================================================
// SimpleStateComponent::SetState
//=========================================================================
void SimpleStateComponent::SetState(const char* stateName)
{
State* newState = State::FindWithName(m_states, stateName);
SetStateInternal(newState);
}
//=========================================================================
// SimpleStateComponent::SetStateByIndex
//=========================================================================
void SimpleStateComponent::SetStateByIndex(AZ::u32 stateIndex)
{
State* newState;
if (stateIndex < m_states.size())
{
newState = &m_states[stateIndex];
}
else
{
newState = nullptr;
AZ_Error("SimpleStateComponent", false, "StateName index '%d' is invalid (currently %d states)", stateIndex, static_cast<AZ::u32>(m_states.size()));
}
SetStateInternal(newState);
}
//=========================================================================
// SimpleStateComponent::SetToNextState
//=========================================================================
void SimpleStateComponent::SetToNextState()
{
if (!m_states.empty())
{
SetStateToOffset(1, m_states.front());
}
}
//=========================================================================
// SimpleStateComponent::SetToPreviousState
//=========================================================================
void SimpleStateComponent::SetToPreviousState()
{
if (!m_states.empty())
{
SetStateToOffset(-1, m_states.back());
}
}
//=========================================================================
// SimpleStateComponent::SetStateToOffset
//=========================================================================
void SimpleStateComponent::SetStateToOffset(AZ::s32 offset, State& fromNullState)
{
AZ_Assert(!m_states.empty(), "violated precondition - must be non-empty");
State* newState;
if (m_currentState)
{
const size_t currentStateIndex = m_currentState - &m_states[0];
AZ_Assert(currentStateIndex < m_states.size(), "Invalid current state pointer");
const size_t numStates = m_states.size();
const size_t newStateIndex = (currentStateIndex + numStates + offset) % numStates;
AZ_Assert(newStateIndex < numStates, "Invalid negative offset");
newState = &m_states[newStateIndex];
}
else
{
// "Advance" to the provided state from NullState
newState = &fromNullState;
}
SetStateInternal(newState);
}
//=========================================================================
// SimpleStateComponent::SetToFirstState
//=========================================================================
void SimpleStateComponent::SetToFirstState()
{
if (!m_states.empty())
{
SetStateInternal(&m_states.front());
}
}
//=========================================================================
// SimpleStateComponent::SetToLastState
//=========================================================================
void SimpleStateComponent::SetToLastState()
{
if (!m_states.empty())
{
SetStateInternal(&m_states.back());
}
}
//=========================================================================
// SimpleStateComponent::GetNumStates
//=========================================================================
AZ::u32 SimpleStateComponent::GetNumStates()
{
return static_cast<AZ::u32>(m_states.size());
}
//=========================================================================
// SimpleStateComponent::GetState
//=========================================================================
const char* SimpleStateComponent::GetCurrentState()
{
return m_currentState ? m_currentState->GetNameCStr() : nullptr;
}
//=========================================================================
// SimpleStateComponent::GetStateNames
//=========================================================================
AZStd::vector<AZStd::string> SimpleStateComponent::GetStateNames() const
{
AZStd::vector<AZStd::string> names;
names.reserve(m_states.size() + 1);
// Provide "no initial state" as an option
names.emplace_back(NullStateName);
for (const State& currState : m_states)
{
names.emplace_back(currState.GetName());
}
return names;
}
} // namespace LmbrCentral
@@ -0,0 +1,133 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/std/containers/vector.h>
#include <AzCore/Component/Component.h>
#include <AzCore/Component/EntityBus.h>
#include <LmbrCentral/Scripting/SimpleStateComponentBus.h>
namespace LmbrCentral
{
/**
* State
*
* Structure describing a single state
*/
class State
: private AZ::EntityBus::MultiHandler
{
public:
AZ_TYPE_INFO(State, "{97BCF9D8-A76D-456F-A4B8-98EFF6897CE7}");
State();
void Init();
void Activate();
void Deactivate();
const char* GetNameCStr() const
{
return m_name.c_str();
}
const AZStd::string& GetName() const
{
return m_name;
}
static State* FindWithName(AZStd::vector<State>& states, const char* stateName);
static void Reflect(AZ::ReflectContext* context);
private:
//////////////////////////////////////////////////////////////////////////
// EntityBus::Handler
void OnEntityExists(const AZ::EntityId& entityId) override;
//////////////////////////////////////////////////////////////////////////
AZ::Crc32 OnStateNameChanged();
void UpdateNameCrc();
// runtime value, not serialized
AZ::Crc32 m_nameCrc;
// serialized values
AZStd::string m_name;
AZStd::vector<AZ::EntityId> m_entityIds;
};
/**
* SimpleStateComponent
*
* SimpleState provides a simple state machine.
*
* Each state is represented by a name and zero or more entities to activate when entered and deactivate when the state is left.
*/
class SimpleStateComponent
: public AZ::Component
, public SimpleStateComponentRequestBus::Handler
{
public:
AZ_COMPONENT(SimpleStateComponent, "{242D4707-BC72-4245-AC96-BCEE38BBC1B7}");
SimpleStateComponent();
~SimpleStateComponent() override {}
//////////////////////////////////////////////////////////////////////////
// AZ::Component
void Init() override;
void Activate() override;
void Deactivate() override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// SimpleStateComponentRequestBus::Handler
//////////////////////////////////////////////////////////////////////////
void SetState(const char* stateName) override;
void SetStateByIndex(AZ::u32 stateIndex) override;
void SetToNextState() override;
void SetToPreviousState() override;
void SetToFirstState() override;
void SetToLastState() override;
AZ::u32 GetNumStates() override;
const char* GetCurrentState() override;
private:
//////////////////////////////////////////////////////////////////////////
// Component descriptor
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("SimpleStateService", 0xbfba531e));
}
//////////////////////////////////////////////////////////////////////////
// Helpers
AZStd::vector<AZStd::string> GetStateNames() const;
void SetStateInternal(State * newState);
void SetStateToOffset(AZ::s32 offset, State& fromNullState);
//////////////////////////////////////////////////////////////////////////
// Runtime state, not serialized
State* m_initialState = nullptr;
State* m_currentState = nullptr;
// Serialized
AZStd::string m_initialStateName;
AZStd::vector<State> m_states;
bool m_resetStateOnActivate = true;
};
} // namespace LmbrCentral
@@ -0,0 +1,547 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "LmbrCentral_precompiled.h"
#include <AzCore/Component/TickBus.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzCore/std/sort.h>
#include <AzCore/Asset/AssetManager.h>
#include <AzFramework/Entity/GameEntityContextBus.h>
#include <AzFramework/Entity/SliceGameEntityOwnershipServiceBus.h>
#include "SpawnerComponent.h"
#ifdef LMBR_CENTRAL_EDITOR
#include "EditorSpawnerComponent.h"
#endif
namespace LmbrCentral
{
// BehaviorContext SpawnerComponentNotificationBus forwarder
class BehaviorSpawnerComponentNotificationBusHandler : public SpawnerComponentNotificationBus::Handler, public AZ::BehaviorEBusHandler
{
public:
AZ_EBUS_BEHAVIOR_BINDER(BehaviorSpawnerComponentNotificationBusHandler, "{AC202871-2522-48A6-9B62-5FDAABB302CD}", AZ::SystemAllocator,
OnSpawnBegin, OnSpawnEnd, OnEntitySpawned, OnSpawnedSliceDestroyed, OnEntitiesSpawned);
void OnSpawnBegin(const AzFramework::SliceInstantiationTicket& ticket) override
{
Call(FN_OnSpawnBegin, ticket);
}
void OnSpawnEnd(const AzFramework::SliceInstantiationTicket& ticket) override
{
Call(FN_OnSpawnEnd, ticket);
}
void OnEntitySpawned(const AzFramework::SliceInstantiationTicket& ticket, const AZ::EntityId& id) override
{
Call(FN_OnEntitySpawned, ticket, id);
}
void OnSpawnedSliceDestroyed(const AzFramework::SliceInstantiationTicket& ticket) override
{
Call(FN_OnSpawnedSliceDestroyed, ticket);
}
//! Single event notification for an entire slice spawn, providing a list of all resulting entity Ids.
void OnEntitiesSpawned(const AzFramework::SliceInstantiationTicket& ticket, const AZStd::vector<AZ::EntityId>& spawnedEntities) override
{
Call(FN_OnEntitiesSpawned, ticket, spawnedEntities);
}
};
// Convert any instances of the old SampleComponent data into the appropriate
// modern editor-component or game-component.
bool ConvertLegacySpawnerComponent(AZ::SerializeContext& serializeContext, AZ::SerializeContext::DataElementNode& classNode)
{
// To determine whether we want an editor or runtime component, we check
// if the old component was contained within GenericComponentWrapper::m_template.
bool isEditorComponent = (classNode.GetName() == AZ::Crc32("m_template"));
// Get Component::m_id from the base class.
AZ::u64 componentId = 0;
if (auto baseClassNode = classNode.FindSubElement(AZ::Crc32("BaseClass1")))
{
baseClassNode->GetChildData(AZ::Crc32("Id"), componentId);
}
// Get data values.
SpawnerConfig config;
classNode.GetChildData(AZ::Crc32("Slice"), config.m_sliceAsset);
classNode.GetChildData(AZ::Crc32("SpawnOnActivate"), config.m_spawnOnActivate);
classNode.GetChildData(AZ::Crc32("DestroyOnDeactivate"), config.m_destroyOnDeactivate);
// Convert this node into the appropriate component-type.
// Note that converting the node will clear all child data nodes.
#ifdef LMBR_CENTRAL_EDITOR
if (isEditorComponent)
{
classNode.Convert(serializeContext, azrtti_typeid<EditorSpawnerComponent>());
// Create a temporary editor-component and write its contents to this node
EditorSpawnerComponent component;
component.SetId(componentId);
component.SetConfiguration(config);
classNode.SetData(serializeContext, component);
}
else
#endif // LMBR_CENTRAL_EDITOR
{
classNode.Convert(serializeContext, azrtti_typeid<SpawnerComponent>());
// Create a temporary game-component and write its contents to this classNode
SpawnerComponent component;
component.SetId(componentId);
component.SetConfiguration(config);
classNode.SetData(serializeContext, component);
}
return true;
}
//=========================================================================
void SpawnerComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->ClassDeprecate("SpawnerComponent", DeprecatedSpawnerComponentTypeId, ConvertLegacySpawnerComponent);
serializeContext->Class<SpawnerComponent, AZ::Component>()
->Version(1)
->Field("Slice", &SpawnerComponent::m_sliceAsset)
->Field("SpawnOnActivate", &SpawnerComponent::m_spawnOnActivate)
->Field("DestroyOnDeactivate", &SpawnerComponent::m_destroyOnDeactivate)
;
}
AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
if (behaviorContext)
{
behaviorContext->EBus<SpawnerComponentRequestBus>("SpawnerComponentRequestBus")
->Event("Spawn", &SpawnerComponentRequestBus::Events::Spawn)
->Event("SpawnRelative", &SpawnerComponentRequestBus::Events::SpawnRelative)
->Event("SpawnAbsolute", &SpawnerComponentRequestBus::Events::SpawnAbsolute)
->Event("DestroySpawnedSlice", &SpawnerComponentRequestBus::Events::DestroySpawnedSlice)
->Event("DestroyAllSpawnedSlices", &SpawnerComponentRequestBus::Events::DestroyAllSpawnedSlices)
->Event("GetCurrentlySpawnedSlices", &SpawnerComponentRequestBus::Events::GetCurrentlySpawnedSlices)
->Event("HasAnyCurrentlySpawnedSlices", &SpawnerComponentRequestBus::Events::HasAnyCurrentlySpawnedSlices)
->Event("GetCurrentEntitiesFromSpawnedSlice", &SpawnerComponentRequestBus::Events::GetCurrentEntitiesFromSpawnedSlice)
->Event("GetAllCurrentlySpawnedEntities", &SpawnerComponentRequestBus::Events::GetAllCurrentlySpawnedEntities)
->Event("SetDynamicSlice", &SpawnerComponentRequestBus::Events::SetDynamicSliceByAssetId)
;
behaviorContext->EBus<SpawnerComponentNotificationBus>("SpawnerComponentNotificationBus")
->Handler<BehaviorSpawnerComponentNotificationBusHandler>()
;
behaviorContext->Constant("SpawnerComponentTypeId", BehaviorConstant(SpawnerComponentTypeId));
behaviorContext->Class<SpawnerConfig>()
//->Property("sliceAsset", BehaviorValueProperty(&SpawnerConfig::m_sliceAsset))
->Property("spawnOnActivate", BehaviorValueProperty(&SpawnerConfig::m_spawnOnActivate))
->Property("destroyOnDeactivate", BehaviorValueProperty(&SpawnerConfig::m_destroyOnDeactivate))
;
}
}
//=========================================================================
void SpawnerComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("SpawnerService", 0xd2f1d7a3));
}
//=========================================================================
void SpawnerComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType&)
{
}
//=========================================================================
void SpawnerComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent)
{
dependent.push_back(AZ_CRC("TransformService", 0x8ee22c50));
}
//=========================================================================
SpawnerComponent::SpawnerComponent()
{
// Slice asset should load purely on-demand.
m_sliceAsset.SetAutoLoadBehavior(AZ::Data::AssetLoadBehavior::NoLoad);
}
//=========================================================================
void SpawnerComponent::Activate()
{
SpawnerComponentRequestBus::Handler::BusConnect(GetEntityId());
if (m_spawnOnActivate)
{
SpawnSliceInternalRelative(m_sliceAsset, AZ::Transform::Identity());
}
}
//=========================================================================
void SpawnerComponent::Deactivate()
{
SpawnerComponentRequestBus::Handler::BusDisconnect();
AzFramework::SliceInstantiationResultBus::MultiHandler::BusDisconnect();
AZ::EntityBus::MultiHandler::BusDisconnect();
AZ::Data::AssetBus::Handler::BusDisconnect();
if (m_destroyOnDeactivate)
{
DestroyAllSpawnedSlices();
}
m_activeTickets.clear();
m_entityToTicketMap.clear();
m_ticketToEntitiesMap.clear();
}
bool SpawnerComponent::ReadInConfig(const AZ::ComponentConfig* spawnerConfig)
{
if (auto config = azrtti_cast<const SpawnerConfig*>(spawnerConfig))
{
m_sliceAsset = config->m_sliceAsset;
m_spawnOnActivate = config->m_spawnOnActivate;
m_destroyOnDeactivate = config->m_destroyOnDeactivate;
return true;
}
return false;
}
bool SpawnerComponent::WriteOutConfig(AZ::ComponentConfig* outSpawnerConfig) const
{
if (auto config = azrtti_cast<SpawnerConfig*>(outSpawnerConfig))
{
config->m_sliceAsset = m_sliceAsset;
config->m_spawnOnActivate = m_spawnOnActivate;
config->m_destroyOnDeactivate = m_destroyOnDeactivate;
return true;
}
return false;
}
//=========================================================================
void SpawnerComponent::SetDynamicSlice(const AZ::Data::Asset<AZ::DynamicSliceAsset>& dynamicSliceAsset)
{
m_sliceAsset = dynamicSliceAsset;
}
//=========================================================================
void SpawnerComponent::SetDynamicSliceByAssetId(AZ::Data::AssetId& assetId)
{
auto sliceAsset = AZ::Data::AssetManager::Instance().GetAsset(assetId, AZ::AzTypeInfo<AZ::DynamicSliceAsset>::Uuid(), m_sliceAsset.GetAutoLoadBehavior());
if (sliceAsset.IsReady())
{
m_sliceAsset = sliceAsset;
}
else
{
AZ::Data::AssetBus::Handler::BusDisconnect();
AZ::Data::AssetBus::Handler::BusConnect(assetId);
}
}
//=========================================================================
void SpawnerComponent::SetSpawnOnActivate(bool spawnOnActivate)
{
m_spawnOnActivate = spawnOnActivate;
}
//=========================================================================
bool SpawnerComponent::GetSpawnOnActivate()
{
return m_spawnOnActivate;
}
//=========================================================================
AzFramework::SliceInstantiationTicket SpawnerComponent::Spawn()
{
return SpawnSliceInternalRelative(m_sliceAsset, AZ::Transform::Identity());
}
//=========================================================================
AzFramework::SliceInstantiationTicket SpawnerComponent::SpawnRelative(const AZ::Transform& relative)
{
return SpawnSliceInternalRelative(m_sliceAsset, relative);
}
//=========================================================================
AzFramework::SliceInstantiationTicket SpawnerComponent::SpawnAbsolute(const AZ::Transform& world)
{
return SpawnSliceInternalAbsolute(m_sliceAsset, world);
}
//=========================================================================
AzFramework::SliceInstantiationTicket SpawnerComponent::SpawnSlice(const AZ::Data::Asset<AZ::Data::AssetData>& slice)
{
return SpawnSliceInternalRelative(slice, AZ::Transform::Identity());
}
//=========================================================================
AzFramework::SliceInstantiationTicket SpawnerComponent::SpawnSliceRelative(const AZ::Data::Asset<AZ::Data::AssetData>& slice, const AZ::Transform& relative)
{
return SpawnSliceInternalRelative(slice, relative);
}
//=========================================================================
AzFramework::SliceInstantiationTicket SpawnerComponent::SpawnSliceAbsolute(const AZ::Data::Asset<AZ::Data::AssetData>& slice, const AZ::Transform& world)
{
return SpawnSliceInternalAbsolute(slice, world);
}
//=========================================================================
AzFramework::SliceInstantiationTicket SpawnerComponent::SpawnSliceInternalAbsolute(const AZ::Data::Asset<AZ::Data::AssetData>& slice, const AZ::Transform& world)
{
AzFramework::SliceInstantiationTicket ticket;
AzFramework::SliceGameEntityOwnershipServiceRequestBus::BroadcastResult(ticket,
&AzFramework::SliceGameEntityOwnershipServiceRequests::InstantiateDynamicSlice, slice, world, nullptr);
if (ticket.IsValid())
{
m_activeTickets.emplace_back(ticket);
m_ticketToEntitiesMap.emplace(ticket); // create entry for ticket, with no entities listed yet
AzFramework::SliceInstantiationResultBus::MultiHandler::BusConnect(ticket);
}
return ticket;
}
//=========================================================================
AzFramework::SliceInstantiationTicket SpawnerComponent::SpawnSliceInternalRelative(const AZ::Data::Asset<AZ::Data::AssetData>& slice, const AZ::Transform& relative)
{
AZ::Transform transform = AZ::Transform::Identity();
EBUS_EVENT_ID_RESULT(transform, GetEntityId(), AZ::TransformBus, GetWorldTM);
transform *= relative;
return SpawnSliceInternalAbsolute(slice, transform);
}
//=========================================================================
void SpawnerComponent::DestroySpawnedSlice(const AzFramework::SliceInstantiationTicket& sliceTicket)
{
auto foundTicketEntities = m_ticketToEntitiesMap.find(sliceTicket);
if (foundTicketEntities != m_ticketToEntitiesMap.end())
{
AZStd::unordered_set<AZ::EntityId>& entitiesInSlice = foundTicketEntities->second;
AzFramework::SliceInstantiationResultBus::MultiHandler::BusDisconnect(sliceTicket); // don't care anymore about events from this ticket
if (entitiesInSlice.empty())
{
AzFramework::SliceGameEntityOwnershipServiceRequestBus::Broadcast(
&AzFramework::SliceGameEntityOwnershipServiceRequestBus::Events::CancelDynamicSliceInstantiation, sliceTicket);
}
else
{
for (AZ::EntityId entity : entitiesInSlice)
{
AZ::EntityBus::MultiHandler::BusDisconnect(entity); // don't care anymore about events from this entity
m_entityToTicketMap.erase(entity);
}
AzFramework::SliceGameEntityOwnershipServiceRequestBus::Broadcast(
&AzFramework::SliceGameEntityOwnershipServiceRequests::DestroyDynamicSliceByEntity, *entitiesInSlice.begin());
}
m_ticketToEntitiesMap.erase(foundTicketEntities);
m_activeTickets.erase(AZStd::remove(m_activeTickets.begin(), m_activeTickets.end(), sliceTicket), m_activeTickets.end());
// slice destruction is queued, so queue the notification as well.
AZ::EntityId entityId = GetEntityId();
AZ::TickBus::QueueFunction(
[entityId, sliceTicket]() // use copies, in case 'this' is destroyed
{
SpawnerComponentNotificationBus::Event(entityId, &SpawnerComponentNotificationBus::Events::OnSpawnedSliceDestroyed, sliceTicket);
});
}
}
//=========================================================================
void SpawnerComponent::DestroyAllSpawnedSlices()
{
AZStd::vector<AzFramework::SliceInstantiationTicket> activeTickets = m_activeTickets; // iterate over a copy of the vector
for (AzFramework::SliceInstantiationTicket& ticket : activeTickets)
{
DestroySpawnedSlice(ticket);
}
AZ_Assert(m_activeTickets.empty(), "SpawnerComponent::DestroyAllSpawnedSlices - tickets still listed");
AZ_Assert(m_entityToTicketMap.empty(), "SpawnerComponent::DestroyAllSpawnedSlices - entities still listed");
AZ_Assert(m_ticketToEntitiesMap.empty(), "SpawnerComponent::DestroyAllSpawnedSlices - ticket entities still listed");
}
//=========================================================================
AZStd::vector<AzFramework::SliceInstantiationTicket> SpawnerComponent::GetCurrentlySpawnedSlices()
{
return m_activeTickets;
}
//=========================================================================
bool SpawnerComponent::HasAnyCurrentlySpawnedSlices()
{
return !m_activeTickets.empty();
}
//=========================================================================
AZStd::vector<AZ::EntityId> SpawnerComponent::GetCurrentEntitiesFromSpawnedSlice(const AzFramework::SliceInstantiationTicket& ticket)
{
AZStd::vector<AZ::EntityId> entities;
auto foundTicketEntities = m_ticketToEntitiesMap.find(ticket);
if (foundTicketEntities != m_ticketToEntitiesMap.end())
{
const AZStd::unordered_set<AZ::EntityId>& ticketEntities = foundTicketEntities->second;
AZ_Warning("SpawnerComponent", !ticketEntities.empty(), "SpawnerComponent::GetCurrentEntitiesFromSpawnedSlice - Spawn has not completed, its entities are not available.");
entities.reserve(ticketEntities.size());
entities.insert(entities.end(), ticketEntities.begin(), ticketEntities.end());
// Sort entities so that results are stable.
AZStd::sort(entities.begin(), entities.end());
}
return entities;
}
//=========================================================================
AZStd::vector<AZ::EntityId> SpawnerComponent::GetAllCurrentlySpawnedEntities()
{
AZStd::vector<AZ::EntityId> entities;
entities.reserve(m_entityToTicketMap.size());
// Return entities in the order their tickets spawned.
// It's not a requirement, but it seems nice to do.
for (const AzFramework::SliceInstantiationTicket& ticket : m_activeTickets)
{
const AZStd::unordered_set<AZ::EntityId>& ticketEntities = m_ticketToEntitiesMap[ticket];
entities.insert(entities.end(), ticketEntities.begin(), ticketEntities.end());
// Sort entities from a given ticket, so that results are stable.
AZStd::sort(entities.end() - ticketEntities.size(), entities.end());
}
return entities;
}
//=========================================================================
void SpawnerComponent::OnSlicePreInstantiate(const AZ::Data::AssetId& /*sliceAssetId*/, [[maybe_unused]] const AZ::SliceComponent::SliceInstanceAddress& sliceAddress)
{
const AzFramework::SliceInstantiationTicket ticket = (*AzFramework::SliceInstantiationResultBus::GetCurrentBusId());
EBUS_EVENT_ID(GetEntityId(), SpawnerComponentNotificationBus, OnSpawnBegin, ticket);
}
//=========================================================================
void SpawnerComponent::OnSliceInstantiated([[maybe_unused]] const AZ::Data::AssetId& sliceAssetId, const AZ::SliceComponent::SliceInstanceAddress& sliceAddress)
{
const AzFramework::SliceInstantiationTicket ticket = (*AzFramework::SliceInstantiationResultBus::GetCurrentBusId());
// Stop listening for this ticket (since it's done). We can have have multiple tickets in flight.
AzFramework::SliceInstantiationResultBus::MultiHandler::BusDisconnect(ticket);
const AZ::SliceComponent::EntityList& entities = sliceAddress.GetInstance()->GetInstantiated()->m_entities;
AZStd::vector<AZ::EntityId> entityIds;
entityIds.reserve(entities.size());
AZStd::unordered_set<AZ::EntityId>& ticketEntities = m_ticketToEntitiesMap[ticket];
for (AZ::Entity* currEntity : entities)
{
AZ::EntityId currEntityId = currEntity->GetId();
entityIds.emplace_back(currEntityId);
// update internal slice tracking data
ticketEntities.emplace(currEntityId);
m_entityToTicketMap.emplace(AZStd::make_pair(currEntityId, ticket));
AZ::EntityBus::MultiHandler::BusConnect(currEntityId);
EBUS_EVENT_ID(GetEntityId(), SpawnerComponentNotificationBus, OnEntitySpawned, ticket, currEntityId);
}
EBUS_EVENT_ID(GetEntityId(), SpawnerComponentNotificationBus, OnSpawnEnd, ticket);
EBUS_EVENT_ID(GetEntityId(), SpawnerComponentNotificationBus, OnEntitiesSpawned, ticket, entityIds);
// If slice had no entities, clean it up
if (entities.empty())
{
DestroySpawnedSlice(ticket);
}
}
//=========================================================================
void SpawnerComponent::OnSliceInstantiationFailedOrCanceled(const AZ::Data::AssetId& sliceAssetId, bool canceled)
{
const AzFramework::SliceInstantiationTicket ticket = *AzFramework::SliceInstantiationResultBus::GetCurrentBusId();
AzFramework::SliceInstantiationResultBus::MultiHandler::BusDisconnect(ticket);
// clean it up
DestroySpawnedSlice(ticket);
if (!canceled)
{
// error msg
if (sliceAssetId == m_sliceAsset.GetId())
{
AZ_Error("SpawnerComponent", false, "Slice %s failed to instantiate", m_sliceAsset.ToString<AZStd::string>().c_str());
}
else
{
AZ_Error("SpawnerComponent", false, "Slice [id:'%s'] failed to instantiate", sliceAssetId.ToString<AZStd::string>().c_str());
}
}
}
//=========================================================================
void SpawnerComponent::OnEntityDestruction(const AZ::EntityId& entityId)
{
AZ::EntityBus::MultiHandler::BusDisconnect(entityId);
auto entityToTicketIter = m_entityToTicketMap.find(entityId);
if (entityToTicketIter != m_entityToTicketMap.end())
{
AzFramework::SliceInstantiationTicket ticket = entityToTicketIter->second;
m_entityToTicketMap.erase(entityToTicketIter);
AZStd::unordered_set<AZ::EntityId>& ticketEntities = m_ticketToEntitiesMap[ticket];
ticketEntities.erase(entityId);
// If this was last entity in the spawn, clean it up
if (ticketEntities.empty())
{
DestroySpawnedSlice(ticket);
}
}
}
void SpawnerComponent::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
{
AZ::Data::AssetBus::Handler::BusDisconnect();
m_sliceAsset = asset;
}
} // namespace LmbrCentral
@@ -0,0 +1,113 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/EntityBus.h>
#include <AzFramework/Slice/SliceInstantiationBus.h>
#include <LmbrCentral/Scripting/SpawnerComponentBus.h>
namespace LmbrCentral
{
/**
* SpawnerComponent
*
* SpawnerComponent facilitates spawning of a design-time selected or run-time provided "*.dynamicslice" at an entity's location with an optional offset.
*/
class SpawnerComponent
: public AZ::Component
, private SpawnerComponentRequestBus::Handler
, private AzFramework::SliceInstantiationResultBus::MultiHandler
, private AZ::EntityBus::MultiHandler
, private AZ::Data::AssetBus::Handler
{
public:
AZ_COMPONENT(SpawnerComponent, SpawnerComponentTypeId);
SpawnerComponent();
SpawnerComponent(const AZ::Data::Asset<AZ::DynamicSliceAsset>& sliceAsset, bool spawnOnActivate);
~SpawnerComponent() override = default;
//////////////////////////////////////////////////////////////////////////
// Component descriptor
static void Reflect(AZ::ReflectContext* context);
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// AZ::Component
void Activate() override;
void Deactivate() override;
bool ReadInConfig(const AZ::ComponentConfig* spawnerConfig) override;
bool WriteOutConfig(AZ::ComponentConfig* outSpawnerConfig) const override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// SpawnerComponentRequestBus::Handler
void SetDynamicSlice(const AZ::Data::Asset<AZ::DynamicSliceAsset>& dynamicSliceAsset) override;
void SetDynamicSliceByAssetId(AZ::Data::AssetId& assetId) override;
void SetSpawnOnActivate(bool spawnOnActivate) override;
bool GetSpawnOnActivate() override;
AzFramework::SliceInstantiationTicket Spawn() override;
AzFramework::SliceInstantiationTicket SpawnRelative(const AZ::Transform& relative) override;
AzFramework::SliceInstantiationTicket SpawnAbsolute(const AZ::Transform& world) override;
AzFramework::SliceInstantiationTicket SpawnSlice(const AZ::Data::Asset<AZ::Data::AssetData>& slice) override;
AzFramework::SliceInstantiationTicket SpawnSliceRelative(const AZ::Data::Asset<AZ::Data::AssetData>& slice, const AZ::Transform& relative) override;
AzFramework::SliceInstantiationTicket SpawnSliceAbsolute(const AZ::Data::Asset<AZ::Data::AssetData>& slice, const AZ::Transform& world) override;
void DestroySpawnedSlice(const AzFramework::SliceInstantiationTicket& ticket) override;
void DestroyAllSpawnedSlices() override;
AZStd::vector<AzFramework::SliceInstantiationTicket> GetCurrentlySpawnedSlices() override;
bool HasAnyCurrentlySpawnedSlices() override;
AZStd::vector<AZ::EntityId> GetCurrentEntitiesFromSpawnedSlice(const AzFramework::SliceInstantiationTicket& ticket) override;
AZStd::vector<AZ::EntityId> GetAllCurrentlySpawnedEntities();
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// SliceInstantiationResultBus::MultiHandler
void OnSlicePreInstantiate(const AZ::Data::AssetId& sliceAssetId, const AZ::SliceComponent::SliceInstanceAddress& sliceAddress) override;
void OnSliceInstantiated(const AZ::Data::AssetId& sliceAssetId, const AZ::SliceComponent::SliceInstanceAddress& sliceAddress) override;
void OnSliceInstantiationFailedOrCanceled(const AZ::Data::AssetId& sliceAssetId, bool canceled) override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// EntityBus::MultiHandler
void OnEntityDestruction(const AZ::EntityId& entityId) override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// AssetBus::Handler
void OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Serialized members
AZ::Data::Asset<AZ::DynamicSliceAsset> m_sliceAsset;
bool m_spawnOnActivate = false;
bool m_destroyOnDeactivate = false;
private:
//////////////////////////////////////////////////////////////////////////
// Private helpers
AzFramework::SliceInstantiationTicket SpawnSliceInternalAbsolute(const AZ::Data::Asset<AZ::Data::AssetData>& slice, const AZ::Transform& world);
AzFramework::SliceInstantiationTicket SpawnSliceInternalRelative(const AZ::Data::Asset<AZ::Data::AssetData>& slice, const AZ::Transform& relative);
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
// Runtime-only members
AZStd::vector<AzFramework::SliceInstantiationTicket> m_activeTickets; ///< tickets listed in order they were spawned
AZStd::unordered_map<AZ::EntityId, AzFramework::SliceInstantiationTicket> m_entityToTicketMap; ///< map from entity to ticket that spawned it
AZStd::unordered_map<AzFramework::SliceInstantiationTicket, AZStd::unordered_set<AZ::EntityId>> m_ticketToEntitiesMap; ///< map from ticket to entities it spawned
};
} // namespace LmbrCentral
@@ -0,0 +1,189 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "LmbrCentral_precompiled.h"
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include "TagComponent.h"
namespace LmbrCentral
{
// BehaviorContext TagComponentNotificationsBus forwarder
class BehaviorTagComponentNotificationsBusHandler : public TagComponentNotificationsBus::Handler, public AZ::BehaviorEBusHandler
{
public:
AZ_EBUS_BEHAVIOR_BINDER(BehaviorTagComponentNotificationsBusHandler,"{7AEDC591-41AB-4E3B-87D2-03346154279D}",AZ::SystemAllocator,
OnTagAdded, OnTagRemoved);
void OnTagAdded(const Tag& tag) override
{
Call(FN_OnTagAdded, tag);
}
void OnTagRemoved(const Tag& tag) override
{
Call(FN_OnTagRemoved, tag);
}
};
class BehaviorTagGlobalNotificationBusHandler : public TagGlobalNotificationBus::Handler, public AZ::BehaviorEBusHandler
{
public:
AZ_EBUS_BEHAVIOR_BINDER(BehaviorTagGlobalNotificationBusHandler, "{87E9363C-C346-4A1E-BCDA-37C0504B1985}", AZ::SystemAllocator,
OnEntityTagAdded, OnEntityTagRemoved);
void OnEntityTagAdded(const AZ::EntityId& entityId) override
{
Call(FN_OnEntityTagAdded, entityId);
}
void OnEntityTagRemoved(const AZ::EntityId& entityId) override
{
Call(FN_OnEntityTagRemoved, entityId);
}
};
class TagComponentBehaviorHelper
{
public:
AZ_RTTI(TagComponentBehaviorHelper, "{9BE9EE51-3705-4C3F-B9F1-F799C628D76F}");
virtual ~TagComponentBehaviorHelper() = default;
static AZStd::vector< AZ::EntityId > FindTaggedEntities(const AZ::Crc32& tagName)
{
AZ::EBusAggregateResults<AZ::EntityId> aggregator;
TagGlobalRequestBus::EventResult(aggregator, tagName, &TagGlobalRequests::RequestTaggedEntities);
return aggregator.values;
}
};
//=========================================================================
// Component Descriptor
//=========================================================================
void TagComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context);
if (serialize)
{
serialize->Class<TagComponent, AZ::Component>()
->Version(1)
->Field("Tags", &TagComponent::m_tags);
}
AZ::BehaviorContext* behaviorContext = azrtti_cast<AZ::BehaviorContext*>(context);
if (behaviorContext)
{
behaviorContext->Class<TagComponentBehaviorHelper>("Tag Helper")
->Method("Get Entities by Tag", &TagComponentBehaviorHelper::FindTaggedEntities)
->Attribute(AZ::Script::Attributes::Ignore, 0)
->Attribute(AZ::Script::Attributes::Category, "Gameplay/Tag")
->Attribute(AZ::ScriptCanvasAttributes::FloatingFunction, 0)
;
behaviorContext->EBus<TagComponentRequestBus>("TagComponentRequestBus")
->Event("HasTag", &TagComponentRequestBus::Events::HasTag)
->Event("AddTag", &TagComponentRequestBus::Events::AddTag)
->Event("RemoveTag", &TagComponentRequestBus::Events::RemoveTag)
;
behaviorContext->EBus<TagGlobalRequestBus>("TagGlobalRequestBus")
->Event("RequestTaggedEntities", &TagGlobalRequestBus::Events::RequestTaggedEntities)
;
behaviorContext->EBus<TagComponentNotificationsBus>("TagComponentNotificationsBus")
->Handler<BehaviorTagComponentNotificationsBusHandler>()
;
behaviorContext->EBus<TagGlobalNotificationBus>("TagGlobalNotificationBus")
->Handler<BehaviorTagGlobalNotificationBusHandler>()
;
}
}
//=========================================================================
// AZ::Component
//=========================================================================
void TagComponent::Activate()
{
for (const Tag& tag : m_tags)
{
TagGlobalRequestBus::MultiHandler::BusConnect(tag);
EBUS_EVENT_ID(tag, TagGlobalNotificationBus, OnEntityTagAdded, GetEntityId());
}
TagComponentRequestBus::Handler::BusConnect(GetEntityId());
}
void TagComponent::Deactivate()
{
TagComponentRequestBus::Handler::BusDisconnect();
for (const Tag& tag : m_tags)
{
TagGlobalRequestBus::MultiHandler::BusDisconnect(tag);
EBUS_EVENT_ID(tag, TagGlobalNotificationBus, OnEntityTagRemoved, GetEntityId());
}
}
//=========================================================================
// EditorTagComponent friend will call this
//=========================================================================
void TagComponent::EditorSetTags(Tags&& editorTagList)
{
m_tags = AZStd::move(editorTagList);
}
//=========================================================================
// TagRequestBus
//=========================================================================
bool TagComponent::HasTag(const Tag& tag)
{
return m_tags.find(tag) != m_tags.end();
}
void TagComponent::AddTag(const Tag& tag)
{
if (m_tags.insert(tag).second)
{
EBUS_EVENT_ID(GetEntityId(), TagComponentNotificationsBus, OnTagAdded, tag);
EBUS_EVENT_ID(tag, TagGlobalNotificationBus, OnEntityTagAdded, GetEntityId());
TagGlobalRequestBus::MultiHandler::BusConnect(tag);
}
}
void TagComponent::AddTags(const Tags& tags)
{
for (const Tag& tag : tags)
{
AddTag(tag);
}
}
void TagComponent::RemoveTag(const Tag& tag)
{
if (m_tags.erase(tag) > 0)
{
EBUS_EVENT_ID(GetEntityId(), TagComponentNotificationsBus, OnTagRemoved, tag);
EBUS_EVENT_ID(tag, TagGlobalNotificationBus, OnEntityTagRemoved, GetEntityId());
TagGlobalRequestBus::MultiHandler::BusDisconnect(tag);
}
}
void TagComponent::RemoveTags(const Tags& tags)
{
for (const Tag& tag : tags)
{
RemoveTag(tag);
}
}
} // namespace LmbrCentral
@@ -0,0 +1,79 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/std/containers/vector.h>
#include <AzCore/Component/Component.h>
#include <LmbrCentral/Scripting/TagComponentBus.h>
namespace LmbrCentral
{
class EditorTagComponent;
/**
* Tag Component
* Simple component that tags an entity with a list of filters or descriptors
*/
class TagComponent
: public AZ::Component
, public TagGlobalRequestBus::MultiHandler
, public TagComponentRequestBus::Handler
{
public:
AZ_COMPONENT(TagComponent, "{0F16A377-EAA0-47D2-8472-9EAAA680B169}");
~TagComponent() override = default;
//////////////////////////////////////////////////////////////////////////
// AZ::Component
void Activate() override;
void Deactivate() override;
//////////////////////////////////////////////////////////////////////////
private:
//////////////////////////////////////////////////////////////////////////
/// EditorTagComponent will call this
friend EditorTagComponent;
void EditorSetTags(Tags&& editorTagList);
//////////////////////////////////////////////////////////////////////////
// TagGlobalRequestBus::MultiHandler
const AZ::EntityId RequestTaggedEntities() override { return GetEntityId(); }
//////////////////////////////////////////////////////////////////////////
// TagComponentRequestBusRequestBus::Handler
bool HasTag(const Tag&) override;
void AddTag(const Tag&) override;
void AddTags(const Tags&) override;
void RemoveTag(const Tag&) override;
void RemoveTags(const Tags&) override;
const Tags& GetTags() override { return m_tags; }
//////////////////////////////////////////////////////////////////////////
// Component descriptor
static void Reflect(AZ::ReflectContext* context);
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
{
provided.push_back(AZ_CRC("TagService", 0xf1ef347d));
}
static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
{
incompatible.push_back(AZ_CRC("TagService", 0xf1ef347d));
}
Tags m_tags = Tags();
};
} // namespace LmbrCentral