kairo-gokko (20) タッチ操作対応など



スイッチ以外をクリックしたときにエラーになるのを修正

Circuit#find_switch_by_position を修正。 クリックした位置にスイッチがない場合に nil を返すように。 もっと早く直しておけばよかった……。

--- a/circuit.rb
+++ b/circuit.rb
@@ -328,5 +328,7 @@ class Circuit
           .find { |switch| switch.pos == pos }
       return pushed_switch if pushed_switch
     }
+
+    nil
   end
 end

タッチ操作対応

これまで PC メインで作業してきてタッチ操作対応は後回しでもいいかなと思ってたんですが、 ブログに貼ったりするとやはりスマホタブレットで触りたくなってきました。

マウスの場合とほとんど同じで大丈夫でした。 これももっと早くやっとけばよかったですね。

# main.rb

    tx = (Input.touch_x / PPC).floor
    ty = (Input.touch_y / PPC).floor

    if Input.touch_push?
      tpos = Point(tx, ty)

      pushed_switch =
        circuit.find_switch_by_position(tpos)

      if pushed_switch
        Sound[:click].play
        pushed_switch.toggle()
      end
    end

以下の iframe で実際に試せます。

ファイルが増えたためロード時間がだいぶ長くなってきました(特にモバイル環境)。 これはこれでなんとかしたい……。

こちらも同じものです。
https://sonota88.github.io/kairo-gokko/pages/20/index.html


動くことが分かったのでリファクタリングします。

スイッチをクリック、またはタッチしたときの処理をメソッドに抽出。

def on_push(pushed_switch)
  Sound[:click].play
  pushed_switch.toggle()
end

無駄な通電判定を抑制

気づいてはいましたが、 通電判定は重い処理ですから、毎フレーム実行するのはかなり無駄です。 スイッチの状態が変わらなければ通電の状態を更新する必要はないはずです。

まあこれは明らかに無駄だと思うので、 測定せずにやってしまいます。


まずはスイッチが変更されたことを示す変数 switch_changed を導入して、スイッチが押されたときだけ true になるようにします。

--- a/main.rb
+++ b/main.rb
@@ -50,6 +50,8 @@ Window.load_resources do
   Window.bgcolor = C_BLACK
 
   Window.loop do
+    switch_changed = false
+
     mx = (Input.mouse_x / PPC).floor
     my = (Input.mouse_y / PPC).floor
 
@@ -61,6 +63,7 @@ Window.load_resources do
 
       if pushed_switch
         on_push(pushed_switch)
+        switch_changed = true
       end
     end
 
@@ -75,6 +78,7 @@ Window.load_resources do
 
       if pushed_switch
         on_push(pushed_switch)
+        switch_changed = true
       end
     end

この switch_changed を見て通電判定処理を実行するか決めるようにします。

    if switch_changed
      circuit.child_circuits.each { |child_circuit|
        child_circuit.update_edges()
      }
    end

これだけだと、アプリケーションの実行が開始されて最初にスイッチが押されるまで 通電判定が行われないことになってしまうので、 初回に一度実行するようにします。

--- a/main.rb
+++ b/main.rb
@@ -40,6 +40,10 @@ end
 
 circuit = Circuit.from_plain(parse_json($data_json))
 
+circuit.child_circuits.each { |child_circuit|
+  child_circuit.update_edges()
+}
+
 view = View.new(PPC)
 
 Sound.register(:click, "click.wav")

さらに、この子回路をイテレートして通電判定する部分は、責務的に Circuit に移した方がよさそうなので、 Circuit#update_tuden_state に抽出しました。

  def update_tuden_state
    @child_circuits.each { |child_circuit|
      child_circuit.update_edges()
    }
  end

んー、メインループ部分が長くなってきたのでこれもメソッドに抽出しましょうか。 main_loop() 自体は長いままなんですが、呼び出し側の部分はすっきりしました。

circuit = Circuit.from_plain(parse_json($data_json))
circuit.update_tuden_state()

view = View.new(PPC)

Sound.register(:click, "click.wav")

Window.load_resources do
  hide_loading()

  Window.bgcolor = C_BLACK

  Window.loop do
    main_loop(circuit, view)
  end
end

今回はここまで。