Dev9 여러 마법 스킬 사용, 스킬 사용 시 마나 소모, 마법 스킬 사운드
드디어 누르는 키에 따라 다른 마법 스킬을 구사할 수 있게 되었다..!Permalink
자 일단 숫자키로 스킬 사용할 거니까 액션 매핑에 추가해준다.
(원래 1번키만 사용하도록 했었는데 2,3번키 추가)
유저가 누른 키 구분은 컨트롤러 스크립트가 해준다.
MainPlayerController.cppPermalink
int AMainPlayerController::WhichKeyDown()
{
int result;
if (this->WasInputKeyJustPressed(EKeys::One) || this->WasInputKeyJustPressed(EKeys::NumPadOne))
{
result = 1;
}
if (this->WasInputKeyJustPressed(EKeys::Two) || this->WasInputKeyJustPressed(EKeys::NumPadTwo))
{
result = 2;
}
if (this->WasInputKeyJustPressed(EKeys::Three) || this->WasInputKeyJustPressed(EKeys::NumPadThree))
{
result = 3;
}
return result;
}
컨트롤러의 WasInputKeyJustPressed 함수로 방금 누른 키가 숫자키인지 확인한다.
만약 숫자키라면 각 숫자를 반환해준다. 이 반환값은 어디에 쓰느냐?
플레이어 스크립트의 SkillNum 변수에 들어간다.
Main.cppPermalink
void AMain::Attack()
{
if (EquippedWeapon && !bAttacking)
{
SkillNum = MainPlayerController->WhichKeyDown();
UBlueprintGeneratedClass* LoadedBP = LoadObject<UBlueprintGeneratedClass>(GetWorld(), TEXT("/Game/Blueprint/MagicAttacks/WindAttack.WindAttack_C")); //초기화 안 하면 ToSpawn에 초기화되지 않은 변수 넣었다고 오류남
switch (SkillNum)
{
case 1:
LoadedBP = LoadObject<UBlueprintGeneratedClass>(GetWorld(), TEXT("/Game/Blueprint/MagicAttacks/WindAttack.WindAttack_C"));
break;
case 2:
LoadedBP = LoadObject<UBlueprintGeneratedClass>(GetWorld(), TEXT("/Game/Blueprint/MagicAttacks/ShockAttack.ShockAttack_C"));
break;
case 3:
LoadedBP = LoadObject<UBlueprintGeneratedClass>(GetWorld(), TEXT("/Game/Blueprint/MagicAttacks/EarthAttack.EarthAttack_C"));
break;
default:
break;
}
ToSpawn = Cast<UClass>(LoadedBP);
bAttacking = true;
SetInterpToEnemy(true);
UAnimInstance* AnimInstance = GetMesh()->GetAnimInstance();
if (AnimInstance && CombatMontage)
{
AnimInstance->Montage_Play(CombatMontage);
AnimInstance->Montage_JumpToSection(FName("Attack"), CombatMontage);
Spawn();
}
}
}
그리고 마법 블루프린트들을 따로 폴더를 만들어서 넣어놨는데
이 친구들을 스크립트로 불러오는 것이다.
SkillNum에 따라서 어떤 블루프린트 클래스를 할당할지 결정된다.
그리고 할당된 블루프린트 클래스는 UClass로 캐스트해서 ToSapwn(플레이어가 쓸 마법, 월드에 스폰될 마법)에 넣어준다.
공격 모션이 나온 뒤에 이제 마법이 나올 차례이므로 스폰 메서드를 호출한다.
void AMain::Spawn()
{
if (ToSpawn && MP >= 20)
{
if (!(SkillNum == 1 || (SkillNum == 2 && MP >= 30) || (SkillNum == 3 && MP >= 40))) return; //If player have not enough MP, then player can't use magic
FTimerHandle WaitHandle;
GetWorld()->GetTimerManager().SetTimer(WaitHandle, FTimerDelegate::CreateLambda([&]()
{
UWorld* world = GetWorld();
if (world)
{
FActorSpawnParameters spawnParams;
spawnParams.Owner = this;
FRotator rotator = this->GetActorRotation();
FVector spawnLocation = AttackArrow->GetComponentTransform().GetLocation();
if (SkillNum != 1 && CombatTarget)
{
spawnLocation = CombatTarget->GetActorLocation();
}
world->SpawnActor<AMagicSkill>(ToSpawn, spawnLocation, rotator, spawnParams);
}
}), 0.6f, false); // 0.6초 뒤 실행, 반복X
switch (SkillNum)// decrease MP
{
case 1:
MP -= 20.f;
break;
case 2:
MP -= 30.f;
break;
case 3:
MP -= 40.f;
break;
}
}
}
첫번째 조건부터 마나가 들어간 것을 볼 수 있는데,
1번 스킬은 20, 2번 스킬은 30, 3번 스킬은 40 이렇게 마나가 소모된다. 따라서 최소한 마나가 20 이상일 때만 마법을 사용 할 수 있다.
그리고 숫자키 2 or 3번을 눌렀을 경우 (SkillNum에 2 or 3이 들어왔을 때) 현재 플레이어의 마나가 해당 스킬의 마나 이상인지 확인한다. 만약 해당 스킬의 소모 마나량보다 마나가 적을 경우 메서드를 탈출한다.
하지만 이 조건에 걸리지 않았다면? 마법을 사용할 수 있는 충분한 마나가 있는 것이므로 마법을 사용할 수 있다.
SkillNum이 1일 경우(모래?바람 공격) 회오리를 일으키며 앞으로 이동하는 마법이기 때문에 플레이어의 AttackArrow로 스폰 위치를 잡아줘도 되지만
스킬 2,3번의 경우 마법이 이동하지 않고 바로 적에게 먹히게 하고 싶기 때문에 타겟의 위치를 불러와서 바로 그 자리에서 스폰되게 한다. (단, 타겟이 없으면 1번 스킬과 같이 AttackArrow의 위치에서 스폰된다. 하지만 이동하는 것을 원치 않기에 블루프린트에서 Initial Speed를 0으로 줬다.)
마지막으로는 사용한 스킬의 마나 소모량만큼 플레이어의 마나를 감소시킨다.
2번 스킬 쇼크웨이브는 중력도 필요치 않아서 0으로 줬다.
3번 스킬 바위 공격은 땅으로 떨어져야 하기에 중력을 적당히 1로 줬다.
그리고 땅을 통과해서 떨어지면 안 되고 땅과 부딪히면 그 자리에서 더 이상 떨어지지 않아야한다.
즉 땅과 콜리전 작용이 일어나야 하기 때문에 콜리전 설정에서 WorldStatic을 블록 처리 했다.
마법을 사용하는데 무음이라면 이 얼마나 심심할 것인가.
사운드는 필수다.
MagicSkill.hPermalink
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sounds")
class USoundCue* ExplosionSound;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sounds")
class USoundCue* MagicSound;
헤더 파일에 폭발 사운드와 (마법이 스폰되는 순간에 적용되는) 마법 사운드 변수를 선언했다.
마법 사운드는 마법이 스폰되자마자 플레이되게 한다. 폭발 사운드만 있는 경우도 플레이시킨다.
따라서 BeginPlay() 에서 실행한다.
(참고로 2, 3번 공격은 마법 사운드가 없고 적과 오버랩되었을 때 나오는 폭발 사운드만 있다.)
void AMagicSkill::BeginPlay()
{
Super::BeginPlay();
Sphere->OnComponentBeginOverlap.AddDynamic(this, &AMagicSkill::OnComponentBeginOverlap);
if (MagicSound)
{
UGameplayStatics::PlaySound2D(this, MagicSound);
}
else if (ExplosionSound) // 폭발 사운드만 존재하는 경우
{
UGameplayStatics::PlaySound2D(this, ExplosionSound);
}
FTimerHandle WaitHandle;
GetWorld()->GetTimerManager().SetTimer(WaitHandle, FTimerDelegate::CreateLambda([&]()
{
if (this->IsValidLowLevel())
{
Destroy(true);
}
}), 3.f, false);
}
또 여기서 중요한 것은 마법 스킬이 어떠한 적 콜리전에도 맞지 않았을 때 1번 스킬의 경우 계속해서 앞으로 이동하게 된다. 상당히 보기가 싫으므로 적당히 3초 뒤면 파괴되게 했다.
2, 3번 스킬 또한 3초 뒤 알아서 소멸한다.
오버랩 메서드에서도 코드가 추가되었다.
void AMagicSkill::OnComponentBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
if (OtherActor)
{
AEnemy* Enemy = Cast<AEnemy>(OtherActor);
if (Enemy)
{
UGameplayStatics::PlaySound2D(this, ExplosionSound);
if (this->GetName().Contains("Wind"))
{
UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), ParticleFX, GetActorLocation());
Destroy(true);
}
}
}
}
일단 세 공격이 공통으로 가지고 있는 폭발 사운드를 재생한다.
1번 스킬의 경우 이동하다가 적 콜리전에 오버랩되어 폭발 파티클이 나온 뒤 파괴되지만
2, 3번 스킬의 경우 타겟 위치에서 바로 스폰되기 때문에 기존 코드대로라면 마법이 제대로 보이기도 전에 바로 파괴되어버린다.
따라서 마법 이름에 Wind가 포함되었을 때만, 즉 1번 공격일 때만 폭발 파티클을 스폰한 뒤 파괴한다.
(2, 3번은 폭발 파티클도 없다.)
사운드들은 블루프린트 클래스에서 직접 넣어준다.
사운드들은 모두 이렇게 생긴 사운드 큐라는 것을 이용한다.
영상 소리 필수!
다음 할 것 : 적 체력바 + 체력 감소, 몬스터 추가 배치