🖌️

【もぉん流】Unity × Pixel Perfect : サクッとやったれ!

に公開

【もぉん流】やったれ!サクッと Pixel Perfect!

この記事では、Sprite だけでなくライトや UI も含めて Pixel Perfect を実現するための手順を紹介します。環境は Unity + URP(2D Renderer) を想定しています。

下記のような方におすすめです!

  • そもそも Pixel Perfect って何!?
  • Pixel Perfect を簡単に設定したい!
  • ライトや UI までまとめてドット絵に揃えたい!

「Pixel Perfect」という言葉は、ドット絵を描くときの線の技術や、Webページをデザイン通り1px単位で再現する手法、RTAで1px単位で合わせるテクニックなど、いろいろな場面で使われています。本記事で扱うのは、ゲーム画面を描画するときにドット絵の整合性を保つ技術です。

直近の事例として、この記事の内容を実際に使ったゲームがこちら!
死後の世界で魚を釣って寿司を握るブラウザゲーム 『三途の川フィッシング』 です。

https://unityroom.com/games/sanzunokawa
https://www.gamespark.jp/article/2026/02/17/162900.html

このゲームを作る中でやったことの備忘録として、本記事をまとめました。
記事内のスクリーンショットや素材も、このゲームのものを使って説明しています。

0. そもそも Pixel Perfect ってなーに?

ドットがドットのまま映ること!

ひとことで言うと 「ドット絵の 1 ピクセルが、画面上でキレイな正方形のまま表示される状態」 のこと。

通常ドット絵は小さい解像度(たとえば 320×180)で描かれますが、実際にゲームが動作するモニターは 1920×1080 などと大きいため、表示するときに 拡大 が必要になります。

この拡大のやり方がマズいと、様々な見た目の問題が起きてしまいます。

症状 原因
ドットがにじむ・ぼける 拡大時にピクセル間の色が補間される
ドットのサイズがバラバラになる 非整数倍(例: 5.3倍)で拡大されて、あるドットは 5px、隣は 6px になる
カメラを動かすとガタつく カメラがサブピクセル(ドットとドットの間)に位置してしまい、フレームごとにドットの並びがブレる
ライトや UI だけぼける Sprite は Pixel Perfect でも、ライトや UI は別の解像度で描画される

Pixel Perfect のキモは「整数倍」

核心はシンプルに ドット絵を整数倍(2倍、3倍、4倍…)で拡大する こと!

 NG: 320 × 3.125 = 1000(中途半端 → ドットのサイズがガタガタ)
 OK: 320 × 3     =  960(ぴったり → 全ドット同じサイズ!)

整数倍で拡大することで、すべてのドットが同じサイズの正方形になります。

この記事でやること

Unity の Pixel Perfect Camera だけでも整数倍の表示は実現できます。
しかし、URP の 2D ライティングは 8bit(256段階)を超える高精度な色情報でライトを計算するため、そのまま画面に出すとグラデーションが滑らかすぎて、ドット絵の世界観から浮いてしまうことがあります。

この記事では、8bit の RenderTexture にいったん描画する ことで、ライトの階調をドット絵らしい荒さに落とす方法を採用します。この方法では 2 つのカメラ を使います。

カメラ 役割 解像度の固定方法 ポイント
メインカメラ Sprite やライトなどインゲームの描画 RenderTexture 8bit に出力することでライトもドット絵らしい階調になる
最終合成カメラ RenderTexture と UI を統合して画面に出力 Pixel Perfect Camera UI をここで描画することで、クリック・タップ座標がズレない

設定していく項目はこちら!

  1. Sprite の Import Settings を整える
  2. RenderTexture とメインカメラ を構築する
  3. TextMeshPro のフォントアセットをドット向けにする
  4. 最終合成カメラ を構築する

やったれ!!

1. Sprite を設定しよう!

まずは素材の設定からです。ここがズレていると、あとで何をやってもドットがにじみます。

Import Settings をこう変えよう

Sprite をクリックして Inspector を開き、以下の項目を確認してください。

項目 理由
Texture Type Sprite (2D and UI) そのままでOK
Pixels Per Unit (PPU) 1マスのドットサイズ ドットの「1マス」を Unity のワールドに揃えるため
Mesh Type Full Rect Tight だと端のピクセルが欠けることがあります
Filter Mode Point (no filter) Bilinear にするとドットがぼけます
Max Size 元画像の解像度以上 小さくすると縮小されてしまいます
Compression None 圧縮するとドットが汚くなります

PPU は全部揃えよう!

例えば、見下ろし型のゲームで1マスが 16×16 ベースのゲームなら 全 Sprite を PPU = 16 に統一してください。バラバラだと 1 ドットのサイズが素材ごとにズレて、いくら頑張っても揃わなくなります。

1マス という概念が存在しないゲームの場合は、100 で統一しても問題ありません。

Sprite Atlas を使うときも忘れずに

Atlas 側にも Filter Mode: PointCompression: None の設定があります。
Atlas の設定が元の Sprite の設定を上書きすることがあるので、Atlas 側を優先して確認してください。

2. RenderTexture とメインカメラを構築しよう!

Sprite やライトなど、インゲームの描画を低解像度の RenderTexture に出力する仕組みをつくります。

Step 1: RenderTexture をつくろう

Project ウィンドウで Create → Render Texture を選択し、以下のように設定します。

項目
Size ゲームの仮想解像度
Anti-aliasing None (1x)
Color Format R8G8B8A8_UNorm
Depth Stencil Format None(2D ならだいたい不要)
Wrap Mode Clamp
Filter Mode Point (no filter) ← ここは絶対 Point です!

この RenderTexture のサイズが、ゲーム世界の「ドット解像度」になります。

Step 2: メインカメラを設定しよう

メインカメラの Inspector で以下を設定します。

  • Output Texture → 作った RenderTexture をセット
  • Pixel Perfect Cameraつけません!

メインカメラには Pixel Perfect Camera は不要です。
RenderTexture のサイズ自体がドット解像度なので、そこに描画するだけで自然とドット解像度になります。

orthographicSize は RenderTexture の高さと PPU から計算して手動で設定してください。

orthographicSize = RenderTexture の高さ ÷ PPU ÷ 2

例: 高さ 180、PPU 16 の場合 → 180 ÷ 16 ÷ 2 = 5.625

2D ライトについて

URP の Light2D(Global Light、Point Light、Freeform Light など)は、
メインカメラに映っていれば RenderTexture にそのまま描画されます。

URP の 2D ライティングは 8bit より高精度に計算されるため、直接画面に出すとグラデーションが滑らかすぎてドット絵感が薄れます。R8G8B8A8_UNorm の RenderTexture を経由する ことで、計算結果が 8bit(256段階)に丸められ、ドット絵らしいカクカクした階調のライトになります。


※どちらもPixel Perfectではあるものの、ライティングの計算が高精度すぎることでドット絵に見えずらい

3. TextMeshPro のフォントアセットを設定しよう!

ドットフォントを TextMeshPro で使うと、デフォルトでは SDF レンダリングによってぼけてしまいます。ビットマップフォントとして生成 することで解決しましょう!

フォントアセットをつくろう

Window → TextMeshPro → Font Asset Creator を開いて、以下のように設定します。

項目 設定 ポイント
Source Font File 使いたいドットフォント(.ttf / .otf)
Sampling Point Size フォントの原寸サイズ(例: 8, 12, 16 ドットフォントがデザインされたサイズを指定します
Padding ↑と同様の値 小さすぎる値だと、文字が綺麗に描画されません
Atlas Resolution グリフ数に合わせて調整(例: 256 × 256
Character Set 必要な文字セットを選択 日本語を含める場合は Custom Characters でまとめて指定したり、生成後にModeをDynamicにします
Render Mode RASTER_HINTED または RASTER ここが一番大事! SDF 系を選ぶとドットがぼけます

設定できたら Generate Font Atlas → Save でアセットを保存してください。

TMP コンポーネントの設定

Font Asset   : 作ったアセット
Font Size    : Sampling Point Size の整数倍(等倍 / 2倍 / 3倍 ...)

Font Size を 1.5 倍などの非整数倍にすると、リサンプリングが走ってドットが崩れます。必ず整数倍 にしてください。

4. 最終合成カメラを構築しよう!

メインカメラの RenderTexture と UI を統合して、画面に出力するカメラをつくります。

全体の構成

┌─────────────────────────────────────────────────
│ 最終合成カメラ
│ Pixel Perfect Camera (Upscale RT + Window Box)
│ ┌───────────────────────────────────────────────
│ │ Canvas (Screen Space - Camera)
│ │  ┌────────────────────────────────────────────
│ │  │ RawImage
│ │  │ (RenderTexture を表示)
│ │  └────────────────────────────────────────────
│ │  ┌────────────────────────────────────────────
│ │  │ UI 要素
│ │  │ (HP バー、メニュー等)
│ │  └────────────────────────────────────────────
│ └───────────────────────────────────────────────
└─────────────────────────────────────────────────
             ↑ 出力解像度に収まる最大の整数倍に自動で拡大
┌─────────────────────────────────────────────────
│ 低解像度 RenderTexture (例: 320×180)
│ ┌────────┐  ┌───────┐
│ │ Sprite │  │ Light │
│ └────────┘  └───────┘
│      ↑ ドット解像度で描画!
│      メインカメラ (出力先: RT)
└─────────────────────────────────────────────────

Step 1: 最終合成カメラをつくろう

新しいカメラを作成し、Pixel Perfect Camera コンポーネントを追加します。

カメラの設定:

  • Clear Flags: Solid Color(黒)
  • Culling Mask: UI レイヤーのみ(ゲームオブジェクトは映さない)

Pixel Perfect Camera の設定:

項目 設定 解説
Assets Pixels Per Unit Sprite の PPU と同じ値
Reference Resolution RenderTexture と同じサイズ
Crop Frame Window Box 整数倍に収まらない領域は、描画せず真っ黒になります
Grid Snapping Upscale Render Texture 整数倍での拡大を有効にします

Window Box を選ぶと、画面サイズがドット解像度の整数倍にぴったり合わない場合に、
余った部分が黒い帯(ピラーボックス / レターボックス)で埋められます。

Step 2: Canvas と RawImage を設定しよう

最終出力用の Canvas を作成し、RenderTexture を画面に映す仕組みをつくります。

Canvas の設定:

項目
Render Mode Screen Space - Camera
Pixel Perfect TRUE
Render Camera 最終合成カメラ

Canvas Scaler の設定:

項目
UI Scale Mode Scale With Screen Size
Reference Resolution ドット絵の解像度
Screen Match Mode Expand
Reference Pixels Per Unit SpriteのPPUと揃える

RawImage の設定:

Canvas の子要素として RawImage を作成します。

項目
Texture メインカメラの RenderTexture
Rect Transform 中央揃え、サイズは RenderTexture と同じ
Raycast Target OFF

これで、メインカメラが描画したドット絵が、最終合成カメラを通して整数倍で画面に表示されます!

Step 3: UI を配置しよう

UI はこの RawImage より下側に配置します。Screen Space - Camera の Canvas 以下であるため ピクセルパーフェクトな UI & 正確なクリック判定 が両立できます!

項目
Rect Transform 中央揃え、サイズは RenderTexture と同じ

まとめ

やること ポイント
0. Pixel Perfect を知る 整数倍で拡大して、全ドットを同じサイズに揃える
1. Sprite を設定 PPU 統一、Point Filter、圧縮なし
2. RenderTexture とメインカメラを構築 8bit の RT にインゲーム(Sprite・ライト)を描画。ライトの階調もドット絵らしくなる
3. TMP フォントアセットを設定 Render Mode を RASTER にする、Font Size は整数倍のみ
4. 最終合成カメラを構築 Pixel Perfect Camera + Canvas + RawImage + UI で画面に出力

Pixel Perfect の基本は 「すべての描画を同じドット解像度に通す」 こと。

メインカメラの RenderTexture でインゲームを、最終合成カメラの Pixel Perfect Camera で UI を、それぞれきっちり整数倍に揃えれば、全部まとめてドットの世界に収まります!

💡 Tips: ポストプロセスの使い分け

この構成ではカメラが 2 つあるため、どちらのカメラでポストプロセスを有効にするか で効果の範囲が変わります。

メインカメラで ON にした場合

ポストプロセスの効果は RenderTexture に描画されるゲーム画面(Sprite・ライト)にだけ かかります。UI には影響しません。

例えば「ゲーム画面だけヴィネットを暗くしたいけど、UI はくっきり見せたい」といったケースに向いています。

最終合成カメラで ON にした場合

ポストプロセスの効果は UI を含む画面全体 にかかります。

例えば「メニューを開いたときに画面全体をぼかしたい」といったケースに向いています。

2 重がけに注意!

両方のカメラで同じ効果を ON にすると 2 重にかかってしまいます
例えば Bloom を両方で有効にすると、想定以上に光が飛んでしまいます。

「このカメラは何を描画しているのか」を常に意識して、効果の配置先を決めてください。

Volume の分離方法:

  • Culling Mask: 各カメラが描画するレイヤーを分ける
  • Volume Mask(URP): カメラごとに影響を受ける Volume を指定できます

これらを使って、メインカメラ用の Volume と最終合成カメラ用の Volume をきっちり分離しておくと、意図しない 2 重がけを防げます。

💡 Tips: Transform の Scale と Rotation は使わない!

Pixel Perfect な見た目を維持するために、Sprite の Scale や Rotation は原則さわらない ようにしましょう。

Rotation(回転)は確実にジャギる

ドット絵を 45° や 30° など中途半端な角度で回転させると、ピクセルの並びが崩れて 必ずジャギ(ギザギザ)が発生します。これはどんな設定をしても避けられません。

回転したドット絵が必要な場合は、回転した状態のスプライトを別途用意する のが確実です。

Scale(拡縮)もジャギりやすい

単純な矩形(四角いUI枠など)であれば目立たないこともありますが、
複雑な形状のスプライトを拡縮すると 高確率でドットの並びが崩れます

拡大・縮小が必要な場合も、サイズ別のスプライトを用意する のがおすすめです。


参考になれば、Like❤️お願いします!

Discussion