GameInstance
-> 엔진이 켜질 때 딱 하나만 존재하는 프로그램 종료까지 유지되는 객체
교체하고 싶다면?
편집-> 프로젝트 셋팅 -> 맵 & 모드 -> 게임 인스턴스 클래스 지정
내가 만든 것으로 지정하고 싶다면?
c++ 클래스 -> 새 c++ 클래스 -> 모든 클래스 -> gameinstance 검색 -> gameinstance 을 상속받아서 자체적인 gameinstance 클래스 제작 (GameInstance_Base)
싱글톤 방식 교체해서 GameInstance_Base 구현
포인터 타입이 아닌 단순 멤버로 둔다 -> 객체가 생성되지 않음 -> 가져오는 함수 제작
#include "CoreMinimal.h"
#include "Engine/GameInstance.h"
#include "Effect/EffectMgr.h"
#include "GameInstance_Base.generated.h"
UCLASS()
class UNREAL_3TH_API UGameInstance_Base : public UGameInstance
{
GENERATED_BODY()
private:
UEffectMgr effectmgr;
public:
UEffectMgr* GetEffectMgr() { return &effectmgr; }
public:
UGameInstance_Base();
~UGameInstance_Base();
};
EffectMgr 자체 C++ 클래스로 제작 ( UObject 상속 x ) => 문제 발생
#include "../Header/global.h"
#include "CoreMinimal.h"
class UEffectMgr
{
public:
void CreateEffect(UWorld* _World, EEFFECT_TYPE _Type, ULevel* _Level, FVector _Location);
};
Fire 테스트
void ACharacter_Base::Fire(const FInputActionInstance& _Instance)
{
bool bToggle = _Instance.GetValue().Get<bool>();
//LOG(Player, Error, TEXT("!! Fire !!"));
if (!IsValid(GetMesh()->GetAnimInstance()) || FireMontage.IsNull())
{
return;
}
if (bToggle && !IsReload)
{
GetMesh()->GetAnimInstance()->Montage_Play(FireMontage.LoadSynchronous(), 2.f);
GetMesh()->GetAnimInstance()->Montage_JumpToSection(FName("Fire"), FireMontage.LoadSynchronous());
IsFire = true;
// Explode Effect 재생해보기
// 이전 방식
//UEffectMgr::GetInst(GetWorld())->CreateEffect(EEFFECT_TYPE::EXPLODE, GetLevel(), GetActorLocation() + GetActorForwardVector() * 100);
// 새로운 방식
UGameInstance_Base* pGameInst = Cast<UGameInstance_Base>(UGameplayStatics::GetGameInstance(GetWorld()));
pGameInst->GetEffectMgr()->CreateEffect(GetWorld(), EEFFECT_TYPE::EXPLODE, GetLevel(), GetActorLocation() + GetActorForwardVector() * 100);
}
else
{
IsFire = false;
}
}
MakeShareable 이용해서 싱글톤? 구현 -> 이걸로 싱글톤 끝
EffectMgr Uobject 상속받았었는데, 바꿈
정적멤버로 자기 자신을 가리키는 것을 생성 ( 정적멤버는 데이터 영역 )
#include "../Header/global.h"
#include "CoreMinimal.h"
#include "UObject/NoExportTypes.h"
#include "EffectMgr.generated.h"
UCLASS()
class UNREAL_3TH_API UEffectMgr : public UObject
{
GENERATED_BODY()
private:
// 정적멤버는 데이터 영역
// 정적멤버로 자기 자신을 가리키는 것을 생성
static TSharedPtr<UEffectMgr> m_This;
public:
static UEffectMgr* GetInst();
UFUNCTION(BlueprintCallable)
void CreateEffect(UWorld* _World, EEFFECT_TYPE _Type, ULevel* _Level, FVector _Location);
};
* UObject로 파생된 애들은 ptr을 쓰면 안된다
가비지컬렉터에 의해 관리가 되다가
sharedptr 로 ..
* 스마트 포인터 공부하기
#include "EffectMgr.h"
#include "../Unreal_3thGameModeBase.h"
#include "Effect_Base.h"
// 정적멤버는 항상 초기화를 해주어야함
TSharedPtr<UEffectMgr> UEffectMgr::m_This = nullptr;
UEffectMgr* UEffectMgr::GetInst()
{
if (!IsValid(m_This.Get()))
{
m_This = MakeShareable(NewObject<UEffectMgr>());
}
return m_This.Get();
}
void UEffectMgr::CreateEffect(UWorld* _World, EEFFECT_TYPE _Type, ULevel* _Level, FVector _Location)
{
FActorSpawnParameters param = {};
param.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
param.OverrideLevel = _Level;
param.bDeferConstruction = true; // 지연생성(BeginPlay 호출 X)
FTransform trans;
trans.SetLocation(_Location);
AEffect_Base* pEffect = _World->SpawnActor<AEffect_Base>(AEffect_Base::StaticClass(), trans, param);
pEffect->SetEffectType(_Type);
pEffect->FinishSpawning(pEffect->GetTransform());
}
void ACharacter_Base::Fire(const FInputActionInstance& _Instance)
{
bool bToggle = _Instance.GetValue().Get<bool>();
//LOG(Player, Error, TEXT("!! Fire !!"));
if (!IsValid(GetMesh()->GetAnimInstance()) || FireMontage.IsNull())
{
return;
}
if (bToggle && !IsReload)
{
GetMesh()->GetAnimInstance()->Montage_Play(FireMontage.LoadSynchronous(), 2.f);
GetMesh()->GetAnimInstance()->Montage_JumpToSection(FName("Fire"), FireMontage.LoadSynchronous());
IsFire = true;
// Explode Effect 재생해보기
// 이전 방식 ( 월드에 붙였다 뗐다 )
//UEffectMgr::GetInst(GetWorld())->CreateEffect(EEFFECT_TYPE::EXPLODE, GetLevel(), GetActorLocation() + GetActorForwardVector() * 100);
// 새로운 방식
UGameInstance_Base* pGameInst = Cast<UGameInstance_Base>(UGameplayStatics::GetGameInstance(GetWorld()));
pGameInst->GetEffectMgr()->CreateEffect(GetWorld(), EEFFECT_TYPE::EXPLODE, GetLevel(), GetActorLocation() + GetActorForwardVector() * 100);
// 새로운 방식 2
UEffectMgr::GetInst()->
}
else
{
IsFire = false;
}
}
충돌
1. 오브젝트 기반의 충돌 (이전에 제작)
Object Channel
스폰된 객체가 어느 그룹에 속하는지 정해주고 충돌 프로파일 (프리셋, 미리 작성해둠) 제작
2. 추적 채널 ( Trace 기반 ) 충돌
편집 -> 프로젝트 셋팅 -> 콜리전 -> 트레이스 채널 , 프리샛 -> visibility( 가시성 ), camera 이미 엔진 내부에서 만들어져 사용 중
기존 프로파일은 상대방 오브젝ㅌ트 어디에 속해있는지 따라 서로 어떻게 반응할지 미리 정의를 해주어야 했다. (항상 발동)
트레이스는 주변을 탐색할 때 사용한다 (한번 발동)
ex)
visibility, camera 에 블록 check 가 되어 있다. => 사용 한다는 의미
광선을 발사하면서 camera 채널을 붙여 트래이스를 하면 내가 쏘는 광선방향으로 누가 있으면? 걸린다?
소바 e 스킬느낌.. 아니면 사이퍼 덫 느낌?
언제 사용 중인지 ?
1. visibility 채널
-> 랜더링 할 때 많이 활용
앞에 있는게 랜더링되면 뒤에 있는게 가려져서 안보이니까 랜더링할 필요가 없는걸 판단
2. camera 채널
-> 벽에 가로막혀 있을 때? 벽이 카메라 채널에 반응하게 되어있다면 카메라의 위치를 당긴다
-> 무시로 되어있다면, 물체의 매쉬를 통과한다
자체적인 Trace 채널 만들기
편집 -> 프로젝트 셋팅 -> 콜리전 -> 트레이스 채널 -> 새 트레이스 채널 -> 이름 : PlayerAttack, 기본 반응 : Ignore -> 몬스터만 해당 trace에 반응하게 블록 check
ex ) 특정 순간에만 반응하게 or 어느 순간에만 반응하게 ( animnotify 에서 공격시작, 끝을 bool 값 줘서 생성 )
Input -> 입력액션 -> IA_QSkill
IMC_Default -> IA_QSkill -> 키 = Q, 트리거 = 눌림
DA_PlayerSetting -> ATTACK 에 IA_QSkill 맵핑
* config -> DefaultEngine.ini 파일
쭉 내리다 보면 자동으로 PlayerAttack 은 ECC_GameTraceChannel5 라고 지정 해둠 ( 자동으로 부여된 enum 값 )
구 형태로 채널 검사
FColor color;
if (bCollision)
color = FColor::Red;
else
color = FColor::Green;
// 위의 if 문과 동일
bCollision ? color = FColor::Red : color = FColor::Green;
void ACharacter_Base::Attack(const FInputActionInstance& _Instance)
{
LOG(Player, Warning, TEXT("Q Skill"));
// 범위 안에 들어온 애들이 여러 명일 수 있다 -> 배열로 받아 둔다
TArray<FHitResult> ResultArray;
FCollisionQueryParams query(NAME_None, false, this);
// 시작과 끝 값을 GetActorLocation() 로 동일하게 한다.
// FQuat::Identity -> 무회전, 구는 회전해도 똑같아서 무회전 사용
bool bCollision = GetWorld()->SweepMultiByChannel(ResultArray
, GetActorLocation(), GetActorLocation()
, FQuat::Identity
, ECC_GameTraceChannel5 // PlayerAttack Trace Channel
, FCollisionShape::MakeSphere(200.f)
, query);
FColor color;
bCollision ? color = FColor::Red : color = FColor::Green;
// DrawDebugSphere -> 임시로 구를 그리는 함수
DrawDebugSphere(GetWorld(), GetActorLocation(), 200.f, 40, color, false, 2.f);
for (int32 i = 0; i < ResultArray.Num(); ++i)
{
// Explode Effect 재생해보기
UEffectMgr::GetInst()->CreateEffect(GetWorld(), EEFFECT_TYPE::EMP, GetLevel(), ResultArray[i].Location);
}
}
* HitResult에 Location과 ImpactPoint가 있고 약간의 차이가 있다.
Location은 충돌 대상과 trace shape가 접촉했을 때 trace shape의 원점 월드 좌표이다.
ImpactPoint는 충돌 대상과 trace shape가 접촉하는 월드 좌표이다.
https://docs.unrealengine.com/5.1/ko/traces-in-unreal-engine---overview/
수류탄 투사체 제작
Projectile 을 상속받은 Granade.h / Granade.cpp 생성
#include "CoreMinimal.h"
#include "Projectile.h"
#include "Granade.generated.h"
UCLASS()
class UNREAL_3TH_API AGranade : public AProjectile
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Info")
TSoftObjectPtr<USoundBase> m_Sound;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Info")
float m_LifeTime;
private:
float m_AccTime;
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
public:
AGranade();
~AGranade();
};
#include "Granade.h"
#include "../Effect/EffectMgr.h"
AGranade::AGranade()
: m_LifeTime(1.f)
, m_AccTime(0.f)
{
}
AGranade::~AGranade()
{
}
void AGranade::BeginPlay()
{
Super::BeginPlay();
}
void AGranade::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
m_AccTime += DeltaTime;
if (m_LifeTime < m_AccTime)
{
// 이펙트 재생
CEffectMgr::GetInst()->CreateEffect(GetWorld(), EEFFECT_TYPE::EXPLODE, GetLevel(), GetActorLocation());
// 사운드 재생
if (!m_Sound.IsNull())
{
UGameplayStatics::PlaySoundAtLocation(GetWorld(), m_Sound.LoadSynchronous(), GetActorLocation());
}
// 객체 삭제
Destroy();
}
}
투사체 상속받아서 쏘면 툭툭 떨어지는 수류탄 형태의 투사체를 생성
주변 원형으로 sweap? 체크해서 데미지 받은 몬스터에게 물리적인 힘을 줘서 밀어내면서 폭발시키는? 거 생성
class UNREAL_3TH_API ACharacter_Base : public ACharacter
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Input")
TSubclassOf<AProjectile> m_Granade;
}
void ACharacter_Base::Attack(const FInputActionInstance& _Instance)
{
// 수류탄 쏘기
if (IsValid(m_Granade))
{
FActorSpawnParameters param = {};
param.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;
param.OverrideLevel = GetLevel();
param.bDeferConstruction = false; // 지연생성(BeginPlay 호출 X)
// 카메라 위치 + 카메라 전방방향 * 10미터
FVector CamForwardPos = m_Cam->GetComponentLocation() + m_Cam->GetForwardVector() * 1000;
// 투사체 생성위치
FVector ProjectileLocation = GetMesh()->GetSocketLocation(FName(TEXT("Light02")));
// 투사체 위치에서 카메라 전방 10미터 위치를 향하는 방향벡터 구하기
FVector vDir = CamForwardPos - ProjectileLocation;
vDir.Normalize();
ProjectileLocation += vDir * 50.f;
AProjectile* pProjectile = GetWorld()->SpawnActor<AProjectile>(m_Granade, ProjectileLocation, FRotator(), param);
pProjectile->m_ProjtileMovement->Velocity = vDir * 500.f;
}
}
예제 영상
사운드
사운드에 공간감을 주려면 ? (현재는 거리가 멀어져도 똑같은 크기로 들림)
순수 사운드 리소스 타입 => 사운드 웨이브
클래스화 시켰을 때는 SoundBase 로 가리키게 되어있다.
블루프린트
사운드->사운드큐
어테뉴에이션 -> 감쇠
사운드->사운드 어테뉴에이션
내부반경 -> 소리 최대치가 들릴 수 있는 거리
정해진 거리까지 감쇠해서 들리다가 그 거리를 벗어나면 아예 안들리게 된다.
* .USoundCue 사용하려면 #include "Sound/SoundCue.h" 참조해야한다.
#include "Sound/SoundCue.h"
class UNREAL_3TH_API AGranade : public AProjectile
{
GENERATED_BODY()
public:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Info")
TSoftObjectPtr<USoundCue> m_Sound;
}
예제 영상
사운드 큐를 따로 제작하지 않고 c++ 에서 조합하는 것도 가능하다.
UGameplayStatics::PlaySoundAtLocation() 괄호 안에서 표현 가능
언리얼에서 제공하는 "앰비언트 사운드"
actor 상속받아서 sound 제작할 수 있는 sound component 들고 있다
'언리얼 5' 카테고리의 다른 글
230913 enum 찾기 / 카메라 진동 / 몬스터 인공지능 (0) | 2023.09.13 |
---|---|
230912 무기 구현 / 몬스터 AI (0) | 2023.09.12 |
230907 투사체 수정 / 카메라가 바라보는 방향으로 spine_01 뼈 회전 / 충돌 설정 / 몬스터 생성 (0) | 2023.09.11 |
230906 Effect_Base 구조 변경 / EffectMgr 싱글톤 / 투사체 생성 (0) | 2023.09.11 |
230905 애니메이션, 리로드 노티파이 / 파티클 이펙트 / Effect_Base (0) | 2023.09.09 |