アラン・ケイの“メッセージングによるプログラミング”という着想に基づき(非同期処理などいろいろ足りていないながらも──)比較的忠実に実装された1970年代の非常に古いSmalltalk-72に実際に触れてみるシリーズ 第2弾です(なお最新のSmalltalkについては Pharo などでお楽しみください!)。
今回は謎言語「Smalltalk-71」で書かれたスペースウォー・ゲームを Smalltalk-72に移植して動かすことを目指します。前回(2019年)を含む他の記事はこちらから→Smalltalk-72で遊ぶOOPの原点 | Advent Calendar 2023 - Qiita
location
(point
)をやめる
Smalltalk-71のタートルの moveto
は x
と y
が含まれる location
を与えることができるようですが、Smalltalk-72 のタートルにはそういうしくみはないので、せっかく point
を定義してもあまりうまみがありません。
見た目を似せようと location
に寄せましたが、結果的にあまり見た目が似ないばかりか、なによりいろいろ面倒なので、ここはあきらめて素直に位置情報は x
と y
のみの locx
と locy
で表すように変更します。
この変更で影響を受けるのは今のところ spaceship
とそこから呼ばれる2つのアクション( moveship
および display
)です。
to spaceship : pilot thrust steer trigger numtorps locx locy speed direction time ( isnew ? (:pilot. :#thrust. :#steer :#trigger. "numtorps _ "speed _ 0. "direction _ 0 + rand * 360. "locx _ rand between 50 462. "locy _ rand between 50 462. "time _ clock) 0 < clock - time + MOVELAG ? ( "time _ clock. moveship. display ship) ) to moveship ( "speed _ speed + SPSCALE * thrust. "direction _ (direction + DIRSCALE * steer) mod 360. "locx _ (locx + (cos direction) * LSCALE * speed) mod 512. "locy _ (locy + (sin direction) * LSCALE * speed) mod 512) to display obj ( :#obj. @ penup goto locx locy up turn direction + 90 pendn. obj SSIZE. (0 < thrust ? (flame SSIZE) 0 > thrust ? (retro flame SSIZE)))
ついでに、direction
の初期値を乱数にするのと、MAVELAG
の二重足しのミスをこの機に修正しました。^^;
フレームレートを意識した display
に修正
Smalltalk-71 版でそれが想定されているのとは違い、Smalltalk-72 に書き直したコードは並列に動かせるわけではないですし、なにより実機同様このエミュレーターも処理系のスピードが遅くて全力で動いてもらわないといけないという事情もあるため、MOVELAG
や FRAMELAG
は 0
です。
しかし、もし仮に処理系が十分速く動作するなら、本来であれば FRAMELAG
は MOVELAG
より大きな値を設定しておくことで、適切な時間間隔で宇宙船の位置が更新され、適切なフレームレートで宇宙船が描画される…というのが想定されているはずです。
そこで、FRAMELAG
も意識したコードへの変更も試みておきましょう。
ただ、前回の更新時刻である time
だけではフレームレートをうまく表現できないので、新たに ftime
を spaceship
のインスタンス変数に追加します。
to spaceship : pilot thrust steer trigger numtorps locx locy speed direction time ftime ( isnew ? (:pilot. :#thrust. :#steer :#trigger. "numtorps _ "speed _ 0. "direction _ 0 + rand * 360. "locx _ rand between 50 462. "locy _ rand between 50 462. "time _ "ftime _ clock) 0 < clock - time + MOVELAG ? ( "time _ clock. moveship. display ship) ) to display obj ( :#obj. 0 < clock - ftime + FRAMELAG ? ( "ftime _ clock. @ penup goto locx locy up turn direction + 90 pendn. obj SSIZE. (0 < thrust ? (flame SSIZE) 0 > thrust ? (retro flame SSIZE)))) @ erase. disp display. disp clear "SSIZE _ 6. "MOVELAG _ 100. "FRAMELAG _ 300. "SPSCALE _ 1.0. "DIRSCALE _ 1.0. "LSCALE _ 1.0. spacewar delete all. "s1 _ spaceship 'Jimmy' 2 15 false. spacewar schedule s1 spacewar run "FRAMELAG _ 0. spacewar run
次図は 'MOVELAG' を 100
、FRAMELAG
を 300
で 4フレーム目までは実行したところで esc
キーで停止して 'FRAMELAGのみ
0` に変えて継続した場合の出力例です。 軌道を変えずに描画だけが頻度を上げているのが(ちょっと分かりづらいかも…ですが^^;)確認できます。
display
で宇宙船の残像を消してから描く
Smalltalk-71 には記述がないのでどうやっているかは不明なままですが、ともあれ Smalltalk-72 でもなんとかして残像を消す処理を加えます。
前の例のように、描画をしていない間も moveship
で刻々と位置情報は更新されているかもしれませんし、speed
と steer
がゼロでなければ、前回の描画時の情報は、描画のたびに異なります。そこで、描画時の位置等の情報を ldir
、'llocx、'llocy
、lthu
として保持しておくことにします。
スラスター情報 lthr
は本体の描画には不要なのですが、スラスター火炎を描く方向(位置)を決めるのに使われているのでこれも必要です。また、isnew
の非偽時処理セクション(コンストラクタ)での初期化も加えます。
to spaceship : pilot thrust steer trigger numtorps locx locy speed direction time ftime llocx llocy ldir lthr ( isnew ? (:pilot. "lthr _ :#thrust. :#steer :#trigger. "numtorps _ "speed _ 0. "direction _ "ldir _ 0 + rand * 360. "locx _ "llocx _ rand between 50 462. "locy _ "llocy _ rand between 50 462. "time _ "ftime _ clock) 0 < clock - time + MOVELAG ? ( "time _ clock. moveship. display ship) )
display
では、まず前回位置等情報を使って、かつ、白( ☺ white
)で描画して残像を消す処理を追加します。また、改めて黒( ☺ black
)で現在の位置等情報を使って描画し、その後、前回位置等情報を更新します。
to display obj ( :#obj. 0 < clock - ftime + FRAMELAG ? ( "ftime _ clock. @ penup goto llocx llocy up turn ldir + 90 pendn white. obj SSIZE. (0 < lthr ? (flame SSIZE) 0 > lthr ? (retro flame SSIZE)). @ penup goto locx locy up turn direction + 90 pendn black. obj SSIZE. (0 < thrust ? (flame SSIZE) 0 > thrust ? (retro flame SSIZE)) "llocx _ locx. "llocy _ locy. "ldir _ direction ."lthr _ thrust)) "SSIZE _ 6. "MOVELAG _ "FRAMELAG _ 0. "SPSCALE _ 1.0. "DIRSCALE _ 1.0. "LSCALE _ 1.0. @ erase. disp display. disp clear spacewar delete all. "s1 _ spaceship 'Jimmy' 5 30 false. spacewar schedule s1. "s2 _ spaceship 'Beth' `5 `20 false. spacewar schedule s2. spacewar run