結論から言うと、顔を動かすのに絵は何枚も要らないんです。 1枚を板として傾けるだけで、ちゃんと「向いた」ように見えます。
なぜ要るか
前の術で1枚絵ができました。置いてみると、ちゃんとゲームらしくなります。
でも動かないんですよね。 何度遊んでも、まったく同じ顔でこちらを見ています。
ここで多くの人が「動かすには何十枚も絵が必要だ」と考えて止まります。違います。 1枚のままで十分です。顔は左右上下に向けられます。しかも追加の道具は要らないんです。
使うのはCSSの傾きだけです。3行で済みます。
使う道具: なし(1枚絵とブラウザだけ)
AIプロンプト
この術の作業をAIへ任せるときは、これをそのまま貼ってください。
index.html のキャラ絵に、カーソルを追って顔が向く動きを付けて。CSSの perspective と rotateY/rotateX で、絵は1枚のまま。振り幅は左右14度・上下10度、動きは0.4秒の ease-out。それ以外は変えないで。
振り幅の14度と10度は、自分の絵で気持ちよかった数字に置き換えてください。
板を傾けているだけです
先に仕組みを言います。やっているのは、絵を板として傾けることだけです。
顔のパーツを動かしているわけではないんですね。1枚の板を、奥行きのある空間に置いて回している。それだけです。
なのに、なぜ顔が「向いた」ように見えるのか。人間は、遠近が付いた顔を「向き」として読むからです。左側が縮んで右側が広がれば、脳が「左を向いた」と解釈してくれるんです。
実際にやったものがこれです。同じ char.png 1枚で、左向きと右向きです。
2枚とも同じ画像ファイルです。 傾きの角度だけが違います。
先に、この方法の限界を書いておきます
うまくいっているように見えますが、このやり方は板を傾けているだけです。だから効きやすいものと、効きにくいものがあります。
同じ14度を、格子の板と顔に掛けたものを並べてみます。
板の方は、ちゃんと「傾いた板」に見えます。 奥側が縮んで、手前が残る。それ以上でも以下でもないので、見ている側も違和感を持ちません。
顔の方は、そうはいきません。 同じ角度なのに「向いた」というより「横に縮んだ」ように見える瞬間があります。原因は角度ではなく、人が顔を見慣れすぎていることです。目と目の間隔、鼻の位置、輪郭の左右差。ほんの少しの歪みでも、脳が先に気づいてしまうんですね。
だから、こう使い分けてください。
| 対象 | 板を傾けるやり方 |
|---|---|
| 札、盤、看板、背景の壁 | よく効きます。 迷わず使ってください |
| キャラクターの顔 | 角度を弱めに。 14度より、8〜10度の方が安全です |
この術ではキャラに使いますが、角度を控えめにして、動く時間(0.4秒)で「首を回した」と感じさせるのが要点になります。角度で押し切ろうとすると、必ず歪みが目立ってしまいますよ。
やってみる
絵を「奥行きのある入れ物」に入れる
index.htmlのキャラの部分を、こうします。<div class="chara"> <img id="charaImg" src="char.png" alt="対戦相手のこより"> </div>divは中身をまとめるための箱、imgが絵そのものです。箱で囲んでおくと、箱と絵に別々の動きを付けられます。CSSはこれだけです。3-2で決めたとおり1ファイル構成なので、
index.htmlの中にある<style>の内側へ書きます。.chara { width: 240px; perspective: 600px; } .chara img { width: 100%; display: block; transition: transform .4s ease-out; }perspectiveが奥行きです。 これを書くと、その箱の中が「手前と奥がある空間」として扱われます。これが無いと、傾けても平べったく潰れるだけで「向いた」ようには見えないんです。数字が小さいほど遠近が強くつきます。向きを変える関数を1つ書く
var charaImg = document.getElementById('charaImg'); function faceTo(x, y) { charaImg.style.transform = 'rotateY(' + (x * 14).toFixed(1) + 'deg) rotateX(' + (-y * 10).toFixed(1) + 'deg)'; }こちらは動きなので、
index.htmlの<script>の内側へ書きます。1行目で、charaImgという名前を付けた絵をコードから触れる形で取り出しています。rotateYが縦の軸を中心にした回転で左右の向きになり、rotateXが横の軸を中心にした回転で上下の向きになります。degは角度の単位です。xとyは -1 から 1 で渡します。faceTo(1, 0)で右を向き、faceTo(-1, 0)で左を向きます。14と10が振り幅です。 ここが今回いちばん大事な数字なので、あとで自分で触ってください。カーソルを追わせる
document.addEventListener('mousemove', function (e) { var x = (e.clientX / window.innerWidth) * 2 - 1; var y = (e.clientY / window.innerHeight) * 2 - 1; faceTo(x, y); });画面のどこにカーソルがあるかを -1〜1 に変換して、そのまま渡しています。
e.clientXがカーソルの横位置、window.innerWidthが画面の横幅です。割ると 0〜1 になり、2倍して1を引くと、左端が -1、中央が 0、右端が 1 になります。mousemoveは、マウスが動くたびにこの中身を呼び出す仕掛けです。開いて、マウスを動かす
open index.htmlブラウザにゲームの画面が出ます。その上でカーソルを左右に振ってください。顔がついてきます。
ここで「思ったより動かない」と感じたら、次を読んでください。それが正常な反応なんです。
仕組みが分かったので、次のキャラからはまとめて頼めます。
claude -p "index.html のキャラ絵に、カーソルを追って顔が向く動きを付けて。CSSの perspective と rotateY/rotateX で、絵は1枚のまま。振り幅は左右14度・上下10度、動きは0.4秒の ease-out。それ以外は変えないで。"数字まで指定しているのに注目してください。 この術で自分の手で見つけた数字は、そのまま次からの指示になるんですね。
保存する
git add . git commit -m "1枚絵の顔が左右上下を向くようにした" git push
小さく表示すると、傾きは見えません
これは僕が実際にやって気づいたことです。
最初、キャラを 180px で表示していました。角度は同じ14度です。まったく分かりませんでした。 動いている実感がゼロでした。
同じコードのまま 320px にしたら、はっきり向くようになったんです。
理由は単純で、傾きは画面上の「ずれた量」でしか知覚できないからです。絵が小さければ、14度傾けてもずれる量は数ピクセルしかないんですね。
対処は3つあります。上から順に試してください。
| 対処 | やり方 |
|---|---|
| 大きく表示する | いちばん効く。240px以上を目安にする |
| 角度を強める | 14 を 20 くらいまで上げる |
| 顔を大きく写す | 絵の余白を切り詰めて、顔の割合を上げる |
逆に、角度を30度以上にすると「板が回った」とバレます。 顔の向きではなく紙が回転しているように見えます。上限は20度くらいと考えてください。
動きの「時間」で生きて見えます
もう1つ、効くのに見落とされやすいものがあります。動く速さです。
上のCSSにこれが入っています。
transition: transform .4s ease-out;
これを消すと、顔がカクッと瞬間移動します。同じ角度でも、生き物に見えなくなるんですね。
0.4秒かけて、しかも終わりが緩やかに減速する(ease-out)。これで「首を回した」ように見えます。角度より、この0.4秒の方が効きます。
本格的にやると何が違うか
正直に書きます。この方法は板を傾けているだけなので、限界があります。
本物の3Dのように見せるには、絵をパーツに分解して、パーツごとに違う量だけ動かします。前髪は大きく、顔は中くらい、後ろ髪は小さく。このずれが立体感を作るんです。
こういう「1枚絵をパーツに分けて動かす」やり方には、既にできあがった仕組みがあります。代表的なのが Live2D です。イラストを分解して動かすための道具で、ゲームや配信で広く使われています。
僕は自分で作った simple-puppet という仕組みを使っています。see-through というAIで1枚絵をレイヤーへ分解し、それを simple-puppet で動かす形です。実際、この講座の公開版の三つの的でも、こよりを simple-puppet へ移して、瞬きと首の揺れを入れています。
顔をきちんと動かしたくなったら、板を傾けるのをやめて、この層へ移ってください。 角度を詰めるより、そちらの方が早く自然になりますよ。
別作品のCNPトレカで使っている咲耶を分解したときは、こういう単位に分かれました。
前髪 / 後ろ髪 / 顔 / 白目 / 瞳 / まつげ / 眉 / 口 / 首 / 上衣 / 下衣 / 手 / 足
隠れて見えない部分まで描き足された状態で出てきます。だから顔を横に向けても、後ろ髪の裏に穴が空きません。
レイヤーは、絵を何枚かの重ねた板に分けたもののことです。分解にはGPU(画像の計算が得意な部品)を時間貸しで借りる必要があるので、1キャラあたり数百円と1時間くらいかかります。
最初の1本には要りません。 板を傾けるだけで、キャラは十分に生きるんです。パーツ分解は「このキャラで長く遊ぶ」と決めてからで間に合います。
つまずきどころ
傾けても何も変わらない
perspective が抜けています。これは親要素(.chara)に書きます。img 側に書いても効きません。
呼吸のような上下運動と喧嘩する
キャラを上下にゆっくり揺らす(呼吸)を足すと、傾きが消えることがあります。transform は1つしか持てないためです。あとから書いた方が前を打ち消します。
解決は入れ子にすることです。外側の div で揺らし、内側の img で傾ける。 上のHTMLが最初から2階層になっているのはこのためです。
顔ではなく体ごと傾いてしまう
そのとおりで、絵ごと傾いています。気になる場合は rotateX を弱めて(10 → 5)、左右の動きを主にしてください。左右は違和感が出にくいです。
スマホで動かない
mousemove はカーソルがある環境のイベントです。スマホでは指の位置で動かすか、ゲームの状況(勝った・負けた)に応じて向きを変える方が自然です。状況で動かす方は4-6でやります。
Windows の場合
コードは同じです。open index.html の代わりに start index.html を使ってください。
次の一歩
14 と 10 の数字を変えて、自分の絵で気持ちのいい角度を探してください。
20 まで上げてみると、どこから「板が回った」に見え始めるかが分かります。その境目を1回見ておくと、以降どのキャラでも迷いません。
顔が向いただけで、もう相手が「見ている」感じが出ます。次はまばたきと口を付けて、生きているところまで持っていきます!
ここまでの三つの的
この講座は、見本ゲーム「三つの的」を術ごとにすこしずつ育てながら進みます。下にあるのはこの術を終えた時点の実物です。そのまま遊べます。

