플레이어는 카메라 시점에서 정면만 보고 움직이게 된다.
즉 캐릭터의 얼굴을 볼일이 없다.
하지만 정면으로 고정이 되어있어서
총을 쏠 때, 정면으로만 총알이 나가는 부자연스러운 방식이 될 것이다.
따라서 카메라 방향의 pitch에 맞춰 플레이어의 리그를 움직이도록
수정해본다.
위처럼 작성해봤는데, Pitch값이 확확 바뀌고, 원하는대로 움직이지 않았다.
현재 Character에는 위처럼
SpringArm이 붙어있고, 거기엔 카메라가 붙어있다.
GetForwardVector를 하면 카메라의 방향벡터를 반환한다고 한다.
(하늘색 화살표)
GetActorForwardVector를 사용하면 정면만 향하고 있기 때문에,
각도의 기준이 될 수 있겠다고 생각을 했다.
(회색 화살표)
따라서, 이전에 Direction을 구한 것처럼
두 벡터의 각도를 구한다.
음...
둘이 같은 말이었다.
GetActorForwardVector가 카메라였다.
정면벡터를 어떻게 구현하지?
float ASatelliteCharacter::GetPitch()
{
// 1. Character의 Controller 가져오기
AController* PlayerController = Cast<APlayerController>(GetController());
if (PlayerController)
{
// 2. Controller의 Pitch 가져오기
FRotator ControllerRotation = PlayerController->GetControlRotation();
PawnPitch = ControllerRotation.Pitch;
return PawnPitch;
}
// Controller가 없는 경우 0을 반환
return 0.0f;
}
위처럼 다시 작성해 보았다.
Character의 pitch값을 그대로 가져와서 사용하는 것이다.
아래를 보면 270
위를 보면 90
정면을 보면 0이나 360으로 뜨게 된다.
즉 아래와 위는 각각
270~360/0~90의 범위를 가지게 된다.
하지만 blend 애니메이션에선
아래가 -90, 정면이 0, 위가 90이었다.
아래에서 정면은 360을 빼면 맞기 때문에 아래 코드를 추가한다.
if (PawnPitch >= 270)
{
PawnPitch -= 360.0f;
}
위는 45도 기울인 값을 찍은 것이다.
정상적으로 잘 뜨게 된다.
하다가 문제를 발견한 부분이 있는데,
아예 아래나 위를 보고 w라는 전진키를 누르면 이동을 하지 않는다.
코드의 방식 때문인 것 같은데..
하늘을 보거나 땅을 보면, controller의 x축이 지면과 직교하도록 되고,
따라서 해당 x축에 값을 더하거나 빼도 움직이지 않는 것으로 보인다.
이동을 수정하는 것도 좋겠지만,
어짜피 pitch의 제한이 필요하다고 생각했다.
완전 하늘을 보거나, 완전 땅바닥을 보는 것은 게임에서 불필요하다고 생각한다.
PawnPitch = FMath::Clamp(PawnPitch, -75.0f, 75.0f);
clamp함수를 넣어보았다.
LookUp의 AddControllerPitchInput으로 실제 PlayerController의 Pitch를 조정하고 있었다.
MouseValue만큼을 pitch에 더하는 방식인데..
MouseValue를 하이재킹하여 먼저 pitch값에 더하고,
해당 값을 -75~75사이로 조정한 뒤,
그 값을 직접 Controller의 Pitch로 대입하는 방식을 사용하려고 했다.
결과는
안된다.
왜일까 모르겠다.
0에서 75까지 clamp되는건 정상동작하지만,
0이하로 내려가는 순간, 강제로 75로 돌아와진다..
왜??
ㅋㅋㅋㅋ 왜?
위의 코드가 답이었다.
Camera의 Pitch를 고정할 수 있으려면
CameraManager의 ViewPitchMax/Min에 접근해야만 했다.
따라서 위의 함수에서 정한 Clamp함수를 제거했다.
왜냐하면 실제 카메라의 값이 고정되어서
해당 카메라로부터 계산하는 값도 범위가 고정될 것이기 때문이다.
정상동작하는 모습
최대 45 (float기 때문에 유사값)
최소 -45의 값으로 고정된 모습.
범위 이상으로 카메라를 올리거나 내릴 수 없다
SpringArm과의 Inherit를 풀고 보면 명확히 보인다.
오늘 위의 내용들을 구현하려고 장장 6시간을 사용하였다.
기능을 구현하는게 어렵다기보다,
언리얼에 구현되어있는 기능을 찾는게 정말 어렵다.
특히 CPP의 경우 배로 어렵다..ㅎ
아래의 글들을 참고하였다.
언리얼 프로젝트에서 가장 넣어보고 싶었던 기능이다.
그 이유는, 언차티드나 데스스트랜딩과 같은 게임에서
구현이 되어있었고, 어떻게 계산하고 적용시키는지 볼때마다 신기했다.
따라서 이번 프로젝트에 넣어보려고 한다.
Foot IK 기능은 Actor Component로 만들고,
Character에 부착시킬 것이다.
따라서 상속클래스를 생성하고, EndPlay를 추가한다.
최종적으로 해당 구조체 정보를 반환하는 함수
그런데 Typedef struct문을 생성하고보니
위처럼 Beginplay부터 오류줄이 발생한다.
시간이 지나니 오류줄이 없어졌다.
FootTrace는 원본 코드에서 변수 이름만 적절히 바꿔주었다.
그러니까 sSocket
에 높이는 캐릭터 위치인 start에서부터
캡슐의 반 높이를 뺀 캡슐 콜리전의 위치에서 fTraceDistance
를 추가로 빼며
캡슐 바깥으로 뻗어나갈 수 있게 만든 것 같다.
https://darkcatgame.tistory.com/23
Linetrace를 진행한 모습인데, 아직 충돌지점은 설정한게 아니니
Start지점만 참고하면 되겠다.
소켓과 x,y좌표가 일치하지만, z좌표가 위와 같다.
생성자에서 m_bDebug의 값이 false로 설정이 되었기 때문에,
디버그모드는 없다.
다른 함수를 통해서 m_bDebug의 값을 true로 만들어야만
linetrace의 디버그를 할 수 있게 된다.
여기서 코드를 수정한 부분이 있는데,
impactNormal사용된 코드가 원래 if문 밖에 있었다.
하지만, 밖에 있는게 논리적으로 맞지 않는다 판단해서 안으로 넣었다.
ImpactNormal 벡터는 발의 방향을 지정하는데 사용되게 된다.
해당 기능을 위해 2개의 함수가 사용된다.
여기서 막히게 된다.
아무리 봐도 위의 예제와 다르다.
Rotation변수를 가져와야하는데,
Rotation변수를 UPROPERTY로 설정해둔게 없다.
Struct의 값을 불러와야 하는건데..
private내에 UPROPERTY설정은 따로 없다.
선언되어있긴한데...
BP에서 어떻게 해당 구조체 내의 멤버에 접근하지?
public으로 해도 저 m_pFootRotation에 접근이 되지 않는다..
https://online-unreal.tistory.com/68
코드가 더 간결한 예제인데..
제발 할 수 있었으면 좋겠다..
코드를 복사했고,
하라는 대로 설정을 만지니..
일단 된다...!!!!
동작원리는 처음 예제와 거의 똑같을 것으로 본다.
우선 적용했으니 각 기능에 대해 차근차근 살펴보고,
내가 원하는 세팅대로 설정해본다.
구현에 필요한 변수들을 AnimInstance에 선언한다.
보면 함수는 3개이다.
메인 함수인 FootIK와, 이를 동작시키기 위한 2개의 함수로 구성되어있다.
<충돌했는지, 시작점과 충돌한 객체와의 거리>
원래는 위처럼 코딩이 되었다.
다른점은 하드코딩 되어있지 않고, ActorLocation을 사용했다는 점이다.
즉 start와 end 모두 캐릭터의 capsule component를 바탕으로 생성이 된다.
<충돌이 발생했는지/충돌객체와의 거리/충돌표면의 법선벡터>
A와 B의 함수는 해당 FootIK를 위한 빌드업이었다.
NativeUpdateAnimation에서 지속적으로 호출되며,
RRot,RIK / LRot,LIK를 계속 계산하고 적용시킨다.
위처럼 노드를 연결한다.
LIK/RIK, LRot/RRot의 값을 바탕으로 실제 Bone의 Transform(위치/회전)을 수정한다.
이건 왜하는 걸까?
Skeletal Controls목록에 해당 LegIK가 존재한다.
즉 Leg뿐만이 아니라 hand부터 spline등
대부분의 bone에 IK를 적용시킬 수 있는 것으로 보인다.
alpha값은 아무리 검색해도 공식 문서에도 없는데,
float값을 받아서 FK의 위치를 설정하는 값으로 보인다.
캡슐의 끝점으로 발을 두도록 설정하는 값 같다...
https://bbagwang.com/unreal-engine/ik%EC%99%80-fk/
FK와 IK에 대한 설명이 위에 있다.
간략히 말해서 FK는 자식이 변해도 본인에게 영향이 가지 않고 독립적이고,
IK는 자식이 변하면 본인도 영향을 받아서 변하게 된다.
노드의 기본 설정인 LegDefinition을 적절히 수정한다.
IK와 FK를 작성해야한다.
IK는 calf라는 장딴지를 두고, FK는 foot이라는 발로 둔다.
일반적으로는 IK는 foot IK라는 위치를 사용한다고 하는데,
그렇게 되면, 무릎이 굽혀지지 않는다발목만 구부려질 것이다.
해당 노드에 설정된 Bone은 Root이다.
Root의 Z위치를 Displacement로 설정한다는 것이다.
Translation Mode
Add to Existing으로 하여,
현재 Root의 값에 값을 더하는 것으로 수정하도록 한다.
Translation Space
(적용시켜보니 World 말고도 똑같이 동작하였다.)
그냥 웬만해서 World로 두는 듯
Root
이번에는 Root가 아닌,
Foot_l, Foot_r에 대한 Transform을 적용시킨다.
여기서 적용시킬 것은, Root에서와 다르게, 위치가 아니라
Rotation값을 적용시킨다.
AnimInstance CPP에서 지속적으로 계산하고 있는 회전값
LRot / RRot를 대입한다.
Transform (Modify) Bone 노드 설정들에 대한 설명이다.
alpha값이 Bone Strength라고 하는데 도대체 뭘까..
기존 이동모션뿐만 아니라,
착지 모션중에도 Foot IK가 적용되는 모습이다.
그리고 일정 수준 이상 올라가면, Linetrace에 충돌하는 객체가 없고,
위처럼 꼿꼿이 서게 된다.