일단 아래 링크 한번 보는거 씹추천
현재
void AABCharacter::SetControlMode(int32 ControlMode)
{
if (ControlMode == 0)
{
SpringArm->TargetArmLength = 450.f;
SpringArm->SetRelativeRotation(FRotator::ZeroRotator);
SpringArm->bUsePawnControlRotation = true;
SpringArm->bInheritPitch = true;
SpringArm->bInheritYaw = true;
SpringArm->bInheritRoll = true;
SpringArm->bDoCollisionTest = true;
bUseControllerRotationYaw = false;
}
}
위의 코드를 분석중인데 위의 코드를 실행을 하면은 SpringArm이 마우스의 회전에 따라 플레이어의 회전에는 영향을 주지 않고 회전을 막한다.
GTA같은 3인칭을 구현을 한 것이다.
bUseControllerRotationYaw = false;
그럼 위의 이거는 뭘까?? Yaw축으로 회전을 한다는 것을 false로 설정한다? 사용하지 않겠다라는 것을 짐작이 가능한데
위에서 내려다 봤을 때 Yaw Z축으로 마우스를 회전하면 플레이어도 같이 돈다...
여기 비행기 그림 한번 참고하고
이 움짤 다시 보도록 하자.
손가락 저모양으로 하고
캐릭터 값 수정하면서 회전 시켜 보도록 하자... 이해 ㅅㅌㅊ급으로 잘되는듯?
그래서
bUseControllerRotationYaw = false;
이것은 Playercontroller의 Yaw회전을 막는것이라 유추~ 가 가능하고
bUsePawnControlRotation 이거는 한국어 블로그는 거의 죄대 코드만 있고 뭐하는건지 설명은 없는듯?
https://kid5.tistory.com/373
여기 함 읽어보셈요.
더 정확한것은
https://docs.unrealengine.com/5.1/en-US/API/Runtime/Engine/GameFramework/USpringArmComponent/
언리얼 공식
일단
bUseControllerRotationYaw = false;
이거 false로 하거나 주석처리하면 마우스의 회전에 따라 캐릭터 회전을 안함. 카메라도 회전을 안함.
Pawn의 회전을 사용을 하는 부분을 true로 켜주거나 false로 켜는 것을 말하는 함수이다.
Actor의 회전값 0, 0, 0은 그 Actor가 바라보는 방향이 월드의 X축방향 (1, 0, 0)을 의미한다.
월드의 X축 방향은 기본 회전값에 대응하는 방향 값이라고 할 수 있다. Actor가 회전하면 액터의 시선 방향도 자연스럽게 다른 값으로 변한다.
PlayerController의 회전값으로 부터 회전 행렬을 생생해서 원하는 방향축을 대입해 캐릭터가 움직일 방향값 얻을 수 있다.
UE에서 시선방향은 X축, 우측 방향은 Y축을 의미한다.
void AABCharacter::UpDown(float NewAxisValue)
{
if (NewAxisValue == 0.f) return;
AddMovementInput(GetActorForwardVector(), NewAxisValue);
}
void AABCharacter::LeftRight(float NewAxisValue)
{
if (NewAxisValue == 0.f) return;
AddMovementInput(GetActorRightVector(), NewAxisValue);
}
위와같은 코드는 카메라의 방향과 상관없이 그냥 액터의 기본 회전값(ZeroVector)가 (바로보는 방향이)월드의 X축방향 (1, 0, 0)을 의미하기 때문에 w키 누르면 앞으로 쭉간다.
GTA처럼 카메라 회전값에 맞게 움직일 수 있게 하려면 위에서 말한것 처럼
PlayerController의 회전값으로 부터 회전 행렬을 생생해서 원하는 방향축을 대입해 캐릭터가 움직일 방향값 얻을 수 있다.
이렇게해야함.
그래서 플레이어 컨트롤러로 부터의 회전값으로 부터 시선 방향과 우측 방향의 벡터값을 가져오도록 코드를 수정해야한다.
void AABCharacter::UpDown(float NewAxisValue)
{
if (NewAxisValue == 0.f) return;
AddMovementInput(FRotationMatrix(GetControlRotation()).GetUnitAxis(EAxis::X), NewAxisValue);
}
void AABCharacter::LeftRight(float NewAxisValue)
{
if (NewAxisValue == 0.f) return;
AddMovementInput(FRotationMatrix(GetControlRotation()).GetUnitAxis(EAxis::Y), NewAxisValue);
}
또한 Character가 자연스럽게 회전할 수 있도록 CharacterMovement컴포넌트의 기능을 사용하도록 하자.
void AABCharacter::SetControlMode(int32 ControlMode)
{
if (ControlMode == 0)
{
SpringArm->TargetArmLength = 450.f;
SpringArm->SetRelativeRotation(FRotator::ZeroRotator);
SpringArm->bUsePawnControlRotation = true;
SpringArm->bInheritPitch = true;
SpringArm->bInheritYaw = true;
SpringArm->bInheritRoll = true;
SpringArm->bDoCollisionTest = true;
bUseControllerRotationYaw = false;
GetCharacterMovement()->bOrientRotationToMovement = true;
GetCharacterMovement()->RotationRate = FRotator(0.f, 720.f, 0.f);
}
}
중간중간 이해가 안가는 FRotationMatrix에 인자를 넣어주는 부분은
https://pppgod.tistory.com/25
이곳에서 FRotator조금 읽도록 하자.
FRotator를 실질적으로 사용을 할려면 "행렬"을 사용을 해야한다.
이후
GetUnitAxis 는
https://pppgod.tistory.com/26
여기 바로 읽도록 하자.
근데 위에부분 회전행렬 구해서 원하는 축을 찾는 코드에서
결과적으로 GetScaledAxis 는 변경된 좌표평면에서 자신이 찾고 싶은 축이 가지는 방향을 가져오는 함수이다. 위의 예제의 경우에는 X 축을 가져오게 된다. 즉 캐릭터가 바라보는 방향이다. 결과적으로 위의 코드는 현재 바라보는 방향으로 Value 만큼 이동한다는 의미가 된다.
void AABCharacter::LeftRight(float NewAxisValue)
{
if (NewAxisValue == 0.f) return;
// AddMovementInput(FRotationMatrix(GetControlRotation()).GetUnitAxis(EAxis::Y), NewAxisValue);
AddMovementInput(FRotationMatrix(GetControlRotation()).GetScaledAxis(EAxis::Y), NewAxisValue);
}
GetUnitAxis나 GetScaledAxis나 동작이 똑같음. 차이점은 구글링해도 안나옴..ㅠ
FRotationMatrix는 회전된 좌표계 정보를 저장하는 행렬이다.
MakeFromX는
함수는 입력 파라미터 벡터를 X축으로 하여 새로운 기저를 구축하고, 그 기저로의 회전 행렬을 리턴한다.
기저로의 회전 행렬을 return한다 == 하나의 벡터값과 이에 직교하는 나머지 두축을 구해 회전행렬을 생성하고 이와 일치하는 FRotator값을 얻어오는 방식을 사용함.
void AABCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
switch (CurrentControlMode)
{
case EControlMode::DIABLO:
{
if (DirectionToMove.SizeSquared() > 0.f)
{
GetController()->SetControlRotation(FRotationMatrix::MakeFromX(DirectionToMove).Rotator());
AddMovementInput(DirectionToMove);
}
}
break;
}
}
InterpTo
지정한 속력으로 목표지점까지 진행하되, 목표지점까지 도달하면 그 값에서 멈추는 기능이다.
위에거 SpringAmr->GetRelativeRotation() 이렇게 바꿔주면됨 (버젼 차이 인듯)
void AABCharacter::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
SpringArm->TargetArmLength = FMath::FInterpTo(SpringArm->TargetArmLength, ArmLengthTo, DeltaTime, ArmLengthSpeed);
switch (CurrentControlMode)
{
case EControlMode::DIABLO:
{
SpringArm->SetRelativeRotation(FMath::RInterpTo(SpringArm->GetRelativeRotation(), ArmRotationTo, DeltaTime, ArmRotationSpeed));
if (DirectionToMove.SizeSquared() > 0.f)
{
GetController()->SetControlRotation(FRotationMatrix::MakeFromX(DirectionToMove).Rotator());
AddMovementInput(DirectionToMove);
}
}
break;
}
switch (CurrentControlMode)
{
case EControlMode::DIABLO:
{
if (DirectionToMove.SizeSquared() > 0.f)
{
GetController()->SetControlRotation(FRotationMatrix::MakeFromX(DirectionToMove).Rotator());
AddMovementInput(DirectionToMove);
}
}
break;
}
}
막 위에 코드처럼 바꾸어 주었는데 이 부분은 크게 어렵지 않은거 같다.
역시나 DX를 제대로 공부 안하고 왔기 때문에 회전행렬이나 뭐 이런 부분 대충은 알지만
MakeFromX와같은 부분 나오거나 설명해주면 이해하기가 많이 어렵고 이해도 잘 안된다.
특히 회전 부분이 나는 많이 취약한거 같다.
그래도 어느정도 코드 분석하고 어거지로라도 게임 분석하면서 따라가지는 정도까지는? 되는거같은데 더 해야할듯...
FRotatorMatrix같은 함수를 써서 회전행렬로 왜 변환해야하는지는...
https://velog.io/@starkshn/Vector-Matrix#matrix 다시 보거나 강의 다시 듣자...