배경 #2

3.15

배경은 모델링을 구입해서 배치한 후, AI를 통해 텍스처링을 할 예정이다. 이 텍스처링 기술은 (주)너디스타(https//nerdystar.io)가 보유하고 있으며, 난 지인찬스로 첫 베타테스터(?)가 되었다.

툴을 받아서 처음으로 돌려보다보니 이런 저런 오류가 보였다. 개발을 하다 보면 문제는 매일 터진다. 처음부터 끝까지, 공정은 완벽하지 않다. 매일이 시행착오의 연속이다.

버그를 수정하는 것은 시간이 걸리기 때문에, 작업은 병렬로 진행해야 한다. 남는 시간에 다른 일을 해보자. 현재의 시스템으로는 캐릭터에 그림자를 드리우기 위해서 투명오브젝트를 사용해야 하는데, 이 셰이더를 개발해두는 편이 좋겠다.

챗GPT에게 물어보니, ShadowCaster라는 라이트모드가 있다고 한다. 해보니 정말로 된다! 오오.

이 오브젝트는 면을 컬링하는 게 아니라, 그림자만 계산하는 것이다. 뎁스와 렌더링 부분이 필요없으니 이 부분을 없애고 셰이더를 가볍게 만들어준다.

이는 캐릭터에게 나무 그림자를 드리우는데 사용할 예정이다. 조명은 정면에서 때리는데, 나뭇잎이 화면을 가리면 안되니까 이런 꼼수를 사용한다.

이것이 빌드를 해도 잘 나올지 테스트해보아야 한다.

배포용 빌드화면. 잘 나온다. 야호

이제 킷배시를 결제할 시간이다.

3.15

킷배시를 결제하기 전에 테스트가 좀 더 필요하다.

근사한 집 3채를 준비했다.

오…!? 확실히 박스보단 좀 더 괜찮은 결과를 보여준다.

한 번 느낌을 보자.

아쉬운 결과가 나왔다. 꽤나 커다란 오브젝트임에도 불구하고, 옆면이 그렇게 잘 보이지는 않는다. 이는 캐릭터를 잘 살리기 위해서 원근을 매우 줄여놓았기 때문이다.

이-렇게 길게 늘여보았지만… 생각보다 잘 보이지 않는다. 흐음.

그렇다면 3D모델을 그대로 사용하는 것의 잇점이 크지 않아 보이는데…

하지만 그것은 사이즈가 충분히 크지 않아서일지도 모른다는 생각.

3.16

여러가지 가능성을 놓고 테스트 중. 씬을 원하는대로 컨트롤할 수 있는 마음이 반, 또 매핑이 그리기 귀찮은 마음이 반. 하지만 절충안은 없는 상황.

여전히 고민 중.

3.18

제작 방식에 대해선 거의 정립은 했다. 배경은 어차피 플랜을 사용할 예정인데, 실루엣만 보고 AI가 이 물건이 무엇인지 맞출 수 있을까?

어..음.. 못알아먹는다.

아래는 모델 직접렌더링 테스트.

사이클렌더가 나쁘지 않지만, 조금 밋밋한 느낌이 있다.

AI는 포기하고, 모델을 바로 쓰되, 렌더링 샷에 리터칭을 하는 방향으로 가는 편이 좋아보인다.

오늘의 깨달음. 유니티의 FoV(Field of View)는 Vertical을 기준으로 씌여진다. 이를 블렌더와 맞추려면 축을 Horizontal로 바꾼 후 변경되는 값을 들고가야 한다. 블렌더는 가로/세로 선택이 없고, 무조건 가로기준으로 계산한다.

원경의 자잘한 오브젝트들은 Plane을 쓸 예정이다. 초안은 통샷을 렌더해서 누끼를 딸 예정이었으나….

플랜을 대고 노마진으로 프로젝션하면 오브젝트 단위로 잘 뽑아준다. 알파까지 예쁘게!

그런데 AO도 구워지고 있는 거 맞나?

맞다. 잘 구워지고 있다.

원경은 대체로 플랜을 사용할 것이다. 메쉬엔 컬러만 지정할 것이니 UV를 펼 필요가 없고, 폴리곤도 절약된다. 문제는 근경이다. 이건 실제 메쉬를 사용해야 한다. 조명상태를 베이킹하기 위해 UV도 펴야 하니 일이 좀 귀찮아진다. 하지만 이 정도는 해야 할 것이다…아마도.

테스트를 하느라 너덜너덜해진 해적의 집.

남은 것은 식물이다. 그림자는 베이킹 시에 고정된다. 하지만 식물은 바람에 흔들려야 한다. 어렵게 생각하면 셰도우, AO패스를 따로 분리해 저장한 뒤, 유니티의 그림자 감쇠값을 받아서 합성해주면 될 것 같은데, 그렇게 수고스럽게까지 작업하고 싶지는 않다. 잘 될지도 모르겠고…식물은 별도로 생각하기로 하자. 그게 아니더라도 해야 할 일은 많으니까.

타격 효과 강화

3.13

배경을 만들기 위해서 필요한 툴을 기다려야 하기 때문에, 남는 시간에 전에 구상해두었던 이펙트를 만들어 보기로 하자. 직접적인 파티클 타격 효과는 아니고, 때렸을 때 삐죽이는 셰이더 효과와 빛이다. 만화에서 보듯 충격이 가해졌을 때 고양이가 털을 세우는 것처럼 몸의 일부가 뾰족하게 보였으면 좋겠다.

원리는 배면법과 같다. 단지 부분부분 적용된다는 것 뿐이다. 이제 이걸 타점에서 뻗어나가도록 벡터화 시켜주어야 한다. 타점을 구한 후엔 빛도 추가할 수 있을 것이다.

타점을 벡터 프로퍼티로 뺀다. 이는 당연히 월드좌표로 계산될 것이다. 그렇다면 현재 버텍스 셰이더에서 오브젝트를 HClip으로 바로 변환하던 것을 버텍스만 월드변환해서 섞은 뒤 WorldToHClip으로 바꾼다. 그런데 이런 함수가 있던가…? 있다! ( UNITY_MATRIX 형제 쓰기 싫어.)

이제 타점을 계산해서 맞을 때 넣어주어야 한다. 코드 계산으로 넘어가자.

타점은 충돌박스끼리 충돌했을 경우, 그 중심점을 구하면 될 것이다. 이런 건 챗GPT가 잘 알 것 같다.

using UnityEngine;

public class ManualOverlapCenter : MonoBehaviour
{
public BoxCollider2D boxCollider1;
public BoxCollider2D boxCollider2;

void Update()
{
if (boxCollider1 != null && boxCollider2 != null)
{
// 첫 번째 박스컬라이더의 경계를 구합니다.
Vector2 min1 = boxCollider1.bounds.min;
Vector2 max1 = boxCollider1.bounds.max;

// 두 번째 박스컬라이더의 경계를 구합니다.
Vector2 min2 = boxCollider2.bounds.min;
Vector2 max2 = boxCollider2.bounds.max;

// 겹친 영역의 최소 및 최대 지점을 구합니다.
float minX = Mathf.Max(min1.x, min2.x);
float minY = Mathf.Max(min1.y, min2.y);
float maxX = Mathf.Min(max1.x, max2.x);
float maxY = Mathf.Min(max1.y, max2.y);

// 겹친 영역의 중심점을 계산합니다.
Vector2 overlapCenter = new Vector2((minX + maxX) / 2f, (minY + maxY) / 2f);

// 겹친 영역의 중심점을 출력합니다.
Debug.Log("Overlap Center: " + overlapCenter);
}
}
}

테스트는 안해봤으나 언뜻 보기에는 맞는 것 같다. 타점은 구했는데, 이걸 셰이더 코드와 연동해주어야 한다. 캐릭터 셰이더 중 해당 프로퍼티가 있는 셰이더를 골라낸 후에, 데미지를 입을 때 값을 변경해 주어야 한다.

…는 타임오버. 내일 해보도록 하자.

러프한 구현 결과, 타점을 중심일 땐 꽤 괜찮은 결과를 보여주지만, 타점이 아래나 위일 땐 그렇게 인상적이지가 않다. 그렇다면 그냥 항상 중간일 때 출력하면 어떨까?

오.. 훨 괜찮은 듯?

3.14

효과가 괜찮은 것 같으니, 셰이더 전반에 추가를 해야 한다. 얼굴이나 눈에는 필요가 없고, 큰 덩어리를 구성하는 셰이더에만 추가하면 될 것이다.

그런데 그랬더니 너무 많이 뾰족하다… 좀 줄여보자.

0.005정도면 어떨까

적당해 보이긴 하는데, 헤어에 적용된 건 얼굴을 너무 많이 뚫는다. 손에는 선이 너무 많다.

헤어는 빼고, 말단부는 제외하는 편이 좋아보인다.

…로 하려다가 헤어는 역시 아쉬워서 추가하고 말단부와 무기만 제거했다. 추가로 랜덤 버텍스는 0.003으로 줄였다.

가시가 얼굴을 뚫는 현상이 많으니, 아무래도 뒤로 배치하는 편이 좋을 것 같다.

한결 좋아보인다.

다음은 타격 시 빛효과…인데, 타임오버. 내일 하자.

TEXCOORD7을 쓰는 날이 올 줄은… 자리가 꽉 찼다.

타점을 기반 DOT연산을 해서 옴니라이트처럼 뿌릴 것이다.

이것은 Distance이다. 이걸 곱해주면

요렇게 되는데… 왜 칼이 안나오지?

무언가..무언가 잘못됐다.

…라고 생각했는데, 플레이를 하면 또 잘 된다. 뷰포트에서 실시간 적용에 문제가 있는 모양.

일단 타점을 중심으로 한 빛은 구현했다. 타격효과는 플레이어마다 색이 다를테고, 이 빛이 데미지를 입은 플레이어에게 뿌려져야 한다.

완성된 구현. 음. 하지만 이것이 썩 괜찮아 보이지는 않는다…

음.. 아닌가? 쓸만한가..?

얼굴에는 왜 안나오지…?

히트 벡터는 제대로 들어온다… 흐음.

원인을 찾았다. 얼굴에는 가시를 일으키지 않기 때문에 타점 갱신이 안되고 있었다.

이젠 눈이 문제. 입은 괜찮아 뵈는데…

이렇게 플레이어에 따라 색을 바꿔줄 예정.

이제 눈동자에도 색깔이 먹는다. 눈동자는 남기고 싶었는데, 채널이 모자라서 그만.

셰이더 손댄김에 전부터 손보고 싶었던 역광 볼드 코드를 집어넣어보자.

역광의 색은 배경을 완성한 후 씬 제어 컴포넌트에서 처리해줄 예정이다.

완성된 것 같으니 이제 다른 캐릭터에게도 삐죽을 심어주자.

밤 배경엔 백라이트를 바꿔주어 분위기를 맞출 수 있다. 역시 역광은 예쁘다.

이제 셰이더에서 할 수 있는 것은 다 한 것 같으니 다시 배경으로 돌아가자.

배경 #1

3.12

드디어 배경 작업이다. 여느 작업처럼 이것도 몇 가지 준비 과정이 필요하다. 처음부터 모든 걸 제작하기는 힘이 드니 모델을 구입해 사용할 예정이다. 지인이 알려준 킷배시와 웹툰 준비 때 자주 들락거렸던 에이콘을 이용해 보자.

https://kitbash3d.com

https://acon3d.com

에이콘은 다양한 모델이 있지만 스케치업 파일이 대부분이 공수가 다소 들어간다. 스케치업에서 fbx로 내보내기를 통해 블렌더로 임포트해야 하기 때문. 킷배시는 월별 구독제로 모든 모델 이용이 가능하고 전용앱으로 블렌더에 바로 임포트가 된다. 이걸 주력으로 쓰되 부족한 것들은 에이콘에서 채우는 식으로 작업하기로 하자.

으음- 할인 폭이 크긴 하지만, 1년이나 쓸 것 같지는 않아…

이런 모델들은 대체로 폴리곤이 너무 많아서 게임에서는 바로 쓰긴 힘드니까, 심플리곤을 미리 깔아두자. 블렌더에도 Decimate가 있지만 심플리곤이 더 최적화를 잘 해준다.

https://www.simplygon.com

그런데 SDK를 받는 곳이 없네…?! 사이트가 바꼈다… 어찌된 일이지.

백방으로 방법을 찾아보았지만, 예전처럼 SDK를 무료로 제공하지는 않는다. 차라리 유료로 팔면 좋겠는데 무조건 B2B인 모양이다. 30일 무료버전 신청란에 회사이름과 도메인을 반드시 적으라고 되어 있다.

에이…그럼 이건 못쓰겠네… 그냥 Decimate를 써야겠다.

3.12

작업에 앞서 스테이지를 좀 길게 만들자. 그러려면 카메라 워킹이 필요하다.

모든 작업의 시작과 끝은 스파2가 있다. 이 오래된 게임의 스크롤방식을 벤치마킹했는데, 이것이 생각보다 어려운 과제였다. 캐릭터들은 가까이 있거나 화면 끝에 있다면 스크롤 되지 않고, 일정거리 이상 벌어져야 스크롤 된다. 그대로 구현하되, 카메라가 갑자기 튈 경우를 방지해 이동에 Lerp를 약간 넣었다.

스크롤이 있으니 확실히 공간이 넓어 보인다. 얼마나 넓어야 할까…일단은 10미터로 책정. 너무 멀어도 도망치는 적을 잡기 피곤하니, 충분해 보인다. 상하 스크롤은 정신 사나워서 취향이 아니므로 건너뛰고, 확대 축소는 작업량이 많으니 포기하자.

잡기 마무리 작업

3.9

바탕이 되는 작업은 끝났으나 여전히 일이 남아있다. 먼저 만들었던 작업들의 피격모션을 각캐릭터에 맞게 복사해주어야 한다. 또한 던지기 후 좌우가 뒤집히는 문제를 해결해야 한다. 그리고 타격형 잡기의 경우, 몇 번을 칠 지 스크립트에서 제어할 수 있게 코드를 수정해야 한다. 그 외에 작업보드에 적혀있는 버그들도 수정하고 가도록 하자.

유저는 같은 캐릭터를 고를 수 있어야 하므로 앨리스에게도 앨리스에게 잡히는 모션이 필요하다. 하지만 같은 파일을 링크할 수는 없으니 임시 파일을 만들어 진행해야 한다. 피격모션은 달래를 기준으로 제작된 것인데, 키에 큰 차이가 없어 그대로 사용해도 무방해 보인다. 앨리스의 발 잡기에 DamageSlam이 들어가므로, 이 모션도 함께 가져와야 한다.

GetUpSlam모션은 달래의 모션으로 일어나게 된다. 이것을 앨리스의 모션으로 바꿔주어야 한다.

잡기를 위해 데미지 모션은 4가지의 스테이트가 더 필요하다.

  • DamageFlying
  • DamageSlam
  • DamageSlamLying
  • GetUpSlam

지금까지 설정한 앨리스의 스테이트는 60개 정도 된다. 남은 것은 도발(D버튼), 등장 2개, 승리 2개, 스핀형 데미지, 스턴, 필살기 3개, 초필살기.. 아이고, 아직도 많구나!

작업 중 통일성이 없는 부분이 발견됐다. 앨리스의 허리는 Euler를 사용하고, 닐리의 허리는 쿼터니온을 쓴다. 이렇게 되면 작업이 호환이 되지 않기 때문에 확실하게 정의하고 가야 한다. 오일러를 쓸 필요가 없으므로 쿼터니온이 옳다. 하지만 이걸 해결하기 위해선 앨리스로 거슬러 올라가야 한다. 모든 애니메이션의 오일러 키를 쿼터니온으로 변경해 줄 것인가? 문제가 생기는 모션만 부분적으로 변경해줄 것인가…

스크립트가 필요하다. 구상은 잠자리에서. 오늘은 여기까지.

3.11

오일러를 쿼터니온으로 변환한다. 구상은 쉬웠는데, 구현이 꽤 어려웠다. 이제 챗GPT없으면 작업못할 듯.

이렇게 오일러로 되어 있는 키를

요로케 쿼터니온 변환해 준다.

ARP에도 회전값 변환기가 들어있기는 한데, 사용이 불편해서 새로 만들었다. Euler모드를 맞춰놓고 돌려야 제대로 작동하는 걸로 보아 비주얼키를 베이킹하는 모양이다. 게다가 액션그룹 지정도 안해줘서 목록이 밖으로 빠져나간다… 여러모로 언젠가는 해야 할 작업이었다.

이제야 다음 스텝으로 넘어갈 수 있다. 내일 하자..

오로라가 오로라를 잡았을 때. 아이고 작다. 이런 문제 때문에 Caught모션을 완전히 공유해 쓸 수가 없다. 사실 키가 같더라도 세컨더리 애니메이션 문제도 있긴 하다.

그래서 오로라는 다리를 쭉 편채로 맞아야 한다.

원래 멱살을 잡아 채는 모션인데… 오로라의 키가 작아서 머리채를 끌어당기게 됐다(…) 유독 오로라에게 더 가혹해져 버린 닐리.

이걸로 피격자의 모션은 일단락됐다.

이제 좌우 반전 문제를 해결해 보자. 좌우를 반전해 쓰고 있으니 완전히 해결할 수는 없고, 움직임이 큰 시점에 반전 모션을 미리 바꿔주어야 한다.

하지만 반전을 시키는 방법은 어렵다. 일단 모든 키를 반전시키는 방법은 내가 아는 한 블렌더에서는 지원하지 않는다. 다시 스크립트가 필요하다.

데이터를 살펴보니 원리는 단순하다. 반전할 수 있는 키의 리스트를 뽑은 후에 위치는 x, 회전은 y,z를 반전해주면 된다.

…하지만 이걸 굳이 해야 하는가?

모션을 2개로 나눈 후의 세컨더리 애니메이션은 생각보다 일이 크다. 잡기 애니메이션은 사실 상당히 러프하게 만들었는데, 수량이 너무 많기 때문이다. 모든 걸 제쳐두고 돌아가게 하는 것이 중요하다.

그렇다면 이건 포기하는 편이 낫겠다. 뭣이 중헌디.

오로라의 잡기

3.6

오로라는 잡기 캐릭터인만큼 잡기 기술이 많을 예정이지만, 스킬을 제외하고 제작 중인만큼 기본잡기만 구현하기로 한다. 기계팔로 잡기가 생각보다 이미지가 안떠올라서 킥잡기를 먼저 작업 중

요렇게 올라가서…

요렇게 때릴 예정.

피격자의 시작 애니메이션도 완료. 애니메이션은 타격자 중심으로 만들어지기 때문에, 키가 다를 경우 피격자의 애니메이션은 조금씩 달라져야 한다.

3.7

이제 마무리 공격. 쾅! 피격자의 상하가 뒤집히지 않기 때문에 Flying이 아닌 DamageJumpHeavy로 연결되는 편이 자연스러울 것이다. 이제 엔진에 넣어보자.

얍얍얍! 잘 작동하는 것 같다. 이 잡기는 기존과는 달리 모든 행동이 끝난 후에 방향이 변하지 않는데, 이에 대한 처리가 추가로 필요하다.

내일은 지인에게 배경 제작법을 전수받으러 가야 한다. 따라서 앨리스가 반대방향으로 날아가는 문제는 모레 처리하도록 하자.

3.8

강펀치잡기. 원래 계획은 잡은 후 바닥에 쾅! 내리찍을 예정이었지만, 내리찍은 후의 방향이 DamageSlam과 맞지 않아서 관두었다. 타격형 잡기가 워낙 어려워서인지 던지기는 이제 좀 수월하게 느껴진다. 엔진에 넣어보자.

잘 작동한다. 이로서 오로라의 기본잡기 모션도 일단락이 되었다.

하지만 잡기 작업이 온전히 끝난 것은 아니다. 잡기 후에 좌우 반전 문제를 해결하고, 피격자의 모션들을 복사해서 각 캐릭터에게 붙여주어야 한다. 그리고 유니티의 애니메이션 컨트롤러에 클립으로 등록도 해주어야 한다.

달래의 잡기

3.4

닐리에 비한다면 달래의 잡기는 심플하다. 때문에 엔진 확인 작업 할 것도 없이 바로 디테일 작업에 들어갔다. 별달리 삽질이 없어서 기록할 것이 적다.

3.5

강펀치용 잡기. 엔진에 적용을 완료. 이것도 의도했던 건 아닌데 달래가 꽤나 속도감있는 캐릭터가 됐다. 나쁘지 않아 보인다.

2번 잡기 러프. 어깨위에 올라갈 땐 오로라를 염두해두어야 한다. 모션을 맞추려면 키가 가장 작은 캐릭터에 맞추어야 하기 때문이다.

달래의 잡기 모션 완료. 이제 고민되는 오로라의 차례.

닐리의 잡기

2.28

닐리의 잡기에 들어가기 전에 중요한 사실을 하나 깨달았다. 지금까지는 잡기 세트를 만들 때 캐릭터의 방향을 반대로 놓고 만들었는데, 링크로 가져온 캐릭터는 게임오브젝트처럼 스케일을 반전시켜도 애니메이션이 깨지지 않는다! 그러므로 캐릭터의 방향을 맞춰서 애니메이션을 만들면 좀 더 나은 품질의 애니메이션을 제작할 수 있다.

▲이렇게 잡기가 끝났을 때 데이터 상으로 변화가 일어나며 좌우가 바뀌는 것이 못내 아쉬웠는데, 이런 현상을 방지할 수 있다.

하지만 이걸 하려면 기존의 애니메이션을 모두 반전시켜야 한다. 조금 귀찮다… 그래도 해 보자.

반전은 키가 있는 프레임을 돌며 ctrl+c -> ctrl+shift+v를 누르면 된다. 이 때 새로운 키가 생기는 걸 원치 않으니 Replace로 조절해준다.

물론 코드를 조금 수정해주어야 한다. 성공이다! 이제 매우 깔끔하게 일어난다.

닐리는 처음엔 예상에 없었던 선생님 이미지가 나왔으니 강손 잡기는 궁디팡팡! 잡은 후에 여러번 때리는 형태로서, 상대가 버튼을 마구 누르면 좀 빠르게 벗어날 수 있다. 달심의 꿀밤때리기나 브랑카의 깨물기 형태의 잡기다. 이를 위해서 잡기는 기본, Loop, End. 총 3가지로 세분화되어야 한다.

코드는 나중의 일이다. 일단 애니메이션을 만들자.

그동안 때렸으니 이제 좀 맞자. 앨리스.

둘 다 무기는 다 어디 갔나…싶지만, 뭣이 중헌디?! 애니메이션 제작 도중 로브에 컬러가 튀는 건 … 왜 그럴까… 내일 심층분석을 해봐야겠다. 오늘은 여기까지.

2.29

닐리의 로브가 튀는 현상은 본이 엉켜있었던 것이 원인으로 파악됐다. 일단은 다행스러운 일.

오늘의 깨달음. 루프 애니메이션을 도입부와 이어지게 만들고 별도의 파일로 분리해낼 때엔 키를 복붙하기보단 액션전체를 카피한 후 중간키를 삭제하는 편이 낫다. 0프레임에는 트랜스폼 이외의 키들이 생각보다 많이 설정되어 있기 때문이다.(회전모드나 제약조건의 영향력 등)

엔드 모션은 테스트가 좀 필요해서 넣어보려니 문제가 많다. 스테이트 매니저를 공용으로 옮기고, 발생했던 버그를 잡고, 그 외에도 한참 리팩토링… 마음이 급해도 코드는 최대한 차근차근 진행해야 나중에 일이 적다.

오늘의 깨달음 2. FSM을 처음 시작할 때 LateUpdate()가 왜 있는지 알게 됐고, 스테이트 처리를 하면서 Awake()가 왜 있는지도 알게 됐다. Start()에서 마땅히 있어야 할 정보가 없을 때, 그리고 그 순서가 ‘얘 다음’이라고 장담할 수 없을 때 미리 만들어둘 확실한 절차가 필요한 것이다.

코드작업을 마무리하지 못했다. 현재는 잡기 관련 코드를 FSM에서 처리하고 있는데, 이걸 게임매니저로 옮겨야 한다. 오늘은 여기까지.

3.1

잡기 코드를 게임매니저로 옮겼다가 다시 개별캐릭터로 가져왔다. 상대방의 FSM을 조작하는 건 월권이라 생각해서 게임매니저로 옮기려고 했던 건데 괜한 삽질이었다. 달리 생각하면 애초에 잡기 자체가 상대방을 조작하는 것인지라 이 쪽에서 처리하는 게 맞는 것 같기도 하고…결국은 눈물의 롤백. 시간만 날렸다.

어쨌거나 코드는 완성됐다. 남은 것은 상대가 발버둥을 치면 좀 더 일찍 잡기가 풀려야 하는 것인데, 2P입력을 아직 안만들었다(…) 뭐, 중요한 것은 아니니 나중에 실제 데미지 처리 하면서 함께 풀도록 하자.

이제 애니메이션 디테일작업에 들어가야 하지만 타임오버. 오늘은 여기까지.

3.2

익스포트한 데이터는 완벽하지 않다. 이는 게임 엔진이 제약조건 기반으로 움직이는 것이 아니라 베이킹된 키에 의해 움직이기 때문이다.(다리가 땅에 붙지 않고 덜덜 떨리는 것을 볼 수 있다.)

이에 대해 프로젝트에 블렌더를 사용하고 있는 지인과 이야기를 했었는데, 키프레임 압축률에 의해 애니메이션이 깨지고 있다는 결론을 얻었다! 처음엔 ARP의 익스포트 문제일거라 생각했는데 내보낸 FBX를 블렌더로 임포트해본 결과, 데이터는 아무런 문제가 없었다. 이럴 수가! 유니티의 최적화 문제였다니.

키프레임 리덕션을 하고 회전 오차를 기본값인 0.5에서 0.1로 줄여주었더니

발이 정상적으로 땅에 붙어 작동한다. 이것이 최적화에 얼마나 악영향을 끼칠 지는 모르겠지만, 적어도 문제를 해결할 방법을 찾았다는 점이 고무적이다.

문제 2번. 잡기를 한 캐릭터를 반대편으로 던져보낼 경우 캐릭터가 반전된다. 이 경우 무기를 쥔 손이 반전되기 때문에 임시로 차일드를 왼손에서 오른손으로 변경해줄 필요가 있다. 제약 조건 중에 Child Of를 양손에 걸고 Influence에 키를 주면 될 것이다. 그런데 이것이… 안된다. 차일드 오브가 2개 이상될 경우 프레임 지연현상이 일어난다.

테스트 결과, 이것이 렌더링을 할 때는 문제가 없다. 제약 조건이 여러 개일 경우, 실시간 뷰포트의 연산을 줄이기 위해 이런 선택을 한 것으로 보인다. 이것이 실시간 익스포트를 할 경우 문제가 된다. 이건 일전에 목이 달랑거리는 현상과 비슷한 문제로 보이는데, 결국 메모리의 문제로 결론이 났었고

단순한 구조로 테스트를 해본 결과는 정상인 것으로 보아, 그 가설에 더욱 힘이 실리는 분위기이다. 그렇다면 이건 답이 없다…별 수 없이 그냥 매프레임 노가다를 해주는 수밖엔. 데이터가 너무 무거운 모양이다.

하지만 삽질은 결국 도움이 되는 무언가를 남긴다. 이 오차를 해결할 수 없을까?싶어 옵션을 뒤지던 중에 애니메이션 오토키프레이밍에서 ‘Only Insert Needed’란 옵션을 발견했다. 이 옵션은 트랜스폼 3대장에 모두 키를 주지 않고 움직인 트랜스폼에 대해서만 키를 준다. 이동과 회전값의 키 이격은 매우 흔하게 발생하므로 이 옵션은 상당히 유용하다. 소득은 있었다!

발 잡기(→K). 윙가르디움 레비오사!

아.쓰다보니 Only Insert Needed가 별로 안편하다[…] 왜 기본값이 꺼져있는지 이해하게 되었다.

닐리의 잡기도 완료!

앨리스의 잡기

2.23

공중에서 점프해서 캐릭터끼리 맞닿을 시에 덜덜거리는 현상이 있다. 이것을 추적하기 위해 코드를 살피던 중, 충돌코드에서 유니티가 기본적으로 제공중인 Intersect()를 쓰는 코드를 발견했다. 오래 전에 이것이 다리의 충돌검사를 제대로 하지 못한데다, 불필요한 연산이 들어가 있어서 직접제작한 걸로 바꿨는데 공교롭게도 이것이 캐릭터를 뛰어넘을 때 도움이 되고 있었다. 연산을 제대로 하니 다리 충돌이 걸려 캐릭터를 못뛰어넘는다.

이론적으로 접근하자면 물론 충돌 박스가 작게 수정되는 것이 맞다.그런데, 가만… 충돌에 한해서 다리를 빼는 것이 더 괜찮은 플레이경험을 제공한다면, 그 쪽이 낫지 않을까?

foreach (BoxCollider2D player1Collider in player1FSM.bodyColliders)
{
if(player1Collider.name.Contains("leg")) continue;

foreach (BoxCollider2D player2Collider in player2FSM.bodyColliders)
{
if(player2Collider.name.Contains("leg")) continue;

이제 뛰어넘을 수 있다.

앨리스는 늘 그래왔다. 언제나 첫작업으로 쓰였기 때문에 많은 시행착오를 거쳐 완성하곤 했다. 이번에도 그러고 있다. 잡기 첫모션 제작 중. 오늘은 여기까지

2.24

잡기 1번은 완료. 방향키+강펀치로 발동한다. 어느 쪽으로 집어던질 지 결정할 수 있다.

2번의 구상은 좀 복잡하다. 한 팔로 멱살을 끌어당긴 후, 균형을 잃은 상대의 등 혹은 뒤통수를 돌아서 발로 차서 넘어뜨리고 등을 찌른다. 복잡하다! 구상대로 잘 될까… 이 작업만 며칠 걸릴 지도 모르겠다.

타이밍은 대충 이렇다. 이제 디테일 파보자….지만 오늘은 여기까지인가.

2.25

오늘의 깨달음. FK-IK를 오가는 애니메이션을 할 때엔 겹키를 주는 것보단, 그냥 해당키에서 각각 FK/IK스위치를 한 번씩 해주는 것이 더 효과적이다. 이렇게 되면 각각의 컨트롤러가 알맞게 보간되어 알아서 부드러운 애니메이션이 된다. 맥스-바이패드의 Plant Key를 사용하던 버릇 때문에 지금까지 겹키를 주는 방식을 택하고 있었는데 블렌더에서는 그럴 필요가 없을 듯? 확실히 적응하니 더 편하다.

..지만 그렇다고 하더라도 점프직후의 까치발을 위해선 겹키를 주어야 한다. 음.. 뭐 그렇다고 해도 이 정도면 품이 많이 드는 것이 아니라 괜찮다.

캐릭터 2명은 뷰포트에서는 꽤 느려서 풀 프레임으로 재생할 수가 없다. 때문에 솔리드 모드에서 전체 모션을 확인해야 한다. 오늘은 여기까지.

2.26

칼로 등을 찌르는 모션을 구현하기 위해서는 몇 가지 단계가 필요하다. 애초에 염두에 두긴 했으나 생각보다 더 귀찮은 작업인 것 같다. 먼저, 피격자가 등을 차인 후 앞으로 쓰러지는 모션은 분리해 제작되어야 한다. 이는 몇몇 기술이 머리를 쳐서 바닥에 처박힐 때 공용으로 사용될 것이다. (KOF의 료가 사용하는 수박깨기처럼) 그렇다면 필연적으로 일어나는 모션이 추가된다. 이를 DamageSlam, GetUpSlam으로 제작한다.

그 후엔 앨리스의 잡기 피격 모션으로 복사해서 이식해서 다른 모션과 이어지도록 제작해야 한다. 발차기 이후의 스테이트를 따로 쓸 수도 있겠으나, 그 어떤 게임적 요인으로 인해 위치가 정확하지 않을 가능성이 있다.

또한 화면 끝에서 기술을 사용할 경우, 발차기를 하는 순간에 필요한 거리만큼 밖으로 빼내주어야 한다. 잡기 세트 모션중에는 충돌체크를 하지 않기 때문에, 수동으로 밀어주어야 하는 것이다. 이 때 밀어주는 거리는 어느 정도 정형화되는 편이 좋다. 안그러면 위치가 튈테니까.

마지막으로 모든 세트 모션이 끝난 후, 제자리에 다시 독립된 개체로 누워있을 수 있도록 피격자의 위치를 교정해주어야 한다. 하지만 이 때까지도 피격자는 Caught모션일 것이다. 잡기는 타격자가 먼저 활동이 가능해지기 때문에 충돌 무시 플래그를 피격자에게 심어놨는지, 타격자에게 심어놨는지 조사해 봐야한다. 이것이 피격자라면 타격자로 바꾸자. 또한 스테이트에서 길이를 15프레임 정도 길게 설정해서 누워있는 대기시간을 주어야 한다. 현재 이것을 Length를 강제로 교정하는 식으로 처리해 놨는데 AdditionalLength같은 식으로 변경하는 쪽이 좋아보인다.

오늘의 깨달음. 차일드를 자주 바꾸는 본은 traj보단 그냥 부모없음.으로 설정하는 편이 애니메이션하기에 더 편하다. 주로 대각선 애니메이션을 할 때!

먼저 DamageSlam, GetUpSlam을 제작

피격자의 애니메이션 제작 중. 애니메이션 자체는 내일 마무리할 수 있을 것 같다.z

2.27

좌우를 와리가리하는 애니메이션을 제작하다보니 머리카락 앤젤링의 코드에 문제가 있는 것을 발견했다. 해당코드는 뷰에 따른 UV를 위아래로 움직이기 위해 바이탄젠트를 쓰고 있는데 좌우가 반전되면 방향도 바뀌는 문제가 있는 것이다. 게임은 로컬 스케일의 X값을 사용해서 캐릭터를 반전하고 있으니 이를 알아내어 곱하면 될 것이다. 이를 위해 챗GPT에게 의뢰하니, unity_ObjectToWorld라는 행렬을 이용하면 된다고 한다.

| r11  r12  r13  tx |
| r21 r22 r23 ty |
| r31 r32 r33 tz |
| 0 0 0 1 |

행렬의 대각선값(r11,r22,r33)은 스케일이다. 나머지는 회전값, 오른쪽 3열은 이동값을 기록한다. 따라서 [0].x를 쓰면 될 줄 알았는데…정작 해보니 변화가 없다…? 생각해 보니 그건 어디까지나 메쉬 오브젝트의 행렬이지, 게임오브젝트와는 관련이 없는 것도 같다. 그럼 수동으로 해 줄수밖에… CPU가 수고 좀 하자.

스케일팩터를 추가하고 방향이 변할 때마다 바꿔주어야 한다.

마참내! 앨리스의 잡기 완료. 코드를 또 한 번 신나게 리팩토링했다.
FSM코드만 1000줄이 넘어가지만 아직까지는 알아볼만 하다.

이제 닐리의 잡기로 넘어가자.

잡기 준비작업

2.19

작업의 방향성을 정하는데 많은 시간을 소요했다. 사실 그동안 계속 고민해오던 문제이긴 하다. 결과적으로 쉬운 방법과 어려운 방법이 있고, 양자택일을 해야 한다. 방향을 정하면 돌아가지 못한다. 쉬운 방법은 모든 잡기를 대략적으로 비슷하게 만드는 것이고, 어려운 방법은 잡기마다 세트 모션을 만드는 것이다.

세트 모션을 만들 경우 잡기 모션 하나당 모든 캐릭터에게 이를 당하는 모션을 추가해주어야 한다. 모든 캐릭터가 잡기를 가지고 있을 테니 그 수는 곱절로 늘어난다. 때문에 처음엔 이를 고려하지 않았는데, 쉬운 방법을 선택하자니 마음이 동하지 않았다. 그래서 스스로 재앙을 만들었다.

시작하기 전에 몇가지 확인이 필요하다. Link로 애니메이션 제어가 되는가?

안된다. 링크로 연결된 객체는 그 무엇도 제어할 수가 없다. 그렇다면 블렌더를 2개 켜놓고 애니메이션을 바꿔서 저장 후에 리프레시하는 방법을 사용해야 된다. 게다가 씬에 같은 파일을 링크할 수는 없다. 흐음.. 그렇다면 같은 캐릭터일 경우 작업용 파일을 복사해서 사용하고, 모션을 만든 후에 합쳐야 한다. 조금 귀찮게 됐다.

컬렉션을 링크하면 Scene탭에 인스펙터가 보인다. 여기서 새로고침을 해주면 저장한 모션으로 변경된다.

먼저 코드 구현을 위한 더미 모션을 만들자. 앨리스는 힘이 세다는 설정이므로 집어 던진다. 이 때 필요한 모션이 다음과 같다.

  • 앨리스 : GrapplingPunch (Kick을 눌러 잡으면 모션이 달라야 한다.)
  • 달래 : GrappledAlicePunch , DamageGrappledFlying(이 모션은 Knockdown으로 연결되어야 한다.)

데미지 모션은 잡기상황을 벗어났을 때 공용으로 사용할 모션이다. 잡기는 여러형태가 있을 수 있다. 예를 들어 백드롭을 하면 DamageGrappledBackdrop이 되어야 할 것이다. 여기까지 고려해서 모션명을 정해야 나중에 삽질이 적다. 파이썬을 제외한 변수나 리소스명은 낙타문자를 사용하고, 숫자를 사용할 땐 가독성을 위해 양 옆으로 언더바를 붙인다.(숫자는 대체로 중요한 정보이다!) 오래된 내 습관이다.

머리가 굳었는지 오랫만에 코드를 보니 정신이 아득해진다. 내일 하자…

2.20

잡기는 가까이 있을 때 방향을 정하고 펀치나 킥을 누르면 발동된다. 하지만 한 가지 의문이 생긴다. 만약 두 캐릭터가 동시에 잡기를 했다면? 이 경우 Update를 먼저 도는 쪽이 이기게 된다. 대체로 데이터가 먼저 만들어지는 1P일 것이다. 그렇다면 1P에게 조금이나마 유리한 부분이 생기게 된다. 유저가 이걸 확인할 길은 없다. 잡기는 딜레이가 없기 때문이다. 때문에 그냥 ‘아차, 내가 늦었구나.’라고 생각하게 되고, 게임은 그럭저럭 돌아갈 것이다.

하지만, 이건 분명히 불합리하다. 격투게임은 원래 1프레임차이로 명암이 갈린다. 그렇다면 잡기에도 명확한 기준이 필요하고, 동시에 잡았을 경우, 공격이 무산되어야 한다. 때문에 2가지 모션이 더 필요하다. 잡기를 시도하는 것과, 동시에 잡았을 때 실패하는 것

  • GrapplingTry
  • GrapplingFail

재료를 더 준비했으니 코딩을 해보자.

러프한 구현. 여엉차!

2.21

작업을 하다보니 잡기를 한 쪽과 당한 쪽의 코드명이 헛갈리는 경우가 많았다. 이에 잡는 쪽은 Grappling, 잡힌 쪽은 Caught로 바꿔쓰기로 했다.

잡기 테스트. 아휴… 생각보다 많은 부분을 뜯어고치고 있다.

2.22

캐릭터마다 잡기는 모두 넣을 예정이지만, 미래는 알 수가 없다. 어쨌거나 강펀치 혹은 킥을 누르면 잡기를 수행하는데 이 데이터가 없을 경우 일반 공격이 나가게 하는 처리가 필요하다. 그리고 이걸 하려면 스테이트 매니저를 캐릭터별로 분리해야 한다. 좀 더 먼 훗날의 일인 줄 알았는데, 생각보다 일찍 분리해야 한다.

앨리스의 설정을 담은 초기화 함수를 만든다. 기본 데이터 위에 캐릭터 별로 다른 데이터를 얹는 식이다. 스테이트 구조체는 정보가 많아서 이를 일일이 컨트롤하기는 힘들기 때문에, 필요한 데이터만 선별해서 바꿔주는 식이다. 잡히는 정보가 없다면 해당기술이 없는 걸로 간주하고 그냥 근접공격을 내보낸다.

잡기든 다른 공격이든 넘어졌다가 일어났을 때 바로 잡히면 안되기 때문에, 약간의 무적시간을 준다. 무적시간은 state에 귀속되어 있지만, GetUp스테이트만은 예외다. 이로서 공격자는 재공격이 불리하게 되고, 피격자는 반격을 노릴 수 있다.

동시에 잡았을 땐 그 누구도 이득을 얻지 말아야 한다.

더미모션을 넣어놨더니 ‘앗..아…’하는 애매한 마무리가 되어버렸다.

예정했던 잡기용 코드 작업은 끝났다. 하지만 프로그램 작업으로 넘어온 김에 노션에 써놨던 작업을 마무리 하고 넘어가도록 하자.

오로라의 애니메이션

2.12

명절이 끝났다. 다시 작업을 시작하자.

오로라의 대기자세는 고민이 많았다. 양손으로 가방끈을 잡고 있는 자세가 실루엣을 단순하게 만든다는 점 때문이었다. 하지만 가방이 주력인 캐릭터이기 때문에 이보다 어울리는 자세를 찾기가 힘들었고, 결국 초안대로 작업되었다. 애니메이션을 적용하니 예상보다 어색하지는 않다. 다행인 일이다.

오로라가 유독 프레임레이트가 안나온다. 본은 닐리가 더 많을텐데… 이것이 블렌더 4.0의 문제일지, 아니면 오로라의 문제인지는 지켜보도록 하자.

유니티로 컨버팅 완료. 이전에 기계팔 컨버팅을 위해 커스텀 본 세팅을 모두 해둔 상태였기 때문에, 큰 문제는 없었다.

머리카락 위로 보이는 눈썹 셰이더가 가장 잘 보이는 캐릭터가 아닐까.

키는 작은데 다리는 더 굵다.

2.13

오로라의 기본 애니메이션 완료. 우우.. 후드티의 리깅이 너무나 후지다. 그것도 문제지만, 실루엣 문제가 의외의 말썽을 일으킨다. 가방을 잡고 있는 팔이 덩치를 작게 만드는 바람에, 점프할 때 갑자기 덩치가 커지는 것처럼 느껴진다. 마치 목도리 도마뱀같이!

기본 모션에 시간을 좀 더 써보는 편이 좋겠다. 하지만 오늘은 여기까지

2.14

프레임 레이트가 잘 안나온다. 캡처가 제대로 안될 정도로 느리다. 블렌더는 죄가 없고, 오로라의 데이터가 무겁다. 그런데 그 이유를 잘 모르겠다. 분명 로봇팔은 고성능의 IK가 하나 더 달려있긴 하지만, 겨우 그것 때문에…?

보기와는 달리 잡기 캐릭이라 전반적으로 공격이 느려야 한다. 안그래 보이지만 실제로 해보면 꽤 답답.

2.15

오로라의 기본 공격 모션. 손이 하나만 나오면 느낌이 약한느낌이니 내일은 두개를 다 꺼내보자.

2.16

몸-> 얼굴을 때린다는 느낌으로 2타 가격.

앉아서 강손은 원래 이게 아니고, 웅크린 상태에서 로봇팔만 나가서 깍지를 낀 상태로 바닥을 치는 거였다. 그런데 예상외로 팔이 짧아서 깍지가 불가능한 게 아닌가. 계획이 틀어져서 ‘어떻게 하지…’ 라며 이것저것 움직여 보던 중에 나온 이상한 만세 자세를 만들어 놓고 일단 자러 갔는데, 다음 날 보니 이게 생뚱맞아 보이는 게 꽤 괜찮아 보였다. 그래서 그대로 확정지은 앉아 강손. 로봇팔은 양쪽이 다 나오는 것이 확실히 안정감도 있고 보기에도 좋아 보인다.

기계팔 제어가 생각보다 어렵고 시간이 오래걸린다. 생각해 보니 팔이 4개니까… 오래걸리는 것이 당연하다. 다리가 4개가 아닌 것이 어디냐… 싶기도

2.17

오토리그 프로엔 캐릭터 전체의 움직임을 제어하기 위한 c_traj이 있다. c_pos는 총 사령관이고, 그 아래 붙어서 전체적인 캐릭터의 위치를 조율하는 역할을 한다. 지금까진 이걸 사용하지 않았다. 필요한 경우가 없었던 것은 아니지만, 무기 본에 붙는 Child of 조건과의 상성이 좋지 않아 의도적으로 기피하고 있었다.(위치가 이상해진다.)

하지만 오로라의 경우 만세 포즈가 많아 대칭자세가 꽤 많은데, 이럴 경우 상당히 편리하다는 깨달음을 얻었다. 어차피 본의 일종이므로 Idle에 기본값으로 0을 넣어두고, 필요할 때 사용해서 깨지지 않으면 사용해도 된다. 엔진에도 잘 넘어가는 것을 확인했다.

점프킥 자세처럼 좌우 대칭모션에 사용하기 좋다.

오로라의 기본 공격모션 완료. 앞선 세 캐릭터는 제자리 점프킥의 모션이 달랐는데, 오로라는 달리 그럴 필요가 없어서 모션이 하나 적다.

이제 다 때렸으니 좀 맞자.

2.18

데미지 모션은 순간적으로 지나간다. 하지만 그 중에서도 정지 포즈에 신경을 써야 하는 모션이라면 바로 knockdown이다. 캐릭터가 누워있는 순간은 꽤 많다. 다리가 걸렸거나 KO됐을 때 캐릭터는 바닥에 눕는다.

애니메이션 중 바닥과 연관있는 애니메이션은 제법 난이도가 있지만 중요도는 낮은 편이다. 이렇게 바닥에 눕거나 튕기거나 일어서거나. 그렇다고 또 대충 만들면 티가 많이 난다. 아쉽게도 중력에 의한 물체의 움직임은 이미 우리 눈에 너무나 익숙하기 때문에, 모든 사람이 전문가적 시각을 갖추고 있기 때문이다. 들이는 공에 비해 보상이 적은 작업. 현업이라면 누군가는 이런 일을 해야 한다. 만약 내 일이 쉽다면, 누군가는 죽어가고 있는 것이다. 하지만 백수인 지금 이런 생각을 하는 것이 마치 전생의 일 같이 느껴진다. 백수 좋아~♬ 늘 새로워. 짜릿해.

오로라의 데미지 모션완료. 이로서 네 캐릭터의 기본 모션은 끝났다. 삽질을 여러차례 거쳐서인지, 오로라의 데미지 모션 작업은 작업시간이 좀 더 단축됐다. (하루만에 끝났다!) 데미지 모션에서 시간을 많이 잡아먹는 부분이 블렌더-유니티를 오가며 타격감을 보는 부분이었는데, 이 부분이 어느정도 공식이 정해지며 시간을 아낄 수 있었다.

이제 다음 단계로 넘어가자. 사실 FSM(Finite State Machine – 유한상태머신)을 제작하며 작업하지 않은 것이 하나 있다. 당시의 난 회사원과 백수의 중간 단계에 있었고 무언가 싱숭생숭한 기분 속에서 아트 작업으로 선회를 했었다. 하지만 이제는 작업을 해야 할 때가 온 것 같다. 바로 ‘잡기’다.