대형 화면용 설계

이 가이드에서는 다양한 Android 폼 팩터에서 작동하도록 게임을 조정하는 방법을 알아봅니다.

시작하기 전에 대형 화면 크기 조절 지원을 참고하세요. Android의 최신 게임은 시스템 호환성 레이어에 의존하지 않고 모든 화면 크기에 맞게 조정되어야 합니다. 이렇게 하면 게임이 표시되는 방식을 항상 제어할 수 있으며, XR과 같은 폼 팩터나 화면을 회전할 수 없는 기기에서도 게임이 프리미엄 플레이어 환경을 제공합니다.

이렇게 하려면 다음 작업을 완료하세요.

  1. 시스템 레터박스를 사용 중지하고 자체 인엔진 레터박스를 구현합니다.
  2. 레터박스 영역을 사용 가능하고 테마에 맞게 만듭니다.
  3. 게임이 21:9에서 1:1, 9:21로 확장될 수 있도록 하는 데 필요한 디자인 고려사항을 해결합니다.

테스트 명령어

게임을 빌드하고 테스트할 때 게임이 레터박스를 직접 렌더링하는지 확인합니다. 또한 TV 및 차량 내 디스플레이와 같은 기기는 기본 가로 모드 또는 세로 모드 방향에서 회전할 수 없으므로 게임을 세로 모드 및 가로 모드 방향으로 강제해야 합니다.

게임 엔진이 시스템 레터박스를 사용하고 있는지 확인하려면 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 또는 PC와 유사한 기기에서 사용자에게 표시되는 내용을 파악할 수 있습니다.

먼저 기기를 앱 방향을 재정의할 수 있는 상태로 설정합니다.

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

시스템 레터박스를 사용 중지하는 이유는 무엇인가요?

시스템은 항상 시스템 레터박스를 제어합니다. 화면 크기를 감지하고 디스플레이 영역을 직접 관리하면 입력이 게임 영역을 벗어날 때 발생하는 입력 누락과 같은 문제를 완화할 수 있습니다. 게임 콘텐츠가 중간에 고정되어 있더라도 게임플레이 이외의 요소를 화면 가장자리까지 확장할 수 있습니다. 시스템 호환성 모드를 사용하면 게임을 사용할 수 있지만, 플레이어에게 훌륭한 게임을 제공하는 것은 게임 개발자의 몫입니다.

활성 창 영역을 강조 표시하는 휴대전화, 폴더블, 태블릿, 노트북 디스플레이의 그림
그림 1. 휴대전화, 폴더블, 태블릿, 노트북 폼 팩터

레터박스 샘플 구현

대부분의 게임 엔진에서는 카메라가 화면에 렌더링되는 위치를 제어할 수 있습니다. 자체 레터박스를 구현하려면 렌더링된 영역의 가로세로 비율을 측정하고 시스템에서 게임에 레터박스를 적용하도록 하는 대신 사용되지 않는 영역의 클리핑 직사각형을 조정합니다. 다음 샘플은 하나의 카메라를 제한된 가로세로 비율로 렌더링하도록 제한하는 동시에 UI 카메라 또는 패스가 전체 논리적 화면 크기로 확장되도록 허용합니다.

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);
}

Unity의 단계별 안내는 대형 화면 및 폴더블용 Unity 게임 최적화 Codelab을 참고하세요.

대형 및 소형 화면 크기 처리

휴대전화 및 폴더블의 외부 디스플레이와 같은 작은 화면에서는 세로 모드 또는 가로 모드로 고정된 게임이 최상의 사용자 환경일 수 있습니다. 태블릿, 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
        }
    }
    

일반적인 문제에 대한 설계

다양한 가로세로 비율과 방향을 지원하면 플레이어에게 최상의 사용자 환경을 제공할 수 있습니다. 휴대전화와 달리 대형 디스플레이는 가로 모드 또는 세로 모드 방향으로 고정되는 경우가 많으며, 화면이 거의 정사각형인 경우도 있습니다. 이러한 대형 기기에서 플레이어는 게임을 플레이할 때 멀티태스킹을 하는 경우가 많습니다. 화면 공간 사용을 극대화하면 더 많은 플레이어를 더 자주, 더 긴 플레이 세션에 참여시킬 수 있습니다.

다음 섹션에서는 휴대전화 화면이 아닌 화면에 삽입된 휴대전화 게임이 아니라 다양한 폼 팩터에서 프리미엄 경험을 제공하는 게임을 만드는 솔루션을 제공합니다.

가로세로 비율 인식 카메라

3D 게임에서 화면이 너무 높으면 플레이어는 좁은 틈새를 통해 게임을 보는 것처럼 느낄 수 있고, 너무 넓으면 플레이어가 설계된 상호작용을 놓칠 수 있습니다. 2D 게임과 마찬가지로 '안전 영역'을 항상 표시할 수 있습니다. 다음 코드는 활성 가로세로 비율을 참조 가로세로 비율인 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 및 UI 요소를 화면의 안전한 가장자리에 고정합니다. 컷아웃을 피하되 가상 컨트롤, 체력 바, 미니맵과 같은 상호작용 요소는 게임플레이 영역에서 최대한 멀리 배치합니다. 이렇게 하면 게임의 상호작용 부분이 전체 화면을 사용하며 태블릿 및 폴더블과 같은 폼 팩터의 플레이어가 손가락을 과도하게 뻗지 않고도 편안하게 게임을 플레이할 수 있습니다.

마지막으로 레터박스 영역을 적절한 테마 또는 다이에제틱으로 만듭니다. 화면 팔레트나 타일 세트와 일치하는 테두리, 보드게임이나 복고풍 게임을 위한 가상 테이블이나 화면, 확장된 미니맵, 장식용 시각 요소를 고려하세요.

다음 예에서 게임은 대부분의 가로세로 비율에서 전체 크기로 렌더링됩니다. 가로세로 비율이 4:3보다 좁으면 테마 색상의 스카이박스를 렌더링하면서 컨트롤과 같은 UI 요소를 화면 가장자리에 계속 배치합니다.

모서리에 터치 컨트롤이 있는 넓은 가로 모드 창에서 전체 화면으로 렌더링된 횡스크롤 숲 게임
그림 2. 표준 와이드 가로세로 비율로 전체 크기로 렌더링되는 게임
4:3보다 좁은 창에서 실행되는 횡스크롤 숲 게임으로, 테마가 있는 노란색 나뭇잎 레터박스 막대가 표시되고 터치 컨트롤이 외부 화면 가장자리에 고정되어 있습니다.
그림 3. 4:3보다 좁은 가로세로 비율로 화면 가장자리에 UI 컨트롤이 고정된 테마 스카이박스를 렌더링하는 게임

전장의 안개

많은 게임 개발자는 플레이어가 극단적인 세로 또는 가로 가로세로 비율로 인해 부당 이익을 얻을 수 있다고 우려합니다. 예를 들어 멀티플레이어 온라인 배틀 아레나(MOBA)와 같은 경쟁 게임의 플레이어는 매우 세로로 긴 화면을 사용하여 상대방의 레인을 깊이 들여다볼 수 있습니다.

하향식 전략 및 시뮬레이션 게임의 경우 전장의 안개를 렌더링하는 것이 좋습니다.

  • 플레이어가 볼 수 있을 때까지 도형을 렌더링하지 않습니다.
  • 플레이어 캐릭터가 적극적으로 볼 수 없는 도형을 동적 요소 없이 어둡게 렌더링합니다.
  • 플레이어 캐릭터가 실제로 볼 수 있는 상대, 수집품, 기타 동적 객체만 렌더링합니다.

이 기법을 완전한 대화형 레터박스와 결합하고 UI를 화면 가장자리까지 렌더링하면, 개발자가 레벨 구조나 팀 균형을 재설계하지 않아도 플레이어는 세련되고 의도적인 경험을 할 수 있습니다.

전장의 안개 없이 더 큰 화면을 채우도록 이 게임을 확장하면 플레이어가 볼 수 없는 맵의 일부가 표시됩니다.

작은 화면에 표시된 탑다운 RPG에서 버그드로이드 경이 방패와 랜턴을 들고 빛나는 검이 있는 받침대, 강 다리, 광산 수레 선로 근처에 서 있습니다.
그림 4. 작은 화면의 원본 카메라 뷰
대형 화면에서 동일한 탑다운 RPG 지도를 확장하여 던전 방 안의 골렘과 보물 상자, 강 건너편의 고블린을 보여주는 모습
그림 5. 플레이어가 볼 수 없는 지도의 일부를 보여주는 확장된 뷰

전장의 안개를 렌더링하고 적과 아이템과 같은 동적 요소를 숨기면 게임에 사용할 수 있는 전체 프레임을 사용하면서 게임의 공정성을 유지할 수 있습니다.

작은 화면에 플레이어 주변의 활성 영역이 표시되는 탑다운 RPG
그림 6. 플레이어 캐릭터 주변의 활성 표시 영역
멀리 있는 지형은 어둡게 표시되고 플레이어의 시야 밖에 있는 적과 아이템은 숨겨진 탑다운 RPG 지도의 확장된 뷰
그림 7. 전장의 안개로 인해 멀리 있는 지형이 흐리게 표시되고 동적 요소가 숨겨진 확장된 뷰

도형 페이드 아웃

많은 게임, 특히 턴 기반 전략 게임에서는 이전 섹션에서 설명한 전장의 안개와 유사한 기법을 사용하며, 플레이어가 발견하지 못했거나 특정 세트피스 중에 플레이어가 상호작용할 수 없는 도형은 렌더링하지 않습니다.

확장된 시야로 인해 플레이어가 보지 말아야 할 세트나 시네마틱의 일부가 드러나는 경우, 깔끔하게 디자인된 방식으로 장면의 특정 부분을 마스킹하면 고정된 정적 테두리를 인식하지 않고도 레터박스와 동일한 실질적인 효과를 얻을 수 있습니다.

다음 예에서는 게임을 더 큰 가로세로 비율로 확장하면 성벽 밖에 좀비 무리가 표시됩니다.

성벽 안에서 버그드로이드와 메카 햄스터가 등장하는 등축 투영법 전술 RPG 뷰
그림 8. 성벽 안의 플레이 영역이 더 좁은 가로세로 비율로 표시됩니다.
성문 아래에서 기다리고 있는 적 무리를 보여주는 확장된 아이소메트릭 전술 RPG 뷰입니다.
그림 9. 성벽 밖에 있는 좀비 무리를 보여주는 더 큰 가로세로 비율

대신 현재 플레이 영역의 경계를 벗어나는 수준의 도형 렌더링을 생략할 수 있습니다.

성벽 안에서 버그드로이드와 메카 햄스터가 등장하는 등각 투영 전술 RPG 뷰
그림 10. 성벽 안의 놀이 공간입니다.
성벽 외부의 레벨 지오메트리와 적이 생략된 확장된 아이소메트릭 전술 RPG 뷰입니다.
그림 11. 현재 플레이 영역을 벗어나면 레벨 지오메트리가 렌더링되지 않는 더 큰 가로세로 비율

다이에제틱 경계

확장된 시야가 1인칭 또는 3인칭 멀티플레이어 게임에서 경쟁 우위로 이어지거나 고정 세트 기반 게임의 작성되지 않은 영역을 드러낼 수 있는 몰입형 3D 게임에서는 직사각형 레터박스에 의존하는 대신 게임에 다이에제틱 요소를 빌드하여 시야를 제한하는 것이 좋습니다. 예를 들면 다음과 같습니다.

  • 자동차나 비행기와 같은 차량의 내부 조종석
  • 헬멧 내부
  • 플레이어 시야의 가장자리를 나타내는 비네팅

UI 및 HUD 요소는 화면 가장자리까지 확장되거나 다이에제틱 경계에 통합되어야 하며, 동적으로 만드는 것이 이상적입니다. 이러한 다이제틱 요소는 멀미를 경험하는 플레이어(특히 XR 폼 팩터에서)에게도 도움이 될 수 있으며, 새로운 가로세로 비율의 복잡성을 완화하는 것 외에도 접근성 지원을 제공합니다.

상호작용이 불가능한 정적 장면 내의 노출 영역에 게임을 렌더링하지 마세요. 이 접근 방식은 레터박스 및 필러박스에 검은색 막대를 렌더링하는 것보다 낫지만 플레이어는 정적 테두리를 빠르게 알아차리고, 좋아하는 게임이 휴대전화가 아닌 폼 팩터에서 휴대전화 크기의 화면 패치로 렌더링되면 불만을 느낄 수 있습니다.

이 예에서는 표준 휴대전화 화면에 맞게 설계된 게임이 폴더블의 내부 디스플레이에서 흔히 볼 수 있는 정사각형에 가까운 가로세로 비율을 수용하기 위해 스큐어모픽 게임패드가 있는 시뮬레이션된 텔레비전 내부에 렌더링됩니다.

시뮬레이션된 CRT 텔레비전 프레임 내부에 렌더링된 3D 미로 게임으로, 전경에는 스큐어모픽 게임패드가 있습니다.
그림 12. 스큐어모픽 게임패드가 있는 정적 시뮬레이션 TV 프레임 내에 렌더링된 게임

일부 3D 게임은 경쟁의 공정성 또는 디자인 고려사항을 위해 시야를 제한해야 할 수 있습니다. 조종석, 헬멧 또는 기타 다이에제틱 메타포를 사용하여 시야를 제한하는 것이 검은색 막대보다 좋습니다.

구형 햄스터 볼 조종석 내부에서 바라본 1인칭 3D 미로 뷰로, 상단에 미니맵이 있고 하단에 터치 컨트롤이 있습니다.
그림 13. HUD와 컨트롤을 화면 가장자리에 고정하면서 시야를 제한하는 다이에제틱 구형 조종석 프레임

추가 리소스