עיצוב למסכים גדולים

במדריך הזה נסביר איך להתאים את המשחק כך שיפעל בגורמי צורה שונים של Android.

לפני שמתחילים, כדאי לקרוא את המאמר תמיכה בשינוי גודל של מסכים גדולים. משחקים מודרניים ב-Android צריכים להתאים לכל גודל מסך בלי להסתמך על שכבות תאימות של המערכת. כך אתם תמיד יכולים לשלוט באופן שבו המשחק מוצג, והמשחק שלכם מספק חוויית משתמש איכותית, גם בגורמי צורה כמו XR או במכשירים שבהם אי אפשר לסובב את המסך.

כדי לעשות את זה, צריך לבצע את המשימות הבאות:

  1. משביתים את פורמט ה-letterbox של המערכת ומטמיעים פורמט letterbox משלכם במנוע.
  2. האזור של הפורמט הרחב צריך להיות שימושי ומתאים מבחינת הנושא.
  3. התייחסו לשיקולי העיצוב שנדרשים כדי לאפשר למשחק להתאים את עצמו ליחסי גובה-רוחב מ-21:9 עד 1:1 ועד 9:21.

פקודות לבדיקה

כשאתם מפתחים ובודקים את המשחק, ודאו שהמשחק עצמו מעבד את הפסים השחורים. כדאי גם לאלץ את המשחק לפעול לאורך ולרוחב, כי מכשירים כמו טלוויזיות ומסכים ברכב לא יכולים להסתובב מכיוון ברירת המחדל שלהם לאורך או לרוחב.

כדי לבדוק אם מנוע המשחק משתמש ב-letterboxing של המערכת, משתמשים בפקודה adb shell cmd window set-letterbox-style. לדוגמה, הפקודה הבאה משנה את הצבע של תיבות הדואר של המערכת לירוק בהיר ומוסיפה רדיוס פינה למשחק, כך שקל לראות אם המשחק חוזר למצב התאימות של מערכת ההפעלה:

adb shell "cmd window set-letterbox-style --backgroundType solid_color --backgroundColor '#00FF00' --cornerRadius 30 --isEducationEnabled false"

כדי לאלץ כיוון מסוים לצורך בדיקה, משתמשים ב-adb shell cmd window user-rotation lock. כך תוכלו לדמות את ההתנהגות של המשחק במכשיר שלא מכבד את ההגדרות של האפליקציה לגבי כיוון המסך, או להבין מה המשתמשים רואים במכשיר XR או במכשיר דמוי מחשב שבו החלונות יכולים להופיע לאורך או לרוחב, בלי קשר לכיוון המכשיר.

קודם כול, צריך להעביר את המכשיר למצב שבו אפשר לשנות את כיוון המסך של האפליקציה:

adb shell cmd window set-ignore-orientation-request true
adb shell cmd window fixed-to-user-rotation enabled

בשלב הבא, בודקים את שינוי המשחק למצב לרוחב:

adb shell cmd window user-rotation lock 1

אחר כך בודקים את השינוי של המשחק למצב לאורך:

adb shell cmd window user-rotation lock 0

כדי לאפס את המכשיר להגדרות ה-OEM, מפעילים אותו מחדש או מריצים את הפקודות הבאות:

adb shell cmd window reset-letterbox-style
adb shell cmd window fixed-to-user-rotation default
adb shell cmd window user-rotation free
adb shell cmd window set-ignore-orientation-request reset

למה להשבית את ה-letterboxing של המערכת?

המערכת תמיד שולטת בפורמט ה-letterbox של המערכת. כדי למנוע בעיות כמו קלט שלא נקלט, שמתרחשות כשהקלט יוצא מאזור המשחק, אתם יכולים לזהות את גודל המסך ולנהל את אזור התצוגה בעצמכם. אתם יכולים גם להרחיב רכיבים שלא קשורים לגיימפליי עד לקצה המסך, גם אם תוכן המשחק נשאר קבוע באמצע. מצבי תאימות של המערכת יכולים להפוך משחק לזמין, אבל אתם, כמפתחי המשחק, צריכים להפוך את המשחק שלכם למצוין עבור השחקנים.

איורים של מסכי טלפון, מכשיר מתקפל, טאבלט ומחשב נייד שמדגישים את אזורי החלונות הפעילים.
תרשים 1. גורמי צורה של טלפון, מכשיר מתקפל, טאבלט ומחשב נייד.

דוגמה להטמעה של Letterbox

רוב מנועי המשחקים מאפשרים לכם לשלוט במיקום שבו המצלמה מעבדת את התמונה במסך. כדי להטמיע פורמט letterbox משלכם, אתם צריכים למדוד את יחס הגובה-רוחב של האזור המעובד ולהתאים את מלבן החיתוך לאזור שלא בשימוש, במקום לאפשר למערכת להוסיף פסי letterbox למשחק. בדוגמה הבאה, מצלמה אחת מוגבלת לעיבוד ביחס גובה-רוחב מוגבל, ומצלמות או מעברים של ממשק המשתמש יכולים להתרחב לגודל המסך הלוגי המלא:

Unity

[SerializeField]
private float minAspectRatio = 4f / 3f;

[SerializeField]
private float maxAspectRatio = 21f / 9f;

private Camera _camera;

private void Awake()
{
    _camera = GetComponent<Camera>();
}

public void MaintainAspectRatio()
{
    int viewWidth = Screen.width;
    int viewHeight = Screen.height;

    // 1. Calculate screen aspect ratio
    float scaleScreen = viewWidth / (float)viewHeight;
    Rect rect = _camera.pixelRect;

    // 2. Scale to the fullest of the lower between width or height
    if (scaleScreen > maxAspectRatio)
    {
        // use height, lock width to max at aspect scale
        rect.width = viewHeight * maxAspectRatio;
        rect.height = viewHeight;
    }
    else if (scaleScreen < minAspectRatio)
    {
        // use width, lock height to min at aspect scale
        rect.width = viewWidth;
        rect.height = viewWidth / minAspectRatio;
    }
    else
    {
        // within allowed aspect ratio range, use full screen
        rect.width = viewWidth;
        rect.height = viewHeight;
    }

    rect.x = Mathf.Floor((viewWidth - rect.width) * 0.5f);
    rect.y = Mathf.Floor((viewHeight - rect.height) * 0.5f);

    Debug.Log("camera view width = " + rect.width +
        " height = " + rect.height +
        " x = " + rect.x +
        " y = " + rect.y);

    // 3. Commit the new size to the camera so that it takes effect
    _camera.pixelRect = rect;
}

‫C++‎‏ (Vulkan)

/**
 * @brief Calculates a bounded-aspect-ratio gameplay viewport and scissor
 * rectangle and records them directly into a Vulkan command buffer.
 *
 * @note Requires `VK_DYNAMIC_STATE_VIEWPORT` and `VK_DYNAMIC_STATE_SCISSOR` to
 * be enabled on the bound graphics `VkPipeline`.
 *
 * @param command_buffer   Active `VkCommandBuffer` in the recording state.
 * @param window_width     Full physical window or swapchain width in pixels.
 * @param window_height    Full physical window or swapchain height in pixels.
 * @param min_aspect_ratio Minimum allowed aspect ratio (`width / height`).
 * @param max_aspect_ratio Maximum allowed aspect ratio (`width / height`).
 */
void MaintainAspectRatio(VkCommandBuffer command_buffer, uint32_t window_width,
                         uint32_t window_height, float min_aspect_ratio,
                         float max_aspect_ratio) {
  if (command_buffer == VK_NULL_HANDLE || window_width == 0 ||
      window_height == 0 || min_aspect_ratio <= 0.0f ||
      max_aspect_ratio < min_aspect_ratio) {
    return;
  }

  // 1. Calculate screen aspect ratio.
  const float view_width = static_cast<float>(window_width);
  const float view_height = static_cast<float>(window_height);
  const float scale_screen = view_width / view_height;

  // 2. Scale to the fullest of the lower between width or height.
  float rect_width = view_width;
  float rect_height = view_height;
  if (scale_screen > max_aspect_ratio) {
    // Use height, lock width to max at aspect scale (pillarbox).
    rect_width = std::floor(view_height * max_aspect_ratio);
    rect_height = view_height;
  } else if (scale_screen < min_aspect_ratio) {
    // Use width, lock height to min at aspect scale (letterbox).
    rect_width = view_width;
    rect_height = std::floor(view_width / min_aspect_ratio);
  } else {
    // Within allowed aspect ratio range, use full screen.
    rect_width = view_width;
    rect_height = view_height;
  }

  const float rect_x = std::floor((view_width - rect_width) * 0.5f);
  const float rect_y = std::floor((view_height - rect_height) * 0.5f);

  // 3. Commit the new viewport and scissor to the command buffer so that
  // subsequent gameplay draw calls are transformed and clipped to this region.
  const VkViewport viewport = {
      .x = rect_x,
      .y = rect_y,
      .width = rect_width,
      .height = rect_height,
      .minDepth = 0.0f,
      .maxDepth = 1.0f,
  };
  const VkRect2D scissor = {
      .offset =
          {
              .x = static_cast<int32_t>(rect_x),
              .y = static_cast<int32_t>(rect_y),
          },
      .extent =
          {
              .width = static_cast<uint32_t>(rect_width),
              .height = static_cast<uint32_t>(rect_height),
          },
  };

  vkCmdSetViewport(command_buffer, 0, 1, &viewport);
  vkCmdSetScissor(command_buffer, 0, 1, &scissor);
}

ב-Codelab בנושא אופטימיזציה של משחקי Unity למסכים גדולים ולמכשירים מתקפלים מוסבר שלב אחר שלב איך לעשות את זה ב-Unity.

תמיכה במסכים בגדלים שונים

במסכים קטנים יותר, כמו טלפונים והמסכים החיצוניים של מכשירים מתקפלים, חוויית המשתמש הטובה ביותר עשויה להיות משחק שנעול לאורך או לרוחב. במסכים גדולים יותר – כמו טאבלטים, מכשירי Chromebook, מסכים ברכב או המסכים הפנימיים של מכשירים מתקפלים – פחות רצוי או אפילו בלתי אפשרי לסובב את המכשיר, אבל המסך הפיזי הגדול יותר מאפשר לשחק במשחק ביחסי גובה-רוחב שונים.

כדי לספק לשחקנים את החוויה הטובה ביותר, כדאי לזהות את גודל המסך בזמן הריצה ולהגביל את הסיבוב כשצריך, תוך מתן גמישות רבה יותר לשחקנים ככל שגודל המסך גדל. ב-Android, הסף האידיאלי הוא sw600dp, רוחב מינימלי של 600 פיקסלים שלא תלויים בצפיפות (600dp). הנתון הזה תואם ל-WindowSizeClass.WIDTH_DP_MEDIUM_LOWER_BOUND. אפשר להשתמש ב-computeMaximumWindowMetrics כדי לאמת את גודל המסך הזמין בהגדרות של תצוגה מרובת מסכים.

כדי לתמוך בכך במשחק, צריך לתמוך בכל הכיוונים במניפסט, ואז להעריך את הרוחב הקטן ביותר גם ב-onCreate() וגם ב-onConfigurationChanged() ולהגדיר את נעילת הכיוון בהתאם:

  1. מסירים את הנעילות הסטטיות של הכיוון במניפסט ומצהירים על טיפול בשינויים בהגדרות:

    <activity
        android:name=".MainActivity"
        android:resizeableActivity="true"
        android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|density|keyboard|keyboardHidden" />
    
  2. מוסיפים את הייבוא הנדרש:

    import android.content.pm.ActivityInfo
    import android.content.res.Configuration
    import android.os.Bundle
    import androidx.activity.enableEdgeToEdge
    import androidx.window.core.layout.WindowSizeClass
    import androidx.window.layout.WindowMetricsCalculator
    import kotlin.math.min
    
  3. מטפלים גם ב-onCreate וגם ב-onConfigurationChanged:

    override fun onCreate(savedInstanceState: Bundle?) {
        enableEdgeToEdge()
        super.onCreate(savedInstanceState)
        applyAdaptiveOrientationPolicy()
        // ...
    }
    
    override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)
        applyAdaptiveOrientationPolicy()
        // ...
    }
    
  4. מעדכנים את הכיוון המבוקש על סמך רוחב המסך הקטן ביותר, ונועלים מסכים קומפקטיים לכיוון העיצוב המועדף של המשחק:

    private fun applyAdaptiveOrientationPolicy() {
        // compute the smallest screen bound in dp
        val maxMetrics = WindowMetricsCalculator.getOrCreate().computeMaximumWindowMetrics(this)
        val minDimensionPx = min(maxMetrics.bounds.width(), maxMetrics.bounds.height())
        val displaySmallestWidthDp = minDimensionPx / maxMetrics.density
    
        val targetOrientation = if (displaySmallestWidthDp >= WindowSizeClass.WIDTH_DP_MEDIUM_LOWER_BOUND) {
            // Large display (>= 600dp): allow free rotation in any orientation
            ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
        } else {
            // Compact display (< 600dp): lock to your game's preferred orientation
            // (use SCREEN_ORIENTATION_SENSOR_LANDSCAPE for landscape-first games)
            ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
        }
    
        // Guard against redundant setter calls to avoid configuration change loops
        if (requestedOrientation != targetOrientation) {
            requestedOrientation = targetOrientation
        }
    }
    

איך לעצב את האתר כדי למנוע בעיות נפוצות

כדי לספק לשחקנים את חוויית המשתמש הטובה ביותר, כדאי לתמוך במגוון יחסי גובה-רוחב וכיוונים. בניגוד לטלפונים, מסכים גדולים יותר הם בדרך כלל קבועים בכיוון לרוחב או לאורך, ובמקרים מסוימים המסך כמעט מרובע. במכשירים הגדולים האלה, השחקנים מבצעים לעיתים קרובות כמה משימות במקביל בזמן שהם משחקים במשחק שלכם. ככל שתנצלו טוב יותר את שטח המסך, כך תוכלו לעורר עניין אצל יותר שחקנים, לעיתים קרובות יותר ולמשך זמן ארוך יותר.

בקטעים הבאים מפורטים פתרונות שיגרמו למשחק שלכם להרגיש כמו חוויית פרימיום בכל גורמי הצורה, ולא כמו משחק לטלפון שמוטמע במסך שאינו של טלפון.

מצלמות שמתאימות את עצמן ליחס הגובה-רוחב

במשחקי תלת-ממד, אם המסך גבוה מדי, השחקנים עלולים להרגיש שהם צופים במשחק דרך חריץ צר. אם המסך רחב מדי, השחקנים עלולים לפספס אינטראקציות שתוכננו במשחק. כמו במשחקי דו-ממד, אפשר להגדיר "אזור בטוח" שתמיד יהיה גלוי. הקוד הבא מחליף את שדה הראייה של המצלמה בין אופקי לאנכי, בהתאם להשוואה בין יחס הגובה-רוחב הפעיל לבין יחס הגובה-רוחב של קובץ העזר (1920x1080). כך מעצבים יכולים ליצור משחק לתצוגה של 1080p ולדעת שכל מה שמעוצב בתוך מסגרת הייחוס הזו יישאר גלוי:

Unity

[SerializeField]
private Camera _camera;

[SerializeField]
private float _referenceWidth = 1920f;

[SerializeField]
private float _referenceHeight = 1080f;

[SerializeField]
private float _referenceVerticalFov = 60f;

public float ReferenceAspectRatio => _referenceWidth / Mathf.Max(0.0001f, _referenceHeight);

private void Awake()
{
    if (_camera == null)
    {
        _camera = GetComponent<Camera>();
    }
}

// Built-in Render Pipeline callback. In URP, subscribe to
// RenderPipelineManager.beginCameraRendering or call UpdateFov() on resize.
private void OnPreCull()
{
    UpdateFov();
}

public void UpdateFov()
{
    if (_camera == null || _camera.orthographic)
    {
        return;
    }

    float referenceAspect = ReferenceAspectRatio;
    float currentAspect = _camera.aspect;
    if (currentAspect <= 0.0001f)
    {
        return;
    }

    float targetVerticalFov;

    if (currentAspect >= referenceAspect)
    {
        // Viewport is wider than (or equal to) the reference safe area
        targetVerticalFov = _referenceVerticalFov;
    }
    else
    {
        // Viewport is skinnier than the reference safe area
        float referenceHorizontalFov = Camera.VerticalToHorizontalFieldOfView(_referenceVerticalFov, referenceAspect);
        targetVerticalFov = Camera.HorizontalToVerticalFieldOfView(referenceHorizontalFov, currentAspect);
        targetVerticalFov = Mathf.Clamp(targetVerticalFov, 1f, 179f);
    }

    if (!Mathf.Approximately(_camera.fieldOfView, targetVerticalFov))
    {
        _camera.fieldOfView = targetVerticalFov;
    }
}

‫C++‎‏ (Vulkan)

/**
 * @brief Calculates an aspect-aware Vulkan perspective projection matrix using
 * GLM that keeps a reference safe area (e.g., 1920x1080) visible across
 * aspect ratios.
 *
 * @note Uses `glm::perspectiveZO` for Vulkan's `[0, 1]` depth range and
 * inverts `projection[1][1]` for Vulkan's top-left (`+Y` down) clip space.
 *
 * @param viewport_width         Active gameplay viewport width in pixels.
 * @param viewport_height        Active gameplay viewport height in pixels.
 * @param reference_width        Authored safe-area reference width in pixels.
 * @param reference_height       Authored safe-area reference height in pixels.
 * @param reference_vertical_fov Authored vertical FOV in degrees (e.g., 60.0f).
 * @param near_plane             Near clipping plane distance (`zNear > 0.0f`).
 * @param far_plane              Far clipping plane distance (`zFar > zNear`).
 * @return Aspect-aware `glm::mat4` perspective projection matrix for Vulkan.
 */
glm::mat4 CalculateAspectAwareProjectionMatrix(
    float viewport_width, float viewport_height, float reference_width = 1920.0f,
    float reference_height = 1080.0f, float reference_vertical_fov = 60.0f,
    float near_plane = 0.1f, float far_plane = 100.0f) {
  if (viewport_width <= 0.0001f || viewport_height <= 0.0001f) {
    return glm::mat4(1.0f);
  }

  // 1. Compare the active viewport aspect ratio against the reference safe
  // area.
  const float reference_aspect =
      reference_width / std::max(0.0001f, reference_height);
  const float current_aspect = viewport_width / viewport_height;

  float target_vertical_fov_rad = glm::radians(reference_vertical_fov);

  if (current_aspect >= reference_aspect) {
    // Viewport is wider than (or equal to) the reference safe area (Hor+).
    target_vertical_fov_rad = glm::radians(reference_vertical_fov);
  } else {
    // Viewport is skinnier than the reference safe area (Vert+):
    // Convert reference vertical FOV -> reference horizontal FOV -> new
    // vertical FOV at current_aspect so the safe area is never cropped.
    const float tan_half_horiz =
        std::tan(target_vertical_fov_rad * 0.5f) * reference_aspect;
    target_vertical_fov_rad = 2.0f * std::atan(tan_half_horiz / current_aspect);
    target_vertical_fov_rad = std::clamp(
        target_vertical_fov_rad, glm::radians(1.0f), glm::radians(179.0f));
  }

  // 2. Build the Vulkan [0, 1] depth perspective matrix and flip +Y clip space.
  glm::mat4 projection = glm::perspectiveZO(
      target_vertical_fov_rad, current_aspect, near_plane, far_plane);
  projection[1][1] *= -1.0f;
  return projection;
}

תיבות דואר אינטראקטיביות לחלוטין

בחלק מהמשחקים יש אזור משחק קבוע, בדרך כלל בהתאם לשיקולי עיצוב שמבוססים על הז'אנר, כמו שדה קבוע של משחק יריות אנכי או גלילה של מסך מתהפך שמשמשת לאסתטיקה בסגנון רטרו.

במשחקים כאלה, צריך לשנות את קנה המידה של אזור הגיימפליי כך שיתאים, כדי שציר אחד של המשחק תמיד יוגדל למקסימום וימלא את אזור הגיימפליי. הציר שמוגדל למקסימום יכול להשתנות בהתאם ליחסי הגובה-רוחב היחסיים של המשחק וגבולות התצוגה.

הצמידו את רכיבי ה-HUD ורכיבים בממשק המשתמש עד לקצה הבטוח של המסך: הימנעו ממגרעות, אבל הציבו רכיבים אינטראקטיביים כמו פקדים וירטואליים, מדדי חיים ומפות קטנות כמה שיותר רחוק מאזור הגיימפליי. כך החלק האינטראקטיבי של המשחק משתמש בכל המסך, ושחקנים במכשירים כמו טאבלטים ומכשירים מתקפלים יכולים לשחק בנוחות בלי למתוח את האצבעות יותר מדי.

לבסוף, כדאי להוסיף לאזור ה-letterbox עיצוב שמתאים לנושא או לעולם של המשחק. אפשר להוסיף גבולות שמתאימים לפלטת הצבעים או לערכת המשבצות שמוצגות במסך, שולחן או מסך וירטואלי למשחקי לוח או למשחקי רטרו, או מפות קטנות מורחבות ואלמנטים חזותיים דקורטיביים.

בדוגמה הבאה, המשחק מוצג בגודל מלא ברוב יחסי הגובה-רוחב. כשיחס הגובה-רוחב צר יותר מ-4:3, הוא יוצר תיבת שמיים בצבעים שמתאימים לנושא, וממשיך להציב רכיבים בממשק המשתמש כמו פקדים בקצה המסך:

משחק גלילה אופקית ביער שמוצג במסך מלא בחלון רחב לרוחב עם שליטה באמצעות מגע בפינות.
איור 2. משחק שמוצג בגודל מלא ביחס גובה-רוחב רחב רגיל.
משחק גלילה אופקית ביער בחלון צר יותר מ-4:3, עם פסי letterbox בצבע צהוב עם שליטה באמצעות מגע שמעוגנים לקצוות החיצוניים של המסך.
איור 3. משחק ביחס גובה-רוחב צר יותר מ-4:3, שבו מוצג סקייבוקס עם רכיבים אינטראקטיביים בממשק המשתמש שמעוגנים לקצוות המסך.

ערפל קרב

הרבה מפתחי משחקים חוששים ששחקנים יכולים לקבל יתרון לא הוגן מיחסי גובה-רוחב אנכיים או אופקיים קיצוניים. לדוגמה, שחקן במשחק תחרותי כמו זירת קרב מרובת משתתפים (MOBA) יכול להשתמש במסך אנכי מאוד כדי לראות את המסלול של היריב.

במשחקי אסטרטגיה וסימולציה עם מבט מלמעלה, כדאי להציג ערפל קרב:

  • אל תעבדו את הגיאומטריה בכלל עד שהשחקן יוכל לראות אותה.
  • הציגו את הגאומטריה שהדמות של השחקן לא יכולה לראות באופן פעיל כמעומעמת, בלי אלמנטים דינמיים.
  • מרנדרים רק יריבים, פריטים לאיסוף ואובייקטים דינמיים אחרים שהדמות של השחקן יכולה לראות באופן פעיל בעולם המשחק.

אם תשלבו את השיטה הזו עם תיבות דואר אינטראקטיביות לחלוטין ותעבדו את ממשק המשתמש עד לקצה המסך, השחקנים ייהנו מחוויה מלוטשת ומכוונת, בלי שתצטרכו לעצב מחדש את מבנה השלבים או את האיזון בין הקבוצות.

אם מרחיבים את המשחק הזה כדי למלא מסך גדול יותר בלי ערפל קרב, נחשפים חלקים במפה שהשחקן לא אמור לראות:

משחק תפקידים במבט מלמעלה על מסך קטן, שבו רואים את סר באגדרואיד מחזיק מגן ופנס ליד חרב זוהרת על כן, גשר מעל נהר ומסילה של קרונית מכרות.
איור 4. התצוגה המקורית של המצלמה במסך קטן יותר.
תצוגה מורחבת של אותה מפת RPG מלמעלה למטה במסך גדול, שבה רואים גולם ותיבת אוצר בתוך חדר בצינוק וגובלינים מעבר לנהר.
איור 5. תצוגה מורחבת שחושפת חלקים מהמפה שהשחקן לא אמור לראות.

כדי לשמור על הוגנות במשחק, אפשר להציג ערפל קרב ולהסתיר אלמנטים דינמיים כמו אויבים ופריטים, תוך שימוש בכל הפריים שזמין למשחק:

משחק תפקידים במבט מלמעלה למטה על מסך קטן שמציג את האזור הפעיל סביב השחקן.
איור 6. האזור הגלוי הפעיל מסביב לדמות השחקן.
תצוגה מורחבת של מפת RPG מלמעלה למטה, שבה השטח המרוחק מעומעם והאויבים והפריטים שנמצאים מחוץ לקו הראייה של השחקן מוסתרים.
איור 7. תצוגה מורחבת עם ערפל קרב שמעמעם את השטח המרוחק ומסתיר אלמנטים דינמיים.

היעלמות הדרגתית של הגיאומטריה

במשחקים רבים, במיוחד במשחקי אסטרטגיה מבוססי-תורות, משתמשים בטכניקה שדומה לערפל הקרב שמתואר בקטע הקודם, ולא מעבדים את הגיאומטריה שהשחקן לא גילה או שהשחקן לא יכול לקיים איתה אינטראקציה במהלך סצנה מסוימת.

אם שדה ראייה מורחב חושף חלקים מהסט או מהסצנה שהשחקן לא אמור לראות, אפשר להסתיר חלקים מסוימים בסצנה בצורה מעוצבת ונקייה. כך אפשר להשיג את אותו אפקט מעשי כמו של פורמט Letterbox, בלי התחושה של גבול סטטי קבוע.

בדוגמה הבאה, הרחבת המשחק ליחס גובה-רוחב גדול יותר חושפת המון זומבים מחוץ לחומות הטירה:

תצוגה איזומטרית של משחק תפקידים טקטי עם Bugdroid ו-Mecha Hamster בתוך חומות של טירה.
איור 8. אזור המשחק בתוך חומות הטירה ביחס גובה-רוחב צר יותר.
תצוגה איזומטרית מורחבת של משחק תפקידים טקטי, שבה רואים המון אויבים שמחכים מחוץ לשער התחתון של הטירה.
איור 9. יחס גובה-רוחב גדול יותר שחושף נחיל זומבים מחוץ לחומות הטירה.

במקום זאת, אפשר להשמיט את העיבוד של הגיאומטריה של השלב מעבר לגבולות של אזור המשחק הנוכחי:

תצוגה איזומטרית של משחק תפקידים טקטי עם Bugdroid ו-Mecha Hamster בתוך חומות הטירה.
איור 10. אזור משחקים בתוך חומות הטירה.
תצוגה איזומטרית מורחבת של משחק תפקידים טקטי, שבה הושמטו הגיאומטריה של השלב והאויבים מחוץ לחומות הטירה.
איור 11. יחס גובה-רוחב גדול יותר, שבו הגיאומטריה של השלב לא מוצגת מעבר לאזור המשחק הנוכחי.

גבולות דיאגטיים

במשחקי תלת-ממד סוחפים שבהם שדה ראייה מורחב יכול להוביל ליתרון תחרותי במשחקים מרובי משתתפים מגוף ראשון או מגוף שלישי, או לחשוף אזורים לא מורשים של משחקים שמבוססים על קבוצות קבועות, כדאי לבנות אלמנטים דיאגטיים במשחק כדי להגביל את שדה הראייה במקום להסתמך על תיבת דואר מלבנית. דוגמאות:

  • תא הנהג או הטייס של כלי רכב כמו מכונית או מטוס
  • החלק הפנימי של קסדה
  • וינייטה שמציינת את קצה שדה הראייה של השחקן

רכיבי ממשק המשתמש וה-HUD עדיין צריכים להגיע עד לקצה המסך או להיות משולבים בגבולות הדיאגטיים, ומומלץ להפוך אותם לדינמיים. האלמנטים הדיאגטיים האלה יכולים גם לעזור לשחקנים שסובלים ממחלת תנועה, במיוחד במכשירי XR, ולספק כלי עזר לנגישות בנוסף להפחתת המורכבות של יחסי גובה-רוחב חדשים.

אל תציגו את המשחק על משטח בתוך סצנה סטטית לא אינטראקטיבית. למרות שהגישה הזו עדיפה על רינדור של פסים שחורים, השחקנים מבחינים במהירות בגבולות הסטטיים ועלולים להתאכזב אם המשחקים האהובים עליהם מוצגים בגודל של מסך טלפון במכשירים עם גורמי צורה אחרים.

בדוגמה הזו, משחק שמיועד למסך טלפון רגיל מוצג בתוך טלוויזיה מדומה עם שלט משחק סקאומורפי, כדי להתאים ליחס הגובה-רוחב הריבועי יותר שנפוץ במסכים הפנימיים של מכשירים מתקפלים:

משחק מבוך תלת-ממדי שמוצג בתוך מסגרת של טלוויזיית CRT מדומה, עם שלט משחק סקאומורפי בחזית.
איור 12. משחק שמוצג בתוך מסגרת טלוויזיה מדומה סטטית עם שלט משחק סקאומורפי.

בחלק מהמשחקים בתלת-ממד, יכול להיות שיהיה צורך להגביל את שדה הראייה כדי לשמור על הוגנות תחרותית או משיקולי עיצוב. עדיף להשתמש בתא טייס, קסדה או מטאפורה דיאגטית אחרת כדי להגביל את שדה הראייה, ולא בפסים שחורים:

תצוגת מבוך תלת-ממדית מגוף ראשון מתוך תא טייס כדורי של אוגר, עם מפה ממוזערת בחלק העליון ושליטה באמצעות מגע בחלק התחתון.
איור 13. מסגרת כדורית דיאגטית של תא הטייס שמגבילה את שדה הראייה, ומעגנת את התצוגה העילית (HUD) ואת אמצעי הבקרה לקצוות המסך.

מקורות מידע נוספים