Among Us Scratch の解析2:タイトルロゴ
前回は背景について解析したので、今回は Among Us のゲームの本体が実装されているはずの、スプライトを調べていく。
スプライトとは?
スクラッチのスプライトとは、簡単には「コードで動かすことのできる絵」だ。この点は前回の背景と似ているが、スプライトは移動させたり消したり変形させたりといった様々なアニメーションが可能だ。
背景ともう一つ違う点は、スプライトはプロジェクト中にいくつでも作ることができるところだ。作ったスプライトはプロジェクト(アプリケーション)を起動すると一斉に動作を始める。
解析するスプライトを選ぶ
Among Us Scratch プロジェクトのスプライトは全部で45個ある。
解析をすすめるにあたって、いきなり巨大なコードを含むスプライトをターゲットにすると、理解できなくてすぐくじけてしまいそうなので、できるだけ簡単そうなものを選びたい。
b l a n k スプライト
どういうわけか、各文字の間に空白がある謎の名前をつけられているスプライト。大きさも位置も設定されているが、コスチュームが怪しい。
縦横0ピクセルのなにもない絵が1枚だけ。コードもまったくないので、このスプライトは、あるだけで何もしていないように見える。
この b l a n k スプライトを次の画面のように見えない状態(目のアイコンに斜め線がはいっているほうを選択する)にしても、完全に削除してもタイトル周辺の動作は変わらなかった。
とりあえず、このスプライトはゴミということで解析完了。完全解析への野望に一歩近づいたが、解析した気がしない。
(もしかしたらこのスプライトは他で役に立つかもしれないが、今は放置する)
Cloudスプライト
これもぱっと見た感じでは、透明で何に使っているかわからないスプライトだがコードをみたところ、これはすごい。これでもごく一部。
名前が「cloud」というところからして、ネットワーキングのコアな部分で、ゲーム中の情報のエンコーディング・デコーディングをやっていたりしそうではある。そのためにこんなに複雑なのかもしれない。
コスチュームからも、高度な技術のオーラが感じられる。
93個の 2 x 2 ピクセルの透明なコスチュームがあり、コスチューム名は、数字、アルファベット大文字小文字、ASCII文字にある括弧や演算子記号になっている。高々スプライトの絵であるコスチュームと、ゲームのオンライン関係の処理になんの関係が...?
さしあたり、このスプライトを2番目の解析とするのは「簡単そうなものを選ぶ」というポリシーに反するので、後回しにする。長い旅に最も大事なのは、何物にも折れない柳のようなスルーパワーだ。
「Among Us」スプライト
このスプライトは見た目わかりやすい。タイトル画面(前回の説明でいえば "intro" モード)に使われているタイトル名をそのまま絵としてもっている。
コスチュームもこの絵だけである。
コードもみたところ簡単そうに見える。
ということで、最初の解析スプライトは、「Among Us」スプライトにする。
Among Us スプライト:ゴミ旗イベントコード
Among Us スプライトでもっとも短いのは次のコード。
「旗がおされたとき」イベントブロックしかない。このブロックは続くブロックをアプリケーション開始時に一度だけ実行する機能を持つ。
ただ、このブロックには続くコードがないので結局何もしない。多分、何か作っている間にうっかりできてしまったゴミであろう。
スクラッチのコードは上下左右に広がりがあるので、コードが大きくなると、整理が追い付かず、コード同士の重なりなども頻繁だ。このようなゴミコードはできやすく、掃除も大変なので邪魔にならない限り放置しやすい。
スクラッチでは、このようなプログラム実行の起点となる「黄色の山型」ブロックを「イベントブロック」と呼んでいる。山型のブロックは「ハットブロック」とも呼ばれる。実行の起点となるという点ではどちらも同じものだ。
Among Us スプライト:最初は隠すコード
もう一つのアプリケーション起動時のコード。「隠す」はスプライトを消す命令。見えなくするだけでなくなるわけではない
スクラッチはアプリケーションを起動するとプロジェクトのすべてのスプライトが一斉に動作開始する。あるタイミングで現れるような絵を作るなら、プロジェクトにスプライトをあらかじめ配置しておき、実行開始時は「隠す」で見えなくして、必要なときに次の説明の「表示する」を実行するのが、多分一番簡単な実装方法だ。
Among Us スプライト:introモードの表示
イベント intro は、画面が intro モード=タイトル画面になったときに送られるイベントだ(前回の画面モードのところ参照)。
イベント直後の「表示する」は文字通り、このタイトル文字「AmongUs」を表示する。アプリケーション起動直後は隠されているので、タイトル画面になったら表示している。ここまでは簡単。
幽霊の効果のコード
幽霊の効果とは、スプライトを半透明にして表示を薄くしたり消したりすることである。例えば、次のコードは画面上では、スプライトが50%半透明で表示される。
少しわかりづらいが、効果を100にすると透明度100%で全く見えなくなる。
効果については、「~づつ変える」というコードもある。効果の値を加算もしくは減算するコードで、「幽霊の効果を?づつ変える」とすれば、次第に見えてきたり、逆に次第に消えていくようなアニメーションを実装できる。次はその例。
このコードは最初幽霊効果=0なので不透明だが、そのあと2づつ変えつづけ、50回繰り返したところ(=50フレーム)で透明度100になり完全に見えなくなる。
しかし、先の Among Us スプライトの幽霊効果のこのコードは、
「0づつ」効果を加算=0を足すだけなので効果はない。つまりこのコードはなんの役にもたたないゴミだ。
きっと昔はこれでふわっと出していて、その残滓で消すのが面倒でとりあえず0にして無効と同じ状態にしたものかもしれない。
本当の本当は、不透明にするつもりで「幽霊の効果を0にする」としたつもりが間違えて「0ずつ変える」にしてしまったバグかもしれない。真相は闇の中だが、動いてはいるので「動いているものを触るな」という正誤どちらともとれるソフトウェア開発の原則の適用範囲かもしれない。
X座標を0,Y座標を110にする、コード
このコードは文字通り、スプライトの位置を指定する。
スクラッチの画面座標系は、次の画面写真のように、画面中心を原点:X座標=0,Y座標=0,として、右と上へ X と Y が正となっている。
スプライトの座標はコスチュームの中心になる。原点:X=0,Y=0にスプライトを置くと、画面の中心とスプライトの中心が一致する。
上の女性アビーは、中心が腰あたりで、この腰のあたりの座標を X=-120、Y=-60 としているので、画面の左下の方に配置される。
(アビー=abby とは、スクラッチに初めから用意されているコスチュームの名前)
Among Us スプライトは、最初からX=0、Y=110に設定されているので、コードで座標を指定するのは不要にみえるかもしれない。
プロジェクトを実行したあと停止すると、スプライトは停止した最期の位置のまま停止する。再実行すると最後に停止した位置からプログラムは動作を開始する。つまり、上のスプライト位置の設定は「プログラム開始時の初期位置」にはならない。
従って、何度実行しても同じ場所から動くようにしたければ、「旗が押されたとき」イベントを使ってコードで位置を設定しなければならない。
scratch のこの座標が残る機能は少し微妙だ。先の、幽霊の効果は実行を停止すると最初の状態に戻る。座標は戻らないのに幽霊効果は戻るのは少し気持ちわるい。
set_volume イベントを受けるコード
前回 set_volume イベントをずっと送り続けるコードがあったが、それを受けるところが、Among Us スプライトにはある。
前回説明の「ゲームモードに応じて音量を変える人」を含めて、Among Us Scrach v2.1 (WIP) の音量調整は次のようになっている。
■ 背景コードが、常に set_volume イベントを送っている。
■ 背景コードが、ゲームモードに応じて #volume 変数を調整をする。
■ Among Us スプライトが、set_volume イベントによって、#volume 変数の音量値を設定する。
つまり、前回説明の背景のコードから指令されて、Among Us はボリュームコントロールをしているのだ。
しかし、ここで気になることがある。
Among Us スプライトは確かに intro モード画面でしか見えないスプライトだが、スクラッチでは、可視不可視はコードが動くかどうかと全く関係がない。
確認で、実行中のタイトル以外の画面で Among Us スプライトのコードを見ると、Among Us は見えていなくても、このコードはちゃんと動作している。
(こうでなければ、一度「隠す」すると自分では二度と「表示する」に戻れなくなる)
気になることとは、set_volume の送り側も受け側もずっと動いているなら、背景の送り側だけで処理してメッセージやめてしまえばいいのではないか?という点だ。このset_volume の受け側コードをやめても、背景のコードは set_volume を送る部分が次のようになるだけだ。
また、タイトルでしか働かないスプライトが、アプリケーション全体の音量設定するのは責任違いにも感じる。
online モードになったら表示をやめるコード
このコードは画面が online モードになったときに動作する。
ゲームモードについては、前回の「最初のモード設定コード」に Among Us Scrach v2.1 (WIP) にあるとおり、modus 変数とイベントで区別してコードを作ることができる。
modus 変数: 動作中の画面モード。intro とか online 等の名前が入っている。
イベント: modus 変数に設定された名前と同じイベントを使って、モードが変わった瞬間。intro イベントなら、起動最初の画面になったとき。
最初の画面には、次の3つのボタンがあり、触れると3つの画面モードのどれかへ進むようになっている。
Among Us スプライトが、このコードで online モードになったとき、最初に modus 変数が online になるまで待っている。
「onlineモードになったらonlineモードになるまで待つ」というのは実にトートロジーな印象を受ける。
もしかしたらイベントとmodus変数の設定はタイミングが違うケースがあるかもしれないが、ルールがあるのに特定のケースでは特殊な方法を要求するのはあまり良いことではない。昔のコードの名残を整理できていないのかもしれない。
その次の「スプライトの他のスクリプトを止める」は、このスプライトの動作中のコードをすべて停止する。
「スクリプト」とは、ブロックの塊一つ分のことを指す。Among Us スプライトのコードには、次のコードの図で赤枠で囲んだ8個のスクリプトがある。
「スプライトの他のスクリプトを止める」は、上の図のうちこのコードが含まれる右下のスクリプト以外のすべてを停止させる。
この後に説明するが、ほかにもイベントを待ち受けているコードがいくつかあり、画面のモードが変わったらそれらを停止して誤動作を防いでいる。
最期に「隠す」で Among Us スプライトを非表示にして、次の online 画面の表示に備える。
how to play 、freeplay モードになったら表示をやめるコード
こちらも、先の online モードで表示をやめるコードとほぼ同じ処理を、how to play モード、freeplay モードになったときに実行する。
こちらのコードは、モードが online になるまで待っていない。この3つのコードは同じスプライトのコードで、パッと見れば「違う」ということはすぐわかるので、バグというのもなにか少し考えづらい。すると、online モードのときだけ動作がすこし違う可能性も残されているかもしれない。
コードのコメント
物忘れが激しいおっさんのために、スクラッチには、コードに説明をつける機能がある。
左上▼記号のボタンで、ボックスを折りたたんで小さくすることもできる。
便利な機能ではあるが、スクラッチ界?ではあまり流行っていないようだ。他人のプロジェクトをみてもコメントがついていることは少ないように思える。
コード全体からみると矩形が多くなって面倒だけど、作った時の気持ちが少しでも書かれていれば、今回のように設計実装の意図を予想することも少なくなると思うが...。
おまけ1:スクリプトを止めるブロック
スクリプトを止める方法には3種類ある。
左の「すべてを止める」はプロジェクト全体のスクリプトの処理をすべて停止する。これ以降プロジェクト全体のコードが一切動作しなくなる。
真ん中の「このスクリプトを止める」は、このブロックのあるスクリプトだけが停止する。同じスプライト内の他のスクリプトやほかのスプライトは停止しない。
右の「スプライトの他のスクリプトを止める」は先ほどの例のように、このブロックが含まれいてるスクリプト以外が停止する。
「スプライトの他のスクリプトを止める」命令は、ほかのスクリプトを止めるだけで自分自身は停止しない。よって、ブロックの下にほかのブロックをつなげる「ノッチ?」があり、停止後の処理をつけられる。
ビジュアルプログラミングでセマンティクスをうまく表現できているよい例のように思う。
おまけ2:set_volume イベントコードは停止しない
「スプライトの他のスクリプトを止める」で、動作が止まるなら、 set_volume のコードも止まるので、set_volume を処理するコードはタイトル画面中しか動作しないようにも見える。
残念なことにその場の処理が止まるだけで、再度set_volume イベントが送られれば止めた後であってもまた動作する。結局、あのコードだけでは Among Us スプライト表示中だけ動くようにはできない。
まとめ:怪しいコードが散見された
背景のコードはシンプルでわかりやすかったが、Among Us スプライトのコードはシンプルではあったが、ややきな臭い香りもするものだった。ソフトウェアにも歴史はあるよね、と思った。