Smalltalk-72で遊ぶOOPの原点:宇宙船の残像を消す

アラン・ケイの“メッセージングによるプログラミング”という着想に基づき(非同期処理などいろいろ足りていないながらも──)比較的忠実に実装された1970年代の非常に古いSmalltalk-72に実際に触れてみるシリーズ 第2弾です(なお最新のSmalltalkについては Pharo などでお楽しみください!)。

今回は謎言語「Smalltalk-71」で書かれたスペースウォー・ゲームSmalltalk-72に移植して動かすことを目指します。前回(2019年)を含む他の記事はこちらから→Smalltalk-72で遊ぶOOPの原点 | Advent Calendar 2023 - Qiita


locationpoint)をやめる

Smalltalk-71のタートルの movetoxy が含まれる locationを与えることができるようですが、Smalltalk-72 のタートルにはそういうしくみはないので、せっかく point を定義してもあまりうまみがありません。

見た目を似せようと location に寄せましたが、結果的にあまり見た目が似ないばかりか、なによりいろいろ面倒なので、ここはあきらめて素直に位置情報は xy のみの locxlocy で表すように変更します。

この変更で影響を受けるのは今のところ 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 に書き直したコードは並列に動かせるわけではないですし、なにより実機同様このエミュレーターも処理系のスピードが遅くて全力で動いてもらわないといけないという事情もあるため、MOVELAGFRAMELAG0 です。

しかし、もし仮に処理系が十分速く動作するなら、本来であれば FRAMELAGMOVELAG より大きな値を設定しておくことで、適切な時間間隔で宇宙船の位置が更新され、適切なフレームレートで宇宙船が描画される…というのが想定されているはずです。

そこで、FRAMELAG も意識したコードへの変更も試みておきましょう。

ただ、前回の更新時刻である time だけではフレームレートをうまく表現できないので、新たに ftimespaceshipインスタンス変数に追加します。

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' を 100FRAMELAG300 で 4フレーム目までは実行したところで escキーで停止して 'FRAMELAGのみ0` に変えて継続した場合の出力例です。 軌道を変えずに描画だけが頻度を上げているのが(ちょっと分かりづらいかも…ですが^^;)確認できます。

display で宇宙船の残像を消してから描く

Smalltalk-71 には記述がないのでどうやっているかは不明なままですが、ともあれ Smalltalk-72 でもなんとかして残像を消す処理を加えます。

前の例のように、描画をしていない間も moveship で刻々と位置情報は更新されているかもしれませんし、speedsteer がゼロでなければ、前回の描画時の情報は、描画のたびに異なります。そこで、描画時の位置等の情報を ldir、'llocx、'llocylthu として保持しておくことにします。

スラスター情報 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

ジョイスティックの動きをキー押下で(雑に)真似る「stick」 へ続く)