コマンドDSLの定義

RubyDSL(DomainSpecificLanguege)に向いた言語だと言われる。
DSLってのは特定用途に特化した仕様のミニ言語で、何かを定義する専用にちょっとした宣言・定義文を作ってそれで書けば確かにやりやすい。
そういうミニ言語をRubyのプログラム内に埋め込もうというのが言語内DSLで、Rubyはメソッドコールの構文、ブロックの存在のおかげでそういったものが作りやすい。
具体的にはこのようなコードになる。
ライブラリ側はアレコレいじり中なので今んとこ非公開だが、前の弾幕生成のんと似たようなものが動くと思ってもらえればよい。
2次元配列を並べるのと比較すると随分読みやすく書きやすい感じになっているのではなかろうか。

require 'dxruby'
require 'dxrubyex'
require 'bulletgenerator'
require 'sprite'

# コマンドDSL拡張ここから
class Command
  attr_reader :command

  def initialize
    @command = []
  end

  def method_missing(m, *arg)
    if m == :wait
      @command.push([:wait=, arg[0]])
    else
      @command.push([m] + arg)
    end
  end
end

class Sprite
  def self.define_command(&block)
      c = Command.new
      c.instance_eval(&block) if block
      c.command
  end
end

class BulletGenerator
  def self.define_command(&block)
      c = Command.new
      c.instance_eval(&block) if block
      c.command
  end
end
# コマンドDSL拡張ここまで

class Shot < Sprite
  @@image = Image.load("image/enemyshot1.png")
  @@command = define_command { # ←コマンドDSL
    wait -1
  }

  def initialize(x, y, angle, speed)
    super(x-@@image.width/2, y-@@image.height/2, angle, speed, @@command)
    self.collision = CollisionBox.new(self, 0, 0, 7, 7)
    self.collision_enable = true
    self.image = @@image
  end
end

class BL_8way < BulletGenerator
  action1 = define_command { # ←コマンドDSL
    fire_relative Shot, 0, 5
    rotation 45
  }
  @@command = define_command { # ←コマンドDSL
    call_command action1, 8
    wait 2
    rotation 12
  }
  def initialize(parent, x, y)
    super(Objects, parent, x, y, 0, @@command)
  end
end

class Enemy2 < Sprite
  @@anime_image = Image.loadToArray("image/enemy2.png", 4, 1)
  @@anime_pattern = [15, [0,1,2,3]]
  @@command = define_command { # ←コマンドDSL
    right 100, 1
    left 100, 1
  }

  def initialize(x, y)
    super(x, y, 0, 0, @@command)
    Objects.push(BL_8way.new(self, 16, 48))
    Objects.push(BL_8way.new(self, 112, 48))
    self.angle = 90
    self.anime_image = @@anime_image
    self.start_animation(@@anime_pattern)
  end
end

Objects = []
Objects.push(Enemy2.new(200,100))

font = Font.new(32)

Window.loop do
  Sprite.update(Objects)
  Sprite.clean(Objects)
  Sprite.draw(Objects)
  Window.drawFont(0, 0, Window.getLoad.to_i.to_s + " %", font, :z => 100.0)
  Window.drawFont(0, 32, Objects.size.to_s + " objects", font, :z => 100.0)
end