트윈

Time 서비스의 클럭으로 구동되는, 31개 이징 커브를 갖춘 일시정지 인식 값 애니메이션.

어떤 값이든 시간에 걸쳐 A에서 B로 애니메이션하세요 — 일시정지 메뉴 대응은 공짜입니다.

인터페이스ITweenService
끄기 스위치NullTweenService
어셈블리CommonGameSystem.Core
시작부팅 시 자동 등록 — 별도 설정 불필요

주요 기능

트윈은 시작 값과 끝 값 사이를 정해진 시간 동안 부드럽게 보간하는 것입니다. Tween 서비스가 매 틱 그 값을 계산하고, 이징 커브가 가속의 형태를 결정합니다(느리게 시작해 빨라지기, 오버슈트 후 안착, 바운스 등). 각 값을 오브젝트에 적용하는 콜백은 여러분이 제공합니다 — 프레임워크는 계산만 합니다. 리플렉션도, 자동 프로퍼티 쓰기도, 외부 의존성도 없습니다.

모든 트윈은 Time 서비스의 클럭 — Gameplay, UI, Background — 중 하나에 바인딩되므로, 추가 코드 없이 애니메이션이 일시정지 메뉴와 슬로 모션을 존중합니다. Gameplay 클럭 위의 문 열림 트윈은 플레이어가 일시정지하면 멈추고, UI 클럭 위의 메뉴 페이드는 계속 돕니다.

빠른 시작

AwakeStart에서 서비스를 한 번 리졸브하고 캐시하세요:

using CommonGameSystem.Core;
using UnityEngine;

[DefaultExecutionOrder(100)] // Run after the framework has started.
public class MyMovingThing : MonoBehaviour
{
    private ITweenService _tween;

    private void Awake()
    {
        _tween = ServiceLocator.Resolve<ITweenService>();
    }

    private void StartMove()
    {
        _tween.To(
            from: transform.position,
            to: new Vector3(5f, 0f, 0f),
            durationSeconds: 0.5f,
            onUpdate: pos => transform.position = pos,
            ease: EaseType.OutQuad
        );
    }
}

API 레퍼런스

타입별 To / From 오버로드 표면

서비스는 정확히 여섯 가지 값 타입을 지원하며, 각 타입마다 전용 타입 오버로드 세트가 있습니다:

float · Vector2 · Vector3 · Vector4 · Color · Quaternion

각 타입에는 네 개의 오버로드 — To 또는 From, 기본 클럭 또는 명시적 Clock 파라미터 — 가 있어 총 24개 메서드입니다. 모두 같은 형태를 공유합니다(여기서는 float 기준이며, 여섯 타입 중 무엇이든 대입하면 됩니다):

IDisposable To(float from, float to, float durationSeconds,
               Action<float> onUpdate,
               EaseType? ease = null,
               Func<float, float> customEase = null,
               Action onComplete = null);

IDisposable To(float from, float to, float durationSeconds, Clock clock,
               Action<float> onUpdate,
               EaseType? ease = null,
               Func<float, float> customEase = null,
               Action onComplete = null);

IDisposable From(float from, float current, float durationSeconds,
                 Action<float> onUpdate,
                 EaseType? ease = null,
                 Func<float, float> customEase = null,
                 Action onComplete = null);

IDisposable From(float from, float current, float durationSeconds, Clock clock,
                 Action<float> onUpdate,
                 EaseType? ease = null,
                 Func<float, float> customEase = null,
                 Action onComplete = null);

From(a, current, ...)To(a, current, ...)와 같은 연산입니다 — 어떤 값에서 현재 값으로 되돌아가게 애니메이션할 때(예: 펀치 인 스케일 효과) 이름이 더 잘 읽힐 뿐입니다.

의도적으로 제네릭 To<T> 메서드는 없습니다: 고정된 타입 오버로드 세트는 Unity의 AOT(IL2CPP) 컴파일에서 확실히 동작하는 반면, 값 타입에 대한 열린 제네릭 메서드는 런타임에 실패할 수 있습니다. Quaternion 트윈은 Quaternion.Slerp를 사용해 최단 호를 따라 회전합니다.

ToFrom 모두 IDisposable 토큰을 반환합니다 — 트윈을 조기에 취소하려면 dispose하세요.

파라미터

  • from, to(또는 current) — 시작 값과 끝 값.
  • durationSeconds — 애니메이션 길이(초). 0이면 즉시 to 값으로 완료됩니다.
  • clock — 선택. Clock.Gameplay(기본값)는 게임과 함께 일시정지되고, Clock.UI는 일시정지 메뉴 중에도 계속 돌며, Clock.Background는 항상 돕니다.
  • onUpdate — 매 틱 보간된 값과 함께 호출됩니다. 오브젝트에 적용하는 것은 여러분의 몫입니다.
  • ease — 선택적 EaseType. 생략하면 기본 이징을 쓰고, 원하면 아래 31개 커브 중 하나를 고르세요.
  • customEase — 진행도(0~1)를 이징된 진행도로 매핑하는 선택적 Func<float, float>. 제공하면 ease를 덮어씁니다.
  • onComplete — 트윈이 100%에 도달할 때 한 번 발화합니다. 취소 시에는 발화하지 않습니다.

31개 이징 커브

EaseType은 Linear에 더해 열 개의 커브 패밀리를 각각 세 방향 — In(가속하며 진입), Out(감속하며 이탈), InOut(둘 다) — 으로 제공합니다:

패밀리멤버특성
LinearLinear일정한 속도, 이징 없음
QuadInQuad · OutQuad · InOutQuad완만한 가속(제곱)
CubicInCubic · OutCubic · InOutCubic중간 가속(세제곱)
QuartInQuart · OutQuart · InOutQuart강한 가속
QuintInQuint · OutQuint · InOutQuint매우 강한 가속
SineInSine · OutSine · InOutSine부드러운 파동 기반 이징
ExpoInExpo · OutExpo · InOutExpo극적인 지수 램프
CircInCirc · OutCirc · InOutCirc원호, 한쪽 끝이 급격함
BackInBack · OutBack · InOutBack목표를 지나쳤다가 안착
ElasticInElastic · OutElastic · InOutElastic목표를 스프링처럼 지나쳐 진동
BounceInBounce · OutBounce · InOutBounce경계에서 공처럼 튕김

경험칙: 대부분의 UI 모션에는 OutQuadOutCubic, 경쾌한 팝인에는 OutBack, 카툰풍 강조에는 OutBounce와 Elastic 계열을 쓰세요.

진단

  • int ActiveCount { get; } — 살아 있는 트윈의 개수. 진단 전용이며 메인 스레드 전용입니다.

예제

패널을 0.3초 동안 페이드 인하고 완료 시 로그 남기기:

CanvasGroup panel = GetComponent<CanvasGroup>();

_tween.To(
    from: 0f,
    to: 1f,
    durationSeconds: 0.3f,
    clock: Clock.UI, // UI clock: keep running while the pause menu is open.
    onUpdate: alpha => panel.alpha = alpha,
    ease: EaseType.OutCubic,
    onComplete: () => Debug.Log("Fade complete!")
);

토큰을 dispose해 트윈 취소하기:

float score = 0f;
IDisposable token = _tween.To(0f, 100f, 1f, v => score = v);
// ... later, if you want to stop early:
token.Dispose();  // The tween cancels; onComplete does NOT fire.

끄기

ServiceLocator.Replace<ITweenService>(new NullTweenService());

모든 트윈이 무음이 됩니다. ToFrom은 공유되는 빈 토큰을 반환하고, 어떤 콜백도 발화하지 않으며, 티커 오브젝트도 생성되지 않습니다. 에디터 툴링이나 자동화 테스트에서 애니메이션을 끌 때 유용합니다. no-op 버전도 입력은 계속 검증합니다: null onUpdate, 유효하지 않은 Clock이나 EaseType, NaN·무한대·음수 durationSeconds는 여전히 예외를 던집니다 — 애니메이션을 꺼도 버그는 계속 보입니다.

흔한 함정

  • 올바른 클럭을 고르세요. 잘못된 클럭을 골라도 오류는 아니지만 애니메이션의 느낌이 어긋납니다 — 예를 들어 Clock.UI에 바인딩된 게임플레이 애니메이션은 일시정지 중에도 계속 움직입니다. 각 클럭이 일시정지와 슬로 모션에서 어떻게 동작하는지는 Time 서비스를 보세요.
  • Quaternion 이징은 오버슈트를 클램프합니다. 오버슈트 커브(InBack, OutBack, InElastic, OutElastic)는 눈에 보이는 회전 오버슈트를 만들지 않습니다. Quaternion.Slerp가 계수를 0–1 범위로 클램프하기 때문입니다. 오버슈트 구간 동안 회전은 끝 값에 머뭅니다.
  • 취소는 완료와 다릅니다. 트윈 토큰을 dispose하면 조용히 취소됩니다 — onComplete는 발화하지 않습니다. 지속 시간의 끝에 도달했을 때는 onComplete가 발화합니다.
  • 델리게이트 할당은 여러분의 비용입니다. 서비스는 내부 트윈 슬롯을 풀링하지만, onUpdate 람다(그리고 customEase 클로저)는 호출마다 여러분이 할당합니다. 할당이 없어야 하는 타이트한 루프에서는 델리게이트를 필드에 한 번 저장해 두고, 새 람다를 쓰는 대신 매 호출에 그 필드를 넘기세요.
  • 완료된 뒤의 토큰을 붙들지 마세요. 끝난 트윈의 슬롯은 이후의 트윈에 재사용됩니다. 오래된 토큰은 감지되어 무시되므로 늦게 dispose해도 해롭지는 않지만, 붙들고 있다는 것 자체가 수명 버그의 징후입니다. 발사 후 잊는(fire-and-forget) 트윈은 토큰을 저장할 필요가 아예 없습니다.

관련 페이지

  • Time — 트윈이 바인딩되는 클럭
  • Tween Sequencing — 순차·병렬 트윈 타임라인
  • Scheduler — 같은 클럭 위의 지연과 반복 타이머