Tween 애니메이션 예제

public  static  class  Tween
{
    /// <param name = "time"> 경과 시간 </ param>
    /// <param name = "initial"> 기본값 </ param>
    /// <param name = "delta"> 종료 값과 초기 값의 차이 </ param>
    /// <param name = "duration"> 수명 </ param>
    public  static  Vector3  EaseInOutExpo ( float  time,  Vector3  initial,  Vector3  delta,  float  duration )
    {
        if  ( time <=  0f )
        {
            return  initial ;
        }

        if  ( time >=  duration )
        {
            return  initial  +  delta ;
        }

        time  /=  ( duration  /  2f );
        if  ( time < 1f )
        {
            return  delta / 2f * Mathf . Pow ( 2f,  10f  *  ( time - 1f ))  +  initial;
        }

        time  -=  1f;
        return  delta / 2f * (-1f  *  Mathf . Pow ( 2f,  -10f * time ) + 2f ) +  initial;
    }
}

애니메이션

  1. Tween의 지속 시간 동안 만 (TakeWhile)
  2. 경과 시간을 스트림으로 흘려 보내고 (Select)
  3. 경과 시간에서 현재 위치를 계산 (Select)
  4. 계산 한 현재 위치를 반영하기 (Subscribe)
void  Start ()
{
    var  start  =  Time.time ;                // 시작 시간
    var  initial  =  transform.position ;     // 시작 위치
    var  delta  =  new  Vector3 ( 5f,  0f,  0f );  // 이동 거리
    var  duration  =  3f ;                     // Tween 기간

    gameObject . UpdateAsObservable ()
        . Select ( _  =>  Time.time  -  start )  // 경과 시간을 흘린다
        . TakeWhile ( time  =>  time  <=  duration )  // 지속 시간 동안 만
        . Select ( time => Tween.EaseInOutExpo ( time, initial, delta, duration ))  // 경과 시간에서 현재 위치를 계산
        . Subscribe ( pos  =>  transform . position  =  pos );  // 계산 한 값을 반영
}

Tween.EaseInOutExpo 가 반환하는 것은 tween 애니메이션으로 변한 위치, Vector3 타입입니다. 이를 Select해서 Subscribe하고 있으므로 Subscribe의 OnNext 이벤트의 인자 pos는 tween 애니메이션으로 변경된 Vector3 값이고 이를 transform.position에 할당하여 최종적인 위치 변환을 계산합니다.

Repeat 연산자를 이용한 반복

void  Start ()
{
    var  start  =  Time.time;                // 시작 시간
    var  initial  =  transform . position ;     // 시작 위치
    var  delta  =  new  Vector3 ( 5f,  0f,  0f );  // 이동 거리
    var  duration  =  3f;                    / / Tween 기간

    gameObject . UpdateAsObservable ()
        . Select ( _  =>  Time.time  -  start )  // 경과 시간을 흘린다
        . TakeWhile ( time  =>  time  <=  duration )  // 지속 시간 동안 만
        . Select ( time  =>  Tween.EaseInOutExpo ( time ,  initial ,  delta ,  duration ))  // 경과 시간에서 현재 위치를 계산
        . Repeat ()  // 반복 처리르 위해서 Repeat 연산자를 삽입!
        . Subscribe ( pos  =>  transform.position  =  pos );  // 계산 한 값을 반영
}

Repeat 연산자를 삽입하는 것만으로는 의도한대로 tween 애니메이션의 반복 처리가 이루어지지 않습니다.

어느 부분이 문제일까요?

애니메이션의 첫 번째 처리후 Repeat연산자를 통해 두 번째 처리시에는 이미 시간이 duration 값을 초과했기 때문에 TakeWhile에서 스트림으로 값을 흘려 보내지 않기 때문입니다.

반복할 때마다 스트림에 흘려 보낼 시작 시간을 초기화하면 문제를 해결할 수 있습니다. 다음은 수정한 코드입니다.

void  Start ()
{
    var  initial  =  transform . position ;     // 시작 위치
    var  delta  =  new  Vector3 ( 5f ,  0f ,  0f );  // 이동 거리
    var  duration  =  3f ;                    // Tween 기간

    Observable.Empty < float > () StartWith (()  =>  Time.time )  // 반복마다 시작 시간을 다시 얻을
        . SelectMany ( start  =>
            gameObject.UpdateAsObservable ()
                .Select ( _  =>  Time.time - start ))  // 경과 시간을 흘린다
        . TakeWhile ( time  =>  time  <=  duration )  // 지속 시간 동안 만
        . Select ( time  =>  Tween.EaseInOutExpo ( time,  initial,  delta,  duration ))  // 경과 시간에서 현재 위치를 계산
        . Repeat ()  // 반복
        . Subscribe ( pos  =>  transform.position = pos );  // 계산 한 값을 반영
}

이제 의도한대로 반복해서 tween 애니메이션을 처리합니다.

Empaty, StartWith

SelectMany: IObservable 호출이 가능?

확장 메소드로 작성

public  static  class  Tween
{
    /// <param name = "time"> 경과 시간 </ param>
    /// <param name = "initial"> 기본값 </ param>
    /// <param name = "delta"> 종료 값과 초기 값의 차이 </ param>
    /// <param name = "duration"> 수명 </ param>
    public  static  Vector3  EaseInOutExpo ( float  time ,  Vector3  initial ,  Vector3  delta ,  float  duration )
    {
        / * 생략 * /
    }

    /// <param name = "initial"> 기본값 </ param>
    /// <param name = "delta"> 종료 값과 초기 값의 차이 </ param>
    /// <param name = "duration"> 지속 시간 </ param>
    public  static  IObservable < Vector3 >  EaseInOutExpo < T > ( this  IObservable < T >  observable ,  
                                                        Vector3  initial ,  Vector3  delta ,  float  duration )
    {
        return  observable . Empty < float > () StartWith (()  =>  time . time )
            . SelectMany ( start  =>  observable . Select ( _  =>  time . time  -  start ))
            . TakeWhile ( time  =>  time  <=  duration )
            . Select ( time  =>  EaseInOutExpo ( time ,  initial ,  delta ,  duration ));
    }

    /// <param name = "dest"> 대상 </ param>
    /// <param name = "duration"> 기간 </ param>
    public  static  IObservable < Vector3 >  MoveTo < T > ( this  IObservable < T >  observable ,  
                                                 gameObject  gameObject ,  Vector3  dest ,  float  duration ,  EaseKind  kind )
    {
        return  observable . Empty < Vector3 > () StartWith (()  =>  gameObject . transform . position )
            . SelectMany ( src  =>  observable .. Ease ( src ,  dest  -  src ,  duration ,  kind ))
            . Do ( x  =>  gameObject . transform . position  =  x );
    }
}

실제 사용예는 아래에 나와 있습니다.

void  Start ()
{
    gameObject.UpdateAsObservable ()
        .EaseInOutExpo ( transform.position ,  new  Vector3 ( 5f,  0f,  0f ),  3f )
        .Repeat ()
        .Subscribe ( pos  =>  transform.position  =  pos );
}
void  Start ()
{
    gameObject.UpdateAsObservable ()
        .MoveTo ( gameObject, new Vector3 ( 5f, 0f, 0f ), 3f, Tween.EaseKind.EaseInCubic )
        .Subscribe (
            x  =>  Debug.Log ( "애니메이션 작업 중" ),  
            () =>  Debug . Log ( "애니메이션 종료!" ));
}

원문: http://qiita.com/idaisuke/items/e9a89f1b9ad24ff39805

results matching ""

    No results matching ""