Toon Toneという色合わせブラウザゲームを作った話
最近、Toon Tone という小さなブラウザゲームを作りました。
Toon Tone は、画面に表示されたターゲットカラーに対して、ユーザーが Hue / Saturation / Brightness のスライダーを調整し、できるだけ近い色を作るゲームです。
1ゲームは10ラウンドで構成されていて、各ラウンドごとに色の近さに応じてスコアが計算されます。
作ったもの
ゲームの基本的な流れは次のようになっています。
- ターゲットカラーを表示する
- ユーザーが HSB スライダーを調整する
- 選択中の色をリアルタイムでプレビューする
- Submit Guess を押す
- ターゲットカラーとの差分を計算してスコア化する
- 10ラウンド分プレイして合計スコアを出す
かなりシンプルなゲームですが、実際にプレイしてみると、少しの Hue や Brightness の違いで印象がかなり変わるので、意外と難しいです。
最初のアイデア
最初は、キャラクターを使った色記憶ゲームを作ろうとしていました。
例えば、
- キャラクターの服の色
- 帽子の色
- アクセサリーの色
などを数秒間見せて、その色をユーザーが再現するというものです。
ただ、実装を考えると画像アセットの管理がかなり大変でした。
キャラクターの一部だけ色を変えるには、最低でも以下のようなレイヤーが必要になります。
- ベース画像
- 色を変える部分のマスク画像
- 影やハイライト用のレイヤー
- それらを完全に同じ座標で重ねる仕組み
AIで画像を生成しても、base / mask / shading の位置が少しずつズレてしまい、Canvasで合成したときに不自然になりました。
そのため、最初のバージョンではキャラクター要素を一旦やめて、色カードだけを使うシンプルなゲームにしました。
MVPでは色カード方式にした
現在のバージョンでは、キャラクターではなく、左右の色カードを使っています。
- 左側: ターゲットカラー
- 右側: ユーザーが調整している色
- 下部: Hue / Saturation / Brightness のスライダー
この構成にしたことで、画像レイヤーやマスク処理に悩まず、ゲームのコア部分だけを先に検証できるようになりました。
HSBを使った理由
色を操作する方法としては RGB もありますが、ゲームとしては HSB の方が直感的だと感じました。
RGB の場合、ユーザーは Red / Green / Blue の値をそれぞれ調整する必要があります。これは開発者には馴染みがありますが、一般ユーザーには少し分かりにくいです。
一方で HSB は以下のように理解しやすいです。
- Hue: 色相
- Saturation: 鮮やかさ
- Brightness: 明るさ
色を見ながら調整するゲームでは、HSB の方が操作感が自然でした。
HSBからRGBへの変換
スライダーでは HSB を扱いますが、画面表示やスコア計算では RGB に変換しています。
実装イメージはこのような感じです。
type HSB = {
h: number; // 0 - 360
s: number; // 0 - 100
b: number; // 0 - 100
};
type RGB = {
r: number;
g: number;
b: number;
};
function hsbToRgb({ h, s, b }: HSB): RGB {
s /= 100;
b /= 100;
const k = (n: number) => (n + h / 60) % 6;
const f = (n: number) =>
b - b * s * Math.max(Math.min(k(n), 4 - k(n), 1), 0);
return {
r: Math.round(255 * f(5)),
g: Math.round(255 * f(3)),
b: Math.round(255 * f(1)),
};
}
