跳过正文

CoopGame07-增强道具

··3747 字·8 分钟
目录
coopgame - 这篇文章属于一个选集。
§ 7: 本文

CoopGame07-增强道具
#

创建底座和道具基类
#

  1. 导入Powerups文件夹
  2. 创建继承ActorC++类SPickUpActor,相当于道具的底座
    SPickupActor.h声明球形组件, 贴花组件变量, 重载重叠函数,并在cpp中初始化组件,定义重叠函数,删除Tick相关函数
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    
    //球形组件
    UPROPERTY(VisibleAnywhere,Category="Components")
    class USphereComponent *SphereComp;
    //贴花组件
    UPROPERTY(VisibleAnywhere,Category="Components")
    class UDecalComponent *DecalComp;
    //重载重叠函数
    virtual void NotifyActorBeginOverlap(AActor* OtherActor) override;
    
    //CPP的构造函数ASPickUpActor()中初始化
    #include "Components/DecalComponent.h"  
    #include "Components/SphereComponent.h"
    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);
    }
    
  3. 创建继承ActorC++类的SPowerUpActor,实际作用的道具。
    SPowerUpActor.h声明道具作用间隔, 道具作用次数变量, 声明道具激活等函数
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    //道具起作用的间隔,类似每隔多少秒加多少血
    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中编写相关逻辑
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    
    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);
    	}
    }
    
  4. 修改SPickUpActor类,给道具底座加上道具
    SPickUpActor.h声明变量道具类,道具类实例,道具生成冷却时间, 道具生成时间句柄变量, 声明生成道具函数
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    UPROPERTY(EditDefaultsOnly, Category="PickUpActor")
    TSubclassOf<class ASPowerUpActor> PowerUpClass;//实际起作用的道具类
    
    class ASPowerUpActor* PowerUpInstance;//实际起作用的道具实例
    
    UPROPERTY(EditInstanceOnly, Category="PickUpActor")
    float CooldownDuration;//道具生成冷却时间
    
    FTimerHandle TimerHandle_RespawnTimer;//生成道具时间句柄
    
    void ResPawn();//生成道具函数
    
    SPickUpActor.cpp中生成道具
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    
    #include "SPowerUpActor.h"
    void ASPickUpActor::BeginPlay()
    {
    	Super::BeginPlay();
    	ResPawn();//刚开始时生成道具
    }
    
    
    void ASPickUpActor::ResPawn()
    {
    	if (PowerUpClass == nullptr)//如果没有实际起作用的道具类则打印错误日志并返回
    	{
    		UE_LOG(LogTemp,Warning,TEXT("PowerUpClass is null in %s"),*GetName());
    		return;
    	}
    	FActorSpawnParameters SpawnParameters;//生成Actor的生成参数
    	SpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;//设置参数设置Actor总是生成
    	PowerUpInstance = GetWorld()->SpawnActor<ASPowerUpActor>(PowerUpClass,GetTransform(),SpawnParameters);//生成道具实例Actor<生成类型>(类,位置,生成参数)
    }
    
    void ASPickUpActor::NotifyActorBeginOverlap(AActor* OtherActor)
    {
    	Super::NotifyActorBeginOverlap(OtherActor);
    	
    	if (PowerUpInstance)//如果生成的道具实例存在,则让道具起作用,然后将道具实例置空,并设置定时器生成下一个道具
    	{
    		PowerUpInstance->ActivatePowerUp();
    		PowerUpInstance = nullptr;
    		GetWorldTimerManager().SetTimer(TimerHandle_RespawnTimer,this,&ASPickUpActor::ResPawn,CooldownDuration);//设置生成道具的定时器,每隔CooldownDuration时间调用一次ResPawn()生成道具的函数。
    	}
    }
    

制作底座贴花材质
#

  1. 创建M_PowerupDecal的蓝图如图
    Untitled.webp

制作加速道具
#

  1. 创建继承PowerUpActor类的蓝图类BP_PowerUpBase放于新建的Powerups文件夹。
  2. 手动添加静态网格体组件,并关闭静态网格体的碰撞预设,设置为NoCollision
  3. 创建继承BP_PowerUpBase蓝图类的Powerup_SuperSpeed蓝图类,用作加速道具
  4. 指定SpeedIcon静态网格体,
  5. 蓝图编写如图
    Untitled 1.webp
  6. 制作加速道具的材质M_Powerup和材质实例MI_PowerupSpeed,M_Powerup如图1,材质实例如图2
    Untitled 2.webp
    Untitled 3.webp
  7. 制作光源函数材质M_PowerupLightFunction并使用,关闭影子
    Untitled 4.webp
  8. 为道具蓝图添加点光源组件,并设置光照函数为M_PowerupLightFunction,关闭阴影,设置光照颜色为蓝色,同时指定静态网格体和材质
    Untitled 5.webp
    Untitled 6.webp

制作加血道具
#

  1. 给健康组件SHealthComponent添加回血函数Heal()
    SHealthComponent.h声明回血函数,并在cpp中编写逻辑
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    UFUNCTION(BlueprintCallable,Category="HealthComponent")
    void Heal(float HealAmount);
    
    // 编写逻辑
    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);
    }
    
  2. 创建继承BP_PowerUpBase蓝图类的Powerup_HealthRegen蓝图类
  3. 手动添加并选择静态网格体HealthIcon,添加浮点型变量HealAmount,设置默认值为20
  4. 编写蓝图逻辑
    Untitled 7.webp
  5. 给加血道具和加速道具设置好材质实例(材质中颜色转换为参数Color),也给加血道具加上灯光,设置好颜色。
  6. 将道具蓝图类Powerup_HealthRegenPowerup_SuperSpeed的静态网格体位置Z设置为50,并给它们都添加上旋转移动组件,实现自转效果。

给底座添加道具
#

SPickUpActor.h声明作用道具类
1
2
3
//实际起作用的道具类,可以在场景实例中设置需要生成的道具类,比如加血的或加速的
UPROPERTY(EditInstanceOnly,Category="PickUpActor")
TSubclassOf<class ASPowerUpActor> PowerUpClass;

联机化
#

  1. 给底座类添加联机逻辑

    SPickUpActor.cpp中修改逻辑
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    
    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. 给道具类设置复制

    SPowerUpActor.h中声明一个是否激活的布尔值,并设置该布尔值改变的时候调用复制函数OnRep_PowerActive
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    //同步激活状态
    UPROPERTY(ReplicatedUsing=OnRep_PowerActive)
    bool bIsPowerActive;
    
    UFUNCTION()
    void OnRep_PowerActive();
    
    //蓝图可实现事件,去蓝图实现
    UFUNCTION(BlueprintImplementableEvent, Category = "Powerups")
    void OnPowerUpStateChanged(bool bNewIsActive);
    
    virtual void GetLifetimeReplicatedProps(TArray<class FLifetimeProperty>& OutLifetimeProps) const override;
    
    SPowerUpActor.cpp实现相关逻辑
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    
    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);
    }
    

  3. 修改道具蓝图基类BP_PowerUpBase,在这里实现道具模型的显示隐藏

    Untitled 8.webp

  4. 修改道具蓝图类加血道具Powerup_HealthRegen,删除子类控制道具模型显示隐藏的逻辑OnActived事件,只保留道具具体逻辑

    Untitled 9.webp

  5. 修改蓝图类Powerup_HealthRegen,将加血逻辑变成群体加血

    Untitled 10.webp

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

    SPowerUpActor.h修改道具激活的函数,添加一个参数用来传递激活的对象,同步修改cpp中的函数参数
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    
    //激活道具
    // void ActivatePowerUp();
    //指定道具作用目标ActiveFor
    void ActivatePowerUp(AActor* ActiveFor);
    
    // UFUNCTION(BlueprintImplementableEvent,Category="Powerups")
    // void OnActivated();
    
    UFUNCTION(BlueprintImplementableEvent,Category="Powerups")
    void OnActivated(AActor* ActiveFor);
    
    SPickUpActor.cpp底座类修改,传递碰撞到道具的Actor类给道具类激活
     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    
    void ASPickUpActor::NotifyActorBeginOverlap(AActor* OtherActor)
    {
    	Super::NotifyActorBeginOverlap(OtherActor);
    //...
    	if (Character && PowerUpInstance && GetLocalRole()==ROLE_Authority)
    	{
            ////这里传入碰到道具的玩家为激活对象
            PowerUpInstance->ActivatePowerUp(OtherActor);//道具起作用
    //...
    	}
    }
    

  7. 最终修改蓝图类PowerUp_SuperSpeed实现功能

    Untitled 11.webp

coopgame - 这篇文章属于一个选集。
§ 7: 本文