ゲーム
ブロック崩しと、canvas のシューティングです。ゲームのために言語へ足したものはありません。
フレームは一通のメッセージ
requestAnimationFrame は extern で呼びます。四行です。
Extern subclass: #Win global: 'window'.
Win class >> requestAnimationFrame: Block[Float Unit] -> Int.
Screen >> tick = (
Win requestAnimationFrame: [:t | me ref tell tick].
...次の状態を作る... ).
コールバックはメッセージを投げるだけです。あとは業務画面と同じで、ハンドラが次の状態を返し、状態が動いたときに描かれます。
ブラウザで測って毎秒60フレーム、そのすべてでアクタが描き直していました。数えたのはページ側の requestAnimationFrame と MutationObserver です。
キーは押している間で見る
押した瞬間に動かすと、押しっぱなしにしたときに一歩動いて止まり、OS がキーリピートを始めてから飛ぶように動きます。
そこで、押されているかどうかだけを覚えておいて、動かすのはフレームの側にします。
Screen >> pressed: e = self with: { goLeft: ((e key) = 'ArrowLeft') or: [goLeft] }.
Screen >> released: e = self with: { goLeft: ((e key) = 'ArrowLeft') ifTrue: [false] ifFalse: [goLeft] }.
これは業務画面でも同じ書き方です。押した回数と押している間は別のものです。
ブロック崩しは DOM
玉も板もブロックも、デザイナの HTML にある見本の複製です。位置は style 属性で動かします。ブロックには key: が付いているので、一つ消えても残りの節点は動きません。
シューティングは canvas
canvas は extern で届きます。
Extern subclass: #Ctx.
Ctx >> fillRect: Int y: Int w: Int h: Int -> Unit.
Ctx >> fillStyle := Str.
こちらは view を使いません。canvas は描いた跡が残るので、毎フレーム自分で消して描き直します。「状態が動いたときだけ描く」という仕組みは働きません。ゲームは毎フレーム描くので、それで困りません。
粒を 10,000 個動かして描く形でも、毎秒60フレームが出ました。このゲームが動かすのは 100 個ほどです。
一つ気付いたこと
点数のラベルに毎フレーム送っていたとき、最悪のフレーム間隔が 48ms まで伸びていました。同じ文字でも新しい状態を返していたので、毎秒60回、同じ内容を DOM に書いていたからです。
show: を「文字が同じなら self を返す」形にしたら、18ms に戻りました。self を返せば描き直しは起きません。