چرا Spine برای UI؟
انیمیشنهای سادهی UI با Tween هم ممکن است، ولی وقتی چند بخش همزمان با همپوشانی حرکت میکنند (نور، ستاره، نوشته، دکمه)، یک اسکلت با تایملاین مشخص کنترل بهتری میدهد و طراح میتواند بدون دست زدن به کد آن را تغییر بدهد.
- همهی بخشهای یک پاپآپ در یک فایل و یک تایملاین مدیریت میشوند.
- انیمیشن بدون تغییر کد قابل جایگزینی است.
- برای اندازههای مختلف صفحه، مقیاسپذیر است.
برای انیمیشنهای خیلی ساده (محو شدن، جابهجایی ساده) Animator یا Tween کافی و سبکتر است. Spine را برای جاهایی نگه دار که واقعاً ارزش دارد.
طراحی انیمیشنهای UI
برای هر عنصر چند انیمیشن با نام ثابت بساز تا کد و طراح همزبان باشند:
| نام | لوپ؟ | کاربرد |
|---|---|---|
open | خیر | ظاهر شدن پاپآپ با پرش یا مقیاس نرم |
idle | بله | حالت انتظار؛ تابش نور، حرکت ملایم ستارهها |
press | خیر | بازخورد لمس دکمه |
close | خیر | بسته شدن |
- زمان ورود را کوتاه نگه دار (حدود نیمثانیه)؛ بازیکن نباید منتظر انیمیشن بماند.
- انیمیشن بستهشدن معمولاً از ورود کوتاهتر است.
- برای حس پرانرژی از Overshoot استفاده کن: کمی از اندازهی نهایی بزرگتر شو و برگرد.
- برای اتفاقهایی که کد باید بداند (مثلاً «لحظهی دریافت جایزه») Event بگذار.
SkeletonGraphic
در Unity برای عناصر داخل Canvas از SkeletonGraphic استفاده میشود، نه SkeletonAnimation. این کامپوننت مثل سایر عناصر UI در سلسلهمراتب Canvas رندر میشود و با ترتیب Hierarchy و Sorting همخوان است.
- برای عناصر UI از اتلس جداگانهی UI استفاده کن تا با اتلس کاراکترها قاطی نشود.
- گزینهی Unscaled Time را برای پاپآپهایی که هنگام توقف بازی (
Time.timeScale = 0) باز میشوند فعال کن، وگرنه انیمیشن آنها هم متوقف میشود. - اگر دکمه باید لمس را دریافت کند، تنظیم Raycast Target و ناحیهی لمس را بررسی کن؛ گاهی بهتر است یک Image شفاف جدا برای دریافت لمس بگذاری.
کنترل پاپآپ با کد
نمونهای که پاپآپ را باز میکند، بعد روی idle نگه میدارد و هنگام بستن بعد از پایان انیمیشن، آبجکت را غیرفعال میکند:
using UnityEngine;
using Spine;
using Spine.Unity;
public class PopupAnimator : MonoBehaviour
{
[SerializeField] SkeletonGraphic graphic;
[SpineAnimation(dataField = "graphic")] public string openAnim = "open";
[SpineAnimation(dataField = "graphic")] public string idleAnim = "idle";
[SpineAnimation(dataField = "graphic")] public string closeAnim = "close";
public void Show()
{
gameObject.SetActive(true);
graphic.AnimationState.SetAnimation(0, openAnim, false);
graphic.AnimationState.AddAnimation(0, idleAnim, true, 0f);
}
public void Hide()
{
TrackEntry entry = graphic.AnimationState.SetAnimation(0, closeAnim, false);
entry.Complete += _ => gameObject.SetActive(false);
}
}
AddAnimationبا تأخیر صفر، انیمیشن بعدی را بلافاصله بعد از پایان فعلی صف میکند.- برای اتفاقهایی مثل «شمارش جایزه شروع شود» از Event استفاده کن و در کد به آن گوش بده، نه با عدد ثابت زمان.
مشکلات رایج
- بازسازی Canvas: هر تغییر در یک Canvas ممکن است کل آن را دوباره بسازد. عناصر متحرک را روی یک Canvas جدا بگذار تا بقیهی UI تحت تأثیر نباشد.
- Layout Group: قرار دادن SkeletonGraphic داخل Layout Group ممکن است اندازه و جای آن را تغییر بدهد؛ از اندازهی ثابت یا ریشهی جدا استفاده کن.
- Pivot و اندازه: نقطهی چرخش و ابعاد اسکلت را از ابتدا با طرح UI هماهنگ کن، وگرنه جای آن در رزولوشنهای مختلف میپرد.
- رنگ و شفافیت: اگر از CanvasGroup برای محو کردن استفاده میکنی، نتیجه را روی دستگاه واقعی تست کن؛ ممکن است با تنظیمات Premultiply Alpha همخوان نباشد.
- تعداد زیاد: دهها SkeletonGraphic همزمان (مثلاً لیست آیتمها) را فقط وقتی لازم است متحرک کن و بقیه را متوقف.
چکلیست
- نام انیمیشنها بین طراح و برنامهنویس یکسان و مستند است.
- برای پاپآپهای هنگام Pause، Unscaled Time فعال است.
- اتلس UI از اتلس کاراکترها جدا و حجمش کنترلشده است.
- روی چند رزولوشن و نسبت تصویر مختلف تست شده است.
پروژهای با Spine یا Unity داری؟
برای انیمیشن کاراکتر، UI و پیادهسازی در Unity میتوانیم با هم صحبت کنیم.
Telegram View Portfolio