2
0
Эх сурвалжийг харах

Fruit storage component
- Component to count apples and spawn crate
- Fixes, changes, integration with ApplePickerComponent
- Reflection in ApplePickerComponent
- Tested - indeed spawns crates
- Prefab modified - added all components

Signed-off-by: Paweł Lech <[email protected]>
Signed-off-by: Adam Dabrowski <[email protected]>

Adam Dabrowski 3 жил өмнө
parent
commit
8bc24deb91

+ 74 - 3
Project/Assets/Importer/apple_kraken.prefab

@@ -417,7 +417,9 @@
                             "$type": "GenericComponentWrapper",
                             "Id": 3570157607065538715,
                             "m_template": {
-                                "$type": "ApplePickerComponent"
+                                "$type": "ApplePickerComponent",
+                                "EffectorEntity": "",
+                                "FruitStorageEntity": ""
                             }
                         }
                     ]
@@ -698,6 +700,56 @@
                 }
             }
         },
+        "Entity_[5913901340443]": {
+            "Id": "Entity_[5913901340443]",
+            "Name": "crate_drop_point",
+            "Components": {
+                "Component_[10232652057703614197]": {
+                    "$type": "EditorOnlyEntityComponent",
+                    "Id": 10232652057703614197
+                },
+                "Component_[11299130102496014478]": {
+                    "$type": "EditorPendingCompositionComponent",
+                    "Id": 11299130102496014478
+                },
+                "Component_[13884277907524885317]": {
+                    "$type": "EditorEntitySortComponent",
+                    "Id": 13884277907524885317
+                },
+                "Component_[15266374551430105842]": {
+                    "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+                    "Id": 15266374551430105842,
+                    "Parent Entity": "Entity_[75475350413532]",
+                    "Transform Data": {
+                        "Translate": [
+                            -1.0,
+                            0.0,
+                            0.20000000298023224
+                        ]
+                    }
+                },
+                "Component_[2248755397406082196]": {
+                    "$type": "EditorVisibilityComponent",
+                    "Id": 2248755397406082196
+                },
+                "Component_[2696593719280132302]": {
+                    "$type": "EditorDisabledCompositionComponent",
+                    "Id": 2696593719280132302
+                },
+                "Component_[5897183047265853050]": {
+                    "$type": "EditorEntityIconComponent",
+                    "Id": 5897183047265853050
+                },
+                "Component_[6051620210793581310]": {
+                    "$type": "EditorInspectorComponent",
+                    "Id": 6051620210793581310
+                },
+                "Component_[870569210833367376]": {
+                    "$type": "EditorLockComponent",
+                    "Id": 870569210833367376
+                }
+            }
+        },
         "Entity_[75380861133020]": {
             "Id": "Entity_[75380861133020]",
             "Name": "wheel_rear_left_link_visual",
@@ -2790,7 +2842,9 @@
                     "$type": "GenericComponentWrapper",
                     "Id": 10827857459669795993,
                     "m_template": {
-                        "$type": "ApplePickerComponent"
+                        "$type": "ApplePickerComponent",
+                        "EffectorEntity": "Entity_[75475350413532]",
+                        "FruitStorageEntity": "Entity_[75475350413532]"
                     }
                 },
                 "Component_[11557544210003172907]": {
@@ -2808,7 +2862,8 @@
                         "Entity_[32188681862897]",
                         "Entity_[75440990675164]",
                         "Entity_[75402335969500]",
-                        "Entity_[34036328831583]"
+                        "Entity_[34036328831583]",
+                        "Entity_[5913901340443]"
                     ]
                 },
                 "Component_[11587227330168097343]": {
@@ -3019,6 +3074,22 @@
                         }
                     }
                 },
+                "Component_[7519155594774373209]": {
+                    "$type": "GenericComponentWrapper",
+                    "Id": 7519155594774373209,
+                    "m_template": {
+                        "$type": "FruitStorageComponent",
+                        "Crate": {
+                            "assetId": {
+                                "guid": "{47A5677E-83C3-59F5-888D-2E3B1F3C0493}",
+                                "subId": 3829363014
+                            },
+                            "assetHint": "prefabs/apple_crate.spawnable"
+                        },
+                        "Capacity": 50,
+                        "CrateDropPoint": "Entity_[5913901340443]"
+                    }
+                },
                 "Component_[7534877009621480012]": {
                     "$type": "EditorColliderComponent",
                     "Id": 7534877009621480012,

+ 6 - 0
Project/Gem/CMakeLists.txt

@@ -36,11 +36,15 @@ ly_add_target(
     INCLUDE_DIRECTORIES
         PUBLIC
             Include
+        PRIVATE
+            Source
     BUILD_DEPENDENCIES
         PUBLIC
             Gem::ROS2.Static
         PRIVATE
             AZ::AzGameFramework
+            AZ::AzToolsFramework
+            AZ::AzFramework
             Gem::LmbrCentral.API
             Gem::AtomLyIntegration_CommonFeatures.Editor.Static
             Gem::Atom_AtomBridge.Static
@@ -61,6 +65,8 @@ ly_add_target(
         PRIVATE
             Gem::ROSConDemo.Static
             AZ::AzCore
+            AZ::AzToolsFramework
+            AZ::AzFramework
 )
 
 # if enabled, ROSConDemo is used by all kinds of applications

+ 43 - 10
Project/Gem/Source/ApplePicker/ApplePickerComponent.cpp

@@ -8,22 +8,22 @@
 
 #include "ApplePickerComponent.h"
 #include "ApplePickingRequests.h"
+#include "FruitStorage/FruitStorageBus.h"
+#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h>
+#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentConstants.h>
+#include <AzCore/Component/ComponentApplicationBus.h>
+#include <AzCore/Component/TransformBus.h>
 #include <AzCore/EBus/Event.h>
 #include <AzCore/Serialization/EditContext.h>
 #include <AzCore/Serialization/EditContextConstants.inl>
+#include <AzFramework/Physics/PhysicsSystem.h>
+#include <AzFramework/Physics/Shape.h>
 #include <ROS2/Frame/ROS2FrameComponent.h>
 #include <ROS2/ROS2Bus.h>
 #include <ROS2/Utilities/ROS2Names.h>
 
 using namespace ROS2;
 
-#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentBus.h>
-#include <AtomLyIntegration/CommonFeatures/Mesh/MeshComponentConstants.h>
-#include <AzCore/Component/ComponentApplicationBus.h>
-#include <AzCore/Component/TransformBus.h>
-#include <AzFramework/Physics/PhysicsSystem.h>
-#include <AzFramework/Physics/Shape.h>
-
 namespace AppleKraken
 {
     namespace Internal
@@ -122,7 +122,11 @@ namespace AppleKraken
 
     void ApplePickerComponent::Activate()
     {
-        m_effectorEntityId = GetEntityId(); // TODO - remove this once we expose this field
+        if (!m_effectorEntityId.IsValid())
+        {
+            AZ_Warning("ApplePicker", false, "Effector entity not set, assuming same entity");
+            m_effectorEntityId = GetEntityId();
+        }
         ApplePickingNotificationBus::Handler::BusConnect();
         AZ::TickBus::Handler::BusConnect();
 
@@ -148,13 +152,33 @@ namespace AppleKraken
     {
         if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
         {
-            serialize->Class<ApplePickerComponent, AZ::Component>()->Version(1);
+            serialize->Class<ApplePickerComponent, AZ::Component>()
+                ->Version(2)
+                ->Field("TriggerServiceTopic", &ApplePickerComponent::m_triggerServiceTopic)
+                ->Field("EffectorEntity", &ApplePickerComponent::m_effectorEntityId)
+                ->Field("FruitStorageEntity", &ApplePickerComponent::m_fruitStorageEntityId);
+
             if (AZ::EditContext* ec = serialize->GetEditContext())
             {
                 ec->Class<ApplePickerComponent>("Apple picking component", "A demo component for apple picking")
                     ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
                     ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game"))
-                    ->Attribute(AZ::Edit::Attributes::Category, "AppleKraken");
+                    ->Attribute(AZ::Edit::Attributes::Category, "AppleKraken")
+                    ->DataElement(
+                        AZ::Edit::UIHandlers::EntityId,
+                        &ApplePickerComponent::m_triggerServiceTopic,
+                        "Trigger",
+                        "ROS2 service name for gathering trigger")
+                    ->DataElement(
+                        AZ::Edit::UIHandlers::EntityId,
+                        &ApplePickerComponent::m_effectorEntityId,
+                        "Effector",
+                        "Effector (manipulator) entity")
+                    ->DataElement(
+                        AZ::Edit::UIHandlers::EntityId,
+                        &ApplePickerComponent::m_fruitStorageEntityId,
+                        "Fruit Storage",
+                        "Fruit storage entity");
             }
         }
     }
@@ -194,6 +218,15 @@ namespace AppleKraken
         AZ_TracePrintf(
             "ApplePicker", "%s. An apple has been retrieved and stored\n", Internal::CurrentTaskString(m_currentAppleTasks).c_str());
         m_currentAppleTasks.pop();
+
+        auto fruitStorageEntityId = m_fruitStorageEntityId;
+        if (!fruitStorageEntityId.IsValid())
+        {
+            AZ_Warning("ApplePicker", false, "Fruit storage entity not set, assuming same entity");
+            fruitStorageEntityId = GetEntityId();
+        }
+        Tags applePickingEventTags = { "automated_picking" };
+        FruitStorageRequestsBus::Event(fruitStorageEntityId, &FruitStorageRequests::AddApple, applePickingEventTags);
         PickNextApple();
     }
 

+ 5 - 6
Project/Gem/Source/ApplePicker/ApplePickerComponent.h

@@ -11,8 +11,8 @@
 #include "ApplePickingRequests.h"
 #include <AzCore/Component/Component.h>
 #include <AzCore/Component/TickBus.h>
-#include <std_srvs/srv/trigger.hpp>
 #include <rclcpp/rclcpp.hpp>
+#include <std_srvs/srv/trigger.hpp>
 
 namespace AppleKraken
 {
@@ -56,12 +56,11 @@ namespace AppleKraken
         void ProcessTriggerServiceCall(const TriggerRequest req, TriggerResponse resp);
 
         AZStd::string m_triggerServiceTopic = "trigger_apple_gathering";
-        rclcpp::Service<std_srvs::srv::Trigger>::SharedPtr m_triggerService;
-
         AZ::EntityId m_effectorEntityId;
-        AZ::Obb m_gatheringArea;
+        AZ::EntityId m_fruitStorageEntityId;
+
+        rclcpp::Service<std_srvs::srv::Trigger>::SharedPtr m_triggerService;
         size_t m_initialTasksSize = 0;
-        AZStd::queue<PickAppleTask>
-            m_currentAppleTasks; //! Populated in StartAutomatedOperation. Tasks are popped when completed or failed.
+        AZStd::queue<PickAppleTask> m_currentAppleTasks;
     };
 } // namespace AppleKraken

+ 1 - 1
Project/Gem/Source/ApplePicker/KrakenEffectorComponent.cpp

@@ -88,7 +88,7 @@ namespace AppleKraken
             {
                 ec->Class<KrakenEffectorComponent>("Kraken Effector", "Manipulator component for picking apples")
                     ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
-                    ->Attribute(AZ::Edit::Attributes::Category, "ROS2")
+                    ->Attribute(AZ::Edit::Attributes::Category, "AppleKraken")
                     ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game"))
                     ->DataElement(
                         AZ::Edit::UIHandlers::Default,

+ 40 - 0
Project/Gem/Source/FruitStorage/FruitStorageBus.h

@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include <AzCore/Component/ComponentBus.h>
+#include <AzCore/EBus/EBus.h>
+#include <AzCore/Math/Transform.h>
+#include <AzCore/RTTI/BehaviorContext.h>
+
+namespace AppleKraken
+{
+    using Tags = AZStd::vector<AZStd::string>;
+    using ApplesGatheredByTag = AZStd::unordered_map<AZStd::string, uint32_t>;
+
+    //! Interface handing fruits storage requests
+    class FruitStorageRequests : public AZ::ComponentBus
+    {
+    public:
+        AZ_RTTI(SpawnerRequests, "{129EAABF-706E-4DC1-B272-93EEACE3E893}");
+        static constexpr AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
+
+        virtual ~FruitStorageRequests() = default;
+
+        //! Gets counts of apples gathered with certain tags. When no tags are provided, returns number of all apples gathered.
+        virtual ApplesGatheredByTag GetTotalGatheredAppleCount(const Tags& tags = {}) const = 0;
+
+        //! Gets only apples currently in storage
+        virtual uint32_t GetCurrentStorageAppleCount() const = 0;
+
+        //! Adds one apple associated with certain tags to the gathered apples counters.
+        virtual void AddApple(const Tags& tags = {}) = 0;
+    };
+
+    using FruitStorageRequestsBus = AZ::EBus<FruitStorageRequests>;
+} // namespace AppleKraken

+ 137 - 0
Project/Gem/Source/FruitStorage/FruitStorageComponent.cpp

@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include "FruitStorageComponent.h"
+#include "FruitStorageBus.h"
+#include <AzCore/Serialization/EditContext.h>
+#include <AzFramework/Components/TransformComponent.h>
+#include <AzToolsFramework/UI/PropertyEditor/PropertyEditorAPI.h>
+
+namespace AppleKraken
+{
+    void FruitStorageComponent::Activate()
+    {
+        FruitStorageRequestsBus::Handler::BusConnect(GetEntityId());
+    }
+
+    void FruitStorageComponent::Deactivate()
+    {
+        FruitStorageRequestsBus::Handler::BusDisconnect(GetEntityId());
+    }
+
+    void FruitStorageComponent::Reflect(AZ::ReflectContext* context)
+    {
+        if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
+        {
+            serialize->Class<FruitStorageComponent, AZ::Component>()
+                ->Version(1)
+                ->Field("Crate", &FruitStorageComponent::m_crateSpawnable)
+                ->Field("Capacity", &FruitStorageComponent::m_crateCapacity)
+                ->Field("CrateDropPoint", &FruitStorageComponent::m_crateDropPoint);
+
+            if (AZ::EditContext* ec = serialize->GetEditContext())
+            {
+                ec->Class<FruitStorageComponent>("Fruit Storage", "Fruit storage component")
+                    ->ClassElement(AZ::Edit::ClassElements::EditorData, "Manages Kraken capacity and spawns a crate when full")
+                    ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game"))
+                    ->Attribute(AZ::Edit::Attributes::Category, "AppleKraken")
+                    ->DataElement(AZ::Edit::UIHandlers::EntityId, &FruitStorageComponent::m_crateSpawnable, "Crate", "Crate spawnable")
+                    ->DataElement(AZ::Edit::UIHandlers::EntityId, &FruitStorageComponent::m_crateCapacity, "Capacity", "Capacity")
+                    ->DataElement(
+                        AZ::Edit::UIHandlers::EntityId,
+                        &FruitStorageComponent::m_crateDropPoint,
+                        "Crate drop point",
+                        "Place this entity behind the robot");
+            }
+        }
+    }
+
+    void FruitStorageComponent::SpawnCrate()
+    {
+        if (!m_crateTicket.IsValid())
+        {
+            m_crateTicket = AzFramework::EntitySpawnTicket(m_crateSpawnable);
+        }
+
+        auto spawner = AZ::Interface<AzFramework::SpawnableEntitiesDefinition>::Get();
+        AzFramework::SpawnAllEntitiesOptionalArgs optionalArgs;
+        optionalArgs.m_preInsertionCallback = [this](auto id, auto view)
+        {
+            this->PreSpawn(id, view);
+        };
+        AZ_TracePrintf("FruitStorageComponent", "Spawning a crate\n");
+        spawner->SpawnAllEntities(m_crateTicket, optionalArgs);
+    }
+
+    void FruitStorageComponent::PreSpawn(
+        AzFramework::EntitySpawnTicket::Id id [[maybe_unused]], AzFramework::SpawnableEntityContainerView view)
+    {
+        if (view.empty())
+        {
+            AZ_Warning("FruitStorageComponent", false, "Can not spawn a crate - no entities\n");
+            return;
+        }
+
+        AZ::Entity* dropPointEntity = nullptr;
+        if (m_crateDropPoint.IsValid())
+        {
+            AZ::ComponentApplicationBus::BroadcastResult(dropPointEntity, &AZ::ComponentApplicationRequests::FindEntity, m_crateDropPoint);
+        }
+        if (dropPointEntity == nullptr)
+        {
+            AZ_Warning("FruitStorageComponent", false, "Crate drop point not set or invalid, spawning a crate within this entity\n");
+            dropPointEntity = GetEntity();
+        }
+
+        AZ::Entity* root = *view.begin();
+        auto* crateTransformInterface = root->FindComponent<AzFramework::TransformComponent>();
+        auto entityTransform = dropPointEntity->FindComponent<AzFramework::TransformComponent>();
+        crateTransformInterface->SetWorldTM(entityTransform->GetWorldTM());
+    }
+
+    ApplesGatheredByTag FruitStorageComponent::GetTotalGatheredAppleCount(const Tags& tags) const
+    {
+        ApplesGatheredByTag result;
+        if (tags.empty())
+        {
+            result["all"] = m_applesGathered;
+        }
+        else
+        {
+            for (const auto& tag : tags)
+            {
+                if (m_tagsStored.contains(tag))
+                {
+                    result[tag] = m_tagsStored.at(tag);
+                }
+            }
+        }
+        return result;
+    }
+
+    uint32_t FruitStorageComponent::GetCurrentStorageAppleCount() const
+    {
+        return m_applesInStorage;
+    }
+
+    void FruitStorageComponent::AddApple(const Tags& tags)
+    {
+        m_applesGathered++;
+        m_applesInStorage++;
+        for (const auto& tag : tags)
+        {
+            m_tagsStored.contains(tag) ? m_tagsStored[tag]++ : m_tagsStored[tag] = 1;
+        }
+
+        if (m_applesInStorage >= m_crateCapacity)
+        {
+            SpawnCrate();
+            m_applesInStorage = 0;
+        }
+    }
+} // namespace AppleKraken

+ 48 - 0
Project/Gem/Source/FruitStorage/FruitStorageComponent.h

@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project.
+ * For complete copyright and license terms please see the LICENSE at the root of this distribution.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+#pragma once
+
+#include "FruitStorageBus.h"
+#include <AzCore/Component/Component.h>
+#include <AzFramework/AzFrameworkModule.h>
+#include <AzFramework/Spawnable/SpawnableEntitiesInterface.h>
+
+namespace AppleKraken
+{
+    //! Component responsible for storing counters of apples gathered by Kraken.
+    class FruitStorageComponent
+        : public AZ::Component
+        , public FruitStorageRequestsBus::Handler
+    {
+    public:
+        AZ_COMPONENT(FruitStorageComponent, "{9AC0B456-9C29-4EDD-AD25-6FAA57D253C5}", AZ::Component, FruitStorageRequestsBus::Handler);
+
+        // AZ::Component interface implementation.
+        FruitStorageComponent() = default;
+        ~FruitStorageComponent() = default;
+        void Activate() override;
+        void Deactivate() override;
+        static void Reflect(AZ::ReflectContext* context);
+
+        ApplesGatheredByTag GetTotalGatheredAppleCount(const Tags& tags = {}) const override;
+        uint32_t GetCurrentStorageAppleCount() const override;
+        void AddApple(const Tags& tags = {}) override;
+
+    private:
+        void SpawnCrate();
+        void PreSpawn(AzFramework::EntitySpawnTicket::Id, AzFramework::SpawnableEntityContainerView);
+
+        AZ::Data::Asset<AzFramework::Spawnable> m_crateSpawnable;
+        AzFramework::EntitySpawnTicket m_crateTicket;
+        AZ::EntityId m_crateDropPoint;
+        uint32_t m_crateCapacity = 0;
+        uint32_t m_applesGathered = 0;
+        uint32_t m_applesInStorage = 0;
+        ApplesGatheredByTag m_tagsStored;
+    };
+} // namespace AppleKraken

+ 5 - 9
Project/Gem/Source/ROSConDemoModule.cpp

@@ -1,6 +1,7 @@
 
 #include "ApplePicker/ApplePickerComponent.h"
 #include "ApplePicker/KrakenEffectorComponent.h"
+#include "FruitStorage/FruitStorageComponent.h"
 #include "ROSConDemoSystemComponent.h"
 #include <AzCore/Memory/SystemAllocator.h>
 #include <AzCore/Module/Module.h>
@@ -16,19 +17,14 @@ namespace ROSConDemo
         ROSConDemoModule()
             : AZ::Module()
         {
-            // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here.
             m_descriptors.insert(
                 m_descriptors.end(),
-                {
-                    ROSConDemoSystemComponent::CreateDescriptor(),
-                    AppleKraken::ApplePickerComponent::CreateDescriptor(),
-                    AppleKraken::KrakenEffectorComponent::CreateDescriptor(),
-                });
+                { ROSConDemoSystemComponent::CreateDescriptor(),
+                  AppleKraken::ApplePickerComponent::CreateDescriptor(),
+                  AppleKraken::KrakenEffectorComponent::CreateDescriptor(),
+                  AppleKraken::FruitStorageComponent::CreateDescriptor() });
         }
 
-        /**
-         * Add required SystemComponents to the SystemEntity.
-         */
         AZ::ComponentTypeList GetRequiredSystemComponents() const override
         {
             return AZ::ComponentTypeList{

+ 3 - 0
Project/Gem/roscondemo_files.cmake

@@ -8,6 +8,9 @@ set(FILES
         Source/ApplePicker/PickingStructs.h
         Source/ApplePicker/KrakenEffectorComponent.cpp
         Source/ApplePicker/KrakenEffectorComponent.h
+        Source/FruitStorage/FruitStorageComponent.cpp
+        Source/FruitStorage/FruitStorageComponent.h
+        Source/FruitStorage/FruitStorageBus.h
         Source/ROSConDemoSystemComponent.cpp
         Source/ROSConDemoSystemComponent.h
         enabled_gems.cmake