[Unreal_C++_DarkSoul]#7_Magic Projectile
Player의 "Wizard" 공격 모드에서 활용하는 마법 발사체 개발 내용입니다.
포트폴리오 진행 사항을 기록하기 위한 포스팅입니다.
Overview
- 문제점
- 문제 해결
- 문제 해결 후 결과 코드
문제점
1. AMagic::SpawnMagicBall()
void AMagic::SpawnMagicBall(const FName& InSocketName)
{
// Transform
AActor* OwnerActor = GetOwner();
CheckNull(OwnerActor);
APlayerCharacter* Player = Cast<APlayerCharacter>(OwnerActor);
CheckNull(Player);
FVector SpawnLocation = Player->GetMesh()->GetSocketLocation(InSocketName);
FRotator SpawnRotation = Player->GetActorRotation();
FTransform SpawnTransform = FTransform(SpawnRotation, SpawnLocation);
// Spawn
switch (MagicProjectileType)
{
case EMagicProjectileType::E_MagicBall:
CheckNull(ProjectileSpawner);
ProjectileSpawner->SpawnProjectile((int32)EMagicProjectileType::E_MagicBall, InSocketName);
break;
case EMagicProjectileType::E_SkullBall:
CheckNull(MagicProjectileClass[(int32)EMagicProjectileType::E_SkullBall]);
MagicProjectile = GetWorld()->SpawnActor<AProjectile_MagicSkullBall>(MagicProjectileClass[(int32)EMagicProjectileType::E_SkullBall], SpawnTransform);
MagicProjectile->SetOwner(this);
break;
case EMagicProjectileType::E_Ray:
CheckNull(MagicProjectileClass[(int32)EMagicProjectileType::E_Ray]);
SpawnTransform = FTransform(SpawnRotation, SpawnLocation);
MagicProjectile = GetWorld()->SpawnActor<AProjectile_MagicRay>(MagicProjectileClass[(int32)EMagicProjectileType::E_Ray], SpawnTransform);
MagicProjectile->SetOwner(this);
break;
case EMagicProjectileType::E_Tornado:
CheckNull(MagicProjectileClass[(int32)EMagicProjectileType::E_Tornado]);
SpawnTransform = FTransform(SpawnRotation, SpawnLocation);
MagicProjectile = GetWorld()->SpawnActor<AProjectile_MagicTornado>(MagicProjectileClass[(int32)EMagicProjectileType::E_Tornado], SpawnTransform);
MagicProjectile->SetOwner(this);
break;
}
}
Problems
- 코드 중복 : 기본 공격 포함 3개의 스킬이 모두 다른 Proejctile을 던지는 바람에 미친 코드 중복이 발생합니다.
- 커플링 : 마법 발사체 클래스와 마법 클래스의 강한 커플링
Solutions
- 컴포넌트 패턴 : Player의 "마법" 공격 모드의 마법 발사체만을 관리하는 "컴포넌트" 구현
- 다형성 : 마법 발사체들 상위에 공통의 상위 클래스를 하나 선언하여, C++가 제공하는 다형성의 이점 활용
- 캡슐화 : 공통의 상위 클래스를 통해 하위 클래스들의 세부 기능들을 숨길 수 있습니다.
문제 해결
1. Projectile_Magic.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Projectile_Magic.generated.h"
/******************************************************************************************************************
[Remark] : Magic Weapon < - > Projectile_Magic 간 충돌 과정에서 발생하는 상호작용
시간 :
22-12-01(목) [03:34]
목적 :
Magic Projectile의 충돌을 통해 Magic Weapon이 Damage를 전달
설명 :
1. FMagicProjectileBeginOverlap 커스텀 Delegate 선언
2. Magic Weapon은 BindUFunction을 통해 바인딩을 수행
3. Magic Projectile의 충돌 컴포넌트의 충돌 발생 시 IsBound() + Execute()를 통해 Magic Weapon의 데미지 전달을 구현
********************************************************************************************************************/
DECLARE_DELEGATE_OneParam(FMagicProjectileBeginOverlap, AActor*);
UCLASS()
class DARKSOUL_API AProjectile_Magic : public AActor
{
GENERATED_BODY()
public:
AProjectile_Magic();
protected:
virtual void BeginPlay() override;
// ******************************************************************************************
// Method
// ******************************************************************************************
protected:
UFUNCTION()
virtual void OnCollision();
UFUNCTION()
virtual void OffCollision();
protected:
UFUNCTION()
virtual void StartLaunched();
UFUNCTION()
virtual void EndLaunched(class AActor* OtherActor);
protected:
UFUNCTION()
virtual void OnCapsuleBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
public:
FMagicProjectileBeginOverlap OnMagicProjectileBeginOverlap;
// *******************************************************************************************
// Data Member
// *******************************************************************************************
protected:
UPROPERTY(EditDefaultsOnly, Category = "Components")
class USceneComponent* Scene;
UPROPERTY(EditDefaultsOnly, Category = "Components")
class UNiagaraComponent* ProjectileEffect;
UPROPERTY(EditDefaultsOnly, Category = "Components")
class UNiagaraComponent* MuzzleEffect;
UPROPERTY(EditDefaultsOnly, Category = "Components")
class UNiagaraComponent* HitEffect;
protected:
UPROPERTY(VisibleAnywhere, Category = "Effects")
FTimerHandle TimerHandler;
UPROPERTY(VisibleAnywhere, Category = "Effects")
float WaitTime = 0.15f;
};
Details
- "마법" 공격 모드에서 던질 마법 발사체의 가장 상위 클래스 선언
- Custom Delegate(FMagicProjectileBeginOverlap) 선언, "충돌" 정보를 "Magic" 클래스에게 전달하기 위함.
- 그밖에 하위 클래스들에게 공통적으로 필요한 데이터 멤버들
Projectile Spawner 컴포넌트
1. C_ProjectileSpawnerComponent.h
UPROPERTY(EditDefaultsOnly, Category = "Proejectile")
TArray<TSubclassOf<class AProjectile_Magic>> ProjectileClass;
UPROPERTY(VisibleAnywhere, Category = "Projectile")
class AProjectile_Magic* Projectile;
2. C_ProjectileSpawnerComponent::SpawnProjectile()
void UC_ProjectileSpawnerComponent::SpawnProjectile(int32 InNum, const FName& InName)
{
AActor* OwnerWeapon = GetOwner();
CheckNull(OwnerWeapon);
APlayerCharacter* Player = Cast<APlayerCharacter>(OwnerWeapon->GetOwner());
CheckNull(Player);
// TODO : Spawn Location 설정
FVector SpawnLocation = Player->GetMesh()->GetSocketLocation(InName);
FRotator SpawnRotation = Player->GetActorRotation();
FTransform SpawnTransform = FTransform(SpawnRotation, SpawnLocation);
// TODO : Spawn Actor의 클래스 설정 여부
CheckNull(ProjectileClass.IsValidIndex(InNum));
Projectile = GetWorld()->SpawnActor<AProjectile_Magic>(ProjectileClass[InNum], SpawnTransform);
Projectile->SetOwner(this->GetOwner());
}
Details
- 마법 발사체 클래스 목록 관리
- 정수 인자를 받아서 스폰할 마법 발사체 종류를 결정 및 마침내 스폰합니다.
결과 코드
1. Magic.h
// ...
private:
UPROPERTY(EditDefaultsOnly, Category = "Components")
class UC_ProjectileSpawnerComponent* ProjectileSpawner;
//...
Details
- Projectile Spawner 컴포넌트를 데이터 멤버로 선언합니다.
2. Magic::SpawnMagicBall()
/*********************************************************************************************************
[Remark]: Switch 문을 통해 EMagicProjectileType 별 Spawn할 Magic_Projectile 결정하는 코드 수정
목적:
Component 패턴 적용
설명:
1. Projectile Spawner 컴포넌트 클래스 생성 및 구현
2. 현재 Projectile Type만 인자로 넘겨, Spawn할 Magic Projectile을 Projectile Spawner 컴포넌트가 결정합니다.
*********************************************************************************************************/
void AMagic::SpawnMagicBall(const FName& InSocketName)
{
CheckNull(ProjectileSpawner);
ProjectileSpawner->SpawnProjectile((int32)MagicProjectileType, InSocketName);
}
Details
- 단순히 몇 가지 데이터들을 인자로 넘겨주어, Projectile Spawner 컴포넌트에게 마법 발사체의 스폰을 맡깁니다.
'개인프로젝트' 카테고리의 다른 글
[Unreal_C++_DarkSoul]#9_기능 구현, Status Effect 기능 (0) | 2022.12.18 |
---|---|
[Unreal_C++_DarkSoul]#8_문제 해결, Send Damage 방식 수정 (0) | 2022.12.18 |
[Unreal_C++_DarkSoul]#6_기능 구현, Targeting 기능 (0) | 2022.12.10 |
[Unreal_C++_DarkSoul]#5_기능 구현, Target Point, Enemy Spawn 위치 (0) | 2022.12.10 |
[Unreal_C++_DarkSoul]#4_문제 해결, Data Table 로드 함수 (0) | 2022.11.27 |