본문 바로가기

강좌/트윈맥스를 쓰자!

트윈맥스를 쓰자! [ 00 ] - 엔터프레임은 이제 좀 쉬게 두세요

안녕하세요. 언노운입니다.

굳이 트윈맥스를 가지고 강좌를 해야 하는가에 대해서 잠깐 고민이 들었지만... 의외로 아직 트윈 API의 존재를 모르고서 작업하시는 분들도 많고, 스스로도 정리를 해보는 기회도 될 겸 해서 시작해 보았습니다.

====================

간단한 플래시입니다. 이런 동작을 구현할 때 어떤 방식을 사용하고 계시나요?

플래시에서는 전통적으로 엔터프레임을 이용해서 애니메이션을 구현했습니다. 이런 방식의 애니메이션은 굳이 플래시에만 있는 건 아닙니다. '연속적으로 함수가 호출될 수 있는' 방법을 이용해서 애니메이션을 구현하는 것은 어느 언어에서나 있으니까요. 플래시에서도 굳이 엔터프레임이 아니라 setInterval이나 Timer를 이용해서도 충분히 구현할 수 있습니다. 여튼 대략 다음과 같은 방법으로 구현하게 되겠죠.

function enterframeHandler( e:Event ):void
{
    circle.x += ( 450 - circle.x ) * 0.1;
}

circle.addEventListener( Event.ENTER_FRAME, enterframeHandler );

위와 같은 코드는 워낙 원시적인 형태인지라 많은 단점을 가지고 있습니다. 대략 꼽아보자면 다음과 같은 단점이 있겠죠.

  1. 많이 쓰는 코드라 가져다 쓰기는 했는데 내가 저 공식을 이해하고 있는 것은 아니라는 점. (...)
  2. 동작이 정지한 다음에도 엔터프레임은 제거되지 않는다는 점.
  3. 애니메이션의 효과 조절이나 시간 조절을 순전히 감에 의존해야 하고 잦은 확인을 통해 맞춰야 한다는 점.

물론 상기의 2번 항목은 오히려 이용할 수 있는 면도 있기는 있습니다. 종료되지 않고 무한히 움직여야 하는 효과라면 되려 엔터프레임을 이용해야 하는 경우도 있을 수 있죠.

만약 이런 애니메이션이 순차적으로 일어난다거나, 복잡한 요구사항들이 생기기 시작하면 코드는 걷잡을 수 없이 증가하고, 복잡해지고, 신경써야 합니다. 다음과 같은 상황을 한번 생각해 보죠.

계속해서 반복됩니다만, 한번만 동작한다고 생각해 보겠습니다.

function move1( e:Event ):void
{
    circle.x += ( 450 - circle.x ) * 0.3;
    
    if( Math.round( circle.x ) == 450 )
    {
        // 좌표의 반올림값이 목표값과 같을 경우 좌표를 정수값으로 지정해버리고
        // 이벤트 제거, 재 지정. 이하 함수 같은 방식.
        circle.x = 450;
        circle.removeEventListener( Event.ENTER_FRAME, move1 );
        circle.addEventListener( Event.ENTER_FRAME, move2 );
    }
}

function move2( e:Event ):void
{
    circle.y += ( 300 - circle.y ) * 0.3;
    
    if( Math.round( circle.y ) == 300 )
    {
        circle.y = 300;
        circle.removeEventListener( Event.ENTER_FRAME, move2 );
        circle.addEventListener( Event.ENTER_FRAME, move3 );
    }
}

function move3( e:Event ):void
{
    circle.x += ( 50 - circle.x ) * 0.3;
    
    if( Math.round( circle.x ) == 50 )
    {
        circle.x = 50;
        circle.removeEventListener( Event.ENTER_FRAME, move3 );
        circle.addEventListener( Event.ENTER_FRAME, move4 );
    }
}

function move4( e:Event ):void
{
    circle.y += ( 50 - circle.y ) * 0.3;
    
    if( Math.round( circle.y ) == 50 )
    {
        // 여기서 끝.
    }
}

circle.addEventListener( Event.ENTER_FRAME, move1 );

이동이 총 4번 이루어집니다. 순차적으로 동작합니다. 조건식을 이용해서 이전의 동작을 계속 체크하고, 그 다음 동작으로 이어 줍니다. 제법 복잡한 코드가 되었습니다.

이 코드의 문제점은 이런 부분에서 드러나기 시작합니다. 만드는 과정이 복잡한 것도 문제지만, 여기에 수정사항들이 생기기 시작하면 매우 귀찮은 일이 됩니다. 좌표를 수정하면 조건식도 수정해야 하고, 한꺼번에 여러개의 속성이 움직일 경우 조건식이 더욱 복잡해지게 되고, 여튼 여러가지로 성가신 상황들은 얼마든지 발생하게 됩니다.

코드를 짜면서 신경을 써야 하는 부분들은 얼마든지 있습니다. 원시적인 방법론을 공부하는 것은 바람직한 일이지만 그것이 공부에만 사용되어야지 실제 작업에 계속되어 반영될 필요는 없습니다. 물론 쏟아지는 요구사항과 높아지는 눈높이를 모두 충족시킬 수 있을 만큼 코딩을 하실 수 있으면 상관 없겠습니다만... 당장 저 애니메이션에 이징(easing)효과를 역으로 주려면 공식을 어떻게 바꿔야 하죠? (...)

우리는 그래서 트윈 API 를 씁니다. 이제는 업데이트가 되지 않는 caurina Tweener (일명 구글 트위터라고 잘못 불리워지는) 를 비롯해 TweenMax, BetweenAS3등등의 많은 트윈 API 는 우리의 코딩을 행복하게 해 줍니다.

자세한 활용법은 다음 강좌에서 보도록 하고, 위 코드를 트윈맥스로 표현하면 다음과 같아집니다.

첫번째 코드는 다음과 같이

import com.greensock.TweenMax;

TweenMax.to( circle, 1, { x:450 } );
// circle 객체를, 1초동안, x 좌표를 450으로

두번째 코드는 다음과 같이

import com.greensock.TweenMax;

TweenMax.to( circle, 1, { x:450 } );
TweenMax.to( circle, 1, { y:450, delay:1 } );
//// circle 객체를, 1초동안, x 좌표를 450으로, 1초 딜레이를 가지고서. 이하 동일.
TweenMax.to( circle, 1, { x:50, delay:2 } );
TweenMax.to( circle, 1, { y:50, delay:3 } );

표현됩니다.

참 쉽죠?

자, 엔터프레임은 이제 그만 쉬게 두세요. 트윈맥스를 씁시다! (혹은 다른 트윈 API 라도)