필요한 개념
<적 피격 처리>
다음은 타격시 이펙트나 카메라 흔들림을 처리하기 위해 적클래스를 생성할 것이다.
-> CEnemy, CEnemy_Dummy 클래스 생성
실질적으로 Dummy가 화면에 올라감
* Enemy는 따로 입력 받는게 없어서 SetupPlayerInputComponent()가 필요없음
CEnemy도 IICharacter를 상속받아 ChangeColor()를 구현함
<시야를 바꿀 것인지 처리>
* Equip 했다면, 장비에 따라 시야가 다를 것이다.
ex) OneHandSword는 캐릭터가 정면만 바라보고, 캐릭터 회전을 안줄 것이다.
반면, Unarmed는 캐릭터가 자유자재로 회전 가능하다.
* 두 변수를 활용
bUseControllerRotationYaw = true; // 캐릭터가 정면만 바라보도록
bOrientRotationToMovement = false; // 캐릭터가 회전 못하도록 함
* FEquipmentData에 추가
// 시야를 바꿀 것인지
UPROPERTY(EditAnywhere)
bool bPawnControl = true;
참고)
FEquipmentData는 우리가 정의한 DataAsset에 들어갈 구조체이다.
// CEquipment.cpp
if (Data.bPawnControl == true)
{
// 칼을 장착하면 정면을 바라보게 됨
OwnerCharacter->bUseControllerRotationYaw = true;
// 회전하지 못하도록 함
OwnerCharacter->GetCharacterMovement()->bOrientRotationToMovement = false;
}
* bPawn = true (bUseControllerRotationYaw = true, bOrientRotationToMovement = false)의 경우
-> 카메라 회전에 따라 캐릭터가 회전하고, 캐릭터의 Yaw 회전은 안되며, 캐릭터는 정면만 바라본다.
* bPawn = false (bUseControllerRotationYaw = false, bOrientRotationToMovement = true)의 경우
OwnerCharacter->bUseControllerRotationYaw = false;
OwnerCharacter->GetCharacterMovement()->bOrientRotationToMovement = true;
-> 카메라 회전에 캐릭터가 영향 받지 않고 , 캐릭터의 Yaw 회전은 자유자재로 가능하며 캐릭터는 어디든 볼 수 있다.
<충돌체 생성 & 충돌체 함수 연결>
* 적 공격은 한가지 생각해야 할게 있음
충돌체는 BP_CAttachment에 생성하고 해당 충돌체가 충돌되었을때 CDoAction의 함수를 호출하도록 델리게이트를 생성
* BP_CAttachment_OneHand에서 CapsuleCollision을 생성해주고 무기 사이즈에 맞게 세팅함
// CAttachment.h
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FAttachmentBeginOverlap, class ACharacter*, InAttacker, class AActor*, InAttackCauser, class ACharacter*, InOtherCharacter);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FAttachmentEndOverlap, class ACharacter*, InAttacker, class AActor*, InAttackCauser, class ACharacter*, InOtherCharacter);
public:
// 충돌처리 공격 델리게이트
UPROPERTY(BlueprintAssignable)
FAttachmentBeginOverlap OnAttachmentBeginOverlap;
UPROPERTY(BlueprintAssignable)
FAttachmentEndOverlap OnAttachmentEndOverlap;
// 충돌체 추가위해
private:
// 액터에서는 루트컴포넌트를 만들어야함
// 루트 컴포넌트(여기다가 충돌체 붙임)
UPROPERTY(VisibleDefaultsOnly)
class USceneComponent* Scene;
private:
// 액터에서는 루트컴포넌트를 만들어야함
// 루트 컴포넌트(여기다가 충돌체 붙임)
class USceneComponent* Scene;
private:
// 충돌 컴포넌트에 원래 있는 OnComponentBeginOverlap delegate에 이 함수 연결
UFUNCTION()
void OnComponentBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
UFUNCTION()
void OnComponentEndOverlap(UPrimitiveComponent* OverlappedComponent ,AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
Tip)
매크로는 BP와 통신하는 것은 직렬화(Serialization) 개념이랑 연관되어있다.
* 세팅한 Collision을 가져와서 Delegate와 연결시켜줌
Collision 컴포넌트 들은 모두 UShapeComponent의 자식들이다.
-> 즉, UShapeComponent로 가져오면 됨
* Collision 가져오기
// UShapeComponent : 언리얼에서 사용되는 충돌체
// UShapeComponent는 Sphere, Cube, Capsule의 부모 컴포넌트 클래스
// GetComponents : <타입> 다 가져옴
GetComponents<UShapeComponent>(ShapeComponents);
// 컴포넌트의 델리게이트에 연결
for (UShapeComponent* component : ShapeComponents)
{
component->OnComponentBeginOverlap.AddDynamic(this, &ACAttachment::OnComponentBeginOverlap);
component->OnComponentEndOverlap.AddDynamic(this, &ACAttachment::OnComponentEndOverlap);
}
* 우리가 만든 델리게이트에 바인드된 함수들 호출됨
void ACAttachment::OnComponentBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
// OwnerCharacter말고 다른애만 공격할 수 있도록(자료형이 다른애만 공격함)
CheckTrue(OwnerCharacter == OtherActor);
CheckTrue(OtherActor->GetClass() == OwnerCharacter->GetClass());
if (OnAttachmentBeginOverlap.IsBound())
OnAttachmentBeginOverlap.Broadcast(OwnerCharacter, this, Cast<ACharacter>(OtherActor));
}
void ACAttachment::OnComponentEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
if (OnAttachmentEndOverlap.IsBound())
OnAttachmentEndOverlap.Broadcast(OwnerCharacter, this, Cast<ACharacter>(OtherActor));
}
<구조>
충돌 된다면, UShapeComponent에 기존에 있는 OnComponentBeginOverlap, OnComponentEndOverlap
델리게이트가 우리가 바인딩 시킨 함수를 호출할 것이고, 이 함수내에서 우리가 정의한 OnAttachmentBeginOverlap, OnAttachmentEndOverlap Delegate의 바인딩된 함수들을 Broadcast()로 콜함
<노티파이때 충돌체가 켜지고, 꺼져야함>
// CAttahment.h
public:
// 충돌체가 켜지고, 꺼지는 함수
void OnCollision();
void OffCollision();
// CAttachment.cpp
void ACAttachment::OnCollision()
{
// 켜줌
for (UShapeComponent* component : ShapeComponents)
component->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
}
void ACAttachment::OffCollision()
{
// 꺼줌
for (UShapeComponent* component : ShapeComponents)
component->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
<CAttachment에 CDoAction의 충돌되었을 때 호출될 함수를 연결함>
누가 공격했는지(InAttacker), 어떤 무기로(InAttackCauser), 데미지는 누가 입는지(InOtherCharacter)를 파라미터로 Delegate로 만들어줌
-> 이 델리게이트에 바인딩된 함수들로 피격 처리가 가능함
// CDoAction.h
// CAttachment 충돌때 콜 될 함수
// 재정의 해서 사용하도록 함
public:
// CAttachment 충돌때 콜 될 함수
UFUNCTION()
virtual void OnAttachmentBeginOverlap(class ACharacter* InAttacker, class AActor* InAttackCauser, class ACharacter* InOtherCharacter) {};
UFUNCTION()
virtual void OnAttachmentEndOverlap(class ACharacter* InAttacker, class AActor* InAttackCauser, class ACharacter* InOtherCharacter) {];
* CActionData BeginPlay 할때 함수 연결해줌
if (!!Attachment)
{
// 충돌 함수 연결해줌
Attachment->OnAttachmentBeginOverlap.AddDynamic(DoAction, &ACDoAction::OnAttachmentBeginOverlap);
Attachment->OnAttachmentEndOverlap.AddDynamic(DoAction, &ACDoAction::OnAttachmentEndOverlap);
}
자식에서 재정의 CDoAction_Melee
* 재정의할때 부모에서 한번 해줬으면 UFUNCTION() 안해줘도 됨
public:
virtual void OnAttachmentBeginOverlap(class ACharacter* InAttacker, class AActor* InAttackCauser, class ACharacter* InOtherCharacter) override;
virtual void OnAttachmentEndOverlap(class ACharacter* InAttacker, class AActor* InAttackCauser, class ACharacter* InOtherCharacter) override;
* Collision키고 끄기 위해 CAnimNotifyState_Collision 생성해 줌
충돌체는 CAttachment에 있으므로 현재 무기인 CAttachment 객체를 가져옴
UCActionComponent* action = CHelpers::GetComponent<UCActionComponent>(MeshComp->GetOwner());
CheckNull(action);
ACAttachment* attachment = action->GetCurrent()->GetAttachment();
CheckNull(attachment);
<데미지 주기>
BP에서는 ApplyDamage, AnyDamage 함수를 쓰지만, (물론 BP에서도 TakeDamage() 있지만 잘 안씀)
C++에서는 TakeDamage() 함수를 사용
* TakeDamage()함수는 AActor 클래스에 정의되어 있으며, 데미지를 주는 쪽에서는 TakeDamage 함수를 호출해주고
데미지를 받는 쪽에서는 재정의해서 사용
// CDoAction_Melee.cpp
void ACDoAction_Melee::OnAttachmentBeginOverlap(class ACharacter* InAttacker, class AActor* InAttackCauser, class ACharacter* InOtherCharacter)
{
CLog::Log(InOtherCharacter->GetName());
Super::OnAttachmentBeginOverlap(InAttacker, InAttackCauser, InOtherCharacter);
CheckNull(InOtherCharacter);
// 데미지 추가 정보를 넘길때
FDamageEvent e;
// 1. 데미지, 2. 데미지 이벤트, 3 : Instigator(컨트롤러), 4 : DamageCursor(데미지 야기하는 친구)
InOtherCharacter->TakeDamage(Datas[Index].Power, e, OwnerCharacter->GetController(), this);
}
* 데미지를 받는 쪽 CEnemy에서는 오버라이딩해준다.
float TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent, class AController* EventInstigator, AActor* DamageCauser) override;
<Enemy가 공격 받았을때 Hitted 상태 들어가고, 색을 바꿔줌>
* Enemy Setting
Enemy는 OptionComponent를 제외하고 다 필요하다.
// Enemy.h
protected:
UPROPERTY(VisibleDefaultsOnly)
class UCActionComponent* Action; // 공격할때
UPROPERTY(VisibleDefaultsOnly)
class UCMontagesComponent* Montages; // 동작 필요함
UPROPERTY(VisibleDefaultsOnly)
class UCStatusComponent* Status; // 속도
UPROPERTY(VisibleDefaultsOnly)
class UCStateComponent* State; // 상태
CActionData.h 추가된 내용
USTRUCT(BlueprintType)
struct FEquipmentData
{
GENERATED_BODY()
public:
...
// 시야를 바꿀 것인지
UPROPERTY(EditAnywhere)
bool bPawnControl = true;
};
CEquipment.cpp 추가된 내용
void ACEquipment::Equip_Implementation()
{
...
if (Data.bPawnControl == true)
{
// 칼을 장착하면 정면을 바라보게 됨
OwnerCharacter->bUseControllerRotationYaw = true;
// 회전하지 못하도록 함
OwnerCharacter->GetCharacterMovement()->bOrientRotationToMovement = false;
}
...
}
void ACEquipment::Unequip_Implementation()
{
...
// 따로 호출할 필요가 없음
OwnerCharacter->bUseControllerRotationYaw = false;
OwnerCharacter->GetCharacterMovement()->bOrientRotationToMovement = true;
}
CAttachment.h 추가된 내용
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "CAttachment.generated.h"
// 충돌체 공격 전달
// 1. 누가 공격했느냐, 2. 어떤 객체가 공격했는지, 3.공격받은 캐릭터
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FAttachmentBeginOverlap, class ACharacter*, InAttacker, class AActor*, InAttackCauser, class ACharacter*, InOtherCharacter);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_ThreeParams(FAttachmentEndOverlap, class ACharacter*, InAttacker, class AActor*, InAttackCauser, class ACharacter*, InOtherCharacter);
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FAttachmentCollision);
UCLASS()
class UONLINE_04_ACTION_API ACAttachment : public AActor
{
GENERATED_BODY()
protected:
// 액터에서는 루트컴포넌트를 만들어야함
// 루트 컴포넌트(여기다가 충돌체 붙임)
UPROPERTY(BlueprintReadOnly, VisibleDefaultsOnly)
class USceneComponent* Scene;
protected:
// BP에서 접근할 수 있도록
// 붙일 소켓이름을 받아서 캐릭터 Mesh에 붙여버림
UFUNCTION(BlueprintCallable)
void AttachTo(FName InSoketName);
UFUNCTION(BlueprintCallable)
void AttachToCollision(class UShapeComponent* InComponent, FName InSoketName);
public:
ACAttachment();
// 노티파이때 충돌체가 켜지고, 꺼진다.
void OnCollision();
void OffCollision();
private:
UFUNCTION()
void OnComponentBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
UFUNCTION()
void OnComponentEndOverlap(UPrimitiveComponent* OverlappedComponent ,AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
public:
// 충돌처리 공격 델리게이트
UPROPERTY(BlueprintAssignable)
FAttachmentBeginOverlap OnAttachmentBeginOverlap;
UPROPERTY(BlueprintAssignable)
FAttachmentEndOverlap OnAttachmentEndOverlap;
UPROPERTY(BlueprintAssignable)
FAttachmentCollision OnAttachmentCollision;
UPROPERTY(BlueprintAssignable)
FAttachmentCollision OffAttachmentCollision;
private:
TArray<class UShapeComponent*> ShapeComponents;
};
CAttachment.cpp 추가된 내용
#include "CAttachment.h"
#include "Global.h"
#include "GameFramework/Character.h"
#include "Components/SkeletalMeshComponent.h"
#include "Components/ShapeComponent.h"
#include "Components/CStateComponent.h"
#include "Components/CStatusComponent.h"
ACAttachment::ACAttachment()
{
CHelpers::CreateComponent<USceneComponent>(this, &Scene, "Scene");
}
void ACAttachment::BeginPlay()
{
// 찾아놓음
OwnerCharacter = Cast<ACharacter>(GetOwner());
State = CHelpers::GetComponent<UCStateComponent>(OwnerCharacter);
Status = CHelpers::GetComponent<UCStatusComponent>(OwnerCharacter);
//UShapeComponent : 언리얼에서 사용되는 충돌체
//UShapeComponent는 Sphere, Cube, Capsule의 부모 컴포넌트 클래스
// GetComponents : <타입> 다 가져옴
GetComponents<UShapeComponent>(ShapeComponents);
for (UShapeComponent* component : ShapeComponents)
{
component->OnComponentBeginOverlap.AddDynamic(this, &ACAttachment::OnComponentBeginOverlap);
component->OnComponentEndOverlap.AddDynamic(this, &ACAttachment::OnComponentEndOverlap);
}
OffCollision();
Super::BeginPlay();
}
void ACAttachment::OnComponentBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
// OwnerCharacter말고 다른애만 공격할 수 있도록(자료형이 다른애만 공격함)
CheckTrue(OwnerCharacter == OtherActor);
CheckTrue(OtherActor->GetClass() == OwnerCharacter->GetClass());
if (OnAttachmentBeginOverlap.IsBound())
OnAttachmentBeginOverlap.Broadcast(OwnerCharacter, this, Cast<ACharacter>(OtherActor));
}
void ACAttachment::OnComponentEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
if (OnAttachmentEndOverlap.IsBound())
OnAttachmentEndOverlap.Broadcast(OwnerCharacter, this, Cast<ACharacter>(OtherActor));
}
void ACAttachment::AttachTo(FName InSoketName)
{
// 얘는 자기 자신을 통째로 붙임
AttachToComponent(OwnerCharacter->GetMesh(), FAttachmentTransformRules(EAttachmentRule::KeepRelative, true), InSoketName);
}
void ACAttachment::AttachToCollision(UShapeComponent* InComponent, FName InSoketName)
{
// 얘는 컴포넌트 하나만 붙임
InComponent->AttachToComponent(OwnerCharacter->GetMesh(), FAttachmentTransformRules(EAttachmentRule::KeepRelative, true), InSoketName);
}
void ACAttachment::OnCollision()
{
// 켜줌
for (UShapeComponent* component : ShapeComponents)
component->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
if (OnAttachmentCollision.IsBound())
OnAttachmentCollision.Broadcast();
}
void ACAttachment::OffCollision()
{
// 꺼줌
for (UShapeComponent* component : ShapeComponents)
component->SetCollisionEnabled(ECollisionEnabled::NoCollision);
if (OffAttachmentCollision.IsBound())
OffAttachmentCollision.Broadcast();
}
CActionData.h 추가된 내용
...
UCLASS()
class UONLINE_04_ACTION_API UCActionData : public UDataAsset
{
GENERATED_BODY()
public:
FORCEINLINE class ACAttachment* GetAttachment() { return Attachment; }
CActionData.cpp 수정된 내용
...
void UCActionData::BeginPlay(class ACharacter* InOwnerCharacter)
{
...
if(!!DoActionClass)
{
DoAction = InOwnerCharacter->GetWorld()->SpawnActorDeferred<ACDoAction>(DoActionClass, transform, InOwnerCharacter);
DoAction->AttachToComponent(InOwnerCharacter->GetMesh(), FAttachmentTransformRules(EAttachmentRule::KeepRelative, true));
DoAction->SetActorLabel(GetLableName(InOwnerCharacter, "DoAction"));
DoAction->SetDatas(DoActionDatas);
UGameplayStatics::FinishSpawningActor(DoAction, transform);
if (!!Attachment)
{
// 충돌 함수 연결해줌
Attachment->OnAttachmentBeginOverlap.AddDynamic(DoAction, &ACDoAction::OnAttachmentBeginOverlap);
Attachment->OnAttachmentEndOverlap.AddDynamic(DoAction, &ACDoAction::OnAttachmentEndOverlap);
Attachment->OnAttachmentCollision.AddDynamic(DoAction, &ACDoAction::OnAttahmentCollision);
Attachment->OffAttachmentCollision.AddDynamic(DoAction, &ACDoAction::OffAttahmentCollision);
}
}
}
CDoAction.h 추가된 내용
#pragma once
...
UCLASS()
class UONLINE_04_ACTION_API ACDoAction : public AActor
{
GENERATED_BODY()
...
public:
// CAttachment 충돌때 콜 될 함수
UFUNCTION()
virtual void OnAttachmentBeginOverlap(class ACharacter* InAttacker, class AActor* InAttackCauser, class ACharacter* InOtherCharacter) {};
UFUNCTION()
virtual void OnAttachmentEndOverlap(class ACharacter* InAttacker, class AActor* InAttackCauser, class ACharacter* InOtherCharacter) {};
UFUNCTION()
virtual void OnAttahmentCollision() {};
UFUNCTION()
virtual void OffAttahmentCollision() {};
...
};
CDoAction_Melee.h 추가된 내용
#pragma once
...
UCLASS()
class UONLINE_04_ACTION_API ACDoAction_Melee : public ACDoAction
{
...
public:
virtual void OnAttachmentBeginOverlap(class ACharacter* InAttacker, class AActor* InAttackCauser, class ACharacter* InOtherCharacter) override;
virtual void OnAttachmentEndOverlap(class ACharacter* InAttacker, class AActor* InAttackCauser, class ACharacter* InOtherCharacter) override;
virtual void OnAttahmentCollision() override;
virtual void OffAttahmentCollision() override;
};
CDoAction_Melee.cpp 추가된 내용
...
void ACDoAction_Melee::OnAttachmentBeginOverlap(class ACharacter* InAttacker, class AActor* InAttackCauser, class ACharacter* InOtherCharacter)
{
Super::OnAttachmentBeginOverlap(InAttacker, InAttackCauser, InOtherCharacter);
CheckNull(InOtherCharacter);
// 데미지 추가 정보를 넘길때
FDamageEvent e;
// 1. 데미지, 2. 데미지 이벤트, 3 : Instigator(컨트롤러), 4 : DamageCursor(데미지 야기하는 친구)
InOtherCharacter->TakeDamage(Datas[Index].Power, e, OwnerCharacter->GetController(), this);
}
void ACDoAction_Melee::OnAttachmentEndOverlap(class ACharacter* InAttacker, class AActor* InAttackCauser, class ACharacter* InOtherCharacter)
{
}
void ACDoAction_Melee::OnAttahmentCollision()
{
}
void ACDoAction_Melee::OffAttahmentCollision()
{
}
CAnimNotifyState_Collision.h
#pragma once
#include "CoreMinimal.h"
#include "Animation/AnimNotifies/AnimNotifyState.h"
#include "CAnimNotifyState_Collision.generated.h"
UCLASS()
class UONLINE_04_ACTION_API UCAnimNotifyState_Collision : public UAnimNotifyState
{
GENERATED_BODY()
public:
FString GetNotifyName_Implementation() const override;
virtual void NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration) override;
virtual void NotifyEnd(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation) override;
};
CAnimNotifyState_Collision.cpp
#include "CAnimNotifyState_Collision.h"
#include "Global.h"
#include "Actions/CActionData.h"
#include "Actions/CAttachment.h"
#include "Components/CActionComponent.h"
FString UCAnimNotifyState_Collision::GetNotifyName_Implementation() const
{
return "Collision";
}
void UCAnimNotifyState_Collision::NotifyBegin(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation, float TotalDuration)
{
Super::NotifyBegin(MeshComp, Animation, TotalDuration);
CheckNull(MeshComp);
CheckNull(MeshComp->GetOwner());
UCActionComponent* action = CHelpers::GetComponent<UCActionComponent>(MeshComp->GetOwner());
CheckNull(action);
ACAttachment* attachment = action->GetCurrent()->GetAttachment();
CheckNull(attachment);
attachment->OnCollision();
}
void UCAnimNotifyState_Collision::NotifyEnd(USkeletalMeshComponent* MeshComp, UAnimSequenceBase* Animation)
{
Super::NotifyEnd(MeshComp, Animation);
CheckNull(MeshComp);
CheckNull(MeshComp->GetOwner());
UCActionComponent* action = CHelpers::GetComponent<UCActionComponent>(MeshComp->GetOwner());
CheckNull(action);
ACAttachment* attachment = action->GetCurrent()->GetAttachment();
CheckNull(attachment);
attachment->OffCollision();
}
CEnemy_Dummy.h
#pragma once
#include "CoreMinimal.h"
#include "Characters/CEnemy.h"
#include "CEnemy_Dummy.generated.h"
UCLASS()
class UONLINE_04_ACTION_API ACEnemy_Dummy : public ACEnemy
{
GENERATED_BODY()
};
CEnemy.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "Characters/ICharacter.h"
#include "Components/CStateComponent.h"
#include "CEnemy.generated.h"
UCLASS()
class UONLINE_04_ACTION_API ACEnemy : public ACharacter, public IICharacter
{
GENERATED_BODY()
protected:
UPROPERTY(VisibleDefaultsOnly)
class UCActionComponent* Action;
UPROPERTY(VisibleDefaultsOnly)
class UCMontagesComponent* Montages;
UPROPERTY(VisibleDefaultsOnly)
class UCStatusComponent* Status;
UPROPERTY(VisibleDefaultsOnly)
class UCStateComponent* State;
public:
ACEnemy();
float TakeDamage(float DamageAmount, struct FDamageEvent const& DamageEvent, class AController* EventInstigator, AActor* DamageCauser) override;
protected:
virtual void BeginPlay() override;
public:
virtual void Tick(float DeltaTime) override;
public:
virtual void ChangeColor(FLinearColor InColor) override;
private:
UFUNCTION()
void OnStateTypeChanged(EStateType InPrevType, EStateType InNewType);
private:
class UMaterialInstanceDynamic* BodyMaterial;
class UMaterialInstanceDynamic* LogoMaterial;
};
CEnemy.cpp
#include "CEnemy.h"
#include "Global.h"
#include "Actions/CActionData.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Animation/AnimInstance.h"
#include "Components/SkeletalMeshComponent.h"
#include "Components/CActionComponent.h"
#include "Components/CMontagesComponent.h"
#include "Components/CStatusComponent.h"
#include "Materials/MaterialInstanceConstant.h"
#include "Materials/MaterialInstanceDynamic.h"
#include "Components/WidgetComponent.h"
#include "Widgets/CUserWidget_Name.h"
#include "Widgets/CUserWidget_Health.h"
ACEnemy::ACEnemy()
{
PrimaryActorTick.bCanEverTick = true;
// Widget은 Mesh()에다가 붙이면 된다.
CHelpers::CreateComponent<UWidgetComponent>(this, &NameWidget, "NameWidget", GetMesh());
CHelpers::CreateComponent<UWidgetComponent>(this, &HealthWidget, "HealthWidget", GetMesh());
CHelpers::CreateActorComponent<UCActionComponent>(this, &Action, "Action");
CHelpers::CreateActorComponent<UCMontagesComponent>(this, &Montages, "Montages");
CHelpers::CreateActorComponent<UCStatusComponent>(this, &Status, "Status");
CHelpers::CreateActorComponent<UCStateComponent>(this, &State, "State");
GetMesh()->SetRelativeLocation(FVector(0, 0, -90));
GetMesh()->SetRelativeRotation(FRotator(0, -90, 0));
USkeletalMesh* mesh;
CHelpers::GetAsset<USkeletalMesh>(&mesh, "SkeletalMesh'/Game/Character/Mesh/SK_Mannequin.SK_Mannequin'");
GetMesh()->SetSkeletalMesh(mesh);
TSubclassOf<UAnimInstance> animInstance;
CHelpers::GetClass<UAnimInstance>(&animInstance, "AnimBlueprint'/Game/Enemies/ABP_CEnemy.ABP_CEnemy_C'");
GetMesh()->SetAnimInstanceClass(animInstance);
GetCharacterMovement()->RotationRate = FRotator(0, 720, 0);
}
void ACEnemy::BeginPlay()
{
UMaterialInstanceConstant* body;
UMaterialInstanceConstant* logo;
CHelpers::GetAssetDynamic(&body, "MaterialInstanceConstant'/Game/Materials/M_UE4Man_Body_Inst.M_UE4Man_Body_Inst'");
CHelpers::GetAssetDynamic(&logo, "MaterialInstanceConstant'/Game/Materials/M_UE4Man_ChestLogo_Inst.M_UE4Man_ChestLogo_Inst'");
BodyMaterial = UMaterialInstanceDynamic::Create(body, this);
LogoMaterial = UMaterialInstanceDynamic::Create(logo, this);
GetMesh()->SetMaterial(0, BodyMaterial);
GetMesh()->SetMaterial(1, LogoMaterial);
State->OnStateTypeChanged.AddDynamic(this, &ACEnemy::OnStateTypeChanged);
Super::BeginPlay();
Action->SetUnarmedMode();
}
void ACEnemy::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void ACEnemy::ChangeColor(FLinearColor InColor)
{
BodyMaterial->SetVectorParameterValue("BodyColor", InColor);
LogoMaterial->SetVectorParameterValue("BodyColor", InColor);
}
void ACEnemy::OnStateTypeChanged(EStateType InPrevType, EStateType InNewType)
{
switch (InNewType)
{
case EStateType::Hitted: Hitted(); break;
}
}
float ACEnemy::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
CLog::Log(Damage);
return 0.0f;
}
void ACEnemy::RestoreColor()
{
FLinearColor color = Action->GetCurrent()->GetEquipmentColor();
// 현재 무기 색으로 돌려줌
ChangeColor(color);
}
결과
'Unreal Engine 4 > C++' 카테고리의 다른 글
<Unreal C++> 51 - Action RPG(Enemy Hitted) (0) | 2022.05.16 |
---|---|
<Unreal C++> 50 - Action RPG (Enemy UI) (0) | 2022.05.16 |
<Unreal C++> 47 - Action RPG (Character Interface) (0) | 2022.05.16 |
<Unreal C++> 46 - Action RPG (Melee Attack Combo) (0) | 2022.05.16 |
<Unreal C++> 44 - Action RPG(CAction & Melee Attack) (0) | 2022.05.09 |