본문 바로가기
Study/Unreal

[Unreal/정리] 충돌 / 파티클

by generous_jeans 2020. 10. 18.

[ 충돌 ]

(언리얼 에디터) 세팅 -> 프로젝트 세팅 -> 엔진-콜리전 

// Object Channels : 오브젝트에 관한 충돌처리.

// Trace Channels : 카메라와 플레이어를 잇는 직선 사이에 부딪히는 물체를 처리.

// Preset : 프로파일

 

// 충돌체들마다 사용하는 프로파일을 가지게 됨. 

 

// 프로파일-BlockAll : 모든 물체와 블록 처리를 함. 

 

// 프로파일-콜리전 켜짐-No Collision : 충돌 안함. 

// 프로파일-콜리전 켜짐-Query Only(No Physics Collision) 

// 프로파일-콜리전 켜짐-Physics Only(No Query Collision) : 물리적 처리만. 

// 프로파일-콜리전 켜짐-Collision Enabled(Query and Physics) : 둘 다 처리.

 

// 프로파일-오브젝트 유형 : 이 프로파일이 기본으로 사용하는 채널.

 

// 프로파일-콜리전 반응-무시

// 프로파일-콜리전 반응-겹침 : 뚫고 지나갈 수 있음.

// 프로파일-콜리전 반응-블록 : 뚫고 지나가지 못함. 

 

// 프로파일-트레이스 유형 : 다른 채널과 충돌처리를 어떻게 할 것인지 설정.

 

// C++ -> Config -> DefaultEngine.ini : 에디터의 설정 정보들이 들어가는 곳.

 

(언리얼 에디터) 세팅 -> 프로젝트 세팅 -> 엔진-콜리전 -> Object Channels -> 새 오브젝트 채널-이름 : "PlayerAttack" 기본 반응 : "Ignore"

(언리얼 에디터) 세팅 -> 프로젝트 세팅 -> 엔진-콜리전 -> Object Channels -> 새 오브젝트 채널-이름 : "Player" 기본 반응 : "Block"

(언리얼 에디터) 세팅 -> 프로젝트 세팅 -> 엔진-콜리전 -> Object Channels -> 새 오브젝트 채널-이름 : "Monster" 기본 반응 : "Block"

// Preset이 모두 블록으로 표시됨.

 

(언리얼 에디터) 세팅 -> 프로젝트 세팅 -> 엔진-콜리전 -> Preset -> 새 프로파일-이름 : "Player" 콜리전 켜짐 : "Collision Enabled" 오브젝트 유형 : "Player" 오브젝트 유형 : "PlayerAttack : 무시  / Player, Monster : 겹침 / 나머지 : 블록"

(언리얼 에디터) 세팅 -> 프로젝트 세팅 -> 엔진-콜리전 -> Preset -> 새 프로파일-이름 : "Monster" 콜리전 켜짐 : "Collision Enabled" 오브젝트 유형 : "Monster" 오브젝트 유형 : "PlayerAttack, Player, Monster : 겹침 / 나머지 : 블록"

// 몬스터끼리 겹칠 때 해결방법 - 집단행동 알고리즘 사용. (일정 반경안에 부딪히지 않는 위치를 파악)

// 또는 "겹침" 사용. 

(언리얼 에디터) 세팅 -> 프로젝트 세팅 -> 엔진-콜리전 -> Preset -> 새 프로파일-이름 : "PlayerAttack" 콜리전 켜짐 : "Collision Enabled" 오브젝트 유형 : "PlayerAttack" 오브젝트 유형 :  "PlayerAttack : 무시 / Monster : 겹침 / 나머지 : 블록"

 

(언리얼 에디터) Preset -> BlockAll -> 오브젝트 유형 : "PlayerAttack : 무시  / 나머지 : 블록"

(언리얼 에디터) Preset -> OverlapAll -> 오브젝트 유형 : "PlayerAttack : 무시  / 나머지 : 겹침"

(언리얼 에디터) Preset -> OverlapAllDynamic -> 오브젝트 유형 : "PlayerAttack : 무시  / 나머지 : 겹침"

(언리얼 에디터) Preset -> IgnoreOnlyPawn -> 오브젝트 유형 : "Pawn, Vehicle, PlayerAttack, Player, Monster : 무시  / 나머지 : 블록"

(언리얼 에디터) Preset -> OverlapOnlyPawn -> 오브젝트 유형 : "PlayerAttack : 무시  / Pawn, Vehicle, Player, Monster : 겹침 / 나머지 : 블록"

(언리얼 에디터) Preset -> OverlapAll -> 오브젝트 유형 : "PlayerAttack : 무시  / 나머지 : 겹침"

 

- 충돌체 만들기 - 

(언리얼 에디터-BPPlayerWukong) Mesh -> 디테일-콜리전-Can Character Step Up On ... : "No"

(언리얼 에디터-BPPlayerWukong) WeaponBox -> 디테일-콜리전-Can Character Step Up On ... : "No"

// Simulation Genertates Hit Events : hit 이벤트를 발생시킬 것인지. 

// Generate Overlap Events : 겹침 처리를 할 때. 두 오브젝트 모두 true로 설정되면 오버랩으로 등록됨.

// Phys Material Override : 어떤 재질인지 구분하기 위해. 물리적인 처리를 하기 위한 재질. 

 

- 충돌 체크 - 

(언리얼 에디터-BPWukongAnim) Ground -> Ground -> NormalAttack -> Attack1

(언리얼 에디터-BPWukongAnim) Primary_Melee_A_Slow

(언리얼 에디터-Primary_Melee_A_Slow) 원하는 위치_노티파이 부분_우클릭_노티파이 추가-새 노티파이 : "AttackStart"

 

(언리얼 에디터-BPWukongAnim) Ground -> Ground -> NormalAttack -> Attack2

(언리얼 에디터-BPWukongAnim) Primary_Melee_B_Slow

(언리얼 에디터-Primary_Melee_B_Slow) 원하는 위치_노티파이 부분_우클릭_노티파이 추가-새 노티파이 : "AttackStart"

 

(언리얼 에디터-BPWukongAnim) Ground -> Ground -> NormalAttack -> Attack3

(언리얼 에디터-BPWukongAnim) Primary_Melee_C_Slow

(언리얼 에디터-Primary_Melee_C_Slow) 원하는 위치_노티파이 부분_우클릭_노티파이 추가-새 노티파이 : "AttackStart"

 

(언리얼 에디터-BPWukongAnim) Ground -> Ground -> NormalAttack -> Attack4

(언리얼 에디터-BPWukongAnim) Primary_Melee_D_Slow

(언리얼 에디터-Primary_Melee_D_Slow) 원하는 위치_노티파이 부분_우클릭_노티파이 추가-새 노티파이 : "AttackStart"

 

(언리얼 에디터-BPWukongAnim) Ground -> Ground -> NormalAttack -> Attack5

(언리얼 에디터-BPWukongAnim) Primary_Melee_E_Slow

(언리얼 에디터-Primary_Melee_E_Slow) 원하는 위치_노티파이 부분_우클릭_노티파이 추가-새 노티파이 : "AttackStart"

 

(언리얼 에디터-BPWukongAnim) Ground -> Ground -> NormalAttack -> Attack5

(언리얼 에디터-BPWukongAnim) Primary_Melee_E_Slow

(언리얼 에디터-Primary_Melee_E_Slow) 원하는 위치_노티파이 부분_우클릭_노티파이 추가-새 노티파이 : "AttackEnd"

 

- 로그 -

(언리얼 에디터) 창 -> 개발자 툴-출력 로그

 

- 데미지 처리 -

// BPMinionAnim

(언리얼 에디터-BPMinionAnim) Ground -> Idle : "Ground"로 바꿈.

(언리얼 에디터-BPMinionAnim) Ground -> Ground

(언리얼 에디터-BPMinionAnim) 우클릭_"Get Anim Type" -> "블렌드 포즈(EMinionAnim)" -> 우클릭_"Idle" "Run" "Attack" "Hit" "Death" 추가

(언리얼 에디터-BPMinionAnim) 블렌드 포즈(EMinionAnim)와 출력 애니메이션 포즈 Result와 연결

 

(언리얼 에디터-BPMinionAnim) 에셋 브라우저 : "Idle_A" -> Idle_A와 디폴트 포즈 연결 -> 디테일-세팅-Loop Animation : "끔"

(언리얼 에디터-BPMinionAnim) 에셋 브라우저 : "Idle_A" -> Idle_A와 Idle 포즈 연결 -> 디테일-세팅-Loop Animation : "끔" 

(언리얼 에디터-BPMinionAnim) 에셋 브라우저 : "Jog_Fwd_Combat_A" -> Jog_Fwd_Combat_ARun 포즈 연결 -> 디테일-세팅-Loop Animation : "끔"

(언리얼 에디터-BPMinionAnim) 에셋 브라우저 : "Attack_A" -> Attack_AAttack 포즈 연결 -> 디테일-세팅-Loop Animation : "끔"

(언리얼 에디터-BPMinionAnim) 에셋 브라우저 : "HitReact_Front" -> HitReact_FrontHit 포즈 연결 -> 디테일-세팅-Loop Animation : "끔"

(언리얼 에디터-BPMinionAnim) 에셋 브라우저 : "Death_A" -> Death_ADeath 포즈 연결 -> 디테일-세팅-Loop Animation : "끔"

 

// BPMinionRangerAnim

(언리얼 에디터-BPMinionRangerAnim) Ground -> Idle : "Ground"로 바꿈.

(언리얼 에디터-BPMinionRangerAnim) Ground -> Ground

(언리얼 에디터-BPMinionRangerAnim) 우클릭_"Get Anim Type" -> "블렌드 포즈(EMinionAnim)" -> 우클릭_"Idle" "Run" "Attack" "Hit" "Death" 추가

(언리얼 에디터-BPMinionRangerAnim) 블렌드 포즈(EMinionRangerAnim)와 출력 애니메이션 포즈 Result와 연결

 

(언리얼 에디터-BPMinionRangerAnim) 에셋 브라우저 : "Idle_A" -> Idle_A와 디폴트 포즈 연결 -> 디테일-세팅-Loop Animation : "끔"

(언리얼 에디터-BPMinionRangerAnim) 에셋 브라우저 : "Idle_A" -> Idle_A와 Idle 포즈 연결 -> 디테일-세팅-Loop Animation : "끔" 

(언리얼 에디터-BPMinionRangerAnim) 에셋 브라우저 : "Jog_Fwd_Combat_A" -> Jog_Fwd_Combat_A Run 포즈 연결 -> 디테일-세팅-Loop Animation : "끔"

(언리얼 에디터-BPMinionRangerAnim) 에셋 브라우저 : "Attack_A" -> Attack_A Attack 포즈 연결 -> 디테일-세팅-Loop Animation : "끔"

(언리얼 에디터-BPMinionRangerAnim) 에셋 브라우저 : "HitReact_Front" -> HitReact_Front Hit 포즈 연결 -> 디테일-세팅-Loop Animation : "끔"

(언리얼 에디터-BPMinionRangerAnim) 에셋 브라우저 : "Death_A" -> Death_A Death 포즈 연결 -> 디테일-세팅-Loop Animation : "끔"

 

// 디테일-Can be Damaged : false로 되어있으면, 부모에서 최종 데미지의 값이 0으로 나옴.

 

(언리얼 에디터-BPPlayerWukongWeaponBox -> 디테일-콜리전-Collision Presets : "Custom" Collision Enabled : "No Collision"

(언리얼 에디터-BPPlayerWukongWeaponBox -> 디테일-콜리전-Collision Presets : "PlayerAttack"

 

(언리얼 에디터-BPPlayerWukongWeaponBox -> 이벤트-On Component Hit 

(언리얼 에디터-BPPlayerWukong) 우클릭_Print String -> On Component Hit(WeaponBox)와 연결

 

(언리얼 에디터-BPMinionsCapsuleComponent -> 디테일-피직스-Simulate Physics : "체크"

 

// 스태틱 메쉬 만들기.

(언리얼 에디터) 콘텐츠 -> InfinityBladeWrapons -> Weapons ->  Blade -> Swords -> Blade_BlackKnight ->  SK_Blade_BlasckKinght

(언리얼 에디터-SK_Blade_BlasckKinght) 스태틱 메쉬 만들기 -> 위치 : "Meshes" 이름 : "BladeMesh"

 

- 충돌 체크 버그 해결 - 

(언리얼 에디터-BPPlayerWukong) WeaponBox -> 디테일-Shape-Box Extent-X : "120.0" Y : "10.0" Z : "10.0"

(언리얼 에디터-BPPlayerWukong) WeaponBox -> 디테일-콜리전-Collision Presets : "PlayerAttack"

 

[ 파티클 ]

(에픽 게임즈 런처) "Infinity Blade:Effects" 프로젝트에 추가

 

(언리얼 에디터) 신규 추가-새폴더 : "Effect"

(언리얼 에디터) C++클래스 -> 우클릭_새 C++클래스 추가 -> 부모 클래스 : "Actor" -> 이름 : "EffectDestroy"

 

[ 소리 ]

언리얼에서 기본적으로 제공하는 오디오 포맷은 wav파일. 

 

프로젝트 파일 -> Content -> Effect에 파일 넣어서 사용.

 

(C++-PlayerWukong.h)

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

#pragma once

#include "PlayerCharacter.h"
#include "PlayerWukong.generated.h"

/**
 * 
 */
UCLASS()
class PROJECT1005_API APlayerWukong : public APlayerCharacter
{
	GENERATED_BODY()
	
public:
	// Sets default values for this character's properties
	APlayerWukong();

protected:
	// UPROPERTY : 사용하지 않으면 언리얼 에디터에 공개가 되지 않음. 내부적인 변수로 사용할 수 있음.
	//             언리얼 에디터에 연결하여 사용하려면 변수를 선언하기 전에 프로퍼티로 변수의 속성을 지정해줘야 함.
	// Category : Camera로 설정. 
	// VisibleAnywhere : 다 보이지만 편집을 할 수 없음.
	// EditAnywhere : 에디터에서도 편집 가능.
	UPROPERTY(Category = Camera, VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
	UCameraComponent* Camera;

	UPROPERTY(Category = Camera, VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
	USpringArmComponent* Arm;

	UPROPERTY(Category = Collision, VisibleAnywhere, BlueprintReadOnly, meta = (AllowPrivateAccess = "true"))
	UBoxComponent* WeaponBox;

	class UWukongAnim* WukongAnim;  // 포인터 변수 선언. 

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	// Called to bind functionality to input
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
	virtual float TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent, class AController* EventInstigator, AActor* DamageCauser);

private:
	void MoveFront(float fScale);  // 앞뒤로 이동.
	void MoveSide(float fScale);  // 좌우로 이동.
	void RotationZ(float fScale);  // 회전.

	void JumpKey();  // 점프.
	void NormalAttack();  // 공격.

	void QuickSlot1();
	void QuickSlot2();
	void QuickSlot3();

public:
	void EnableWeaponCollision(bool bEnable);

public:
	virtual void NotifyHit(class UPrimitiveComponent* MyComp, AActor* Other, class UPrimitiveComponent* OtherComp, bool bSelfMoved, FVector HitLocation, FVector HitNormal, FVector NormalImpulse, const FHitResult& Hit);

	UFUNCTION()
	void OnHit(UPrimitiveComponent* HitCom, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& result);

	UFUNCTION()
	void WeaponBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);

	UFUNCTION()
	void WeaponEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
};

(C++-PlayerWukong.cpp)

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


#include "PlayerWukong.h"
#include "WukongAnim.h" 
#include "Weapon.h"
#include "EffectDestroy.h"
#include "DrawDebugHelpers.h"  // 디버깅 용도의 출력 기능. 

// Sets default values
APlayerWukong::APlayerWukong()
{
	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera"));  // 카메라 컴포넌트 생성. 생성자에서 생성해야 함. 
	Arm = CreateDefaultSubobject<USpringArmComponent>(TEXT("Arm"));  // Sprint Arm 컴포넌트 생성.

	// 셀카봉 같은 느낌. 
	Arm->SetupAttachment(GetMesh());  // Arm의 부로모 Mesh를 지정.
	Camera->SetupAttachment(Arm);  // 카메라의 부모로 Arm을 지정. 

	// 애니메이션 클래스 지정. 
	static ConstructorHelpers::FClassFinder<UWukongAnim> AnimAsset(TEXT("AnimBlueprint'/Game/Player/BPWukongAnim.BPWukongAnim_C'"));

	if (AnimAsset.Succeeded())
		GetMesh()->SetAnimInstanceClass(AnimAsset.Class);

	Weapon = nullptr;

	// 점프의 속도 조절. 
	// GetCharacterMovement()->JumpZVelocity  // JumpZVelocity : 위로 올라가는 속도. 

	// 원하는 프로파일 세팅.
	GetCapsuleComponent()->SetCollisionProfileName(TEXT("Player"));

	WeaponBox = CreateDefaultSubobject<UBoxComponent>(TEXT("WeaponBox"));

	WeaponBox->SetupAttachment(GetMesh(), TEXT("FX_Staff_Mid"));

	WeaponBox->SetCollisionProfileName(TEXT("PlayerAttack"));

	WeaponBox->SetBoxExtent(FVector(120.f, 10.f, 10.f));  // 박스의 크기 설정. 

}

// Called when the game starts or when spawned
void APlayerWukong::BeginPlay()
{
	Super::BeginPlay(); // 이 액터가 레벨에 입장할 때 배치되고 게임이 시작할 때 딱 한 번만 호출됨.

	WukongAnim = Cast<UWukongAnim>(GetMesh()->GetAnimInstance());  // WraithAnim을 받아옴. 

	// 기본 무기 설정. 
	// GetWorld() : 월드 객체를 얻어올 수 있음. 액터를 월드에 배치할 때 사용. 

	// AlwaysSpawn : 무조건 생성.
	// AdjustIfPossibleButAlwaysSpawn : 충돌되었을 때 충돌되지 않는 위치를 찾아 스폰하는 것.
	// AdjustIfPossibleButDontSpawnIfColliding : 충돌안될 때만 스폰.
	FActorSpawnParameters params;
	params.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;
	
	Weapon = GetWorld()->SpawnActor<AWeapon>(FVector::ZeroVector, FRotator::ZeroRotator, params);

	// AttachToActor : 지정된 액터의 루트 컴포넌트에 붙게 됨.
	// AttachToComponent : 원하는 컴포넌트 지정 가능. 

	// KeepRelativeTransform : 상대적인 정보를 그대로 유지. 
	// KeepWorldTransform : 월드 정보를 그대로 유지.
	Weapon->AttachToComponent(GetMesh(), FAttachmentTransformRules::KeepRelativeTransform, TEXT("weapon_l_socket"));

	Weapon->LoadMesh(TEXT("SkeletalMesh'/Game/InfinityBladeWeapons/Weapons/Blade/Swords/Blade_BlackKnight/SK_Blade_BlackKnight.SK_Blade_BlackKnight'"));

	// 겹침이 시작될 때 1번만 호출.
	WeaponBox->OnComponentBeginOverlap.AddDynamic(this, &APlayerWukong::WeaponBeginOverlap);
	// 겹침이 되다가 빠져나갈 때 호출. 
	WeaponBox->OnComponentEndOverlap.AddDynamic(this, &APlayerWukong::WeaponEndOverlap);

	// WeaponBox->OnComponentHit.AddDynamic(this, &APlayerWukong::OnHit);

	WeaponBox->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}

// Called every frame
void APlayerWukong::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);  // 게임이 동작되는 매프레임마다 호출되는 함수.
}

// Called to bind functionality to input
// UInputComponent : 입력받은 것을 연결해서 호출해줄 함수를 연결하는 바인딩 역할을 함. 
void APlayerWukong::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	Super::SetupPlayerInputComponent(PlayerInputComponent);  // 플레이어 컨트롤러가 빙의가 된 후에 호출. 

	// 언리얼은 유니코드 문자열을 사용하기 때문에 TEXT("")를 사용해야 함. 
	// this : 어떤 객체를 쓸 것인지.
	PlayerInputComponent->BindAxis(TEXT("MoveFront"), this, &APlayerWukong::MoveFront);
	PlayerInputComponent->BindAxis(TEXT("MoveSide"), this, &APlayerWukong::MoveSide);
	PlayerInputComponent->BindAxis(TEXT("RotationZ"), this, &APlayerWukong::RotationZ);

	// EInputEvent::IE_Pressed : 눌렀을 때.
	// EInputEvent::IE_Released : 눌렀다가 뗐을 때.
	PlayerInputComponent->BindAction(TEXT("Jump"), EInputEvent::IE_Pressed, this, &APlayerWukong::JumpKey);
	PlayerInputComponent->BindAction(TEXT("NormalAttack"), EInputEvent::IE_Pressed, this, &APlayerWukong::NormalAttack);

	PlayerInputComponent->BindAction(TEXT("QuickSlot1"), EInputEvent::IE_Pressed, this, &APlayerWukong::QuickSlot1);
	PlayerInputComponent->BindAction(TEXT("QuickSlot2"), EInputEvent::IE_Pressed, this, &APlayerWukong::QuickSlot2);
	PlayerInputComponent->BindAction(TEXT("QuickSlot3"), EInputEvent::IE_Pressed, this, &APlayerWukong::QuickSlot3);

}

float APlayerWukong::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
	float fDamage = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);

	return fDamage;
}

void APlayerWukong::MoveFront(float fScale)  // 앞뒤로 이동. 
{
	if (!bStartAnimation)
		return;

	// GetActorForwardVector()와 fScale을 곱함.
	// fScale이 0이면 움직이지 않고, 1이면 앞으로, -1이면 뒤로 가게 됨. 
	AddMovementInput(GetActorForwardVector(), fScale);

	if (IsValid(WukongAnim))  // AnimInstance가 있을 때.
	{
		if (fScale > 0.f)  // 앞으로. 
			WukongAnim->SetMoveDir(EDir::Front);
		else if (fScale < 0.f)  // 뒤로. 
			WukongAnim->SetMoveDir(EDir::Back);
	}
}

void APlayerWukong::MoveSide(float fScale)  // 좌우로 이동. 
{
	if (!bStartAnimation)
		return;

	// GetActorRightVector()와 fScale을 곱함.
	// fScale이 0이면 움직이지 않고, 1이면 오른쪽으로, -1이면 왼쪽으로 가게 됨. 
	AddMovementInput(GetActorRightVector(), fScale);

	if (IsValid(WukongAnim))  // AnimInstance가 있을 때.
	{
		if (fScale > 0.f)  // 오른쪽으로. 
			WukongAnim->SetMoveDir(EDir::Right);
		else if (fScale < 0.f)  // 왼쪽으로. 
			WukongAnim->SetMoveDir(EDir::Left);
	} 
}

void APlayerWukong::RotationZ(float fScale)  // 회전. 
{
	if (!bStartAnimation)
		return;

	AddControllerYawInput(fScale);
}

void APlayerWukong::JumpKey()  // 점프.
{
	if (!bStartAnimation)
		return;

	if (IsValid(WukongAnim))
		WukongAnim->Jump();

	Jump(); // Jump를 지원해줌.  
}

void APlayerWukong::NormalAttack()  // 공격.
{
	if (!bStartAnimation)
		return;

	if (IsValid(WukongAnim))
		WukongAnim->NormalAttack();

}

void APlayerWukong::QuickSlot1()
{
	if (!bStartAnimation)
		return;

	if (IsValid(WukongAnim))
		WukongAnim->Skill1();
}

void APlayerWukong::QuickSlot2()
{
	if (!bStartAnimation)
		return;
}

void APlayerWukong::QuickSlot3()
{
	if (!bStartAnimation)
		return;
}

void APlayerWukong::EnableWeaponCollision(bool bEnable)
{
	if (bEnable)
		WeaponBox->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
	else
		WeaponBox->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}

void APlayerWukong::NotifyHit(class UPrimitiveComponent* MyComp, AActor* Other, class UPrimitiveComponent* OtherComp, bool bSelfMoved, FVector HitLocation, FVector HitNormal, FVector NormalImpulse, const FHitResult& Hit)
{
	LOG(TEXT("Name : %s"), *MyComp->GetName());
}

void APlayerWukong::OnHit(UPrimitiveComponent* HitCom, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& result)
{
	LOG(TEXT("Name : %s"), *OtherComp->GetName());
}

void APlayerWukong::WeaponBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	// UE_LOG(Project1005, Warning, TEXT("BeginOverlap"));
	// LOG(TEXT("BeginOverlap"));

	FDamageEvent DamageEvent;
	float fDamage = OtherActor->TakeDamage(10.f, DamageEvent, GetController(), this);

	// 충돌감지를 위한 소켓의 위치정보를 얻어옴. 
	FVector vStart = GetMesh()->GetSocketLocation(TEXT("FX_Staff_Tip_B"));
	FVector vEnd = GetMesh()->GetSocketLocation(TEXT("FX_Staff_Tip_A"));

	FVector vDir = vEnd - vStart;
	vDir.Normalize();

	FCollisionQueryParams tColParams(NAME_None, false, this);
	// TArray : 언리얼 엔진에서 제공하는 배열. STL의 vector처럼 만들어짐. 
	TArray<FHitResult> HitArray;
	// SweepSingleBy : 직선과 부딪히는 것을 만듦.
	// SweepMultiBy : 부딪히는 것 여러 개 중에서 오버랩이랑 같을 것을 찾음. 
	bool bCollision = GetWorld()->SweepMultiByProfile(HitArray, vStart, vEnd, FQuat::Identity, TEXT("PlayerAttack"), FCollisionShape::MakeBox(FVector(120.f, 10.f, 10.f)), tColParams);

	FVector vImpactPoint = FVector::ZeroVector;  // ZeroVector로 초기화.
	FVector vImpactNormal = FVector::ZeroVector;  // ZeroVector로 초기화.

	LOG(TEXT("Hit Count : %d"), HitArray.Num());
	LOG(TEXT("Other Actor Name : %s"), *OtherActor->GetName());

	// Num()함수는 현재 array가 가지고 있는 갯수를 반환해줌. 
	// 아래 for문과 같음. 
	/*for (int32 i = 0; i < HitArray.Num(); ++i)
	{
	}*/
	for (auto hit : HitArray)
	{
		LOG(TEXT("Hit Actor Name : %s"), *hit.GetActor()->GetName());

		if (hit.GetActor() == OtherActor)  // 같을 경우, 부딪쳤을 경우
		{
			vImpactPoint = hit.ImpactPoint;
			vImpactNormal = hit.ImpactNormal;

			break;
		}
	}

#if ENABLE_DRAW_DEBUG
	FVector vCenter = (vStart + vEnd) / 2.f;  // Center는 start와 end 사이. 

	FColor DrawColor = HitArray.Num() > 0 ? FColor::Red : FColor::Green;  // 충돌되면 빨강, 충돌 안되면 초록. 

	DrawDebugBox(GetWorld(), vCenter, FVector(120.f, 10.f, 10.f), vDir.ToOrientationQuat(), DrawColor, false, 2.f);

#endif // ENABLE_DRAW_DEBUG 

	// 파티클
	FActorSpawnParameters tParams;
	tParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;

	AEffectDestroy* Effect = GetWorld()->SpawnActor<AEffectDestroy>(vImpactPoint, vImpactNormal.Rotation(), tParams);

	Effect->SetEffectPath(TEXT("ParticleSystem'/Game/InfinityBladeEffects/Effects/FX_Skill_Leap/P_Skill_Leap_Fire_Crushing_Impact.P_Skill_Leap_Fire_Crushing_Impact'"), TEXT("SoundWave'/Game/Effect/Lightsaber.Lightsaber'"));
	Effect->SetActorScale3D(FVector(0.5f, 0.5f, 0.5f));
}

void APlayerWukong::WeaponEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex) 
{

}

(C++-Monster.cpp)

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


#include "Monster.h"

// Sets default values
AMonster::AMonster()
{
 	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	SpawnPoint = nullptr;

	// 원하는 프로파일 세팅.
	GetCapsuleComponent()->SetCollisionProfileName(TEXT("Monster"));

	// Simulation Generate Hit를 체크한 것과 같음. 
	// GetCapsuleComponent()->SetNotifyRigidBodyCollision(true);

}

AMonster::~AMonster()  // 소멸자. 몬스터가 죽었을 때
{
	if (SpawnPoint)
		SpawnPoint->Respawn();
}

// Called when the game starts or when spawned
void AMonster::BeginPlay()
{
	Super::BeginPlay();
	
}

// Called every frame
void AMonster::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

(C++-GameInfo.h)

#pragma once

#include "EngineMinimal.h"  // 추가. 

UENUM(BlueprintType, Meta = (Bitflags)) 
enum class EDir : uint8  // 방향 설정.
{
	Front,
	Back, 
	Left, 
	Right, 
	Up,
	Down
};

DECLARE_LOG_CATEGORY_EXTERN(Project1005, Log, All);
#define	LOG_CALLINFO (FString(__FUNCTION__) + TEXT("{") + FString::FromInt(__LINE__) + TEXT("}"))  
// 어떤 Function의 몇 번째 줄. FromInt() : 숫자를 문자열로 바꿔주는 함수.
#define LOG(Format, ...) UE_LOG(Project1005, Warning, TEXT("%s : %s"), *LOG_CALLINFO, *FString::Printf(Format, ##__VA_ARGS__))
// (LOG_CALLINFO + 출력할 문자열) 출력. 

// TEXT("") : 2바이트 문자열을 만들어주는 매크로. 
// 여러 나라의 문자를 사용하기 위해서는 유니코드 문자열을 사용함. 

(C++-GameInfo.cpp)

#include "GameInfo.h"

DEFINE_LOG_CATEGORY(Project1005);

(C++-PlayerBaseAnim.h)

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

#pragma once

#include "GameInfo.h"  // CoreMinimal.h를 GameInfo.h로 바꿈. 
#include "Animation/AnimInstance.h"
#include "PlayerBaseAnim.generated.h"

UENUM(BlueprintType, Meta = (Bitflags)) // enum문을 사용할 때 사용.
enum class EPlayerBaseAnim : uint8  // uint8로 사용하겠다고 설정. 
{
	Idle,
	Run,
	Attack,
	Hit,
	Jump
};

UCLASS()
class PROJECT1005_API UPlayerBaseAnim : public UAnimInstance
{
	GENERATED_BODY()

public:
	UPlayerBaseAnim();  // 생성자.

protected:
	uint8* BaseAnimType;

	// 방향 설정. 
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true", Bitmask, BitmaskEnum = "EDir"))
	uint8 Dir;

	// 멈춰있는 지 확인. 
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
	bool MoveStop;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
	bool AttackEnable;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
	uint8 NextAttackIndex;

	UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
	bool IsGround;
		
	uint8 NextAttackMax;
	bool NextAttack;  // 다음 공격할 것인지.
	bool NextAttackEnable;  // 다음 공격을 할 것인지 말 것인지.
	bool NextAttackInputEnable;

public:
	void SetMoveDir(EDir eDir)
	{
		Dir = (uint8)eDir;
	}

public:
	// UAnimInstance에 가상함수로 정의되어 있으므로 재정의 하여 사용할 수 있음. 
	virtual void NativeInitializeAnimation();  // 초기화될 때 사용. 
	virtual void NativeUpdateAnimation(float DeltaSeconds);  // 매 프레임마다 생성. 

public:
	UFUNCTION()  // 노티파이를 호출하기 위해 사용. 언리얼 함수인 것을 알려줌. 
	void AnimNotify_MoveStop();  // AnimNotify_(노티파이 이름)으로 해야함.

	UFUNCTION()
	void AnimNotify_AttackEnd();  // AttackEnd 노티파이 추가. 

	UFUNCTION()
	void AnimNotify_InIdle();

	UFUNCTION()
	void AnimNotify_NextAttackEnd();   // NextAttackEnd 노티파이 추가. 

	UFUNCTION()
	void AnimNotify_ReturnIdle();  // jump후 idle로 돌아가는 노티파이.

	UFUNCTION()
	void AnimNotify_PlayerStart();  // 플레이어가 시작 모션을 끝내는 노티파이.

	UFUNCTION()
	void AnimNotify_AttackStart();

public:
	void NormalAttack();  // 공격. 
	void Jump();

public:
	virtual void InheritMoveStopNotify();
	virtual void InheritAttackStartNotify();
	virtual void InheritAttackEndNotify();
	virtual void InheritInIdleNotify();
	virtual void InheritNormalAttack();

};

(C++-PlayerBaseAnim.cpp)

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


#include "PlayerBaseAnim.h"
#include "PlayerCharacter.h"  // 추가.

UPlayerBaseAnim::UPlayerBaseAnim()  // 생성자.
{
	Dir = (uint8)EDir::Front;  // 기본으로  Front 설정. 

	MoveStop = true;  // 처음에는 멈춰있으므로 true.
	AttackEnable = true;  // 처음에 공격 가능. 
	NextAttackEnable = false;  // 처음에 다음 공격 불가능하게 설정. 
	NextAttack = false;
	NextAttackInputEnable = false;
	NextAttackMax = 0;
	NextAttackIndex = 0;
}

void UPlayerBaseAnim::NativeInitializeAnimation()  // 초기화될 때 사용. 
{
	Super::NativeInitializeAnimation();

	*BaseAnimType = (uint8)EPlayerBaseAnim::Idle;  
}

void UPlayerBaseAnim::NativeUpdateAnimation(float DeltaSeconds)  // 매 프레임마다 생성.
{
	Super::NativeUpdateAnimation(DeltaSeconds);

	// 언리얼에서는 Cast를 사용하여 형변환을 해야 함. 
	APlayerCharacter* pOwner = Cast<APlayerCharacter>(TryGetPawnOwner()); // 이 애니메이션 컴포넌트를 가지고 있는 폰을 얻어오는 함수. 

	if (IsValid(pOwner))  // IsValid : U오프젝트에 대한 유효성 검사를 해줌. 제대로 된 것인지 확인을 해줘서 더 안전함. 
	{
		UCharacterMovementComponent* pMovement = pOwner->GetCharacterMovement();  // Movement를 얻어옴. 

		if (IsValid(pMovement))
		{
			IsGround = pMovement->IsMovingOnGround();  // IsMovingOnGround() : 땅을 밟고 있는 지 판단.
			if (IsGround)  
			{
				// Velocity.Size() : 이동속도의 크기
				if (pMovement->Velocity.Size() > 0.f)  // 0보다 크면 움직임.
				{
					if (*BaseAnimType == (uint8)EPlayerBaseAnim::Attack)
						InheritAttackEndNotify();

					MoveStop = false;  // 이동 중에는 false.

					NextAttackInputEnable = false;
					NextAttackIndex = 0;
					NextAttack = false;
					*BaseAnimType = (uint8)EPlayerBaseAnim::Run;
				}
				else  // 0이면 움직이지 않음. 
				{
					MoveStop = true;  // 멈췄을 때 true.
				}
			}
			else
			{
				if (*BaseAnimType == (uint8)EPlayerBaseAnim::Attack)
					InheritAttackEndNotify();
			}
		}
	}
}

void UPlayerBaseAnim::AnimNotify_MoveStop()  // 노티파이.
{
	*BaseAnimType = (uint8)EPlayerBaseAnim::Idle;  // 동작이 끝나면 Idle로 돌아감. 

	InheritMoveStopNotify();
}

void UPlayerBaseAnim::AnimNotify_AttackEnd()  // 노티파이.
{
	*BaseAnimType = (uint8)EPlayerBaseAnim::Idle;  // 동작이 끝나면 Idle로 돌아감. 

	NextAttackInputEnable = false;
	NextAttackIndex = 0;
	NextAttack = false;

	InheritAttackEndNotify();
}

void UPlayerBaseAnim::AnimNotify_InIdle()  // 노티파이.
{
	AttackEnable = true;  // 공격 가능 상태로.

	InheritInIdleNotify();
}

void UPlayerBaseAnim::AnimNotify_NextAttackEnd()  // NextAttackEnd() 노티파이 추가. 
{
	if (NextAttack)
	{
		++NextAttackIndex;

		if (NextAttackIndex >= NextAttackMax)
			NextAttackIndex = 0;
		NextAttack = false;
	}
	else
	{
		NextAttackInputEnable = false;
	}

	InheritAttackEndNotify();
}

void UPlayerBaseAnim::AnimNotify_ReturnIdle()  // jump후 idle로 돌아가는 노티파이. 
{
	*BaseAnimType = (uint8)EPlayerBaseAnim::Idle;

	NextAttackInputEnable = false;
	NextAttackIndex = 0;
	NextAttack = false;
}

void UPlayerBaseAnim::AnimNotify_PlayerStart()  // 플레이어가 시작 모션을 끝내는 노티파이.
{
	APlayerCharacter* pOwner = Cast<APlayerCharacter>(TryGetPawnOwner()); // 이 애니메이션 컴포넌트를 가지고 있는 폰을 얻어오는 함수. 

	if (IsValid(pOwner))
		pOwner->StartAnimation();
}

void UPlayerBaseAnim::AnimNotify_AttackStart()
{
	InheritAttackStartNotify();
}

void UPlayerBaseAnim::NormalAttack()  // 공격.
{
	if (AttackEnable)
	{
		*BaseAnimType = (uint8)EPlayerBaseAnim::Attack;

		if (NextAttackEnable && NextAttackInputEnable)
		{
			NextAttack = true;
		}
		else if(!NextAttackEnable)
			AttackEnable = false;  // 공격 불가능 상태로.

		NextAttackInputEnable = true;
	}

	InheritNormalAttack();
}

void UPlayerBaseAnim::Jump()
{
	*BaseAnimType = (uint8)EPlayerBaseAnim::Jump;
}

void UPlayerBaseAnim::InheritMoveStopNotify()
{

}

void UPlayerBaseAnim::InheritAttackStartNotify()
{

}

void UPlayerBaseAnim::InheritAttackEndNotify()
{

}

void UPlayerBaseAnim::InheritInIdleNotify()
{

}

void UPlayerBaseAnim::InheritNormalAttack()
{

}

(C++-WukongAnim.h)

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

#pragma once

#include "PlayerBaseAnim.h"
#include "WukongAnim.generated.h"

UENUM(BlueprintType, Meta = (Bitflags)) // enum문을 사용할 때 사용.
enum class EWukongAnim : uint8  // uint8로 사용하겠다고 설정. 
{
	Idle,
	Run,
	Attack,
	Hit,
	Jump
};

UCLASS()
class PROJECT1005_API UWukongAnim : public UPlayerBaseAnim
{
	GENERATED_BODY()
		
public:
	UWukongAnim();

protected:
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true", Bitmask, BitmaskEnum = "EWukongAnim"))
	uint8 AnimType;

	UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true", Bitmask, BitmaskEnum = "EWukongAnim"))
	UAnimMontage* Skill1Montage;

public:
	// UAnimInstance에 가상함수로 정의되어 있으므로 재정의 하여 사용할 수 있음. 
	virtual void NativeInitializeAnimation();  // 초기화될 때 사용. 
	virtual void NativeUpdateAnimation(float DeltaSeconds);  // 매 프레임마다 생성. 

public:
	virtual void InheritMoveStopNotify();
	virtual void InheritAttackStartNotify();
	virtual void InheritAttackEndNotify();
	virtual void InheritInIdleNotify();
	virtual void InheritNormalAttack();
	
public:
	void Skill1();
};

(C++-WukongAnim.cpp)

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


#include "WukongAnim.h"
#include "PlayerWukong.h"

UWukongAnim::UWukongAnim()  // 생성자.
{
	BaseAnimType = &AnimType;
	NextAttackEnable = true;  // Wukong에서는 가능. 
	NextAttackMax = 5;

	// 몽타주 에셋 읽음, 
	static ConstructorHelpers::FObjectFinder<UAnimMontage> Skill1Asset(TEXT("AnimMontage'/Game/Player/MTGWukongAttack.MTGWukongAttack'"));

	if (Skill1Asset.Succeeded())
		Skill1Montage = Skill1Asset.Object;
}

void UWukongAnim::NativeInitializeAnimation()  // 초기화될 때 사용. 
{
	Super::NativeInitializeAnimation();
}

void UWukongAnim::NativeUpdateAnimation(float DeltaSeconds)  // 매 프레임마다 생성.
{
	Super::NativeUpdateAnimation(DeltaSeconds);

	// 언리얼에서는 Cast를 사용하여 형변환을 해야 함. 
	APlayerWukong* pOwner = Cast<APlayerWukong>(TryGetPawnOwner()); // 이 애니메이션 컴포넌트를 가지고 있는 폰을 얻어오는 함수. 

	if (IsValid(pOwner))  // IsValid : U오프젝트에 대한 유효성 검사를 해줌. 제대로 된 것인지 확인을 해줘서 더 안전함. 
	{
		UCharacterMovementComponent* pMovement = pOwner->GetCharacterMovement();  // Movement를 얻어옴. 

		if (IsValid(pMovement))
		{
		}
	}
}

void UWukongAnim::InheritMoveStopNotify()
{

}

void UWukongAnim::InheritAttackStartNotify()
{
	APlayerWukong* pOwner = Cast<APlayerWukong>(TryGetPawnOwner());

	if (IsValid(pOwner))
		pOwner->EnableWeaponCollision(true);

	LOG(TEXT("AttackStart"));
}

void UWukongAnim::InheritAttackEndNotify()
{
	APlayerWukong* pOwner = Cast<APlayerWukong>(TryGetPawnOwner());

	if (IsValid(pOwner))
		pOwner->EnableWeaponCollision(false);

	LOG(TEXT("AttackEnd"));
}

void UWukongAnim::InheritInIdleNotify()
{

}

void UWukongAnim::InheritNormalAttack()
{

}

void UWukongAnim::Skill1()
{
	if (!Montage_IsPlaying(Skill1Montage))
		Montage_Play(Skill1Montage);
}

(C++-MinionRangerAnim.h)

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

#pragma once

#include "GameInfo.h"
#include "Animation/AnimInstance.h"
#include "MinionRangerAnim.generated.h"

UENUM(BlueprintType, Meta = (Bitflags))
enum class EMinionRangerAnim : uint8
{
	Idle,
	Run,
	Attack,
	Hit,
	Death
};

UCLASS()
class PROJECT1005_API UMinionRangerAnim : public UAnimInstance
{
	GENERATED_BODY()
	
public:
	UMinionRangerAnim();  // 생성자.

protected:
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true", Bitmask, BitmaskEnum = "EMinionRangerAnim"))
	uint8 AnimType;

	// 멈춰있는 지 확인. 
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
	bool MoveStop;

	UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
	bool IsGround;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
	bool AttackEnable;

public:
	// UAnimInstance에 가상함수로 정의되어 있으므로 재정의 하여 사용할 수 있음. 
	virtual void NativeInitializeAnimation();  // 초기화될 때 사용. 
	virtual void NativeUpdateAnimation(float DeltaSeconds);  // 매 프레임마다 생성. 
};

(C++-MinionRangerAnim.cpp)

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


#include "MinionRangerAnim.h"
#include "MinionRanger.h"

UMinionRangerAnim::UMinionRangerAnim()  // 생성자.
{
	MoveStop = true;  // 처음에는 멈춰있으므로 true.
	AttackEnable = true;  // 처음에 공격 가능. 
	IsGround = true;

	AnimType = (uint8)EMinionRangerAnim::Idle;
}

void UMinionRangerAnim::NativeInitializeAnimation()  // 초기화될 때 사용. 
{
	Super::NativeInitializeAnimation();
}

void UMinionRangerAnim::NativeUpdateAnimation(float DeltaSeconds)  // 매 프레임마다 생성.
{
	Super::NativeUpdateAnimation(DeltaSeconds);

	// 언리얼에서는 Cast를 사용하여 형변환을 해야 함. 
	AMinionRanger* pOwner = Cast<AMinionRanger>(TryGetPawnOwner()); // 이 애니메이션 컴포넌트를 가지고 있는 폰을 얻어오는 함수. 

	if (IsValid(pOwner))  // IsValid : U오프젝트에 대한 유효성 검사를 해줌. 제대로 된 것인지 확인을 해줘서 더 안전함. 
	{
		UCharacterMovementComponent* pMovement = pOwner->GetCharacterMovement();  // Movement를 얻어옴. 

		if (IsValid(pMovement))
		{
			IsGround = pMovement->IsMovingOnGround();  // IsMovingOnGround() : 땅을 밟고 있는 지 판단.
			if (IsGround)
			{
				// Velocity.Size() : 이동속도의 크기
				if (pMovement->Velocity.Size() > 0.f)  // 0보다 크면 움직임.
				{
					MoveStop = false;  // 이동 중에는 false.

					AnimType = (uint8)EMinionRangerAnim::Run;
				}
				else  // 0이면 움직이지 않음. 
				{
					MoveStop = true;  // 멈췄을 때 true.
				}
			}
		}
	}
}

(C++-MinionRanger.h)

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

#pragma once

#include "CoreMinimal.h"
#include "Monster.h"
#include "MinionRanger.generated.h"

/**
 * 
 */
UCLASS()
class PROJECT1005_API AMinionRanger : public AMonster
{
	GENERATED_BODY()

public:
	// Sets default values for this character's properties
	AMinionRanger();

protected:
	class UMinionRangerAnim* MinionRangerAnim;

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:
	// Called every frame
	virtual void Tick(float DeltaTime) override;
	virtual float TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent, class AController* EventInstigator, AActor* DamageCauser);

};

(C++-MinionRanger.cpp)

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


#include "MinionRanger.h"
#include "MinionRangerAnim.h"

// Sets default values
AMinionRanger::AMinionRanger()
{
	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	// static을 붙여서 1번만 읽어오도록 함. 
	// 미니언 에셋을 불러오기.
	static ConstructorHelpers::FObjectFinder<USkeletalMesh>	MeshAsset(TEXT("SkeletalMesh'/Game/ParagonMinions/Characters/Minions/Down_Minions/Meshes/Minion_Lane_Ranged_Dawn.Minion_Lane_Ranged_Dawn'"));

	if (MeshAsset.Succeeded())
		GetMesh()->SetSkeletalMesh(MeshAsset.Object);

	// AnimInstance 설정. 
	// SetAnimInstanceClass()로 클래스 타입을 지정해주면 내부적으로 생성자가 지나고 나서 AnimInstance를 만들어냄. 
	// 생성자에서 AnimInstance를 얻어오는 방법은 없음. 
	static ConstructorHelpers::FClassFinder<UMinionRangerAnim> AnimAsset(TEXT("AnimBlueprint'/Game/Monster/BPMinionRangerAnim.BPMinionRangerAnim_C'"));

	if (AnimAsset.Succeeded())
		GetMesh()->SetAnimInstanceClass(AnimAsset.Class);

	// 캡슐컴포넌트에 맞춰 위치 조절. 
	GetMesh()->SetRelativeLocation(FVector(0.f, 0.f, -70.f));  // 아래로 내림.
	GetMesh()->SetRelativeRotation(FRotator(0.f, -90.f, 0.f));  // 회전. 
}

// Called when the game starts or when spawned
void AMinionRanger::BeginPlay()
{
	Super::BeginPlay();
	
	// 생성자에서 AnimInstance를 얻어오지 못하므로 BeginPlay에서 얻음.
	MinionRangerAnim = Cast<UMinionRangerAnim>(GetMesh()->GetAnimInstance());

}

// Called every frame
void AMinionRanger::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

float AMinionRanger::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
	float fDamage = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);

	return fDamage;
}

(C++-Minion.h)

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

#pragma once

#include "Monster.h"
#include "Minion.generated.h"

/**
 * 
 */
UCLASS()
class PROJECT1005_API AMinion : public AMonster
{
	GENERATED_BODY()
	
public:
	// Sets default values for this character's properties
	AMinion();

protected:
	class UMinionAnim* MinionAnim;

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:
	// Called every frame
	virtual void Tick(float DeltaTime) override;
	virtual float TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent, class AController* EventInstigator, AActor* DamageCauser);
	// AActor안에 TakeDamage() 함수가 있음. 
};

(C++-Minion.cpp)

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


#include "Minion.h"
#include "MinionAnim.h"

// Sets default values
AMinion::AMinion()
{
	// Set this character to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	// static을 붙여서 1번만 읽어오도록 함. 
	// 미니언 에셋을 불러오기.
	static ConstructorHelpers::FObjectFinder<USkeletalMesh>	MeshAsset(TEXT("SkeletalMesh'/Game/ParagonMinions/Characters/Minions/Down_Minions/Meshes/Minion_Lane_Melee_Core_Dawn.Minion_Lane_Melee_Core_Dawn'"));

	if (MeshAsset.Succeeded())
		GetMesh()->SetSkeletalMesh(MeshAsset.Object);

	// AnimInstance 설정. 
	// SetAnimInstanceClass()로 클래스 타입을 지정해주면 내부적으로 생성자가 지나고 나서 AnimInstance를 만들어냄. 
	// 생성자에서 AnimInstance를 얻어오는 방법은 없음. 
	static ConstructorHelpers::FClassFinder<UMinionAnim> AnimAsset(TEXT("AnimBlueprint'/Game/Monster/BPMinionAnim.BPMinionAnim_C'"));

	if (AnimAsset.Succeeded())
		GetMesh()->SetAnimInstanceClass(AnimAsset.Class);

	// 캡슐컴포넌트에 맞춰 위치 조절. 
	GetMesh()->SetRelativeLocation(FVector(0.f, 0.f, -70.f));  // 아래로 내림.
	GetMesh()->SetRelativeRotation(FRotator(0.f, -90.f, 0.f));  // 회전. 
}

// Called when the game starts or when spawned
void AMinion::BeginPlay()
{
	Super::BeginPlay();

	// 생성자에서 AnimInstance를 얻어오지 못하므로 BeginPlay에서 얻음.
	MinionAnim = Cast<UMinionAnim> (GetMesh()->GetAnimInstance());

}

// Called every frame
void AMinion::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

float AMinion::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
	float fDamage = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);

	LOG(TEXT("Damage"));

	return fDamage;
}

(C++-EffectDestroy.h)

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

#pragma once

#include "GameInfo.h"
#include "GameFramework/Actor.h"
#include "EffectDestroy.generated.h"

UCLASS()
class PROJECT1005_API AEffectDestroy : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	AEffectDestroy();

protected:
	UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Meta = (AllowPrivateAccess = "true"))
	UParticleSystemComponent* Particle;

public:
	void SetEffectPath(const FString& strPath, const FString& strSoundPath);

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

public:
	UFUNCTION()
	void Finish(UParticleSystemComponent* Com);

};

(C++-EffectDestroy.cpp)

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


#include "EffectDestroy.h"

// Sets default values
AEffectDestroy::AEffectDestroy()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;

	Particle = CreateDefaultSubobject<UParticleSystemComponent>(TEXT("Particle"));

	SetRootComponent(Particle);
}

void AEffectDestroy::SetEffectPath(const FString& strPath, const FString& strSoundPath)
{
	UParticleSystem* Asset = LoadObject<UParticleSystem>(nullptr, *strPath);

	if (IsValid(Asset))
		Particle->SetTemplate(Asset);

	Particle->OnSystemFinished.AddDynamic(this, &AEffectDestroy::Finish);

	USoundBase* SoundAsset = LoadObject<USoundBase>(nullptr, *strSoundPath);
	UGameplayStatics::PlaySoundAtLocation(GetWorld(), SoundAsset, GetActorLocation());
	// PlaySound2D : 보통 BGM에 사용.
	// 감쇠 처리도 가능. 

}

// Called when the game starts or when spawned
void AEffectDestroy::BeginPlay()
{
	Super::BeginPlay();
	
}

// Called every frame
void AEffectDestroy::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

void AEffectDestroy::Finish(UParticleSystemComponent* Com)
{
	Destroy();
}

 

댓글