표정이 없으니 좀 무서운데... (Rive 표정 구현기)
그림 한 장으로 희로애락 구현하기: 개발자스러운 표정 연출법
햄스터가 나를 쳐다본다. 계속…
어제 장장 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)을 다시 활용했다. 이번엔 좀 섬세한 수술이다.
- 파츠 분리: 눈(좌/우), 눈썹, 입을 각각 레이어로 오려냈다(
Ctrl+Shift+J). - 민달걀 만들기: 눈코입이 떨어져 나간 얼굴…은 꽤 끔찍하다(공포 영화인 줄). 구멍 뻥 뚫린 피부를
Spot Healing Brush와Generative Fill로 메꿔서, 매끈한 살색 달걀귀신으로 만들었다. - 재조립: 민달걀 위에 오려낸 눈, 입 레이어를 다시 얹었다.
겉보기엔 아까랑 똑같다. 하지만 이제 눈과 입이 독립적인 객체가 되었다.
2. 구현: 그리지 말고 ‘변형’하라
이제 Rive로 돌아와서 이 파츠들을 조작해 표정을 만들었다.
새로 그린 건 단 1픽셀도 없다. 오직 Transform과 Mesh 뿐.
눈 깜빡임 (Blink)
처음엔 눈 감은 이미지를 찾아 합성해야 하나 싶었다. 근데 생각해보니 애니메이션 원리상 그냥 납작해지면 감은 거 아닌가?
- 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) | 눈썹 |
|---|---|---|---|
| Idle | 3.5초마다 깜빡임 (Scale Y 10%) | 기본 | 기본 |
| Walk | 정면 응시 | 살짝 미소 (Vertex Y -2px) | - |
| Success | 감은 눈 (> <) Scale Y: 10% Rotation: ±15도 | 활짝 (D) Scale Y: 120% 꼬리 대폭 상승 | 위로 들뜸 |
| Fail | 실눈 (- -) Scale Y: 20% | 시무룩 (へ) 꼬리 대폭 하강 | 팔자(八) 눈썹 |
마치며
이미지가 없으면 코드로 비틀면 된다.
확실히 개발자 관점에서 접근하니, 리소스를 무한정 찍어내는 것보다 이렇게 ‘변수(Variable)’ 조절하듯 표정을 만드는 게 훨씬 효율적이고 유지보수하기도 좋다. (입꼬리 각도만 수정하면 모든 표정이 바뀌니까!)
이제 내 햄스터는 숨도 쉬고, 걷기도 하고, 눈도 깜빡이며 웃을 줄 안다.
광기는 사라지고 제법 귀여워졌다.
마침