マガジン

拡大するとぼやける:描画解像度と表示解像度の差

遊びをつくる

執筆にClaudeを使用しています。

色紙の試作コース、小さな駒、ノートを並べたゲーム制作の作業机。
記事のテーマをもとに制作したAI生成イメージ。実際のゲーム画面ではありません。

横320×縦180の小さなキャンバスでゲームを作り、CSSで画面幅いっぱいに伸ばす場面を考えてみます。パソコンの小さな画面では問題なく見えても、高解像度のノートPCやスマートフォンで開くと、細い線や文字の縁がにじんで見えることがあります。ゲームのロジックは変わっていないのに、見た目だけが「安っぽく」なります。 原因は、数字が2種類あることです。1つは、ゲームが絵を描いている内部の細かさ(描画解像度)です。もう1つは、その絵が画面上で占める大きさ(表示サイズ)です。2つがずれたまま引き伸ばすと、ブラウザが足りない点を補間して埋めるため、ぼやけます。 この記事では、同じ絵を「伸ばしただけ」と「画面の細かさに合わせた」の2通りで描き分ける小さなテストページを用意します。読者が自分の端末で切り替えて見比べる手順と、結果の読み方を解説し、最後にアソバースへ投稿する前に確かめたい点を整理します。ぼやけ方の程度は端末ごとに違うため、この記事に結果は書きません。見え方は、ご自身の画面で確かめてください。

この記事の目次
  1. 描画解像度と表示解像度、そして物理ピクセル比
  2. ビフォーアフターを自分で作る
  3. 結果の読み方
  4. ぼやけ以外の落とし穴
  5. アソバースの作品に当てはめて考える
  6. 次の行動
  7. 参考資料

描画解像度と表示解像度、そして物理ピクセル比

ブラウザのcanvas要素には、似た名前の数字が2組あります。

  • 描画解像度:HTMLのwidth・height属性で決まる、canvasの内部の点の数です。
  • 表示サイズ:CSSのwidth・heightで決まる、画面上の大きさです。単位はCSSピクセルです。

もう1つ、画面そのものの細かさを表す物理ピクセル比(window.devicePixelRatio)があります。1CSSピクセルが、画面の物理的な点いくつ分にあたるかを示す値です。一般に、高精細な画面では1より大きい値になります。この値が2なら、横1CSSピクセルは物理的に2点に相当します。

描画解像度が320、表示サイズが640CSSピクセル、物理ピクセル比が2の場合を考えてみます。画面上では物理的に1280点ぶんの幅に、わずか320点の絵が引き伸ばされます。4倍の拡大になり、補間によるぼやけが出やすい条件です。

「画面に合わせる」とは、描画解像度を「表示サイズ × 物理ピクセル比」に近づけることです。そのうえで、ゲームの座標系はCSSピクセルのまま使えるように、描画側で倍率をかけます。

ビフォーアフターを自分で作る

次のHTMLをtest.htmlのような名前で保存し、ブラウザで開いてください。外部ライブラリは不要で、1ファイルで動きます。ボタンで「まえ」(描画解像度を表示サイズと同じに固定)と「あと」(物理ピクセル比を掛ける)を切り替えます。

html
<!doctype html>
<meta charset="utf-8">
<style>
 body{margin:0;font-family:sans-serif}
 canvas{width:100%;height:240px;display:block;background:#fff}
</style>
<button id="b">切り替え</button> <span id="s"></span>
<canvas id="c"></canvas>
<script>
const c=document.getElementById('c'),x=c.getContext('2d');
let sharp=false;
function draw(){
  const dpr=sharp?(window.devicePixelRatio||1):1;
  const w=c.clientWidth,h=c.clientHeight;
  c.width=Math.round(w*dpr);c.height=Math.round(h*dpr);
  x.setTransform(dpr,0,0,dpr,0,0);
  x.clearRect(0,0,w,h);
  x.strokeStyle='#000';x.lineWidth=1;
  x.beginPath();x.moveTo(10,20.5);x.lineTo(w-10,20.5);x.stroke();
  x.beginPath();x.arc(80,120,50,0,7);x.stroke();
  x.beginPath();x.moveTo(160,200);x.lineTo(300,60);x.stroke();
  x.font='16px sans-serif';x.fillStyle='#000';
  x.fillText('Hello 12345 あいうえお',160,140);
  s.textContent=(sharp?'あと':'まえ')+' 内部'+c.width+'×'+c.height+' / dpr '+dpr;
}
b.onclick=()=>{sharp=!sharp;draw()};
addEventListener('resize',draw);
draw();
</script>

ポイントは3行です。

  1. c.width = w * dpr:内部の点の数を、表示サイズより増やします。
  2. x.setTransform(dpr,0,0,dpr,0,0):描画命令の座標をCSSピクセルのまま書けるようにします。
  3. addEventListener('resize', draw):ウィンドウの大きさが変わったら描き直します。

記録の取り方

次の手順で、各条件を同じ順番で見比べてください。

  1. 「まえ」の状態で、線・円・文字をよく見ます。画面のスクリーンショットを撮っておくと、後で並べて比べられます。
  2. ボタンを押して「あと」にし、同じ場所を見ます。画面右の表示で、内部サイズの数字が変わっていることも確認します。
  3. ブラウザの拡大(CtrlまたはCommandキーと「+」)を何段階か変え、そのたびに「まえ」「あと」を見比べます。
  4. 可能なら、普通の解像度の画面と高精細な画面の両方で同じ手順を行います。

記録は、条件(端末・画面・ブラウザの拡大率)ごとに「内部サイズ」「物理ピクセル比」「線と文字の見え方の印象」をメモすれば十分です。1条件につき2回ほど見直すと、思い込みを減らせます。

結果の読み方

見え方と数字を、次の表で照らし合わせてください。

  • 「まえ」と「あと」で見え方がほとんど変わらない、読み方:物理ピクセル比が1に近い画面の可能性があります。表示のdprの値を確認してください
  • 「あと」のほうが線や文字がくっきりする、読み方:描画解像度の不足が原因だったと判断できます
  • 「あと」でも線が太く灰色がかって見える、読み方:座標が半ピクセルにずれている可能性があります。1px線は座標を.5にする書き方を試します
  • 拡大率を変えると「あと」の鮮明さが保たれる、読み方:リサイズ時の再設定が働いています
  • 拡大率を変えると崩れる、または元に戻る、読み方:resizeでの再設定が足りない可能性があります

「あと」を選んでも変化がない場合、故障とは限りません。端末の画面が高精細でなければ、差はそもそも出ません。まずdprの値を確認することが、判断の出発点です。

ぼやけ以外の落とし穴

描画解像度を上げると、ぼやけは改善しやすくなりますが、別の問題も起きます。

  • ポインタ座標のずれ:マウスやタッチの座標はCSSピクセル基準で届きます。setTransformで倍率をかけた描画とは単位が違う、と混同すると当たり判定がずれます。座標の基準をどちらにそろえるかは、最初に決めておきます。
  • メモリと処理の増加:物理ピクセル比が大きい端末では、canvasの点の数が一気に増えます。比の上限を決める(たとえばMath.min(dpr, 2))という判断もあります。上限をいくつにするかは、対象の端末で確かめてください。
  • ドット絵の扱い:ドット絵は、あえて補間しないほうが見栄えがよい場合があります。CSSのimage-rendering: pixelatedを使い、倍率を整数にそろえて拡大する手があります。この場合は「ぼやけを消す」より「にじませない」が目的になります。
  • リサイズ時の状態の消失:resizeで描き直すときに、ゲームの状態まで初期化してしまう実装があります。描画の再設定と、ゲーム状態のリセットは分けて書きます。

アソバースの作品に当てはめて考える

アソバースでは、ゲームはsandbox付きのiframeの中で実行され、アップロードしたコードはメインサイトと別のオリジンから配信されます。iframeの大きさが変われば、中のcanvasの表示サイズも変わります。ですから、固定サイズを前提にせず、表示サイズに応じて描き直す設計が向いています。

確認済みの情報として、公式の13作品には「言語変更とプレイ画面の拡大で進行を保つ」という共通の仕様があります。ここから読み取れるのは、画面の大きさが変わる操作があっても、遊んでいる状態が失われないことを前提に作られている、という点です。前の章の「描画の再設定」と「ゲーム状態のリセット」を分ける考え方と、方向が重なります。

一方、個々の作品がcanvasを使っているか、描画解像度をどう決めているかは、確認済みの情報にはありません。この部分は作品を開いて確かめてください。ブラウザの拡大や、プレイ画面の拡大を試し、文字や線のくっきり具合と、途中の進行が保たれるかを見ます。

投稿前の確認は、次の順で行えます。

  1. 直下にindex.htmlを置き、素材は相対パスで参照します。
  2. 自分の端末で、通常の拡大率と大きめの拡大率の両方で見え方を確認します。
  3. アップロードは20MiB以内、ZIP展開後は合計100MiB・1,000ファイル以内です。高解像度用の画像を2倍・3倍の大きさで用意する場合は、容量にも気を配ります。
  4. 投稿画面の「表示の確認」の段階で、実際の表示を見直します。

次の行動

まず上のテストページを保存し、自分の端末で「まえ」と「あと」を切り替えて、dprの値と見え方をメモしてください。次に、手元のゲームのcanvasにdevicePixelRatioの考え方とresizeでの再設定を入れ、ポインタ座標がずれていないかを確かめます。その状態でアソバースに投稿し、プレイ画面の拡大でも崩れないかを見ておくと安心です。

参考資料

  • MDN Web Docs「Window: devicePixelRatio プロパティ」
  • MDN Web Docs「Canvas API」(高解像度ディスプレイ向けの最適化に関する解説)
  • MDN Web Docs「image-rendering」