소리꽃 KeyBloom TIL 6
파티클에 나선 기둥과 물고기 유영 모션을 추가하며 나온 것들.
기능을 데이터로 두면
새 기능이 값 조합이 된다
앞선 TIL 1에서 파티클 모션을 종류마다 따로 짜지 않고, 태어날 때(스폰) 속도·중력·흔들림·수명 같은 값으로 환원해 공용 적분기 하나로 굴리는 구조를 잡았다.
이 구조의 값을 이번에 실감했다. “토네이도 같은데 위로 안 퍼지고 원통으로 도는 것”이라는 새 모션이, 알고 보니 값 하나 차이였다. 흔들림 진폭이 시간에 따라 커지는 정도(퍼짐)를 0으로 두면 원뿔이 원통이 된다.
이러면 새 기능을 만들 때 묻는 질문이 “어떻게 구현하지”가 아니라 “어떤 값이면 그 모양이 나오지”로 바뀐다. 뒤엣것은 만들어 보면서 슬라이더를 움직여 답할 수 있고, 앞엣것은 코드를 새로 짜야 답할 수 있다.
기능 종류가 앞으로 늘어날 것 같으면, 종류마다 코드를 두는 대신 값으로 환원되는 공통 계산을 먼저 만들어 두는 게 크게 남는다. 대신 처음 한두 개는 더 오래 걸린다.
여럿이 만드는 것
형태는 개체가 아니라 분포가 만든다
나선 기둥은 파티클 하나하나가 그냥 좌우로 왔다 갔다 한다. 그런데 개체마다 그 왕복의 시작 지점 — 사인파에서 지금 어디쯤인지를 가리키는 값, 곧 위상 — 을 다르게 주면, 멀리서 볼 때 원통 표면을 도는 것처럼 보인다.
개체가 하는 일은 단순한 좌우 왕복인데 군집이 만드는 형태는 입체다. 개체의 궤적과 전체 인상은 다른 층위의 이야기다.
여럿이 모여 무언가를 만드는 화면을 설계할 때, 개체를 복잡하게 만드는 것과 개체를 흩는 방식을 바꾸는 것 중 후자가 대개 싸고 효과가 크다. 개체를 복잡하게 하면 개수만큼 비용이 늘지만, 흩는 방식은 값 하나다.
두 움직임을 같은 박자로 묶으면 인과가 보인다
물고기는 좌우로 꼬리만 흔들게 했더니 헤엄으로 안 보였다. 실제 물고기는 꼬리를 칠 때 훅 나아가고 그 사이에는 미끄러진다.
그래서 나아가는 속도도 같이 출렁이게 했는데(추진 맥동), 중요한 건 그 출렁임을 꼬리 흔들림과 같은 위상에 묶은 것이다. 따로 두면 꼬리 따로 몸 따로 노는 기계처럼 보인다. 같은 박자로 묶으니 “꼬리를 쳐서 나아간다”는 인과가 눈에 보인다.
여러 움직임을 겹칠 때 각각 자연스럽게 만드는 것만으로는 부족하다. 그것들 사이의 관계가 보여야 하나의 동작으로 읽힌다. 무작위를 줄 때도 “무엇은 흩고 무엇은 묶을지”를 갈라야 한다.
나오면 안 되는 값
조건문으로 걸러 내지 말고 분포로 뺀다
물고기가 왼쪽이나 오른쪽으로 가게 하려고 좌우 속도를 무작위로 흩었더니, 0 근처가 뽑힌 개체는 방향 없이 애매하게 직진했다.
원하는 건 “왼쪽이든 오른쪽이든 정하고 그쪽으로 가는 것”이다. 그래서 방향과 크기를 따로 뽑았다. 방향은 좌우 중 하나, 크기는 최소값 이상에서 무작위. 이러면 0 근처가 아예 안 나온다.
“무작위인데 어떤 값은 나오면 안 된다”는 요구는 생각보다 자주 나온다. 뽑고 나서 걸러 내면 조건문이 붙고 분포도 미묘하게 틀어지는데, 뽑는 방식 자체를 설계하면 조건문 없이 깔끔하다.
무작위를 쓸 때 “어느 범위에서 뽑을까”만 정하고 넘어가기 쉬운데, “이 범위 안에 나오면 곤란한 값이 있나”를 한 번 물어보면 이런 게 미리 잡힌다.
요약
- 기능 종류가 늘어날 것 같으면 값으로 환원되는 공통 계산을 먼저 만든다 — 새 기능이 값 조합이 된다
- 여럿이 만드는 형태는 개체를 복잡하게 하기보다 흩는 방식을 바꾸는 게 싸다
- 여러 움직임을 겹칠 땐 무엇을 흩고 무엇을 같은 박자로 묶을지 가른다 — 묶여야 인과가 보인다
- 나오면 곤란한 값은 뽑고 나서 걸러 내지 말고 뽑는 방식으로 뺀다