虚幻引擎5 Gameplay框架(三)

Gameplay重要类及重要功能使用方法(二)

Actor的碰撞检测事件

  • 做个火的碰撞检测实验,创建一个Actor添加上火的粒子,开启碰撞事件,当玩家进入到碰撞范围就打印名字,离开范围也打印
  • 新建一个Actor作为实验目标
    在这里插入图片描述
  • 声明场景组件作为根组件使用,碰撞胶囊组件,粒子组件,碰撞处理函数作为碰撞事件的绑定函数使用
  • 触发器中提供了两个与一个绑定宏用来判断组件是否被重叠,这两个函数是要参与UE反射系统的,我们需要使用UFUNCTION进行标明,UFUNCTION里面可以什么参数不要,我们称这个操作为多播委托
    • OnComponentBeginOverlap:事件,当某些内容开始与此组件重叠时调用,例如玩家进入触发器。
    • OnComponentEndOverlap:事件,当组件停止重叠时调用
  • AddDynamic宏:这个宏需要传入一个绑定函数,绑定函数参数格式如下UE源码,因为UE是把参数与参数类型都当做参数了,所以我们需要把逗号删除 。
    在这里插入图片描述
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "FireItem.generated.h"

UCLASS()
class GAMEPLAYCODEPARSING_API AFireItem : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	AFireItem();

	//场景组件作为根组件使用
	UPROPERTY(EditAnywhere,BlueprintReadWrite,Category = "Components")
	class USceneComponent* SceneComponent;
	//碰撞胶囊组件
	UPROPERTY(EditAnywhere,BlueprintReadWrite,Category = "Components")
	class USphereComponent* SphereCollisionComponent;
	//粒子组件
	UPROPERTY(EditAnywhere,BlueprintReadWrite,Category="Components")
	class UParticleSystemComponent* ParticleComponent;

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	//碰撞处理函数
	UFUNCTION()
	void BeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
	UFUNCTION()
	void EndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
};

  • 做一下限定只要在独立服务器的时候才会执行碰撞事件
// Fill out your copyright notice in the Description page of Project Settings.

#include "FireItem.h"
#include "Components/SphereComponent.h"
#include "Particles/ParticleSystemComponent.h"
#include "Utils/BFL_LogManager.h"

DEFINE_LOG_CATEGORY_STATIC(MyLog_Collision, Log, All);

// Sets default values
AFireItem::AFireItem()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	//让屏幕组件成为根组件
	SceneComponent = CreateDefaultSubobject<USceneComponent>(TEXT("SceneComponent"));
	SceneComponent->SetupAttachment(GetRootComponent());

	SphereCollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereCollisionComponent"));
	SphereCollisionComponent->SetupAttachment(SceneComponent);
	SphereCollisionComponent->SetSphereRadius(150.);

	ParticleComponent = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("ParticleComponent"));
	ParticleComponent->SetupAttachment(SceneComponent);
}

// Called when the game starts or when spawned
void AFireItem::BeginPlay()
{
	Super::BeginPlay();
	//做限定,碰撞事件只在服务器上运行
	if (GetNetMode() == ENetMode::NM_DedicatedServer)
	{
		SphereCollisionComponent->OnComponentBeginOverlap.AddDynamic(this, &AFireItem::BeginOverlap);
		SphereCollisionComponent->OnComponentEndOverlap.AddDynamic(this, &AFireItem::EndOverlap);
	}
}

// Called every frame
void AFireItem::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

void AFireItem::BeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, 
	int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	if (OtherActor)
	{
		UE_LOG(MyLog_Collision, Log, TEXT("__StartPlayerMode__%s"), *UBFL_LogManager::GetNetModeStr(this));
	}
	
}

void AFireItem::EndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
	UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
	if (OtherActor)
	{
		UE_LOG(MyLog_Collision, Log, TEXT("__EndPlayerMode__%s"), *UBFL_LogManager::GetNetModeStr(this));
	}
}
  • 因为做了限定所以要在独立服务器模式下运行,用客户端模式也可以,因为后台会自动帮我们启动一个独立服务器。运行结果:
    在这里插入图片描述
    在这里插入图片描述

Actor的属性同步

玩家属性注册同步

  • 玩家扣血事件是在服务器中进行的,然后服务器要把扣血的数据传递到客户端,客户端只是用接收服务器的消息
  • 新建一个PlayerState类,这个类是专门用来存放玩家属性
//注册同步属性
virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
  • TArray<FLifetimeProperty>& OutLifetimeProps参数是一个引用,用来存储那些需要被网络复制的属性信息
	//属性同步,单独的服务器把要改变的属性传给客户端
	UPROPERTY(Replicated)
	float MaxHP =100;

UPROPERTY(Replicated): 
这个宏标记了MaxHP变量需要在网络中进行复制。
这意味着每当服务器上的MaxHP值发生变化时,该变化会自动广播给所有连接的客户端,
确保所有客户端上的MaxHP值与服务器保持一致。这里还直接初始化MaxHP为100//属性通知,即传递了数据还可以写个函数执行行为
	UPROPERTY(ReplicatedUsing = "OnRep_ChangeCurHP")
	float CurHP = MaxHP;

UPROPERTY(ReplicatedUsing = "OnRep_ChangeCurHP"): 
这个宏不仅指示了CurHP属性需要在网络间复制,而且还指定了一个名为OnRep_ChangeCurHP的函数来处理属性复制之后的动作。
这意味着每当服务器上的CurHP值发生改变并同步到客户端时,还会自动调用OnRep_ChangeCurHP函数。
这个函数允许开发者在属性更新后执行额外的逻辑或行为,比如播放动画、触发UI更新等。
  • 注册同步属性
    在这里插入图片描述
  • 进行同步
    在这里插入图片描述

玩家扣血逻辑

  • 在玩家类中新建两个函数用于扣血与走出火的碰撞圈结束扣血,思路就是使用定时器进行扣血,当出了碰撞圈就关闭计数器
    在这里插入图片描述
    在这里插入图片描述
  • 然后在火的Actor类中碰撞事件上调用该函数
    在这里插入图片描述
  • 最后将PlayerState注册到GameMode
    在这里插入图片描述
  • 运行结果
    在这里插入图片描述

AInfo与APlayerState简介

  • 探索APlayerState源码类
  • PlayerState是为服务器上的每个玩家创建的(或在独立游戏中)PlayerStates被复制到所有客户端,并包含有关玩家的网络游戏相关信息,如玩家名,分数等。
    在这里插入图片描述
  • APlayerState是继承自AInfo
  • AInfo是所有保存信息保存类的根类,没有任何碰撞移动类型的代码
  • InfoActor的基类,它并不意味着在世界中具有物理表示,主要使用于“管理器”类型的类,该类保存有关世界的设置数据,但出于复制目的可能需要是Actor。在这里插入图片描述
  • Info类中基本是没什么代码的吗,它本身也是个抽象类
  • WITH_EDITOR的意思是在编译器编译的时候才有用,否则就没用,打包的话这些代码都无效了
    在这里插入图片描述
  • Info派生的类:这些的共性只做为数据的管理类,有没有被实体化不重要。
    在这里插入图片描述
  • PlayerState类中比较重要的函数CopyProperties:复制需要保存在非活动PlayerState中的属性
    在这里插入图片描述
  • CopyProperties:用与切换关卡的时候进行数据交换
    • 因为切换关卡会创建一个新的PlayerState
    • 一个关卡对应一个GameModePlayerState是注册在GameMode中的,当关卡切换了PlayerState也会有新的,为了切换关卡数据不丢失所以就存在这个函数了
      在这里插入图片描述

Actor的网络同步

  • 做一个小功能当人物在火焰上扣血时生成一个Actor方块
  • 在角色类中添加一个生成Actor的函数承接生成Actor的模版类
    在这里插入图片描述
  • 生成Actor函数逻辑
    在这里插入图片描述
  • 在开始掉血的时候就调用这个函数
    在这里插入图片描述
  • 然后在虚幻引擎中新建一个蓝图,将模型添加上,打开复制到远程机
    在这里插入图片描述
  • 在角色蓝图中添加生成的Actor
    在这里插入图片描述
  • 运行结果
    在这里插入图片描述
  • 在运行过程中是可以选择进入到别的世界的
    在这里插入图片描述

RPC函数_Client_服务器调用客户端执行

  • RPC函数
    • UFUNCTION(Reliable,Client):服务器调用,仅在自己客户端执行。<RunOnOwningClient>
    • UFUNCTION(Reliable,Server):客户端调用,服务器执行。<血量控制>
    • UFUNCTION(Reliable,NetMulticast):服务器调用,服务器和所有客户端执行
  • 制作一个功能来讲解Client服务器调用客户端执行函数
    • 实现在客户端的UI面板上显示之前生成的Actor的有多少个
  • 在角色类中新建一个客户端RPC函数与一个计数的变量
    在这里插入图片描述
  • 写入逻辑
    在这里插入图片描述
  • 在角色信息UI里面添加一个文本UI组件用于显示,记得将这些组件的作用域标为公有
    在这里插入图片描述
  • 绑定好这个新建的这个UI组件
    在这里插入图片描述
  • 新建两个玩家进行测试
    在这里插入图片描述
  • 运行结果,验证UFUNCTION(Reliable,Client):服务器调用,仅在自己客户端执行在这里插入图片描述

RPC函数_Server_客户端调用服务器执行

  • 制作一个功能来讲解Server客户端调用服务器执行函数
  • 功能:按H键瞬间满血
  • 新建一个服务器RPC函数与封装这个回血函数的普通函数
    在这里插入图片描述
  • 逻辑
    在这里插入图片描述
    在这里插入图片描述
  • 然后绑定按键H执行恢复血量函数
    在这里插入图片描述
    在这里插入图片描述
  • 然后在虚幻引擎中添加操作绑定到映射中
    在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 运行结果
    在这里插入图片描述

RPC函数_NetMulticast_服务器调用全部执行

  • 服务器与各客户端关系图
    在这里插入图片描述
  • 多播是在服务器上调用,所有服务器和客户端都会执行一遍
  • 我们使用多播验证一下关系图结论
  • 声明一个多播RPC函数
    在这里插入图片描述
  • 抒写打印流程信息逻辑
    在这里插入图片描述
  • 在服务器中调用
    在这里插入图片描述
  • 将定时器改为10一次执行,开始就执行一次
    在这里插入图片描述
  • 用批处理脚本打开两个客户端与一个服务器验证关系图
  • 我们发现客户端1在火上所以打印了三次NetMulticast三次,但是客户端2只打印了一次,客户端之间不会拥有对方的HUD与控制器,但是他们在服务器端都会有一个自己的控制器
    在这里插入图片描述

Actor的网络身份

  • 先说结论,当我们使用客户端1进入到火焰中时,服务器和客户端都有有控制器,但是这个控制器在客户端1是自治的身份,在服务器上是权威身份,在其他客户端就是模拟代理的身份
    在这里插入图片描述
  • 验证
  • 在打印日志类里面新建三个打印获取网络身份的函数
    在这里插入图片描述
  • 打印网络身份函数
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 运行结果:使用批处理脚本开一个服务器与两个客户端,让客户端1踩上火焰
  • 可以看见服务器上客户端1的控制器是权威一个与自治一个
    在这里插入图片描述
  • 而客户端1自身控制器是自治的,然后客户端1的远程端就是服务器也就是权威的
    在这里插入图片描述
  • 客户端2是模拟的,客户端2的远程端是服务器也是权威的在这里插入图片描述
  • 27
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值