UE4 RPC在C++中的使用简例

            UE4在网络发面主要通过两种方式来同步,一种是属性复制,另外一种就是RPC。 下面先是我从官网整理的文档,可以先读一遍做基础了解,如果不是很懂,可以接着看完第二部分,再回来看第一部分。

(1)官网RPC介绍

 使用 RPC

 

要将一个函数声明为 RPC,您只需将 ServerClientNetMulticast 关键字添加到UFUNCTION 声明。

例如,若要将某个函数声明为一个要在服务器上调用、但需要在客户端上执行的 RPC,您可以这样做:

UFUNCTION( Client );
void ClientRPCFunction();

要将某个函数声明为一个要在客户端上调用、但需要在服务器上执行的 RPC,您可以采取类似的方法,但需要使用 Server 关键字:

UFUNCTION( Server );
void ServerRPCFunction();

此外,还有一种叫做多播(Multicast)的特殊类型的 RPC 函数。多播 RPC 可以从服务器调用,然后在服务器和当前连接的所有客户端上执行。 要声明一个多播函数,您只需使用NetMulticast 关键字:

UFUNCTION( NetMulticast );
void MulticastRPCFunction();

多播 RPC 还可以从客户端调用,但这时就只能在本地执行。

快速提示

注意我们是如何在函数的开头预置 ClientServerMulticast 关键字的。这是我们在内部所做的一个约定,用来告诉程序员所用的函数将分别在客户端、服务器或所有客户端上调用。

它有一个非常重要的作用,就是事先确定该函数将在多人游戏会话期间被哪些机器调用。

要求和注意事项

您必须满足一些要求才能充分发挥 RPC 的作用:

  1. 它们必须从 Actor 上调用。

  2. Actor 必须被复制。

  3. 如果 RPC 是从服务器调用并在客户端上执行,则只有实际拥有这个 Actor 的客户端才会执行函数。

  4. 如果 RPC 是从客户端调用并在服务器上执行,客户端就必须拥有调用 RPC 的 Actor。

  5. 多播 RPC 则是个例外:

    • 如果它们是从服务器调用,服务器将在本地和所有已连接的客户端上执行它们。

    • 如果它们是从客户端调用,则只在本地而非服务器上执行。

    • 现在,我们有了一个简单的多播事件限制机制:在特定 Actor 的网络更新期内,多播函数将不会复制两次以上。按长期计划,我们会对此进行改善,同时更好的支持跨通道流量管理与限制。

        上面注意事项十分重要,如果没有看懂就多看几遍,今天没懂,就改天再看。

下面的表格根据执行调用的 actor 的所有权(最左边的一列),总结了特定类型的 RPC 将在哪里执行。

 

从服务器调用的 RPC

 

Actor 所有权

未复制

NetMulticast

Server

Client

Client-owned actor

在服务器上运行

在服务器和所有客户端上运行

在服务器上运行

在 actor 的所属客户端上运行

Server-owned actor

在服务器上运行

在服务器和所有客户端上运行

在服务器上运行

在服务器上运行

Unowned actor

在服务器上运行

在服务器和所有客户端上运行

在服务器上运行

在服务器上运行

从客户端调用的 RPC

 

Actor 所有权

未复制

NetMulticast

Server

Client

Owned by invoking client

在执行调用的客户端上运行

在执行调用的客户端上运行

在服务器上运行

在执行调用的客户端上运行

Owned by a different client

在执行调用的客户端上运行

在执行调用的客户端上运行

丢弃

在执行调用的客户端上运行

Server-owned actor

在执行调用的客户端上运行

在执行调用的客户端上运行

丢弃

在执行调用的客户端上运行

Unowned actor

在执行调用的客户端上运行

在执行调用的客户端上运行

丢弃

在执行调用的客户端上运行

 

 

(2)一个简单的例子

   .h头文件

 

    void CheckInterval();

    UFUNCTION(BlueprintCallable, Category = "Fire")
    bool CanFire() const;

    //Fire
    UFUNCTION(BlueprintCallable, Category = "Fire")
    void Fire();

    UFUNCTION(Server, Reliable, WithValidation, Category = "Fire")
    void FireServer();

    UFUNCTION(NetMulticast, Reliable, Category = "Fire")
    void FireNetwork(FRotator FireRotator);

    //Fire Event
    UFUNCTION(BlueprintImplementableEvent, Category = "Fire")
    void FireEvent();

   //最后开火时间
    UPROPERTY( BlueprintReadWrite, Category = "CurrentState")
    float LastFireTime;

    //判断是否间隔结束,每发弹药的间隔
    UPROPERTY(Replicated, BlueprintReadWrite, Category = "CurrentState")
    bool bIntervalFinish;

    // Bullet Current Num in the Clip
    UPROPERTY(Replicated, BlueprintReadWrite, Category = "CurrentState")
    int32 CurClipBullet;
//本文为CSDN博主执手画眉弯原创,未经允许不得转载!

    .cpp 文件

//tick中检测冷却,只在服务器进行
void AAICharacterBase::CheckInterval()
{
    if (HasAuthority() == true && State == EAICharacterState::Normal && CurClipBullet > 0)
    {
        if (LastFireTime + FireInterval < UKismetSystemLibrary::GetGameTimeInSeconds(GetWorld()))
            bIntervalFinish = true;
    }
}

//是否可以开火判定
bool AAICharacterBase::CanFire() const
{
    if (State != EAICharacterState::Normal)
        return false;
    //弹夹中没有子弹
    if (CurClipBullet <= 0)
        return false;

    //是否冷却完毕,该值为Replicated值,由服务器在tick中判定,然后复制给客户端
    return bIntervalFinish;
}

//Fire
void AAICharacterBase::Fire()
{
    //如果可以开火,则调用服务器的开火事件
    if (CanFire())
        FireServer();
}
//本文为CSDN博主执手画眉弯原创,未经允许不得转载!
//开火的服务器事件
void AAICharacterBase::FireServer_Implementation()
{
    //只在服务器端记录上次开火事件
    LastFireTime = UKismetSystemLibrary::GetGameTimeInSeconds(GetWorld());
    bIntervalFinish = false;
    if (CurClipBullet > MaxClipBullet)
        CurClipBullet = MaxClipBullet;
    if (CurClipBullet > 0)
        CurClipBullet--;
    FRotator FireRotator = GetActorRotation();
    //子弹扩散
    FireRotator.Yaw += UKismetMathLibrary::RandomFloatInRange(-1.f * DiffuseRadian, DiffuseRadian);
    FireRotator.Pitch += UKismetMathLibrary::RandomFloatInRange(-1.f * DiffuseRadian, DiffuseRadian);
    //调用开火的多播事件
    FireNetwork(FireRotator);
}

//服务器事件的验证,返回false时候,服务器会把该客户端踢掉
bool AAICharacterBase::FireServer_Validate()
{
    return true;
}

//开火的多播事件
void AAICharacterBase::FireNetwork_Implementation(FRotator FireRotator)
{
    if (State != EAICharacterState::Normal)
        return;

    //判定该端是否为专用服,专用服不用表现音效和特效等
    if (UKismetSystemLibrary::IsDedicatedServer(GetWorld()) == false)
    {
        FireParticle->SetActive(true, true);
        FireParticle->SetWorldRotation(FireRotator, false);
    }

    //产生子弹
    if (ProjectileClass)
    {
        FActorSpawnParameters SpawnParams;
        SpawnParams.Owner = this;
        SpawnParams.Instigator = Instigator;
        FVector FireLocation = FireParticle->K2_GetComponentLocation();
        AProjectileActor* Projectile = GetWorld()->SpawnActor<AProjectileActor>(ProjectileClass, FireLocation, FireRotator, SpawnParams);

        //调用到蓝图,在蓝图中处理一部分开火时候的事件
        FireEvent();
    }
}

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值