瀏覽代碼

Alternate example of multiplier sample entity filtering component using occlusion culling bus

This is another example of a multiplayer entity filtering component that should prevent network replication for players or objects that cannot be seen by the client, if there is an active occlusion culling system that utilizes the occlusion request bus.

Signed-off-by: Guthrie Adams <[email protected]>
Guthrie Adams 1 年之前
父節點
當前提交
46182e1896

+ 80 - 0
Gem/Code/Source/Components/OcclusionFilteredEntityComponent.cpp

@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#include <AzCore/Serialization/EditContext.h>
+#include <AzFramework/Visibility/OcclusionBus.h>
+#include <Components/OcclusionFilteredEntityComponent.h>
+#include <Multiplayer/IMultiplayer.h>
+#include <Multiplayer/Components/NetBindComponent.h>
+
+namespace MultiplayerSample
+{
+    void OcclusionFilteredEntityComponent::Reflect(AZ::ReflectContext* context)
+    {
+        AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
+        if (serializeContext)
+        {
+            serializeContext->Class<OcclusionFilteredEntityComponent, AZ::Component>()
+                ->Version(1);
+
+            if (AZ::EditContext* editContext = serializeContext->GetEditContext())
+            {
+                editContext->Class<OcclusionFilteredEntityComponent>("OcclusionFilteredEntityComponent", "Filters occluded player entities out of network replication")
+                    ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
+                    ->Attribute(AZ::Edit::Attributes::Category, "MultiplayerSample")
+                    ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Level"))
+                ;
+            }
+        }
+    }
+
+    void OcclusionFilteredEntityComponent::Activate()
+    {
+        AZ::Interface<IFilterEntityManager>::Register(this);
+
+        // Create an occlusion view just for performing clear to player visibility tests.
+        AzFramework::OcclusionRequestBus::Broadcast(&AzFramework::OcclusionRequestBus::Events::CreateOcclusionView, m_occlusionViewName);
+    }
+
+    void OcclusionFilteredEntityComponent::Deactivate()
+    {
+        AZ::Interface<IFilterEntityManager>::Unregister(this);
+        AzFramework::OcclusionRequestBus::Broadcast(&AzFramework::OcclusionRequestBus::Events::DestroyOcclusionView, m_occlusionViewName);
+    }
+
+    bool OcclusionFilteredEntityComponent::IsEntityFiltered(
+        [[maybe_unused]] AZ::Entity* entity,
+        [[maybe_unused]] Multiplayer::ConstNetworkEntityHandle controllerEntity,
+        [[maybe_unused]] AzNetworking::ConnectionId connectionId)
+    {
+        bool result = false;
+
+        // If one is available, use the occlusion culling system to filter out players that cannot be seen by the controlled player.
+        const auto entityNetBindComponent = entity->FindComponent<Multiplayer::NetBindComponent>();
+        const auto entityConnectionId = entityNetBindComponent ? entityNetBindComponent->GetOwningConnectionId() : AzNetworking::InvalidConnectionId;
+        if (entityConnectionId != AzNetworking::InvalidConnectionId)
+        {
+            AzFramework::OcclusionRequestBus::Broadcast([&](AzFramework::OcclusionRequestBus::Events* occlusionHandler) {
+                // Check for a preexisting occlusion view or create one if necessary.
+                if (!occlusionHandler->IsOcclusionView(m_occlusionViewName))
+                {
+                    occlusionHandler->CreateOcclusionView(m_occlusionViewName);
+                }
+                if (occlusionHandler->IsOcclusionView(m_occlusionViewName))
+                {
+                    // Perform an occlusion query to determine if the controlled entity can see the filtered entity.
+                    const AZStd::vector<bool> visibility = occlusionHandler->GetOcclusionViewEntityToEntityVisibility(
+                        m_occlusionViewName, controllerEntity.GetEntity()->GetId(), AZStd::vector<AZ::EntityId>{ entity->GetId() }
+                    );
+                    // If the query succeeded and the entity cannot be seend then filter it out from network replication.
+                    result = !visibility.empty() && !visibility[0];
+                }
+                });
+        }
+        return result;
+    }
+}

+ 40 - 0
Gem/Code/Source/Components/OcclusionFilteredEntityComponent.h

@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) Contributors to the Open 3D Engine Project
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR MIT
+ *
+ */
+
+#pragma once
+
+#include <AzCore/Component/Component.h>
+#include <Multiplayer/NetworkEntity/IFilterEntityManager.h>
+
+namespace MultiplayerSample
+{
+    //! @class OcclusionFilteredEntityComponent
+    //! @brief An example of using IFilterEntityManager that filters occluded player entities out of network replication.
+    class OcclusionFilteredEntityComponent final
+        : public AZ::Component
+        , public Multiplayer::IFilterEntityManager
+    {
+    public:
+        AZ_COMPONENT(MultiplayerSample::OcclusionFilteredEntityComponent, "{D1C628E6-F165-47F4-9C12-7BA96A80DF50}");
+
+        static void Reflect(AZ::ReflectContext* context);
+
+        //! AZ::Component overrides.
+        //! @{
+        void Activate() override;
+        void Deactivate() override;
+        //! }@
+
+        //! IFilterEntityManager overrides.
+        //! @{
+        bool IsEntityFiltered(AZ::Entity* entity, Multiplayer::ConstNetworkEntityHandle controllerEntity, AzNetworking::ConnectionId connectionId) override;
+        //! }@
+
+    private:
+        const AZ::Name m_occlusionViewName{ "ExampleFilteredEntityView" };
+    };
+}

+ 2 - 0
Gem/Code/Source/MultiplayerSampleModule.cpp

@@ -9,6 +9,7 @@
 #include <AzCore/Module/Module.h>
 #include <Components/AttachPlayerWeaponComponent.h>
 #include <Components/ExampleFilteredEntityComponent.h>
+#include <Components/OcclusionFilteredEntityComponent.h>
 #include <Components/PerfTest/NetworkPrefabSpawnerComponent.h>
 #include <Components/UI/UiCoinCountComponent.h>
 #include <Components/UI/UiGameOverComponent.h>
@@ -44,6 +45,7 @@ namespace MultiplayerSample
                 MultiplayerSampleSystemComponent::CreateDescriptor(),
                 AttachPlayerWeaponComponent::CreateDescriptor(),
                 ExampleFilteredEntityComponent::CreateDescriptor(),
+                OcclusionFilteredEntityComponent::CreateDescriptor(),
                 NetworkPrefabSpawnerComponent::CreateDescriptor(),
                 UiCoinCountComponent::CreateDescriptor(),
                 BackgroundMusicComponent::CreateDescriptor(),

+ 2 - 0
Gem/Code/multiplayersample_files.cmake

@@ -39,6 +39,8 @@ set(FILES
     Source/Components/NetworkWeaponsComponent.h
     Source/Components/NetworkSimplePlayerCameraComponent.cpp
     Source/Components/NetworkSimplePlayerCameraComponent.h
+    Source/Components/OcclusionFilteredEntityComponent.h
+    Source/Components/OcclusionFilteredEntityComponent.cpp
     Source/Components/PerfTest/NetworkPrefabSpawnerComponent.cpp
     Source/Components/PerfTest/NetworkPrefabSpawnerComponent.h
     Source/Components/PerfTest/NetworkRandomImpulseComponent.cpp