マスコットパックってなあに?

今日は……わたしが先生の日だよ。うまく話せるかな。
今回は、ダッシュボードのデスクトップ画面を歩き回るマスコットの話です。ゆれ子もそのひとりで、見た目・動き・せりふは「パック」と呼ばれるひとまとまりのファイルに書かれています。ここではパックの中身と、自分でパックを作るときの用意・描き方・確かめ方を順に見ていきます。
パックはファイルふたつ
パックは、manifest.json とスプライトシート(コマを並べた画像)1 枚だけが入ったディレクトリです。シートは PNG・GIF・WebP のどれかです。マニフェストには「どのコマをどの順に、何ミリ秒ずつ見せるか」といったことを宣言的に書くだけで、スクリプトは入れられません。SWING には yureko・mochi・neko の 3 つのパックが同梱されていて、既定ではゆれ子だけが表示されます。

わたしの中身も、manifest.json と sprite.png だけなの。シートは 256×256 の PNG で、ちょっと恥ずかしいけど……ぜんぶ並んでるよ。
自分で作ったパックは、設定の [dashboard] mascots_dir で指したディレクトリに置きます。その直下のサブディレクトリ 1 つが 1 パックで、ディレクトリ名がそのままパックの id になります。
<mascots_dir>/
my-cat/
manifest.json
sprite.png
設定ファイルでは次のように書き、環境変数なら SWING_DASHBOARD_MASCOTS_DIR で指定します。
[dashboard]
mascots_dir = "/path/to/mascots"
パックは swing up の起動時に 1 回だけ読み込まれるので、足したり直したりしたあとは swing up を再起動します。表示するかどうかは、デスクトップ画面の「コントロール パネル」の「マスコット」タブで選びます。
最小のパックと、用意するコマ
必須のアニメーションは待機の idle だけです。ただ、walk が無いと歩くときも idle の絵のまま横に滑っていくので、実際には walk も用意するとよいでしょう。最小の形は次のとおりです。
{
"format": 1,
"name": "わたしのねこ",
"sprite": "sprite.png",
"frame": { "width": 32, "height": 32 },
"animations": {
"idle": { "frames": [0, 1], "fps": 2 },
"walk": { "frames": [2, 3, 4, 5], "fps": 8 }
}
}
ほかに使えるアニメーションは、バルーンを出している間の talk、しばらく放っておかれたときの sleep、おしらせが来たときの surprise、つままれている間の drag、離されて落ちる間の fall です。どれも無ければ idle で代わりに描かれます。
これとは別に、土台の絵の上に重ねる「重ね絵」として、目を閉じた絵の blink と、口の開け閉めの mouth があります。blink は目のところだけを描いてほかを透明にし、mouth はバルーンの文字送りに合わせて動きます。

わたしの場合は……7 種類のアニメーションをぜんぶ使ってて、重ね絵は mouth だけ。まばたきは無いの。idle は、1 コマ目を 1800ms、2 コマ目を 120ms にしてるよ。
同梱のパックはそれぞれ見本になっています。mochi はここで挙げたアニメーションと重ね絵をすべて使った例、neko は重ね絵なしの例、yureko は重ね絵に mouth だけを使う例です。
シートの描き方
シートには同じ大きさのコマを格子状に並べます。番号は左上から右へ 0, 1, 2…と振られ、行の端まで来たら次の行の左端へ続きます。1 コマの大きさは 32〜64px くらいが扱いやすく、大きく見せたいときは描き込みを増やすより scale を 2 か 3 にするほうがドット絵らしさが残ります。
![]()

わたしは 1 コマ 64×64 で、scale が 2。だから画面では 128px になってるみたい。256×256 のシートに 4 列 × 4 行で並んでて、使ってるのは 0 番から 12 番までだよ。
絵は右向きで描くのが基本です。facing の既定は right で、左へ歩くときは自動で左右反転して描かれます。左向きで描いたなら "facing": "left" にします。
いちばん気をつけたいのは足元です。全部のコマで、足のいちばん下の行を同じ高さにそろえます。マニフェストの anchor は足元の点で、既定はコマの下端の中央です。この点が地面(タスクバーの上端)に合わせられます。足の位置がコマごとにずれていると、アニメーションが切り替わった瞬間に沈んだり浮いたりして見えます。

わたしの anchor は { "x": 32, "y": 63 }。座標は画素の境目で数えるから、64 がコマの下端なの。わたしの足は y = 62 だから、そのすぐ下の 63 に合わせてあるよ。
歩くコマでは、体の位置がコマごとに左右へぶれないようにします。ぶれるとカクカクして見えるので、揺らすとしても全体を上下に 1px までにとどめます。縦のずらしは各コマの dy でも付けられ、ゆれ子の walk は 2 コマ目と 4 コマ目に "dy": -0.5 を付けて、ほんの少しだけ弾ませています。
透明な部分は、きちんと透明にしておきます。アルファ値が 128 未満の画素はクリックが下に抜けるので、輪郭をぼかすと縁の当たり判定が欠けてしまいます。輪郭はくっきり描きましょう。重ね絵のコマは、土台のコマとぴったり重なる位置に描きます。
歩く速さの決め方は 2 通りあります。1 つは walk の各コマに { "index": 2, "ms": 80, "dx": 2 } のように進む距離 dx を書く方法で、地面に着いた足が次のコマで後ろへずれた分と dx を同じにすると足が滑りません。足をはっきり動かす neko がこの方法です。もう 1 つは dx を書かずに speed(シート上の px / 秒、既定 24)でなめらかに進ませる方法で、足の動きが小さい mochi に向いています。どちらも scale 倍されて画面上の速さになります。

わたしは dx を書かずに、speed を 8 にしてるよ。ゆっくり歩くほうが、性に合ってるみたい……
バルーンとせりふ
バルーンのしっぽは、balloon で指定した点を指します。既定はコマの上端の中央なので、頭の上に透明な余白がある絵だとバルーンが浮いて見えます。その場合は "balloon": { "x": 16, "y": 6 } のように、頭のすぐ上の点を指定します。ゆれ子は { "x": 32, "y": 3 } です。
せりふは lines に、種類ごとに文字列か文字列の配列で書きます。配列なら出すたびにどれか 1 つが選ばれ、書かなかった種類には組み込みの文が使われます。種類には、初めて表示したときの greet、ときどきのひとりごとの idle、クリックされたときの click のほか、おしらせ用の 3 組があります。ミラーしているサイトが保存されたとき(site-stored・sites-stored-many)、自分のサイトを公開したとき(site-published・sites-published-many)、自分のサイトをミラーしてくれる人が増えたとき(replica-added・replicas-added-many)で、それぞれ 1 件用と複数件用があります。
1 件用には {title} を入れておくと何のおしらせかが分かりやすく、2 件以上用には {count} を使います。種類の違うおしらせが同時に来ると、せりふが 1 行ずつ並んで 1 つのバルーンに入るので、1 つのせりふは全角 20 字ほどまでにしておくとバルーンが縦に伸びすぎません。

わたしのは、「{title} が更新されたみたい」とか「更新が {count} 件あるみたい」とか。短いほうが、読みやすいもんね。
動作確認
パックを置いたら swing up を再起動し、「マスコット」タブにパックの名前とプレビューが出るかを見ます。出ていなければ、まず swing up のログを見ます。サーバ側で読み飛ばしたパックは、理由付きの警告がログに出ます。ログに何も無ければ、次はブラウザの開発者ツールのコンソールです。値の範囲などはブラウザ側で確かめていて、合わないパックは理由付きの警告を出して読み飛ばされます。

よく引っかかる制限もまとめておきます。manifest.json は 64 KiB まで、シートは 1 MiB まで、シートの幅・高さはそれぞれ 4096px まで、幅 × 高さは 2048 × 2048 ピクセル分までです。シートの拡張子と中身の形式は一致している必要があり、PNG を .gif という名前で置くと読み込まれません。シンボリックリンクとハードリンクは使えません。コマの表示時間は 16〜60000ms(fps なら 60 まで)、1 つのアニメーションは 256 コマまでです。パックは id の順に先頭から 32 個まで読まれ、同梱のパックと同じ id のディレクトリは読み飛ばされるので、同梱のパックを差し替えることはできません。

だから、yureko って名前のディレクトリを置いても、わたしは入れ替わらないの。ちょっと安心……
マスコットの権利について
なお、SWING 本体は MIT License ですが、ゆれ子のキャラクターとその画像はその対象外で、権利が留保されています。SWING やその改変版・再配布物の一部として、改変せずに複製・配布・表示することはできますが、改変や別の作品での利用には許諾が必要です(README のライセンスの節)。パックを人に配るときは、絵の権利とライセンスを添えておくとよいでしょう。

わたしの manifest.json は、書き方の見本にしてくれたらうれしいな。絵は……あなたの子を描いてあげてね。
詳しい手順は docs/mascot-guide.md、詳細なドキュメントは docs/architecture/dashboard/mascot/pack.md にあります。
新しい子を待ちながら
自作のパックを「マスコット」タブで選べば、デスクトップ画面のタスクバーの上を、ゆれ子と並んで歩くようになります。マスコットと一緒に新しい記事を読むひとときをお楽しみください。

先生役、ちょっと疲れちゃった……。となりに来る子、楽しみにしてるね。