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

まばたきと口パクをつける

こより

まばたきしないものは、生き物に見えないんだ。目を閉じた絵を1枚、0.1秒だけ差し替えてみて。

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

生きているように見える

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

なぜ要るか

前の術で顔が向くようになりました。でも、まだ目が一度も閉じません。

これは想像以上に効きます。まばたきをしないものは、生き物に見えません。 逆に言えば、まばたき1つ足すだけで「生きている」に切り替わります。

やり方は驚くほど単純です。目を閉じた絵を1枚用意して、0.1秒だけ差し替える。 それだけです。

使う道具: Codex の画像生成

AIプロンプト

この術の作業をAIへ任せるときは、これをそのまま貼ってください。

参照画像: char.png

参照画像のキャラクター(口寄せ使いの少女忍者こより)の顔ちがいを作る。
参照の造形と配色を変えない。

共通仕様(3枚すべて共通。顔の表情以外は同一の絵にする):
- 正面向き、全身、持ち物は巻物だけ
- flat solid #ff00ff background, no gradient, no shadow, no glow, no outline
- clean bold outlines, flat cel shading, chunky shapes, no fine loose hair strands
- 被写体はキャンバスの縁に接しない
- サイズ 1024x1024、文字は入れない

3枚作る。
1) char-magenta.png       ふだんの顔。目を開け、口を閉じている
2) char-me-magenta.png    1と同一の絵で、目だけ閉じている
3) char-kuchi-magenta.png 1と同一の絵で、口だけ小さく開けている

キャラの説明の部分は、自分のキャラのものに置き換えてください。

差分は2枚だけ用意します

必要なのはこの2枚です。

ファイル 何の絵 使いどころ
char-me.png 目を閉じた顔 まばたき
char-kuchi.png 口を開けた顔 しゃべっているとき

3枚目は要りません。 この2枚と元絵の計3枚で、まばたきも口パクも成立します。

実際に作った3枚がこれです。

忍者の少女の3枚の絵。左がふだんの顔、中央が目を閉じた顔、右が口を開けた顔

昔のゲームで「立ち絵の差分」と呼ばれていたものと同じ考え方です。全部を描き直さず、変わる部分だけ別の絵にする。

3枚は「1回でまとめて」頼みます

先に一番大事なことを書きます。3枚を別々に頼むと揃いません。

1枚できたあとで「同じ絵で目だけ閉じて」と追加で頼むと、髪の模様や目の大きさが微妙に変わります。生成AIは部分修正をしているのではなく、毎回描き直しているからです。

対策は単純で、最初から3枚まとめて1回で頼むことです。同じ指示の中で作られるので、揃う確率が段違いに上がります。

僕はこの順番を逆にして一度失敗しました。1枚ずつ足していったら、しゃべるたびに別人になりました。

やってみる

  1. 3枚まとめて作らせる

    4-1と同じキー色(マゼンタ)の背景で作ります。あとで一緒に透過するためです。1枚目の指定は4-1と同じで、参照画像に4-1の完成品を渡して造形を固定します。

    cd my-first-game
    codex exec "参照画像: char.png
    
    参照画像のキャラクター(口寄せ使いの少女忍者こより)の顔ちがいを作る。
    参照の造形と配色を変えない。
    
    共通仕様(3枚すべて共通。顔の表情以外は同一の絵にする):
    - 正面向き、全身、持ち物は巻物だけ
    - flat solid #ff00ff background, no gradient, no shadow, no glow, no outline
    - clean bold outlines, flat cel shading, chunky shapes, no fine loose hair strands
    - 被写体はキャンバスの縁に接しない
    - サイズ 1024x1024、文字は入れない
    
    3枚作る。
    1) char-magenta.png       ふだんの顔。目を開け、口を閉じている
    2) char-me-magenta.png    1と同一の絵で、目だけ閉じている
    3) char-kuchi-magenta.png 1と同一の絵で、口だけ小さく開けている"
    

    「顔の表情以外は同一の絵にする」が効きます。 これを入れると、体の位置まで揃います。

  2. 3枚を透過する

    4-1と同じやり方です。3枚に同じ処理をかけます。

    for n in char char-me char-kuchi; do
      python3 keyout.py --input $n-magenta.png --out $n.png --max-height 512
    done
    

    for は同じ処理を繰り返す書き方です。n の中身が char、char-me、char-kuchi と順に入れ替わり、3回同じ透過処理が走ります。終わると、背景が透明な3枚が並びます。

  3. 3枚を同じ枠で切る

    ここは飛ばさないでください。枚ごとに余白を自動で切り詰めると、位置がずれます。

    透過した3枚は、キャラの位置がぴったり同じでなければいけません。切り詰めるなら、3枚まとめて同じ枠で切ります。

    for n in char char-me char-kuchi; do
      magick $n.png -crop 400x400+43+62 +repage $n.png
    done
    

    -crop 400x400+43+62 は、左から43px・上から62pxの位置を起点に、400px四方を切り出すという指定です。+repage は切り出したあとの余りの情報を捨てて、絵の大きさを400x400へ揃えます。

    数字は自分の絵に合わせて決めます。3枚とも同じ数字を使うことだけが条件です。

    サイズも確認しておきます。

    sips -g pixelWidth char.png char-me.png char-kuchi.png
    

    3枚の横幅を表示するコマンドです。同じ数字が3つ並ぶのが正解です。

    サイズや位置が違うと、差し替えた瞬間に顔がガタッと動きます。 一番気づきにくい不具合なので、ここで潰します。

    ちなみに生成の生データは、僕の場合 1024 と頼んで 1254px で出てきました。指定どおりのサイズで出てこないことがある前提でいてください。

  4. まばたきを付ける

    var FACE = { open: 'char.png', shut: 'char-me.png', mouth: 'char-kuchi.png' };
    var talking = false;
    
    function blink() {
      if (!talking) {
        charaImg.src = FACE.shut;
        setTimeout(function () { if (!talking) charaImg.src = FACE.open; }, 120);
      }
      setTimeout(blink, 2000 + Math.random() * 3000);
    }
    setTimeout(blink, 1500);
    

    やっているのは差し替えです。charaImg.src に別のファイル名を入れると、そこに表示される絵が入れ替わります。setTimeout は「指定した時間だけ待ってから1回実行する」仕掛けなので、閉じた絵にして120ミリ秒後に開いた絵へ戻す、を繰り返しています。最後の行が自分自身をまた予約しているので、まばたきは止まりません。

    数字は2つだけです。

    • 120 ミリ秒 = 目を閉じている時間
    • 2000〜5000 ミリ秒 = 次のまばたきまでの間隔

    間隔をランダムにするのが要点です。 きっちり3秒ごとに閉じると、生き物ではなく機械に見えます。

  5. 口パクを付ける

    function talk(ms) {
      talking = true;
      var openMouth = false;
      var timer = setInterval(function () {
        openMouth = !openMouth;
        charaImg.src = openMouth ? FACE.mouth : FACE.open;
      }, 150);
      setTimeout(function () {
        clearInterval(timer);
        talking = false;
        charaImg.src = FACE.open;
      }, ms);
    }
    

    setInterval は「指定した間隔で繰り返す」仕掛けで、clearInterval でその繰り返しを止めます。talking は、いましゃべっている最中かどうかを覚えておくための目印です。

    talk(1200) で1.2秒しゃべります。150ミリ秒ごとに口の開閉を繰り返すだけです。

  6. 呼吸を足す

    これは絵が要りません。CSSだけです。効果は3つの中でいちばん高いです。

    .chara {
      animation: kokyu 4s ease-in-out infinite;
    }
    @keyframes kokyu {
      0%, 100% { transform: translateY(0); }
      50%      { transform: translateY(-5px); }
    }
    

    @keyframes は動きの型です。0%から100%までを1回分として、途中でどう変わるかを書きます。ここでは50%の時点だけ5px上へ持ち上げているので、上がって下がる動きになります。infinite はそれを止めずに繰り返す指定です。

    4秒かけて5px上下するだけです。止まっているはずのキャラが、静止画に見えなくなります。

  7. 開いて眺める

    open index.html
    

    何も操作せず、10秒ほど見てください。勝手にまばたきします。

    ゲーム画面の中で目を閉じた瞬間がこれです。

    ゲーム画面。上部の忍者の少女が目を閉じている

  8. 保存する

    git add .
    git commit -m "まばたきと口パクと呼吸をつけた"
    git push
    

それでもズレたときの考え方

まとめて頼んでも、細部は完全一致しないことがあります。目の大きさが少し違う、口の位置が1pxずれる、といった程度です。

対処を、効く順に書きます。

対処 効果
気にしない まばたきは0.1秒。人間はその差に気づけない
3枚まとめてもう一度作る 揃った組が出るまで引く。1枚だけ足すより速い
画像編集で目だけ差し替える 完全に揃うが手間がかかる

1番目を推します。 実際に動かしてみると、まばたきの0.1秒で細部が違うことは、まったく分かりません。

こよりでも実際に起きました。口を開けた差分が、口を頬の位置に描いて返ってきたのです。しゃべるたびに口が右へ飛ぶので、開いた口だけを切り出して正しい位置へ貼り直しました(3番目のやり方)。位置のズレは、絵の中の口の位置がズレていても起きます。 差し替えの仕組みを疑う前に、絵そのものを拡大して見てください。

ただし口パクは要注意です。 1.2秒間ずっと開閉するので、目の大きさが違うと「しゃべる時だけ別人」に見えます。口の差分だけは、少し粘って作り直す価値があります。

AIに「変更部分だけ合成して」と頼んではいけません

賢い解決を思いついたので試しました。「生成した絵から変わった部分だけを切り出して元絵に貼れ」と頼んだのです。

絵が壊れました。 顔の模様が消え、目のあった場所が黒く潰れた画像が出てきました。切り出す範囲の判断が雑になるためです。

素直に生成した3枚をそのまま使う方が、結果が良かったです。 凝った工程を足すと、そこが新しい壊れどころになります。

つまずきどころ

まばたきと口パクが喧嘩する

まばたきの最中に口パクが始まると、絵が飛び跳ねます。上のコードに talking という旗があるのはこのためです。しゃべっている間はまばたきを止めます。

旗を消して試してみると、どう壊れるかが分かります。1回見ておくと、以降のキャラでも同じ構造にできます。

別のタブに切り替えたら止まった/遅くなった

これは正常です。 ブラウザは、見えていないタブのタイマーを間引きます。僕の測定でも、裏に回した状態では150ミリ秒の口パクが500ミリ秒間隔になりました。

戻ってくれば元に戻ります。直す必要はありません。

まばたきが速すぎる/遅すぎる

120 を触ってください。80より短いと気づかれず、200を超えると眠そうになります。 キャラの性格に合わせて決めていいところです。

差し替えた瞬間に絵がチカッと光る

初回だけ起きます。その画像をまだ読み込んでいないためです。先に読ませておけば消えます。

[FACE.shut, FACE.mouth].forEach(function (src) { new Image().src = src; });

差分の2枚を、画面に出す前に読み込ませておくコードです。読み込み済みなら、差し替えは一瞬で終わります。

口パクが不自然

音声がまだ無いので、実は口の動きが合っているかを判定できません。 5章で声を付けると「合っていない」がはっきり分かるようになります。いまは開閉しているだけで十分です。

Windows の場合

sips は macOS のコマンドです。Windows ではサイズ確認をAIに頼んでください。

codex exec "char.png と char-me.png と char-kuchi.png の縦横サイズを教えて。"

for の書き方も違います。PowerShell ではこうです。

foreach ($n in "char","char-me","char-kuchi") {
  python keyout.py --input "$n-magenta.png" --out "$n.png" --max-height 512
}

codex execkeyout.py の使い方そのものは同じです。

次の一歩

まばたきの間隔を 2000 + Math.random() * 3000 から 3000 に変えて、比べてみてください。

固定にすると、驚くほど機械っぽくなります。ランダムが生き物らしさを作っていることが体感できます。この感覚は、5章の演出でもそのまま効きます。

これでキャラは息をして、まばたきをして、しゃべるようになりました。次はゲーム画面の中に、ちゃんと立たせます!

ここまでの三つの的

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

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

術の一覧へ戻る

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