카테고리 없음

7/15 TIL

blog13869 2026. 7. 15. 19:39

[ PROJECT F ]

 

오늘의 구현 목록

 

[ 수류탄 코드 구현 ]

 

< PFGrenade >

더보기

< 헤더 파일 >

 

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/GameModeBase.h"
#include "PFGameMode.generated.h"

/**
 * 
 */
class APFGameState;
class APFLevelPortal;
class APFBaseItem;

UCLASS()
class PROJECTF_API APFGameMode : public AGameModeBase
{
    GENERATED_BODY()
    
public:
    
    APFGameMode();
    
    void OnMonsterKilled();
    
    void ProceedToNextLevel();
    
    // 게임오버 이동
    void GameOver();
    
    UFUNCTION(BlueprintCallable, Category = "Item|Drop")
    void HandleMonsterDeath(const FVector& SpawnLocation);
    
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "UI")
    TSubclassOf<class UUserWidget> LoadingWidgetClass;
    
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item|Classes")
    TSubclassOf<APFBaseItem> HealingItemClass;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item|Classes")
    TSubclassOf<APFBaseItem> PistolAmmoClass;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item|Classes")
    TSubclassOf<APFBaseItem> RifleAmmoClass;

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item|Classes")
    TSubclassOf<APFBaseItem> GrenadeItemClass;

    // 기본 드랍 확률 (20%)
    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Item|DropSettings")
    float OverallDropChance = 0.2f;
    
    void RegisterLevelPortal(class APFLevelPortal* Portal) { LevelPortalInstance = Portal; }
    
protected:
    
    virtual void BeginPlay() override;
    
    void TimerCountDown();
    void StartWave();
    void WaveClear();
    void LevelClear();
    
    
    
private:
    
    void SpawnRandomItem(const FVector& SpawnLocation, int32 CurrentLevel);
    
    UPROPERTY(VisibleAnywhere, Category = "Game Data")
    APFGameState* PFGameState;
    
    FTimerHandle PFTimerHandle;
    
    UPROPERTY()
    class APFWaveManager* PFWaveManager;
    UPROPERTY()
    class APFSpawnManager* PFSpawnManager;
    
    UPROPERTY(EditAnywhere, Category = "Wave Data")
    UDataTable* WaveDataTableAsset;
    
    class APFLevelPortal* LevelPortalInstance;
};

 

< 소스 파일 >

 

#include "Weapon/PFGrenade.h"
#include "Components/SphereComponent.h"
#include "Components/StaticMeshComponent.h"
#include "GameFramework/ProjectileMovementComponent.h"
#include "Kismet/GameplayStatics.h" 
#include "NiagaraComponent.h" 
#include "DrawDebugHelpers.h"
#include "Monster/PFMonsterCharacter.h"


APFGrenade::APFGrenade()
{
    PrimaryActorTick.bCanEverTick = false;

    CollisionComponent = CreateDefaultSubobject<USphereComponent>(TEXT("SphereComponent"));
    CollisionComponent->InitSphereRadius(10.0f);
    CollisionComponent->SetCollisionProfileName(TEXT("BlockAll"));
    RootComponent = CollisionComponent;

    GrenadeMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("GrenadeMesh"));
    GrenadeMesh->SetupAttachment(RootComponent);
    
    ExplosionSphereComponent = CreateDefaultSubobject<USphereComponent>(TEXT("ExplosionSphereComponent"));
    ExplosionSphereComponent->SetupAttachment(RootComponent);
    ExplosionSphereComponent->InitSphereRadius(400.0f);
    ExplosionSphereComponent->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
    ExplosionSphereComponent->SetCollisionProfileName(TEXT("OverlapAllDynamic"));

    ExplosionEffect = CreateDefaultSubobject<UNiagaraComponent>(TEXT("ExplosionEffect"));
    ExplosionEffect->SetupAttachment(RootComponent);
    ExplosionEffect->bAutoActivate = false;
    
    ProjectileMovement = CreateDefaultSubobject<UProjectileMovementComponent>(TEXT("ProjectileMovement"));
    ProjectileMovement->UpdatedComponent = CollisionComponent;
    ProjectileMovement->InitialSpeed = 1500.f;
    ProjectileMovement->MaxSpeed = 1500.f;
    ProjectileMovement->bRotationFollowsVelocity = true;
    ProjectileMovement->bShouldBounce = true;
    ProjectileMovement->Bounciness = 0.3f;
}


void APFGrenade::BeginPlay()
{
    Super::BeginPlay();
    // 타이머 설정
    GetWorldTimerManager().SetTimer(ExplodeTimerHandle, this, &APFGrenade::Explode, FuseTime, false);
}



void APFGrenade::Explode()
{
    FVector ExplodeLocation = GetActorLocation();

    // 나이아가라 이펙트 on(활성화)
    if (ExplosionEffect)
    {
       ExplosionEffect->DetachFromComponent(FDetachmentTransformRules::KeepWorldTransform);
       ExplosionEffect->Activate(true);
    }

    // 청각 효과 (사운드 재생)
    if (ExplosionSound)
    {
       UGameplayStatics::PlaySoundAtLocation(GetWorld(), ExplosionSound, ExplodeLocation);
    }

    if (ExplosionSphereComponent)
    {
       TArray<AActor*> OverlappingActors;
        
       //  ExplosionSphereComponent 이랑 overlap된 액터 탐색
       ExplosionSphereComponent->GetOverlappingActors(OverlappingActors);

       for (AActor* HitActor : OverlappingActors)
       {
          if (HitActor)
          {
             // 몬스터만 피격
             APFMonsterCharacter* Monster = Cast<APFMonsterCharacter>(HitActor);
             if (Monster)
             {
                Monster->TakeDamageFromMonster(ExplosionDamage, GetOwner());
                    
                UE_LOG(LogTemp, Log, TEXT("[Grenade] %s 몬스터에게 수류탄 데미지 %f 적용 완료!"), 
                   *Monster->GetName(), ExplosionDamage);
             }
          }
       }
    }

    // 이후 처리
    if (GrenadeMesh)
    {
       GrenadeMesh->SetHiddenInGame(true);
    }
    
    if (CollisionComponent)
    {
       CollisionComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
    }
    
    if (ProjectileMovement)
    {
       ProjectileMovement->StopMovementImmediately();
    }

    FTimerHandle DestroyTimerHandle;
    GetWorldTimerManager().SetTimer(DestroyTimerHandle, [this]()
       {this->Destroy();}, 5.0f, false);
}

 

< 이 후 BP 래핑 >

< 수류탄 효과 (TakeDamageFromMonster)는 적용되는데 나이아가라 이펙트가 나오지않는 이슈 발생 >

더보기

해결 방법

 

 

CombatComponent 에서의 Grenade Class 를 작성한 BP_PFGrenade_N으로 교체

< 테스트 화면 >

 

 

[ 몬스터 스폰 포탈, Level포탈/보스 포탈 나이아가라 이펙트 수정 ]

 

더보기
몬스터 포탈

 

보스/레벨 포탈