Framer로 취미생활.
T-Rex라는 공룡게임을 아시나요? 구글 크롬에서 인터넷이 끊겼을 때 할 수 있는 미니 게임입니다. 이 게임을 Framer로 간단히 구현한 제작 과정을 공유합니다.
저는 True Balance라는 모바일 앱을 서비스하는 스타트업에서 UX 디자이너로 일하고 있습니다. 작년까지 pxd라는 디자인 회사에 있다가, 어쩌다보니 이렇게 1년 넘게 지내고 있습니다.
(축하해주세요! True Balance가 구글플레이 기준 누적 다운로드 500만을 넘겼고, 현재 인도 구글플레이의 메인 피쳐로 등록되었습니다! 7/19)
스타트업에서 일하며 힘든 점들이 많지만, 좋은 점도 여러가지가 있습니다. 그 중 하나가 업무 프로세스와 툴의 자유도가 높다는 것입니다. 그래서 가끔 필요에 의해 Framer를 쓰기도 하는데요, 주말동안 취미활동으로 간단한 프로토타입을 하나 만들어봤습니다. True Balance는 인도에서만 서비스를 하고 있는데, 인도는 워낙 네트워크 사정이 좋지 않아서 출장가면 이 게임을 자주 했거든요. 그리고 이 게임을 저희 앱에 넣는 것이 오래된 염원입니다 :)
저는 Framer를 정식으로 공부한 사람(?)은 아닙니다. 그래서 중요한 초식이나 널리 알려진 기술들은 모른채, 제가 하고 싶은 것만 무작정 찾고 공부해서 만드는 성격입니다. 따라서 아래의 튜토리얼중에 다소 바보같은 내용들이 있을 수도 있습니다. 그래도 제가 삽질한 경험을 바라보며 다른 디자이너분들도 Framer를 통해 다양한 프로토타이핑을 할 수 있지 않을까 하는 마음에 제작기를 공유합니다.
디자이너가 코딩을 공부해야 하냐구요? 꼭 그렇지는 않다고 생각합니다. 하지만 이런 프로토타이핑을 위한 코딩 정도는, 머릿속에 떠오르는 인터랙션을 개발팀에 설명하기 좋았습니다. 사실, pxd 이재용 대표님은 다른 관점으로 그 필요성을 주장하고 계십니다 :)
Framer로 공룡게임을 만들 수 있을까를 고민하면서, 지금까지 제가 알고있는 지식을 벗어나는 두 가지의 어려움이 있었습니다.
1. 여러 애니메이션이 동시에 동작할 수 있나? (횡스크롤과 점프)
2. 오브젝트가 부딛히는 것을 감지할 수 있나? (공룡이 나무에 닿으면 게임이 끝나야 함)
결론은 ‘된다’ 입니다. 이 두가지를 먼저 삽질을 통해 구현하고, 실제의 작업에 들어갔습니다.
그 다음으로, 어떤 재료가 필요할까, 어떤 연장이 필요할까, 어떤 구조가 필요할까를 고민했습니다.
1. Sketch로 소스를 만든다 (바닥, 공룡, 나무)
2. 코드 구조 잡기와 사전 설정
3. 횡스크롤이 되도록 만든다
4. 점프가 되도록 만든다
5. 공룡과 나무가 부딛히면 끝나게 만든다
6. 재시작 할 수 있게 만든다
참 쉽죠? 그럼.. 실제 제작기를 공유합니다.
먼저 Sketch로 리소스를 만듭니다. 바닥은 Framer에서 복제하여 반복시킬 예정이라서 딱 스크린 너비에 맞게만 제작했습니다.
코드 구조 잡기는 제가 지금까지 Framer 작업을 하며 익숙해진 구조에 맞춰 필요한 변수를 지정하는 작업입니다, 사실 이건 계획을 세웠다기보다 만들다보니 생긴 것들입니다.
코드의 구조는 대략 아래와 같습니다.
1. Sketch 불러오기 코드
2. 각종 변수 설정하기 코드
3. 애니메이션/함수 설정 코드
4. 이벤트 감지 및 액션 코드
처음 Sketch 파일을 불러오는건, Framer의 ‘Import’ 기능을 사용하면 쉽게 진행할 수 있습니다. 아래의 소스 중 '#'으로 시작하는 줄은 주석이라, 실제 코드가 아닌 설명입니다.
#Sketch에서 Import기능을 하면 자동 생성되는 코드
# Import file "m-rex" (sizes and positions are scaled 1:2)
mrex = Framer.Importer.load("imported/m-rex@2x")
횡스크롤과 관련된 계산을 하기 위해서, 설정된 디바이스의 너비를 변수에 넣습니다. 한김에, 나무의 위치도 스크린 우측 바깥으로 수정합니다. 왜 나무의 x값이 복잡하냐고요? 이따가 설명드리겠습니다.
# 디바이스 width 값 저장, 나무 위치 설정
dWidth = Framer.Device.screen.width
mrex.tree.x = dWidth *1.5 - mrex.tree.width
그리고 Sketch로 불러온 리소스들을 쓰기 쉬운 이름으로 다시 지정합니다. 이 작업은 꼭 필요한 작업은 아닙니다. 아, 그리고 게임오버되었을 때 재시작을 위한 ‘Retry’버튼은 숨겨놓습니다.
# 스케치에서 임포트한 레이어들의 이름을 짧게 설정
RoadA = mrex.road
Tree = mrex.tree
Monkey = mrex.monkey
Retry = mrex.retry
Retry.visible = false
그리고 다음으로, 바닥을 복제하여 좌측으로 스크롤되어도 계속 표시될 수 있도록, 복제를 하고, 'Road'라는 새로운 레이어를 만들어서, 기존 바닥레이어와 복제된 레이어가 'Road'의 하위 레이어가 되도록 묶어줍니다.
# 바닥 복제 및 위치 설정, Parent layer 지정
Road = new Layer
width:1
height:1
RoadB = RoadA.copy()
RoadB.props =
parent : Road
x : dWidth
RoadA.parent = Road
RoadB.parent = Road
이렇게까지 설정을 하고 나면, Framer 코드상에서 오브젝트들은 아래와 같은 위치에 자리를 잡게 되고, 'Road' 레이어를 제어하면 RoadA,B가 동시에 움직이게 됩니다.
자, 이제 실제로 좀 동작이 되는 애들을 만들어보죠. 먼저 바닥과 나무를 횡스크롤 시키는 작업입니다. 첫 번째로, 애니메이션을 설정해둡니다. 아래와 같이 애니메이션을 만들어 두면, 나중에 필요한 시점에 해당 애니메이션을 실행시킬 수 있습니다.
따로 세세한 설명은 드리지 않겠습니다. Framer Docs에 자세히 나와있거든요. repeat는 꼼수인데, 이 애니메이션을 반복할 횟수입니다. 1000번 하면.. 계속 스크롤 되는 것처럼 보이겠죠?
# 땅 횡스크롤 애니메이션
roadAni = new Animation
layer : Road
properties:
x: - dWidth
time: 1.5
curve: "linear"
repeat: 1000
# 나무 횡스크롤 애니메이션 설정
treeAni = new Animation
layer : Tree
properties:
x: - dWidth * 0.5
time: 3
curve: "linear"
repeat: 1000
# 땅/나무 횡스크롤 애니메이션 시작하기
roadAni.start()
treeAni.start()
아, 그리고 roadAni의 x가 -dWidth이고, treeAni의 x가 - dWidth*0.5 인 이유는 아래와 같습니다. 위에서 아래로 애니메이션의 진행 순서입니다.
지금까지 만든 것들이 어떻게 동작하나 한번 볼까요? 지금까지 적은 코드를 Framer로 넣으시면 아래와 같이 출력물이 표시됩니다.
3번, 땅 스크롤을 성공하셨다면 이것도 쉽습니다. 점프 애니메이션을 설정하고, 탭했을 때 점프 애니메이션이 실행되게 만들면 됩니다. 먼저 점프 애니메이션을 설정하고..
# 원숭이 점프 애니메이션 설정
Mjump = new Animation
layer: Monkey
properties:
y : 550
time: 0.3
curve: "ease-in-out"
# 원숭이 점프 애니메이션 : 아래로 돌아오기
MjumpB = Mjump.reverse()
Mjump.on(Events.AnimationEnd, MjumpB.start)
위의 ‘아래로 돌아오기’는, 위로 올라가는 Mjump 애니메이션이 끝난 후, 다시 Mjump 애니메이션을 거꾸로 작동하게 하는 코드입니다.
그리고 이제 어디를 탭했을 때 실행되게 할지를 설정해야 하는데요, 화면 전체의 백그라운드 레이어를 만들어서, 어디를 탭하든 실행되도록 하겠습니다.
# 백그라운드 레이어 만들기
bg = new BackgroundLayer
# 탭 하면 점프!
bg.onTap ->
Mjump.start()
자, 이제 지금까지 작성한 코드가 잘 동작하는지 실행해볼까요?
이제 거의 막바지이면서, 산수를 싫어했던 제가 가장 머리아팠던 작업인데요, 코드를 보시죠
# 원숭이와 나무가 만나는 것 감지 & 이후 액션
Tree.on "change:x", ->
if Tree.x > Monkey.x + Monkey.width
else if Tree.x + Tree.width < Monkey.x
else
if Monkey.y + Monkey.height < Tree.y
else
roadAni.stop()
treeAni.stop()
Mjump.stop()
MjumpB.stop()
Retry.visible = true
Retry.onTap ->
location.reload()
자세한 설명은 생략하겠습니다 ^^ 아마.. 다들 잘 해석하시리라 생각합니다. 아, 그리고 소소하게.. Score가 나오는 코드도 추가했습니다.
사실 Flash action script를 써보신 분이라면 그냥 눈으로봐도 아실 것 같아요. 개발을 하시는 분이라면, 이 글을 읽지 마시고....
링크 : Sketch 소스
링크 : 전체 코드 소스
다 완성하고 나니, 인터랙션 프로토타이핑 툴인 Framer를 이상한 용도로 쓴 것 같네요. 하지만 공룡게임을 만들었다는 것에 만족감을 느끼며! Framer로 공룡게임 만들기 제작기를 마칩니다 :)