这次作业量和之前的相比可能会有点大,我先整理一下这次的作业清单:
- 魔法子弹的飞行声音以及爆炸声音
- 给玩家增加受击的闪亮的效果,和立方体相同的那种
- 增加一个health max的变量,把widget里头的health节点更换为health max节点,同时给health设置一个上下限,上限是health max , 下限是0
- 给我们刚创建出的显示伤害的widget设置一个动画,在受击的时候播放出来,同时伤害的显示不能写死,用变量来表示
- 新建一个生命药水,可以恢复生命值,回复一次之后,10秒内不能再次使用,10秒后可再次使用。当玩家血量满的时候忽略玩家的交互
- 给玩家的手上附加一个粒子效果(这个我没看明白,没写)
- 增加一个摇晃摄像头的效果
一共7项作业,量可以说是非常大了。。。不过还是一个一个来做。
1.魔法子弹的飞行声音以及爆炸效果:
我把这个功能写到了SFatherMagicProjectile里头
SFatherMagicProjectile.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SFatherMagicProjectile.generated.h"
UCLASS()
class ACTIONROGUELIKE_API ASFatherMagicProjectile : public AActor
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere)
class USphereComponent * SphereComp;
UPROPERTY(EditAnywhere)
class UProjectileMovementComponent * MovementComp;
UPROPERTY(EditAnywhere)
class UParticleSystemComponent * ParticleComp;
//一个声音组件
UPROPERTY(EditAnywhere)
class UAudioComponent * AudioComp;
//爆炸声
UPROPERTY(EditAnywhere)
class USoundBase * Sound;
public:
// Sets default values for this actor's properties
ASFatherMagicProjectile();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
UFUNCTION()
virtual void OnComponentHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, FHitResult Hit);
UFUNCTION()
void OnComponentOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
};
SFatherMagicProjectile.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "SFatherMagicProjectile.h"
#include "Components/SphereComponent.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "Particles/ParticleSystemComponent.h"
#include "SAttributeComponent.h"
#include "Components/AudioComponent.h"
#include "Kismet/GameplayStatics.h"
// Sets default values
ASFatherMagicProjectile::ASFatherMagicProjectile()
{
// 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;
FScriptDelegate OnHit;
OnHit.BindUFunction(this, STATIC_FUNCTION_FNAME(TEXT("ASFatherMagicProjectile::OnComponentHit")));
FScriptDelegate OnOverlap;
OnOverlap.BindUFunction(this, STATIC_FUNCTION_FNAME(TEXT("ASFatherMagicProjectile::OnComponentOverlap")));
SphereComp = CreateDefaultSubobject<USphereComponent>(TEXT("Sphere Comp"));
SphereComp->SetCollisionProfileName("Projectile");
SphereComp->SetSphereRadius(10.0f, true);
SphereComp->OnComponentHit.Add(OnHit);
SphereComp->OnComponentBeginOverlap.Add(OnOverlap);
RootComponent = SphereComp;
MovementComp = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("Movement Comp"));
MovementComp->InitialSpeed = 1000.0f;
MovementComp->bRotationFollowsVelocity = true;
MovementComp->bInitialVelocityInLocalSpace = true;
MovementComp->ProjectileGravityScale = 0.0f;
ParticleComp = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("Particle Comp"));
ParticleComp->SetupAttachment(SphereComp);
AudioComp = CreateDefaultSubobject<UAudioComponent>(TEXT("Audio Comp"));
}
// Called when the game starts or when spawned
void ASFatherMagicProjectile::BeginPlay()
{
Super::BeginPlay();
APawn* Instigator_01 = AActor::GetInstigator();
SphereComp->IgnoreActorWhenMoving(Instigator_01, true);
//播放飞行声效
AudioComp->Play(0.0f);
}
// Called every frame
void ASFatherMagicProjectile::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void ASFatherMagicProjectile::OnComponentHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, FHitResult Hit)
{
APawn* Instigator_01 = AActor::GetInstigator();
if (Instigator_01!=OtherActor)
{
//播放爆炸声效
UGameplayStatics::PlaySoundAtLocation(GetWorld(),Sound , this->GetActorLocation(), 1.0f, 1.0f);
GetWorld()->DestroyActor(this);
DrawDebugSphere(GetWorld(), Hit.ImpactPoint, 10.0f, 16, FColor::Red, false, 2.0f, 0U, 1.0f);
}
}
void ASFatherMagicProjectile::OnComponentOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
APawn* Instigator_01 = AActor::GetInstigator();
if (OtherActor)
{
USAttributeComponent* AttributeComp = Cast<USAttributeComponent>(OtherActor->GetComponentByClass(USAttributeComponent::StaticClass()));
if (AttributeComp)
{
if (Instigator_01 != OtherActor)
{
UGameplayStatics::Play
AtLocation(GetWorld(), Sound, this->GetActorLocation(), 1.0f, 1.0f);
AttributeComp->ApplyHealthChange(-20.0f);
GetWorld()->DestroyActor(this);
}
}
}
}
2.给玩家增加受击的闪亮的效果,和立方体相同的那种
这个首先得对我们的Gideon的材质进行修改
使用MatLayerBlend_Emissive函数,然后把我们的MF_HitFlash给连上去就好,再之后就是玩家角色的代码更改
SCharacter.cpp
void ASCharacter::OnHealthChanged(AActor* InstigatorActor, USAttributeComponent* OwningComp, float NewHealth, float Delta)
{
//这个函数做了更改,第一点是只有受伤才会触发闪亮的效果,加血的时候不需要触发这个效果
if (NewHealth<0.0f)
{
this->GetMesh()->SetScalarParameterValueOnMaterials("TimeToHit", GetWorld()->TimeSeconds);
if (Delta<0.0f)
{
APlayerController* PC = Cast<APlayerController>(GetController());
DisableInput(PC);
}
}
}
3.增加一个health max的变量,把widget里头的health节点更换为health max节点,同时给health设置一个上下限,上限是health max , 下限是0
这个蛮简单的,更改如下
SAttributeComponent.cpp
bool USAttributeComponent::ApplyHealthChange(float Delta)
{
//使用Clamp函数,Health的更改在Clamp函数的第一个参数那里显示了
Health = FMath::Clamp(Health+Delta, 0, HealthMax);
OnHealthChanged.Broadcast(nullptr,this,Health,Delta);
return true;
}
4.给我们刚创建出的显示伤害的widget设置一个动画,在受击的时候播放出来,同时伤害的显示不能写死,用变量来表示
Cube的蓝图更改
至于动画怎么播放,看各位的设计了,这里是千人千面的。
5.新建一个生命药水,可以恢复生命值,回复一次之后,10秒内不能再次使用,10秒后可再次使用。当玩家血量满的时候忽略玩家的交互
这也没遇上啥大问题,这个的示例就是我们的可以开关的那个箱子
SHealBottle.h
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "SGameplayInterface.h"
#include "SHealBottle.generated.h"
UCLASS()
class ACTIONROGUELIKE_API ASHealBottle : public AActor, public ISGameplayInterface
{
GENERATED_BODY()
void Interact_Implementation(APawn* InstigatorPawn) override;
UPROPERTY(VisibleAnywhere)
class UStaticMeshComponent* MeshComp;
private:
UFUNCTION()
void RefreshBottle();
private:
bool CanHeal;
FTimerHandle Timer;
public:
// Sets default values for this actor's properties
ASHealBottle();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
};
SHealBottle.cpp
// Fill out your copyright notice in the Description page of Project Settings.
#include "SHealBottle.h"
#include "Components/StaticMeshComponent.h"
#include "SAttributeComponent.h"
//接口函数的实现
void ASHealBottle::Interact_Implementation(APawn* InstigatorPawn)
{
ASCharacter* InstigatorCharacter_01 = Cast<ASCharacter>(InstigatorPawn);
USAttributeComponent* attributeComp_01 = InstigatorCharacter_01->AttributeComp;
if (attributeComp_01->GetHealth()>=100||!CanHeal)
{
return;
}
attributeComp_01->ApplyHealthChange(10.0f);
CanHeal = false;
MeshComp->SetVisibility(false);
//消失10秒
GetWorldTimerManager().SetTimer(Timer,this, &ASHealBottle::RefreshBottle, 10.0f);
}
void ASHealBottle::RefreshBottle()
{
CanHeal = true;
MeshComp->SetVisibility(true);
}
// Sets default values
ASHealBottle::ASHealBottle()
{
// 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;
MeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh Comp"));
CanHeal = true;
}
// Called when the game starts or when spawned
void ASHealBottle::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void ASHealBottle::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
7.增加一个摇晃摄像头的效果
SCharacter.cpp
void ASCharacter::PrimaryAttack()
{
//获得控制器
APlayerController* pc = Cast<APlayerController>(GetController());
//播放摄像机的震荡
pc->ClientStartCameraShake(CameraShakeComp);
GetWorldTimerManager().SetTimer(TimerHandle_PrimaryAttack,this,&ASCharacter::PrimaryAttack_TimeElasped,0.2f);
}