좀비 에셋을 구매하였지만 애니메이션이 마땅치않아 고민하던 중에 Mixamo라는 사이트를 알게 되었다.
이 사이트는 캐릭터도 구할수 있지만 캐릭터 원본만 있다면 그 캐릭터에 사이트에 있는 애니메이션을 자동으로 적용이 되게 변환해주는 기능도 있다.
일단 사이트에 들어가 검색창에 필요한 애니메이션 이름을 검색해준다.
그 후 오른쪽에 있는 "UPLOAD CHARACTER" 를 눌러준다. (애니메이션이 아닌 캐릭터까지 필요하다면 그냥 다운로드를 하면 된다.)
적용할 캐릭터를 넣는다. (.Zip도 가능은 하지만 안되는경우, .Fbx파일만 따로 추출하여 업로드시키면 된다.)
업로드가 완료되면 Next를 눌러 적용을 시킨다.
그 후 "DOWNLOAD" 버튼을 누르면 저런 창이 나온다.
"Format"은 "FBX for Unity(.fbx)"를 선택해준다.
"Skin"은 해당 캐릭터의 아바타까지 필요하다면 "With Skin"을 선택해주고, 애니메이션만 필요하다면 "With Out Skin"을 선택해주면 된다.
그 후 다운로드된 파일은 유니티 프로젝트창에 드래그&드랍 으로 가져오면 된다.
만일 타입이 "Humanoid" 타입이라면, Animation카테고리에 들어가 밑을 보면 여러가지가 있을것이다.
이동을 해야하는 애니메이션이라면, "Loop Time"을 체크해주고 나머지는 체크하지 않고, "Root Transform Position(Y)"에서 "Based Upon"을 "Feet"으로 바꿔준 뒤에 Apply를 눌러주면 된다.
적용이 완료되면 애니메이션만 필요하다면, 애니메이션을 클릭 후에 "Ctrl + D"를 눌러 복제하여 사용하면 된다.
캐릭터까지 전체 사용할 것이라면, 바로 하이라키창으로 전체 묶음을 옮겨서 사용하면 된다.
"Apply Root Motion"은 오브젝트의 위치와 회전을 애니메이션이 제어하도록 할 것이냐를 결정하는 곳이다.
true(체크) 한다면, 애니메이션이 해당 애니메이터가 적용된 오브젝트의 Transform에 영향이 갈 것이다. (캐릭터의 이동속도가 없어도 애니메이션에 의해서 앞으로 가긴 하지만, 직진으로만 가기에 매우 작은 이동속도를 부여해주면 될 것이다.)
false 한다면, 애니메이션은 해당 오브젝트의 움직임 그 자체에는 영향을 주지 못한다. (캐릭터가 이동 속도가 없다면 제자리하는 모습이 나올것이다.)
오브젝트의 회전을 애니메이션이 제어하게 할 것인가를 정하는 카테고리
Bake Into Pose : 활성화를 한다면, 애니메이션에 관계없이 Transform의 좌표축에 고정을 시킨다. 게임 오브젝트가 애니메이션에 의해서 회전을 하지 않게 된다.
Based Upon : 오브젝트의 중심 좌표를 어디에 둘건지 정하는 것이다.
오브젝트의 Y값을 애니메이션이 제어하게 할 것인가를 정하는 카테고리
오브젝트의 X, Y값을 애니메이션이 제어하게 할 것인가를 정하는 카테고리
순찰쪽 노드 부분에서 대기시간이 끝나면 새로운 목적지로 이동하는 것이 아닌 살짝 움찔했다가 다시 대기시간에 들어가고 그 뒤에 다시 목적지가 설정되서 움직이는 버그가 있었다.
private INode.E_NodeState RandomPositionAssignment()
{
if (_detectedPlayer == null & _patrolRandomPosCheck == true)
{
_correctPos.x = Random.Range(_patrolMinPos.x, _patrolMaxPos.x);
_correctPos.z = Random.Range(_patrolMinPos.y, _patrolMaxPos.y);
AnimationWalkCheck();
_agent.SetDestination(_correctPos);
_patrolRandomPosCheck = false;
}
return INode.E_NodeState.ENS_Failure;
}
private INode.E_NodeState CorrectPathCheck()
{
// 경로가 유요하지 않거나 초기화되지 않은지 체크
if (_agent.pathStatus == NavMeshPathStatus.PathInvalid)
{
DebugLogger.LogError("Agent Path is Invalid");
_patrolRandomPosCheck = true;
return INode.E_NodeState.ENS_Running;
}
return INode.E_NodeState.ENS_Failure;
}
private INode.E_NodeState CheckArrivalAtDestination()
{
if (_agent.pathPending)
{
return INode.E_NodeState.ENS_Running;
}
if (_agent.remainingDistance < Mathf.Epsilon)
{
return INode.E_NodeState.ENS_Success;
}
return INode.E_NodeState.ENS_Failure;
}
private INode.E_NodeState IdleWaitTimeCheck()
{
// 최초 한번 시간 할당.
if (_idleWaitCheck == true)
{
AnimationIdleCheck();
_idleDurationTime = Random.Range(3f, 5f);
_idleStartTime = Time.time;
_idleWaitCheck = false;
}
if (Time.time - _idleStartTime > _idleDurationTime)
{
_patrolRandomPosCheck = true;
_idleWaitCheck = true;
return INode.E_NodeState.ENS_Failure;
}
return INode.E_NodeState.ENS_Running;
}
private INode.E_NodeState MoveSetPosition()
{
AnimationWalkCheck();
_agent.SetDestination(_correctPos);
return INode.E_NodeState.ENS_Running;
}
목적지를 셋팅해주는 이동노드를 빼고 순찰셀렉토노드 가장 왼쪽(첫번째)노드 에서 랜덤 목적지를 할당해주면서 바로 새로운 목적지(SetDestination)을 할당해주어 해결하였다.
랜덤 목적지는 최초 할당을 하고, 목적지에 도착하고 대기시간이 다 지나야만 새롭게 할당되기에 비효율적으로 계속 목적지를 반복해서 찍어주지 않아 효율적으로 노드를 순회할 수 있게 되었다.
NavMeshAgent가 목적지에 다다르면, 다음 행동을 준비하고 하려는것 이 아닌, 목적지를 인식하지 못하고 주의를 빙글빙글 도는 문제가 있었다.
애니메이션을 위에 설명한 방법처럼 적용을 하다가 애니메이션의 자연스러움을 위해 "Apply Root Motion"을 체크해주엇는데, 그것이 미세한 위치 오차를 불러와 인식하지 못한 것이었다.
나의 경우 agnet의 StoppingDistance를 Mathf.Epsilon을 사용하여 정교하게 잡으려고 하였지만, 그 이유때문에 목적지를 인식하지 못하는 것이었다.
agnet.StoppingDistance를 0.1f로 바꿔주었다.
agent.remainingDistance 또한 0.1f로 변경하여 동일하게 제자리 대기 노드로 들어갈 수 있도록 하였다.
자연스러운 움직임의 연속을 위해 "Atuo Braking"을 꺼주었다.