演示效果,模拟专用服务器多人游戏时候的同步场景,本文只作功能解析:
本人郑重说明,Unreal是可以做任何类型游戏的,门槛非常高
呀哈哈,就这么一个点击移动的功能,如果放在Unity中几句代码,放在Unreal中可是非常的复杂,因为
这个行为在Unreal中认为是一个AI行为,点击了地面,然后自动移动过去,没有玩家的参与,这不就是AI行为?
Unreal是这么认识的,所以这个行为会经过Unreal中AI、检测、导航、移动、目标、及Network;
涉及到的功能点包括:AI、检测、导航、移动、目标、及Network
#2 AI部分说明
点击地面进行移动需要有AI的功能,我们操作的角色不是AI,Unreal中的角色可以携带一个AI功能;不需要这个角色本身就是一个AI的主体,每个Character的子类都有AIController的功能,默认情况下会生成一个
如果制作AI也会用到,这里的AI表示自动化,并不真正的要表示是一个AI。
#3 当前使用到检测功能
点击需要检测鼠标点击到地面的位置信息,有两种方式,一般使用第二种,效率高
转换的地方,这个源码资料很少有的
// Trace to see what is under the mouse cursor
FHitResult Hit;
GetHitResultUnderCursor(ECC_Visibility, false, Hit);
GetHitResultUnderCursorByChannel(UEngineTypes::ConvertToTraceType(ECC_Camera), true, Hit);
#4 Unreal中的导航功能
Unreal的导航功能咋一看好像非常的强大,应该是非常的强大,在AI的移动的时候可以看到;
在Project Settings
中也有NavMeshSystem相关的设置,
MoveToLocation(const FVector& Dest, float AcceptanceRadius, bool bStopOnOverlap, bool bUsePathfinding, bool bProjectDestinationToNavigation, bool bCanStrafe, TSubclassOf<UNavigationQueryFilter> FilterClass, bool bAllowPartialPaths
#5 导航移动
如果是使用默认的AIController,可以使用这个简单的移动功能,更加高级的移动功能需要自定义AIController,在
AIController中进行功能封装;
UAIBlueprintHelperLibrary::SimpleMoveToLocation(this, DestLocation);
AAIController* AIC = UAIBlueprintHelperLibrary::GetAIController(SelectedCharacter);
if (AIC)
{
AHowToCTM_AIController* AICC = Cast<AHowToCTM_AIController>(AIC);
if (AICC)
{
AICC->MoveUnit(Location);
//Ctm_AIController->MoveUnit();
}
}
#6 点击的目标
Unreal的NavMesh如果在多人Online的情况,是在服务器端才有控制权的,客户端默认不会开启控制权,需要手动开启客户端的NavMesh导航寻路功能,见tag1标记处
#7 Uneral中的RPC Network
Unreal的多人游戏制作,如果使用UnrealNetwork.h
;则会进入混合开发,客户端也是服务器,服务器也是客户端;非常方便的进行网络游戏的开发及调试;Unreal使用的SC网络,Server对所有客户端都进行同步;
客户端默认情况下只有Owning
权限,需要告诉服务器那些事情需要让其他客户端知道;
点击移动中,客户端需要让服务器知道,我请求移动,其中:
private:
UFUNCTION(Server, Unreliable)
void Server_NewDestination(const FVector DestLocation);
void Server_NewDestination_Implementation(const FVector DestLocation);
一般情况下使用Unreliable,差不多理解为UDP网络把,Reliable相当于TCP;如果使用Reliable,数据是需要经过一次确认的,然后再执行,使用Unreliable是数据是不需要经过确认的,各有优劣。
放个工具方法,方便分屏调试使用
FString GetPIENetModeNamePrefix(UWorld* WorldContext)
{
FString NameType("");
if (WorldContext)
{
if (WorldContext->WorldType == EWorldType::PIE)
{
switch (WorldContext->GetNetMode())
{
case NM_Standalone:
NameType = TEXT("Standalone:");
break;
case NM_DedicatedServer:
NameType = TEXT("DedicatedServer:");
break;
case NM_ListenServer:
NameType = TEXT("ListenServer:");
break;
case NM_Client:
NameType = FString::Printf(TEXT("Client %d:"), GPlayInEditorID - 1);
break;
case NM_MAX:
NameType = TEXT("MAX:");
break;
default: ;
}
}
}
return NameType;
}