四川营销型网站建设/全网网络营销

四川营销型网站建设,全网网络营销,c 全套网站开发,建设产品网站本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P35 网络角色(Network Role)》 的学习笔记,该系列教学视频为计算机工程师、程序员、游戏开发者、作家(Engineer, Programmer, Game Developer, Author) Stephe…

本文为B站系列教学视频 《UE5_C++多人TPS完整教程》 —— 《P35 网络角色(Network Role)》 的学习笔记,该系列教学视频为计算机工程师、程序员、游戏开发者、作家(Engineer, Programmer, Game Developer, Author) Stephen Ulibarri 发布在 Udemy 上的课程 《Unreal Engine 5 C++ Multiplayer Shooter》 的中文字幕翻译版,UP主(也是译者)为 游戏引擎能吃么。
在这里插入图片描述


文章目录

  • P35 网络角色(Network Role)
  • 35.1 网络角色概念
  • 35.2 创建显示网络角色的控件
  • 35.3 显示本地网络角色
  • 35.4 显示远程网络角色
  • 35.5 Summary


P35 网络角色(Network Role)

本节课将讨论虚幻引擎中网络角色(Network role)的概念以及如何在多人游戏中使用它;“网络角色” 包括本地角色(Local role)和远程角色(Remote role),我们将比较两者的不同之处,接着为了更好地掌握(Get a better grasp)网络角色的概念并学以致用,我们将创建一个特殊的部件,它的功能是将玩家控制的游戏人物的当前角色显示在人物头上(Overhead)。
在这里插入图片描述

注意:
这里需要避免与虚幻引擎中 “Character” 的中文翻译 “角色” 混淆,以及与 “Actor Role” 进行区分。


在Unreal Engine 4(UE4)中,Network Role 和 Actor Role 是两个相关但不同的概念。
区别:

  • Network Role 主要用于描述Actor在网络环境中的角色,决定了Actor在客户端和服务器之间的行为和职责。
  • Actor Role 主要用于描述Actor在游戏逻辑中的角色,决定了Actor在游戏中的行为和职责。

联系:

  • 在大多数情况下,Network Role 和 Actor Role 是一致的。例如,服务器上的Actor通常同时具有 ROLE_Authority 的 Network Role 和 Actor Role。
  • 在客户端上,玩家控制的角色通常同时具有 ROLE_AutonomousProxy 的 Network Role 和 Actor Role。

非玩家控制的角色或其他对象通常同时具有 ROLE_SimulatedProxy 的 Network Role 和 Actor Role。


—— CSDN 《【UE 网络】Network Role and Authority、Actors Owner、Actor Role and RemoteRole》


35.1 网络角色概念

  1. 在多人游戏中,玩家控制的任何给定人物都有多个版本(Mutiple versions)。如果玩家是连接到服务器的客户端,那么他所控制的游戏人物在自己和其他客户端以及服务器都分别存在一个版本;例如,多人游戏中有两个玩家,那么其中一个玩家所控制的游戏人物在自己的机器上有一个版本,在服务器和另一个玩家的机器上也有一个版本(无法被另一个玩家所控制)。由此可知,如果多人游戏中有三个玩家,那么其中一个玩家将会在其他机器上有三个副本,因此如何区分(Distinguish)我们正在处理的角色属于哪个版本就至关重要了。
    在这里插入图片描述

  2. 为了解决这个问题(Sort this problem out),虚幻引擎引入了网络角色的概念以及相应的枚举变量(Enum)“ENetRole”,它包含几个常用的枚举常量(Enum constant),以供我们识别任何给定的玩家人物的网络角色:

    • ENetRole::ROLE_Authority”:虚幻引擎使用权威服务器模型(Authoritative server model),“ROLE_Authority” 会被分配给(Be assigned to)存在于服务器上的任何人物。
    • ENetRole::ROLE_SimulatedProxy”:“SimulatedProxy” 可以翻译成 “模拟代理”,顾名思义,它存在于任何不控制当前玩家人物的其他客户端机器上,即当你在自己的机器上控制人物时,你的机器不是服务器,那么 “`ROLE_SimulatedProxy``” 将会分配给你看到其他人物,它们来自服务器和其他客户端,被其他玩家控制。
    • ENetRole::ROLE_AutonomousProxy”:“AutonomousProxy” 可以翻译成 “自主代理” 存在于可控制当前玩家人物的客户端机器上,即当你在自己的机器上控制人物时,假设你的机器不是服务器,那么“ROLE_AutonomousProxy” 会被分给你的机器;如果你的机器是服务器。
    • ENetRole::ROLE_None”:分配给没有被定义网络角色的人物。

    在这里插入图片描述


    虚幻引擎使用的默认模型是 服务器授权,意味着服务器对游戏状态固定具有权限,而信息固定从服务器复制到客户端。服务器上的Actor应具有授权的本地角色,而其在远程客户端上的对应Actor应具有模拟或自主代理的本地角色。

    1. Authority (权威角色 / 权威端)
      Authority Actor,又叫做权威端。指的是服务器上的 Actor。服务器是游戏的权威,负责管理和验证所有的游戏状态和操作。

      • 服务器控制:Authority Actor在服务器上有完全的控制权,所有的游戏逻辑和状态更新都在服务器上进行。
      • 状态验证:服务器会验证客户端发送的请求和操作,确保游戏的公平性和一致性。
      • 广播更新:服务器会将更新后的状态广播给所有相关的客户端,确保所有客户端的游戏状态保持一致。
    2. Simulated Proxy (模拟代理 / 模拟端)
      Simulated Proxy Actor通常用于客户端上的非自主 Actor 。这些 Actor 在客户端上进行模拟,但最终的状态由服务器决定。

      • 客户端模拟:Simulated Proxy Actor 在客户端上进行模拟,以提供即时的反馈和流畅的游戏体验。
      • 服务器同步:尽管客户端进行模拟,最终的状态还是由服务器决定并同步到客户端。
      • 减少延迟感:通过在客户端进行模拟,可以减少网络延迟带来的影响,使游戏体验更加流畅。
    3. Autonomous Proxy (自主代理 / 主动端)
      Autonomous Proxy Actor 通常用于客户端拥有的 Actor ,例如玩家控制的角色(Player Character)。这种 Actor 在客户端上有更多的控制权,并且可以自主地进行一些操作。在UE4的网络架构中,主动端(Autonomous Proxy)主要用于玩家角色,以便直接响应玩家输入并进行本地预测。

      • 客户端控制:Autonomous Proxy Actor 在客户端上有更多的控制权,允许客户端直接对 Actor 进行输入和操作。
      • 本地预测:客户端可以进行本地预测,以减少网络延迟带来的影响。例如,玩家移动时,客户端可以立即显示移动效果,而不必等待服务器的确认。
      • 同步到服务器:尽管客户端有更多的控制权,但最终的状态还是需要同步到服务器,服务器会进行验证和纠正。

    —— CSDN 《【UE 网络】Network Role and Authority、Actors Owner、Actor Role and RemoteRole》


35.2 创建显示网络角色的控件

  1. 在虚幻引擎内容浏览器 “C++ 类”(C++ Classes)目录下新建一个 “UserWidget” C++ 类,命名为 “OverheadWidget”,路径为“.../Blaster/HUD”。
    在这里插入图片描述
    在这里插入图片描述

  2. 在 Visual Studio 中打开头文件 “OverheadWidget.h”,声明 "UTextBlock 类变量 “DisplayText” ,然后进行编译。

    // Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h"
    #include "Blueprint/UserWidget.h"
    #include "OverheadWidget.generated.h"/*** */
    UCLASS()
    class BLASTER_API UOverheadWidget : public UUserWidget
    {GENERATED_BODY()/* P35 网络角色(Network Role)*/public:UPROPERTY((meta = BindWidget))	// 将 C++ 变量 DisplayText 与蓝图部件中的文本块 DisplayText 关联class UTextBlock* DisplayText;	// 创建文本块 C++ 类,我们对这个变量的任何更改都会关联到蓝图部件中的文本块/* P35 网络角色(Network Role)*/
    };
  3. 在虚幻引擎的内容浏览器 “/内容/Blueprints” 目录下新建文件夹 “HUD”,然后新建一个 “控件蓝图” 类 “WBP_OverheadWidget”。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  4. 双击 “WBP_OverheadWidget”,进入用户控件设计器窗口。如果在左下 “层级” 面板中有 “画布画板” (Canvas Panel),需要将其删除,接着在 “控制板” 面板中将 “通用” 选项卡下的 “文本”(Text)组件拖拽到设计器中,调整其大小(Resize),重命名为 “DisplayText”,这里需要和头文件 “OverheadWidget.h” 中 "UTextBlock 类变量 “DisplayText” 的变量名保持一致;然后在右侧 “细节” 面板的 “字体”(Font)选项卡下设置 “字体样式”(Typeface)为 “常规”(Regular),设置 “对齐”(Justification)为 “居中对齐”(Align center text
    在这里插入图片描述

  5. 在右上方点击 “图表”(Graph)按钮,进入图表编辑模式,在上方工具栏点击 “类设置”(Class Settings),然后在左下方 “细节”(Details)面板中设置 “类选项” 下的 “父类”(Parent Class)为 “OverheadWidget”。
    在这里插入图片描述
    在这里插入图片描述


35.3 显示本地网络角色

  1. 返回 Visual Studio,打开 “OverheadWidget.h” 和 “OverheadWidget.cpp”,覆写原生函数 “OnLevelRemovedFromWorld()”,当离开当前关卡或进行关卡转移时将调用此函数移除部件 “OverheadWidget”(注意在 5.1 之后的版本中 “virtual void OnLevelRemoveFromWorld()” 被去除,取而代之的是 “virtual void NativeDestruct());接着,声明并定义函数 “SetDisplayText()” 和 “ShowPlayerNetRole()”,用于获取并展示本机玩家网络角色后设置部件 “OverheadWidget” 中文本块 “DisplayText” 显示的文本为玩家的网络角色。
    /*** OverheadWidget.h ***/// Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h"
    #include "Blueprint/UserWidget.h"
    #include "OverheadWidget.generated.h"/*** */
    UCLASS()
    class BLASTER_API UOverheadWidget : public UUserWidget
    {GENERATED_BODY()/* P35 网络角色(Network Role)*/public:UPROPERTY(meta = (BindWidget))	// 将 C++ 变量 DisplayText 与蓝图部件中的文本块 DisplayText 关联class UTextBlock* DisplayText;	// 创建文本块 C++ 类,我们对这个变量的任何更改都会关联到蓝图部件中的文本块void SetDisplayText(FString TextToDisplay);	// 用于设置并显示文本块的文本UFUNCTION(BlueprintCallable)				// 可在蓝图类 BP_Blaster 调用void ShowPlayerNetRole(APawn* InPawn);		// 获取并展示本机玩家网络角色protected:virtual void OnLevelRemovedFromWorld(ULevel* InLevel, UWorld* InWorld) override;	// 覆写原生函数 OnLevelRemovedFromWorld(),当离开当前关卡或进行关卡转移时将调用此函数移除部件// void OnLevelRemovedFromWorld(): https://docs.unrealengine.com/5.0/en-US/API/Runtime/UMG/Blueprint/UUserWidget/OnLevelRemovedFromWorld/// 在 5.1 之后的版本中 virtual void OnLevelRemoveFromWorld() 被去除,取而代之的是 virtual void NativeDestruct() // void NativeDestruct(): https://docs.unrealengine.com/5.1/en-US/API/Runtime/UMG/Blueprint/UUserWidget/NativeDestruct/// virtual void NativeDestruct() override;/* P35 网络角色(Network Role)*/
    };
    
    /*** OverheadWidget.cpp ***/// Fill out your copyright notice in the Description page of Project Settings./* P35 网络角色(Network Role)*/
    #include "OverheadWidget.h"	// 原来自动生成的代码是 #include "HUD/OverheadWidget.h",这里需要把 "GameMode/" 去掉,否则找不到文件 "LobbyGameMode.h"
    #include "Components/TextBlock.h"void UOverheadWidget::SetDisplayText(FString TextToDisplay)		// 设置文本块 DisplayText 显示的文本
    {	if (DisplayText){DisplayText->SetText(FText::FromString(TextToDisplay));	// 将要展示的文本 TextToDisplay 由虚幻引擎字符流类型 FString 转换为文本类型 FText,并将文本块的文本设置为 TextToDisplay 的内容}
    }void UOverheadWidget::ShowPlayerNetRole(APawn* InPawn)	// 展示本地玩家网络角色
    {ENetRole LocalRole = InPawn->GetLocalRole();		// 获取本地玩家网络角色(本地网络角色会因调用它的机器不同)FString Role;switch (LocalRole)								// 根据 LocalRole 的值来给 Role 赋值{case ENetRole::ROLE_Authority:					// 本地玩家是 Authority (权威角色 / 权威端)Role = FString("Authority");break;										// 添加 Break 语句直接退出 switch 分支,后面的 case 语句将不再执行case ENetRole::ROLE_AutonomousProxy:			// 本地玩家是 Autonomous Proxy (权威角色 / 权威端)Role = FString("Autonomous Proxy");	break;					case ENetRole::ROLE_SimulatedProxy:				// 本地玩家是 Simulated Proxy(模拟代理 / 模拟端)Role = FString("Simulated Proxy");break;case ENetRole::ROLE_None:						// 本地玩家没有分配网络角色Role = FString("None");						break;default:break;}FString LocalRoleString = FString::Printf(TEXT("Local Role: %s"), *Role);	// 打印网络角色以便进行调试SetDisplayText(LocalRoleString);											// 设置文本块 DisplayText 显示的文本			
    }void UOverheadWidget::OnLevelRemovedFromWorld(ULevel* InLevel, UWorld* InWorld)	// 当转移关卡时删除控件
    {RemoveFromParent();	// 从父类删除实体,用于从场景中删除实体(Removes this entity from its parent. This is used to remove entities from the scene.)// https://dev.epicgames.com/documentation/zh-cn/uefn/verse-api/unrealenginedotcom/temporary/scenegraph/entity/removefromparent?application_version=1.0Super::OnLevelRemovedFromWorld(InLevel, InWorld);	// 调用父类的 NativeInitializeAnimation() 函数
    }
    /*
    void UMenu::NativeDestruct()
    {MenuTearDown();Super::NativeDestruct();	// 调用父类的 NativeDestruct() 函数
    }
    *//* P35 网络角色(Network Role)*/
    
  1. 打开 “BlasterCharacter.h”,声明头部组件 “OverheadWidget” 为 “ABlasterCharacter” 类的私有成员变量;然后在 “BlasterCharacter.cpp” 的构造函数中创建头部组件对象,然后进行编译。

    /*** BlasterCharacter.h ***/// Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h"
    #include "GameFramework/Character.h"
    #include "BlasterCharacter.generated.h"UCLASS()
    class BLASTER_API ABlasterCharacter : public ACharacter
    {GENERATED_BODY()public:// Sets default values for this character's propertiesABlasterCharacter();// Called every framevirtual void Tick(float DeltaTime) override;// Called to bind functionality to inputvirtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;protected:// Called when the game starts or when spawnedvirtual void BeginPlay() override;// 与轴映射相对应的函数void MoveForward(float Value);	// 角色前进或后退void MoveRight(float Value);	// 角色左移或右移void Turn(float Value);			// 角色视角左转或右转void LookUp(float Value);		// 角色俯视或仰视private:UPROPERTY(VisibleAnywhere, Category = Camera)	class USpringArmComponent* CameraBoom;			// 添加弹簧臂组件,归类为 “Camera”UPROPERTY(VisibleAnywhere, Category = Camera)class UCameraComponent* FollowCamera;			// 添加摄像机组件,归类为 “Camera”/* P35 网络角色(Network Role)*/// BlueprintReadOnly:表示该变量只能在蓝图中进行读取操作,不能在蓝图中进行写入操作。常用于定义只读变量。// 我们不能在私有变量中使用关键字 BlueprintReadOnly和 BlueprintReadWrite,除非使用了 meta = (AllowPrivateAccess = "true") 进行指定// UE4中用于定义蓝图变量的元数据(metadata)的所有关键字及其解释和作用可以参见:https://blog.csdn.net/u013007305/article/details/130450354UPROPERTY(EditAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))	class UWidgetComponent* OverheadWidget;												// 添加头部组件/* P35 网络角色(Network Role)*/public:	};
    
    /*** BlasterCharacter.cpp ***/// Fill out your copyright notice in the Description page of Project Settings.#include "BlasterCharacter.h"
    #include "GameFramework/SpringArmComponent.h"
    #include "Camera/CameraComponent.h"
    #include "GameFramework/CharacterMovementComponent.h"/* P35 网络角色(Network Role)*/
    #include "Components/WidgetComponent.h"
    /* P35 网络角色(Network Role)*/// Sets default values
    ABlasterCharacter::ABlasterCharacter()
    {// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.PrimaryActorTick.bCanEverTick = true;// 创建弹簧臂对象 CameraBoom 并设置 CameraBoom 的默认属性CameraBoom = CreateDefaultSubobject<USpringArmComponent>(TEXT("CameraBoom"));	// 基于弹簧臂组件类创建对象CameraBoom->SetupAttachment(GetMesh());											// 设置弹簧臂附加到角色的骨骼网格体组件,如果附加到胶囊体上,角色在做蹲下的动作时,由于胶囊体的大小和路线会发生改变,弹簧臂的高度也会发生改变(弹簧臂将会移动)CameraBoom->TargetArmLength = 600.f;											// 设置弹簧臂长度CameraBoom->bUsePawnControlRotation = true;										// 设置弹簧臂跟随角色控制器旋转// 创建摄像机对象 FollowCamera 并设置 FollowCamera 的默认属性FollowCamera = CreateDefaultSubobject<UCameraComponent>(TEXT("FollowCamera"));	// 基于摄像机组件类创建对象FollowCamera->SetupAttachment(CameraBoom, USpringArmComponent::SocketName);		// 将摄像机附加到弹簧臂 CameraBoom 上,并指定插槽名为虚幻引擎摄像机组件成员变量 SocketNameFollowCamera->bUsePawnControlRotation = false;									// 设置摄像机不跟随角色控制器旋转// 调整弹簧臂和摄像机的相对位置(也可以在虚幻引擎的蓝图编辑器中进行设置)CameraBoom->SetRelativeLocation(FVector(0, 0, 88));								// 设置弹簧臂和摄像机在蓝图类 “BP_BlasterCharacter” 的相对位置为 (0, 0, 88),以避免它们与地面相撞bUseControllerRotationYaw = false;												// 设置人物不跟随控制器(镜头)转向,也可以在 BP_BlasterCharacter 蓝图编辑器中实现GetCharacterMovement()->bOrientRotationToMovement = true;						// 获取角色移动组件,角色移动时向加速度方向旋转角色,BP_BlasterCharacter 蓝图编辑器中实现/* P35 网络角色(Network Role)*/OverheadWidget = CreateDefaultSubobject<UWidgetComponent> (TEXT("OverheadWidget"));	// 基于头部组件类创建对象OverheadWidget->SetupAttachment(RootComponent);										// 将头部组件附加到人物根组件 RootComponent 上/* P35 网络角色(Network Role)*/
    }...
    
  2. 在虚幻引擎打开 “BP_BlasterCharacter” 蓝图编辑器,在左侧 “组件”(Component)面板中可以看到头部组件 “OverheadWidget” 已经附加到 “胶囊体组件 (CollisionCylinder)”(Capsule Component(CollisionCylinder)) ,点击它,并在右侧 “细节”(Details)面板 “用户界面”(USER INTERFACE)选项卡下将 “空间”(Space)从 “世界”(World) 设置为 “屏幕”(Screen),将 “控件类”(Widget Class)设置为 “WBP_OverheadWidget”,勾选 “以所需大小绘制”(Draw at Desired Size),这样我们就不必手动设置(Manually set)这个部件的大小。在这里插入图片描述

  3. 在左侧 “组件”(Component)面板中将 “OverheadWidget” 拖拽至 “事件图表”(Event Graph),然后按照下图连接蓝图节点,这段蓝图程序实现了在玩家角色的头顶显示网络角色的功能:节点 “获取用户控件对象”(Get User Widget Object)用于获取当前角色上的 “OverheadWidget” 组件,返回的是一个 “用户控件” (User Widget)类型的值;节点 “类型转换为 WBP_OverheadWidget”(Cast To WBP_OverheadWidget)将 “用户控件” (User Widget)类型转换为 “WBP_OverheadWidget” 类型,成功转换后,就可以调用 “WBP_OverheadWidget” 内部的函数 “Show PlayerNetRole()”;这个函数的 “目标”(Target) 是 OverheadWidget,输入参数 “In Pawn” 传入 “Self”(当前角色 “BlasterCharacter”)。
    在这里插入图片描述

  4. 点击上方 “视口”(Viewport) 选项卡,接着在左侧 “组件”(Component)面板中点击 “OverheadWidget”,然后在 “视口” 中将该组件拖拽移动至人物头顶。
    在这里插入图片描述

  5. 打开关卡 “BlasterMap”,在工具栏点击 “ ⋮ \vdots ”,这是 “修改游戏模式和游戏设置”(Change Play Mode and Play Settings) 的按钮, 修改 “玩家数量”(Number of Players) 为 3,“网络模式”(Net Mode)为 “以监听服务器”(Play as Listen Server),当我们进行测试时,其中一个玩家将作为监听服务器,而其他两个玩家为客户端。
    在这里插入图片描述

  6. 点击工具栏的 “播放”()按钮启动运行,可以看到视口面板中的玩家是监听服务器,弹出的两个窗口为客户端。视口面板中显示三个玩家的 本地 网络角色都是 “Authority”,因为在服务器上的人物都具有 本地 “Authority” 角色(权威角色 / 权威端);而能在其中一个客户端被控制的那个人物(下图红圈标注)具有 本地 “Autonomous Proxy” 角色(自主代理 / 主动端),其他不能被控制的人物具有 本地 “Simulated Proxy” 角色(模拟代理 / 模拟端)。但是,我们无法仅从本地角色中分辨出哪个人物是由服务器控制的,下面我们将尝试显示人物的 远程 网络角色。
    在这里插入图片描述


35.4 显示远程网络角色

  1. 返回 Visual Studio,打开 “OverheadWidget.cpp”,将函数 “ShowPlayerNetRole()” 中的变量名 “LocalRole” 改为 “RemoteRole”。
    /*** OverheadWidget.cpp ***/...void UOverheadWidget::ShowPlayerNetRole(APawn* InPawn)	// 展示本地玩家网络角色
    {// ENetRole LocalRole = InPawn->GetLocalRole();		// 获取本地玩家网络角色(本地网络角色会因调用它的机器不同)ENetRole RemoteRole = InPawn->GetRemoteRole();		// 获取远程玩家网络角色FString Role;/*switch (LocalRole)								// 根据 LocalRole 的值来给 Role 赋值{case ENetRole::ROLE_Authority:					// 本地玩家是 Authority (权威角色 / 权威端)Role = FString("Authority");break;										// 添加 Break 语句直接退出 switch 分支,后面的 case 语句将不再执行case ENetRole::ROLE_AutonomousProxy:			// 本地玩家是 Autonomous Proxy (权威角色 / 权威端)Role = FString("Autonomous Proxy");	break;					case ENetRole::ROLE_SimulatedProxy:				// 本地玩家是 Simulated Proxy(模拟代理 / 模拟端)Role = FString("Simulated Proxy");break;case ENetRole::ROLE_None:						// 本地玩家没有分配网络角色Role = FString("None");						break;default:break;}*/switch (RemoteRole)								// 根据 RemoteRole 的值来给 Role 赋值{case ENetRole::ROLE_Authority:					// 远程玩家是 Authority (权威角色 / 权威端)Role = FString("Authority");break;										// 添加 Break 语句直接退出 switch 分支,后面的 case 语句将不再执行case ENetRole::ROLE_AutonomousProxy:			// 远程玩家是 Autonomous Proxy (权威角色 / 权威端)Role = FString("Autonomous Proxy");break;case ENetRole::ROLE_SimulatedProxy:				// 远程玩家是 Simulated Proxy(模拟代理 / 模拟端)Role = FString("Simulated Proxy");break;case ENetRole::ROLE_None:						// 远程玩家没有分配网络角色Role = FString("None");break;default:break;}// FString LocalRoleString = FString::Printf(TEXT("Local Role: %s"), *Role);	// 打印网络角色以便进行调试// SetDisplayText(LocalRoleString);												// 设置文本块 DisplayText 显示的文本FString RemoteRoleString = FString::Printf(TEXT("Remote Role: %s"), *Role);		// 打印网络角色以便进行调试SetDisplayText(RemoteRoleString);												// 设置文本块 DisplayText 显示的文本			
    }...
    
  2. 点击工具栏的 “播放”()按钮启动运行,可以看到视口面板中的玩家是监听服务器,弹出的两个窗口为客户端。视口面板中能被控制的那个人物(下图红圈标注)具有 远程 “Autonomous Proxy” 角色(自主代理 / 主动端),其他不能被控制的人物具有 远程 “Simulated Proxy” 角色(模拟代理 / 模拟端);而在客户端中,所有人物无论可不可以被控制,远程 网络角色都是 “Authority”。由此我们就可以根据人物 本地远程 网络角色来判断玩家的机器属于服务器端还是客户端。
    在这里插入图片描述

    在 Actor 的复制过程中,有两个属性扮演了重要角色,分别是 Role 和 RemoteRole。
    有了这两个属性,您可以知道:

    • 谁拥有 actor 的主控权
    • actor 是否被复制
    • 复制模式

    首先一件要确定的事,就是谁拥有特定 actor 的主控权。要确定当前运行的引擎实例是否有主控者,需要查看 Role 属性是否为 ROLE_Authority。如果是,就表明这个运行中的 虚幻引擎 实例负责掌管此 actor(决定其是否被复制)。
    如果 Role 是 ROLE_Authority,RemoteRole 是 ROLE_SimulatedProxyROLE_AutonomousProxy就说明这个引擎实例负责将此 actor 复制到远程连接

    就目前而言,只有服务器能够向已连接的客户端同步 Actor (客户端永远都不能向服务器同步)。始终记住这一点, 只有 服务器 才能看到 Role == ROLE_AuthorityRemoteRole == ROLE_SimulatedProxy 或者 ROLE_AutonomousProxy


    Role/RemoteRole 对调
    对于不同的数值观察者,它们的 Role 和 RemoteRole 值可能发生对调。例如,如果您的服务器上有这样的配置:

    • Role == ROLE_Authority
    • RemoteRole == ROLE_SimulatedProxy

    客户端会将其识别为以下形式:

    • Role == ROLE_SimulatedProxy
    • RemoteRole == ROLE_Authority

    这种情况是正常的,因为服务器要负责掌管 actor 并将其复制到客户端。而客户端只是接收更新,并在更新的间歇模拟 actor


    复制模式
    服务器不会在每次更新时复制 actor。这会消耗太多的带宽和 CPU 资源。实际上,服务器会按照 AActor::NetUpdateFrequency 属性指定的频度来复制 actor。
    因此在 actor 更新的间歇,会有一些时间数据被传递到客户端。这会导致 actor 呈现出断续、不连贯的移动。为了弥补这个缺陷,客户端将在更新的间歇中模拟 actor。
    目前共有两种类型的模拟。

    • ROLE_SimulatedProxy
      这是标准的模拟途径,通常是根据上次获得的速率对移动进行推算。当服务器为特定的 actor 发送更新时,客户端将向着新的方位调整其位置,然后利用更新的间歇,根据由服务器发送的最近的速率值来继续移动 actor。
      使用上次获得的速率值进行模拟,只是普通模拟方式中的一种。您完全可以编写自己的定制代码,在服务器更新的间隔使用其他的一些信息来进行推算。
    • ROLE_AutonomousProxy
      这种模拟通常只用于 PlayerController 所拥有的 actor。这说明此 actor 会接收来自真人控制者的输入,所以在我们进行推算时,我们会有更多一些的信息,而且能使用真人输入内容来补足缺失的信息(而不是根据上次获得的速率来进行推算)。

    虚幻引擎官方文档 《Actor 的 Role 和 RemoteRole 属性》


35.5 Summary

本节课围绕虚幻引擎的网络角色展开,在多人游戏中,玩家控制的任何给定人物都有多个版本,如果多人游戏中有三个玩家,那么其中一个玩家将会在其他机器上有三个副本,为了解决如何区分我们正在处理的角色属于哪个版本,虚幻引擎引入了网络角色的概念以及相应的枚举变量“ENetRole”,它包含 “ENetRole::ROLE_Authority”“ENetRole::ROLE_SimulatedProxy”、“ENetRole::ROLE_AutonomousProxy”:以及 “ENetRole::ROLE_None” 四个常用的枚举常量。为了查看玩家人物在监听服务器和客户端上的本地网络角色和远程网络角色,我们创建 “OverheadWidget” 控件类,绑定 “UTextBlock” 文本组件,在玩家人物的蓝图类 “BlasterCharacter” 中将控件 “OverheadWidget” 附加至角色头顶,这样就可以动态显示网络角色。最后我们进行了多端测试验证,以监听服务器模式启动多玩家实例,进一步理解本地网络角色和远程网络角色在服务端与客户端的显示逻辑。
在这里插入图片描述
35.3 显示本地网路角色35.4 显示远程网路角色 中,我们在进行测试时,可以看到两种情况下监听服务器端和客户端出现了 本地网络角色和远程网络角色对调 的现象,对于不同的数值观察者,它们的 “(Local)Role” 和 “RemoteRole” 值可能发生对调,即如果服务器上有这样的配置 “(Local)Role == ROLE_Authority” 以及“RemoteRole == ROLE_SimulatedProxy”,客户端会将其识别为 “(Local)Role == ROLE_SimulatedProxy” 以及 “RemoteRole == ROLE_Authority”,这种情况是正常的,因为服务器要负责掌管 Actor 并将其复制到客户端。而客户端只是接收更新,并在更新的间歇模拟 Actor。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/897229.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

微信小程序引入vant-weapp组件教程

本章教程,介绍如何在微信小程序中引入vant-weapp。 vant-weapp文档:https://vant-ui.github.io/vant-weapp/#/button 一、新建一个小程序 二、npm初始化 npm init三、安装 Vant Weapp‘ npm i @vant/weapp -

C++ 作业 DAY5

作业 代码 Widtget.h class Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nullptr);~Widget();private:Ui::Widget *ui;/************************ 起始终止坐标 ************************/QPoint end;QPoint start;QVector<QPoint> per_start_lis…

【js逆向】iwencai国内某金融网站实战

地址&#xff1a;aHR0cHM6Ly93d3cuaXdlbmNhaS5jb20vdW5pZmllZHdhcC9ob21lL2luZGV4 在搜索框中随便输入关键词 查看请求标头&#xff0c;请求头中有一个特殊的 Hexin-V,它是加密过的&#xff1b;响应数据包中全是明文。搞清楚Hexin-V的值是怎么生成的&#xff0c;这个值和cooki…

使用Node.js从零搭建DeepSeek本地部署(Express框架、Ollama)

目录 1.安装Node.js和npm2.初始化项目3.安装Ollama4.下载DeepSeek模型5.创建Node.js服务器6.运行服务器7.Web UI对话-Chrome插件-Page Assist 1.安装Node.js和npm 首先确保我们机器上已经安装了Node.js和npm。如果未安装&#xff0c;可以通过以下链接下载并安装适合我们操作系…

BUUCTF——[GYCTF2020]FlaskApp1 SSTI模板注入/PIN学习

目录 一、网页功能探索 二、SSTI注入 三、方法一 四、方法二 使用PIN码 &#xff08;1&#xff09;服务器运行flask登录所需的用户名 &#xff08;2&#xff09;modename &#xff08;3&#xff09;flask库下app.py的绝对路径 &#xff08;4&#xff09;当前网络的mac地…

FPGA学习篇——Verilog学习3(关键字+注释方法+程序基本框架)

1 Verilog常用关键字 大概知道以下哪些是关键字就好&#xff0c;如何使用还是得在编写代码中来学习。 2 Verilog注释方法 Verilog有两种注释方式&#xff1a; 2.1 “ // ” 单行。 2.2 “ /* ... */ ” 可扩展多行。 3 Verilog程序基本框架 Verilog 的基本设计单元是“…

FPGA之USB通信实战:基于FX2芯片的Slave FIFO回环测试详解

FPGA之Usb数据传输 Usb 通信 你也许会有疑问&#xff0c;明明有这么多通信方式和数据传输&#xff08;SPI、I2C、UART、以太网&#xff09;为什么偏偏使用USB呢? 原因有很多&#xff0c;如下&#xff1a; 1. 高速数据传输能力 高带宽&#xff1a;USB接口提供了较高的数据传…

深入理解与配置 Nginx TCP 日志输出

一、背景介绍 在现代网络架构中&#xff0c;Nginx 作为一款高性能的 Web 服务器和反向代理服务器&#xff0c;广泛应用于各种场景。除了对 HTTP/HTTPS 协议的出色支持&#xff0c;Nginx 从 1.9.0 版本开始引入了对 TCP 和 UDP 协议的代理功能&#xff0c;这使得它在处理数据库…

selenium库

一、什么是selenium库&#xff1f; selenim是一个用于Web应用程序自动化测试工具&#xff0c;selenium测试直接运行在浏览器中 像真正的用户在操作一样&#xff0c;驱动浏览器执行特定的动作&#xff0c;如点击&#xff0c;下拉等操作 二、selenium在爬虫中的应用 获取动态…

十七、从0开始卷出一个新项目之瑞萨RZN2L定时器(GPT)+DMA生成PWM的运动控制

一、概述 嵌入式科普(34)通过对比看透DMA的本质 分享瑞萨RZN2L使用DMA生成PWM的运动控制的例程源码 rzn2l必要的外设资源&#xff1a; rzn2l拥有32-bit timer General PWM Timer (GPT) with 18 channels CPU、GPT最高频率400Mhz DMAC0 and DMAC1 8 channels 8 channels 还…

MR的环形缓冲区(底层)

MapReduce的大致流程&#xff1a; 1、HDFS读取数据&#xff1b; 2、按照规则进行分片&#xff0c;形成若干个spilt&#xff1b; 3、进行Map 4、打上分区标签&#xff08;patition&#xff09; 5、数据入环形缓冲区&#xff08;KVbuffer&#xff09; 6、原地排序&#xff…

解锁STM32外设:开启嵌入式开发新世界

✨✨✨这里是小韩学长yyds的BLOG(喜欢作者的点个关注吧) ✨✨✨想要了解更多内容可以访问我的主页 小韩学长yyds-CSDN博客 目录 探索 STM32 强大的外设家族 初窥门径&#xff1a;STM32 外设开发基础 开发方式与工具 外设配置基础步骤 深入剖析&#xff1a;常见外设应用实例…

大模型AI平台DeepSeek 眼中的SQL2API平台:QuickAPI、dbapi 和 Magic API 介绍与对比

目录 1 QuickAPI 介绍 2 dbapi 介绍 3 Magic API 介绍 4 简单对比 5 总结 统一数据服务平台是一种低代码的方式&#xff0c;实现一般是通过SQL能直接生成数据API&#xff0c;同时能对产生的数据API进行全生命周期的管理&#xff0c;典型的SQL2API的实现模式。 以下是针对…

人工智能之数学基础:对线性代数中逆矩阵的思考?

本文重点 逆矩阵是线性代数中的一个重要概念,它在线性方程组、矩阵方程、动态系统、密码学、经济学和金融学以及计算机图形学等领域都有广泛的应用。通过了解逆矩阵的定义、性质、计算方法和应用,我们可以更好地理解和应用线性代数知识,解决各种实际问题。 关于逆矩阵的思…

[傻瓜式教学]如何将MathType公式编辑器内嵌到WPS工具栏中

[傻瓜式教学]如何将MathType公式编辑器内嵌到WPS工具栏中 将MathType公式编辑器内嵌到WPS工具栏中 下载好所需文件 我用夸克网盘分享了「mathtype安装教程超简单易上手.zip」&#xff0c;点击链接即可保存。打开「夸克APP」 链接&#xff1a;https://pan.quark.cn/s/4726c684…

【Linux】线程同步与互斥

线程同步与互斥 一.线程互斥1.互斥相关概念2.互斥锁 Mutex3.互斥锁接口4.互斥锁实现原理5.互斥锁封装 二.线程同步1.同步相关概念2.条件变量 Condition Variable3.条件变量接口4.条件变量封装5.信号量 Semaphore6.信号量接口7.信号量封装8.生产者 - 消费者模型1.基于 Blocking …

URIError: URI malformed

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》、《前端求职突破计划》 &#x1f35a; 蓝桥云课签约作者、…

鸿蒙与DeepSeek深度整合:构建下一代智能操作系统生态

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 https://www.captainbed.cn/north 目录 技术融合背景与价值鸿蒙分布式架构解析DeepSeek技术体系剖析核心整合架构设计智能调度系统实现…

LeetCode - 28 找出字符串中第一个匹配项的下标

题目来源 28. 找出字符串中第一个匹配项的下标 - 力扣&#xff08;LeetCode&#xff09; 题目解析 暴力解法 本题如果采用暴力解法的话&#xff0c;可以定义两个指针 i&#xff0c;j&#xff0c;其中 i 指针用于扫描 S&#xff08;haystack&#xff09;串&#xff0c;j 指针…

0.大模型开发知识点需求综述

文章目录 一、机器学习与深度学习基础二、自然语言处理&#xff08;NLP&#xff09;基础三、大模型架构四、训练优化技术五、数据处理与预处理六、分布式训练与并行化策略七、微调方法与参数高效微调八、训练框架、工具与自动化流程九、评估与部署十、前沿技术与未来趋势 已更新…