심플리곤이 없으니 그냥 블렌더에서 기본제공하는 Decimate : 0.1, 그리고 그림자가 너무 얇으니 볼품이 없어서 Fatten 0.005를 주어서 조금 두껍게 해주었다. 그런데도 폴리곤이 6300개 정도 된다.
하지만 메쉬 셰도우는 평면에서만 쓸 수 있다. 따라서 바닥에 그리는 오브젝트와의 큐문제가 있을 수 있기 때문에 배경보다 큐를 하나 더 높게 주어 항상 출력되도록 하자.
이제 배경보다 항상 위에 출력된다. 배경 아래로 들어가는 것보다야 이 쪽이 나을 것이다.
이제 다른 캐릭터들의 그림자를 생성해야 한다. 하지만 오늘은 타임오버.
3.20
그림자는 공통된 머티리얼을 사용하고 있으며 스테이지마다 그림자의 색을 변경할 수 있도록 설계되어 있다. 다만 빛 방향에 따라서 각도가 변하지는 않는다. 이는 역광이어도 마찬가지인데, 원리대로라면 그림자도 반전되는 것이 옳지만 이 경우 캐릭터의 위치를 빠르게 파악하는 정보역할을 하기 때문이다.
배경은 모델링을 구입해서 배치한 후, 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패스를 따로 분리해 저장한 뒤, 유니티의 그림자 감쇠값을 받아서 합성해주면 될 것 같은데, 그렇게 수고스럽게까지 작업하고 싶지는 않다. 잘 될지도 모르겠고…식물은 별도로 생각하기로 하자. 그게 아니더라도 해야 할 일은 많으니까.
배경을 만들기 위해서 필요한 툴을 기다려야 하기 때문에, 남는 시간에 전에 구상해두었던 이펙트를 만들어 보기로 하자. 직접적인 파티클 타격 효과는 아니고, 때렸을 때 삐죽이는 셰이더 효과와 빛이다. 만화에서 보듯 충격이 가해졌을 때 고양이가 털을 세우는 것처럼 몸의 일부가 뾰족하게 보였으면 좋겠다.
원리는 배면법과 같다. 단지 부분부분 적용된다는 것 뿐이다. 이제 이걸 타점에서 뻗어나가도록 벡터화 시켜주어야 한다. 타점을 구한 후엔 빛도 추가할 수 있을 것이다.
타점을 벡터 프로퍼티로 뺀다. 이는 당연히 월드좌표로 계산될 것이다. 그렇다면 현재 버텍스 셰이더에서 오브젝트를 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);
에이콘은 다양한 모델이 있지만 스케치업 파일이 대부분이 공수가 다소 들어간다. 스케치업에서 fbx로 내보내기를 통해 블렌더로 임포트해야 하기 때문. 킷배시는 월별 구독제로 모든 모델 이용이 가능하고 전용앱으로 블렌더에 바로 임포트가 된다. 이걸 주력으로 쓰되 부족한 것들은 에이콘에서 채우는 식으로 작업하기로 하자.
으음- 할인 폭이 크긴 하지만, 1년이나 쓸 것 같지는 않아…
이런 모델들은 대체로 폴리곤이 너무 많아서 게임에서는 바로 쓰긴 힘드니까, 심플리곤을 미리 깔아두자. 블렌더에도 Decimate가 있지만 심플리곤이 더 최적화를 잘 해준다.
백방으로 방법을 찾아보았지만, 예전처럼 SDK를 무료로 제공하지는 않는다. 차라리 유료로 팔면 좋겠는데 무조건 B2B인 모양이다. 30일 무료버전 신청란에 회사이름과 도메인을 반드시 적으라고 되어 있다.
에이…그럼 이건 못쓰겠네… 그냥 Decimate를 써야겠다.
3.12
작업에 앞서 스테이지를 좀 길게 만들자. 그러려면 카메라 워킹이 필요하다.
모든 작업의 시작과 끝은 스파2가 있다. 이 오래된 게임의 스크롤방식을 벤치마킹했는데, 이것이 생각보다 어려운 과제였다. 캐릭터들은 가까이 있거나 화면 끝에 있다면 스크롤 되지 않고, 일정거리 이상 벌어져야 스크롤 된다. 그대로 구현하되, 카메라가 갑자기 튈 경우를 방지해 이동에 Lerp를 약간 넣었다.
스크롤이 있으니 확실히 공간이 넓어 보인다. 얼마나 넓어야 할까…일단은 10미터로 책정. 너무 멀어도 도망치는 적을 잡기 피곤하니, 충분해 보인다. 상하 스크롤은 정신 사나워서 취향이 아니므로 건너뛰고, 확대 축소는 작업량이 많으니 포기하자.
바탕이 되는 작업은 끝났으나 여전히 일이 남아있다. 먼저 만들었던 작업들의 피격모션을 각캐릭터에 맞게 복사해주어야 한다. 또한 던지기 후 좌우가 뒤집히는 문제를 해결해야 한다. 그리고 타격형 잡기의 경우, 몇 번을 칠 지 스크립트에서 제어할 수 있게 코드를 수정해야 한다. 그 외에 작업보드에 적혀있는 버그들도 수정하고 가도록 하자.
유저는 같은 캐릭터를 고를 수 있어야 하므로 앨리스에게도 앨리스에게 잡히는 모션이 필요하다. 하지만 같은 파일을 링크할 수는 없으니 임시 파일을 만들어 진행해야 한다. 피격모션은 달래를 기준으로 제작된 것인데, 키에 큰 차이가 없어 그대로 사용해도 무방해 보인다. 앨리스의 발 잡기에 DamageSlam이 들어가므로, 이 모션도 함께 가져와야 한다.
GetUpSlam모션은 달래의 모션으로 일어나게 된다. 이것을 앨리스의 모션으로 바꿔주어야 한다.
잡기를 위해 데미지 모션은 4가지의 스테이트가 더 필요하다.
DamageFlying
DamageSlam
DamageSlamLying
GetUpSlam
지금까지 설정한 앨리스의 스테이트는 60개 정도 된다. 남은 것은 도발(D버튼), 등장 2개, 승리 2개, 스핀형 데미지, 스턴, 필살기 3개, 초필살기.. 아이고, 아직도 많구나!
작업 중 통일성이 없는 부분이 발견됐다. 앨리스의 허리는 Euler를 사용하고, 닐리의 허리는 쿼터니온을 쓴다. 이렇게 되면 작업이 호환이 되지 않기 때문에 확실하게 정의하고 가야 한다. 오일러를 쓸 필요가 없으므로 쿼터니온이 옳다. 하지만 이걸 해결하기 위해선 앨리스로 거슬러 올라가야 한다. 모든 애니메이션의 오일러 키를 쿼터니온으로 변경해 줄 것인가? 문제가 생기는 모션만 부분적으로 변경해줄 것인가…
스크립트가 필요하다. 구상은 잠자리에서. 오늘은 여기까지.
3.11
오일러를 쿼터니온으로 변환한다. 구상은 쉬웠는데, 구현이 꽤 어려웠다. 이제 챗GPT없으면 작업못할 듯.
이렇게 오일러로 되어 있는 키를
요로케 쿼터니온 변환해 준다.
ARP에도 회전값 변환기가 들어있기는 한데, 사용이 불편해서 새로 만들었다. Euler모드를 맞춰놓고 돌려야 제대로 작동하는 걸로 보아 비주얼키를 베이킹하는 모양이다. 게다가 액션그룹 지정도 안해줘서 목록이 밖으로 빠져나간다… 여러모로 언젠가는 해야 할 작업이었다.
이제야 다음 스텝으로 넘어갈 수 있다. 내일 하자..
오로라가 오로라를 잡았을 때. 아이고 작다. 이런 문제 때문에 Caught모션을 완전히 공유해 쓸 수가 없다. 사실 키가 같더라도 세컨더리 애니메이션 문제도 있긴 하다.
그래서 오로라는 다리를 쭉 편채로 맞아야 한다.
원래 멱살을 잡아 채는 모션인데… 오로라의 키가 작아서 머리채를 끌어당기게 됐다(…) 유독 오로라에게 더 가혹해져 버린 닐리.
이걸로 피격자의 모션은 일단락됐다.
이제 좌우 반전 문제를 해결해 보자. 좌우를 반전해 쓰고 있으니 완전히 해결할 수는 없고, 움직임이 큰 시점에 반전 모션을 미리 바꿔주어야 한다.
하지만 반전을 시키는 방법은 어렵다. 일단 모든 키를 반전시키는 방법은 내가 아는 한 블렌더에서는 지원하지 않는다. 다시 스크립트가 필요하다.
데이터를 살펴보니 원리는 단순하다. 반전할 수 있는 키의 리스트를 뽑은 후에 위치는 x, 회전은 y,z를 반전해주면 된다.
…하지만 이걸 굳이 해야 하는가?
모션을 2개로 나눈 후의 세컨더리 애니메이션은 생각보다 일이 크다. 잡기 애니메이션은 사실 상당히 러프하게 만들었는데, 수량이 너무 많기 때문이다. 모든 걸 제쳐두고 돌아가게 하는 것이 중요하다.