پشتیبانی از قابلیت تغییر اندازه صفحه‌نمایش بزرگ

گسترش دادن بازی از تلفن به انواع مختلف ضرایب شکل صفحه‌نمایش بزرگ ملاحظاتی را درباره نحوه مدیریت پنجره توسط بازی شما ایجاد می‌کند. در Googlebook،‏ ChromeOS، و بازی‌های Google Play در رایانه، بازی شما می‌تواند در حالت پنجره‌ای روی میانای اصلی میزکار اجرا شود. در رایانه‌های لوحی Android و دستگاه‌های تاشو جدید که Android 12L (سطح API‏ ۳۲) یا بالاتر را با عرض صفحه‌نمایش > ۶۰۰ پیکسل مستقل از تراکم اجرا می‌کنند، بازی شما می‌تواند در حالت صفحهٔ دونیمه در کنار برنامه‌های دیگر اجرا شود، اندازه آن تغییر کند، و حتی بین نمایشگر داخلی و خارجی در دستگاه‌های تاشو جابه‌جا شود که منجر به تغییر پیکربندی برای اندازه پنجره و در برخی دستگاه‌ها، جهت می‌شود.

تغییرپذیری اندازه در بازی‌های Unity

پیکربندی پایه صفحه‌نمایش بزرگ

اعلام کنید که آیا بازی‌تان می‌تواند قابلیت تغییر اندازه را مدیریت کند یا نه:

<android:resizeableActivity="true" or "false" />

اگر امکان پشتیبانی از قابلیت تغییر اندازه وجود ندارد، مطمئن شوید که مانیفست بازی به‌طور صریح حداقل و حداکثر نسبت‌های ابعادی پشتیبانی‌شده را تعریف می‌کند:

<!-- Render full screen between 3:2 and 21:9 aspect ratio -->
<!-- Let the platform letterbox otherwise -->
<activity android:minAspectRatio="1.5">
<activity android:maxAspectRatio="2.33">

بازی‌های Google Play در رایانه

برای «بازی‌های Google Play در رایانه»، پلاتفرم قابلیت تغییر اندازه پنجره را با رعایت نسبت ابعادی مشخص‌شده مدیریت می‌کند. اندازه پنجره به‌طور خودکار روی ابعاد بهینه قفل می‌شود. اگر جهت اصلی شما افقی است، باید حداقل از نسبت ابعادی ۱۶:۹ پشتیبانی کنید و اگر بازی شما در حالت عمودی است، باید از نسبت ابعادی ۹:۱۶ پشتیبانی کنید. برای داشتن بهترین تجربه، به‌طور صریح از نسبت‌های ابعادی ۲۱:۹،‏ ۱۶:۱۰، و ۳:۲ برای بازی افقی پشتیبانی کنید. در اینجا به قابلیت تغییر اندازه پنجره نیازی نیست، اما برای سازگاری با سایر ابعاد دستگاه خوب است که این قابلیت را داشته باشید.

برای اطلاعات بیشتر و روال‌های مطلوب، به پیکربندی گرافیک برای «بازی‌های Google Play در رایانه» مراجعه کنید.

صفحه‌نمایش بزرگ

برای به‌حداکثر رساندن ناحیه قابل‌مشاهده برای بازی‌تان در حالت تمام‌صفحه در رایانه‌های لوحی با صفحه‌نمایش بزرگ، دستگاه‌های تاشو، Googlebook،‏ ChromeOS، و نمایشگرهای متصل، از حالت فراگیر تمام‌صفحه پشتیبانی کنید و با تنظیم پرچم‌ها در decorView، نمایان بودن واسط کاربر سیستم، یا ازطریق میانای برنامه‌سازی کاربردی WindowInsetsCompat، نوارهای سیستم را پنهان کنید. همچنین باید رویدادهای پیکربندی چرخش و تغییر اندازه را به‌درستی مدیریت کنید یا از وقوع آن‌ها جلوگیری کنید.

توجه داشته باشید که در دستگاه‌های Android با صفحه‌نمایش بزرگ، بازی شما می‌تواند در پیکربندی‌هایی اجرا شود که ممکن است قبلاً آن‌ها را مدیریت نکرده باشید. اگر بازی شما از همه پیکربندی‌های اندازه و جهت پنجره پشتیبانی نمی‌کند، پلاتفرم بازی شما را در حالت سازگاری به‌صورت جعبه‌ای نمایش می‌دهد و درصورت لزوم، قبل‌از تغییر به پیکربندی پشتیبانی‌نشده، از بازیکن درخواست تأیید می‌کند.

شکل ۱. کادر گفتگوی سازگاری پیکربندی.

در برخی‌از دستگاه‌ها، وقتی بازیکن به پیکربندی پشتیبانی‌نشده‌ای می‌رود، ممکن است گزینه‌ای برای بار کردن مجدد بازی و بازآفرینی فعالیت برای تطبیق بهتر با چیدمان پنجره جدید به او نشان داده شود که این امر تجربه بازی را مختل می‌کند. بازی‌تان را در پیکربندی‌های مختلف حالت چندپنجره‌ای (اندازه پنجره ۲/۳، ۱/۲، ۱/۳) آزمایش کنید و مطمئن شوید که هیچ‌کدام از عناصر میانای کاربر یا بازی قطع یا غیرقابل‌دسترس نمی‌شوند. علاوه‌براین، نحوه پاسخ بازی‌تان به تداوم تاشو را هنگام جابه‌جایی بین صفحه‌نمایش داخلی و خارجی در دستگاه‌های تاشو آزمایش کنید. اگر مشکلی دیدید، این رویدادهای پیکربندی را به‌طور صریح مدیریت کنید و پشتیبانی پیشرفته از تغییر اندازه صفحه‌نمایش بزرگ را اضافه کنید.

قابلیت تغییر اندازه پیشرفته صفحه‌نمایش بزرگ

شکل ۲. واسط‌های کاربر متفاوت در رایانه و دستگاه تاشو در حالت رومیزی.

برای خارج شدن از حالت سازگاری و جلوگیری از بازآفرینی فعالیت، کارهای زیر را انجام دهید:

  1. فعالیت اصلی‌تان را به‌عنوان قابل‌تغییر اندازه اعلام کنید:

    <android:resizeableActivity="true" />
    
  2. برای دریافت همه رویدادهای پیکربندی صفحه بزرگ، پشتیبانی صریح از «جهت»، «screenSize»، «smallestScreenSize»، «screenLayout»، و «density» را در مشخصه android:configChanges عنصر <activity> مانیفست بازی‌تان اعلام کنید:

    <android:configChanges="screenSize | smallestScreenSize | screenLayout | orientation | keyboard |
                            keyboardHidden | density" />
    
  3. onConfigurationChanged() را لغو کنید و رویداد پیکربندی را، ازجمله جهت کنونی، اندازه پنجره، عرض، و ارتفاع، مدیریت کنید:

    کاتلین

    override fun onConfigurationChanged(newConfig: Configuration) {
       super.onConfigurationChanged(newConfig)
       val density: Float = resources.displayMetrics.density
       val newScreenWidthPixels =
    (newConfig.screenWidthDp * density).toInt()
       val newScreenHeightPixels =
    (newConfig.screenHeightDp * density).toInt()
    
       // Configuration.ORIENTATION_PORTRAIT or ORIENTATION_LANDSCAPE
       val newScreenOrientation: Int = newConfig.orientation
    
       // ROTATION_0, ROTATION_90, ROTATION_180, or ROTATION_270
       val newScreenRotation: Int =
    windowManager.defaultDisplay.rotation
    }

    جاوا

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
       super.onConfigurationChanged(newConfig);
       float density = getResources().getDisplayMetrics().density;
       int newScreenWidthPixels = (int) (newConfig.screenWidthDp * density);
       int newScreenHeightPixels = (int) (newConfig.screenHeightDp * density);
    
       // Configuration.ORIENTATION_PORTRAIT or ORIENTATION_LANDSCAPE
       int newScreenOrientation = newConfig.orientation;
    
       // ROTATION_0, ROTATION_90, ROTATION_180, or ROTATION_270
       int newScreenRotation = getWindowManager().getDefaultDisplay()
               .getRotation();
    }

همچنین می‌توانید WindowManager را پرسمان کنید تا چرخش فعلی دستگاه را بررسی کنید. بااستفاده از این فراداده، ابعاد پنجره جدید را بررسی کنید و آن را در اندازه کامل پنجره ارائه دهید. این روش ممکن است به‌دلیل تفاوت‌های نسبت ابعادی در همه موارد کار نکند، بنابراین به‌عنوان جایگزین، واسط کاربر بازی‌تان را به اندازه پنجره جدید متصل کنید و محتوای اصلی بازی‌تان را در قالب جعبه نامه قرار دهید. اگر محدودیت‌های فنی یا طراحی مانع از هریک از این رویکردها می‌شود، برای حفظ نسبت ابعادی، کادربندی سینمایی درون موتور خودتان را انجام دهید و ضمن اعلام resizeableActivity = false و اجتناب از حالت پیکربندی، به بهترین ابعاد ممکن مقیاس‌بندی کنید.

صرف‌نظر از رویکردی که اتخاذ می‌کنید، بازی‌تان را در پیکربندی‌های مختلف آزمایش کنید (تاشده و بازشده، تغییرات چرخش مختلف، حالت صفحهٔ دونیمه) و مطمئن شوید که هیچ عنصر میانای کاربر درون‌بازی قطع یا هم‌پوشانی ندارد، مشکلی در دسترس‌پذیری هدف لمسی وجود ندارد، یا مشکل نسبت ابعادی وجود ندارد که باعث شود بازی کشیده، فشرده، یا به‌نحوی دیگر تحریف شود.

علاوه‌براین، صفحه‌نمایش‌های بزرگ‌تر معمولاً به‌معنای پیکسل‌های بزرگ‌تر است، زیرا تعداد پیکسل‌های یکسانی برای منطقه‌ای بسیار بزرگ‌تر دارید. این امر می‌تواند باعث پیکسلی شدن میان‌گیرهای پردازش‌شده کوچک‌شده یا دارایی‌های با وضوح پایین‌تر شود. از دارایی‌های با بالاترین کیفیت در دستگاه‌های صفحه‌بزرگ استفاده کنید و نمایه عملکرد بازی‌تان را بررسی کنید تا مطمئن شوید مشکلی وجود ندارد. اگر بازی‌تان از چند سطح کیفیت پشتیبانی می‌کند، مطمئن شوید که دستگاه‌های با صفحه‌نمایش بزرگ را درنظر می‌گیرد.

حالت چندپنجره‌ای

حالت چندپنجره‌ای به چند برنامه امکان می‌دهد به‌طور هم‌زمان از یک صفحه‌نمایش استفاده کنند. حالت چندپنجره‌ای چرخه حیات فعالیت را تغییر نمی‌دهد؛ بااین‌حال، وضعیت ازسرگرفته‌شده برنامه‌ها در چند پنجره در نسخه‌های مختلف Android متفاوت است (به چرخه حیات فعالیت در حالت چندپنجره‌ای در پشتیبانی از حالت چندپنجره‌ای مراجعه کنید).

وقتی بازیکن برنامه یا بازی را در حالت چندپنجره‌ای قرار می‌دهد، سیستم تغییر پیکربندی را به فعالیت اطلاع می‌دهد، همان‌طور که در بخش تغییر اندازه پیشرفته صفحه‌نمایش بزرگ مشخص شده است. تغییر پیکربندی زمانی نیز اتفاق می‌افتد که بازیکن اندازه بازی را تغییر می‌دهد یا بازی را به حالت تمام‌صفحه برمی‌گرداند.

هیچ تضمینی وجود ندارد که برنامه هنگام قرار گرفتن در حالت چندپنجره‌ای، دوباره فوکوس را به‌دست آورد. بنابراین، اگر از هرکدام از رویدادهای وضعیت برنامه برای توقف موقت بازی استفاده می‌کنید، برای ازسر گرفتن بازی به رویداد کسب تمرکز (onWindowFocusChanged() با مقدار تمرکز درست) اتکا نکنید. به‌جای آن، از دیگر کنترل‌کننده‌های رویداد یا کنترل‌کننده‌های تغییر وضعیت مثل onConfigurationChanged() یا onResume() استفاده کنید. توجه داشته باشید که همیشه می‌توانید از روش isInMultiWindowMode() برای تشخیص اینکه فعالیت فعلی در حالت چندپنجره‌ای اجرا می‌شود یا نه استفاده کنید.

با پنجره‌بندی رایانه‌ای در Googlebook،‏ ChromeOS، و رایانه‌های لوحی و دستگاه‌های تاشو با صفحه‌نمایش بزرگ، ابعاد اولیه پنجره به ملاحظه مهمی تبدیل می‌شود. بازی لازم نیست تمام‌صفحه باشد و باید اعلام کنید که اندازه پنجره در آن حالت چقدر باید باشد. دو روش توصیه‌شده برای انجام این کار وجود دارد.

گزینه اول بااستفاده از مشخصه‌های خاص در برچسب <layout> در مانیفست Android شما کار می‌کند. شاخصه‌های defaultHeight و defaultWidth ابعاد اولیه را کنترل می‌کنند. همچنین به مشخصه‌های minHeight و minWidth توجه داشته باشید تا بازیکنان نتوانند اندازه پنجره بازی را به ابعادی که پشتیبانی نمی‌کنید تغییر دهند. درنهایت، مشخصه gravity وجود دارد که تعیین می‌کند پنجره هنگام راه‌اندازی در کجای صفحه ظاهر شود. در اینجا نمونه‌ای از برچسب چیدمان بااستفاده از این مشخصه‌ها آورده شده است:

<layout android:defaultHeight="500dp"
        android:defaultWidth="600dp"
        android:gravity="top|end"
        android:minHeight="450dp"
        android:minWidth="300dp" />

گزینه دوم برای تنظیم اندازه پنجره ازطریق استفاده از مرزهای راه‌اندازی پویا کار می‌کند. بااستفاده از setLaunchBounds(Rect)⁠⁠، می‌توانید ابعاد پنجره شروع را تعریف کنید. اگر مستطیل خالی مشخص شده باشد، فعالیت در حالت بیشینه‌شده شروع می‌شود.

علاوه‌براین، اگر از موتورهای بازی Unity یا Unreal استفاده می‌کنید، مطمئن شوید که از نسخه جدیدی (Unity 2019.4.40 و Unreal 5.3 یا جدیدتر) استفاده می‌کنید که پشتیبانی خوبی از حالت چندپنجره‌ای ارائه می‌دهد.

پشتیبانی از حالت تاشو

از کتابخانه چیدمان WindowManager Jetpack برای پشتیبانی از حالت‌های تاشو، مانند حالت رومیزی، استفاده کنید تا غوطه‌وری و تعامل بازیکن را افزایش دهید:

شکل ۳. بازی در حالت رومیزی با نمای اصلی در بخش عمودی نمایشگر و کنترل‌ها در بخش افقی.

کاتلین

fun isTableTopPosture(foldFeature : FoldingFeature?) : Boolean {
    contract { returns(true) implies (foldFeature != null) }
    return foldFeature?.state == FoldingFeature.State.HALF_OPENED &&
            foldFeature.orientation == FoldingFeature.Orientation.HORIZONTAL
}

جاوا

boolean isTableTopPosture(FoldingFeature foldFeature) {
    return (foldFeature != null) &&
           (foldFeature.getState() == FoldingFeature.State.HALF_OPENED) &&
           (foldFeature.getOrientation() == FoldingFeature.Orientation.HORIZONTAL);
}