-
블루아카이브 루트슈터 팬게임 프로젝트(25) - 총기의 특수효과 추가루트슈터 프로젝트 2024. 1. 29. 00:05
안녕하세요. 이번에는 기존의 무기들의 상위 등급인 유니크 등급의 무기를 만들어 특수효과가 부여된 총기를 구현해 보도록 하겠습니다. 이것을 구현하기 이전에, C++코드의 구조를 다시 정비하였습니다. 지금까지 제 게임에서 총기류를 담당하는 클래스는 Gun 클래스 하나였습니다. 하지만 각각의 총기에 특수효과가 붙거나, 총기의 종류가 다르다면 하나의 클래스만으로 이 총기류들을 전부 처리해줄 수 없습니다. 따라서 Gun 클래스를 추상클래스, Abstract 클래스로 만들고, 그 하위 클래스에 특수효과가 붙은 총기나 일반 총기류들을 담당하는 클래스를 자식으로 추가하기로 하였습니다.
이 포스팅을 기반으로 코드를 재정비 하였습니다. 추상 클래스와 DIP 에 대한 자세한 설명 감사합니다.
Gun.h
UCLASS(Abstract) class PROJECTBLUELANDS_API AGun : public AItemBase { GENERATED_BODY() public: // Sets default values for this actor's properties AGun(); protected: // Called when the game starts or when spawned virtual void BeginPlay() override; virtual void PostInitializeComponents(); virtual void SpawnBullet(FVector direction); void MuzzleFlashEffect(); UFUNCTION() void OnCharacterOverlap(UPrimitiveComponent* OverlappedComp, AActor* OtherActor, //오버랩 될 경우 call될 함수 UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult); UPROPERTY(EditDefaultsOnly, Category = "Combat") USceneComponent* projectileSpawnPoint; UPROPERTY(VisibleAnywhere) class UArrowComponent* muzzleFlash; //총구 화염 이펙트 public: // Called every frame virtual void Tick(float DeltaTime) override; virtual void Fire(FVector startPoint, FVector endPoint); FTransform GetLeftHandSocketTransform() const; UPROPERTY(EditAnywhere, BlueprintReadWrite) FWeaponStat gunStat; //총기 스탯에 대한 구조체 UPROPERTY(EditAnywhere, BlueprintReadWrite) int32 nowMag; //지금 총에 들어있는 탄환 개수; UPROPERTY(VisibleAnyWhere, BlueprintReadWrite, meta = (AllowPrivateAccess = "true")) class UNiagaraComponent* dropEffect; //땅바닥에 떨어진 총의 등급별 이펙트 UStaticMeshComponent* GetGunMesh(); virtual void MoveToInventory(class APlayerCharacter* character, int slotNum); UPROPERTY(VisibleAnywhere) class UBoxComponent* triggerBox; //총에 가까이 갈 시 트리거를 감지하는 박스 UPROPERTY(EditDefaultsOnly) class UParticleSystem* MuzzleEffect; virtual void GunReloaded(); //총이 장전 완료됐다면 private: UPROPERTY(VisibleAnywhere) USceneComponent* RootComp; //루트씬 컴포넌트 UPROPERTY(VisibleAnywhere) UStaticMeshComponent* gunMesh; //총 스태틱메시 };
언리얼에서 C++ 클래스를 가상 클래스로 만드는 방법은 맨위에 UCLASS()의 괄호안에 Abstract 키워드를 추가하는 것 입니다. 또한 몇가지 하위 클래스에서 변동이 필요할 것 같은 함수들에 virtual 키워드로 붙여 순수 가상함수로 만들었습니다.
참고로 일반적인 C++에서는 순수 가상함수가 1개라도 가지고 있는 클래스를 가상 클래스 라고 합니다. 여기서 순수 가상함수란 비어있는 가상함수를 뜻합니다.
Gun.cpp
void AGun::Fire(FVector startPoint, FVector endPoint) { } void AGun::SpawnBullet(FVector direction) { } void AGun::GunReloaded() { }
저는 이 함수 4개를 순수 가상함수로 만들어 두었습니다. 나머지 함수들은 아마 자식 클래스도 똑같이 동작할 것이라고 판단하였기 때문에 가상함수로 만들지 않았습니다.
UniqueGun.h
UCLASS(Abstract) class PROJECTBLUELANDS_API AUniqueGun : public AGun { GENERATED_BODY() };
총기 클래스의 하위 클래스로 유니크 총기들을 포괄하는 UniqueGun 가상클래스를 하나 더 만들었습니다. 이렇게 한 이유는 혹시몰라서 유니크 등급 총기들을 한번 묶어둘 필요가 있다고 느꼈기 때문입니다. 아직은 여기에 어떠한 것도 구현해놓지 않았습니다.
이제 본격적으로 특수효과가 붙은 유니크 총기를 코드로 구현해 보도록 하겠습니다. 이번에 구현할 총기는 쿠로미 세리카의 총인 신시어리티 입니다.
세리카의 고유무기의 이름은 신시어리티 입니다. 정직, 성실이라는 의미에 걸맞게 적중당 5초간 6%의 데미지 상승, 최대 5중첩이 가능하도록 하는 효과를 부여하였습니다. 세리카의 EX 스킬 효과도 공격력 증가이기 때문에 잘 맞는다고 생각했습니다.
Sincerity.h
UCLASS() class PROJECTBLUELANDS_API ASincerity : public AUniqueGun { GENERATED_BODY() public: ASincerity(); public: void Fire(FVector startPoint, FVector endPoint) override; void GunReloaded() override; void GetBuff(); private: int buffStack = 0; //신시어리티 버프스택 UFUNCTION() void LoseBuff(); void SpawnBullet(FVector direction) override; UPROPERTY(EditDefaultsOnly, Category = "Combat") TSubclassOf<class ASincerityProjectile> projectileClass; //총이 가진 총알 };
UniqueGun 클래스를 상속받은 신시어리티 고유무기의 클래스 파일입니다. 부모 Gun 클래스에 있던 가상함수들을 override 하고 신시어리티만의 특별한 함수와 변수들을 추가해줬습니다. 신시어리티는 버프 스택이 쌓인만큼 데미지가 증가해야 하므로 버프를 관리하는 함수들과 변수를 선언해 두었습니다.
Sincerity.cpp
void ASincerity::GetBuff() { buffStack = FMath::Clamp(buffStack + 1, 0, 5); FLatentActionInfo lose; lose.Linkage = 0; lose.CallbackTarget = this; lose.ExecutionFunction = "LoseBuff"; lose.UUID = GetUniqueID(); UKismetSystemLibrary::RetriggerableDelay(GetWorld(), 5.0, lose); } void ASincerity::LoseBuff() { buffStack = 0; } void ASincerity::SpawnBullet(FVector direction) { Super::SpawnBullet(direction); APlayerCharacter* player = Cast<APlayerCharacter>(GetOwner()); AProjectile* proj = (GetWorld()->SpawnActor<AProjectile>( projectileClass , projectileSpawnPoint->GetComponentLocation(), direction.Rotation() //projectileSpawnPoint->GetComponentRotation() )); if(proj && player) { float bulletDamage = gunStat.weaponDamage * (1.0 + player->playerStat[1]/100.0) * (1.00 + buffStack * 0.06); //플레이어의 총기 데미지 스탯 및 신시어리티 버프스택 가산 if(FMath::RandRange(0.0, 100.0) < this->gunStat.critChance + player->playerStat[5]) //크리티컬 성공시 { proj->SetDamage(bulletDamage * ((this->gunStat.critDamage + player->playerStat[6]) / 100.0)); //투사체 스폰 및 데미지 설정 proj->bIsCritical = true; } else{ proj->SetDamage(bulletDamage); //투사체 스폰 및 데미지 설정 proj->bIsCritical = false; } proj->SetOwner(this); //이 총알의 주인 = 총 proj->SetVelocity(direction); nowMag--; } }
버프를 얻는 GetBuff 함수와 5초 후 적중한 적이 없다면 모든 버프 스택을 잃어버리게 하는 LoseBuff 함수를 구현하였고, 총알을 스폰한 후 데미지를 설정하는 부분에서 버프스택당 6%의 데미지가 가산되도록 하였습니다.
SincerityProjectile.cpp
void ASincerityProjectile::OnHit(UPrimitiveComponent *hitComp, AActor *otherActor, UPrimitiveComponent *otherComp, FVector normalImpulse, const FHitResult &hitResult) { Super::OnHit(hitComp, otherActor, otherComp, normalImpulse, hitResult); auto myOwner = GetOwner(); if(!myOwner) { Destroy(); return; } AController* myOwnerInstigator = myOwner->GetInstigatorController(); UClass* damageT = UDamageType::StaticClass(); if(otherActor && otherActor != this && otherActor != myOwner && Cast<ASincerity>(myOwner)) { if(otherActor->ActorHasTag(FName("Enemy")))Cast<ASincerity>(myOwner)->GetBuff(); UGameplayStatics::ApplyDamage(otherActor, damage, myOwnerInstigator, this, damageT); } Destroy(); }
그리고 투사체 클래스에서 OnHit 함수를 가상함수로 만들어 신시어리티의 고유한 기능을 부여할 수 있도록 신시어리티만의 투사체 클래스를 하나 생성하였습니다. OnHit 함수에서 액터의 태그들을 확인하여 Enemy 태그가 있다면 적에게 적중한 것이므로 총알의 Owner(즉 신시어리티 총기) 에게 버프를 얻으라고 함수를 발동하라고 시킵니다. 이렇게 구현하면 적 적중시 버프 스택을 1개 얻고 5초안에 다른 적중이 없다면 버프를 모두 잃게 됩니다.
만들어진 유니크 아이템의 툴팁 UI입니다. 유니크 효과의 툴팁 구현에 대해서는 나중에 설명하도록 하겠습니다.
결과물입니다. 잘 안보이실 수 있지만 한발씩 적이 맞을수록 데미지가 상승하는 것을 볼 수 있습니다.
'루트슈터 프로젝트' 카테고리의 다른 글
블루아카이브 루트슈터 팬게임 프로젝트(26) - 패시브 트리 기능 (1) 2024.01.30 블루아카이브 루트슈터 팬게임 프로젝트(24) - 무기 사운드 추가 (0) 2024.01.26 블루아카이브 루트슈터 팬게임 프로젝트(23) - 재장전 & 무기교체 애니메이션 (1) 2024.01.26 블루아카이브 루트슈터 팬게임 프로젝트(22) - 메인화면 구현 (1) 2024.01.25 블루아카이브 루트슈터 팬게임 프로젝트(21) - 게임 저장 시스템 (1) 2024.01.23