第4章 キャラクターの作り方/2 本目 / 全 6/約25分

1枚絵の顔を動かす

こより

絵は1枚のまま、少し傾けるだけで顔は向いて見えるよ。角度より、0.4秒という時間のほうが効く。

案内役 こより
この術でできるようになること

顔が左右上下に向く

画像は押すと原寸で開きます。

なぜ要るか

前の術で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つ並べたもの。左は顔が左を向き、右は顔が右を向いて見える

2枚とも同じ画像ファイルです。 傾きの角度だけが違います。

やってみる

  1. 絵を「奥行きのある入れ物」に入れる

    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 が奥行きです。 これを書くと、その箱の中が「手前と奥がある空間」として扱われます。これが無いと、傾けても平べったく潰れるだけで「向いた」ようには見えません。数字が小さいほど遠近が強くつきます。

  2. 向きを変える関数を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 は角度の単位です。

    xy-1 から 1 で渡します。faceTo(1, 0) で右を向き、faceTo(-1, 0) で左を向きます。

    1410 が振り幅です。 ここが今回いちばん大事な数字なので、あとで自分で触ってください。

  3. カーソルを追わせる

    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 は、マウスが動くたびにこの中身を呼び出す仕掛けです。

  4. 開いて、マウスを動かす

    open index.html
    

    ブラウザにゲームの画面が出ます。その上でカーソルを左右に振ってください。顔がついてきます。

    ここで「思ったより動かない」と感じたら、次を読んでください。それが正常な反応です。

    仕組みが分かったので、次のキャラからはまとめて頼めます。

    claude -p "index.html のキャラ絵に、カーソルを追って顔が向く動きを付けて。CSSの perspective と rotateY/rotateX で、絵は1枚のまま。振り幅は左右14度・上下10度、動きは0.4秒の ease-out。それ以外は変えないで。"
    

    数字まで指定しているのに注目してください。 この術で自分の手で見つけた数字は、そのまま次からの指示になります。

  5. 保存する

    git add .
    git commit -m "1枚絵の顔が左右上下を向くようにした"
    git push
    

小さく表示すると、傾きは見えません

これは僕が実際にやって気づいたことです。

最初、キャラを 180px で表示していました。角度は同じ14度です。まったく分かりませんでした。 動いている実感がゼロでした。

同じコードのまま 320px にしたら、はっきり向くようになりました。

理由は単純で、傾きは画面上の「ずれた量」でしか知覚できないからです。絵が小さければ、14度傾けてもずれる量は数ピクセルしかありません。

対処は3つあります。上から順に試してください。

対処 やり方
大きく表示する いちばん効く。240px以上を目安にする
角度を強める 1420 くらいまで上げる
顔を大きく写す 絵の余白を切り詰めて、顔の割合を上げる

逆に、角度を30度以上にすると「板が回った」とバレます。 顔の向きではなく紙が回転しているように見えます。上限は20度くらいと考えてください。

動きの「時間」で生きて見えます

もう1つ、効くのに見落とされやすいものがあります。動く速さです。

上のCSSにこれが入っています。

transition: transform .4s ease-out;

これを消すと、顔がカクッと瞬間移動します。同じ角度でも、生き物に見えなくなります。

0.4秒かけて、しかも終わりが緩やかに減速する(ease-out)。これで「首を回した」ように見えます。角度より、この0.4秒の方が効きます。

本格的にやると何が違うか

正直に書きます。この方法は板を傾けているだけなので、限界があります。

本物の3Dのように見せるには、絵をパーツに分解して、パーツごとに違う量だけ動かします。前髪は大きく、顔は中くらい、後ろ髪は小さく。このずれが立体感を作ります。

僕はここまでやる場合、see-through で1枚絵をレイヤーに分解して、simple-puppet で動かしています。実際、この講座の公開版の三つの的でも、こよりを simple-puppet へ移して、瞬きと首の揺れを入れています(講座の範囲外のおまけです。板を傾けるだけでも十分に成立します)。

別作品のCNPトレカで使っている咲耶を分解したときは、こういう単位に分かれました。

前髪 / 後ろ髪 / 顔 / 白目 / 瞳 / まつげ / 眉 / 口 / 首 / 上衣 / 下衣 / 手 / 足

隠れて見えない部分まで描き足された状態で出てきます。だから顔を横に向けても、後ろ髪の裏に穴が空きません。

レイヤーは、絵を何枚かの重ねた板に分けたもののことです。分解にはGPU(画像の計算が得意な部品)を時間貸しで借りる必要があるので、1キャラあたり数百円と1時間くらいかかります。

最初の1本には要りません。 板を傾けるだけで、キャラは十分に生きます。パーツ分解は「このキャラで長く遊ぶ」と決めてからで間に合います。

つまずきどころ

傾けても何も変わらない

perspective が抜けています。これは親要素.chara)に書きます。img 側に書いても効きません。

呼吸のような上下運動と喧嘩する

キャラを上下にゆっくり揺らす(呼吸)を足すと、傾きが消えることがあります。transform は1つしか持てないためです。あとから書いた方が前を打ち消します。

解決は入れ子にすることです。外側の div で揺らし、内側の img で傾ける。 上のHTMLが最初から2階層になっているのはこのためです。

顔ではなく体ごと傾いてしまう

そのとおりで、絵ごと傾いています。気になる場合は rotateX を弱めて(105)、左右の動きを主にしてください。左右は違和感が出にくいです。

スマホで動かない

mousemove はカーソルがある環境のイベントです。スマホでは指の位置で動かすか、ゲームの状況(勝った・負けた)に応じて向きを変える方が自然です。状況で動かす方は4-6でやります。

Windows の場合

コードは同じです。open index.html の代わりに start index.html を使ってください。

次の一歩

1410 の数字を変えて、自分の絵で気持ちのいい角度を探してください。

20 まで上げてみると、どこから「板が回った」に見え始めるかが分かります。その境目を1回見ておくと、以降どのキャラでも迷いません。

顔が向いただけで、もう相手が「見ている」感じが出ます。次はまばたきと口を付けて、生きているところまで持っていきます!

ここまでの三つの的

この講座は、見本ゲーム「三つの的」を術ごとにすこしずつ育てながら進みます。下にあるのはこの術を終えた時点の実物です。そのまま遊べます。

別の画面でこのゲームを開く

術の一覧へ戻る

メルマガ(無料)忖度なしのAI事情を、メールでお届けします。ご登録の方に、プレゼントをお渡ししています