LeverDashMan

「アクションゲームアルゴリズムマニアックス」っつー本がある。
話のタネにと思ってずっと前に買ったのだが例によってそのまま積んであった。
タイトルの通り、アクションゲームにありそーなアルゴリズムについて片っ端から説明とサンプルコードが書いてある。
ありそーなって言うか、そのほとんどはかなり古いアイデア一発ゲー時代のものを取り上げているのだから、どっかで見たことあるものばっかりだ。
これをDXRubyで作ったらどうなるかなー、と思って、試してみた。

画像はサンプルのものを縮小加工した。元は縮小描画していたが、それだと何かと面倒・不便だ。DXRubyは4点指定で縮小回転描画を自由にできるようには作っていない。
色は面倒だったから画像をそのまま(白で描かれている)。サンプルの実行ファイルは白黒反転していて、これはテクスチャの色を黒で単色塗りつぶし描画しているためだ。これもDXRubyには無い。


んで、コードの方は元がC++なのを、なるべくそのまま保つように努力してみた。
サンプルのフレームワークまでそのまま移植する価値は感じられないから簡略化した。サンプルコードになっている部分(サンプルでいうStage1.hのところ、クラスで言えばLeverDashMan、StaticFloor、StaticTree、LeverDashStage)はほぼそのままのクラス構成、アルゴリズムとなっている。
どっちかと言うとこれをRubyっぽく書き直すとどうなるか、というのが気にはなるが、そもそも俺は「Rubyっぽいコード」は書けないから無茶な話だ。誰か書いてみて。
あと、絵はサンプルを加工したものだから、それを含んで公開することはできない。

require 'dxruby'

Window.width = 800
Window.height = 600
MAX_X = 16
MAX_Y = 12

class Task
  @@tasklist = []

  def initialize
    @@tasklist << self
  end

  def self.list
    @@tasklist
  end

  def draw
    Window.draw(@x * Window.width / MAX_X, @y * Window.height / MAX_Y, @image)
  end
end

class Game
  def initialize(stage)
    @stage = stage.new
  end

  def move
    @stage.move
    Task.list.delete_if {|obj| obj.move}
  end

  def draw
    Task.list.each {|obj| obj.draw}
  end
end

class LeverDashMan < Task
  attr_reader :vx
  @@image = Image.load("texture/man.png")
  MAX_SPEED = 0.5
  ACCEL = 0.01

  def initialize
    @x = 3
    @y = MAX_Y - 2
    @vx = 0.1
    @image = @@image
    super
  end

  def move
    if Input.key_down?(K_RIGHT)
      @vx += ACCEL
    elsif Input.key_down?(K_LEFT)
      @vx -= ACCEL
    else
      @vx -= ACCEL * 0.5
    end

    @vx = 0         if @vx < 0
    @vx = MAX_SPEED if @vx > MAX_SPEED

    @angle = @vx / MAX_SPEED * 0.1

    return false
  end

  def draw
    Window.draw_rot(@x * Window.width / MAX_X, @y * Window.height / MAX_Y, @image, 360 * @angle)
  end
end

class StaticFloor < Task
  attr_accessor :x
  @@image = Image.load("texture/floor.png")

  def initialize(x)
    @x = x
    @y = MAX_Y - 1
    @image = @@image
    super()
  end

  def move
    @x += MAX_X + 1 if @x <= -1
    return false
  end
end

class StaticTree < Task
  attr_accessor :x
  @@image = Image.load("texture/tree.png")

  def initialize
    @x = MAX_X + 3
    @y = MAX_Y - 4
    @image = @@image
    super
  end

  def move
    return @x <= -3
  end
end

class LeverDashStage
  def initialize
    @man = LeverDashMan.new
    (0..16).each do |x|
      StaticFloor.new(x)
    end
    @dist = 0
  end

  def move
    Task.list.each do |obj|
      obj.x -= @man.vx if !obj.is_a?(LeverDashMan)
    end
    @dist -= @man.vx
    if @dist < 0
      StaticTree.new
      @dist = rand * MAX_X
    end
  end
end

game = Game.new(LeverDashStage)

Window.loop do
  game.move
  game.draw
end