앨리스 리깅

11.10

본을 어디까지 얼마나 쓸 것인가? 는 늘 고민거리다. 어느 정도 틀이 잡힌 프로젝트라면 모를까, 처음 시작할 땐 적정수준이란 걸 가늠하기가 상당히 어렵다.

줄기를 늘릴까, 마디를 늘릴까

앞머리는 고정하는 편이 예쁘므로 마디가 없어도 될 줄 알았는데, 정작 해보니 생각보다 더 딱딱해 보인다.

대충 건드려본 결과물로도 분명한 차이가 있다.

그런데 앞머리의 움직임 패턴을 생각하면 또 마디가 없어도 될 것 같다.

11.17

기나긴 애드온 제작을 끝내고 다시 리깅작업으로. 그런데 이거 맞나? 싶다… 추가본만 100개가 넘는데…아무리 PC게임이라지만 게임 데이터가 이래도 되나…

어.. 진짜 이거 맞나? 벌써 어지러운데..

11.18

로폴 작업할 때까진 브러시 기능따위 왜 있는거야 생각했는데, 폴리곤이 많아지니 웨이팅 작업은 오히려 편하다. 인체공부를 안해뒀다면 꽤 괴로울 뻔 했다.

하지만… 그래도 쉽지 않다..

11.18

인내는 썼지만, 그 열매는 달았다. 잘 움직이는구나!

실전으로 들어가면 좀 더 사소한 것들이 발목을 잡는다. 이름지을 때 좌우첨자 고려를 안했더니 좌우반전이 안된다거나 하는 것들. 이게 왜 필요하냐면 블렌더는 좌우반전 본을 이름으로 구분한다. 이름에 .l이나 _l이 들어가 있다면 된다. 그리고 이게 없으면 본에 제약조건을 걸었을 때 본 심메트리가 안된다. 블렌더의 제약조건은 복붙이 안되는데, 심메트리를 이용해서 이를 해결할 수 있기 때문에 이 혜택(?)을 누리려면 본 이름 맨끝에 좌우첨자를 붙여주어야 한다.

추가로 실제로 이름을 지어보니 언더바를 많이 써서 넘버링 사이의 첨자를 쩜으로 고쳤다. 이는 옛날 개발자의 습성이기도 한데, 고대의 개발자들은 파일명에 공백이 없어야 한다는 오래된 규칙 때문에 빈칸을 기피하는 경향이 있다. (…) 비슷한 이유로 파일명을 숫자로 시작하지도 않는다. 뭐 지금은 둘 다 된다. 게다가 윈도우 파일명과 블렌더 내의 본 이름규칙과는 다르므로 사실 2개는 아무런 관계가 없다.

참고로 본의 이름에 쩜을 추가하면 언리얼에선 이를 언더바로 치환한다. 공백은 확인해보진 않았지만, 아마 치환당하지 않을까 싶다.

leg_helper를 왜 넣었는지 기억이 났다! Autorig Pro의 본 구조상 종아리의 본은 허벅지의 자식본이 아니다. 아마도 IK때문에 root에 연결되어 있는 것 같다. 때문에 종아리가 접힐 때 살이 눌려 양쪽으로 살집이 튀어나오는 처리를 하기 위해 넣은 bulge본의 드라이버를 위해 다리의 굽힘 정도를 알 수 있는 헬퍼본이 필요했었다.

리깅이 끝났어!이제 시작이다!

하지만 삽질이 또 한가득었기 때문에 다른 캐릭터의 리깅부터 진행해보는 게 좋겠다

11.20

리깅이 끝난 줄 알았는데, 포즈를 잡았을 때 다리의 본이 예쁘게 정렬되지 않는 문제가 있었다. 기존엔 트위스트본이 2관절이었는데, 본쓰는 김에 좀 더 쓰면 어떠리..싶어서 관절을 4개로 늘렸다. 결과는 대만족!이렇게 쉽게 해결될 녀석이었나! 다른 캐릭터의 리깅은 아직 진행전이니, 팔에도 적용해 놓는 편이 좋겠다.

레퍼런스 모델에서 얼굴을 수정했을 경우, 꽤나 많은 걸 수정해야 한다. 이걸 손으로 하면 시간이 너무 오래걸리기 때문에 스크립트가 필요하다. 각 드라이버를 돌면서 깨진 타겟을 다시 연결해주고, 머티리얼에 걸린 드라이버도 재정의된 셰이프키를 연결해준다. 드라이버에 접근하는 건 매우 까다롭다. 꼭꼭 숨어있기 때문이다. 짧은 코드지만, 이걸 쓰는데는 2시간이 걸렸다.

obj = bpy.context.object

for fcurve in obj.data.shape_keys.key_blocks.data.animation_data.drivers:
    for variables in fcurve.driver.variables:
        variables.targets[0].id = bpy.data.objects['Girl_rig']
        
obj.data.shape_keys.name = 'FaceKey'        
target_material_names = ['Face', 'Mouth', 'Eye']

for i, material in enumerate(obj.data.materials):
    for name in target_material_names:
        if name in material.name:
            mat = bpy.data.materials.get(name)
            obj.data.materials[i] = mat

            for fcurve in mat.node_tree.nodes.data.animation_data.drivers:
                for variables in fcurve.driver.variables:
                    if variables.targets[0].id_type == 'KEY':
                        variables.targets[0].id = obj.data.shape_keys  
                        
face_obj = bpy.data.objects.get('Face')

if face_obj:
    obj.location = face_obj.location    
    obj.name = 'Face'
    delete(face_obj)    

  

이제 얼굴의 모양을 고치면 그냥 들고 오면 된다.

11.21

카메라를 멀리서 볼 때 캐릭터의 인상이 약해서 아이셰도우를 추가했는데 성형키로 인해 아이셰도우의 모양이 눈매와 맞지 않는 문제가 있었다. 이를 위해선 얼굴의 셰이프키를 고쳐야 하고, 그걸 가져오면 부모관계 및 드라이버가 모두 깨진다. 위의 코드는 이를 쉽게 처리할 수 있는 스크립트가 필요했기 때문에 작성한 코드다. 이걸 손으로 하면 지금까지 했던 캐릭터들에게 모두 같은 일을 해주어야 하므로 족히 1시간은 걸릴 일이다. 실수도 잦다!

눈가장자리로 갈수록 눈썹이 붉어지는 건 잘못딸려오긴 했는데.. 그냥 예쁘므로 두기로 하자. 이전 프로젝트에서도 얼굴을 새로 업데이트하는 처리를 했었지만, 이 방법은 그 전보다 세련됐다. 왜 전엔 이렇게 하지 못했을까…

앨리스를 작업하며 했던 삽질을 또 다른 캐릭터에게도 적용했다. 데이터가 복잡하니 할 일도 많다.

오늘의 깨달음

ctrl+f2를 누르면 일괄 이름변경을 할 수 있다. 전에도 알고는 있었으나 사용법이 알기 어려워 안썼는데 특정 키워드를 지우거나 바꾸기엔 매우 좋다.

내일은 정말로 닐리 리깅에 들어가보도록 하자.

애니메이션 애드온의 제작 #2

11.15

경직구간 설정. 충돌은 아니지만 충돌을 처리하기 위해 만들었다. 다른 기능과는 다르게 본 단위로 움직이므로 가장 마지막 단계에서 처리하는 편이 좋겠다. 원리는 단순한데, 시작과 끝의 보간을 없애 그래프를 평평하게 만든다. 완전히 자연스러운 애니메이션이라고 보기는 힘들지만, 설정된 값으로 최대한 쉽게 애니메이션을 만들 수 있다. 어려울 줄 알았는데, 의외로 쉽게 풀렸다. 오굳

첫프레임 보간비율을 추가. 어쩐지 여기 항목 점점 늘어난다(…) 이 값은 키를 물려줬을 때, 키를 미는 과정에서 0프레임에 키가 비는 현상을 보간해준다.

더불어 오일러와 쿼터니온 중 안쓰는 커브를 삭제하는 기능도 추가. 데이터가 좀 더 깔끔해졌다.

이제 루핑키를 처리할 수 있다.

그것까진 좋은데, 키가 너무 많다. 사용하는 키는 20프레임인데, 이를 만들기 위해 양쪽으로 40프레임씩이 더 붙어야 한다. 유니티가 이걸 어떻게 받아들이는지 확인해보자.

맙소사. 마이너스 프레임을 0으로 친다. 키 최적화 기능이 필요하다.

키 최적화는 단순히 앞뒤로 짜르면 되겠지 싶었는데, 키를 지우며 리스트가 동적으로 변해 오류를 냈다. 이를 해결하려면 키를 뒤에서부터 지워야 한다. for~in reverse()를 사용하면 된다. 이걸 알아내느라 한참 걸렸다.

캐릭터가 다리를 움직여 치마가 들리면 불규칙한 움직임이 일어난다. 회전 보간은 이럴 때 사용하기 위한 기능이다. 원래 계획에 있었던 것이지만, 구현은 예상보다 더 괜찮은 결과를 보여줬다. 흐뭇.

지금까지 한 것과, 앞으로 할 것들. 거..거의 끝나간다!

11.16

월드축 기준으로 붙이기는 어느 정도 원형을 보존해주기는 하지만, 약간 아쉬울 때가 많았다. 섞기를 만들자.

0.5비율로 섞었을 때

월드 0.7 : 로컬 0.3일 때

코드 리팩토링을 몇 번이나 한 보람이 있다. 흑흑. 뒤로 갈수록 구현이 쉬워진다.

이제 남은 건 이렇게 2개이다.

초기값으로 보간은 alt+r(초기값으로 되돌리기)이 너무 극단적이라 중간값을 쓰고 싶을 때 사용한다. 대략 1/3정도씩 줄어든다.

회전키는 사람손으로 부드럽게 잡기는 어렵다. 이걸 잘 처리하기 위해선 원형커브를 만들고 룩앳을 달아서 그걸 쫓도록 하는 아주 번거로운 과정을 거쳐야 하는데, 그냥 회전값에 sin, cos을 더하면 될 일 아닌가? 싶어서 러프하게 구현해봤더니 잘 된다. 해당 키는 8각을 그리는 키인데, 좀 더 부드러운 곡선을 원한다면 각을 늘려주면 된다. 좋아. 회전 프로퍼티를 넣어보자…싶었는데 타임오버. 오늘은 여기까지

11.17

생각보다 설정할 것들이 많아 카테고리를 따로 뺐다. 대부분 예상했던대로 잘 동작했으나, 계산을 오일러 기반으로 하고 있기 때문에 짐벌락 현상이 발생한다! 때때로 아주 과한 애니메이션에 부적합할 수 있다. 이걸 해결한 게 쿼터니온인데, 그건 컴터만 알아듣는 숫자고. 내가 할 수 있는 건 여기까지인 것 같다.

다음으로 할 일은 그룹본이 아닌 본에게 키를 복사하는 기능이다.

…는 enum만 추가하니 쉽게 됐다.

야호.

회전키 제작과 루프키 버그 수정. 또한 그룹본이 아닌 본에도 애니메이션을 줄 수 있도록 한다. 예상했던 기능들은 이제 모두 구현했다. 이제 미뤄왔던 애드온 기능을 추가하고 마무리 하면 되겠다.

오늘의 깨달음:

  • matrix_basis는 부모를 기준으로 한 상대적인 회전행렬을 리턴하지만, 이걸 오일러로 바꾸어 숫자를 조작한다고 해서 반드시 축대로 정확히 움직이는 것은 아니다. 아마도 오일러 공식 때문인 것으로 보인다.
  • 이를 해결하려면 bpy.ops.transform.rotate()를 사용해야 한다.

애니메이션 애드온의 제작 #1

11.8

날짜를 강조해서 쓰기 위해 마크업 문법 “###”(제목표시줄3)를 첫머리에 쓰면 저런 도움말이 뜬다. 그 다음 날짜를 쓰면 저 문구가 사라지며 내용이 써지는 식이다. 내가 늘상 헤딩하고 있는 건 맞는데, 블로그가 그렇게 말하니 기분나쁘네!? 야이..

리깅은 제약조건과의 싸움이다. 리깅의 기본이 되는 IK/FK도 제약조건(Constraint)이 만들어낸 마술이다. 하지만 이러한 제약조건들은 단일 모션에는 적합할지 몰라도 범용적으로 사용하긴 어렵다. 제약조건이 복잡해 질수록 의도치 않은 문제가 생긴다. 컨트롤 본이 늘어나는 것은 덤이다. 가뜩이나 복잡한 릭이 더 복잡해진다. 때문에 게임 실무에서는 이걸 잘 안쓴다. 본이 몸을 뚫든 말든 스프링본을 돌리고 베이킹해서 끝낸다. 하지만 타임라인에 지저분한 키가 남는다. 이게 마음에 안드는 이들은 그냥 손으로 잡는다. 하지만 품이 너무 많이 든다.

세컨더리 애니메이션을 아름답게 처리할 수 있는 방법이 없을까? 리깅 전에 이걸 먼저 해결해야 할 것 같다.

먼저 테스트 모델을 제작한다.

애니메이션 스크립트는 가장 건들기 싫은 부류의 스크립트다. 블렌더의 애니메이션 시스템도 복잡하거니와 챗GPT도 엉뚱한 답만 내놓기 때문에 스스로 생각해야 하기 때문이다.

블렌더의 애니메이션 데이터는 Action이 들고 있다. 본이 아니다. 때문에 액션에서 커브를 찾아 매칭시켜야 한다.

  • armature
    • animation_data
      • action
        • fcurve
          • keyframe_point

우리가 애니메이션을 만들면 action에 fcurve를 추가하게 된다. 블렌더는 이 때 해당 본의 이름으로 data_path를 생성하고 인덱스를 부여해서 커브를 구성하게 된다.

그런데 문제가 있다. fcurve가 복사가 안된다!

키프레임도 안된다! 되는 게 뭐야!

그렇다며언…으음 모두 수동으로 구성해주는 방법뿐이다. 어쩔 수 없지

러프한 구현. 이렇게 움직인 다음, 스크립트를 돌리면

오프셋된 키가 만들어진다.

오일러 테스트. 생각보다 잘 작동한다. 오오

아직은 비루한 메뉴ㅋㅋ

스프링 베이킹과는 달리, 키를 망치지 않는다. 그냥 전이할 뿐이다.

가능성이 보이니 계속해 보자. 현재 코드는 차일드가 1개뿐일 때만을 계산한다. 더 아래에 있는 차일드에게도 전이하도록 해야 한다.

…는 그냥 되고 있었는데 코드를 잘못 봐서 조기리턴되고 있었다.

바람이 치마를 훑고 가기 위해선 키를 밀어야 한다. 하는 김에 트랜스폼 3형제를 따로 밀 수 있도록 구현했다. 그렇다면 자식본에 회전값이 아니라, 선택한 값을 모두 물려주는 메뉴로 바꿀 수도 있겠다. 이것도 구현했다. 본격적인 사용은 좀 더 나중이 될 것이다.

다음은 순환 본의 처리. 치마 중 하나를 움직인 후 나머지 본에 모두 같은 값을 붙이는 기능이 필요하다. 월드와 로컬로 나뉘어야 하는데 이게 매우 어려워 보인다. 일단 자리부터 깔자

옆본 보간은 본을 하나만 제어했을 경우 나머지 본을 알아서 제어해 줄 수 있게 만들 예정이다. 뜻대로 잘 되면 좋겠다.먼저 키 따라하기부터 만들어보자.

11.10

오늘의 깨달음 :

  • 본 정보의 matrix는 월드 기준 행렬, matrix_basis는 부모 기준 행렬이다.
  • .to_quaternion()메서드는 해당 본의 회전정보를 쿼터니온으로 뽑아준다.
  • 여기에 .to_matrix()를 붙이면 행렬화 해주고
  • 여기에 .to_4x4()를 붙이면 4×4행렬로 만들어준다.
  • 이제 이걸 붙이고 싶은 본의 역행렬과 곱해주면 본의 회전정보를 같게 만든다.
  • 이걸 다시 .to_quaternion()으로 뽑아서 회전값에 넣어주면 된다.
a = bpy.context.selected_pose_bones[0]
b = bpy.context.selected_pose_bones[1]
        
m = a.matrix.to_quaternion().to_matrix().to_4x4()        
b.rotation_quaternion = (b.matrix.inverted() @ m).to_quaternion()

이렇게 되면 2개의 본은 완전히 같은 회전값을 가진다. 하지만 내가 하고 싶은 건 상대적 월드 회전값이다. 예를 들어 A본을 45도 돌리면 B본도 45도 돌아야 한다. 로컬기준 회전은 그냥 값을 복사하면 되지만, 월드기준으로 돌아야 하는 것은 꽤 까다롭다. 이걸 알아내는데 꼬박 하루를 보냈는데, 풀지 못했다. 그런데 저녁먹고 밤되서 다시 해보니 10분만에 됐다.(!?)

저녁의 깨달음:

  • 본 정보의 matrix_chennel은 원본 대비 회전값을 월드좌표 기준으로 저장한 행렬이다. 이를 이용해서 다른 본도 같은 회전값을 갖게 할 수 있다.
  • 포즈본이 아닌 데이터본 정보의 matrix_local은 월드좌표 기준 행렬이다. 즉, 초기값이다. alt+rgs누르면 돌아가는 그 값
  • 회전값만 뽑은 행렬을 원본 행렬과 곱해주면 위치가 어긋난다. 하지만 여기서 다시 회전값만 뽑으면 잘 된다. 내가 헛갈린 부분이 이 부분인데, 행렬곱은 만능으로 쓸 수 있는 공간 전환 마법이 아니었다.

하지만 그럼에도 불구하고 문제를 완전히 해결하진 못했다. 결국 이것 또한 삽질이었다.

11.12

컴퓨터 그래픽스에서 오브젝트의 회전은 공간이 회전하는 것이다. 공간은 행렬로 이루어져 있는데, 그동안 공부를 기피해왔었다. 어려웠기 때문이다.

하지만 원하는 걸 하기 위해선 행렬을 알아야 했고, 3일동안 공부를 해서 쓸만큼은 이해했는데, 뭔가 여전히 잘 작동하지 않는다. 그래서 챗GPT에게 물어봤더니… 와. 금방 알려줬다(…) 처음엔 왜 안알려줬어??!! 어쩌면 난 질문을 하기 위해 공부한 것일 지도 모르겠다.

import bpy
from math import radians
from mathutils import Matrix

def rotate_bone(pose_bone, angles_deg):
    # angles_deg: [x_angle, y_angle, z_angle]

    # 각도를 라디안으로 변환
    angles_rad = [radians(angle) for angle in angles_deg]

    # X, Y, Z 축을 기준으로 회전하는 행렬 생성
    rotation_matrices = [
        Matrix.Rotation(angles_rad[0], 4, 'X'),
        Matrix.Rotation(angles_rad[1], 4, 'Y'),
        Matrix.Rotation(angles_rad[2], 4, 'Z')
    ]

    # 회전 행렬들을 결합
    combined_matrix = rotation_matrices[2] @ rotation_matrices[1] @ rotation_matrices[0]

    # 현재 본의 전역 행렬을 가져옴
    world_matrix = pose_bone.bone.matrix

    # 결합된 회전 행렬을 전역 행렬에 적용
    pose_bone.bone.matrix = combined_matrix @ world_matrix

    # 화면 업데이트
    bpy.context.view_layer.update()

# 테스트를 위해 활성 포즈 객체와 본을 얻음
pose = bpy.context.active_pose_bone

# X, Y, Z 축을 기준으로 각각 45도씩 회전
rotate_bone(pose, [45.0, 45.0, 45.0])

코드에 문제가 아주 없는 건 아닌데, 그건 사소한 문제다. 핵심은 다 들어있다. 3일 밤낮으로 고민하던 게 이렇게 쉽고 허무하게…흑흑

하지만 적어도 이번 삽질 덕분에 행렬이 어떻게 생겼는지 조금은 이해하게 됐다. 이동값은 오른쪽끝에, 회전값은 3×3공간에 사인과 코사인값으로 나누어져 들어가 있고, 스케일은 대각선을 곱한다. 블렌더에서 제공되는 본의 행렬은 4가지가 있다. world, basis, channel, local

어쨌거나 이제야 다음 단계로 넘어갈 수 있게 됐다. 어휴.. 3년은 늙은 것 같다…

11.13

아휴… 난잡해… 하지만 그림이라도 없으면 카테고리 분류가 너무 안된다. 아이콘 리스트는 여기

기능은 모두 완료되지는 않았다. 하지만, 가장 어려웠던 건 해결했다. 이제 그냥 차근차근 코드 리팩토링과 함께 진행만 하면된다.

11.14

드디어 도메인이 연결됐다! 이제 https://ix9.net으로 접속할 수 있다.

블렌더의 포즈본 시스템은 어찌된 건지, 본을 선택하면 순서대로 리스트에 들어가지 않는다. 굳이 뒤죽박죽 섞는다! 왜! 그것 때문에 본의 선택 순서를 장담할 수가 없다. 그래서 한 번에 본을 잡고 뭔가를 처리하는 것에 제약이 많이 걸린다.

이 애드온은 세컨더리 애니메이션에 사용할 요령으로 제작했다. 머리카락이나 치마같이 흩날리는 애니메이션을 제작할 경우에 손으로 잡는 키를 최소화하기 위해 쓰일 것이다. 그런데 생각보다 이해가 쉽지 않고, 벌써 손이 안가는 메뉴들도 생겼다.(…) 며칠간 좀 더 손을 봐야 할 것으로 보인다.

부모본의 값들을 자식에게 물려줄 때 배율이 좀 더 강했으면 좋겠다. 싶어서 배율항목을 추가하고 값에 이를 곱해주면 되겠지. 싶었는데… 쿼터니온은 값이 오른다고 회전이 2배가 되는 것이 아니다.

그래서 키를 물려줄 때 오일러로 변환 후 다시 쿼터니온으로 변환하는 번잡시러운 과정을 거쳐야 한다. 그럼 쿼터니온 공식을 직접 조작하는 게 좋지 않을까 싶어서 챗GPT에게 물어보니

뭐라는 거야? ^^

내장함수가 왜 존재하는 지 알 것 같았다.

여차저차 구현 후 적용. 이것은 기존의 1배율 물려주기

이것이 배율을 높여서(1.4배) 물려주기이다. 확실히 더 자연스럽다. 그런데 이게 버그다… 의도했던 움직임이 아니다.

이것이 의도했던 순차적 오프셋. 그런데 버그느낌이 더 좋다! 의도적으로 키간격을 축소시키는 기능을 추가해보자.

정작 해보니 어느 상황에나 잘 맞아떨어지진 않는다. 상황봐가며 적당히 사용하도록 하자.

번호순으로 키 밀기기능도 완료. 회전 애니메이션 시에 용이하게 쓰일 것으로 기대한다.

모델링들을 모아보자.

11.8

왼쪽부터 앨리스 – 닐리 – 달래 – 오로라.

리깅에 들어가기 전에 달래의 디테일을 손보려 했는데, 모아놓고 보니 오히려 앨리스가 복잡해 보이는 현상이 있었다. 디테일을 추가하기로 한 계획은 취소하고, 치마 앞단의 무늬만 바꾸고 마무리.

폴리곤은 5만 전후. 앨리스와 오로라가 좀 많다. 정말 신기한 게, 폴리곤을 원없이 쓰자! 10만쓰자! 라고 생각하고 시작한 작업인데 5만개를 쓰니 더 이상을 쓸 곳이 없다. 팔을 8각으로 만드는 습성처럼 정해진 비율의 폴리곤작업이 몸에 베어있다보니, 폴리곤이 늘어나는 것도 한계가 있는 것이다. 물론 여기서 섭디를 한 번 더 주면 20만까지 늘어나지만, 이미 충분히 마음에 든다.

마지막 캐릭터인 오로라를 작업하며 모델링 삽질은 많았지만 셰이더 이슈가 없었다. 이젠 모델링 제작 프로세스는 완전히 정착되었다 봐도 좋을 것 같다.

이제 리깅이다. 모델링도 복잡했지만, 리깅은 더할 것이다. 심기일전하고 내일부터 데이터청소를 시작으로 앨리스 리깅에 들어가 보자.

11.9

정리만으로 하루가 다 갔다. 캐릭터 하나당 체크해야 할 사항이 이만-큼이나 된다.

  • 얼굴을 제외한 모든 오브젝트를 하나로 합친다.
  • 이 과정에서 버텍스 컬러가 문제를 일으킬 수 있으니 꼼꼼히 보자
    • 채널이름은 Attribute를 사용한다.
    • 특히 스킨셰이더 B채널은 눈에 띄지 않기 때문에 빠뜨릴 가능성이 높다.
  • 커스텀 노말을 간과하기 쉬우니, 샤프데이터 제대로 들어가 있나 꼭 확인
  • UV채널이 2개인지 확인
  • Outline그룹을 제외한 모든 버텍스 그룹을 삭제
  • Apply를 해주자.
  • 카피해서 Nude_01로 복장을 가져온다. Face와 Body외의 오브젝트는 모두 날린다.
  • 재빨리 저장부터 하자.
  • 릭이 있는 콜렉션(Girl)으로 Body를 옮겨준다.
  • 키 스케일이 1.0이 아니라면
    • 릭의 스케일을 줄인다.
    • 릭 스케일을 Apply한다.
    • 이러면 얼굴 컨트롤러의 위치가 어긋난다. 1/스케일만큼 다시 곱해준다.
    • 얼굴을 Parents와 분리한다. 이 때 트랜스폼을 유지하지 않아야 위치가 제대로 잡힌다.
    • 얼굴의 스케일을 리셋한다.(Apply가 아니라 1.0으로 되돌리는 것이다. 주의!)
    • 키 보정치만큼 올려주거나 내려준다.
    • 다시 얼굴을 본에 붙인다.
  • 레퍼런스 본 수정모드로 들어가 힐만큼 키를 올린다.
  • 3번레이어의 본들을 모두 잡고 키만큼 올린다.
  • 이렇게 되면 겨드랑이 본의 길이가 맞지 않는다. 제약조건에 들어가 Original Length옆의 X를 눌러 맞춰준다.
  • 바인딩한다.
  • 메쉬의 아마추어 모디파이어에서 Preserve Volume을 제거한다.
  • Body에서 버텍스 그룹을 데이터 이전한다. 제대로 이전되었는지는 손가락으로 확인한다.
  • Body는 이제 수명을 다 했으므로 삭제한다.
  • 머티리얼의 이름에서 .001을 떼고, 셰이더를 Nude_01에 내장된 것으로 교체한다.
  • 리커시브 후에 셰이더 노드창에서 Add-Group을 확인해 쓰레기 노드가 있는지 살피자.
  • 아웃라이너에서 .001머티리얼을 삭제한다.
  • .001이미지가 있다면 리맵해주자.
  • 얼굴 성형 후에 키를 잠근다.
  • 머리색깔과 눈색깔을 캐릭터에 맞게 바꾼다.
  • 이제 커스텀본을 심으며 개별 캐릭터의 세팅을 한다.

오로라 모델링 #2

11.2

생긴 것에 비해 본 구조는 단순하다. 그냥 사람 팔과 같다. 하지만 어깨 관절부는 구형 움직임으로 디자인을 변경하는 편이 좋겠다. 움직임이는데 생각보다 제약이 크다.

이거 그리면서도 어디서 봤나 했더니 그거였구나. 나와라! 가제트 팔!

꽤 많이 썼다고 생각했지만, 이래봤자 폴리곤이 5만개 좀 넘는 수준. 이 중 로봇팔은 만개도 안된다. 기계류는 언제나 폴리곤을 들인 것에 비해 더 좋은 효과를 보여주는 것 같다.

11.3

다시 돌아온 매핑 시즌.

11.4

아휴.. 얕봤는데 진짜 쉬운 게 하나도 없는 캐릭터네ㅋㅋ

후드티를 끝내고 싶었는데 x매듭리본이 복병이었다.

11.5

어제 작업이 마음에 안들었기 때문에 결국 다시 했다. 이번 작업은 마음에 든다.

아이고 멀었네. 손 마이 간다. 마이 가.

11.6

가방 매핑 중 타임오버

11.7

가방은 처음부터 끝까지 말썽이었다.어제 작업이 마음에 안들어서 결국 눈을 없애고 다시 디자인.

겨우겨우 상반신이 끝났다.

오늘의 블렌더 팁 : Shift + Numpad 4, 6(좌우)키를 누르면 카메라를 Roll기준으로 회전시킬 수 있다.

어쩐지 유독 어려웠던 모델이었지만 결과는 마음에 든다. 오로라 모델링 완료.

리깅 전에 계속 눈에 거슬렸던 달래 모델링을 조금 손보고 가도록 하자.

FSM을 만들어보자. #3

11.1

선입력 시스템을 추가. 조작감이 한결 부드러워졌다. 겉으로 변한 것은 없지만 많은 리팩토링이 있었다.

아따따뚜겐!

하지만 그럼에도 불구하고 두번째 기술이 자꾸 씹히는 현상이 있었다. 이유인즉, 스테이트가 배뀔 때 기존 입력이 초기화되는데, 사람이 키를 누르는 속도는 이보다 빠르다. 키를 누르다가 초기화 당해버리는 것이다

그래서 선입력 시스템을 갈아엎어 2개까지 받도록 만들어서 해결했다.처음엔 3개까지 받았지만, 이러니 너무 예약된 움직임이 많아서 게임하는데는 방해가 됐다.

11.3

스파게티코드가 되지 않기 위해 열심히 리팩토링 중. 오늘은 박치기를 구현.(↓→A)

11.6

  • 장풍 구현. 커맨드는 마데꾸.
  • 적이 점프할 때 맞으면 날아가는 구현. 이건 점프로직을 바꿔놔서 수월하게 진행.

방어도 구현했다.

….생각보다 예상했던 작업이 일찍 끝났다. 으으음. 이제 뭐하지…?

오로라 모델링 #1

10.25

오로라의 컨셉은 조금 변했다. 원래 가방에서 나오는 것은 각종 총기 및 무기였고, 이에 따라 원거리 캐릭터로 구상했었으나, 잡기 캐릭터가 필요하다는 생각에 노선을 변경했다. 본체는 약하지만 뒷팔이 힘쎄고 강하니까! 하지만 뜻밖에 가방의 이미지가 너무 안잡혀서 몇 번을 갈아엎었는지 모른다.

10.26

이제 모델링에 들어가보자.

모델링을 할 때 명심할 점. 흐름 먼저. 모양은 나중에.

이번에는 러프 모델링을 우선 뽑아본다. 비율보기가 상당히 좋다. 진작에 이렇게 할 걸!

디테일 작업 중.

10.28

누드모델이 힐을 신은 키가 기준이 되다보니 생각보다 많이 줄였다. 무려 145cm.

아마 등장인물 중(추후 등장할 사천왕을 포함해서) 가장 작은 캐릭터가 될 것이다.

10.29~30

감기때문에 작업을 못했다. 아이고오

10.31

이거 전에 오로라 작업할 때도 느낀 거긴 한데… 뒷면 잘 뵈지도 않을 거에 디자인을 쓸데없이 많이 해놔서 손해보는 느낌이다. 시간만 많이 들고.

아톰다리 뒷모습 실루엣이 예쁘게 잘 나온 것아 뿌듯하다. 애니할 때 어떻게든 뒷테를 보여줘야겠다.

로봇팔을 만들자.

티스토리는 글을 쓰고 그림을 붙이는 게 자연스러웠는데, 워드프레스는 반대로 하는 편이 보기에 좋다. 익숙해지자.

11.01

손가락을 만드는 것은 늘 어렵다. 뜻밖에 고전중.

손설계가 꽤 어렵다.

11.2

러프모델링은 대강 끝났으니 이대로 정리를 해보자.

영차영차 정리 중

손을 정리하자.

달래 모델링 #2

10.22

티스토리가 짜른 원화를 다시 올리자. 다시 생각해도 빡치네. 으.

디자인이 별게 없다보니 생각보다 더 심심한 느낌이 든다.

10.23

옷소매는 원래 팔이 약간 접힌 상태를 기준으로 제작한다. 하지만 한복의 특성인지, 어제 작업을 해본 결과 실제로는 이도저도 아닌 결과물을 보여주어 실망했기 때문에 아예 로우레벨로 돌아가 다시 만들었다.

그리고 깨달음을 얻었다.디테일은 문제가 아니다. 실루엣이 문제였던 것이다.

10.24

문제는 계속 일어난다. 알 수가 없군.

맷캡 접합부에서 문제가 일어나는데 원인을 알 수가 없다.

블렌더의 컬러시스템은 좀 이상한 것이… 컬러 스페이스 변환을 해도 리니어와 sRGB가 완전히 보간되지 않는다.원인이 뭐지? 내가 뭘 놓치고 있는거지..?

조사 결과 계산방법이 바보같았다. 블렌더는 죄가 없다.

레퍼런스 모델에 업데이트된 머티리얼을 추가할 때 사본이 생기는 것 때문에 꽤 골치가 아팠는데, Remap기능을 사용하면 편하게 할 수 있다. 이처럼 요소를 Append하면 생기는 .001사본은

아웃라이너에서 해당노드를 선택해서 Remap해주면 된다.

그러면 짜잔. 이제 씬 내의 모든 노드가 새로운 노드로 대체된다.

장기적인 작업을 위해 깔끔한 데이터 관리는 필수적이다. 사본은 바퀴벌레 같아서 한 마리 생기는 순간 우후죽순 생겨나기 때문에 빨리빨리 없애주는 게 좋다.

약간 더 손을 봐준 모델. 이런것들이 겉으로 티는 잘 안나지만, 마음을 정리해주는 효과가 있다.

예를 들면 요런 것들.

이로서 완성.

앨리스나 닐리에 비해 좀 단촐한 느낌이지만, 원래 한복이 그런 여백이 많은 옷이니까.

하지만 여전히 작업공정에 삽질이 많았다. 모델링을 하나 더 해보는 게 좋겠다.

FSM을 만들어보자 #2

홈페이지를 이전하는 사이 기록을 하진 못했지만 많은 시도를 했다. 결론적으로 충돌체크 과정에서 고민이 생겼는데, 격투게임은 일반적인 게임보다 더 자세한 충돌박수를 사용해야 한다. 이를 위한 동적 콜라이더를 사용하려면 콜라이더가 애니메이션되어야 하지만, 또 이를 위해선 애니메이션을 편집가능한 상태로 만들어야 한다. 애니메이션을 보면서 콜라이더의 크기를 제어해야 하기 때문이다. 하지만 이렇게 되면 FBX와의 연결을 끊고 새로운 애니메이션 클립으로 복사해야 한다.

그렇다면 아예 애니메이션 단계에서 콜라이더를 제작해오는 것은 어떨까. 콜라이더는 유니티의 속성이므로, 1×1의 플랜을 매핑시켜서 콜라이더에 대응하는 것이다. 이를 위해선 2가지 확인이 필요하다.

  • Collider2D가 폴리곤 모양대로의 임포트된 데이터를 지원하는가?
  • Collider2D가 트랜스폼의 영향을 받는가?

일단 2번은 쉽게 확인할 수 있으니 해보자.

오..일단, 이건 잘된다.

그렇다면, 폴리곤 모양의 임포트 데이터를 지원할까? 블렌더를 켜고

대충만든 이걸 가져가보자.

불러오긴 했는데…으에에… 마땅한 컬라이더가 없다.

처음엔 폴리곤 콜라이더에서 메쉬를 받을 수 있을 것이라 생각했지만, 생각보다 잘되진 않는다. 이걸 위해선 3D데이터로 메쉬를 받은 후, 버텍스의 정보를 폴리곤 콜라이더2D에 넘겨주는 식의 작업을 처리해야 하는 모양이다.

한 번만 하면 되는 일일테지만 어찌할 지 모르겠어! 그냥 블렌더에서 플랜의 크기를 정규화해서 들고 오는 편이 낫겠다. 다시 블렌더로 가서 데이터를 수정하고, 애니메이션을 준 후에 들고와 보자.

임포트된 데이터에서 메쉬를 끄고 대신 컬라이더를 사용한다. 잘 작동한다!

그렇다면 이제 충돌체에 대한 제작방침을 확정할 수 있다. 블렌더에선 정규화된(1×1)사이즈의 플랜을 사용해서 커스텀본에 붙이고, 임포트된 데이터에선 플랜을 박스컬라이더로 변경하면 된다.

마음이 편해졌으므로 이제 다시 충돌로 넘어가보자.

기본구현은 완료. 다음은 공격을 해보자.

10.23

오늘의 깨달음.

  • 콜라이더가 disable된 상태에서 불러다 쓸 경우 쓰레기값이 들어간다. 반드시 enable확인하자.
  • Collider2D는 z가 0이어야 제대로 작동한다. 90도를 돌린 오브젝트에 사용할 경우 z값을 가로로 이동시켜 충돌을 일으킬 수 없다.
  • 스케일이 0이어도 충돌이 일어난다.
  • 코드로 제어한 콜라이더의 enable/disable은 애니메이션보다 우선순위가 떨어진다.

짠손을 구현

밀려나기는 내일 해보자.

10.25

타격 시 밀려남을 추가.

짠손을 빠르게 타격시 모션이 안맞는 문제가 있었다. 두가지 원인이 있었는데

  • 타격의 경직 때문에 시간이 멈추었을 때에도 play()함수가 작동하면서 모션이 바뀐다. 그렇다고 play()를 막을 경우, 맞는 포즈가 나오지 않는 문제가 있다. 모션이 변할 수 없도록 time scale이 0일 땐 키입력을 금지해야 한다.
  • 애니메이션 시작부분을 정하는 play()함수의 세번째 인자는 정규화된 시간을 사용하여야 한다.

10.26

데미지 처리를 하며 점프코드를 리팩토링. 현재 0에서 시작하고 있는 사인함수를 절반 짤라쓰기로 하자. 이제 중력은 유지하되 점프가 필요할 땐 파워로 제어할 수 있다.

이제 다리걸기가 예쁘게 돌아간다.

벽 충돌이 지속적으로 말썽을 일으켜서 며칠 째 보고 있다. 충돌은 총 3번 일어난다.

  • 캐릭터가 겹쳤을 때 서로를 밀어낸다.
  • 한쪽 캐릭터가 벽에 몰려있다면 몰리지 않은 쪽만 밀어낸다.
  • 한쪽 캐릭터가 벽에 몰려있을 때 데미지를 입었을 경우 많이 밀어낸다.

이젠 문제없기를.(이라고 말하는 순간 다른 문제가 일어나서 또 수정)

이제 선입력을 위해 커맨드를 만들어야 한다. 이를 작업하며 알아낸 사실이 있다. 이 버튼들은 유니티 프로젝트 설정에 반드시 포함되어야 한다. 없으면 지속적으로 에러를 내뱉어서 콘솔창을 어지럽게 만든다.

  • Submit
  • Cancel
  • Horizontal
  • Vertical

이동을 Axis만으로 처리할 경우 문제가 있다. 키 이벤트는 KeyDown으로 받아야 하는데, Axis로 처리할 경우 2개의 키를 동시에 누를 때 0으로 처리된다. 이렇게 되면 아주 빠르게 조작할 때 커맨드가 제대로 들어오지 않는다. 프로젝트 설정에서 Input을 바꿔주면 되긴 하는데, 이게 조이스틱 대응이 잘되는지 모르겠다. 조만간 조이스틱을 하나 사야할 것 같다.

아도겐!

커맨드를 먼저 구현한 이유는 조작감을 위한 선입력때문이다. 비단 캔슬기 뿐 아니라 약손이나 다리걸기등은 하위기술 중간에 모션이 빠르게 변할 필요가 있다.

러프 구현은 했지만 코드가 꼬인다. 문제가 많을 것으로 예상된다. 으으음…좀 더 좋은 방법은 없을까?