下面方案的思路是:
每个Actor,为其定义一个代理(ActorProxy),真实的Actor放在服务端,代理ActorProxy放在客户端,移动Actor时,实际是移动服务端上的Actor,然后对客户端ActorProxy的位置进行同步。
摄像机绑定的是ActorProxy,服务端的真实Actor不用摄像机;而AIController实际控制的是服务端Actor,客户端其实没有AIController。
另外,下面例子的同步机制使用的UE4自身集成的Replication(也就是常用的UFUNCTION(Server, Reliable, WithValidation)),这个是非常耗费带宽的,因为是定时且频繁的同步数据。这个如果是做局域网服务端还可以,但是如果是大型高负载服务端,建议自己实现同步机制。另外,v4.4开始,shipping编译出来的版本会自动删掉UE4的server模式。原因肯定是官方也不希望这个方便测试的同步机制被开发者应用到生产环境中。所以下面例子中,参考下它的思路即可,具体的同步处理的细节问题可以忽略。
RTS游戏中多角色同时移动的方案:
为在每个自定义Pawn类定义个AIController,群体移动时,遍历所有Pawn,依次调用各自的AIController->MoveToLocation。
另外注意点:AIController->MoveToLocation无法像UNavigationSystem->SimpleMoveToLocation()那样可以主动绕开障碍物,一般建议在服务端通过UNavigationSystem获取移动路径的数据,然后将这些数据与客户端同步。
可参考:
Creating a Movement Component for an RTS in UE4
http://www.gamedev.net/page/resources/_/technical/game-programming/creating-a-movement-component-for-an-rts-in-ue4-r4019
=============================================================================
=============================================================================
下面文章有点不足的问题是:服务端是通过AIController->MoveToLocation来寻路的,如果对于一个有障碍物的地图来啊,移动的时候会出现停滞。另外文章建议说服务端不要用UNavigationSystem>SimpleMoveToLocation,这个可能是误解,服务端是可以使用的。
正文:
[UE4] Getting Multiplayer Player Pawn AI Navigation to work (C++)
http://droneah.com/content/ue4-getting-multiplayer-player-pawn-ai-navigation-work-c
Unreal Engine is an awesome piece of technology making it easy to do almost anything you might want.
When using the Top Down view however, there is a hurdle to get over when trying to get multiplayer to work. This is a C++ project solution to this problem based on a BluePrints solution.
The basic problem stems from the fact that
"SimpleMoveToLocation was never intended to be used in a network environment. It's simple after all ;) Currently there's no dedicated engine way of making player pawn follow a path. " (from the same page)
To be able to get a working version of SimpleMoveToLocation, we need to do the following:
Create a proxy player class (BP_WarriorProxy is BP solution)
Set the proxy class as the default player controller class
Move the camera to the proxy (Since the actual player class is on the server, we can't put a camera on it to display on the client)
The BP solution talks about four classes - our counterparts are as follows:
BP_WarriorProxy: ADemoPlayerProxy
BP_WarriorController: ADemoPlayerController (Auto-created when creating a c++ top down project)
BP_Warrior: ADemoCharacter (Auto-created when creating a C++ top down project)
BP_WarriorAI: AAIController - we have no reason to subclass it.
So, from a standard c++ top down project, the only class we need to add is the ADemoPlayerProxy - so go ahead and do that first.
The first thing we'll do is rewire the ADemoGameMode class to initialise the proxy class instead of the default MyCharacter Blueprint.
ADemoGameMode::ADemoGameMode(const class FPostConstructInitializeProperties& PCIP) : Super(PCIP) { // use our custom PlayerController class PlayerControllerClass = ADemoPlayerController::StaticClass(); // set default pawn class to our Blueprinted character /* static ConstructorHelpers::FClassFinder<apawn> PlayerPawnBPClass(TEXT("/Game/Blueprints/MyCharacter")); if (PlayerPawnBPClass.Class != NULL) { DefaultPawnClass = PlayerPawnBPClass.Class; }*/ DefaultPawnClass = ADemoPlayerProxy::StaticClass(); }
Our Player Proxy class declaration
/* This class will work as a proxy on the client - tracking the movements of the * real Character on the server side and sending back controls. */ UCLASS() class Demo_API ADemoPlayerProxy : public APawn { GENERATED_UCLASS_BODY() /** Top down camera */ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera) TSubobjectPtr<class ucameracomponent> TopDownCameraComponent; /** Camera boom positioning the camera above the character */ UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = Camera) TSubobjectPtr<class uspringarmcomponent> CameraBoom; // Needed so we can pick up the class in the constructor and spawn it elsewhere TSubclassOf<aactor> CharacterClass; // Pointer to the actual character. We replicate it so we know its location for the camera on the client UPROPERTY(Replicated) ADemoCharacter* Character; // The AI Controller we will use to auto-navigate the player AAIController* PlayerAI; // We spawn the real player character and other such elements here virtual void BeginPlay() override; // Use do keep this actor in sync with the real one void Tick(float DeltaTime); // Used by the controller to get moving to work void MoveToLocation(const FVector& vector); };
and the definition:
#include "Demo.h" #include "DemoCharacter.h" #include "AIController.h" #include "DemoPlayerProxy.h" #include "UnrealNetwork.h" ADemoPlayerProxy::ADemoPlayerProxy(const class FPostConstructInitializeProperties& PCIP) : Super(PCIP) { // Don't rotate character to camera direction bUseControllerRotationPitch = false; bUseControllerRotationYaw = false; bUseControllerRotationRoll = false; // It seems that without a RootComponent, we can't place the Actual Character easily TSubobjectPtr<UCapsuleComponent> TouchCapsule = PCIP.CreateDefaultSubobject<ucapsulecomponent>(this, TEXT("dummy")); TouchCapsule->InitCapsuleSize(1.0f, 1.0f); TouchCapsule->SetCollisionEnabled(ECollisionEnabled::NoCollision); TouchCapsule->SetCollisionResponseToAllChannels(ECR_Ignore); RootComponent = TouchCapsule; // Create a camera boom... CameraBoom = PCIP.CreateDefaultSubobject<USpringArmComponent>(this, TEXT("CameraBoom")); CameraBoom->AttachTo(RootComponent); CameraBoom->bAbsoluteRotation = true; // Don't want arm to rotate when character does CameraBoom->TargetArmLength = 800.f; CameraBoom->RelativeRotation = FRotator(-60.f, 0.f, 0.f); CameraBoom->bDoCollisionTest = false; // Don't want to pull camera in when it collides with level // Create a camera... TopDownCameraComponent = PCIP.CreateDefaultSubobject<UCameraComponent>(this, TEXT("TopDownCamera")); TopDownCameraComponent->AttachTo(CameraBoom, USpringArmComponent::SocketName); TopDownCameraComponent->bUseControllerViewRotation = false; // Camera does not rotate relative to arm if (Role == ROLE_Authority) { static ConstructorHelpers::FObjectFinder<UClass> PlayerPawnBPClass(TEXT("/Game/Blueprints/MyCharacter.MyCharacter_C")); CharacterClass = PlayerPawnBPClass.Object; } } void ADemoPlayerProxy::BeginPlay() { Super::BeginPlay(); if (Role == ROLE_Authority) { // Get current location of the Player Proxy FVector Location = GetActorLocation(); FRotator Rotation = GetActorRotation(); FActorSpawnParameters SpawnParams; SpawnParams.Owner = this; SpawnParams.Instigator = Instigator; SpawnParams.bNoCollisionFail = true; // Spawn the actual player character at the same location as the Proxy Character = Cast<ADemoCharacter>(GetWorld()->SpawnActor(CharacterClass, &Location, &Rotation, SpawnParams)); // We use the PlayerAI to control the Player Character for Navigation PlayerAI = GetWorld()->SpawnActor<AAIController>(GetActorLocation(), GetActorRotation()); PlayerAI->Possess(Character); } } void ADemoPlayerProxy::Tick(float DeltaTime) { Super::Tick(DeltaTime); if (Character) { // Keep the Proxy in sync with the real character FTransform CharTransform = Character->GetTransform(); FTransform MyTransform = GetTransform(); FTransform Transform; Transform.LerpTranslationScale3D(CharTransform, MyTransform, ScalarRegister(0.5f)); SetActorTransform(Transform); } } void ADemoPlayerProxy::MoveToLocation(const FVector& DestLocation) { /** Looks easy - doesn't it. * What this does is to engage the AI to pathfind. * The AI will then "route" the character correctly. * The Proxy (and with it the camera), on each tick, moves to the location of the real character * * And thus, we get the illusion of moving with the Player Character */ PlayerAI->MoveToLocation(DestLocation); } void ADemoPlayerProxy::GetLifetimeReplicatedProps(TArray< class FLifetimeProperty > & OutLifetimeProps) const { Super::GetLifetimeReplicatedProps(OutLifetimeProps); // Replicate to Everyone DOREPLIFETIME(ADemoPlayerProxy, Character); }
We'll now cover changes to the Player Controller. We'll rewire it here to ask the proxy to move, which will in turn ask the AIController to find a path and move the real player character.
This involves changing the SetMoveDestination method to call a server side method to move the character. When the character moves, the player Proxy will automatically mirror the movements.
In the header file, add the following function
/** Navigate player to the given world location (Server Version) */ UFUNCTION(reliable, server, WithValidation) void ServerSetNewMoveDestination(const FVector DestLocation);
Now let's implement it (DemoPlayerController.cpp):
bool ADemoPlayerController::ServerSetNewMoveDestination_Validate(const FVector DestLocation) { return true; } /* Actual implementation of the ServerSetMoveDestination method */ void ADemoPlayerController::ServerSetNewMoveDestination_Implementation(const FVector DestLocation) { ADemoPlayerProxy* Pawn = Cast<ademoplayerproxy>(GetPawn()); if (Pawn) { UNavigationSystem* const NaDemoys = GetWorld()->GetNavigationSystem(); float const Distance = FVector::Dist(DestLocation, Pawn->GetActorLocation()); // We need to issue move command only if far enough in order for walk animation to play correctly if (NaDemoys && (Distance > 120.0f)) { //NaDemoys->SimpleMoveToLocation(this, DestLocation); Pawn->MoveToLocation(DestLocation); } } }
And finally, the rewiring of the original method:
void ADemoPlayerController::SetNewMoveDestination(const FVector DestLocation) { ServerSetNewMoveDestination(DestLocation); }
Finally, in terms of the character class, the only change is really to remove the camera components that we moved to the Player Proxy which I shall leave to you :-p
相关推荐
在UE4(Unreal Engine 4)中,游戏对象的行为和交互主要通过C++或蓝图实现。本笔记将深入探讨如何使用C++编程语言来控制球体的运动,并结合移动粒子效果来提升游戏视觉体验。以下是你需要了解的关键知识点: 1. **...
Pawn在UE4中代表游戏中的角色或实体,它可以是玩家角色、NPC或者其他移动的物体。在跑酷游戏中,Pawn通常作为主角的控制对象,负责处理移动、跳跃、攀爬等各种动作。在模板中,Pawn的设置和行为逻辑是关键,它会...
(平面游戏)此图文能教你让Actor向着鼠标(看向鼠标)的位置旋转,一般以游戏人物为中心(但都不影响),看之前先学习基础UE4教程,应用于人物Pawn蓝图中。
Pawn在UE4中扮演着角色或者玩家控制器的角色,它可以是游戏世界中的主角,允许玩家进行移动、交互等操作。 首先,我们来看`MyPawn.h`头文件。在这个文件中,我们将定义自定义的Pawn类。通常,我们会继承自UE4的`...
3. **游戏对象和组件**:在UE4中,游戏世界由各种对象组成,如Actor、Pawn、Character等。每个对象都可包含多个组件,如StaticMeshComponent(静态网格组件)、SkeletalMeshComponent(骨骼网格组件)等。这些组件...
UE4蓝图教程 UE4蓝图是一种特殊资源,提供直观、基于节点的接口,用于创建新类型的actor和脚本关卡事件。设计师和程序员可以使用蓝图在虚幻编辑器中快速创建并迭代gameplay,不需要进行代码编写。 一、蓝图命名...
7. **网络同步**:学习如何在蓝图中实现多人游戏功能,处理网络同步和数据通信。 8. **AI行为树**:探讨如何使用行为树来设计NPC的智能行为,包括路径规划、决策逻辑等。 9. **蓝图可视化脚本**:深入理解蓝图的...
在 UE4 中,Pawn 是一个基本的 Actor,它可以被控制。我们可以新建一个 Pawn,并将其命名为 Camera。这样,我们就可以控制摄像机的行为。 添加摄像机 在 Viewport 中,我们可以添加一个摄像机。我们可以在蓝图类 ...
4. **空间定位**:利用VR设备提供的空间定位信息,更新角色的位置和朝向,确保用户在虚拟世界中的体验与实际动作同步。 5. **碰撞检测**:设置碰撞组件,确保VR角色与场景中的其他对象正确交互,避免穿模问题。 6....
虚幻4(Unreal Engine 4,简称UE4)是一款由Epic Games开发的强大游戏引擎,广泛应用于游戏开发、影视制作、虚拟现实等领域。这款引擎以其高性能、灵活的蓝图系统和丰富的工具集著称,同时也支持C++编程,使得开发者...
Pawn是一个强大的物品评估系统,它允许玩家根据自己的角色属性、职业需求以及个人喜好来定制评分规则,以便更有效地管理和优化角色的装备。 Pawn的核心特性在于其自定义评分机制。通过这个插件,玩家可以为每种物品...
在UE5中切换多个固定摄像机视角是一项基本但至关重要的技能,它允许开发者和设计师在场景中自由移动视线,模拟不同的观察角度,从而提供更加丰富的用户体验。本文将深入探讨如何在UE5中实现这一功能。 首先,我们...
实现了AI机器人自动巡逻,发现敌人互相通知并追赶攻击,受伤自动躲避,失去敌人恢复巡逻等简单AI。
实现UE GlobePawn 功能所需的文件
UE4提供了NetDriver和Replication等机制,用于在网络环境中同步游戏状态。 8. **优化与发布** 在游戏开发的后期,优化性能和准备发布是必不可少的步骤。这包括内存管理、GPU性能分析、打包和发布设置等。UE4的...
总的来说,这个文档涵盖了UE4中C++和蓝图的结合使用,以及角色控制、游戏模式定制、输入绑定、角色移动和旋转、NPC创建等多个核心知识点。通过这样的实践,开发者可以更深入地理解和掌握UE4的游戏开发流程。
该项目封装了UE4中具有多人能力的赛车游戏的基础知识,该游戏使用基于子步的自定义射线广播物理学的棋子。 特征: 单人竞赛模式 多人竞赛模式(具有客户端物理功能的听服务器) 基本主菜单/暂停菜单/服务器浏览器...
MMT_Content, 机械建模工具包是UE4的一个插件 MMT_Content机械建模工具包是UE4的一个插件。 这个插件提供了一些基本的方法来在图纸中添加自定义物理代码。 这个库包含了插件和内容示例,它是如何使用它的。这个插件...
《Unreal Engine 4 AI 编程精要》是一本专注于在Unreal Engine 4(UE4)游戏引擎中实现人工智能(AI)编程的教程书籍。本书内容全面,适合初学者和进阶开发者,提供了大量示例和代码,帮助读者快速掌握如何在UE4中...