Article · 03

خروجی Spine برای Unity: اتلس، Draw Call و کنترل انیمیشن با کد

انیمیشن عالی در Spine کافی نیست؛ باید با کمترین Draw Call و تنظیمات درست در Unity اجرا شود. این مقاله مسیر خروجی تا کنترل با کد را پوشش می‌دهد.

داود جنگجو مهر ۱۴۰۵ مطالعه: ۱۰ دقیقه متوسط تا پیشرفته
SpineUnityC#Optimization

هماهنگی نسخه‌ها

ران‌تایم Spine باید با نسخه‌ی ادیتوری که خروجی گرفته هم‌خوان باشد. مثلاً خروجی نسخه‌ی ۴٫۲ را باید با ران‌تایم ۴٫۲ در Unity باز کرد. قبل از شروع پروژه نسخه را قفل کن و در تیم یادداشت کن؛ ارتقای نسخه‌ی Spine وسط پروژه می‌تواند همه‌ی خروجی‌ها را دوباره بخواهد.

خروجی و اتلس

  • فرمت داده: JSON خوانا و راحت برای دیباگ است؛ فرمت باینری (.skel) معمولاً کوچک‌تر و سریع‌تر لود می‌شود و برای نسخه‌ی نهایی مناسب‌تر است.
  • اتلس: در Texture Packer خود Spine همه‌ی تصاویر را در چند صفحه‌ی بزرگ جمع کن. کاراکترهای هم‌خانواده را در یک اتلس بگذار.
  • Premultiply Alpha: بسته به شیدری که در Unity استفاده می‌کنی فعال یا غیرفعال می‌شود و باید با تنظیمات ران‌تایم یکی باشد؛ نتیجه‌ی ناهماهنگی لبه‌های تیره یا روشن اطراف تصویر است.
  • اندازه‌ی صفحه: برای موبایل ۲۰۴۸×۲۰۴۸ انتخاب امنی است؛ صفحه‌ی بزرگ‌تر روی گوشی‌های ضعیف مشکل‌ساز می‌شود.
  • Padding: کمی فاصله بین تصاویر بگذار تا در Mip و فیلتر، لبه‌ها به هم نشت نکنند.
تجربه‌ی عملی

بعد از هر خروجی اول بازی را روی خود گوشی تست کن، نه فقط داخل ادیتور Unity. تفاوت رنگ لبه و Blend در دستگاه واقعی بهتر دیده می‌شود.

کامپوننت‌های Unity

کامپوننتکاربرد
SkeletonAnimationکاراکتر و آبجکت‌های داخل صحنه با Mesh Renderer
SkeletonGraphicانیمیشن داخل UI (Canvas)؛ مناسب منو، دکمه و پاپ‌آپ
SkeletonMecanimاگر می‌خواهی با Animator Controller کار کنی

برای هر اسکلت دو دارایی ساخته می‌شود: SkeletonDataAsset (داده‌ی استخوان و انیمیشن) و Atlas Asset (تصاویر). Spine داخل Unity این‌ها را موقع Import خودکار می‌سازد.

کاهش Draw Call

هر مجموعه از اسلات‌هایی که اتلس و متریال یکسان دارند و پشت سر هم کشیده می‌شوند می‌توانند در یک Draw Call رندر شوند. هر چیزی که این زنجیره را بشکند هزینه‌ی اضافه دارد:

  • چند صفحه‌ی اتلس در یک کاراکتر: هر بار تغییر صفحه یک Batch جدید است. اجزایی را که کنار هم کشیده می‌شوند در یک صفحه بچین.
  • حالت‌های ترکیب (Additive، Multiply، Screen): هرکدام متریال جداگانه می‌خواهد و باعث شکستن Batch می‌شود. فقط وقتی لازم است استفاده کن.
  • Clipping Attachment و Tint Black هزینه‌ی اضافه دارند؛ برای کاراکترهای پرتعداد حساب‌شده استفاده کن.
  • ترتیب Draw Order را طوری مرتب کن که اسلات‌های هم‌متریال کنار هم باشند.

کنترل با C#

سیستم انیمیشن Spine چند Track دارد؛ مثلاً Track صفر برای حرکت پایه (Idle/Run) و Track یک برای حرکات رویی مثل شلیک. نمونه‌ی ساده:

using UnityEngine;
using Spine;
using Spine.Unity;

public class HeroAnimator : MonoBehaviour
{
    [SpineAnimation] public string idle   = "idle";
    [SpineAnimation] public string run    = "run";
    [SpineAnimation] public string attack = "attack";

    SkeletonAnimation skeleton;

    void Awake()
    {
        skeleton = GetComponent<SkeletonAnimation>();
        // زمان انتقال نرم پیش‌فرض بین انیمیشن‌ها
        skeleton.AnimationState.Data.DefaultMix = 0.15f;
        skeleton.AnimationState.Event += OnSpineEvent;
    }

    public void SetMoving(bool moving)
    {
        // اگر همین انیمیشن در حال پخش است دوباره شروعش نکن
        var current = skeleton.AnimationState.GetCurrent(0);
        string target = moving ? run : idle;
        if (current != null && current.Animation.Name == target) return;

        skeleton.AnimationState.SetAnimation(0, target, true);
    }

    public void Attack()
    {
        // روی Track 1 پخش شود تا حرکت پایه قطع نشود
        skeleton.AnimationState.SetAnimation(1, attack, false);
        skeleton.AnimationState.AddEmptyAnimation(1, 0.1f, 0f);
    }

    void OnSpineEvent(TrackEntry entry, Spine.Event e)
    {
        // در Spine یک Event به نام "hit" روی فریم ضربه بگذار
        if (e.Data.Name == "hit")
            Debug.Log("Damage frame!");
    }

    void OnDestroy()
    {
        if (skeleton != null)
            skeleton.AnimationState.Event -= OnSpineEvent;
    }
}
  • رویدادهای گیم‌پلی (لحظه‌ی ضربه، صدای قدم) را با Event داخل Spine بگذار، نه با زمان‌بندی دستی در کد؛ با تغییر انیمیشن، تایمینگ خودکار درست می‌ماند.
  • میکس بین انیمیشن‌ها با DefaultMix یا SetMix(from, to, duration) تنظیم می‌شود.
  • مقدار انیمیشن‌ها را با [SpineAnimation] از لیست انتخاب کن تا غلط املایی در رشته‌ها پیش نیاید.

چک‌لیست عملکرد

  • همه‌ی کاراکترهای یک صحنه، تا حد ممکن از یک اتلس و یک متریال استفاده می‌کنند.
  • تعداد ورتکس و استخوان کاراکترهای پرتعداد (دشمن‌ها، جمعیت) محدود و تست‌شده است.
  • آبجکت‌های خارج از دید، آپدیت غیرضروری ندارند؛ تنظیمات به‌روزرسانی در حالت نامرئی را روی کامپوننت‌ها بررسی کن.
  • اتلس‌ها و فایل‌ها قبل از ساخت نهایی با Profiler روی دستگاه هدف سنجیده شده‌اند.
  • در UI فقط جایی از SkeletonGraphic استفاده شده که لازم است، چون هزینه‌ی Canvas بازسازی می‌شود.

با رعایت همین چند اصل، انیمیشن‌های Spine حتی روی گوشی‌های ضعیف هم روان اجرا می‌شوند.

پروژه‌ای با Spine یا Unity داری؟

برای انیمیشن کاراکتر، UI و پیاده‌سازی در Unity می‌توانیم با هم صحبت کنیم.

Telegram View Portfolio