<그간의 작업을 하며 느낀 필자의 기분>


안녕하세요 여러분 긴시간 삽질을 통해서 죽지도 않고 다시 돌아왔습니다. 예상했던것보다 더 오랜시간이 걸렸습니다. 그래도 원하는 바를 이루어 내서 필자의 기분은 너무나 좋네요

우선은 결과물을 먼저 보여드리는게 좋을것 같습니다.


<필자의 마음을 편안하게 하는 부드러움>


저번과 다르게 발바닥의 끈끈이가 사라진듯 부드럽게 잘 올라가는 캐릭터가 보이실겁니다.

사실 구현은 했지만 엔진의 설계에 맞도록 구현을 했는지는 필자 본인도 알수가 없습니다. 엔진을 속속들이 다 알고있다면 모를 이야기지만 필자는 지금 읽고계시는 여러분과 마찬가지로 일개 사용자일 뿐이기 때문입니다.


저번에 작성한 글중에 일부러 코드를 안보여드린다는 글을 적은적이 있는데 누군가에게는 도움이 될법하지 않을까 싶기도 하고 여기까지 하는 사람이 얼마나 있겠냐는 심정에 열심히 작업한 내용을 올리려고 합니다. 물론 필자 스스로도 잘 정리를 해두어서 나중에 참고할 수 있는 정도를 기대하는것이긴 하지만요


일단 저번 포스팅에서 말씀드린 것들은 너무 많은 함수와 구조체가 필요로 하여서 필자 나름대로 필요한 부분만을 도출해 내서 입맛대로 코드를 재구성 해보았습니다. 하지만 재구성을 하면서 필요한것들을 몇가지 첨가하다 보니까 사실상 숫자상에서 크게 달라진 부분은 없는것 같은건 비밀아닌 비밀입니다.


일일히 나열하는것 보다는 하나하나 풀어가면서 필요한 순간에 연관된 내용을 정리해 나가는 방식으로 글을 쓰도록 하겠습니다.


우선 가장 크게 변한 부분은 TickComponent 함수입니다.

실질적인 중력이나 이동관련 부분을 SimulateMovement함수에서 처리하도록 옮겨서 매우 깔끔해진게 필자가 보기에 아주 좋습니다.


함수로 들어와 보면 FHitResult와 FRotator변수를 선언해줍니다. 이것들은 이번 시뮬레이션 동안 다른 함수에도 파견을 나가서 근무할 친구들이라 미리 선언해주는것입니다.

언리얼에서 제공해주는 CharacterMovement에서는 캐릭터의 이동상태가 Move_Walking일 경우에 중력을 체크 안하던 것으로 알고 있는데 필자또한 비슷하게 작성했습니다. 우선적으로 중력을 시뮬레이션 하고 그 다음에 이동을 처리합니다.


코드가 꽤나 길기 때문에 두부분으로 나눠서 진행을 하고 설명하기에 앞서서 한가지 함수를 먼저 이야기 하고 가겠습니다. IsMovingOnGround함수는 UNavMovementComponent클래스에 선언이 되어있지만 비어있는 가상함수이기 때문에 필요에 따라서 오버라이딩 하여 사용하는 함수입니다. 위와같은 내용은 언리얼에서 제공하는 CharacterMovement에서 고대로 가져온 친구이기 때문에 큰 설명이 필요없으리라 믿습니다.


IsMovingOnGround함수를 통해 현재 캐릭터의 이동상태를 확인하여 걷는 상태가 아닐경우에 중력을 통해 밑으로 떨어져야 하니까 Velocity에 중력값을 계산해서 대입합니다.


자연스럽게 SafeMoveUpdatedComponent를 사용하고 Hit결과 부딪힌 물체가 있고 IsWalkable함수의 반환값이 true면 CurrentFloor를 세팅해주고 캐릭터의 이동상태가 MOVE_Walking이 아니라면 MOVE_Walking으로 바꿔줍니다. 이후 충돌점으로부터 땅에서 Root컴포넌트가 떨어져야 할만큼 Z값을 조정해서 다시 대입해줍니다.


만약 부딪힌 물체가 없다면 여전히 공중에서 자유롭게 낙하중이기 때문에 RootComponent를 편안히 이동시켜주시면 됩니다.


이번에 두가지 함수가 나왔는데 IsWalkable과 SetFromSweep입니다. 우선은 IsWalkable 함수를 먼저 차근차근 알아보도록 하겠습니다.


이 함수에서는 중력을 통해 밟은 물체에 대해서 걸어다닐수 있는 물체인지 판단을 해주는 역할을 합니다.


필자도 내부에 있는 내용을 정확히 이해하고 있는것은 아니지만 충돌한 면의 Normal벡터를 이용하여 걸어다닐수 있는 높이값을 계산하여서 결과를 bool값으로 반환해줍니다.


SetFromSweep함수는 들어온 매개변수를 이용해서 CurrentFloor를 설정해주는 역할을 합니다.


비교적 다른건 이해하기 어려운부분은 적지만 Sweep에 대한 부분은 필자가 아직 잘 모르기 때문에 자세히 설명해 드릴수 없는점 양해해 주시면 감사하겠습니다.


이로써 이번에 사용된두가지 함수를 알아보았고 이제 다음으로는 중력처리 당시 IsMovingOnGround함수의 결과값이 true일때에 처리하는 부분을 알아보도록 하겠습니다.


캐릭터 이동상태가 바닥에 붙어서 움직이고 있다면 중력을 적용할필요는 없고 바닥의 높낮이에 맞춰서 캐릭터의 위치를 동기화 시켜주기만 하면 됩니다. 별로 크게 어려운 부분은 아니지요 어떤 방식을 하느냐에 따라서 달라지긴 하지만 말입니다.


필자는 언리얼 엔진에서 사용했듯이 LineTraceSingleByChannel(&)함수를 이용하겠습니다.

출처를 따라가 보시면 Hit결과 구조체의 멤버변수들이 무엇을 의미하는지도 확인할수 있으니 꼭 한번 보시기 바랍니다.


필자의 경우 RootComponent가 바닥에서 어느정도 거리가 있기 때문에 그 거리만큼 선 시작점 위치를 내려주어야 하기때문에 GroundOffsetZ를 썼지만 이 경우 너무 딱 달라붙어서 선충돌 검사를 해야할 대상을 지나쳐 버리는 일이 간혹 발생하는 바람에 캡슐로부터 받아온 PawnHalfHeight값을 두배로 하여 이용했습니다.


그리고 TraceDist변수는 쉽게 말해서 이번 프레임에 바닥을 확인할 최대 거리를 의미하며 2.4f 라는 수는 언리얼에서 고정적으로 쓰고있는 변수의 값이길래 그대로 하드코딩으로 넣었습니다.


if문을 보시면 두개를 걸어두었는데 혹시 몰라서 두개를 걸어둔것이니 읽고계신 여러분은 bBlockingHit만 확인하셔도 충분하시리라 생각됩니다.


선충돌이 정상적으로 작동했다면 떨어지다가 바닥에 부딪혔을때와 별로 다를것은 없습니다. Hit결과값을 통해서 CurrentFloor를 새로 지정해주고 부딪힌 위치를 기반으로 캐릭터가 바닥에 잘 붙어있도록 위치값을 좀 조정해주면 끝입니다.


만일 선충돌이 일어나지 않았다면 그건 곧 내려갈수 있는 계단이나 경사의 높이를 지나쳐서 떨어지고 있다는 의미가 되기때문에 캐릭터의 이동상태가 MOVE_Falling아니라면 MOVE_Falling으로 바꿔주고 CurrentFloor를 Clear시킵니다. 왜냐하면 현재 밟고있는 바닥은 존재하지 않고 영원한 나락으로 떨어지기 시작했기 때문이지요


이제 중력에 관련된 내용을 열심히 설명했으니까 본격적으로 지형 이동시에 높낮이로 생기는 문제를 해결한 이동코드를 설명해야합니다.


설명해야하는게 맞는데 이거 쓰는데도 시간이 너무 오래걸리고 말았네요. 오늘은 좀 쉬면서 다시한번 정리하고 내일 나머지 부분을 올리도록 하겠습니다.


<포스팅을 끝낸 필자>

Posted by 별수집가
,

몇일간 소스코드를 보면서 발악을 해보았지만 캐릭터 무브먼트를

필자의 입맛대로 구성해서 사용하도록 코드를 짠다는게 쉽지 않은일 이었습니다.


CharacterMovement클래스를 지겨울정도로 봤던거 같지만 아직도 전체를 다 이해하지는 못했습니다. 오히려 필요한 부분도 40%정도만 이해를 했기때문에 사용자 캐릭터는 다시 구성을 하겠지만 지금까지 들인 시간이 아까워서 이해한 부분에 대해서 정리해 두려고 합니다.


<급격한 분노를 참지 못해버린 필자>


잡담으로도 매우 대충 적어 두었지만 일단 언리얼에서 제공하는 CharacterMovement에서는 이동을 ENUM형으로 관리하고있습니다. 현재 캐릭터가 떨어지고있는지 수영하는지 걸어다닐수 있는 지형위에서 걷고 있는지 등을 확인해서말이지요.


그래서 기본적으로 현재 이동상태를 설정해주는 MovementMode라는 변수가 있고 이 변수의 변화에 관여하는 함수가 두어개 있습니다. 찾아가며 보기는 했는데 실제로 적용하는 코드는 제대로 못찾았습니다 넓으신 마음으로 읽는분들이 이해해 주시길 바랍니다.


코드가 만줄이 넘어가는 바람에 찾아서 읽고 다시 찾고 하는데 시간이 너무 오래걸렸습니다. 모든 분들이 그럴지는 모르겠지만 타인이 작성한 코드를 죽어라 읽고 있으면 진도는 안나가고 읽는것도 어렵고 이해하기도 어려워서 금방 지치게 됩니다. 사족이 길었네요 그냥 이렇다는 변명이 있는걸로만 알아주시면 됩니다.


지형 이동간에 버벅이는 구간을 해결하기 위해서 필요한 부분만을 짐작하여 집중적으로 보았고 필자가 확인한 바로는 약 9개의 함수와 2개의 구조체, 이동상태를 저장하는 변수, 현재 밟고있는 바닥오브젝트 에 대한 정보가 필요합니다.


가장 먼저 살펴볼 함수는 SimulateMovement입니다.

이동에 관해서 실질적으로 시작하는 함수라고 볼수 있으며 이 안에서 중력적용이라던가 현재 밟고 있는 혹은 이제 밟게될 이동가능한 지형에 대한 판단을 하게되는 부분입니다.


우선은 시작과 동시에 MoveSmooth함수를 통해서 이동을 먼저 확인해 봅니다.

함수에 진입하면 몇가지 다른 내용들이 있는데 가장 중요한건 현재 캐릭터가 지형위에서 움직이고 있는지를 먼저 IsMovingOnGround함수를 통해서 확인하고 지형위에서 움직이고 있다면 MoveAlongFloor함수로 넘어갑니다. 아닐경우 언리얼 소스코드에서는 날고있는지를 판단하기도 하지만 저는 날게할 생각이 없기 때문에 넘어갔습니다.


MoveAlongFloor함수에 들어가자마자 CurrentFloor가 걸어다닐수 있는 바닥인지 확인하고 아닐경우 바로 리턴을 해버립니다. 아닐경우 현재 캐릭터가 지형에서 걸어다니고 있는것인데 이경우 높낮이로 인하여 생기는 문제를 해결하기위해서 ComputeGroundMovementDelta함수를 이용합니다.


<필자가 계속해서 말하고있는 높낮이에 따른 문제점>

<발바닥에 끈끈이라도 붙었는지 언덕만가면 극심히 느려진다>


ComputeGroundMovementDelta함수에서는 충돌한 위치의 노말값과 지형의 해당 위치 노말값을 이용해서 부딪히지 않고서 지나갈수 있는 방향으로 이동값을 계산해줍니다. 이후에 반환받은 이동값을 통해 SafeMoveUpdatedComponent함수로 또 다시한번 검사를 통한 이동을 합니다.


이번 검사에서 다시 충돌이 일어나거나 충돌을 검사하는 시작지점이 오브젝트를 뚫었는지 확인하여 미끄러지게 하거나 오를수 있는 높이의 계단인지 등을 확인하여 동작합니다. 


필자가 이해한 바로는 bStartPenetrating 상태가 true면 앞에있는 물체가 거대하여 밟고 올라갈수 없기때문에 미끄러지게 하고 그것이 아니라 IsValidBlockingHit 상태가 true일 경우 부딪히기는 하였으나 물체가 몸을 가로막을정도로 큰것이 아니기에 밟고 지나갈수 있는지를 확인하는데 이때 사용하는 함수가 StepUp함수입니다.


StepUp함수에서는 이름과는 다르게 현재 캐릭터의 진행방향으로 가로막는 물체를 밟고 올라가는지와 그게 아니라면 전진, 하강 할수 있는지에 대해서 전부 확인해봅니다. 결과로 false가 나올시에는 미끄러지는게 다입니다.


여기까지 오면 MoveSmooth함수에서 나오게 됩니다. 함수를 통해서 이동을 기본적으로 처리했으며 다음으로는 캐릭터가 지형을 탈출해서 떨어지는지에 대해서 체크를 하게됩니다. 지금까지의 과정중 StepUp함수에서 FindFloor라는 함수를 이용하게 되는데 이 함수는 진행방향에 이동가능한 바닥이 있는지 찾아내주는 함수입니다. 그 결과가 FStepDownResult 변수에 들어오고 이를 통해서 확인하게 됩니다.


FStepDownResult에서 bComputedFloor가 true면 찾아낸 바닥으로 CurrentFloor를 변경합니다.

만약 떨어지고 있는 중이라면 아까 언급한 FindFloor를 한번더 실행하고 둘다 아니라면 CurrentFloor의 정보를 Clear시킵니다.


이제 현재 밟고 있는 CurrentFloor에 대한 정보가 변경이 되었고 이 바뀐 CurrentFloor가 걸어다닐수 없으면 현재 캐릭터의 이동상태를 Falling으로 바꿉니다. 만일 CurrentFloor가 걸어다닐수 있는 바닥이며 캐릭터의 이동상태가 Falling이면 바닥에 착지하게 해줍니다.


<분노를 강력히 표출하고 있는 필자>


좀더 정리를 하면서 코드를 읽으니까 느끼게 된것이지만 아직도 정확히 파악하고 있지 못하고있다는 사실을 알게되었습니다. 만줄씩이나 되는 코드에서 필요한 부분을 추적하며 공부하는건 참 쉽지 않은 일이었습니다. 그래도 들인 노력이 아까우니까 이해한 한도내에서 간략화 해서 약간 야매스럽게 코드를 구현해 보려고 합니다.

누가 그러지 않습니까? 일단 굴러가면 되는거라고 필자도 강력히 동의하는 바입니다. 최적화는 이미 죽인지 오래전이거든요


다음 포스팅때는 간략화 시킨 버전을 테스트하고 결과를 들고서 오겠습니다. 읽는 모든 여러분 필자와 같은 삽질은 피하시고 속편하게 Character클래스를 상속받아서 구현하시기 바랍니다.

Posted by 별수집가
,

작업하던 도중 막히는 부분이 생겨서 생각보다 좀 오래걸렸습니다.

사실은 에픽에서 공짜로준 서브노티카를 너무 재미있게 해서.. 아무튼

저번에는 캐릭터가 허공답보를 하는상태까지 진행했는데요.


앞으로 무엇을 손봐야 할지 대~충 견적을 잡아봅시다.

1. 깡으로 박아둔 카메라컴포넌트를 SpringArmComponent(&)밑에 붙여주기

2. 캐릭터가 위아래로 회전해서 하늘로, 바닥으로 가고있는데 좌우만 회전하게 만들기

3. 중력이 없는 중생에게 중력 선사하기


SpringArm은 Mesh밑으로 들어가고 Camera는 SpringArm밑으로 들어가면 됩니다.

물론 여러분은 센스있게 블루프린트에서 카메라를 좀 손봐주셨으리라 믿습니다.

단순하게 컴포넌트를 하나 추가하고 카메라를 그 밑에 달기만 하면



편안 해지실수 있습니다.

다음으로 해야할건 캐릭터가 막 바닥으로 눕고 하늘로 눕고 하는걸 막는것이죠

이건 생각보다 어렵지 않습니다. 다만 잘 모르니까 찾느라 시간이 오래걸렸지요


위에 보이는것처럼 RotationPich 설정을 해제해주면 됩니다.

순서대로 Pitch, Yaw, Roll(&)이 있는데 해당하는 축은 접힌글을 펼치시면 볼수있습니다.

봐도 잘 모르시겠다구요? 저도 그렇습니다. 아무튼간에 Pitch를 막으면 되는것이죠


이제 땅바닥으로 돌진하거나 하늘로 돌진하는일이 없어졌습니다.

아무리 가상의 존재라고는 하나 위아래는 구분할줄알아야(?) 하지 않겠습니까?

이제 시점을 위아래로 돌린다고 캐릭터도 돌아가버리는 머리 돌아버리는 상황을 방지했습니다.


견적 잡았던것중에 두가지가 부드럽게 지나갔군요. 실제로 크게 어려운것도 없는 단순한 작업이었다고 필자 스스로도 생각하고 있습니다. 다만 마지막으로 남은 중력 적용하기라는것이 필자를 너무나 괴롭혔다는 사실만 빼면 말이지요.


중력을 적용하기까지 많은 시간 CharacterMovement 클래스를 뒤져보고 실제로 이동에 관여하는 함수가 무엇인지 이동이 어느곳에서 실질적으로 적용되는지에 대해서 찾아보느라 시간이 오래걸렸습니다. 필자가 밟았던 길을 다시한번 되돌아보면서 중력을 적용시켜보도록 하겠습니다.


우선은 생성자와 중력이 얼만큼 적용될지를 조절해주는 GravityScale이라는 변수를 만들어줍니다.

이 GravityScale변수는 생성자부분에서 1.0f으로 초기화 시켜주시고 중력을 적용시키기 위한 코드를 작성하러 가보도록 하겠습니다.


위 사진에 밑줄쳐져있는 GetWorld() 함수를 호출해서 사용하시려면

#include "Engine/World.h" 를 해주셔야 합니다.


HitResult 변수는 중력을 먼저 체크하기위해서 밖으로 빼두었고 Rotator를 매번 함수를 통해

받아오는게 마음에 들지 않아서 따로 받아둔다음에 여러번 사용하게 만들었습니다.


우선 이번 프레임의 중력값을 Velocity에 값을 계산하여 넣어주고 SafeMoveUpdatedComponent(&)함수를 통해서 이동을 시켜봅니다. 


하염없이 떨어지는 그대여 어디까지 가나요

이놈의 캐릭터는 중력좀 적용한다 해봤더니 그런거 모르겠고 일단 내려갑니다.

중력인지 엘리베이터인지 알수가 없네요 아주 ㅋㅋ


쓸데없는 가설로 인하여 수시간의 삽질을 한결과 이유를 알아내기는 했습니다.

일단 SafeMoveUpdatedComponent함수에서는 이연결되어있는 UpdatedComponent를

기준으로 충돌체크를 하게 되는것인데 현재 연결되어있는건 RootComponent이고

이 RootComponent는 SceneComponent로 되어있습니다. 이게 무슨소리인가 하면 이 SceneComponent는 충돌체크를 할만한 뭐 껀덕지가 없어요 너무작고 충돌체도 존재하지 않습니다.


이때 깨닫게 된것입니다. 언리얼에서 기본 제공해주는 Character클래스의 RootComponent가 Capsule인지에 대해서 말이죠. 이러한 이유였던 것입니다. Movement클래스에서 업데이트해줄 대상은 보통 RootComponent가 되는것인데 이렇게 SceneComponent를 Root로 두게된다면 이와같이 중력적용시에 문제가 생기게 된다는것이죠


일단 모르겠고 그럼 업데이트할 대상을 현재 Root인 SceneComponenet가 아니라 Mesh밑에 있는 Capsule로 지정해준다면 해결할수 있는 문제아니겠습니까? 생각이 들어서 바로 실행해 봤습니다.


움짤 화질이 좀 별로여서 그렇지만 잘 보시면 캡슐형태의 충돌체가 중력으로 인해 바닥에 붙어서 움직이는것을 보실수 있습니다. 그런데 보시면 좀 의아하게 느껴지시는 부분이 있을텐데 캐릭터의 메쉬는 안움직이고 중심을 기준으로 캡슐이 움직이고 있는걸 알수 있으실겁니다.

이건 3D프로그래밍의 기초적인 부분이니까 설명하지 않아도 되리라 믿습니다.


결과적으로 Movement컴포넌트에서 업데이트 해줄 대상을 충돌확인이 훨씬 유리한 캡슐컴포넌트로 바꾸었더니 잘 작동합니다. 대신 실질적으로 움직이고 있는건 캡슐 컴포넌트이기 때문에 메쉬 그러니까 즉 RootComponent가 이동하고 충돌검사는 캡슐로만 할수있게 해야합니다.


몇가지 과정을 처리해주시면 이렇게 안정적으로 땅을 밟고서 물체와 충돌처리도 되는

훌륭해진 캐릭터를 볼수있게 됩니다.


이번 글도 여기서 마무리를 하도록 하겠습니다. 쓴 내용은 별것도 없는데 시간이 너무 많이 지나가 버렸네요 다음에는 뭘 할지 생각좀 해보고 천천히 올리도록 하겠습니다.


과정은 어디갔냐구요? 필자는 힌트를 다 드렸으니까 읽는 여러분이 직접 부딪히면서 해결방법을 찾아보세요. 절대로 나만 이 족같은 기분을 느낄수 없어서 이런것은 아닙니다.

Posted by 별수집가
,

저번 포스팅에서 플레이어나 몬스터의 베이스가 될 클래스에

기본적으로 필요할법한 것들을 넣어줬습니다.

이번에는 공통적으로 가지고 있을법한 데이터를 구조체로 선언해서 관리할 생각입니다.


지금 당장은 대충 느낌만 살리기 위해서 이것저것 넣어두고 이번에 실제로 사용할 정보는 MoveSpeed 저거 딱 하나입니다. 나머지는 왜 있냐고 물어보신다면 있는게 그럴싸해보여서 라고 답할수 있겠네요


해당하는 구조체를 변수로 선언 및 초기화를 간단하게 해주시면 됩니다.

필요없는 정보지만 참고로 저는 MoveSpeed에 200을 줬습니다.


이제 플레이어를 위한 클래스를 만들시간이 되었군요, CharacterBase클래스를 상속받아서 PlayerBase라는 클래스를 하나 만들어 줍니다.


선생님 저희는 무엇을 해야하죠?

또다시 돌아온 텅빈 클래스입니다. 이번에는 저번보다 더 심하네요

마찬가지로 당황하지 않고 침착하게 할것을 하나하나 생각해봅니다.


이동관련 처리를 해줄 MovementComponent가 필요할것이고 키입력을 지정해주어야 합니다.

또 플레이어의 눈이 되어줄 CameraComponent는 필수적인부분이 되겠네요

대충 견적이 나온듯 하니까 문서를 보면서 잘 따라가봅니다.


가장먼저 손쉽게 할수있는 프로젝트 세팅의 입력을 위와같이 처리해줍니다.


일단은 위와같이 함수 6개를 먼저 선언해 줍니다. 오버라이딩되어 있는 두 함수는 조금있다가 천천히 등장할 예정이니 아 이런게 있구나 생각만 하고 계시면 됩니다.


보시는바와 같이 SetupPlayerInputComponent함수는 방금전 프로젝트세팅의 입력에서 설정한 키의 이름에 맞는 입력값에 작동할 함수를 연결해줄때 쓰게됩니다. 4개의 이동 및 시점변경 함수에 대해서는 별도의 설명이 없어도 될것 같기는 하지만 일단 필자의 경우 시점변경 함수에 45라고 하드코딩이 되어있는데 테스트를 위해서 해둔것이니 이 글을 보시는 분들은 별도의 변수를 통해서 조절할수 있도록 바꿔주시는게 좋을것 같습니다.


다음으로는 입력을 통한 이동을 처리해줄 MovementComponent의 생성인데 튜토리얼을 따라 PawnMovementComponent를 상속받아서 PlayerMovement라는 클래스를 생성합니다.


상속받은 클래스에는 아무것도 없는데 TickComponent함수를 오버라이딩해서 위와같이 작업해주시면 됩니다.

위 함수는 Tick함수라고 대충 알아두시면 됩니다. 사실 저도 그렇게만 알기 떄문에 더 알려드릴수가 없어서 그런건 비밀아닌 비밀이죠, 코드중에 보시면 Velocity 변수에 이동벡터를 집어넣는게 보이실텐데 나중에 애니메이션 블루프린트에서 걷기와 서있는 모션을 변경할때 사용할 예정입니다.


다시금 PlayerBase 클래스로 돌아와서 헤더에다가 이렇게 먼저 선언을 해주시면 됩니다. 전방선언은 센스있게 하셨으리라 믿습니다.

이번에 테스트를 하면서 확인한게 있는데 뒤에있는 meta = (AllowPrivateAccess = "true")

이 부분은 읽는것과 마찬가지로 private영역에 있는 변수를 언리얼에디터에서 접근할수 있도록 해주는 그런 역할을 가지고 있었습니다. 이미 알고 계셨다면 뒷북은 이해해주시길 바랍니다.

위와같이 두 컴포넌트를 생성해 주시고 그릇에 물을 받아온다음 컴퓨터 앞에두고서 기도를 하면서 빌드를 한번 해봅시다. 문제없이 빌드가 되었다면 축하드리고 아니라면 저는 죄가 없습니다 판사님

아무튼 다시 돌아와서 PlayerBase클래스를 기반으로 블루프린트를 만들어서 마네킹을 넣어주시고 게임모드의 Default Pawn Class에 적용시키신 다음에 실행을 해보시면 터질수도있고 안터질수도 있습니다.


그리고 기분좋게 손수만든 캐릭터를 움직여 보신다면?

마치 익숙한 어느분처럼 허공답보 아니.. 지금은 애니메이션조차 없으니 공중부양을 하게되는 캐릭터를 보실수 있게 될텐데 너무 당황하시면 안돼고 침착하게 숨을 고르시면 됩니다.

기본적으로 아까 상속받아 만든 PlayerMovement 클래스는 중력 적용이 안되는데요 이건 기본적으로 원래 그런것이다 보니까 어쩔수 없습니다. 추후에 직접 중력을 적용시키는 코드를 작성해서 넣는 방법밖에 없어요. 그래서 이건 나중으로 미뤄두고 애니메이션을 먼저 해보겠습니다.


우선적으로 애니메이션 블루프린트를 하나 만들고 그래프에다가 스테이트머신을 하나 만들어줍니다.

왜 애니메이션은 블루프린트로 하는지 궁금하신 분이 있으실거 같은데 제가 애니메이션 부분은 잘 모르기도 하고 C++로 작업하려니까 마땅한 참고자료가 없어서 블루프린트의 힘을 빌리기로 했습니다.


스테이트 머신을 더블클릭하여 안으로 들어오신다음에 기본적으로 있을 Idel애니메이션과 이동할때 바꿔줄 Walk애니메이션을 올려주시면 됩니다. 저 애니메이션 두개의 출처는 다들 아시리라 믿고있지만 언리얼에서 기본제공해주는 3인칭 게임 프로젝트를 만들어 보시면 안에 있으니 쏙쏙 빼서 가져오시면 됩니다.

그리고는 Idel에서 Walk로 갈때와 Walk에서 Idel로 갈때의 조건을 깔끔하게 작성해 주시면 됩니다.


어떻게 하냐구요? 위에있는 사진처럼 해주시면 됩니다.

아까 PlayerMovement클래스에서 오버라이딩한 TickComponent함수에 Velocity값에 계속 대입을 해주고 있었는데 이곳에서 사용하기 위해서 였습니다. Velocity값이 0보다 크면 움직이는 중인것이고 0과 같으면 멈춰있는것인 것이죠!

그리고 보시면 경고메시지가 생기게 되는데 저도 정확한 원인과 해결방법을 몰라서 그냥 내버려 두고있습니다. 혹시 아시는분이 있다면 알려주세요!


이렇게 고생고생(?)해서 만든 애니메이션을 적용시켜준다음에 움직여 보신다면

여전히 공중부양을 하고있기는 하지만 허공답보로 진화한 캐릭터를 보실수 있습니다.


이번에도 여전히 별로 쓴것도 없는데 시간은 너무 빨리 흘러버리고 저의 체력도 떨어졌군요.

이번 포스팅은 여기에서 마무리하고 근시일내로 돌아올수 있도록 하겠습니다.

혹시 뭔가 잘 안되거나 하시는분들은 댓글로 알려주신다면 확인했을때 답글 달아드리겠습니다.

Posted by 별수집가
,

작업을 실제로 시작한건 2일전 부터지만 흔적을 남겨두는게 좋을것 같아서 글을 쓰기 시작한다.

필자 또한 언리얼을 제대로 공부한적은 없고 수박 겉핥기 식으로 블루프린트를 통해서 무언가를 만들어본 경험이 조금 있다.


그러므로 이 글은 언리얼에 대한 지식이 아주 조금이라도 있는 사람을 기반으로 하게될것 같으니

너무 세세하게는 설명을 하지 않고 넘어가도록 하겠습니다.


일단은 명색이 프로그래머이니 C++을 통해서 만들어야 한다는 생각을 하게되었고

아무것도 모르는 상태에서 작업을 시작했다.


http://api.unrealengine.com/KOR/Programming/Tutorials/index.html

역시 엔진 공부에 가장 훌륭한 선생님은 공식문서나 도큐먼트다.

필자 역시 시작을 하기 위해서 메뉴얼을 통해서 C++로 어떻게 해야하는지에 대해 공부해가면서 작업을 했다.


성격상 차근차근 읽어서 다 따라하는건 힘들다 보니 지금 당장 구현하고 싶은 부분에 대해서

찾아서 공부한다음에 바로 작업에 들어갔는데

Pawn클래스를 상속받아서 CharacterBase라는 클래스를 만들고 그 기반하에

PlayerBase와 MonsterBase클래스를 만들생각이다.


언리얼을 조금 공부해보거나 알고 있는 사람들이라면 왜 Character클래스를 상속받아서 안만드는지 궁금할텐데

필자의 생각에 Character클래스 자체에 대해서 잘 모르기도 하고 불필요한 부분을 상속받아 만들필요는 없다는 판단이 섰기에 Pawn클래스를 상속받아서 필요한 부분을 채워서 하기로 결정했습니다.


어디까지나 개인적으로 직접 구현하는걸 좋아하기 때문이니 이글을 읽고 계신분들은

Character클래스를 상속받아서 만들어도 상관 없습니다.


C++ 클래스 만들기를 선택해서 Pawn클래스를 부모로 선택해 준다.

필자가 아까 말한것처럼 플레이어와 몬스터 클래스의 베이스가 되어줄 CharacterBase 클래스를 만든다.


생성하고 나면 뭐부터 해야할지 막막한 텅빈 클래스가 등장하는데 당황하지 않으셔도 됩니다.

프로그래머라면 너무 익숙한 상황이니까 침착하게 필요한것들을 생각해봅시다.


이 캐릭터베이스 클래스는 플레이어와 몬스터에서 상속받아서 사용할 것이니까

메시 컴포넌트가 필요할것이고 기본적인 충돌처리를 위한 충돌체

지금 당장 생각나는건 이 두가지 정도가 다이기 때문에 추가를 해줍니다.


당장 위에서는 두가지만을 추가하자 했지만 일단은 4가지를 전방선언해줍니다.

이유는 바로 아래에서 설명을 할테니까 묻지도말고 따지지도 말고 하시면 될것입니다.


튜토리얼에는 UPROPERTY에 대한 내용이 없지만 에디터 상에서 값에 접근하고 조절하려면 위와 같이 설정을 해주어야 합니다.

사실 필자또한 마지막에 있는 'meta = 어쩌구' 부분에 대해서는 잘 모르지만 궁금하신 분들을 위해서 간략히 설명해 드리자면

언리얼 C++에는 언리얼의 독자적인 리플렉션 시스템이 있기때문에 저러한 지정을 해주어서 에디터상에서 사용할

변수나 함수, 클래스 등을 읽어들일수 있게 해주는 방식입니다.


더 자세하게 알고 싶으시다면 공식문서의 'C++프로그래밍 입문'항목의 '언리얼 리플렉션 시스템' 부분을 읽어보시면 자세히 알 수 있습니다.

하단링크는 공식문서의 링크이며 해당 부분을 읽어보시면 언리얼 블로그에 올라와 있는 리플렉션 설명글 링크가 있으니

거길 통해서 더 많이 알아보실수 있습니다.

http://api.unrealengine.com/KOR/Programming/Introduction/index.html


위 사진을 보시면 RootComponent에 SceneComponent를 할당해 주었는데 필자도 정확히는 모르겠지만

SkeletalMeshComponent를 Root에 바로 할당하면 에디터 상에서 크기조절을 제외한 회전이나 위치조절이 불가능했다.

그래서 에디터에서 Pawn클래스 블루프린터를 만들어 확인해 보니

RootComponent에는 SceneComponent가 붙어있길래 그걸 따라서 지정해 준것이다.


저 ArrowComponent의 경우에는 에디터상에서 정면을 알려주는 용도로 사용이 되는데

언리얼 기본 클래스인 ACharacter클래스에서 보고 그대로 가져왔기 때문에 별로 설명할건 없다.


이제 작성한 코드를 빌드시키면 언리얼 에디터에서 자동적으로 리플렉션을 통해 업데이트를 해주고

CharacterBase클래스를 기반으로 블루프린트를 만들어 Mesh에 기본적인 마네킹을 넣어주면

위와같은 화면을 볼수가 있게된다.


아까 작성한 코드대로 SceneComponent가 RootComponent가 되고 그 밑에 SkeletalMeshComponent

그 밑에는 CapsuleComponent가 붙는다. ArrowComponent는 정면을 알려주는 용도이기 때문에

에디터를 벗어나면 보이지 않으니 걱정하지 않아도 된다.


글재주가 없어서 인지 내용은 별것도 없는데 쓰는시간만 오래걸렸다.

기본적인 캐릭터 베이스는 완성이 되었으니 필요한 부분은 나중에 추가를 하기로 하고

이글은 여기서 마무리를 짖도록 한다.

Posted by 별수집가
,