跳过正文

CoopGame07-增强道具

··2995 字·6 分钟
coopgame - 这篇文章属于一个选集。
§ 7: 本文

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

1
2
3
4
5
6
7
8
    //球形组件
	UPROPERTY(VisibleAnywhere,Category="Components")
	class USphereComponent *SphereComp;
	//贴花组件
	UPROPERTY(VisibleAnywhere,Category="Components")
	class UDecalComponent *DecalComp;
	//重载重叠函数
	virtual void NotifyActorBeginOverlap(AActor* OtherActor) override;

SPickupActor.cpp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
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

 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
42
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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
	//实际起作用的道具类
	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
32
33
34
35
36
37
38
39
40
41
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

蓝图如图

Untitled.webp

PickUpActor
BP_PickUpActor

1.指定贴画材质M_PowerupDecal ###5.创建继承PowerUpActorC++类的BP_PowerUpBase蓝图类。 1.手动添加静态网格体组件,并关闭静态网格体的碰撞预设,设置为NoCollision。 ###4.创建继承BP_PowerUpBase蓝图类的Powerup_SuperSpeed蓝图类。 1指定SpeedIcon静态网格体, 2.蓝图编写如图

Untitled 1.webp

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

M_Powerup

Untitled 2.webp

MI_PowerupSpeed

Untitled 3.webp

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

Untitled 4.webp

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

Untitled 5.webp

Untitled 6.webp

#加血道具 ###1.给健康组件

SHealthComponent

添加回血函数

Heal()

SHealthComponent.h

1
2
    UFUNCTION(BlueprintCallable,Category="HealthComponent")
	void Heal(float HealAmount);

SHealthComponent.cpp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
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.编写蓝图逻辑

Untitled 7.webp

3.修改PickUpActor.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
25
    //实际起作用的道具类,可以在场景实例中设置需要生成的道具类,比如加血的或加速的
	// UPROPERTY(EditDefaultsOnly, Category="PickUpActor")
	// TSubclassOf<class ASPowerUpActor> PowerUpClass;
	UPROPERTY(EditInstanceOnly,Category="PickUpActor")
	TSubclassOf<class ASPowerUpActor> PowerUpClass;
```\\
>4.将道具的材质中颜色转换为参数,创建材质实例以适用蓝色的加速道具,绿色的加血道具。
>>

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

>6.将起作用的道具类`Powerup_HealthRegen`和`Powerup_SuperSpeed`的静态网格体位置Z设置为`50`,并给它们都添加上`旋转移动组件`,实现自转效果。
#联机化
###1.设置复制
>SPowerUpActor.h
```c++
    //同步激活状态
	UPROPERTY(ReplicatedUsing=OnRep_PowerActive)
	bool bIsPowerActive;
	UFUNCTION()
	void OnRep_PowerActive();
	//蓝图可实现事件,去蓝图实现
	UFUNCTION(BlueprintImplementableEvent, Category = "Powerups")
	void OnPowerupStateChanged(bool bNewIsActive);

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);
}

SPickUpActor.h

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.修改蓝图类

BP_PowerUpBase

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

Untitled 8.webp

###3.修改蓝图类

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

Untitled 9.webp

###4.修改蓝图类

Powerup_HealthRegen

,修改为玩家群体加血

Untitled 10.webp

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

1.SPowerUpActor.h

 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);

2.SPowerUpActor.cpp

1
2
3
4
5
6
7

//void ASPowerUpActor::ActivatePowerUp(){}修改为带参的函数
void ASPowerUpActor::ActivatePowerUp(AActor* ActiveFor)
{
	OnActivated(ActiveFor);
    //...
}

3.SPickUpActor.cpp

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
void ASPickUpActor::NotifyActorBeginOverlap(AActor* OtherActor)
{
	Super::NotifyActorBeginOverlap(OtherActor);
//...
	if (Character && PowerUpInstance && GetLocalRole()==ROLE_Authority)
	{
        ////这里传入碰到道具的玩家为激活对象
        PowerUpInstance->ActivatePowerUp(OtherActor);//道具起作用
//...
	}

}

4.修改蓝图类 PowerUp_SuperSpeed

Untitled 11.webp

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