Selaa lähdekoodia

Improve ROS2ProjectTemplate (#321)

* Added Editor template code
* Improve ROS2ProjectTemplate:
- add EditorModule and EditorSystemComponent
- add Sample component that shows integration with ROS2, o3de and imgui debugging

Signed-off-by: Michał Pełka <[email protected]>
Co-authored-by: Paweł Budziszewski <[email protected]>
Co-authored-by: Steve Pham <[email protected]>
Michał Pełka 2 vuotta sitten
vanhempi
commit
7ce9b9c6b7

+ 61 - 0
Templates/Ros2ProjectTemplate/Template/.clang-format

@@ -0,0 +1,61 @@
+Language: Cpp
+
+AccessModifierOffset: -4
+AlignAfterOpenBracket: AlwaysBreak
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: false
+AlignEscapedNewlines: Right
+AlignOperands: false
+AlignTrailingComments: false
+AllowAllArgumentsOnNextLine: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortFunctionsOnASingleLine: None
+AllowShortLambdasOnASingleLine: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakTemplateDeclarations: true
+BinPackArguments: false
+BinPackParameters: false
+BreakBeforeBraces: Custom
+BraceWrapping:
+    AfterClass: true
+    AfterControlStatement: true
+    AfterEnum: true
+    AfterFunction: true
+    AfterNamespace: true
+    BeforeLambdaBody: true
+    AfterStruct: true
+    BeforeElse: true
+    SplitEmptyFunction: true
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializers: BeforeComma
+BreakInheritanceList: BeforeComma
+ColumnLimit: 140
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: false
+FixNamespaceComments: true
+IncludeBlocks: Preserve
+IndentCaseBlocks: true
+IndentCaseLabels: false
+IndentPPDirectives: None
+IndentWidth: 4
+KeepEmptyLinesAtTheStartOfBlocks: false
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: All
+PenaltyReturnTypeOnItsOwnLine: 1000
+PointerAlignment: Left
+SortIncludes: true
+SpaceAfterLogicalNot: false
+SpaceAfterTemplateKeyword: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCpp11BracedList: false
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeParens: ControlStatements
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceInEmptyParentheses: false
+SpacesInAngles: false
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+Standard: c++17
+UseTab: Never

+ 5 - 5
Templates/Ros2ProjectTemplate/Template/Examples/slam_navigation/launch/config/navigation_params.yaml

@@ -82,17 +82,17 @@ controller_server:
     FollowPath:
       plugin: "dwb_core::DWBLocalPlanner"
       debug_trajectory_details: True
-      min_vel_x: 0.0
+      min_vel_x: -1.2
       min_vel_y: 0.0
-      max_vel_x: 0.26
+      max_vel_x: 1.2
       max_vel_y: 0.0
-      max_vel_theta: 1.0
+      max_vel_theta: 2.0
       min_speed_xy: 0.0
-      max_speed_xy: 0.26
+      max_speed_xy: 2.0
       min_speed_theta: 0.0
       # Add high threshold velocity for turtlebot 3 issue.
       # https://github.com/ROBOTIS-GIT/turtlebot3_simulations/issues/75
-      acc_lim_x: 2.5
+      acc_lim_x: 5.0
       acc_lim_y: 0.0
       acc_lim_theta: 3.2
       decel_lim_x: -2.5

+ 12 - 0
Templates/Ros2ProjectTemplate/Template/Gem/${NameLower}_editor_files.cmake

@@ -0,0 +1,12 @@
+# {BEGIN_LICENSE}
+# 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
+#
+# {END_LICENSE}
+
+set(FILES
+    Source/${Name}EditorSystemComponent.cpp
+    Source/${Name}EditorSystemComponent.h
+)

+ 11 - 0
Templates/Ros2ProjectTemplate/Template/Gem/${NameLower}_editor_shared_files.cmake

@@ -0,0 +1,11 @@
+# {BEGIN_LICENSE}
+# 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
+#
+# {END_LICENSE}
+
+set(FILES
+    Source/${Name}EditorModule.cpp
+)

+ 2 - 0
Templates/Ros2ProjectTemplate/Template/Gem/${NameLower}_files.cmake

@@ -10,5 +10,7 @@ set(FILES
     Include/${Name}/${Name}Bus.h
     Source/${Name}SystemComponent.cpp
     Source/${Name}SystemComponent.h
+    Source/${Name}SampleComponent.cpp
+    Source/${Name}SampleComponent.h
     enabled_gems.cmake
 )

+ 54 - 2
Templates/Ros2ProjectTemplate/Template/Gem/CMakeLists.txt

@@ -57,11 +57,18 @@ ly_add_target(
         PUBLIC
             Include
     BUILD_DEPENDENCIES
+        PUBLIC
+            Gem::ImGui.Static
+            Gem::ROS2.Static
         PRIVATE
             AZ::AzGameFramework
             Gem::Atom_AtomBridge.Static
+            Gem::ImGui.Static
 )
 
+# Request ROS2 packages to be included 
+target_depends_on_ros2_packages(${gem_name}.Private.Object geometry_msgs)
+
 ly_add_target(
     NAME ${gem_name} ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE}
     NAMESPACE Gem
@@ -72,14 +79,59 @@ ly_add_target(
         PUBLIC
             Include
     BUILD_DEPENDENCIES
+        PUBLIC
+            Gem::ImGui.Static
         PRIVATE
             Gem::${gem_name}.Private.Object
             AZ::AzCore
 )
 
+
+ly_add_target(
+    NAME ${gem_name}.Editor.Static STATIC
+    NAMESPACE Gem
+    AUTOMOC
+    AUTORCC
+    FILES_CMAKE
+        ${NameLower}_editor_files.cmake
+    INCLUDE_DIRECTORIES
+        PRIVATE
+            Source
+            Source/RobotImporter
+        PUBLIC
+            Include
+    BUILD_DEPENDENCIES
+        PUBLIC
+            Gem::ImGui.Static
+            Gem::ROS2.Static
+            AZ::AzToolsFramework
+            Gem::AtomLyIntegration_CommonFeatures.Editor.Static
+            Gem::LmbrCentral.API
+        PRIVATE
+            ${gem_name}.Private.Object
+)
+
+
+ly_add_target(
+    NAME ${gem_name}.Editor GEM_MODULE
+    NAMESPACE Gem
+    FILES_CMAKE
+        ${NameLower}_editor_shared_files.cmake
+    INCLUDE_DIRECTORIES
+        PRIVATE
+            Source
+        PUBLIC
+            Include
+    BUILD_DEPENDENCIES
+        PUBLIC
+            ${gem_name}.Editor.Static
+            Gem::Atom_Feature_Common.Static
+)
+
+
 # if enabled, ${gem_name} is used by all kinds of applications
-ly_create_alias(NAME ${gem_name}.Builders NAMESPACE Gem TARGETS Gem::${gem_name})
-ly_create_alias(NAME ${gem_name}.Tools    NAMESPACE Gem TARGETS Gem::${gem_name})
+ly_create_alias(NAME ${gem_name}.Builders NAMESPACE Gem TARGETS Gem::${gem_name}.Editor)
+ly_create_alias(NAME ${gem_name}.Tools    NAMESPACE Gem TARGETS Gem::${gem_name}.Editor)
 ly_create_alias(NAME ${gem_name}.Clients  NAMESPACE Gem TARGETS Gem::${gem_name})
 ly_create_alias(NAME ${gem_name}.Servers  NAMESPACE Gem TARGETS Gem::${gem_name})
 

+ 49 - 0
Templates/Ros2ProjectTemplate/Template/Gem/Source/${Name}EditorModule.cpp

@@ -0,0 +1,49 @@
+// {BEGIN_LICENSE}
+/*
+ * 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
+ *
+ */
+// {END_LICENSE}
+
+#include <${Name}ModuleInterface.h>
+#include "${Name}EditorSystemComponent.h"
+
+#include "${SanitizedCppName}SampleComponent.h"
+namespace ${SanitizedCppName}
+{
+    class ${SanitizedCppName}EditorModule
+        : public ${SanitizedCppName}ModuleInterface
+    {
+    public:
+        AZ_RTTI(${SanitizedCppName}EditorModule, "${ModuleClassId}", ${SanitizedCppName}ModuleInterface);
+        AZ_CLASS_ALLOCATOR(${SanitizedCppName}EditorModule, AZ::SystemAllocator, 0);
+
+        ${SanitizedCppName}EditorModule()
+        {
+            // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here.
+            // Add ALL components descriptors associated with this gem to m_descriptors.
+            // This will associate the AzTypeInfo information for the components with the the SerializeContext, BehaviorContext and EditContext.
+            // This happens through the [MyComponent]::Reflect() function.
+            m_descriptors.insert(m_descriptors.end(), {
+                ${SanitizedCppName}EditorSystemComponent::CreateDescriptor(),
+                ${SanitizedCppName}SampleComponent::CreateDescriptor(),
+            });
+        }
+
+        /**
+         * Add required SystemComponents to the SystemEntity.
+         * Non-SystemComponents should not be added here
+         */
+        AZ::ComponentTypeList GetRequiredSystemComponents() const override
+        {
+            return AZ::ComponentTypeList {
+                azrtti_typeid<${SanitizedCppName}EditorSystemComponent>(),
+            };
+        }
+    };
+}// namespace ${SanitizedCppName}
+
+AZ_DECLARE_MODULE_CLASS(Gem_${SanitizedCppName}, ${SanitizedCppName}::${SanitizedCppName}EditorModule)

+ 63 - 0
Templates/Ros2ProjectTemplate/Template/Gem/Source/${Name}EditorSystemComponent.cpp

@@ -0,0 +1,63 @@
+// {BEGIN_LICENSE}
+/*
+ * 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
+ *
+ */
+ // {END_LICENSE}
+
+#include <AzCore/Serialization/SerializeContext.h>
+#include "${Name}EditorSystemComponent.h"
+
+namespace ${SanitizedCppName}
+{
+    void ${SanitizedCppName}EditorSystemComponent::Reflect(AZ::ReflectContext* context)
+    {
+        if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
+        {
+            serializeContext->Class<${SanitizedCppName}EditorSystemComponent, ${SanitizedCppName}SystemComponent>()
+                ->Version(0);
+        }
+    }
+
+    ${SanitizedCppName}EditorSystemComponent::${SanitizedCppName}EditorSystemComponent() = default;
+
+    ${SanitizedCppName}EditorSystemComponent::~${SanitizedCppName}EditorSystemComponent() = default;
+
+    void ${SanitizedCppName}EditorSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
+    {
+        BaseSystemComponent::GetProvidedServices(provided);
+        provided.push_back(AZ_CRC_CE("${SanitizedCppName}EditorService"));
+    }
+
+    void ${SanitizedCppName}EditorSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible)
+    {
+        BaseSystemComponent::GetIncompatibleServices(incompatible);
+        incompatible.push_back(AZ_CRC_CE("${SanitizedCppName}EditorService"));
+    }
+
+    void ${SanitizedCppName}EditorSystemComponent::GetRequiredServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& required)
+    {
+        BaseSystemComponent::GetRequiredServices(required);
+    }
+
+    void ${SanitizedCppName}EditorSystemComponent::GetDependentServices([[maybe_unused]] AZ::ComponentDescriptor::DependencyArrayType& dependent)
+    {
+        BaseSystemComponent::GetDependentServices(dependent);
+    }
+
+    void ${SanitizedCppName}EditorSystemComponent::Activate()
+    {
+        ${SanitizedCppName}SystemComponent::Activate();
+        AzToolsFramework::EditorEvents::Bus::Handler::BusConnect();
+    }
+
+    void ${SanitizedCppName}EditorSystemComponent::Deactivate()
+    {
+        AzToolsFramework::EditorEvents::Bus::Handler::BusDisconnect();
+        ${SanitizedCppName}SystemComponent::Deactivate();
+    }
+
+} // namespace ${SanitizedCppName}

+ 42 - 0
Templates/Ros2ProjectTemplate/Template/Gem/Source/${Name}EditorSystemComponent.h

@@ -0,0 +1,42 @@
+// {BEGIN_LICENSE}
+/*
+ * 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
+ *
+ */
+ // {END_LICENSE}
+
+#pragma once
+
+#include <AzToolsFramework/API/ToolsApplicationAPI.h>
+
+#include <${Name}SystemComponent.h>
+
+namespace ${SanitizedCppName}
+{
+    /// System component for ${SanitizedCppName} editor
+    class ${SanitizedCppName}EditorSystemComponent
+        : public ${SanitizedCppName}SystemComponent
+        , protected AzToolsFramework::EditorEvents::Bus::Handler
+    {
+        using BaseSystemComponent = ${SanitizedCppName}SystemComponent;
+    public:
+        AZ_COMPONENT(${SanitizedCppName}EditorSystemComponent, "${EditorSysCompClassId}", BaseSystemComponent);
+        static void Reflect(AZ::ReflectContext* context);
+
+        ${SanitizedCppName}EditorSystemComponent();
+        ~${SanitizedCppName}EditorSystemComponent();
+
+    private:
+        static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided);
+        static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible);
+        static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required);
+        static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent);
+
+        // AZ::Component
+        void Activate() override;
+        void Deactivate() override;
+    };
+} // namespace ${SanitizedCppName}

+ 4 - 0
Templates/Ros2ProjectTemplate/Template/Gem/Source/${Name}Module.cpp

@@ -1,3 +1,4 @@
+// {BEGIN_LICENSE}
 /*
  * 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.
@@ -5,11 +6,13 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  */
+// {END_LICENSE}
 
 #include <AzCore/Memory/SystemAllocator.h>
 #include <AzCore/Module/Module.h>
 
 #include "${Name}SystemComponent.h"
+#include "${SanitizedCppName}SampleComponent.h"
 
 namespace ${SanitizedCppName}
 {
@@ -26,6 +29,7 @@ namespace ${SanitizedCppName}
             // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here.
             m_descriptors.insert(m_descriptors.end(), {
                 ${SanitizedCppName}SystemComponent::CreateDescriptor(),
+                ${SanitizedCppName}SampleComponent::CreateDescriptor(),
             });
         }
 

+ 45 - 0
Templates/Ros2ProjectTemplate/Template/Gem/Source/${Name}ModuleInterface.h

@@ -0,0 +1,45 @@
+// {BEGIN_LICENSE}
+/*
+ * 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
+ *
+ */
+// {END_LICENSE}
+
+#include <AzCore/Memory/SystemAllocator.h>
+#include <AzCore/Module/Module.h>
+#include <${Name}SystemComponent.h>
+
+namespace ${SanitizedCppName}
+{
+    class ${SanitizedCppName}ModuleInterface
+        : public AZ::Module
+    {
+    public:
+        AZ_RTTI(${SanitizedCppName}ModuleInterface, "{${Random_Uuid}}", AZ::Module);
+        AZ_CLASS_ALLOCATOR(${SanitizedCppName}ModuleInterface, AZ::SystemAllocator, 0);
+
+        ${SanitizedCppName}ModuleInterface()
+        {
+            // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here.
+            // Add ALL components descriptors associated with this gem to m_descriptors.
+            // This will associate the AzTypeInfo information for the components with the the SerializeContext, BehaviorContext and EditContext.
+            // This happens through the [MyComponent]::Reflect() function.
+            m_descriptors.insert(m_descriptors.end(), {
+                ${SanitizedCppName}SystemComponent::CreateDescriptor(),
+                });
+        }
+
+        /**
+         * Add required SystemComponents to the SystemEntity.
+         */
+        AZ::ComponentTypeList GetRequiredSystemComponents() const override
+        {
+            return AZ::ComponentTypeList{
+                azrtti_typeid<${SanitizedCppName}SystemComponent>(),
+            };
+        }
+    };
+}// namespace ${SanitizedCppName}

+ 95 - 0
Templates/Ros2ProjectTemplate/Template/Gem/Source/${Name}SampleComponent.cpp

@@ -0,0 +1,95 @@
+
+// {BEGIN_LICENSE}
+/*
+ * 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
+ *
+ */
+// {END_LICENSE}
+
+#include "${SanitizedCppName}SampleComponent.h"
+#include <AzCore/Component/ComponentApplicationBus.h>
+#include <AzCore/Component/TransformBus.h>
+#include <AzCore/Math/Transform.h>
+#include <AzCore/Serialization/EditContext.h>
+#include <AzCore/Serialization/SerializeContext.h>
+
+#include <imgui/imgui.h>
+
+#include <ROS2/ROS2Bus.h>
+#include <ROS2/ROS2GemUtilities.h>
+#include <ROS2/Utilities/ROS2Conversions.h>
+#include <ROS2/Utilities/ROS2Names.h>
+
+namespace ${SanitizedCppName}
+{
+
+    ${SanitizedCppName}SampleComponent::${SanitizedCppName}SampleComponent()
+    {
+        m_goalTopicConfiguration.m_topic = "/goal_pose";
+        m_goalTopicConfiguration.m_type = "geometry_msgs::msg::PoseStamped";
+    }
+
+    void ${SanitizedCppName}SampleComponent::Activate()
+    {
+        auto ros2Node = ROS2::ROS2Interface::Get()->GetNode();
+        m_goalPublisher = ros2Node->create_publisher<geometry_msgs::msg::PoseStamped>(m_goalTopicConfiguration.m_topic.data(), m_goalTopicConfiguration.GetQoS());
+        ImGui::ImGuiUpdateListenerBus::Handler::BusConnect();
+
+    }
+
+    void ${SanitizedCppName}SampleComponent::Deactivate()
+    {
+        m_goalPublisher.reset();
+        ImGui::ImGuiUpdateListenerBus::Handler::BusDisconnect();
+    }
+
+    void ${SanitizedCppName}SampleComponent::Reflect(AZ::ReflectContext* context)
+    {
+        if (AZ::SerializeContext* serialize = azrtti_cast<AZ::SerializeContext*>(context))
+        {
+            serialize->Class<${SanitizedCppName}SampleComponent, AZ::Component>()
+                ->Version(1)
+                ->Field("goals", &${SanitizedCppName}SampleComponent::m_goalEntities)
+                ->Field("goalMessageTopic", &${SanitizedCppName}SampleComponent::m_goalTopicConfiguration);
+
+            if (AZ::EditContext* ec = serialize->GetEditContext())
+            {
+                ec->Class<${SanitizedCppName}SampleComponent>("${SanitizedCppName}SampleComponent", "A sample component that sends goal")
+                    ->ClassElement(AZ::Edit::ClassElements::EditorData, "${SanitizedCppName}SampleComponent")
+                    ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("Game"))
+                    ->Attribute(AZ::Edit::Attributes::Category, "Sample ROS2 Project")
+                    ->DataElement(AZ::Edit::UIHandlers::Default, &${SanitizedCppName}SampleComponent::m_goalEntities, "Goals", "Entities goals")
+                    ->DataElement(
+                        AZ::Edit::UIHandlers::Default,
+                        &${SanitizedCppName}SampleComponent::m_goalTopicConfiguration,
+                        "Topic for goal message",
+                        "Configuration for ROS2 topic to send goal message to");
+            }
+        }
+    }
+
+    void ${SanitizedCppName}SampleComponent::OnImGuiUpdate()
+    {
+        ImGui::Begin("${SanitizedCppName}SampleComponent");
+        for (const auto& entityId : m_goalEntities)
+        {
+            AZStd::string entityName;
+            AZ::ComponentApplicationBus::BroadcastResult(entityName, &AZ::ComponentApplicationRequests::GetEntityName, entityId);
+            const AZStd::string buttonName = AZStd::string::format("Send goal %s", entityName.c_str());
+
+            if (ImGui::Button(buttonName.c_str()))
+            {
+                AZ::Transform transform;
+                AZ::TransformBus::EventResult(transform, entityId, &AZ::TransformBus::Events::GetWorldTM);
+                geometry_msgs::msg::PoseStamped poseStamped;
+                poseStamped.header.frame_id = "odom";
+                poseStamped.pose = ROS2::ROS2Conversions::ToROS2Pose(transform);
+                m_goalPublisher->publish(poseStamped);
+            }
+        }
+        ImGui::End();
+    }
+} // namespace ${SanitizedCppName}

+ 49 - 0
Templates/Ros2ProjectTemplate/Template/Gem/Source/${Name}SampleComponent.h

@@ -0,0 +1,49 @@
+
+// {BEGIN_LICENSE}
+/*
+ * 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
+ *
+ */
+// {END_LICENSE}
+#pragma once
+
+#include <AzCore/std/containers/vector.h>
+#include <AzCore/Component/Component.h>
+#include <AzCore/Component/EntityId.h>
+#include <ImGuiBus.h>
+
+#include <ROS2/ROS2Bus.h>
+#include <ROS2/Utilities/ROS2Names.h>
+#include <ROS2/Communication/TopicConfiguration.h>
+
+#include <rclcpp/publisher.hpp>
+
+#include <geometry_msgs/msg/pose_stamped.hpp>
+
+namespace ${SanitizedCppName}
+{
+
+    class ${SanitizedCppName}SampleComponent
+        : public AZ::Component
+        , public ImGui::ImGuiUpdateListenerBus::Handler
+    {
+    public:
+        AZ_COMPONENT(${SanitizedCppName}SampleComponent, "{${Random_Uuid}}", AZ::Component);
+        ${SanitizedCppName}SampleComponent();
+        ~${SanitizedCppName}SampleComponent() = default;
+        void Activate() override;
+        void Deactivate() override;
+        static void Reflect(AZ::ReflectContext* context);
+
+    private:
+        void OnImGuiUpdate() override;
+
+        AZStd::vector<AZ::EntityId> m_goalEntities;
+        std::shared_ptr<rclcpp::Publisher<geometry_msgs::msg::PoseStamped>> m_goalPublisher;
+        geometry_msgs::msg::PoseStamped m_goalMessage;
+        ROS2::TopicConfiguration m_goalTopicConfiguration;
+    };
+} // namespace ${SanitizedCppName}

+ 3 - 0
Templates/Ros2ProjectTemplate/Template/Gem/Source/${Name}SystemComponent.cpp

@@ -1,3 +1,4 @@
+// {BEGIN_LICENSE}
 /*
  * 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.
@@ -5,6 +6,8 @@
  * SPDX-License-Identifier: Apache-2.0 OR MIT
  *
  */
+// {END_LICENSE}
+
 
 #include <AzCore/Serialization/SerializeContext.h>
 #include <AzCore/Serialization/EditContext.h>

+ 2 - 1
Templates/Ros2ProjectTemplate/Template/Gem/enabled_gems.cmake

@@ -32,5 +32,6 @@ set(ENABLED_GEMS
     Compression
     WarehouseSample
     RosRobotSample
-    ROS2    
+    ROS2
+    ImGui
 )

+ 360 - 5
Templates/Ros2ProjectTemplate/Template/Levels/DemoLevel/DemoLevel.prefab

@@ -24,6 +24,7 @@
                 "$type": "EditorEntitySortComponent",
                 "Id": 14126657869720434043,
                 "Child Entity Order": [
+                    "Entity_[541237100205]",
                     "Instance_[650636534085]/ContainerEntity",
                     "Entity_[1176639161715]",
                     "Instance_[594075265400]/ContainerEntity"
@@ -167,6 +168,360 @@
                 }
             }
         },
+        "Entity_[519412679849]": {
+            "Id": "Entity_[519412679849]",
+            "Name": "marker",
+            "Components": {
+                "Component_[12345039422781644436]": {
+                    "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+                    "Id": 12345039422781644436,
+                    "Parent Entity": "Entity_[549827034797]",
+                    "Transform Data": {
+                        "UniformScale": 0.10000000149011612
+                    }
+                },
+                "Component_[16311924938308571969]": {
+                    "$type": "AZ::Render::EditorMeshComponent",
+                    "Id": 16311924938308571969,
+                    "Controller": {
+                        "Configuration": {
+                            "ModelAsset": {
+                                "assetId": {
+                                    "guid": "{1EE46427-D1C2-5698-B30D-B4CCF46B15CD}",
+                                    "subId": 274434668
+                                },
+                                "assetHint": "objects/_primitives/_sphere_1x1.azmodel"
+                            }
+                        }
+                    }
+                },
+                "Component_[17981273470482180971]": {
+                    "$type": "EditorEntitySortComponent",
+                    "Id": 17981273470482180971
+                },
+                "Component_[2065222331617120332]": {
+                    "$type": "EditorVisibilityComponent",
+                    "Id": 2065222331617120332
+                },
+                "Component_[2096811794606887530]": {
+                    "$type": "EditorDisabledCompositionComponent",
+                    "Id": 2096811794606887530
+                },
+                "Component_[2855690690078728486]": {
+                    "$type": "EditorPendingCompositionComponent",
+                    "Id": 2855690690078728486
+                },
+                "Component_[2949627029165663181]": {
+                    "$type": "EditorMaterialComponent",
+                    "Id": 2949627029165663181,
+                    "Controller": {
+                        "Configuration": {
+                            "materials": [
+                                {
+                                    "Key": {
+                                        "materialSlotStableId": 1738373820
+                                    },
+                                    "Value": {
+                                        "MaterialAsset": {
+                                            "assetId": {
+                                                "guid": "{9C47066E-BD8F-5C1B-B935-933296BBE312}"
+                                            },
+                                            "assetHint": "materials/presets/macbeth/15_red.azmaterial"
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    }
+                },
+                "Component_[3984524579386645338]": {
+                    "$type": "EditorLockComponent",
+                    "Id": 3984524579386645338
+                },
+                "Component_[6180558729729631244]": {
+                    "$type": "EditorInspectorComponent",
+                    "Id": 6180558729729631244
+                },
+                "Component_[6203285465432256414]": {
+                    "$type": "EditorOnlyEntityComponent",
+                    "Id": 6203285465432256414
+                },
+                "Component_[7639289353853227968]": {
+                    "$type": "EditorEntityIconComponent",
+                    "Id": 7639289353853227968
+                }
+            }
+        },
+        "Entity_[558067385513]": {
+            "Id": "Entity_[558067385513]",
+            "Name": "marker",
+            "Components": {
+                "Component_[12345039422781644436]": {
+                    "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+                    "Id": 12345039422781644436,
+                    "Parent Entity": "Entity_[545532067501]",
+                    "Transform Data": {
+                        "UniformScale": 0.10000000149011612
+                    }
+                },
+                "Component_[16311924938308571969]": {
+                    "$type": "AZ::Render::EditorMeshComponent",
+                    "Id": 16311924938308571969,
+                    "Controller": {
+                        "Configuration": {
+                            "ModelAsset": {
+                                "assetId": {
+                                    "guid": "{1EE46427-D1C2-5698-B30D-B4CCF46B15CD}",
+                                    "subId": 274434668
+                                },
+                                "assetHint": "objects/_primitives/_sphere_1x1.azmodel"
+                            }
+                        }
+                    }
+                },
+                "Component_[17981273470482180971]": {
+                    "$type": "EditorEntitySortComponent",
+                    "Id": 17981273470482180971
+                },
+                "Component_[2065222331617120332]": {
+                    "$type": "EditorVisibilityComponent",
+                    "Id": 2065222331617120332
+                },
+                "Component_[2096811794606887530]": {
+                    "$type": "EditorDisabledCompositionComponent",
+                    "Id": 2096811794606887530
+                },
+                "Component_[2855690690078728486]": {
+                    "$type": "EditorPendingCompositionComponent",
+                    "Id": 2855690690078728486
+                },
+                "Component_[2915573670259884143]": {
+                    "$type": "EditorMaterialComponent",
+                    "Id": 2915573670259884143,
+                    "Controller": {
+                        "Configuration": {
+                            "materials": [
+                                {
+                                    "Key": {
+                                        "materialSlotStableId": 1738373820
+                                    },
+                                    "Value": {
+                                        "MaterialAsset": {
+                                            "assetId": {
+                                                "guid": "{9C47066E-BD8F-5C1B-B935-933296BBE312}"
+                                            }
+                                        }
+                                    }
+                                }
+                            ]
+                        }
+                    }
+                },
+                "Component_[3984524579386645338]": {
+                    "$type": "EditorLockComponent",
+                    "Id": 3984524579386645338
+                },
+                "Component_[6180558729729631244]": {
+                    "$type": "EditorInspectorComponent",
+                    "Id": 6180558729729631244
+                },
+                "Component_[6203285465432256414]": {
+                    "$type": "EditorOnlyEntityComponent",
+                    "Id": 6203285465432256414
+                },
+                "Component_[7639289353853227968]": {
+                    "$type": "EditorEntityIconComponent",
+                    "Id": 7639289353853227968
+                }
+            }
+        },                
+        "Entity_[541237100205]": {
+            "Id": "Entity_[541237100205]",
+            "Name": "Navigation Goals",
+            "Components": {
+                "Component_[17164346591282540237]": {
+                    "$type": "EditorLockComponent",
+                    "Id": 17164346591282540237
+                },
+                "Component_[17415240968328691967]": {
+                    "$type": "EditorVisibilityComponent",
+                    "Id": 17415240968328691967
+                },
+                "Component_[2318932367744128274]": {
+                    "$type": "GenericComponentWrapper",
+                    "Id": 2318932367744128274,
+                    "m_template": {
+                        "$type": "${SanitizedCppName}SampleComponent",
+                        "goals": [
+                            "Entity_[549827034797]",
+                            "Entity_[545532067501]"
+                        ],
+                        "goalMessageTopic": {
+                            "QoS": {
+                                "Reliability": 1
+                            }
+                        }
+                    }
+                },
+                "Component_[3203687041529654758]": {
+                    "$type": "EditorPendingCompositionComponent",
+                    "Id": 3203687041529654758
+                },
+                "Component_[3619370023426190724]": {
+                    "$type": "EditorEntityIconComponent",
+                    "Id": 3619370023426190724
+                },
+                "Component_[4148220384898132613]": {
+                    "$type": "EditorDisabledCompositionComponent",
+                    "Id": 4148220384898132613
+                },
+                "Component_[5899025984044065453]": {
+                    "$type": "EditorInspectorComponent",
+                    "Id": 5899025984044065453
+                },
+                "Component_[6832755442212802241]": {
+                    "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+                    "Id": 6832755442212802241,
+                    "Parent Entity": "Entity_[1146574390643]"
+                },
+                "Component_[8029515861578295782]": {
+                    "$type": "EditorEntitySortComponent",
+                    "Id": 8029515861578295782,
+                    "Child Entity Order": [
+                        "Entity_[549827034797]",
+                        "Entity_[545532067501]"
+                    ]
+                },
+                "Component_[9981274412068549118]": {
+                    "$type": "EditorOnlyEntityComponent",
+                    "Id": 9981274412068549118
+                }
+            }
+        },
+        "Entity_[545532067501]": {
+            "Id": "Entity_[545532067501]",
+            "Name": "Goal1",
+            "Components": {
+                "Component_[11277380933974085137]": {
+                    "$type": "EditorPendingCompositionComponent",
+                    "Id": 11277380933974085137
+                },
+                "Component_[12905810954379407220]": {
+                    "$type": "EditorEntityIconComponent",
+                    "Id": 12905810954379407220
+                },
+                "Component_[17328806750758612971]": {
+                    "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+                    "Id": 17328806750758612971,
+                    "Parent Entity": "Entity_[541237100205]",
+                    "Transform Data": {
+                        "Translate": [
+                            1.9122638702392578,
+                            -12.367170333862305,
+                            0.3763278126716614
+                        ]
+                    }
+                },
+                "Component_[18390908158299660119]": {
+                    "$type": "EditorLockComponent",
+                    "Id": 18390908158299660119
+                },
+                "Component_[256417676809350783]": {
+                    "$type": "EditorInspectorComponent",
+                    "Id": 256417676809350783
+                },
+                "Component_[3366337885083272070]": {
+                    "$type": "EditorDebugDrawTextComponent",
+                    "Id": 3366337885083272070,
+                    "Element": {
+                        "Text": "Goal1",
+                        "DrawMode": 1,
+                        "TargetEntity": ""
+                    }
+                },
+                "Component_[3950088389572830633]": {
+                    "$type": "EditorEntitySortComponent",
+                    "Id": 3950088389572830633
+                },
+                "Component_[5284476042649108894]": {
+                    "$type": "EditorVisibilityComponent",
+                    "Id": 5284476042649108894
+                },
+                "Component_[5792898783935716826]": {
+                    "$type": "EditorDisabledCompositionComponent",
+                    "Id": 5792898783935716826
+                },
+                "Component_[9226016495091865703]": {
+                    "$type": "EditorOnlyEntityComponent",
+                    "Id": 3950088389572830633,
+                    "Child Entity Order": [
+                        "Entity_[558067385513]"
+                    ]
+                }
+            }
+        },
+        "Entity_[549827034797]": {
+            "Id": "Entity_[549827034797]",
+            "Name": "Goal2",
+            "Components": {
+                "Component_[12380249969967309339]": {
+                    "$type": "EditorPendingCompositionComponent",
+                    "Id": 12380249969967309339
+                },
+                "Component_[1279615831687276180]": {
+                    "$type": "EditorVisibilityComponent",
+                    "Id": 1279615831687276180
+                },
+                "Component_[15829810397729534730]": {
+                    "$type": "EditorEntityIconComponent",
+                    "Id": 15829810397729534730
+                },
+                "Component_[16607348232201416003]": {
+                    "$type": "EditorOnlyEntityComponent",
+                    "Id": 16607348232201416003
+                },
+                "Component_[16857701992694867242]": {
+                    "$type": "EditorInspectorComponent",
+                    "Id": 16857701992694867242
+                },
+                "Component_[18080104825946547885]": {
+                    "$type": "EditorEntitySortComponent",
+                    "Id": 18080104825946547885,
+                    "Child Entity Order": [
+                        "Entity_[519412679849]"
+                    ]
+                },
+                "Component_[2090523752184566705]": {
+                    "$type": "EditorDisabledCompositionComponent",
+                    "Id": 2090523752184566705
+                },
+                "Component_[4266310762232934077]": {
+                    "$type": "EditorDebugDrawTextComponent",
+                    "Id": 4266310762232934077,
+                    "Element": {
+                        "Text": "Goal2",
+                        "DrawMode": 1,
+                        "TargetEntity": ""
+                    }
+                },
+                "Component_[442524716645776730]": {
+                    "$type": "EditorLockComponent",
+                    "Id": 442524716645776730
+                },
+                "Component_[4482050777360513753]": {
+                    "$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
+                    "Id": 4482050777360513753,
+                    "Parent Entity": "Entity_[541237100205]",
+                    "Transform Data": {
+                        "Translate": [
+                            5.516890525817871,
+                            -12.526392936706543,
+                            0.16321372985839844
+                        ]
+                    }
+                }
+            }
+        },
         "Entity_[884702554292]": {
             "Id": "Entity_[884702554292]",
             "Name": "Environmental Light",
@@ -272,7 +627,7 @@
                 {
                     "op": "replace",
                     "path": "/Instances/Instance_[3732164110801]/Entities/Entity_[7512134816442]/Components/Component_[2887398288324076962]/Configuration/Centre of mass offset/0",
-                    "value": 8.756181335556334e-10
+                    "value": 8.756181335556331e-10
                 },
                 {
                     "op": "replace",
@@ -312,7 +667,7 @@
                 {
                     "op": "replace",
                     "path": "/Instances/Instance_[3732164110801]/Entities/Entity_[7486365012666]/Components/Component_[17075084475750345983]/Configuration/Centre of mass offset/0",
-                    "value": 8.756181335556334e-10
+                    "value": 8.756181335556331e-10
                 },
                 {
                     "op": "replace",
@@ -342,7 +697,7 @@
                 {
                     "op": "replace",
                     "path": "/Instances/Instance_[3732164110801]/Entities/Entity_[7494954947258]/Components/Component_[5546255188884728002]/Configuration/Centre of mass offset/0",
-                    "value": 8.756181335556334e-10
+                    "value": 8.756181335556331e-10
                 },
                 {
                     "op": "replace",
@@ -377,7 +732,7 @@
                 {
                     "op": "replace",
                     "path": "/Instances/Instance_[3732164110801]/Entities/Entity_[7525019718330]/Components/Component_[11685358238844952494]/Configuration/Centre of mass offset/0",
-                    "value": 8.756181335556334e-10
+                    "value": 8.756181335556331e-10
                 },
                 {
                     "op": "replace",
@@ -462,7 +817,7 @@
                 {
                     "op": "replace",
                     "path": "/ContainerEntity/Components/Component_[959982562508610]/Transform Data/Translate/1",
-                    "value": -12.669219970703125
+                    "value": -12.669219970703123
                 },
                 {
                     "op": "replace",

+ 38 - 1
Templates/Ros2ProjectTemplate/template.json

@@ -14,6 +14,10 @@
     ],
     "icon_path": "preview.png",
     "copyFiles": [
+        {
+            "file": ".clang-format",
+            "isTemplated": false
+        },
         {
             "file": ".gitattributes",
             "isTemplated": false
@@ -42,10 +46,19 @@
             "file": "Gem/${NameLower}_files.cmake",
             "isTemplated": true
         },
+        {
+            "file": "Gem/${NameLower}_editor_files.cmake",
+            "isTemplated": true
+        },
         {
             "file": "Gem/${NameLower}_shared_files.cmake",
             "isTemplated": true
         },
+        {
+            "file": "Gem/${NameLower}_editor_shared_files.cmake",
+            "isTemplated": true
+        },
+        
         {
             "file": "Gem/CMakeLists.txt",
             "isTemplated": true
@@ -122,6 +135,14 @@
             "file": "Gem/Source/${Name}Module.cpp",
             "isTemplated": true
         },
+        {
+            "file": "Gem/Source/${Name}EditorModule.cpp",
+            "isTemplated": true
+        },
+        {
+            "file": "Gem/Source/${Name}ModuleInterface.h",
+            "isTemplated": true
+        },
         {
             "file": "Gem/Source/${Name}SystemComponent.cpp",
             "isTemplated": true
@@ -130,6 +151,22 @@
             "file": "Gem/Source/${Name}SystemComponent.h",
             "isTemplated": true
         },
+        {
+            "file": "Gem/Source/${Name}EditorSystemComponent.cpp",
+            "isTemplated": true
+        },
+        {
+            "file": "Gem/Source/${Name}EditorSystemComponent.h",
+            "isTemplated": true
+        },
+        {
+            "file": "Gem/Source/${Name}SampleComponent.cpp",
+            "isTemplated": true
+        },
+        {
+            "file": "Gem/Source/${Name}SampleComponent.h",
+            "isTemplated": true
+        },
         {
             "file": "Gem/enabled_gems.cmake",
             "isTemplated": true
@@ -140,7 +177,7 @@
         },
         {
             "file": "Levels/DemoLevel/DemoLevel.prefab",
-            "isTemplated": false
+            "isTemplated": true
         },
         {
             "file": "Platform/Android/android_project.cmake",