[クリスタ]CLIP STUDIO PAINTのオートアクション機能でライフゲームらしきものを動かす
初手成果物
この記事は、最終的にこうなります。
これだけだと何が起きているのか完璧にはわからない人も多いでしょうから、以下で説明を行っていきます。
前置き
CLIP STUDIO PAINTについて
CLIP STUDIO PAINT略して「クリスタ」は、2024年8月時点でかなりのシェアを誇っているお絵描きアプリです。すべて把握するだけでも難しいほどの豊富な[1]機能の山を伴っており、絵描きですらない筆者にとってはもはや迷宮に近いと常々思っています。
とはいえ筆者は一応、クリスタのエディションでも最上位[2]のEXライセンスを使える立場にいます。自由度とは本来自分を高めてくれるものであって、与えられた力に恐れをなしていては何もできません。なので筆者は定期的にクリスタの知らない機能を試してみる会を(一人で)開いているのですが……その折に、発見したわけです。
オートアクション機能を。
オートアクションについて
オートアクションはクリスタのいち機能で、要するにできることとしてはマクロです。
クリスタ公式のTIPSページにも……
オートアクションとは、複数の操作を記録してまとめて行うことができる機能です。自分がよく行う操作を記録しておけば、作業効率が格段にアップします。
……と書かれています。
あまり他のお絵描きアプリにおけるマクロ機能について知らないのもあって詳細は割愛しますが、「ユーザーの取ったアクションを記録し、必要に応じて機械的に再実行する」 というのが基本的な機能になります。
例えば特定のレイヤーを縁取りするとか、何か複雑なフィルターを加えるとか……そういった手間のかかる、しかし単純作業と言えるような動作を代替するのを目的としているのでしょう。基本的にユーザーが実演する形でしかコマンドを入力できない[3]ことや条件分岐の概念がないこともあり、プログラミング的文脈での悪ふざけは難しそうです。
逆に言えば、難しそうなだけです。
ライフゲームについて
ライフゲームとは1970年にジョン・ホートン・コンウェイによって考案された数理モデルで(Wikipediaをちらりと見る)、8近傍の格子の中におさまったセルが隣のセルの状態によって死んだり生き返ったりするやつです。というか、当のWikipediaを直接貼ったほうが早い気がしてきました。
クリスタはそもそも二次元的な描画ツールですから、こういう2Dであらわされる数理モデルを表すにはもってこいでしょう。ライフゲームを動かすことができれば、オートアクションでもある程度の計算はできることの証明になるはずです。読まなくてもいい言い訳――チューリング完全性について
チューリング完全性について
ライフゲームは計算メカニズムとしてチューリング完全なので、ライフゲームが内部的に実装できる計算メカニズムも同様にチューリング完全であり、まあおおよそ全ての計算をできるといえます。
……しかし、これはそのメカニズムが無限ループ可能であることを前提とした話です。冒頭のGIFを思い出していただければというか、もう一回貼るんですが……
ポチポチしてますね……マウスを。
クリスタのオートアクションには全自動で繰り返しを行う機能がないため、計算を進めるためには人類が人差し指を動かす必要があります。というかそもそもキャンバスサイズが(おそらく)有限なので、その観点でもチューリング完全を主張するには要件が足りません。タイトルがライフゲームらしきものを動かすなどと回りくどい書き方になっているのは、このあたりが原因にあるのですね。
実装
方向性
オートアクションはいろいろできますが、今回は 「選択中のレイヤーをライフゲームの格子であると解釈したうえで、そのライフゲームを1ティック進める」 という内容のアクションで作っていきます。
格子用のレイヤーでは、
- 「生存セル」を黒(#000000)のドット
- 「死滅セル」は透明のドット
でそれぞれ表現します。ドットは当然ながら1px四方なので、透明レイヤーを用意したうえで、デフォルトツールの『ドットペン』で打つといい感じでした。
クリスタで新規ファイルを作成した際についてくる白地の用紙については、特に動作にかかわらないのでどうしてもいいです。非表示にしてもいいですし、なんなら別の背景に差し替えてもいいです。
いざ
まずはクリスタ側の準備です。新規ファイル作成用のウィンドウから 『イラスト』 を選び、30px四方[4]のキャンバスを作りました。
さらにField
という名前のキャンバスを作り、ドットペンでセルを打ちます[5]。
ひとまず、このセルをライフゲーム的に1ティック進めることを考えてみましょう。
ライフゲームにおける各セルは、自分の周囲の8マスの状態を参照して変化します。これを表すためにいろいろ考えた結果、Field
を増やして重ねることにしました。
つまりこういうことです。
これはField
の不透明度を20%にしたものを8つ複製し、周囲8マスとの位置関係に対応するようそれぞれ移動させた状態です。
不透明度が100%ではないため重なり合った複製どうしで干渉を起こし、近くに生存セルが多いセルの上ほど、濃い灰色が描画される状態になるわけです。
ここでライフゲームのルールを見てみましょう:
- 生存セルの場合:
- 近傍生存セルの数が2,3なら生き残る
- 4以上なら死ぬ
- 1以下でも死ぬ
- 死滅セルの場合:
- 近傍生存セルの数が3なら生まれる
- それ以外なら死んだまま
簡略化すると、こう言うこともできます:
- 生きる:
- 「近傍生存3」
- 「生存セル」&&「近傍生存2」
- 死ぬ:
- それ以外
つまり近くのセルの生存数が 2
か3
のセルにだけ目を向けておけば、0
~1
と4
~8
の7パターンについては本来の生死を問わず、全部殺していいわけです。なんなら2
と3
についても別に選択範囲の足し算などで論理演算をする必要はなく、
-
Field
における近傍生存数2
以外をすべて「死滅」にする -
-
Field
における近傍生存数3
をすべて「生存」にする
-
の2ステップで大丈夫です[1:1]。
具体的な実装としては、まず8つ存在するFieldのコピー
レイヤーを「選択中のレイヤーを統合」で1つにまとめます。そのうえで、
- 「選択範囲>色域選択」、上の画像における灰色の中で2番目[6]に薄い色を選ぶ[7]
-
Field
に移動しつつ選択範囲を反転し、キャンバス上でDelキーを押す[8] - もう一度統合レイヤーに戻りつつ選択を解除し、再び「選択範囲>色域選択」。上の画像における灰色の中で3番目に薄い色を選ぶ
-
Field
に移動し、選択範囲ランチャーから塗りつぶしを実行 - 選択を解除し、統合レイヤーを消去する
この一連の流れをGIFにしたものがこれです:
あとはこの工程を記録したオートアクションを連打することで、冒頭のGIFのような状況になるわけですね。
まとめ
恐怖を感じました
-
いったんマイナーチェンジを除くと、DEBUT、PRO、EXという3種類のライセンスがある ↩︎
-
例えば「選択中のレイヤーを削除する」コマンドをチェーンに含めるためには、実際に「アクションを記録」ボタンを押した後にユーザー自身でキャンバス上のレイヤーを消す必要がある ↩︎
-
ここのサイズは任意だが、あまり大きい解像度で試していないので場合によっては何か不都合が起きるかも ↩︎
-
GIFと見比べればわかるが、実際にドットペンで打ったのは画像より一つ前の状態の格子。しかしCtrl+Zの回数制限に達して最初の状態まで復元できなかったため、いったん過去を改ざんするほかなかった ↩︎
-
完全な透明も含めて3番目。なお灰色とはいってもRGB的には(0,0,0)で、透明度で差別化されているに過ぎない ↩︎
-
「色域選択」の対象色はスポイトで選ぶが、オートアクションとして記録される際の引数は「スポイトで選択するキャンバス上の座標」ではなく「オートアクション記録時にスポイトが示した色」なので盤面が変わったらまずいのではといった杞憂は不要。しかし基本色を黒から変えると対象色も修正しないと動かない欠点もある ↩︎
-
反転せず「選択範囲外を消去」でもいいことに今気づいた ↩︎
Discussion