写在前面
在写虚幻引擎的多人游戏时,脑子里必须有鲜明的C/S概念,即虚幻引擎的多人游戏模式下的网络,包括了一个Server,主要负责维持游戏的一个权威副本(Authority),然后Client,连接Server,并单独对Server发送更新的信息接受Server的信息与Server的权威副本保持一致。这种多对一的模式相比于p2p的模式极大简化了信息的交流量。
然而我们写的代码,是Server和Client都在用的,所以我们先定义Actor们在所有电脑上的初始状态,接着单独处理Server逻辑,在需要更新时,再通知相应的Client进行更新,这里的更新一般是通过Replication来完成的。
创建C++武器类
一样遵从先有c++类(高度拓展性),再有蓝图类(易用性)的准则。我们先创建c++类。
武器Actor包括了两个组件,一个是skeletal mesh, 用来定义武器的模型,以及Sphere Area,用来定义武器的碰撞事件。武器模型本身,我们需要它能够在碰到任何物体上时,都会被block, 但是玩家能够正常碰到它;而Sphere Area,主要用来探测玩家是否接近它,如果接近它了,就会亮起提示或者可被捡起,因此在碰撞通道上,它对于其他所有物体都是Ignore状态,只有对玩家才是Overlap状态,以产生overlap事件并且做出响应。
此外,我们先写Server的逻辑,在有必要时,再通知Client即可。
// header -------------------------------------------------------------------------------------------------
UENUM(BlueprintType)
enum class EWeaponStatus : uint8
{
EWS_Initial UMETA(DisplayName = "Initial Status"),
EWS_Equipped UMETA(DisplayName = "Equipped Status"),
EWS_Dropped UMETA(DisplayName = "Dropped Status"),
EWS_MAX UMETA(DisplayName = "DefaultMAX")
};
UCLASS()
class BLASTER__API AWeapon : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AWeapon();
// Called every frame
virtual void Tick(float DeltaTime) override;
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
private:
UPROPERTY(VisibleAnywhere, Category = "Weapon Parameters")
class USkeletalMeshComponent *WeaponMesh;
UPROPERTY(VisibleAnywhere, Category = "Weapon Parameters")
class USphereComponent *SphereArea;
UPROPERTY(VisibleAnywhere, Category = "Weapon Parameters")
EWeaponStatus status;
};
// cpp-----------------------------------------------------------------------------------------------------
// No Collision: 对象将不参与任何碰撞检测或物理交互,无论“Collision Channel”如何设置。
// Query Only (No Physics Collision): 对象将进行碰撞检测,但不会物理交互。在这种情况下,如果“Collision Channel”设置为:
// Ignore: 不会检测碰撞。
// Overlap: 会触发重叠事件,但不会阻挡。
// Block: 对象会被其他对象阻挡,但不会因为物理反应发生移动。
// Physics Only (No Query Collision): 对象将进行物理交互,但不会触发碰撞事件。如果“Collision Channel”设置为:
// Ignore: 不会响应碰撞或重叠。
// Overlap: 不常用,因为此设置通常用于只需要物理交互的情况。
// Block: 对象物理上会阻挡其他对象。
// Collision Enabled (Query and Physics): 对象将参与碰撞检测并进行物理交互。如果“Collision Channel”设置为:
// Ignore: 不响应碰撞。
// Overlap: 会触发重叠事件,但不会物理阻挡。
// Block: 对象会阻挡其他对象,同时会触发碰撞事件。
// Sets default values
AWeapon::AWeapon()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = false;
// 设置是否可复制
bReplicates = 1;
this->WeaponMesh = CreateDefaultSubobject<USkeletalMeshComponent>("Weapon Mesh");
this->WeaponMesh->SetupAttachment(RootComponent);
// 将WeaponMesh设置为根组件,它拥有最高的transformation authority
SetRootComponent(this->WeaponMesh);
// 进行枪支的碰撞初始化 注意枪支一开始是被拿在手上的,它是属于pawn的一部分, 因此以下的设定都是在pawn抛出枪支之后的效果
// 枪支将block一切的物体,即会阻挡物体
this->WeaponMesh->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Block);
// 对玩家,将会无视碰撞 意味我们可以穿过枪体,并且没有一点事件触发
this->WeaponMesh->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Ignore);
// 先将碰撞效果设置为无
this->WeaponMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);
this->SphereArea = CreateDefaultSubobject<USphereComponent>("Sphere Area");
this->SphereArea->SetupAttachment(RootComponent);
// 忽略全部的物体
this->SphereArea->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore);
this->SphereArea->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
// Called when the game starts or when spawned
void AWeapon::BeginPlay()
{
Super::BeginPlay();
// 如果是在服务器上, 才让SphereArea正常发挥碰撞作用
if (HasAuthority())
{
this->SphereArea->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
this->SphereArea->SetCollisionResponseToChannel(ECollisionChannel::ECC_Pawn, ECollisionResponse::ECR_Overlap);
}
}
// Called every frame
void AWeapon::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
创建武器蓝图类
我们注意到,那些在C++中加的组件,全部都已经在蓝图上体现,接着我们加上合适的组件即可。