Sceneにメインループを置く例

昨日のコードには2つほど問題がある。一つはシーン切り替えのトリガが1フレーム処理の最後にあるところで、まあこれは単純なミスだ。
もう一つは、シーン切り替えの瞬間のみInput.updateが1フレーム中に2回呼ばれるところ。2回呼ぶのはDXRuby的には反則なのだが、これを1回にするように修正すると今度はシーン切り替え後のInput.key_push?(K_SPACE)がしょっぱなからtrueになって、SecondSceneがいきなり終わることになる。最初の入力を無視するのも面倒だし、どうしていくのがいいのかはおいおい考えるとして、今はこの状態で先に進めることとする。と思ったけどこれちょっと前に発覚したバグじゃん。まあ、結局のところ後で考えるわけだが。

今回はメニューぽいSceneを追加してみよう。


Escapeキーでメニュー表示するが、このときにメインのシーンはそのまま実行し続ける、ということをしてみる。一応フェードアウトでちょっと暗くしてみる。メニューはぜんぜんメニューじゃないけどまあ何か表示するというだけにしておいて、Escapeでフェードインして元のシーンに戻る。とする。
とりあえずFirstSceneを修正する。メニューシーンから呼べるようにupdateとdrawのメソッドを追加して、EscapeキーでMenuSceneを作ってrunを呼ぶ。呼び返しができるようにselfを渡しておく。

class FirstScene < Scene
  def initialize
    @objects = []
    @screen_collision = Sprite.new
    @screen_collision.collision = [0, 0, 639, 479]
  end

  def run
    Window.loop do
      if Input.key_push?(K_SPACE)
        return SecondScene.new
      end
      if Input.key_push?(K_ESCAPE)
        MenuScene.new(self).run
      end

      update
      @objects = @screen_collision.check(@objects)
      draw
    end
  end

  def update
    @objects.push Hoge.new
    Sprite.update @objects
  end

  def draw
    Sprite.draw @objects
  end
end

MenuSceneのほうは適当にフェードアウトとフェードインを書いて、その間にメニューぽいものを描画するメインループを置く。ここでカーソル移動やらなんやらをすればちゃんとしたメニューになるはずだが、面倒なのでこれでよい。

class MenuScene < Scene
  @@fadeimage = Image.new(1,1,C_BLACK)
  @@backimage = Image.new(1,1,[192,0,0,0])
  @@menuimage = Image.new(100,50,C_BLACK).box(0, 0, 99, 49, C_WHITE)
                                         .draw_font_ex(10,10,"メニュー",Font.new(32))

  def initialize(s)
    @s = s
  end

  def run
    Window.times(30) do |i|
      @s.update
      @s.draw
      Window.draw_ex(0, 0, @@fadeimage, :alpha=>255*i/45, :scale_x=>640, :scale_y=>480, :center_x => 0, :center_y=>0)
    end

    Window.loop do
      if Input.key_push?(K_ESCAPE)
        break
      end

      @s.update
      @s.draw
      Window.draw_scale(0, 0, @@backimage, 640, 480, 0, 0)
      Window.draw(270, 215, @@menuimage)
    end

    Window.times(30) do |i|
      @s.update
      @s.draw
      Window.draw_ex(0, 0, @@fadeimage, :alpha=>255*(29 - i)/45, :scale_x=>640, :scale_y=>480, :center_x => 0, :center_y=>0)
    end

    return nil
  end
  
end

フェードアウト/フェードインをするのにあると便利だったのでWindow.timesを作ってみた。

module Window
  def self.times(count)
    unless @@created
      Window.create
      @@created = true
    end

    count.times do |i|
      raise ExitException if Input.update
      yield i
      Window.sync
      Window.update
    end
  end
end

FirstSceneと同じようにSecondSceneを修正すると、どっちからでもメニューが呼べるようになる。こういう画面になる。