Animation - kuimoani/defold GitHub Wiki

Animation

DefoldλŠ” 였브젝트 μ»΄ν¬λ„ŒνŠΈμ˜ κ·Έλž˜ν”½ μ†ŒμŠ€λ‘œ μ‚¬μš©ν•  수 μžˆλŠ” λ‹€μ–‘ν•œ μ’…λ₯˜μ˜ μ• λ‹ˆλ©”μ΄μ…˜μ„ μ§€μ›ν•©λ‹ˆλ‹€.

Flip-book animation

ν”Œλ¦½λΆ μ• λ‹ˆλ©”μ΄μ…˜μ€ μ—°μ†μ μœΌλ‘œ ν‘œμ‹œλ˜λŠ” 일련의 μŠ€ν‹Έμ»· μ΄λ―Έμ§€λ“€λ‘œ κ΅¬μ„±λ©λ‹ˆλ‹€. 이 κΈ°μˆ μ€ 전톡적인 μ…€ μ—λ‹ˆλ©”μ΄μ…˜κ³Ό 맀우 λΉ„μŠ·ν•©λ‹ˆλ‹€(http://en.wikipedia.org/wiki/Traditional_animation μ°Έκ³ ). 이 방식은 각각의 ν”„λ ˆμž„μ„ κ°œλ³„μ μœΌλ‘œ μ‘°μž‘ν•˜λ―€λ‘œ λ¬΄ν•œν•œ κ°€λŠ₯성이 μžˆμŠ΅λ‹ˆλ‹€. ν•˜μ§€λ§Œ ν”„λ ˆμž„ λ§ˆλ‹€ κ³ μœ ν•œ μ΄λ―Έμ§€λ‘œ μ €μž₯ν•΄μ•Ό ν•˜λ―€λ‘œ λ©”λͺ¨λ¦¬ 곡간이 많이 ν•„μš”ν•  수 μžˆμŠ΅λ‹ˆλ‹€. λ˜ν•œ μ• λ‹ˆλ©”μ΄μ…˜μ„ λΆ€λ“œλŸ½κ²Œ ν‘œν˜„ν•˜λ €λ©΄ λ§€ μ΄ˆλ§ˆλ‹€ λ‚˜νƒ€λ‚˜λŠ” μ΄λ―Έμ§€μ˜ 수λ₯Ό λŠ˜λ¦¬λŠ”λ° μ˜μ‘΄ν•΄μ•Ό ν•˜λ―€λ‘œ 그만큼 μž‘μ—…λŸ‰ λ˜ν•œ μ¦κ°€ν•˜κ²Œ λ©λ‹ˆλ‹€. Defold의 ν”Œλ¦½λΆ μ• λ‹ˆλ©”μ΄μ…˜μ€ κ°œλ³„ 이미지λ₯Ό μ•„ν‹€λΌμŠ€μ— μΆ”κ°€ν•˜μ—¬ μ €μž₯ν•˜κ±°λ‚˜ λͺ¨λ“  ν”„λ ˆμž„μ΄ μˆ˜ν‰μ μœΌλ‘œ 배치된 타일 μ†ŒμŠ€μ— μ €μž₯λ©λ‹ˆλ‹€.

Spine animation

슀파인 μ• λ‹ˆλ©”μ΄μ…˜μ€ 2D μŠ€μΌˆλ ˆν†€ μ• λ‹ˆλ©”μ΄μ…˜μ„ μ œκ³΅ν•©λ‹ˆλ‹€ (http://en.wikipedia.org/wiki/Skeletal_animation μ°Έκ³ ). μ΄λŠ” 컷아웃 μ• λ‹ˆλ©”μ΄μ…˜κ³ΌλŠ” 근본적으둜 λ‹€λ₯Έ κΈ°μˆ μž…λ‹ˆλ‹€. 컷아웃 μ• λ‹ˆλ©”μ΄μ…˜μ—μ„œλŠ” μ• λ‹ˆλ©”μ΄μ…˜ 였브젝트의 κ°œλ³„ 쑰각듀(예λ₯Ό λ“€μ–΄ λͺΈμ²΄, 눈, μž… λ“±)이 각 ν”„λ ˆμž„κ°„ κ°œλ³„μ μœΌλ‘œ μ›€μ§μž…λ‹ˆλ‹€. 슀파인 μ• λ‹ˆλ©”μ΄μ…˜μ€ μ„œλ‘œ μ—°κ²°λœ λΌˆλŒ€ ꡬ쑰둜 κ΅¬μ„±λœ 보이지 μ•ŠλŠ” κ°€μƒμ˜ 골격을 λ§Œλ“­λ‹ˆλ‹€. κ°œλ³„ 이미지λ₯Ό 각 λΌˆλŒ€μ— 뢙이고 이 골격(skeleton) ν˜Ήμ€ λ¦­(rig)을 μ• λ‹ˆλ©”μ΄μ…˜ 처리 ν•  수 μžˆμŠ΅λ‹ˆλ‹€. DefoldλŠ” 슀파인 JSON 포멧으둜 μ• λ‹ˆλ©”μ΄μ…˜μ„ μƒμ„±ν•˜κ±°λ‚˜ 읡슀포트 ν•˜λŠ” 것을 μ§€μ›ν•©λ‹ˆλ‹€. μŠ€μΌˆλ ˆν†€ μ• λ‹ˆλ©”μ΄μ…˜μ€ 엔진이 λ§€ ν”„λ ˆμž„λ§ˆλ‹€ 각 λΌˆλŒ€μ˜ μœ„μΉ˜λ₯Ό 보간(interpolate)ν•΄ μ£ΌκΈ° λ•Œλ¬Έμ— 맀우 λΆ€λ“œλŸ½κ²Œ μ›€μ§μž…λ‹ˆλ‹€.

슀파인 데이터λ₯Ό μ–΄λ–»κ²Œ μž„ν¬νŠΈ ν•˜λŠ”μ§€ μžμ„Ένžˆ 보렀면 Spine λ¬Έμ„œλ₯Ό μ°Έκ³  λ°”λžλ‹ˆλ‹€.

3D skinned animation

3D λͺ¨λΈμ˜ μŠ€μΌˆλ ˆν†€ μ• λ‹ˆλ©”μ΄μ…˜μ€ 슀파인 μ• λ‹ˆλ©”μ΄μ…˜κ³Ό λΉ„μŠ·ν•˜μ§€λ§Œ 2Dκ°€ μ•„λ‹ˆλΌ 3D둜 λ™μž‘ν•©λ‹ˆλ‹€. 3D λͺ¨λΈμ€ λ³„λ„μ˜ 파트둜 μž˜λ¦¬μ§€ μ•Šκ³  컷아웃 μ• λ‹ˆλ©”μ΄μ…˜ 처럼 λΌˆλŒ€λ‘œ λ¬Άμž…λ‹ˆλ‹€. λŒ€μ‹  이 λΌˆλŒ€λ“€μ€ λͺ¨λΈμ˜ λ²„ν…μŠ€(vertex)에 λ³€ν˜•(deformation)을 μ μš©ν•  수 있으며, λΌˆλŒ€κ°€ λ²„ν…μŠ€μ— 영ν–₯을 λ―ΈμΉ˜λŠ” 정도λ₯Ό μ œμ–΄ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

3D 데이터λ₯Ό μ• λ‹ˆλ©”μ΄μ…˜ λͺ¨λΈλ‘œ μž„ν¬νŠΈ ν•˜λŠ” μžμ„Έν•œ 방법은 3D graphics λ¬Έμ„œλ₯Ό μ°Έκ³  λ°”λžλ‹ˆλ‹€.

Property animation

숫자둜 된 λͺ¨λ“  속성(숫자, vector3, vector4, μΏΌν„°λ‹ˆμ˜¨(quaterion))은 λ‚΄μž₯된 μ• λ‹ˆλ©”μ΄μ…˜ μ‹œμŠ€ν…œμ—μ„œ go.animate() ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•΄μ„œ μ• λ‹ˆλ©”μ΄μ…˜ 효과λ₯Ό 쀄 수 있으며 μ…‹νŒ…ν•œ ν”Œλ ˆμ΄λ°± λͺ¨λ“œ(playback mode)λ‚˜ 이징(easing) ν•¨μˆ˜μ— 따라 엔진이 μžλ™μ μœΌλ‘œ 속성값듀을 νŠΈμœ„λ‹(tween) ν•©λ‹ˆλ‹€. λ˜ν•œ 이징 ν•¨μˆ˜λ₯Ό μ»€μŠ€ν…€ν•˜κ²Œ λ³€κ²½ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.

Playing flip-book animations

μŠ€ν”„λΌμ΄νŠΈμ™€ GUI λ°•μŠ€ λ…Έλ“œλŠ” ν”Œλ¦½λΆ μ• λ‹ˆλ©”μ΄μ…˜μ„ μž¬μƒν•  수 있으며 λŸ°νƒ€μž„μ‹œ 이λ₯Ό μ œμ–΄ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Sprites

μŠ€ν”„λΌμ΄νŠΈλ₯Ό λŸ°νƒ€μž„μ‹œ μ• λ‹ˆλ©”μ΄μ…˜μ„ ν•˜κΈ° μœ„ν•΄μ„œλŠ” play_animation λ©”μ„Έμ§€λ₯Ό ν…μŠ€μ³λ₯Ό μ• λ‹ˆλ©”μ΄μ…˜ ν•˜κΈΈ μ›ν•˜λŠ” μŠ€ν”„λΌμ΄νŠΈ μ»΄ν¬λ„ŒνŠΈμ— 보내야 ν•©λ‹ˆλ‹€. μ• λ‹ˆλ©”μ΄μ…˜μ΄ ν”Œλ ˆμ΄λ₯Ό λλ‚΄μž 마자, 엔진은 play_animation λ©”μ„Έμ§€λ₯Ό λ³΄λƒˆλ˜ 슀크립트둜 animation_done λ©”μ„Έμ§€λ₯Ό 돌렀 μ€λ‹ˆλ‹€.

GUI box nodes

GUI λ°•μŠ€ λ…Έλ“œλ₯Ό λŸ°νƒ€μž„μ‹œ μ• λ‹ˆλ©”μ΄μ…˜μ„ ν•˜κΈ° μœ„ν•΄μ„œλŠ” gui.play_flipbook() ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€. μ•„λž˜ 예제λ₯Ό μ‚΄νŽ΄λ³΄κΈ° λ°”λžλ‹ˆλ‹€.

Sprite example

λ‹Ήμ‹ μ˜ κ²Œμž„μ— "dodge"λΌλŠ” κΈ°λŠ₯이 μžˆλ‹€κ³  κ°€μ •ν•΄ λ΄…μ‹œλ‹€. μ΄λŠ” νŠΉμ • λ²„νŠΌμ„ λˆ„λ₯΄λ©΄ ν”Œλ ˆμ΄μ–΄κ°€ νšŒν”Ό λ™μž‘μ„ ν•˜κ²Œ λ©λ‹ˆλ‹€. 당신은 4 μ’…λ₯˜μ˜ μ• λ‹ˆλ©”μ΄μ…˜μ„ λ§Œλ“€μ–΄μ„œ μ‹œκ°μ μΈ ν”Όλ“œλ°±μ„ μ£ΌλŠ” κΈ°λŠ₯을 μ§€μ›ν•©λ‹ˆλ‹€:

"idle"

ν”Œλ ˆμ΄μ–΄ 캐릭터가 λŒ€κΈ°ν•˜κ³  μžˆλŠ” 반볡 μ• λ‹ˆλ©”μ΄μ…˜.

"dodge_idle"

A looping animation of the player character idling while being in the dodging stance. ν”Œλ ˆμ΄μ–΄ 캐릭터가 νšŒν”Όλ™μž‘μ„ μ§€μ†ν•˜κ³  μžˆλŠ” 반볡 μ• λ‹ˆλ©”μ΄μ…˜.

"start_dodge"

ν”Œλ ˆμ΄μ–΄ 캐릭터가 νšŒν”Όλ₯Ό μ‹œμž‘ν•˜λŠ” μΌνšŒμ„±(play-once)의 과도기(transition) μ• λ‹ˆλ©”μ΄μ…˜.

"stop_dodge"

ν”Œλ ˆμ΄μ–΄ 캐릭터가 νšŒν”Όλ₯Ό λλ‚΄λŠ” μΌνšŒμ„±(play-once)의 과도기(transition) μ• λ‹ˆλ©”μ΄μ…˜.

이 λ‘œμ§μ€ μ•„λž˜ 슀크립트 처럼 κ΅¬ν˜„ν•  수 μžˆμŠ΅λ‹ˆλ‹€:

function on_input(self, action_id, action)
    -- λ²„νŠΌμž…λ ₯μ‹œ "dodge" λ™μž‘μ„ μ‹œμž‘ν•¨
    if action_id == hash("dodge") then
        if action.pressed then
            msg.post("#sprite", "play_animation", {id = hash("start_dodge")})
            -- νšŒν”Όλ™μž‘ μƒνƒœμΈμ§€ κΈ°μ–΅ν•˜κΈ°
            self.dodge = true
        elseif action.released then
            msg.post("#sprite", "play_animation", {id = hash("stop_dodge")})
            -- νšŒν”Όλ™μž‘μ„ 끝냄
            self.dodge = false
        end
    end
end

function on_message(self, message_id, message, sender)
    if message_id == hash("animation_done") then
        -- μ• λ‹ˆλ©”μ΄μ…˜μ˜ νŠΈλžœμ§€μ…˜μ΄ λλ‚˜κ³  반볡 μ• λ‹ˆλ©”μ΄μ…˜μ„ μ‹œμž‘ν•¨
        if self.dodge then
            msg.post("#sprite", "play_animation", {id = hash("dodge_idle")})
        else
            msg.post("#sprite", "play_animation", {id = hash("idle")})
        end
    end
end

GUI box node example

λ…Έλ“œμ˜ μ• λ‹ˆλ©”μ΄μ…˜μ΄λ‚˜ 이미지λ₯Ό μ„ νƒν•˜λŠ” 쀑에도, 이미지 μ†ŒμŠ€(μ•„ν‹€λΌμŠ€λ‚˜ νƒ€μΌμ†ŒμŠ€)와 κΈ°λ³Έ μ• λ‹ˆλ©”μ΄μ…˜μ„ ν•œλ°©μ— ν• λ‹Ή ν•  수 μžˆμŠ΅λ‹ˆλ‹€. 이미지 μ†ŒμŠ€λŠ” λ…Έλ“œμ— μ •μ μœΌλ‘œ μ…‹νŒ…λ˜μ§€λ§Œ ν˜„μž¬ μ• λ‹ˆλ©”μ΄μ…˜μ˜ ν”Œλ ˆμ΄λ₯Ό λŸ°νƒ€μž„μ‹œμ—λ„ λ³€κ²½ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μŠ€ν‹Έμ»· 이미지듀은 ν•œ ν”„λ ˆμž„μ§œλ¦¬ μ• λ‹ˆλ©”μ΄μ…˜ 처럼 λ‹€λ€„μ§€λ―€λ‘œ λŸ°νƒ€μž„μ‹œ 이미지λ₯Ό λ°”κΎΌλ‹€λŠ” 것은 λ…Έλ“œμ—μ„œ 각기 λ‹€λ₯Έ ν”Œλ¦½λΆ μ• λ‹ˆλ©”μ΄μ…˜μ„ ν”Œλ ˆμ΄ ν•˜λŠ”κ²ƒκ³Ό μ™„μ „νžˆ λ™μΌν•©λ‹ˆλ‹€:

local function flipbook_done(self)
    msg.post("#", "jump_completed")
end

function init(self)
    local character_node = gui.get_node("character")
	-- λ…Έλ“œκ°€ 이 μž‘μ—…μ„ μˆ˜ν–‰ν•˜λ €λ©΄ μž¬μƒμ€‘μΈ μƒˆ μ• λ‹ˆλ©”μ΄μ…˜/이미지와 λ™μΌν•œ μ•„ν‹€λΌμŠ€ λ˜λŠ” νƒ€μΌμ†ŒμŠ€μ— κΈ°λ³Έ μ• λ‹ˆλ©”μ΄μ…˜μ„ κ°€μ§€κ³  μžˆμ–΄μ•Ό ν•©λ‹ˆλ‹€.
    gui.play_flipbook(character_node, "jump_left", flipbook_done)
end

μ™„λ£Œμ‹œ ν˜ΈμΆœλ˜λŠ” 선택적(optional) ν•¨μˆ˜κ°€ 제곡될 수 μžˆμŠ΅λ‹ˆλ‹€(μœ„ 예제의 flipbook_done ν•¨μˆ˜μ²˜λŸΌ). μ΄λŠ” ONCE__* λͺ¨λ“œμ—μ„œ μž¬μƒλ˜λŠ” μ• λ‹ˆλ©”μ΄μ…˜μœΌλ‘œλΆ€ν„° ν˜ΈμΆœλ©λ‹ˆλ‹€.

Animating Spine models

슀파인 λͺ¨λΈμ— μ• λ‹ˆλ©”μ΄μ…˜μ„ μ μš©ν•˜λ €λ©΄, κ°„λ‹¨νžˆ spine.play_anim() ν•¨μˆ˜λ₯Ό ν˜ΈμΆœν•˜λ©΄ λ©λ‹ˆλ‹€:

local function anim_done(self)
    -- μ• λ‹ˆλ©”μ΄μ…˜μ΄ μ’…λ£Œλ˜κ³ , λ­”κ°€ μœ μš©ν•œ 일을 ν•©λ‹ˆλ‹€...
end

function init(self)
    -- "spinemodel" μ»΄ν¬λ„ŒνŠΈμ˜ "walk" μ• λ‹ˆλ©”μ΄μ…˜μ„ ν”Œλ ˆμ΄ν•˜κ³  첫 0.1초 λ™μ•ˆ 이전 μ• λ‹ˆλ©”μ΄μ…˜κ³Ό λΈ”λžœλ”©ν•œ ν›„ μ½œλ°±μ„ ν˜ΈμΆœν•©λ‹ˆλ‹€.
    local anim_props = { blend_duration = 0.1 }
    spine.play_anim("#spinemodel", "walk", go.PLAYBACK_LOOP_FORWARD, anim_props, anim_done)
end

Spine model in game

μ• λ‹ˆλ©”μ΄μ…˜μ΄ go.PLAYBACK_ONCE_ λͺ¨λ“œλ‘œ ν”Œλ ˆμ΄λ˜κ³  spine.play_anim() ν•¨μˆ˜μ— 콜백 ν•¨μˆ˜λ₯Ό λ„˜κΈ°λ©΄ μ• λ‹ˆλ©”μ΄μ…˜ μ™„λ£Œμ‹œ 이 콜백 ν•¨μˆ˜κ°€ μ‹€ν–‰λ©λ‹ˆλ‹€. μ•„λž˜μ˜ 콜백(callback) 정보λ₯Ό μ°Έκ³ λ°”λžλ‹ˆλ‹€.

Cursor animation

spine.play_anim() λ₯Ό μ‚¬μš©ν•΄μ„œ 슀파인 μ• λ‹ˆλ©”μ΄μ…˜μ„ μ§„ν–‰ν•˜λŠ” 것 외에도, Spine Model μ»΄ν¬λ„ŒνŠΈλŠ” go.animate() 으둜 λ‹€λ£° 수 μžˆλŠ” "cursor" 속성을 μ œκ³΅ν•©λ‹ˆλ‹€.

-- 슀파인 λͺ¨λΈμ— μ• λ‹ˆλ©”μ΄μ…˜μ„ μ„€μ •ν•˜κ³  μž¬μƒμ€ μ•ˆν•¨
spine.play_anim("#spinemodel", "run_right", go.PLAYBACK_NONE)

-- μ»€μ„œ μœ„μΉ˜λ₯Ό 0으둜 섀정함
go.set("#spinemodel", "cursor", 0)

-- μ»€μ„œλ₯Ό 느리게 νŠΈμœ„λ‹ μ‹œν‚€κ³  in-out quad 이징(easing) 효과λ₯Ό μ£Όκ³  핑퐁 μ• λ‹ˆλ©”μ΄μ…˜ μ²˜λ¦¬ν•¨
go.animate("#spinemodel", "cursor", go.PLAYBACK_LOOP_PINGPONG, 1, go.EASING_INOUTQUAD, 6)

μ»€μ„œκ°€ νŠΈμœ„λ‹(tween)λ˜κ±°λ‚˜ μ…‹νŒ…λ˜λ©΄, νƒ€μž„λΌμΈ 이벀트(timeline events)κ°€ μ˜ˆμƒλŒ€λ‘œ λ°œμƒν•˜μ§€ μ•Šμ„ 수 μžˆμŠ΅λ‹ˆλ‹€.

The bone hierarchy

슀파인 μŠ€μΌˆλ ˆν†€μ˜ κ°œλ³„ λΌˆλŒ€λ“€μ€ κ²Œμž„ μ˜€λΈŒμ νŠΈμ— λ‚΄λΆ€μ μœΌλ‘œ λ‚˜νƒ€λ‚©λ‹ˆλ‹€. 슀파인 λͺ¨λΈ μ»΄ν¬λ„ŒνŠΈμ˜ 아웃라인 μ°½(Outline)μ—μ„œ 전체 계측ꡬ쑰가 ν‘œμ‹œλ©λ‹ˆλ‹€. μ—¬κΈ°μ„œ μŠ€μΌˆλ ˆν†€ ꡬ쑰의 각 λΌˆλŒ€μ˜ 이름과 μœ„μΉ˜λ₯Ό λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

Spine model hierarchy

λΌˆλŒ€μ˜ 이름을 μ‚¬μš©ν•˜μ—¬, λŸ°νƒ€μž„μ‹œ λΌˆλŒ€μ˜ μΈμŠ€ν„΄μŠ€ 아이디λ₯Ό 검색할 수 μžˆμŠ΅λ‹ˆλ‹€. spine.get_go() ν•¨μˆ˜λŠ” νŠΉμ • λΌˆλŒ€μ˜ 아이디λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, μ›€μ§μ΄λŠ” κ²Œμž„ 였브젝트 μ•„λž˜μ— λ‹€λ₯Έ 였브젝트λ₯Ό μžμ‹ 객체둜 넣을 수 μžˆμŠ΅λ‹ˆλ‹€:

-- heroine의 손에 ꢌ총 였브젝트λ₯Ό κ°–λ‹€ λΆ™μž„
local hand = spine.get_go("heroine#spinemodel", "front_hand")
msg.post("pistol", "set_parent", { parent_id = hand })

Timeline events

슀파인 μ• λ‹ˆλ©”μ΄μ…˜μ€ μ •ν™•ν•œ μˆœκ°„μ— λ©”μ„Έμ§€λ₯Ό λ³΄λ‚΄μ„œ 이벀트λ₯Ό 트리거 ν•  수 μžˆμŠ΅λ‹ˆλ‹€. μ΄λŠ” 발자ꡭ μ†Œλ¦¬λ₯Ό μž¬μƒν•˜κ±°λ‚˜, νŒŒν‹°ν΄ 효과λ₯Ό μŠ€ν°ν•˜κ±°λ‚˜, λΌˆλŒ€ 계측ꡬ쑰에 였브젝트λ₯Ό 뢙이고 λ–Όκ±°λ‚˜ ν•˜λŠ” λ“±λ“±μ˜ 이벀트λ₯Ό μ• λ‹ˆλ©”μ΄μ…˜κ³Ό 동기화 λ˜μ–΄ 배치 ν•΄μ•Όλ§Œ ν•˜λŠ” κ²½μš°μ— 맀우 μœ μš©ν•©λ‹ˆλ‹€.

μ΄λ²€νŠΈλ“€μ€ 슀파인 μ†Œν”„νŠΈμ›¨μ–΄μ—μ„œ μΆ”κ°€ν•  수 있으며 ν”Œλ ˆμ΄λ°± νƒ€μž„λΌμΈ(playback timeline)μ—μ„œ μ‹œκ°ν™” λ©λ‹ˆλ‹€:

Spine events

각 μ΄λ²€νŠΈλŠ” 이름 μ‹λ³„μž(name identifier: μœ„ μ˜ˆμ œμ—μ„œ "bump")둜 참쑰되며 νƒ€μž„λΌμΈμ˜ 각 이벀트 μΈμŠ€ν„΄μŠ€λŠ” μΆ”κ°€ 정보λ₯Ό 포함할 수 μžˆμŠ΅λ‹ˆλ‹€:

Integer

μ •μˆ˜ν˜• μˆ«μžκ°’

Float

λΆ€λ™μ†Œμˆ˜μ  μˆ«μžκ°’

String

λ¬Έμžμ—΄ κ°’

μ• λ‹ˆλ©”μ΄μ…˜μ΄ ν”Œλ ˆμ΄λ˜κ³  νƒ€μž„λΌμΈ μ΄λ²€νŠΈκ°€ λ°œμƒν•˜λ©΄, spine_event λ©”μ„Έμ§€κ°€ spine.play()λ₯Ό ν˜ΈμΆœν–ˆλ˜ 슀크립트 μ»΄ν¬λ„ŒνŠΈλ‘œ μ „μ†‘λ©λ‹ˆλ‹€. 이 λ©”μ„Έμ§€ λ°μ΄ν„°μ—λŠ” μ΄λ²€νŠΈμ— λ‚΄μž₯된 μ»€μŠ€ν…€ μˆ«μžλ‚˜ λ¬Έμžμ—΄ 뿐만 μ•„λ‹ˆλΌ λ•Œλ‘œλŠ” μœ μš©ν•œ 좔가적인 ν•„λ“œλ„ ν¬ν•¨ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€:

t

μ• λ‹ˆλ©”μ΄μ…˜μ˜ 첫 번째 ν”„λ ˆμž„ 이후 경과된 μ‹œκ°„(초)

animation_id

μ• λ‹ˆλ©”μ΄μ…˜ 이름, 해쉬(hash)됨

string

제곡된 λ¬Έμžμ—΄ κ°’, 해쉬(hash)됨

float

제곡된 뢀동 μ†Œμˆ˜μ  μˆ«μžκ°’

integer

제곡된 μ •μˆ˜ν˜• μˆ«μžκ°’

event_id

이벀트 μ‹λ³„μž, 해쉬(hash)됨

blend_weight

μ• λ‹ˆλ©”μ΄μ…˜μ΄ μ–Όλ§ˆλ‚˜ λΈ”λ Œλ“œ(blend) λ˜μ—ˆλŠ”μ§€μ— λŒ€ν•œ κ°’. 0은 ν˜„μž¬ μ• λ‹ˆλ©”μ΄μ…˜μ΄ 아직 λΈ”λ Œλ“œμ˜ 일뢀뢄이 μ•„λ‹˜μ„ μ˜λ―Έν•˜λ©°, 1은 ν˜„μž¬ μ• λ‹ˆλ©”μ΄μ…˜μ΄ 100%둜 κ΅¬μ„±λœ λΈ”λ Œλ“œ λΌλŠ”κ²ƒμ„ μ˜λ―Έν•¨.

-- 슀파인 μ• λ‹ˆλ©”μ΄μ…˜μ΄ μ• λ‹ˆλ©”μ΄μ…˜κ³Ό λ™κΈ°ν™”λœ μ‚¬μš΄λ“œλ₯Ό ν”Œλ ˆμ΄ν•˜κΈ° μœ„ν•΄ 이벀트λ₯Ό ν¬ν•¨ν•˜κ³  있음.
-- μ—¬κΈ°λ‘œ λ©”μ„Έμ§€κ°€ 도착함.
function on_message(self, message_id, message, sender)
  if message_id == hash("spine_event") and message.event_id == hash("play_sound") then
    -- μ• λ‹ˆλ©”μ΄μ…˜ μ‚¬μš΄λ“œ ν”Œλ ˆμ΄ν•˜κΈ°. μ»€μŠ€ν…€ 이벀트 데이터에 μ‚¬μš΄λ“œ μ»΄ν¬λ„ŒνŠΈ 이름과 좜λ ₯κ°’(gain)이 λ“€μ–΄ 있음
    local url = msg.url("sounds")
    url.fragment = message.string
    msg.post(url, "play_sound", { gain = message.float })
  end
end

3D Model animation

λͺ¨λΈμ€ model.play_anim() ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•΄μ„œ μ• λ‹ˆλ©”μ΄μ…˜ λ©λ‹ˆλ‹€:

function init(self)
	-- #modelμ—μ„œ μ•žλ’€λ‘œ μ›€μ§μ΄λŠ” "wiggle" μ• λ‹ˆλ©”μ΄μ…˜ μ‹œμž‘
    model.play_anim("#model", "wiggle", go.PLAYBACK_LOOP_PINGPONG)
end

DefoldλŠ” ν˜„μž¬ ν˜„μž¬ 베이크 μ• λ‹ˆλ©”μ΄μ…˜(baked animations)만 μ§€μ›ν•©λ‹ˆλ‹€. μ• λ‹ˆλ©”μ΄μ…˜μ€ ν‚€ν”„λ ˆμž„λ§ˆλ‹€ 각각의 μ• λ‹ˆλ©”μ΄μ…˜ λ³Έ(animated bone)을 μœ„ν•œ λ©”νŠΈλ¦­μŠ€λ₯Ό κ°€μ Έμ•Ό ν•˜λ©° μœ„μΉ˜, νšŒμ „, μŠ€μΌ€μΌμ€ λ³„λ„μ˜ ν‚€λ‘œ κ°€μ§€μ§€ 말아야 ν•©λ‹ˆλ‹€.

λ˜ν•œ μ• λ‹ˆλ©”μ΄μ…˜μ€ μ„ ν˜•μ μœΌλ‘œ 보간(linearly interpolated)λ©λ‹ˆλ‹€. κ³ κΈ‰ 곑선 보간(curve interpolation)을 ν•˜λ €λ©΄ μ• λ‹ˆλ©”μ΄μ…˜μ„ μ΅μŠ€ν¬ν„°(exporter)μ—μ„œ 미리 κ΅¬μ›Œ(prebake)μ•Ό ν•©λ‹ˆλ‹€.

Collada의 μ• λ‹ˆλ©”μ΄μ…˜ 클립(Animation clip)은 μ§€μ›ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. λͺ¨λΈλ³„λ‘œ λ‹€μˆ˜μ˜ μ• λ‹ˆλ©”μ΄μ…˜μ„ μ‚¬μš©ν•˜κΈ° μœ„ν•΄μ„œλŠ” 각각 λ³„λ„μ˜ .dae 파일둜 읡슀포트 ν•˜κ³  Defold의 .animationset 파일둜 합쳐야 ν•©λ‹ˆλ‹€.

The bone hierarchy

λͺ¨λΈ μŠ€μΌˆλ ˆν†€μ˜ λΌˆλŒ€λ“€μ€ κ²Œμž„ μ˜€λΈŒμ νŠΈμ— λ‚΄λΆ€μ μœΌλ‘œ λ‚˜νƒ€λ‚©λ‹ˆλ‹€. λͺ¨λΈ μ»΄ν¬λ„ŒνŠΈμ˜ 아웃라인 μ°½(Outline)μ—μ„œ λΌˆλŒ€μ˜ 계측ꡬ쑰와 각 λΌˆλŒ€μ˜ 이름을 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

Model hierarchy

λŸ°νƒ€μž„μ‹œ λΌˆλŒ€ κ²Œμž„μ˜€λΈŒμ νŠΈμ˜ μΈμŠ€ν„΄μŠ€ 아이디λ₯Ό 탐색할 수 μžˆμŠ΅λ‹ˆλ‹€. model.get_go() ν•¨μˆ˜λŠ” νŠΉμ • λΌˆλŒ€μ˜ 아이디λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.

-- wiggler λͺ¨λΈμ˜ 쀑간 뼈λ₯Ό κ°€μ Έμ˜΄
local bone_go = model.get_go("#wiggler", "Bone_002")

-- 이제 κ²Œμž„ 였브젝트둜 λ­”κ°€ μœ μš©ν•œ μž‘μ—…μ„ ν•΄λ³ΌκΉŒλ‚˜...

Cursor animation

슀파인 λͺ¨λΈκ³Ό λ§ˆμ°¬κ°€μ§€λ‘œ, 3D λͺ¨λΈμ€ "cursor" 속성을 μ‘°μž‘ν•΄μ„œ μ• λ‹ˆλ©”μ΄μ…˜μ„ μ²˜λ¦¬ν•  수 μžˆμŠ΅λ‹ˆλ‹€:

-- #model에 μ• λ‹ˆλ©”μ΄μ…˜μ„ μ„€μ •ν•˜κ³  μž¬μƒμ€ 아직 μ•ˆν•¨
model.play_anim("#model", "wiggle", go.PLAYBACK_NONE)
-- μ• λ‹ˆλ©”μ΄μ…˜μ˜ μ‹œμž‘μ μœΌλ‘œ μ»€μ„œλ₯Ό μ…‹νŒ…ν•¨
go.set("#model", "cursor", 0)
-- 0κ³Ό 1 μ‚¬μ΄λ‘œ μ»€μ„œλ₯Ό νŠΈμœ„λ‹(Tween)함
go.animate("#model", "cursor", go.PLAYBACK_LOOP_PINGPONG, 1, go.EASING_INOUTQUAD, 3)

Property animation

κ²Œμž„ μ˜€λΈŒμ νŠΈλ‚˜ μ»΄ν¬λ„ŒνŠΈμ˜ 속성값을 μ• λ‹ˆλ©”μ΄μ…˜ μ²˜λ¦¬ν•˜κΈ° μœ„ν•΄μ„œλŠ” go.animate() ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•΄μ•Ό ν•©λ‹ˆλ‹€. GUI λ…Έλ“œ 속성을 μœ„ν•΄μ„œλŠ” gui.animate() λ₯Ό μ‚¬μš©ν•˜λ©΄ λ©λ‹ˆλ‹€.

-- μ»΄ν¬λ„ŒνŠΈμ˜ position의 y 속성값을 200으둜 μ„€μ •
go.set(".", "position.y", 200)
-- 그리고 μ• λ‹ˆλ©”μ΄μ…˜ μ‹œν‚΄
go.animate(".", "position.y", go.PLAYBACK_LOOP_PINGPONG, 100, go.EASING_OUTBOUNCE, 2)

μ£Όμ–΄μ§„ μ†μ„±μ˜ λͺ¨λ“  μ• λ‹ˆλ©”μ΄μ…˜μ„ μ€‘μ§€ν•˜λ €λ©΄, go.cancel_animations() λ₯Ό ν˜ΈμΆœν•˜κ±°λ‚˜ GUI λ…Έλ“œμ—μ„œλŠ” gui.cancel_animation()λ₯Ό ν˜ΈμΆœν•˜λ©΄ λ©λ‹ˆλ‹€:

-- ν˜„μž¬ κ²Œμž„μ˜€λΈŒμ νŠΈμ—μ„œ 였일러(euler) zμΆ•μ˜ νšŒμ „ μ• λ‹ˆλ©”μ΄μ…˜μ„ 쀑지함
go.cancel_animation(".", "euler.z")

λ§Œμ•½ "position"κ³Ό 같이 볡합적인(composite) μ†μ„±μ˜ μ• λ‹ˆλ©”μ΄μ…˜μ„ μ·¨μ†Œν•˜λ©΄, ν•˜μœ„ μš”μ†Œλ“€("position.x", "position.y", "position.z")의 μ• λ‹ˆλ©”μ΄μ…˜λ„ ν•¨κ»˜ μ·¨μ†Œλ©λ‹ˆλ‹€.

Properties 맀뉴얼에 κ²Œμž„ 였브젝트, μ»΄ν¬λ„ŒνŠΈ, GUIλ…Έλ“œμ˜ λͺ¨λ“  속성듀에 λŒ€ν•œ μ„€λͺ…이 μžˆμŠ΅λ‹ˆλ‹€.

GUI node property animation

λŒ€λΆ€λΆ„μ˜ λͺ¨λ“  GUI λ…Έλ“œ 속성듀은 μ• λ‹ˆλ©”μ΄μ…˜ κ°€λŠ₯ν•©λ‹ˆλ‹€. 예λ₯Ό λ“€μ–΄, "color" 속성을 μ„€μ •ν•΄μ„œ 투λͺ…값을 μ‘°μ ˆν•˜μ—¬ λ…Έλ“œλ₯Ό 보이지 μ•Šκ²Œ λ§Œλ“  후에, 색상을 ν•˜μ–—κ²Œ(tint μ»¬λŸ¬κ°€ μ•„λ‹˜) μ• λ‹ˆλ©”μ΄μ…˜ μ²˜λ¦¬ν•΄μ„œ νŽ˜μ΄λ“œμΈ 효과λ₯Ό 쀄 수 μžˆμŠ΅λ‹ˆλ‹€.

local node = gui.get_node("button")
local color = gui.get_color(node)
-- 색상을 ν•˜μ–—κ²Œ μ• λ‹ˆλ©”μ΄μ…˜ μ²˜λ¦¬ν•˜κΈ°
gui.animate(node, gui.PROP_COLOR, vmath.vector4(1, 1, 1, 1), gui.EASING_INOUTQUAD, 0.5)
-- 외곽을 λΆ‰μ€μƒ‰μœΌλ‘œ μ• λ‹ˆλ©”μ΄μ…˜ μ²˜λ¦¬ν•˜κΈ°
gui.animate(node, "outline.x", 1, gui.EASING_INOUTQUAD, 0.5)
-- 그리고 xμ’Œν‘œλ₯Ό 100으둜 μ΄λ™μ‹œν‚€κΈ°
gui.animate(node, hash("position.x"), 100, gui.EASING_INOUTQUAD, 0.5)

Playback Modes

μ• λ‹ˆλ©”μ΄μ…˜μ€ ν•œ 번 λ˜λŠ” 반볡적으둜 μž¬μƒλ  수 μžˆμŠ΅λ‹ˆλ‹€. μ• λ‹ˆλ©”μ΄μ…˜ μž¬μƒ 방법은 ν”Œλ ˆμ΄λ°± λͺ¨λ“œ(playback mode)에 μ˜ν•΄ κ²°μ • λ©λ‹ˆλ‹€:

  • go.PLAYBACK_NONE
  • go.PLAYBACK_ONCE_FORWARD
  • go.PLAYBACK_ONCE_BACKWARD
  • go.PLAYBACK_ONCE_PINGPONG
  • go.PLAYBACK_LOOP_FORWARD
  • go.PLAYBACK_LOOP_BACKWARD
  • go.PLAYBACK_LOOP_PINGPONG

핑퐁(pingpong) λͺ¨λ“œλŠ” μ• λ‹ˆλ©”μ΄μ…˜μ„ μ²˜μŒμ—” μ •λ°©ν–₯으둜 μž¬μƒν•˜κ³ , λ‹€μŒμ—” μ—­λ°©ν–₯으둜 μž¬μƒν•©λ‹ˆλ‹€. GUI 속성을 μ• λ‹ˆλ©”μ΄μ…˜ ν•˜κΈ° μœ„ν•œ λͺ¨λ“œλŠ” μ•„λž˜μ™€ κ°™μŠ΅λ‹ˆλ‹€:

  • gui.PLAYBACK_NONE
  • gui.PLAYBACK_ONCE_FORWARD
  • gui.PLAYBACK_ONCE_BACKWARD
  • gui.PLAYBACK_ONCE_PINGPONG
  • gui.PLAYBACK_LOOP_FORWARD
  • gui.PLAYBACK_LOOP_BACKWARD
  • gui.PLAYBACK_LOOP_PINGPONG

Easing

이징(easing)은 μ• λ‹ˆλ©”μ΄μ…˜ 값이 μ‹œκ°„μ— 따라 μ–΄λ–»κ²Œ λ³€ν™”ν•˜λŠ”μ§€λ₯Ό μ •μ˜ν•©λ‹ˆλ‹€. μ•„λž˜ 이미지듀은 μ΄μ§•μ˜ μ’…λ₯˜λ³„λ‘œ μ‹œκ°„μ— λ”°λ₯Έ λ³€ν™”λ₯Ό κ·Έλž˜ν”„λ‘œ μ„€λͺ…ν•˜κ³  μžˆμŠ΅λ‹ˆλ‹€.

go.animate() λ₯Ό ν˜ΈμΆœν•  λ•Œ μ‚¬μš©ν•˜λŠ” μ λ‹Ήν•œ 값은 go.EASING_LINEAR, go.EASING_INBACK, go.EASING_OUTBACK 등이 μžˆμŠ΅λ‹ˆλ‹€.

gui.animate() λ₯Ό ν˜ΈμΆœν•  λ•Œ μ‚¬μš©ν•˜λŠ” 값은 gui.EASING_LINEAR, gui.EASING_INBACK, gui.EASING_OUTBACK λ“±μž…λ‹ˆλ‹€.

Custom easing

κ°’μ˜ λͺ¨μŒ(set of values)으둜 벑터(vector)λ₯Ό μ •μ˜ν•΄μ„œ μœ„μ—μ„œ 미리 μ •μ˜λœ 이징 μƒμˆ˜(easing constants) λŒ€μ‹ μ— 이징 컀브(easing curve)λ₯Ό μ»€μŠ€ν…€ν•˜κ²Œ λ§Œλ“€ 수 μžˆμŠ΅λ‹ˆλ‹€:

local values = { 0, 0, 0, 0, 0, 0, 0, 0,
                 1, 1, 1, 1, 1, 1, 1, 1,
                 0, 0, 0, 0, 0, 0, 0, 0,
                 1, 1, 1, 1, 1, 1, 1, 1,
                 0, 0, 0, 0, 0, 0, 0, 0,
                 1, 1, 1, 1, 1, 1, 1, 1,
                 0, 0, 0, 0, 0, 0, 0, 0,
                 1, 1, 1, 1, 1, 1, 1, 1 }
local square_easing = vmath.vector(values)
go.animate("go", "position.y", go.PLAYBACK_LOOP_PINGPONG, 100, square_easing, 2.0)

Completion callbacks

λͺ¨λ“  μ• λ‹ˆλ©”μ΄μ…˜ ν•¨μˆ˜(go.animate(), gui.animate(), gui.play_flipbook(), gui.play_spine_anim(), spine.play_anim(), model.play_anim())λŠ” λ§ˆμ§€λ§‰ μΈμžκ°’μœΌλ‘œ 선택적인 Lua 콜백 ν•¨μˆ˜(optional Lua callback function)λ₯Ό μ§€μ›ν•©λ‹ˆλ‹€. 이 ν•¨μˆ˜λŠ” μ• λ‹ˆλ©”μ΄μ…˜μ˜ μž¬μƒμ΄ μ’…λ£Œλ˜λ©΄ ν˜ΈμΆœλ©λ‹ˆλ‹€. 이 ν•¨μˆ˜λŠ” 루프 μ• λ‹ˆλ©”μ΄μ…˜μΌ κ²½μš°μ΄κ±°λ‚˜ μ• λ‹ˆλ©”μ΄μ…˜μ΄ go.cancel_animations()둜 μˆ˜λ™μ μœΌλ‘œ μ·¨μ†Œ λ˜μ—ˆμ„ κ²½μš°μ—” ν˜ΈμΆœλ˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€. μ½œλ°±μ€ μ• λ‹ˆλ©”μ΄μ…˜ μ™„λ£Œμ‹œ 이벀트λ₯Ό νŠΈλ¦¬κ±°ν•˜κ±°λ‚˜ μ—¬λŸ¬ μ• λ‹ˆλ©”μ΄μ…˜μ„ ν•¨κ»˜ μ—°κ²°ν•˜λŠ”λ° μ‚¬μš©λ©λ‹ˆλ‹€.

콜백의 μ •ν™•ν•œ ν•¨μˆ˜ ν˜•νƒœ(signature)λŠ” μ• λ‹ˆλ©”μ΄μ…˜ ν•¨μˆ˜λ§ˆλ‹€ μ‘°κΈˆμ”© λ‹€λ¦…λ‹ˆλ‹€. μžμ„Έν•œ 것은 API λ¬Έμ„œλ₯Ό μ°Έκ³  λ°”λžλ‹ˆλ‹€.

local function done_bouncing(self, url, property)
    -- μ• λ‹ˆλ©”μ΄μ…˜μ„ λλƒˆλ‹€.. 이제 λ­”κ°€ ν•΄λ³΄μž...
end

function init(self)
    go.animate(".", "position.y", go.PLAYBACK_ONCE_FORWARD, 100, go.EASING_OUTBOUNCE, 2, 0, done_bouncing)
end