표정이 없으니 좀 무서운데... (Rive 표정 구현기)

그림 한 장으로 희로애락 구현하기: 개발자스러운 표정 연출법

Jun Noh

햄스터가 나를 쳐다본다. 계속…

어제 장장 6시간의 삽질 끝에 햄스터를 걷게 만들고(Walk), 점프도 시키고(Success), 좌절도 시켰다(Fail). Rive 런타임을 React Native에 올려서 시뮬레이터를 돌려보니, 아주 쫀득하게 잘 움직인다. 뿌듯했다.

그런데… 계속 보고 있으니 기분이 좀 이상하다.

이 녀석, 눈을 안 깜빡인다.

숨은 쉬는데(Idle), 눈은 부릅뜨고 시선 고정이다. 웃지도 않고 울지도 않는다. 그냥 무표정으로 나를 쳐다본다.

귀여운 게 아니라 점점 광기가 느껴진다.

“아, 표정을 안 만들었구나.”

문제는 내 에셋이 단 한 장이라는 거다. 보통 게임 개발할 때 표정 스프라이트(Sprite)를 face_smile.png, face_sad.png 이렇게 따로 그리지 않나?

난 그림을 못 그린다. 그리고 AI한테 “똑같은 햄스터인데 웃는 거 그려줘”라고 하면, 미세하게 다른 햄스터 10마리를 뱉어낸다. (일관성 붕괴)

그래서 고민했다. “개발자스럽게, 로직으로 표정을 만들 순 없을까?”

결론부터 말하면, 가능하다. 그것도 아주 훌륭하게.

1. 접근: 얼굴을 컴포넌트화(Componentization) 하기

프론트엔드 개발할 때 버튼을 통짜 이미지로 안 쓰고 Container, Label, Icon으로 쪼개서 CSS로 조작하듯이, 얼굴도 쪼개기로 했다.

어차피 이미지는 비트맵이지만, Rive 안에서는 벡터처럼 조작할 수 있으니까.

포토샵 수술 집도 (2일 차)

어제 배운 포토샵 스킬(L, J, S)을 다시 활용했다. 이번엔 좀 섬세한 수술이다.

  1. 파츠 분리: 눈(좌/우), 눈썹, 입을 각각 레이어로 오려냈다(Ctrl+Shift+J).
  2. 민달걀 만들기: 눈코입이 떨어져 나간 얼굴…은 꽤 끔찍하다(공포 영화인 줄). 구멍 뻥 뚫린 피부를 Spot Healing BrushGenerative Fill로 메꿔서, 매끈한 살색 달걀귀신으로 만들었다.
  3. 재조립: 민달걀 위에 오려낸 눈, 입 레이어를 다시 얹었다.

겉보기엔 아까랑 똑같다. 하지만 이제 눈과 입이 독립적인 객체가 되었다.


2. 구현: 그리지 말고 ‘변형’하라

이제 Rive로 돌아와서 이 파츠들을 조작해 표정을 만들었다.

새로 그린 건 단 1픽셀도 없다. 오직 TransformMesh 뿐.

처음엔 눈 감은 이미지를 찾아 합성해야 하나 싶었다. 근데 생각해보니 애니메이션 원리상 그냥 납작해지면 감은 거 아닌가?

  • Logic: 눈 레이어의 Scale Y (세로 크기)를 100% -> 10%로 줄였다.
  • Result: 완벽하게 눈을 감는다. 0%로 아예 없애는 것보다 10% 정도 남기니 ‘실눈’ 뜬 것처럼 더 자연스럽다.

😄 웃는 입 vs 😫 슬픈 입

입 모양이 제일 문제였다. 웃으면 입꼬리가 올라가야 하는데(U), 내 이미지는 일자(-)다. 여기서 어제 배운 **메쉬(Mesh)**가 빛을 발했다.

  • Logic: 입 이미지에 메쉬를 씌우고, 양쪽 입꼬리에 해당하는 점(Vertex)을 잡았다.
    • Smile: 입꼬리 점을 위로 당긴다.
    • Sad: 입꼬리 점을 아래로 내린다.
    • Open: 가운데 점을 아래로 당긴다.
  • Result: 고무줄 늘리듯 입 모양이 자유자재로 바뀐다.

3. Rive State Machine: 표정 레이어 분리

이제 기술적인 부분이다.

몸동작(Walk, Jump)과 표정(Blink, Smile)이 섞여야 한다.

걸어가면서 웃을 수도 있고, 서 있으면서 울 수도 있어야 하니까.

이걸 하나의 타임라인에 다 찍으면 경우의 수가 너무 많아진다. (Walk_Smile, Walk_Sad, Idle_Smile… 끔찍하다.)

그래서 Rive의 Layer 기능을 썼다.

  • Base Layer: 몸 전체 애니메이션 (Idle, Walk…)
  • Face Layer: 표정 애니메이션 (Smile, Sad…)
  • Blink Layer: 눈 깜빡임 (독립 루프)

이렇게 레이어를 나누고 Mixing을 켜두니, 몸은 걷고 있는데 얼굴만 웃는 표정으로 자연스럽게 오버라이딩(Overriding) 된다. 개발적으로 아주 깔끔한 구조다.

4. 표정 가이드 (Cheat Sheet)

나중을 위해 설정값을 정리해둔다.

표정눈 (Eyes)입 (Mouth Mesh)눈썹
Idle3.5초마다 깜빡임 (Scale Y 10%)기본기본
Walk정면 응시살짝 미소 (Vertex Y -2px)-
Success감은 눈 (> <)
Scale Y: 10%
Rotation: ±15도
활짝 (D)
Scale Y: 120%
꼬리 대폭 상승
위로 들뜸
Fail실눈 (- -)
Scale Y: 20%
시무룩 (へ)
꼬리 대폭 하강
팔자(八) 눈썹

마치며

이미지가 없으면 코드로 비틀면 된다.

확실히 개발자 관점에서 접근하니, 리소스를 무한정 찍어내는 것보다 이렇게 ‘변수(Variable)’ 조절하듯 표정을 만드는 게 훨씬 효율적이고 유지보수하기도 좋다. (입꼬리 각도만 수정하면 모든 표정이 바뀌니까!)

이제 내 햄스터는 숨도 쉬고, 걷기도 하고, 눈도 깜빡이며 웃을 줄 안다.

광기는 사라지고 제법 귀여워졌다.

마침

다른 글 보기