なぜ要るか
ここまでで、動くキャラができました。でもまだキャラとゲームが別々です。
この術でつなぎます。そして大事なのは、置くだけでは「いる」ことにならないという点です。
画面に絵が貼ってあるだけなら、それは飾りです。こちらの手に反応して初めて「相手がいる」と感じます。 今回やるのはそこまでです。
使う道具: なし(ここまでで作ったものだけ)
AIプロンプト
この術の作業をAIへ任せるときは、これをそのまま貼ってください。
index.html にキャラを立たせて、こちらの手に反応させて。
1) タイトルの下、相手の手札の上にキャラの絵を置く。幅は240px。
絵には、どんな相手なのかが伝わる alt を付ける。
2) 表情の差し替えとセリフ表示をまとめた react(表情, 言葉) を1つ作って、
反応はすべてこの関数から出す。呼び出し側には書かない。
表情は ふだん / 驚き / 勝ち / 負け の4つ。
3) reveal() の最後で、自分と相手が同じ的にぶつかったかどうかで一言を変える。
finish() で、勝ち・負け・引き分けごとに表情と一言を出す。
反応はこの3つだけにして、増やさないで。それ以外は変えないで。
表情の絵のファイル名は、自分のキャラのものに置き換えてください。
どこに置くかは、もう決まっています
3章で作った画面は、上から順にこうなっています。
タイトル
相手の手札
的が3つ(上が相手の札、下が自分の札)
自分の手札
キャラは「相手の手札」の上に置きます。 迷う必要はありません。
理由は、画面の上が相手の陣地だと決まっているからです。そこにキャラを置けば、説明なしで「この手札はこの忍者のものだ」と伝わります。
置いた画面がこれです。
透過しておいた効果がここで出ます
この画面をよく見てください。キャラの周りに四角い枠が見えません。
4-1で背景を透過したからです。透明な部分にはゲームの背景がそのまま出るので、置くだけで馴染みます。
そして、ここが透過にしておいた本当の価値です。背景を変えても、そのまま使えます。
いまのゲームの背景は #101820 の暗い色です。もし6章で明るい背景に変えたくなったら、透過されていればキャラは1枚も作り直しません。 背景色に合わせて作っていたら、その1色専用の絵なので全部作り直しです。
さらに、この絵は他のゲームにも持っていけます。 8章で公開したあと、別の作品で同じキャラを使うときにも効きます。
透過に払った工程1つが、ここから先ずっと効き続けます。
やってみる
相手の手札の上に置く
<h1>三つの的</h1> <div class="chara" id="chara"> <img id="charaImg" src="assets/koyori-guide.png" alt="白と藍の忍装束を着た、藍色の髪の少女。対戦相手のこより"> <p class="fukidashi" id="serifu"></p> </div> <p class="label">相手の手札</p>altは、絵の代わりに読まれる説明文です。絵が表示できなかったときに出るほか、画面を読み上げて使う人にはこの文が読まれます。p class="fukidashi"は、セリフを出すための空の場所です。中身は次の手順で入れます。altを必ず書いてください。絵が読めない人にも相手がいることが伝わります。 ここは手を抜くところではありません。反応の出口を1つにする
ここが一番大事です。反応させる場所を増やす前に、反応そのものを1箇所にまとめます。
var KAO = { guide: 'koyori-guide.png', odoroki: 'koyori-odoroki.png', kachi: 'koyori-kachi.png', make: 'koyori-make.png', }; function react(kao, words) { el('charaImg').src = 'assets/' + KAO[kao]; el('serifu').textContent = words || ''; }KAOは、表情の呼び名と絵のファイル名の対応表です。react()はその表を引いて絵を差し替え、textContentでセリフの文字を入れ替えます。やっているのは、絵1枚とセリフ1行の書き換えだけです。これで、呼ぶ側は
react('kachi', 'わたしの勝ち!')の1行だけで済みます。4-2で作った傾きも、4-3で作った口パクも、この関数の中から呼びます。 呼び出し側には書きません。出口を1つにしておかないと、あとで「口パクだけ止めたい」となったときに全箇所を探すことになります。
AGENTS.mdの「同じ処理を2箇所に書かない」が、そのまま効く場所です。札を開いた瞬間に反応させる
3-1で作った
reveal()の最後に、2行足すだけです。if (myMove.target === cpuMove.target) { react('odoroki', 'おっと、同じ的でぶつかったね。'); } else { react('guide', 'そこに置いたか……。'); }reveal()は、伏せて置いた札を0.5秒後にひらく処理です。ひらいた直後は、自分と相手がどこに置いたかが確定した瞬間なので、反応を出すのにいちばん都合がいい場所になります。同じ的にぶつかったかどうかで、言うことを変えています。 ここが「見ている」感じの正体です。盤面のどこにも書いていない情報を、キャラが言葉にしています。
相手の的がリードした場面がこれです。
この一言があるだけで、盤面の数字が「事件」になります。
決着で締める
finish()は、手札を置き切ったときに勝敗を出す処理です。その中で、勝敗ごとに1行です。if (myTargets > cpuTargets) { react('make', 'まいった。次は勝つよ。'); } else if (cpuTargets > myTargets) { react('kachi', 'わたしの勝ち!'); } else { react('guide', 'いい勝負だったね。'); }自分が勝ったときに、相手が悔しがる。 これが決着の手応えを作ります。勝敗の文字だけでは出ない部分です。
3回遊ぶ
open index.html判定はこれだけです。相手がいる感じがするか。
していなければ、反応が足りません。次を読んでください。
保存する
git add . git commit -m "キャラをゲーム画面に立たせて、勝敗に反応させた" git push
反応は「3つまで」にします
僕が最初にやった失敗です。全部の出来事に反応させたら、うるさくなりました。
札を選んだら喋る、勝ったら喋る、負けたら喋る、引き分けでも喋る。常に動いているキャラは、逆に無視されます。
いま入れているのは3つだけです。
| 出来事 | 反応 |
|---|---|
| 札を開いた | 一言だけ。同じ的にぶつかったかで変える |
| 相手が的を取った | 喜ぶ顔に変える |
| 決着 | 表情と一言で締める |
1手ごとに大げさに動かさないのが要点です。開いた瞬間の一言は短く、決着だけ強く。強弱があるから、決着が効きます。
ファイルが増えました
3-2で「1ファイルで動かす」と決めました。いまはこうです。
index.html
assets/koyori-guide.png
assets/koyori-odoroki.png
assets/koyori-kachi.png
assets/koyori-make.png
表情の数だけ増えます。 これは想定どおりの増え方です。
大事なのは、まだサーバーが要らないことです。このフォルダごと置けば、ダブルクリックで動きます。人に渡すときはフォルダごと送ればいい、それだけの違いです。
8章で公開するときも、このフォルダを置くだけで済みます。外部から何も読み込んでいないので、公開が一番簡単な形のままです。
3Dモデルを作った人はどうするか
4-4と4-5で3Dを作った人向けです。いま作った1ファイル構成に、3Dをそのまま入れることはできません。
3Dを画面に出すには、ブラウザで3Dを描くための部品(three.js など)が必要です。それを読み込むと「外部のものを読まない」という前提が崩れます。3-2で避けると決めたところです。
選べる道は2つです。
| やり方 | 向いている場合 |
|---|---|
| 3Dモデルを画像に書き出して、この術と同じ仕組みで置く | 最初の1本を完成させたい |
| three.js を入れて3Dのまま出す | 3Dで動かすことが目的 |
僕は前者を推します。 Blenderで正面・斜め・横の3方向を書き出せば、それは差分画像です。ここまで作った仕組み(差し替えと傾き)が全部そのまま使えます。
3Dのまま出すのは、この講座の枠を1つ超えます。まず1本完成させて、次の作品で挑んでください。
つまずきどころ
キャラが大きすぎて手札が見えない
.chara の width を下げてください。ただし4-2で書いたとおり、小さくすると傾きが見えなくなります。 240pxあたりが折り合いです。
キャラの周りに四角い枠が見える
透過できていません。背景が単色で残っています。4-1のキー抜きをやり直してください。
キャラの輪郭に緑や白の縁が見える
透過は効いているものの、縁に色が残っています。4-1で書いたとおり、単純な色置換で抜くとこうなります。soft-matte と despill が入った処理で抜き直してください。
スマホで縦に長くなりすぎる
画面幅に合わせます。
.chara { width: min(240px, 45vw); }
vw は画面の横幅を100とした単位で、45vwなら画面幅の45%です。min() は小さい方を採るので、広い画面では240px、狭い画面では画面幅に合わせた幅になります。
キャラが動きすぎてうるさい
反応を減らしてください。迷ったら1手ごとの一言を消すのが効きます。決着だけ残せば、うるささは消えて手応えは残ります。
claude -p "index.html のキャラの反応が多すぎるので、札を開いた直後の一言だけやめて、決着のときだけ反応するようにして。react() の呼び出しを消すだけにして、react() 自体は残して。"
反応しているのか分からない
表情の差が小さいです。普段の顔と、決着の顔が似すぎていないかを見てください。似ているなら、決着用にもっと振り切った絵を1枚足すのが早いです。react() の中から呼ぶ faceTo の角度を上げるのも効きます。
Windows の場合
コードは同じです。open index.html の代わりに start index.html を使ってください。
次の一歩
誰かに1回遊んでもらってください。 そして相手の顔を見てください。
キャラが反応した瞬間に笑うか、何も起きないか。そこが「いる」と「貼ってある」の差です。 反応が弱ければ、上の3つを調整してください。
これで4章は終わりです。数字だけだった画面に、息をして、まばたきをして、勝ったら黙って上を向く相手が立ちました。次の章では音を付けて、手触りを上げていきます!
ここまでの三つの的
この講座は、見本ゲーム「三つの的」を術ごとにすこしずつ育てながら進みます。下にあるのはこの術を終えた時点の実物です。そのまま遊べます。

