11 - PlayerController, State, Camera Animation

Overcle·2023년 2월 13일
0

학원

목록 보기
3/29

요약
1. 변수 저장 방법은, 블루프린터 UPROPERTY 방식으로 만들어서 하는방법과,
플레이어 스테이트 클래스를 활용하는 방법이 있다.
2. PlayerState는 PlayerController가 Pawn에 빙의될 때. PlayerState에 생성된 포인터를 전달해놓기 때문에 빙의 후에 된다는걸 명심하자.

오전 : 케릭터 마우스 조작 구현,
오후 : 블렌드 스페이스 애니메이션 수업.

용어 설명
1. 짐벌락 ? : 카메라를 상하 회전 할 때 . 90도 이상으로 넘어가면 꼬이는 현상 90도를 안넘어가게 제한을 두는걸로 방지.
2. Additib : 히트 모션이 포함되어있는? 걸 의미한다.
3. TArray : 언리얼의 배열 클래스

Tip
1. 언리얼에서는 형변환을 할때 cast<>로 해줘야한다 안그럼 큰일남
2. 언리얼에서 정수를 사용할려면 int-32를 사용해야한다.

따로 공부해서 정리해둘것
1.

1. 카메라 조작


마우스 움직임 감지도 축매핑으로 조작한다.

오른쪽으로 움직일 때는 1, 왼쪽은 -1 을 반환한다.

void APlayerCharacter::RotationCameraZ(float Scale)
{
	if (Scale == 0.f)
		return;

		mSpringArm->AddRelativeRotation(FRotator(0.f, Scale * 180.f * GetWorld()->GetDeltaSeconds() , 0.f));

}

GetActorRightVector 함수는 내부코드로 가속도를 계산하지만
위에 작성된 AddRelativeRotation은 순전히 더하고 빼기만 한다.

GetWorld 함수는 언리얼 내 변수를 가져올 수 있는 함수.

플레이어 회전 방법에는 2가지가 있다.

  • 컨트롤러를 조작해서 캐릭터를 회전.
  • 폰을 조작해서 캐릭터를 회전.
void APlayerCharacter::RotationCameraZ(float Scale)
{
	if (Scale == 0.f)
		return;

	//mSpringArm->AddRelativeRotation(FRotator(0.f, Scale * 180.f * GetWorld()->GetDeltaSeconds() , 0.f));
	AddControllerYawInput(Scale * 180.f * GetWorld()->GetDeltaSeconds()); 
    // AddControllerYawInput : 폰을 기준으로 회전 하는 방법
} 

상하 회전. - 짐벌락 방지 구현 필수

void APlayerCharacter::RotationCameraY(float Scale)
{
	if (Scale == 0.f)
		return;

	mSpringArm->AddRelativeRotation(FRotator(Scale * 180.f * GetWorld()->GetDeltaSeconds(), 0.f, 0.f));
}

카메라 줌 - 최대 최소값 구현 필수

void APlayerCharacter::CameraZoom(float Scale)
{
	if (Scale == 0.f)
		return;

	mSpringArm->TargetArmLength += Scale * -5.f;
}

2. 형변환


언리얼에서는 C++의 형변환처럼하면 큰일난다고 한다.
그래서 Cast라는 언리얼 제공 함수를 이용해야하며
//Cast : 해당 타입일 경우 해당 매모리 주소를 형변환하여 반환하고 아닐경우 nullptr을 반환한다.

3. 애니메이션


애니메이션은 여러방법으로 구현할 수 있다.
한가지만 반복만 할 꺼면 Asset 방식을 권장하며.
여러 애니메이션을 구현할 목적이면 블루 프린터가 좋다.

수업에서는 블루 프린터로 진행을 한다.

애니메이션은 AnimInstance 조작한다.

클래스 생성 > 모든클래스 > AnimInstance

수업에서는 전체적인 부모 클래스 애니메이션을 만들고.
그걸 상속받는 각 직업별 애니메이션을 구현한다

경로 폴더를 만든 후. 우클릭 >애니메이션 > 애니메이션 블루프린터 생성.

만든 블루 프린트를 우클릭 해서 레퍼런스 복사후 Knight.cpp로 와서 구현

	static ConstructorHelpers::FClassFinder<UAnimInstance> AnimClass(TEXT("/Script/Engine.AnimBlueprint'/Game/Player/Animation/Knight.Knight_C'"));
    if (AnimClass.Succeeded())
		GetMesh()->SetAnimInstanceClass(AnimClass.Class);

ClassFinder를 할때는 무조건 레퍼런스 주소 뒤에 '_C'를 붙혀줘야 한다.

추후 애니메이션을 구현할때 달리면서 공격하는 경우. 하체 상체를 분리해서 각각 애니메이션을 달리는것과 공격을 구현하는 걸로 Run-Attack을 구현한다.

3-1 블렌드 스페이스 애니메이션


에니메이션 2개를 섞을 수 있다.

우클릭 > 애니메이션 > 블렌드 스페이스 선택.
수업에서는 Idle-Run 을 하기에 기준을 Speed로 잡는다.

이름은 Speed, 최대 축값을 600(걷는속도), 그리드 분할은 1개로 설정한다.
하지만 실제 게임에서는 버프, 디버프, 등등 기준으로 걷는속도가 변경될 수 있으니 1로 설정하고 계산하는 방식으로 하는걸 권장한다.


위 사진 : 가로축 설정

위 사진 : 가운데 아래 칸에 각 모서리마다 애니메이션 할당.

위 사진 : 에니메이션 편집창으로 와서 만들었던 블렌드 에니메이션을 선언 후. 아래 코딩으로 선언된 계산된 속도값을 설정.

.h
class UE11_API UPlayerAnimInstance : public UAnimInstance
{
	GENERATED_BODY()

public:
	UPlayerAnimInstance();
protected:
	UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta=(AllowPrivateAccess=true))
	float mSpeedRatio;

public:
	virtual void NativeInitializeAnimation();
	virtual void MativeUpdateAnimation(float DeletaSeconds);
	
};
.cpp
UPlayerAnimInstance::UPlayerAnimInstance()
{
}

void UPlayerAnimInstance::NativeInitializeAnimation()
{
	Super::NativeInitializeAnimation();
}

void UPlayerAnimInstance::NativeUpdateAnimation(float DeletaSeconds)
{
	Super::NativeUpdateAnimation(DeletaSeconds);

	APlayerCharacter* PlayerCharacter = Cast<APlayerCharacter>(TryGetPawnOwner());

	if (IsValid(PlayerCharacter))
	{
		UCharacterMovementComponent* Movement = PlayerCharacter->GetCharacterMovement();
        mSpeedRatio = Movement->Velocity.Size() / Movement->MaxWalkSpeed;
	}
}

위 사진 : 그 후 Speed 변수를 앞서 만들었던걸로 지정한다.

3-2 BindAction


	PlayerInputComponent->BindAction<APlayerCharacter>(TEXT("NormalAttack"), EInputEvent::IE_Pressed, this, &APlayerCharacter::NomalAttack);

3-3 블렌드 스페이스 방향구하기


protected:
	UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta=(AllowPrivateAccess=true))
	float mSpeedRatio;

	UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = true))
	float mMoveDir;

public:
	void SetMoveDir(float Dir)
	{
		mMoveDir = Dir;
	}


void APlayerCharacter::MoveFront(float Scale)
{
	mMoveDir = Scale;



	if (Scale == 0.f)
		return;
	
	AddMovementInput(GetActorForwardVector(), Scale);

}

void APlayerCharacter::MoveSide(float Scale)
{
	if (mMoveDir == 1)
	{
		if (Scale == 0.f)
			mAnimInst->SetMoveDir(0.f);
		else if (Scale == 1.f)
			mAnimInst->SetMoveDir(45.f);
		else if (Scale == -1.f)
			mAnimInst->SetMoveDir(-45.f);
	}
	else if (mMoveDir == -1.f)
	{
		if (Scale == 0.f)
			mAnimInst->SetMoveDir(180.f);
		else if (Scale == 1.f)
			mAnimInst->SetMoveDir(135.f);
		else if (Scale == -1.f)
			mAnimInst->SetMoveDir(-135.f);
	}
	else if(mMoveDir == 0.f)
	{
		if (Scale == 0.f)
			mAnimInst->SetMoveDir(0.f);
		else if (Scale == 1.f)
			mAnimInst->SetMoveDir(90.f);
		else if (Scale == -1.f)
			mAnimInst->SetMoveDir(-90.f);
	}

	if (Scale == 0.f)
		return;

구조 순서상 Front > Side 순이기 떄문에 Front에는 예외 처리 전 MoveDir에 입력 받기 전의 값도 저장한다. 그 후 Side 함수 내에서 각 방향에 맞는 값을 구현한다.

3-4 공격 애니메이션 구현 - 몽타주


가만히 있다가 공격애니메이션 출력.
달리면서 공격애니메이션. 등
이질감을 해결하기 위한 구현방법이 있다.

수업에서는 몽타주로 진행을 한다.

가운데 몽타주 클릭 > 슬롯 매니저 클릭 > 우측 하단 새 그룹 클릭 > Player 그룹 생성 > 새 필터 Attack 생성 > 가운데 몽타주 새 슬롯 에서 생성된 Player.Attack 클릭 > 가운데 파란 Defualt 클릭 > 우측 상단 디테일란에 섹션 이름 Attack 설정

PlayerAnimInstance.h

	UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = true))
	TArray<UAnimMontage*>	mAttackMontageArray;

그 후 캐릭터 애니메이션 에디터 창에서 상단의 클래스 디폴트 클릭 > 좌측 디테일 란에 만들었던 배열 4개까지 증가 후. Attack 애니메이션을 4개까지 복사해서 각각 Slow로 변경 (아래 사진 참조)

Attack 슬롯 생성방법 : 빈공간 우클릭 > 슬롯 검색 > DefaultSlot 클릭 > 디테일란에 생성된 슬롯명 선택

앞서 만든 애니메이션 배열을 사용하기 위해
변수 선언

PlayerAnimInstance.h

	UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = true))
	bool	mAttackEnable;

	UPROPERTY(VisibleAnywhere, BlueprintReadWrite, meta = (AllowPrivateAccess = true))
	int32	mAttackIndex;
PlayerAnimInstance.cpp

UPlayerAnimInstance::UPlayerAnimInstance()
{
	mMoveDir = 0.f;
	mAttackEnable = true;
	mAttackIndex = 0;
}

void UPlayerAnimInstance::Attack()
{
	// 공격 불가능 상태일때는 공격키 무반응으로 설정
	if (!mAttackEnable)
		return;

	//공격중에 다시 공격버튼을 클릭 못하도록 불가능상태로 변경
	mAttackEnable = false;

	// Montage_IsPlaying : 인자로 들어간 몽타주가 재생중인지 판단
	if (!Montage_IsPlaying(mAttackMontageArray[mAttackIndex]))
	{
		Montage_SetPosition(mAttackMontageArray[mAttackIndex], 0.f);
		Montage_Play(mAttackMontageArray[mAttackIndex]);

		mAttackIndex = (mAttackIndex + 1) % mAttackMontageArray.Num();
	}

}
PlayerCharacter.cpp

void APlayerCharacter::NomalAttack()
{
	mAnimInst->Attack();
}
profile
게임 프로그래머 지망생의 발자취

0개의 댓글