キャラ移動の仕組み
久しぶりに更新。
何かやっていたかというと、Ruby1.9.1のVMを調べたり、そんな感じ。
別にそれで何かすごいことを発表しようというわけではなく、前から考えていたキャラ移動をコマンド列でラクに作ろうという試みのプロトタイプができたかなー、という話。
画面はパッとしないがとりあえず。
require 'dxruby' class Sprite attr_accessor :x, :y, :wait, :dx, :dy attr_reader :angle, :speed, :delete TO_RADIAN = 1 / 180.0 * Math::PI def initialize(x, y, angle, speed, image, command) @x = x @y = y @angle = angle @speed = speed @image = image @pc = 0 @wait = 0 @dx = Math.cos(@angle * TO_RADIAN) * @speed @dy = Math.sin(@angle * TO_RADIAN) * @speed @command = command @delete = false @width = @image.width @height = @image.height end def update while(@wait == 0) do c = @command[@pc] self.__send__ c[0], *c[1] @pc += 1 @pc = 0 if @pc == @command.size end @x += @dx @y += @dy self.out_screen if @x + @width < 0 or @x > 639 or @y + @height < 0 or @y > 479 @wait -= 1 end def set(x,y,angle,speed) @x = x @y = y @angle = angle @speed = speed @dx = Math.cos(angle * TO_RADIAN) * speed @dy = Math.sin(angle * TO_RADIAN) * speed end def angle=(angle) @angle = angle @dx = Math.cos(angle * TO_RADIAN) * @speed @dy = Math.sin(angle * TO_RADIAN) * @speed end def rotation(d_angle) @angle += d_angle @dx = Math.cos(@angle * TO_RADIAN) * @speed @dy = Math.sin(@angle * TO_RADIAN) * @speed end def speed=(speed) @speed = speed @dx = Math.cos(@angle * TO_RADIAN) * speed @dy = Math.sin(@angle * TO_RADIAN) * speed end def accel(d_speed) @speed += d_speed @dx = Math.cos(@angle * TO_RADIAN) * @speed @dy = Math.sin(@angle * TO_RADIAN) * @speed end def draw Window.drawRot(@x, @y, @image, @angle - 90) end def out_screen @delete = true end end class Shot < Sprite @@image = Image.new(10,10,[255,255,0]) @@command = [ [:wait=, -1], ] def initialize(x, y, angle) super(x, y, angle, 5, @@image, @@command) end def out_screen @dx = -@dx if @x + @width < 0 or @x > 639 @dy = -@dy if @y + @height < 0 or @y > 439 end end class Enemy < Sprite @@image = Image.new(10,10,[255,255,255]) @@command = [ [:wait=, 2], [:rotation, 10], [:fire, nil], ] def initialize(x, y) super(x, y, 0, 2, @@image, @@command) end def fire Objects.push(Shot.new(@x, @y, @angle)) end end a = Enemy.new(200,200) Objects = [a] font = Font.new(32) Window.loop do Objects.each do |obj| obj.update end Objects.delete_if do |obj| obj.delete end Objects.each do |obj| obj.draw end 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
まだ基本機能すら揃ってない状態ではあるが、動くので。
コマンドはメソッド名のシンボルで、それと引数をまとめて配列にしたものを配列にしたものがコマンド列となる。
ようするにひたすらメソッドを呼ぶだけだ。
ただのメソッドコールだから、派生クラスでコマンドを勝手に作ることもできる。
あとは、直後のコマンドを指定回数繰り返すとか、別のコマンド列をコールするとか、そういうことができるようになれば、そこそこ柔軟になるかな。
見ればわかると思うがwait=コマンドが来るまで繰り返しコマンドを実行し、wait=で指定されたフレーム分だけそのまま移動する。wait=にマイナスを指定すると無限回数となる。
実際にはずっと動かしていればFixnumがオーバーフローするが、60fpsで動かしていてオーバーフローするってどんだけー。