CommandとExecuter

DXRubyFramework0.0.2ではSpriteクラスはコマンド列を処理する機能を持っているが、コマンド列は配列であるので、それをもうちょっと簡単に簡易DSLで記述するためのCommandクラスを用意した。
用意したというほどのものでもない。

class Command
  attr_reader :command

  def initialize
    @command = []
  end

  def method_missing(m, *arg)
    @command.push([m] + arg)
  end

  def self.generate(&block)
    c = Command.new
    c.instance_eval(&block) if block
    c.command
  end
end

これをrequireしておいて、Command.generateでコマンドを並べれば配列が返ってくる。
前に作ったものを大幅に簡素化しただけだ。シンプルなものが好みなのでこうなった。


0.0.1のSpriteクラスのクラスメソッド(updateなど)はSpriteオブジェクトしか扱えなかったが、今回はSpriteオブジェクト以外の場合にupdateやdrawメソッドがあった時にのみそれを呼び出すようにしてみた。
Spriteじゃなくてもそのメソッドがあれば、配列に突っ込んで1フレーム1回呼び出すことができる。Sprite.cleanはdelete_flagメソッドの戻りを見て配列から削除してくれる。いまのところ衝突判定までは対応しておらず、Sprite以外は無視される。
この機能を応用するとSpriteクラスを使わない自作の描画プリミティブや、そもそも描画と関係無い処理まで統一して扱えるようになる、かもしれない。別にすばらしいアイデアというわけではなく、タスクでシステム全てを管理しようというのはよくある話である。そのようにやろうと思えばできるし、やらなくてもよい。
で、これを使って汎用のコマンド処理を提供するためのExecuterクラスも用意した。
用意したというほどのものでもない。

class Executer
  attr_accessor :wait, :delete_flag, :command

  def initialize(command)
    @pc = 0
    @wait = 0
    @command = command
    @delete_flag = false
  end

  def update
    while(@wait == 0)
      c = @command[@pc]
      if c.size == 2
        self.__send__ c[0], c[1]
      else
        self.__send__ c[0], *c[1..-1]
      end
      @pc += 1
      if @pc == @command.size
        @pc = 0
      end
    end
    @wait -= 1
  end

  def wait(count)
    @wait = count
  end

  def vanish
    @delete_flag = true
  end
end

これも前のやつを大幅に簡素化したもので、waitとvanishだけをコマンドとして実装してある。描画プリミティブではないのでdrawは無い。
ただし、これを使うとアリガチなupdateメソッドを作れなくなるので、その辺がちょっと困った感じだ。
Cで書いてもよかったのだが、Spriteと違って大量に処理するようなものに使うこともないだろうし、Cで書いてもほとんど速度は変わらない(どうせメソッド呼び出ししかしない)ので、中身が見えるようにRubyで。
特性としては、ノンプリエンプティブなマルチタスク、ようするにコルーチンモドキのような振る舞いをする。
これで何ができるかというと、アイデア次第で何でも、ということになるわけだが、例えばSTGで敵の出現パターンをこれで作ることができる。
実際に出現させるコードはコマンドとしてメソッドに書いて、それを指定のフレームで呼び出すようにコマンド列を作る感じだ。
その他、弾幕生成器を作ったり、Sprite.updateから呼び出す使い方から離れれば、フレーム数指定で動くもの以外の、なんでも。モノがシンプルだしパブリックドメインのコードにするから、好きなように改造して拡張して自由に使ってもらえるとよい。