노티파이 옮김 TPS_05에

Untitled

CRIfle.cpp : Attach시켜야 하는 부분

void ACRifle::Begin_Equip() //장착 
{
	AttachToComponent(OwnerCharacter->GetMesh(), FAttachmentTransformRules(EAttachmentRule::KeepRelative, true), RightHandSocket);
	bEquipped = true; //장착완료

//Begin일때 Rotation풀어주는 것 정면을 향해야 하는 것 (ownercharacter에서)
	OwnerCharacter->bUseControllerRotationYaw = true;
	OwnerCharacter->GetCharacterMovement()->bOrientRotationToMovement = false;
}

void ACRifle::End_Equip()
{
	bEquipping = false; //장착완료후 처리
}

void ACRifle::Unequip()
{
	OwnerCharacter->PlayAnimMontage(UngrabMontage, UngrabMontageSpeed); //플레이 시키고 나서
}

void ACRifle::Begin_Unequip()
{
	AttachToComponent(OwnerCharacter->GetMesh(), FAttachmentTransformRules(EAttachmentRule::KeepRelative, true), HolsterSocket);
	bEquipped = false; //위 장착과 반대 

//Unequip후에는 풀어주기( Rotation) 해제 할 때 원본값으로 
	OwnerCharacter->bUseControllerRotationYaw = false;
	OwnerCharacter->GetCharacterMovement()->bOrientRotationToMovement = true;
}

void ACRifle::End_Unequip()
{
	bEquipping = false;
}

bool ACRifle::IsAvaliableZoom()
{
	bool b = true;
	b &= !!CHelpers::GetComponent<USpringArmComponent>(OwnerCharacter);
	b &= !!CHelpers::GetComponent<UCameraComponent>(OwnerCharacter);

	return b;
}

AttachToComponent : 무기 부착 하기 위함

IRifle.h 인터페이스 에서 추가 = CPlayer.h 에서 재정의

public:
	virtual bool GetEquipped() = 0;
	
	virtual void Begin_Equip_Rifle() = 0; //순수가상함수
	virtual void End_Equip_Rifle() = 0;

	virtual void Begin_Unequip_Rifle() = 0;
	virtual void End_Unequip_Rifle() = 0;

CPlayer.h

void Begin_Equip_Rifle() override;
void End_Equip_Rifle() override;

void Begin_Unequip_Rifle() override;
void End_Unequip_Rifle() override;

Untitled

Tip : 한꺼번에 바꾸기 단축키

CPlayer.cpp

void ACPlayer::Begin_Equip_Rifle()
{
	Rifle->Begin_Equip();
}

void ACPlayer::End_Equip_Rifle()
{
	Rifle->End_Equip();
}

void ACPlayer::Begin_Unequip_Rifle()
{
	Rifle->Begin_Unequip();
}

void ACPlayer::End_Unequip_Rifle()
{
	Rifle->End_Unequip();
}

Untitled

CAnimNotifyState_Equip.cpp

#include "CAnimNotifyState_Equip.h"
#include "Global.h"
#include "05_TPS/IRifle.h"

FString UCAnimNotifyState_Equip::GetNotifyName_Implementation() const
{
	return "Equip";
}

//Equip
void UCAnimNotifyState_Equip::NotifyBegin(USkeletalMeshComponent * MeshComp, UAnimSequenceBase * Animation, float TotalDuration)
{
	Super::NotifyBegin(MeshComp, Animation, TotalDuration);
	CheckNull(MeshComp);
	CheckNull(MeshComp->GetOwner());

	IIRifle* rifle = Cast<IIRifle>(MeshComp->GetOwner());
	CheckNull(rifle);

	if (bUnequip)
	{
		rifle->Begin_Unequip_Rifle();

		return;
	}

	rifle->Begin_Equip_Rifle();
}

void UCAnimNotifyState_Equip::NotifyTick(USkeletalMeshComponent * MeshComp, UAnimSequenceBase * Animation, float FrameDeltaTime)
{
	Super::NotifyTick(MeshComp, Animation, FrameDeltaTime);

	//CLog::Log("Tick");
}

//End 도 똑같음 
void UCAnimNotifyState_Equip::NotifyEnd(USkeletalMeshComponent * MeshComp, UAnimSequenceBase * Animation)
{
	Super::NotifyEnd(MeshComp, Animation);

	CheckNull(MeshComp);
	CheckNull(MeshComp->GetOwner());

	IIRifle* rifle = Cast<IIRifle>(MeshComp->GetOwner());
	CheckNull(rifle);

	if (bUnequip)
	{
		rifle->End_Unequip_Rifle();

		return;
	}

	rifle->End_Equip_Rifle();
}

Editor : 노티파이 사용시 Unequip 체크 하는 부분이 있다. C로 하면 이렇게 노티파이 설정이 가능해진다. Equip노티파이 이지만. Uneqiup 몽타쥬로 가서. 체크 해주면. Unequip으로 다시 놓는 동작으로 사용할 수 있다.

Untitled

물론 몽타쥬 뒤로 돌려주는거 해주고

Untitled

Aim

CRifle.h

private:
//Zoom을 할 수 있는 조건
	bool IsAvaliableZoom();

public:
	void Begin_Aim();
	void End_Aim();

Untitled

키값 셋팅 →프로젝트 셋팅 →입력 SubAction할당 우클릭

CPlayer.h

void OnSubAction();
void OffSubAction();

Untitled

CPlayer.cpp ? void ACPlayer::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)

PlayerInputComponent->BindAction("SubAction", EInputEvent::IE_Pressed, this, &ACPlayer::OnSubAction);
PlayerInputComponent->BindAction("SubAction", EInputEvent::IE_Released, this, &ACPlayer::OffSubAction);
void ACPlayer::OnSubAction()
{
	if(!!Rifle)
	{ 
		Rifle->Begin_Aim();

		return;
	}
		
	//TODO: 마우스 오른쪽 버튼 처리
}

void ACPlayer::OffSubAction()
{
	if (!!Rifle)
	{
		Rifle->End_Aim();

		return;
	}

	//TODO: 마우스 오른쪽 버튼 처리
}

Tip : TODO 와 책갈피 사용하는 법

GetComponentbyclass 의 문제점

SubClassOf 해주고 현재 ActorComponent로 리턴하게 되어있다. ActorComponent는 Component의 최상위 이니까. 필요할 때 마다. 다시 캐스팅 해서 사용해야한다. 쓰기 편하도록

Untitled

Untitled

CHelpers.h = Component 만들어주기

template<typename T>
	static T* GetComponent(AActor* InActor) //어디서 찾을 것인가. 
	{
		//return Cast<USkeletalMeshComponent>(GetComponentByClass(USkeletalMeshComponent::StaticClass()));
		return Cast<T>(InActor->GetComponentByClass(T::StaticClass())); //클래스타입 은 T로 바꿀 수 있다. 
	}

//같은 컴포넌트가 있으면 이름으로 찾을 수 있기에
	template<typename T>
	static T* GetComponent(AActor* InActor, const FString& InName) //Name으로 가져오기
	{
		TArray<T *> components; //구조체 이니까. 복사 할 필요 없음 
		InActor->GetComponents<T>(components); //예를 들어 스켈레탈 메쉬 컴포넌트가 T에있다하면 다 찾아온다. 

//받는 Name과 같은 것만 찾아오면 된다. 
		for (T* component : components)
		{
			if (component->GetName() == InName)//찾은거는 return 
				return component;
		}

		return nullptr;
	}

InActor->GetComponentByClass(?????) 설명

이곳에 TSubClassOf가 들어갔었다.

TSubClassOf(UActorCompoent) 

이것을 사용할 려면. T형으로 여기서 받았다.

Untitled

TSubClassOf랑 1:1로 매칭 되는 것은 UCLASS 라고 하였다. 그래서 TSubClassOf대신에 UCLASS로 넣어줄려고 한다. 해당 클래스에대한 UCLASS 타입을 가져오는 것은 모든 클래스 타입

클래스명::StaticClass() // 자기 클래스의 UCLASS로 리턴해준다. 

모든 클래스 타입 : 블루프린트에 공개될 수 있는 클래스 . 클래스를 정의 할 때 UCLASS가 붙어야 하는데. 이건 Reflection DECLASS? 직렬화된 클래스 라고 한다. 리플렉션 된 클래스는 모두다 ::StaticClass를 가지고 있다.

AActor::GetComponentByClass

UActorComponent * GetComponentByClass
(
    TSubclassOf< UActorComponent > ComponentClass
) const

//TSubclassOf를 클래스 타입으로 부터 직접 구하는 방법은 없다.(언리얼에서) 
// 1:1로 맵핑 되게 해당 클래스에 대한 UCLASS포인터를 가져오면 된다. 

Ex)

return Cast<USkeletalMeshComponent>(GetComponentByClass(USkeletalMeshComponent::StaticClass()));

Untitled

staticClass를 보면 자기 타입에 대한 UClass로 리턴해준다. 이런식으로 해서 리턴을 받아 올 수 있다. 위에서GetComponentbyClass가 리턴하는데 UActorComponent인데. 쓰고자 하는것은 SkeletalMeshComponent이다. 이 경우 다시 캐스팅 해주면 된다.

Cast<USkeletalMeshComponent>

UActorComponent가 Component의 최상위 이니까. 그러니까 부모형으로 리턴 된것이기에 우리가 쓸려면 자식형으로 다시 캐스팅 해줘야한다.

CRifle.h

private:
	bool bAiming;

CRifle.cpp

bool ACRifle::IsAvaliableZoom()
{ //줌을 할 수 있는 상황을 체크해줘야 한다. 
	bool b = true; //false만들면 안됨 
	b &= !!CHelpers::GetComponent<USpringArmComponent>(OwnerCharacter); //스프링암
	b &= !!CHelpers::GetComponent<UCameraComponent>(OwnerCharacter); //카메라 

	return b; //둘다 있으면 True가 나올 것 
}

void ACRifle::Begin_Aim()
{
	CheckFalse(bEquipped); //false면 장착이 안되있고 할 필요 없는것 
	CheckTrue(bEquipping); //장착중이라면 
	CheckFalse(IsAvaliableZoom());

	bAiming = true;

	USpringArmComponent* springArm = CHelpers::GetComponent<USpringArmComponent>(OwnerCharacter);
	UCameraComponent* camera = CHelpers::GetComponent<UCameraComponent>(OwnerCharacter);

//aim처리 될때 
	springArm->TargetArmLength = 100;
	springArm->SocketOffset = FVector(0, 30, 10);
	springArm->bEnableCameraLag = false;

	//camera->FieldOfView = 45;
	Timeline.PlayFromStart();
}

void ACRifle::End_Aim()
{
	CheckFalse(bEquipped);
	CheckTrue(bEquipping);
	CheckFalse(IsAvaliableZoom());

	
	bAiming = false;

	USpringArmComponent* springArm = CHelpers::GetComponent<USpringArmComponent>(OwnerCharacter);
	UCameraComponent* camera = CHelpers::GetComponent<UCameraComponent>(OwnerCharacter);

//aim들어가서 나오면 원본으로 돌려주기 
	springArm->TargetArmLength = 200;
	springArm->SocketOffset = FVector(0, 60, 0);
	springArm->bEnableCameraLag = true;

	//camera->FieldOfView = 90;
	Timeline.ReverseFromEnd();

Aiming할때

스프링 암과 카메라 aim할때 원본 다시 다 돌려주기 springarm→

총 쥐는 거 애니메이션 처리

총 조준점 자체에다 aim주는 법

Curve생성 90 → 20초 값 45

Untitled

레퍼런스 복사 → CRifle.cpp에 넣기전에

블프에서는 타임라인 사용하는게 매크로로 정의되어 있지만. C에서는 직접 불러와줘야한다.

CRifle.h

UPROPERTY(EditDefaultsOnly, Category = "Aim")
		class UCurveFloat* Curve;

//플레이속도 지정 
UPROPERTY(EditDefaultsOnly, Category = "Aim")
		float CurveSpeed = 200;
#include "Components/TimelineComponent.h"

private:
	FTimeline Timeline; //이것은 게임 모드 일때 시작을 한다. 

CRifle.cpp

CHelpers::GetAsset<UCurveFloat>(&Curve, "CurveFloat'/Game/05_TPS/Curve_Aim.Curve_Aim'");

타임라인

void ACRifle::BeginPlay()
{
	Super::BeginPlay();

	OwnerCharacter = Cast<ACharacter>(GetOwner());
	AttachToComponent(OwnerCharacter->GetMesh(), FAttachmentTransformRules(EAttachmentRule::KeepRelative, true), HolsterSocket);

	FOnTimelineFloat timelineFloat;
	//하나만 들어가기 때문에BindUFunction
	timelineFloat.BindUFunction(this, "OnZooming"); //이름으로 
	
	Timeline.AddInterpFloat(Curve, timelineFloat); //float하나 이니까. 
	Timeline.SetPlayRate(CurveSpeed); //타임라인 플레이 속도 
}

void ACRifle::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime); 
//타임라인은 Tick에서 한번 콜해줘야한다. 
	Timeline.TickTimeline(DeltaTime);
}

AddInterpFloat 정의

void FTimeline::AddInterpFloat(UCurveFloat* FloatCurve, FOnTimelineFloat InterpFunc, FName PropertyName, FName TrackName)
{
	FTimelineFloatTrack NewEntry;
	NewEntry.FloatCurve = FloatCurve;
	NewEntry.InterpFunc = InterpFunc;
	NewEntry.TrackName = TrackName;
	NewEntry.FloatPropertyName = PropertyName;

	InterpFloats.Add(NewEntry);
}

FOnTimelineFloat 정의

DECLARE_DYNAMIC_DELEGATE_OneParam( FOnTimelineFloat, float, Output );

FOnTimelineFloat 에 대한 변수 하나 만들어 줘서 사용

CRifle.h =델리게이트 연결 함수 만들어주기 내부 사용

private:
	UFUNCTION()
		void OnZooming(float Output);

CRifle.cpp : 필드오브뷰만 조정해주면 된다. 카메라 있다고 검증 하고 들어온 것이기에 있느냐 검증 해줄 필요 없다.

void ACRifle::OnZooming(float Output)
{
	CHelpers::GetComponent<UCameraComponent>(OwnerCharacter)->FieldOfView = Output;
}
//타임라인 플레이 시켜주기 
void ACRifle::Begin_Aim()
{
	//camera->FieldOfView = 45;
	Timeline.PlayFromStart();
}

void ACRifle::End_Aim()
{
	//camera->FieldOfView = 90;
	Timeline.ReverseFromEnd(); //다시 돌려주기 
}

Untitled

Untitled

Aiming상태에서 애니메이션이 달라질 것이다.

IRifle.h

	virtual bool GetAiming_Rifle() = 0;

Untitled

AIming 상태 인지 리턴

CRIfle.h

FORCEINLINE bool GetAiming() { return bAiming; }

Untitled

CPlayer.h로 가서 재정의 (순수 가상 함수 override붙여주는 게 의무는 아니다. 가상함수만 override의무인데. 그냥 다 붙여준다.)

bool GetAiming_Rifle() override;

Untitled

정의 해주기

CPlayer.cpp

bool ACPlayer::GetAiming_Rifle()
{
	if (!!Rifle)
		return Rifle->GetAiming();

	return false;
}

애니메이션에서도 작업할 수 있게

CAnimInstance.h

UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Animation")
		bool bAiming;

Untitled

CAnimInstance.cpp

에디터