Version:

第40章 多人游戏摄像机

第40章 多人游戏摄像机

介绍

注意:
本章随附的源代码和资源可以在 GitHub 上找到: https://github.com/AMZN-Olex/O3DEBookCode2111/tree/ch40_multiplayer_camera

第 39 章,Team Spawner,创建了两队鸡,但断开了摄像机对鸡的跟踪。本章将把摄像机连接到玩家控制的鸡上。关卡中有八 (8) 只鸡,因此我们需要一些额外的信息来找出适合每个客户的鸡。

自主控制器

在开发多人游戏的过程中,您通常需要执行一些游戏逻辑,这些逻辑应该只在本地玩家控制的实体上运行,而不应该在其他任何地方运行,而不是在服务器上运行,也不应该在由其他客户端控制的实体上运行。例如,在本章中,我们希望摄像机仅跟随本地玩家控制的鸡。关卡中的其他鸡不得抢夺相机。这就是自主角色的用武之地。

自主角色是一种特殊角色,仅授予由生成器专门分配的客户端实体,正如我们在第 34 章 简单玩家生成器和第 39 章 团队生成器中所做的那样。

常规客户端组件不会获得控制器,但 Autonomous Entity 会获得控制器,并且仅适用于该 Autonomous Entity。仅允许自治实体向服务器发送输入。您可以使用 MultiplayerController::IsAutonomous 方法判断您的控制器是否是自主的。

void ChickenCameraComponentController::OnActivate(...)
{
if (IsAutonomous())
{
// ...

如果您的客户端实体具有自治控制器,则可以放心地假设这是客户端控制的实体,这使其成为摄像机的正确锚点。

Chicken Camera 组件

Chicken Camera 组件将是一个覆盖其控制器的多人游戏组件,以便我们可以将摄像机跟随逻辑放置在玩家控制的 Chicken 实体上。它将允许用户指定其相机偏移量,以便我们可以将相机放在鸡后面。

<ArchetypeProperty Type="AZ::Vector3" Name="CameraOffset"
ExposeToEditor="true" />

在构造函数中,我们将注册游戏 tick 事件,但前提是控制器是自主的。

void ChickenCameraComponentController::OnActivate(
Multiplayer::EntityIsMigrating)
{
if (IsAutonomous())
        {
            AZ::TickBus::Handler::BusConnect();
        }
    }

图 40.1.Chicken Camera 组件的设计

  1. 在每个游戏 tick 上,抓住活动的摄像机。CameraSystemRequestBus 是一个 EBus,它将为您提供相机。
AZ::Entity* ChickenCameraComponentController::GetActiveCamera()
{
using namespace AZ;
using namespace Camera;
EntityId activeCameraId;
CameraSystemRequestBus::BroadcastResult(
activeCameraId,
&CameraSystemRequestBus::Events::GetActiveCamera);
auto ca = Interface<ComponentApplicationRequests>::Get();
return ca->FindEntity(activeCameraId);
}
  1. 查询玩家控制的鸡的当前位置和方向。
AZ::Transform chicken = GetParent().
GetTransformComponent()->GetWorldTM();
  1. 通过 GetCameraOffset() 偏移位置,并将摄像机移动到鸡后面。
void ChickenCameraComponentController::OnTick(
float, AZ::ScriptTimePoint)
{
 ...
        AZ::Transform chicken =
            GetParent().GetTransformComponent()->GetWorldTM();
        AZ::Vector3 camera = chicken.GetTranslation() +
            chicken.GetRotation().TransformVector(GetCameraOffset());
        chicken.SetTranslation(camera);
 m_activeCameraEntity->GetTransform()->SetWorldTM(chicken);
    }

实体更改

将 Chicken Camera 组件放置在 Chicken_Team_0.prefab 和 Chicken_Team_1.prefab 中的 Chicken 实体上。

提示:

将 Chicken Camera 组件添加到预制件并保存后,您可以在没有 Editor 的情况下修改组件值。

  1. 在 MyProject\Prefabs\Chicken_Team_0.prefab 中打开预制件
  2. 搜索 “ChickenCamera”。
  3. 这将采用组件的 JSON 格式配置。
"m_template": {
  "$type": "MyGem::ChickenCameraComponent",
  "CameraOffset": [
      0.0,
      -3.0,
      1.5
    ]
}
  1. 进行所需的更改并保存预制件文件。

小结

{{note}} 本章随附的源代码和资源可以在 GitHub 上找到: https://github.com/AMZN-Olex/O3DEBookCode2111/tree/ch40_multiplayer_camera {{/note}}

在本章中,我们创建了一个组件,该组件将摄像机保持在玩家在客户端上控制的鸡后面。这允许我们在关卡上保留单个摄像机组件。在服务器上,摄像机永远不会移动,因为服务器上的控制器是权威的,而不是自主的。在每个客户端上,相机将仅由 Chicken Camera 组件之一移动。

例 40.1.鸡后面的摄像机

例 40.2. ChickenCameraComponent.AutoComponent.xml

<?xml version="1.0"?>
 <Component
 Name="ChickenCameraComponent"
 Namespace="MyGem"
 OverrideComponent="false"
 OverrideController="true"
 OverrideInclude="Multiplayer/ChickenCameraComponent.h"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
 <ComponentRelation Constraint="Weak" HasController="false"
 Name="TransformComponent" Namespace="AzFramework"
 Include="AzFramework/Components/TransformComponent.h"/>
 <ArchetypeProperty Type="AZ::Vector3" Name="CameraOffset"
 ExposeToEditor="true" />
 </Component>

例 40.3. ChickenCameraComponent.h

#pragma once
 #include <AzCore/Component/TickBus.h>
 #include <Source/AutoGen/ChickenCameraComponent.AutoComponent.h>
 namespace MyGem
 {
 class ChickenCameraComponentController
        : 
public ChickenCameraComponentControllerBase
        , 
public AZ::TickBus::Handler
    {
 public:
        ChickenCameraComponentController(ChickenCameraComponent& p);
 void OnActivate(Multiplayer::EntityIsMigrating) override;
 void OnDeactivate(Multiplayer::EntityIsMigrating) override;
  // TickBus
 void OnTick(float deltaTime, AZ::ScriptTimePoint) override;
 private:
        AZ::Entity* m_activeCameraEntity = nullptr;
        AZ::Entity* GetActiveCamera();
    };
 }

例 40.4. ChickenCameraComponent.cpp

 #include <AzCore/Component/ComponentApplicationBus.h>
 #include <AzFramework/Components/CameraBus.h>
 #include <AzFramework/Components/TransformComponent.h>
 #include <Multiplayer/ChickenCameraComponent.h>
 namespace MyGem
 {
    ChickenCameraComponentController::
        ChickenCameraComponentController(ChickenCameraComponent& p)
        : ChickenCameraComponentControllerBase(p) {}
 void ChickenCameraComponentController::OnActivate(
        Multiplayer::EntityIsMigrating)
    {
 if (IsAutonomous())
        {
            AZ::TickBus::Handler::BusConnect();
        }
    }
 void ChickenCameraComponentController::OnDeactivate(
        Multiplayer::EntityIsMigrating)
    {
        AZ::TickBus::Handler::BusDisconnect();
    }
 void ChickenCameraComponentController::OnTick(
 float, AZ::ScriptTimePoint)
    {
 if (!m_activeCameraEntity)
        {
            m_activeCameraEntity = GetActiveCamera();
 return;
        }
        AZ::Transform chicken =
            GetParent().GetTransformComponent()->GetWorldTM();
        AZ::Vector3 camera = chicken.GetTranslation() +
            chicken.GetRotation().TransformVector(GetCameraOffset());
        chicken.SetTranslation(camera);
        m_activeCameraEntity->GetTransform()->SetWorldTM(chicken);
    }
     AZ::Entity* ChickenCameraComponentController::GetActiveCamera()
    {
 using namespace AZ;
 using namespace Camera;
        EntityId activeCameraId;
        CameraSystemRequestBus::BroadcastResult( activeCameraId,
            &CameraSystemRequestBus::Events::GetActiveCamera);
 auto ca = Interface<ComponentApplicationRequests>::Get();
 return ca->FindEntity(activeCameraId);
    }
 }