CoopGame07-增强道具

type: Post
status: Published
date: 2022/10/18
slug: CoopGame07
summary: UE4 C++多人游戏入门
category: Unreal

跟随B站up主“技术宅阿棍儿”的教程制作的笔记。教程链接

加速道具

0.导入素材,导入最新工程版本的Powerups文件夹

1.创建拾取物类

1.创建继承ActorC++类SPickUpActorC++类,相当于道具的底座。

SPickupActor.h
    //球形组件
    UPROPERTY(VisibleAnywhere,Category="Components")
    class USphereComponent *SphereComp;
    //贴花组件
    UPROPERTY(VisibleAnywhere,Category="Components")
    class UDecalComponent *DecalComp;
    //重载重叠函数
    virtual void NotifyActorBeginOverlap(AActor* OtherActor) override;
SPickupActor.cpp
ASPickupActor::ASPickUpActor()
{
//...
    SphereComp = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComp"));
    SphereComp->SetSphereRadius(75);
    RootComponent = SphereComp;

    DecalComp = CreateDefaultSubobject<UDecalComponent>(TEXT("DeaclComp"));
    DecalComp->SetRelativeRotation(FRotator(90,0,0));
    DecalComp->DecalSize=FVector(64,75,75);
    DecalComp->SetupAttachment(RootComponent);
}

void ASPickupActor::NotifyActorBeginOverlap(AActor* OtherActor)
{
    Super::NotifyActorBeginOverlap(OtherActor);
}

2.创建继承ActorC++类的SPowerUpActorC++类,实际作用的道具。

SPowerUpActor.h
//道具起作用的间隔,类似每隔多少秒加多少血
    UPROPERTY(EditDefaultsOnly, Category="Powerups")
    float PowerUpInterval;
    //道具起作用的总次数
    UPROPERTY(EditDefaultsOnly, Category="Powerups")
    int32 TotalNrOfTicks;

    FTimerHandle TimerHandle_PowerUpTicks;
    //道具已经起作用的次数
    int32 TickProcessed;
    //道具起作用函数
    UFUNCTION()
    void OnTickPowerUp();
    //激活道具
    void ActivatePowerUp();

    UFUNCTION(BlueprintImplementableEvent, Category="Powerups")
    void OnActivated();

    UFUNCTION(BlueprintImplementableEvent, Category="Powerups")
    void OnPowerUpTicked();

    UFUNCTION(BlueprintImplementableEvent, Category="Powerups")
    void OnExpired();
SPowerUpActor.cpp
ASPowerUpActor::ASPowerUpActor()
{
    PrimaryActorTick.bCanEverTick = true;
    //道具作用间隔
    PowerUpInterval = 0.f;
    //作用总次数
    TotalNrOfTicks = 0;
}

void ASPowerUpActor::ActivatePowerUp()
{
    //激活道具
    OnActivated();
    //如果道具是有间隔的道具,则需要使用定时器,比如:10秒内加100滴血的道具
    if (PowerUpInterval > 0)
    {
        //设置定时器每PowerUpInterval时间间隔调用一次OnTickPowerUp()(时间句柄变量,调用对象,调用函数,调用间隔,是否循环,延迟)
        GetWorldTimerManager().SetTimer(TimerHandle_PowerUpTicks, this, &ASPowerUpActor::OnTickPowerUp, PowerUpInterval,
                                        true, 0.f);
    }
    else
    {
        //如果是不需要作用时间的道具则让道具直接起作用,比如加速道具只加一次速
        OnTickPowerUp();
    }
}

void ASPowerUpActor::OnTickPowerUp()
{
    //道具已经起作用的次数:每起作用一次就自增一次
    TickProcessed++;

    OnPowerUpTicked();
    //如果道具作用次数达到总次数,就使道具失效,同时清除定时器
    if (TickProcessed >= TotalNrOfTicks)
    {
        //使道具失效函数
        OnExpired();
        //清除定时器
        GetWorldTimerManager().ClearTimer(TimerHandle_PowerUpTicks);
    }
}

2.修改SPickUpActor类

1.创建SPickUpActor类继承Actor类,相当于道具的底座。

SPickupActor.h
    //实际起作用的道具类
    UPROPERTY(EditDefaultsOnly, Category="PickUpActor")
    TSubclassOf<class ASPowerUpActor> PowerUpClass;
    //实际起作用的道具实例
    class ASPowerUpActor* PowerUpInstance;
    //道具生成冷却时间
    UPROPERTY(EditInstanceOnly, Category="PickUpActor")
    float CooldownDuration;
    //生成道具时间句柄
    FTimerHandle TimerHandle_RespawnTimer;
    //生成道具函数
    void ResPawn();
SPickupActor.cpp
void ASPickUpActor::BeginPlay()
{
    Super::BeginPlay();
    //刚开始时生成道具
    ResPawn();
}

void ASPickUpActor::Tick(float DeltaTime)
{
    Super::Tick(DeltaTime);

}

void ASPickUpActor::ResPawn()
{
    //如果没有实际起作用的道具类则打印错误日志并返回
    if (PowerUpClass == nullptr)
    {
        UE_LOG(LogTemp,Warning,TEXT("PowerUpClass is null in %s"),*GetName());
        return;
    }
    //生成Actor的生成参数
    FActorSpawnParameters SpawnParameters;
    //设置参数设置Actor总是生成
    SpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
    //生成道具实例Actor<生成类型>(类,位置,生成参数)
    PowerUpInstance = GetWorld()->SpawnActor<ASPowerUpActor>(PowerUpClass,GetTransform(),SpawnParameters);
}

void ASPickUpActor::NotifyActorBeginOverlap(AActor* OtherActor)
{
    Super::NotifyActorBeginOverlap(OtherActor);
    //如果生成的道具实例存在,则让道具起作用,然后将道具实例置空,并设置定时器生成下一个道具
    if (PowerUpInstance)
    {
        PowerUpInstance->ActivatePowerUp();
        PowerUpInstance = nullptr;
        //设置生成道具的定时器,每隔CooldownDuration时间调用一次ResPawn()生成道具的函数。
        GetWorldTimerManager().SetTimer(TimerHandle_RespawnTimer,this,&ASPickUpActor::ResPawn,CooldownDuration);
    }
}

3.制作贴花材质M_PowerupDecal

蓝图如图

CoopGame07-增强道具

PickUpActor
BP_PickUpActor
1.指定贴画材质M_PowerupDecal

5.创建继承PowerUpActorC++类的BP_PowerUpBase蓝图类。

1.手动添加静态网格体组件,并关闭静态网格体的碰撞预设,设置为NoCollision。

4.创建继承BP_PowerUpBase蓝图类的Powerup_SuperSpeed蓝图类。

1指定SpeedIcon静态网格体,
2.蓝图编写如图

CoopGame07-增强道具

3.制作加速道具的材质M_Powerup和材质实例MI_PowerupSpeed。

M_Powerup

CoopGame07-增强道具

MI_PowerupSpeed

CoopGame07-增强道具

4.制作光源函数材质M_PowerupLightFunction并使用,关闭影子

CoopGame07-增强道具

添加点光源组件,并设置光照函数为M_PowerupLightFunction,关闭阴影,设置光照颜色为蓝色

CoopGame07-增强道具

CoopGame07-增强道具

加血道具

1.给健康组件

SHealthComponent

添加回血函数

Heal()
SHealthComponent.h
    UFUNCTION(BlueprintCallable,Category="HealthComponent")
    void Heal(float HealAmount);
SHealthComponent.cpp
void USHealthComponent::Heal(float HealAmount)
{
    //如果加血值为0,或者已经挂了,就返回
    if (HealAmount<=0 || Health<=0)
    {
        return;
    }
    //加血后的生命值限制在0到默认值100之间
    Health = FMath::Clamp(Health+HealAmount,0.0f,DefaultHealth);
    //打印生命值改变。
    UE_LOG(LogTemp,Log,TEXT("Health Changed: %s (+%s)"),FString::SanitizeFloat(Health));
    //广播伤害值为负数则为加血
    OnHealthChanged.Broadcast(this,Health,-HealAmount,nullptr,nullptr,nullptr);
}

1.创建继承BP_PowerUpBase蓝图类的Powerup_HealthRegen蓝图类。

1.手动添加并选择静态网格体,添加浮点型变量HealAmount,设置默认值为20。
2.编写蓝图逻辑

CoopGame07-增强道具

3.修改PickUpActor.h,意思是放置在场景中的蓝图类,在放置之后用场景中的实例指定更好,比如每个道具的冷却时间不同,要生成的道具的种类不同,在放置后设置。
    //实际起作用的道具类,可以在场景实例中设置需要生成的道具类,比如加血的或加速的
    // UPROPERTY(EditDefaultsOnly, Category="PickUpActor")
    // TSubclassOf<class ASPowerUpActor> PowerUpClass;
    UPROPERTY(EditInstanceOnly,Category="PickUpActor")
    TSubclassOf<class ASPowerUpActor> PowerUpClass;

4.将道具的材质中颜色转换为参数,创建材质实例以适用蓝色的加速道具,绿色的加血道具。

5.给加血道具也加上灯光,设置颜色等。

6.将起作用的道具类Powerup_HealthRegenPowerup_SuperSpeed的静态网格体位置Z设置为50,并给它们都添加上旋转移动组件,实现自转效果。

联机化

1.设置复制

SPowerUpActor.h
    //同步激活状态
    UPROPERTY(ReplicatedUsing=OnRep_PowerActive)
    bool bIsPowerActive;
    UFUNCTION()
    void OnRep_PowerActive();
    //蓝图可实现事件,去蓝图实现
    UFUNCTION(BlueprintImplementableEvent, Category = "Powerups")
    void OnPowerupStateChanged(bool bNewIsActive);
SPowerUpActor.cpp
ASPowerUpActor::ASPowerUpActor()
{
//...
    //设置网络复制
    SetReplicates(true);
    bIsPowerActive = false;
}

void ASPowerUpActor::OnTickPowerUp()
{
//...
    if (TickProcessed>=TotalNrOfTicks)//作用次数达到总次数
    {
        //...
        //设置激活状态为否,调用同步函数。
        bIsPowerActive = false;
        OnRep_PowerActive();
        GetWorldTimerManager().ClearTimer(TimerHandle_PowerUpTicks);//清除时间句柄
    }
}

void ASPowerUpActor::ActivatePowerUp()
{
    //激活道具
    OnActivated();
    //设置激活状态为否,调用同步函数。
    bIsPowerActive = true;
    OnRep_PowerActive();
//...
}

void ASPowerUpActor::OnRep_PowerActive()
{
    OnPowerUpStateChanged(bIsPowerActive);
}

void ASPowerUpActor::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);
    DOREPLIFETIME(ASPowerUpActor,bIsPowerActive);
}
SPickUpActor.h
SpickUpActor.cpp
ASPickUpActor::ASPickUpActor()
{
//...
    SetReplicates(true);
}

void ASPickUpActor::BeginPlay()
{
    Super::BeginPlay();
    //只在服务端生成道具
    if (GetLocalRole() == ROLE_Authority)
    {
        //刚开始时生成道具
        ResPawn();
    }
}

void ASPickUpActor::NotifyActorBeginOverlap(AActor* OtherActor)
{
    Super::NotifyActorBeginOverlap(OtherActor);
    //如果生成的道具实例存在,则让道具起作用,然后将道具实例置空,并设置定时器生成下一个道具
    //同时需要判断碰到的是不是角色,并且只在服务端激活道具
    ASCharacter* Character = Cast<ASCharacter>(OtherActor);
    if (Character && PowerUpInstance && GetLocalRole()==ROLE_Authority)
    {
        PowerUpInstance->ActivatePowerUp();
        PowerUpInstance = nullptr;
        //设置生成道具的定时器,每隔CooldownDuration时间调用一次ResPawn()生成道具的函数。
        GetWorldTimerManager().SetTimer(TimerHandle_RespawnTimer, this, &ASPickUpActor::ResPawn, CooldownDuration);
    }
}

2.修改蓝图类

BP_PowerUpBase

,用道具基类实现道具显示和销毁逻辑

CoopGame07-增强道具

3.修改蓝图类

Powerup_HealthRegen,子类负责实现具体功能,删除设置可视性逻辑。

CoopGame07-增强道具

4.修改蓝图类

Powerup_HealthRegen

,修改为玩家群体加血

CoopGame07-增强道具

5.实现联机的单人加速(需要指定道具作用的对象,获取玩家Pawn只能获取到玩家0,思路是在玩家重叠道具的时候传那个OtherActor)

1.SPowerUpActor.h
    //激活道具
    // void ActivatePowerUp();
    //指定道具作用目标ActiveFor
    void ActivatePowerUp(AActor* ActiveFor);

    // UFUNCTION(BlueprintImplementableEvent,Category="Powerups")
    // void OnActivated();

    UFUNCTION(BlueprintImplementableEvent,Category="Powerups")
    void OnActivated(AActor* ActiveFor);
2.SPowerUpActor.cpp

//void ASPowerUpActor::ActivatePowerUp(){}修改为带参的函数
void ASPowerUpActor::ActivatePowerUp(AActor* ActiveFor)
{
    OnActivated(ActiveFor);
    //...
}
3.SPickUpActor.cpp
void ASPickUpActor::NotifyActorBeginOverlap(AActor* OtherActor)
{
    Super::NotifyActorBeginOverlap(OtherActor);
//...
    if (Character && PowerUpInstance && GetLocalRole()==ROLE_Authority)
    {
        ////这里传入碰到道具的玩家为激活对象
        PowerUpInstance->ActivatePowerUp(OtherActor);//道具起作用
//...
    }

}
4.修改蓝图类 PowerUp_SuperSpeed

CoopGame07-增强道具

文章目录