2008-01-11
[Ruby]マリオカートっぽい画面をどうやって実現するかのメモ
昨日の話の続で、座標を3D管理すると何が必要かを考えていたら、コードができたのでメモしておく。久しぶりに3D考えたけど、以前みたいに頭の中で座標系を回せなくなってる(汗)。回転順序とかちょっと迷った。
自分の思いつく限りだと、やっぱり座標に関しては3D処理を真面目にやっちゃうのが、キャラクター制御とかゲームレベルの実装をするときに楽だと思う。 座標を3Dで管理するといっても下のようにそれほど複雑にはならない。
地面に関しては、Ground#renderで計算している地面のスクリーン上の4点座標をテクスチャーに渡して描画したい。
下のカメラは、視点の位置と角度を設定するAPIになっているけど、これは注視点の位置とそこからの距離(と角度)を設定するAPIにしたほうが使いやすい。マリオカートとかだとキャラクターの後ろにカメラが来るので。
class Vector3D < Array
%w(x y z).each_with_index do |e, i|
eval "def #{e} ; self[#{i}] ; end"
eval "def #{e}=a ; self[#{i}] = a ; end"
end
%w(+ - * /).each do |e|
eval "def #{e}d
Vector3D[self[0] #{e} d[0], self[1] #{e} d[1], self[2] #{e} d[2]]
end"
end
include Math
def rotate_zyx(angle)
ax, ay, az = angle
px, py, pz = x, y, z
px, py = px * cos(az) - py * sin(az), px * sin(az) + py * cos(az)
pz, px = pz * cos(ay) - px * sin(ay), pz * sin(ay) + px * cos(ay)
py, pz = py * cos(ax) - pz * sin(ax), py * sin(ax) + pz * cos(ax)
Vector3D[px, py, pz]
end
end
class Camera
attr_accessor :position, :angle, :scale, :screen_z
def initialize
@position = Vector3D[0, -200, -1000]
@angle = Vector3D[Math::PI / 20, 0, 0]
@screen_z = 800.0
@scale = 1.0
end
def to_screen(vector3d)
temp = (vector3d - position).rotate_zyx(angle)
perse = screen_z / temp.z
[temp.x * perse + screen.w / 2, temp.y * perse + screen.h / 2, perse * @scale]
end
def render(*objects)
objects.to_a.flatten.each do |e|
e.view.x, e.view.y, e.view.scale = to_screen(e.position)
e.render
end
end
end
class Character
attr_accessor :position
attr_reader :view
def self.image_resource(fname)
class_eval "def image_file_name; '#{fname}'; end"
end
def initialize
@view = TransparentImage.new(image_file_name)
@position = Vector3D[0, 0, 0]
end
def update
@view.update
end
def render
@view.render
end
end
class Ground
def initialize(camera)
@camera = camera
@tops = [
Vector3D[-1000, 0, -1000],
Vector3D[ 1000, 0, -1000],
Vector3D[ 1000, 0, 1000],
Vector3D[-1000, 0, 1000],
]
end
def render
tops_2d = @tops.map {|top| @camera.to_screen(top) }
# @view.render ...
end
end
Ruby/SDLだと拡大縮小が遅くて使い物にならないから、この手の制御はダメだったんだけど、 Star Rubyは拡大縮小が高速らしいので、その辺に期待してる。