노티파이 옮김 TPS_05에
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;
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();
}
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으로 다시 놓는 동작으로 사용할 수 있다.
물론 몽타쥬 뒤로 돌려주는거 해주고
Aim
CRifle.h
private:
//Zoom을 할 수 있는 조건
bool IsAvaliableZoom();
public:
void Begin_Aim();
void End_Aim();
키값 셋팅 →프로젝트 셋팅 →입력 SubAction할당 우클릭
CPlayer.h
void OnSubAction();
void OffSubAction();
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: 마우스 오른쪽 버튼 처리
}
GetComponentbyclass 의 문제점
SubClassOf 해주고 현재 ActorComponent로 리턴하게 되어있다. ActorComponent는 Component의 최상위 이니까. 필요할 때 마다. 다시 캐스팅 해서 사용해야한다. 쓰기 편하도록
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형으로 여기서 받았다.
TSubClassOf랑 1:1로 매칭 되는 것은 UCLASS 라고 하였다. 그래서 TSubClassOf대신에 UCLASS로 넣어줄려고 한다. 해당 클래스에대한 UCLASS 타입을 가져오는 것은 모든 클래스 타입
클래스명::StaticClass() // 자기 클래스의 UCLASS로 리턴해준다.
모든 클래스 타입 : 블루프린트에 공개될 수 있는 클래스 . 클래스를 정의 할 때 UCLASS가 붙어야 하는데. 이건 Reflection DECLASS? 직렬화된 클래스 라고 한다. 리플렉션 된 클래스는 모두다 ::StaticClass를 가지고 있다.
UActorComponent * GetComponentByClass
(
TSubclassOf< UActorComponent > ComponentClass
) const
//TSubclassOf를 클래스 타입으로 부터 직접 구하는 방법은 없다.(언리얼에서)
// 1:1로 맵핑 되게 해당 클래스에 대한 UCLASS포인터를 가져오면 된다.
Ex)
return Cast<USkeletalMeshComponent>(GetComponentByClass(USkeletalMeshComponent::StaticClass()));
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→
Curve생성 90 → 20초 값 45
레퍼런스 복사 → 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(); //다시 돌려주기
}
Aiming상태에서 애니메이션이 달라질 것이다.
IRifle.h
virtual bool GetAiming_Rifle() = 0;
AIming 상태 인지 리턴
CRIfle.h
FORCEINLINE bool GetAiming() { return bAiming; }
CPlayer.h로 가서 재정의 (순수 가상 함수 override붙여주는 게 의무는 아니다. 가상함수만 override의무인데. 그냥 다 붙여준다.)
bool GetAiming_Rifle() override;
정의 해주기
CPlayer.cpp
bool ACPlayer::GetAiming_Rifle()
{
if (!!Rifle)
return Rifle->GetAiming();
return false;
}
애니메이션에서도 작업할 수 있게
CAnimInstance.h
UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Animation")
bool bAiming;
CAnimInstance.cpp
에디터