Skip to content

SafeArea と HUD 回避 ​

ゲーム画面には、避けないと欠ける領域が 2 つあります。システム UI(ノッチ・角丸・ホームインジケータ)と、UZU がゲームの上に重ねる HUD(メニューボタン + アクションバー)です。

ホストは常に全画面でゲームを描きます。避けるのはゲーム側の仕事で、そのための値がホストから配られます。

素の env(safe-area-inset-*) は使えません

iframe / WebView の境界を越えないため、ホストによっては 0 を返します。下記の CSS 変数か getInsets() を使ってください。

配られるもの ​

SDK の init() が、以下の CSS カスタムプロパティを起動時に設定します。

変数意味
--uzu-safe-area-inset-{top,right,bottom,left}デバイスの safe area(ノッチ / 角丸 / ホームインジケータ)
--uzu-hud-inset-x / --uzu-hud-inset-yHUD 矩形の右下座標(safe area 内側の左上が原点)
--uzu-content-inset-{top,right,bottom,left}上 2 つを合成済み(top にだけ HUD の高さを足す)。迷ったらこれを使う

使い方 ​

env() をフォールバックに置いておくと、ブラウザで直接開いたときも同じ CSS が効きます。

css
/* style.css */
.game-ui {
  padding-top: var(--uzu-content-inset-top, env(safe-area-inset-top, 0px));
  padding-right: var(--uzu-content-inset-right, env(safe-area-inset-right, 0px));
  padding-bottom: var(--uzu-content-inset-bottom, env(safe-area-inset-bottom, 0px));
  padding-left: var(--uzu-content-inset-left, env(safe-area-inset-left, 0px));
}

HUD の右隣まで使いたいときだけ、下 2 層を自分で合成します。

css
.top-left-ui {
  margin-top: calc(var(--uzu-safe-area-inset-top, 0px) + var(--uzu-hud-inset-y, 0px));
  margin-left: calc(var(--uzu-safe-area-inset-left, 0px) + var(--uzu-hud-inset-x, 0px));
}

canvas / WebGL ​

canvas の中身は CSS で避けられません。JS API から数値で読みます。

ts
import { getInsets, onInsetsChange } from '@uzuhq/code-sdk';

const { safeArea, content, hud } = getInsets();
layout(content);

// 回転や PiP で値が変わったら呼ばれる。戻り値で解除できる。
const off = onInsetsChange((insets) => layout(insets.content));

resize では追従できません

CSS 変数の変化は resize イベントを発火させません。 window.addEventListener('resize', …) だけを見ている実装は、回転や PiP で inset が変わっても再レイアウトしません。onInsetsChange を使ってください。

値は初回ペイント前に確定しているので、setup の中で getInsets() を読んで構いません。

画面を safe area の内側に収めたいとき ​

「ノッチに一切かからない絵にしたい」場合は、描画の基準になる要素を自分で狭めます。

css
#app {
  position: absolute;
  top: var(--uzu-content-inset-top, 0px);
  left: var(--uzu-content-inset-left, 0px);
  width: calc(100% - var(--uzu-content-inset-left, 0px) - var(--uzu-content-inset-right, 0px));
  height: calc(100% - var(--uzu-content-inset-top, 0px) - var(--uzu-content-inset-bottom, 0px));
}

小さい画面で下端が切れるとき ​

inset は実 px で、画面サイズに比例しません。同じ値でも、画面が小さいほど占める割合が大きくなります。 横持ちで上下に消える高さ(--uzu-content-inset-top + --uzu-content-inset-bottom)は次のとおりです。

画面の短辺上下で消える高さ比率
720px(PC ブラウザ)52px(HUD のみ)7%
390px(iPhone 横持ち)73px(HUD 52 + ホームインジケータ 21)19%
360px(下限)73px20%

横画面は元々高さが足りないので、ここで 2 割を取られると下端が切れます。PC の広いウィンドウで作っていると最後まで気付けません。

避ける余白そのものは削れません(HUD もホームインジケータも実際にそこにある)。削るのは中身の側です。

css
/* 舞台の高さに比例させる。px 直書きだと小さい画面で縮まない */
.stage {
  container-type: size;
  font-size: 2cqh; /* 下限を clamp() で止めるなら、下限自体が
                      基準サイズで成立しているか必ず確かめる */
}

/* 余白・文字サイズは em で書き、舞台と一緒に縮ませる */
.panel {
  padding: 1em 1.4em;
  font-size: 1.1em;
}

clamp() の下限に注意

font-size: clamp(11px, 2cqh, 19px) のように下限を置くと、舞台の高さが 550px を切った時点で全部 11px に張り付き、そこから先は何も縮まなくなります。下限を置くなら、基準サイズの一番小さいところでその下限のまま収まるかを確認してください。

値を確かめる ​

example_scenarios/safe_area_test が、素の env() とホストが配る 3 層を並べて表示します。dev harness では ActionBar の端末アイコンで擬似ノッチを ON/OFF できます。

向きで避ける辺が入れ替わる(縦持ちは上下、横持ちは左右)ので、uzu dev --orientation landscape で横持ちも確認してください。