ドラクエ的メニュー

Window.loopとInput.updateをどのようにしようかと考えていたのだが、結局のところこれはこのままでいいのではないかという結論に達した。Window.loopを複数置けるようにするとウィンドウを閉じたときの動作で困ることになるし、1フレーム内にInput.updateを複数置いた場合、1回目の後と2回目の後でInput.key_push?の値が変わるのは当たり前であるわけだ。
また、この動きでは再入後のWindow.loopからInput.key_push?(K_ESCAPE)で判定して戻ってきたとして、Input.updateは発行されないので、シーン呼び出しの後でInput.key_push?(K_ESCAPE)と書いてあるとそのまま終わっていく。これも当たり前である。イヤならrunした後にretryと書いておけばよい。

さて、そのあたりが定まったということで、この仕掛けを利用してドラクエ的メニューを作ってみた。コードは手抜きなのでひどいことになっているが一応それっぽく動く。


とりあえず背景を描画するシーンを作る。

class FirstScene < Scene
  @@image = Image.new(1,1,C_GREEN)
  def initialize
  end

  def run
    Window.loop do
      if Input.key_push?(K_ESCAPE)
        break
      end
      if Input.key_push?(K_SPACE)
        MenuScene.new([self], 100, 100).run
      end

      draw
    end
    return nil
  end

  def draw
    Window.draw_scale(0,0,@@image,640,480,0,0)
  end
end

メニュー呼び出しのときにselfを配列にしているのと、引数が増えているところがポイントだ。メニューは階層構造になっていて再帰呼び出しでたどるから、描画するSceneオブジェクトを配列に突っ込んでいく感じになる。あと、増えた引数はメニューの描画位置だ。
メニューのコードはこんな感じ。画像を作るあたりと座標関連は無理矢理なのでスルーしてください。

class MenuScene < Scene

  @@font = Font.new(24)
  @@image = Image.new(204,124,C_BLACK).box_fill(2,2,201,121,C_WHITE).box_fill(7,7,196,116,C_BLACK)
  @@image.draw_font_ex(32, 22, "0001", @@font).draw_font_ex(120, 22, "0002", @@font)
  @@image.draw_font_ex(32, 54, "0003", @@font).draw_font_ex(120, 54, "0004", @@font)
  @@image.draw_font_ex(32, 86, "0005", @@font).draw_font_ex(120, 86, "0006", @@font)
  @@image.box_fill(60, 0, 139, 23, [0,0,0,0])
  @@cursor = Image.new(12,12).triangle_fill(0,0,5,5,0,10,C_WHITE)
  @@menu = Image.new(80, 24, C_BLACK).draw_font_ex(10, -2, "MENU", @@font)

  def initialize(s, x, y)
    @s = s
    @x = x
    @y = y
    @cx = 0
    @cy = 0
  end

  def run
    Window.loop do
      if Input.key_push?(K_ESCAPE)
        break
      end
      if Input.key_push?(K_SPACE)
        MenuScene.new(@s + [self], @x + @cx * 88 - 45, @y + @cy * 32 + 24).run
      end

      @cx = 1 - @cx if Input.key_push?(K_LEFT) or Input.key_push?(K_RIGHT)
      @cy -= 1 if Input.key_push?(K_UP)
      @cy += 1 if Input.key_push?(K_DOWN)
      @cy = 2 if @cy < 0
      @cy = 0 if @cy >2

      Sprite.draw @s
      draw
      Window.draw(@x + @cx * 88 + 16,@y + @cy * 32 + 29, @@cursor)
    end

    return nil
  end

  def draw
      Window.draw(@x, @y, @@image)
      if @s.size == 1
        Window.draw(@x + 60, @y, @@menu)
      end
  end
end

なんでdraw_font_exを使っているかというと、draw_fontは手元のバージョンでは修正中でまともに動かないからで深い意味はない。
ポイントになるのはMenuScene.newの第1引数で配列に自分を追加してるあたりと、Sprite.drawで配列の中身を全部描画しているところか。このクラスはSceneであって決してSpriteではないのだが、Sprite.drawはSpriteかどうかは関係なくdrawを呼びまくる機能なのでこれで問題なく動く。なんにせよ、再帰呼び出ししながら追加していった配列の中身を全部描画することで、再帰構造のメニューの描画は実現できる。drawを再帰呼び出しで辿っていくのも可能だが、そうすると描画順が逆になるから描画順のzを指定するようにしないとおかしくなる。
あとはメニューをなんらかのデータで持つようにして、きちんとした汎用メニュークラスみたいなのが作れれば実用になるんだろうけど、それすると長くなっちゃうし作っても俺使わないしということで作る予定は無い。