iTween은 상당히 강력한 기능을 가지고 있으나, 아래와 같은 문제가 있다.
{
fadeScreen.SetActive (true);
Hashtable tweenParams = new Hashtable();
tweenParams.Add ("from", 1.0f);
tweenParams.Add ("to", 0.0f);
tweenParams.Add ("time", 2.0f);
tweenParams.Add ("onupdate", "OnOpacityUpdated");
iTween.ValueTo(gameObject, tweenParams);
}
{
Color color = fadeScreen.GetComponent<Image>().color;
color.a = opacity;
fadeScreen.GetComponent<Image>().color = color;
if (opacity < 0.01f) {
fadeScreen.SetActive (false);
}
}
private void FadeIn()
{
fadeScreen.SetActive(true);
FFani.Tween (
target: fadeScreen.GetComponent<Image>(),
propertyName: "color.a",
from: 1.0f,
to: 0.0f,
duration: 2.0f
).Start();
}
fadeScreen의 Image.color.a에 접근 가능하기 때문에 OnOpacityUpdated 메서드는 이제 필요가 없어졌다. UI를 애니메이션 시킬때마다 콜백을 하나씩 만들어야 했던 고통에서 벗어나게 되었다.
2. 콜백
애니메이션이 종료한 후에 바로 다른 코드를 실행시키기 위해서는 콜백을 설정해 주어야 한다. 예를 들어 현재 개발중인 SRPG의 경우 시작시 Fade-in과 함께 카메라 애니메이션이 시작되며 이것이 종료되면 엔트리 픽 모드로 들어간다. 엔트리 픽 모드로 들어 갈 때 약간의 카메라 줌인이 필요하며 이를 위해 아래와 같이 구현했었다.
public void onEntryPickMode()
{
// 초기 실행 부분 생략
Hashtable tweenParams = new Hashtable();
tweenParams.Add ("time", 3.0f);
tweenParams.Add ("p-osition", originPosition);
// 애니메이션 종료시 실행할 콜백을 여기서 설정한다.
tweenParams.Add ("oncomplete", "onActivateEntryPick");
tweenParams.Add ("oncompletetarget", gameObject);
iTween.MoveFrom(GameObject.Find ("CamPivot"), tweenParams);
}
public void onActivateEntryPick()
{
// 실제 중요한 코드는 여기 다 있다.
}
단지 3초간의 카메라 애니메이션을 위해서 코드가 두 개로 분리되어있다. onActivateEntryPick은 다른 곳에서는 호출되지 않으므로 굳이 나눠줄 필요가 없지만 iTween에서 반드시 콜백 함수의 이름을 넘겨 주어야 콜백이 되기 때문에 애니메이션 종료 후 실행할 코드를 별도의 메서드로 만들어야 했다.
위의 코드를 아래와 같이 수정한다.
public void onEntryPickMode()
{
// 초기 실행 부분 생략
FFani.Tween(
target: GameObject.Find ("CamPivot").transform,
propertyName: "p-osition",
from: originp-osition,
duration: 3.0f,
easingCurve: FFaniEasing.OutCubic
).Remind (
() => {
// onActivateEntryPick()의 코드를 여기로 옮김.
}
).Start();
}
콜백 함수를 람다로 전달받으므로 따로 메서드를 만들 필요 없이 애니메이션 종료 후 실행할 코드를 Remind()내부에 넣으면 된다. (주의!! 현재 애니메이션 실행시 화면이 깜빡이는 버그가 있다.)
3. 가독성
iTween를 사용할 때의 문제점은, 코드를 위에서 아래로 읽으면 파라미터를 먼저 읽고 제일 중요한 정보인MoveTo인지 MoveFrom인지, 대상 오브젝트가 무엇인지를 나중에 알게 된다는데에 있다.
Hashtable tweenParams = new Hashtable();
tweenParams.Add ("from", 1.0f);
tweenParams.Add ("to", 0.0f);
tweenParams.Add ("time", 2.0f);
// 제일 중요한 정보를 제일 나중에 알 수 있다.
iTween.ValueTo(gameObject, tweenParams);
위와 똑같은 동작을 아래와 같이 표기한다.
FFani.Tween (
target: fadeScreen.GetComponent<Image>(),
propertyName: "color.a",
from: 1.0f,
to: 0.0f,
duration: 2.0f
).Start();
대상 컴퍼넌트와 프로퍼티 이름을 맨 위에서 명확히 하였고 그 후에 from, to, duration 정보가 온다. 파라미터의 순서 자체는 코드의 동작에 전혀 문제가 없지만, 위와 같은 순서로 할 경우 가독성이 확실히 좋아진다. 또한 Hashtable API를 귀찮게 이용하지 않아도 되기 때문에 작성하기에도 훨씬 편하다.
4. 순서대로 애니메이션 플레이
iTween에서는 두 개의 애니메이션을 순서대로 플레이 시키기 위해서 delay를 이용해 애니메이션을 시간차를 두고 플레이시키는 편법을 사용하였다.
아래 코드는 장면전환시 사용하도록 fade-out -> fade-in을 순차적으로 실행하는 애니메이션이다. 이경우 fade-in은 fade-out의 플레이시간에 맞춰 delay를 주어서 애니메이션의 순서를 맞추었다.
// fade-out
Hashtable tweenParams = new Hashtable();
tweenParams.Add ("from", 0.0f);
tweenParams.Add ("to", 1.0f);
tweenParams.Add ("time", 2.0f);
iTween.ValueTo(gameObject, tweenParams);
// fade-in
Hashtable tweenParams2 = new Hashtable();
tweenParams2.Add ("from", 1.0f);
tweenParams2.Add ("to", 0.0f);
tweenParams2.Add ("time", 2.0f);
tweenParams2.Add ("delay", 2.0f);
iTween.ValueTo(gameObject, tweenParams2);
이 경우에는 뒤에 연결되는 애니메이션이 하나밖에 없으므로 별 문제가 없으나 여러 개가 연결된 애니메이션을 delay로만 연결시킬 경우, 앞의 애니메이션의 플레이 시간이 변경되면 뒤따라 오는 애니메이션의 delay 파라미터를 전부 재계산해 주어야 하는 문제점이 있다.
위의 코드를 아래와 같이 고칠 수 있다.
FFani.Serial (
FFani.Tween ( // fade-out
target: fadeScreen.GetComponent<Image>(),
propertyName: "color.a",
from: 1.0f,
to: 0.0f,
duration: 2.0f
),
FFani.Tween ( // fade-in
target: fadeScreen.GetComponent<Image>(),
propertyName: "color.a",
from: 0.0f,
to: 1.0f,
duration: 2.0f
)
).Start ();
두 개의 FFani.Tween을 FFani.Serial로 묶어줌으로써 순서대로 애니메이션을 실행 시킬 수 있다. 뒤의 애니메이션은 앞의 애니메이션이 끝나면 자동으로 실행된다. 앞의 애니메이션의 플레이시간 (duration)이 바껴도 뒤의 애니메이션이 이를 의식할 필요가 없다.
현재 내가 직접 작성한 코드는 iTween을 없애고 FFani.Tween으로 대체할 계획이다. 아직은 지원되지 않는 기능도 있고 테스트도 충분히 하지 못했기 때문에 당분간은 FFani.Tween의 완성도를 높이는 데에 주력해야 될 듯 싶다.