2014/06/22(日) [n年前の日記]
#1 [dxruby] DXRubyで通路の奥に進むようなソレ
DXRubyを使って、通路の奥に進んでいくようなソレを実験。以下のような感じに。
うむ。レイフォースですな。あるいはギャラクシーフォース。
やってることは、真ん中に穴が開いた1枚画を、z値に基づいて、拡大縮小表示してるだけ。
予想では、描画面積がとんでもなく大きくなるから処理落ちするんじゃないか ―― 複数画像に分割して組み合わせて描画して、最低限の面積を描画するようにしないと実用にならないのでは、と思っていたけど。おそらくGPUがイイ感じに頑張ってくれているのか、分割せずに1枚画を連続でドドドと描画しても全然処理落ちせずに済みました。ありがたや。
ソースは以下。
_passagewaytypea.rb
他のスクリプトからも呼び出せるようにしておきました。以下のような感じで呼び出せます。
使用画像も置いときます。
_wall1.png
_wall2.png
_floor_fg.png
ソースと画像のライセンスは、 Public Domain または CC0 ってことで。
うむ。レイフォースですな。あるいはギャラクシーフォース。
やってることは、真ん中に穴が開いた1枚画を、z値に基づいて、拡大縮小表示してるだけ。
予想では、描画面積がとんでもなく大きくなるから処理落ちするんじゃないか ―― 複数画像に分割して組み合わせて描画して、最低限の面積を描画するようにしないと実用にならないのでは、と思っていたけど。おそらくGPUがイイ感じに頑張ってくれているのか、分割せずに1枚画を連続でドドドと描画しても全然処理落ちせずに済みました。ありがたや。
ソースは以下。
_passagewaytypea.rb
require 'dxruby' # # 通路を奥に進んでいく背景演出 # class PassageWayTypeA attr_accessor :walls # # 壁1枚分 # class Wall attr_accessor :image attr_accessor :x, :y, :z attr_accessor :sz attr_accessor :angle_z attr_accessor :shader # 明度調整用 @@hlsl = <<EOS float v; texture tex0; sampler Samp0 = sampler_state { Texture =<tex0>; }; float4 PS(float2 input : TEXCOORD0) : COLOR0 { float4 output; output = tex2D( Samp0, input ); output.rgb *= v; return output; } technique { pass { PixelShader = compile ps_2_0 PS(); } } EOS # # コンストラクタ # # @param [Object] img Imageオブジェクト # @param [Number] x 初期位置x # @param [Number] y 初期位置y # @param [Number] z 初期位置z # @param [Number] scr_z 視点からスクリーン(画面)までの距離 # def initialize(img, x, y, z, scr_z) self.image = img self.sz = scr_z self.angle_z = 0 self.x = x self.y = y self.z = z core = Shader::Core.new(@@hlsl, {:v => :float}) self.shader = Shader.new(core) self.shader.v = 1.0 end # # 座標を更新 # # @param [Number] dx 速度x # @param [Number] dy 速度y # @param [Number] dz 速度z # @param [Number] d_ang_z z軸回転速度 # def update(dx, dy, dz, d_ang_z = 0) self.x += dx self.y += dy self.z += dz self.angle_z += d_ang_z end # # 描画 # # @param [Number] bx 描画位置オフセットx # @param [Number] by 描画位置オフセットy # def draw(bx, by) sx = (self.sz * (bx + self.x) / self.z) + Window.width / 2 sy = (self.sz * (by + self.y) / self.z) + Window.height / 2 scale = self.sz / self.z a = 1.0 - (1.0 * (self.z - 300) / 2900.0) a = 0 if a < 0 a = 1.0 if a > 1.0 self.shader.v = a Window.drawEx(sx, sy, self.image, :scale_x => scale, :scale_y => scale, :center_x => self.image.width / 2, :center_y => self.image.height / 2, :angle => self.angle_z, :offset_sync => true, :z => -self.z, :shader => self.shader) end end # # コンストラクタ # # @param [Array] imgs Imageオブジェクトの配列 # @param [Number] scr_z 視点からスクリーン(画面)までの距離 # @param [Number] num 壁の枚数 # def initialize(imgs, scr_z, num = 8) self.walls = [] z = 3200.0 zadd = 3200 / num num.times do |i| x, y = 0, 0 self.walls.push(Wall.new(imgs[i % imgs.length], x, y, z, scr_z)) z += zadd end end # # 座標を更新 # # @param [Number] dx 速度x # @param [Number] dy 速度y # @param [Number] dz 速度z # @param [Number] d_ang_z z軸回転速度 # @param [Boolean] del_enable trueなら、スクリーン手前に来た段階で壁を消去 # @return [Number] 壁の数を返す # def update(dx, dy, dz, d_ang_z = 0, del_enable = false) self.walls.each do |spr| spr.update(dx, dy, dz, d_ang_z) if spr.z < spr.sz - 100 # 投影面より手前に来たので if del_enable self.walls.delete(spr) else # 遠方に配置し直し spr.z += 3200.0 spr.x, spr.y = 0, 0 end end end return self.walls.size end # # 描画 # # @param [Number] bx 描画位置オフセットx # @param [Number] by 描画位置オフセットy # def draw(bx, by) self.walls.each { |spr| spr.draw(bx, by) } end end if $0 == __FILE__ # ---------------------------------------- # 使用例 font = Font.new(14) wall_max = 8 scrz = 300 imgs = [ Image.load("wall1.png"), Image.load("wall2.png"), ] bg = PassageWayTypeA.new(imgs, scrz, wall_max) floor = Image.load("floor_fg.png") step = 0 mouse_move = false Window.loop do break if Input.keyPush?(K_ESCAPE) # Xキーでマウス移動を反映させるか否かを反転 mouse_move = !mouse_move if Input.keyPush?(K_X) if mouse_move mx = Input.mousePosX - Window.width / 2 my = Input.mousePosY - Window.height / 2 else mx, my = 0, 0 end case step when 0 # 通常描画 bg.update(0, 0, -25) bg.draw(mx, my) # zキーが押されたら壁が消えていく処理へ移行 step += 1 if Input.keyPush?(K_Z) when 1 # 壁が徐々に消えていく処理 if bg.update(0, 0, -25, 0, true) <= 0 # 壁がすべて消えた step += 1 end bg.draw(mx, my) when 2 # 壁を再発生 bg = PassageWayTypeA.new(imgs, scrz, wall_max) step = 0 end Window.draw(rand(8) - 4, rand(3), floor) s = "#{Window.real_fps.to_i} FPS CPU: #{Window.getLoad.to_i} %" Window.drawFont(4, 4, s, font) end end
他のスクリプトからも呼び出せるようにしておきました。以下のような感じで呼び出せます。
require "dxruby" require_relative "passagewaytypea" imgs = [ Image.load("wall1.png"), Image.load("wall2.png"), ] bg = PassageWayTypeA.new(imgs, 300) floor = Image.load("floor_fg.png") Window.loop do break if Input.keyPush?(K_ESCAPE) bg.update(0, 0, -25) bg.draw(0, 0) Window.draw(rand(8) - 4, rand(3), floor) end
使用画像も置いときます。
_wall1.png
_wall2.png
_floor_fg.png
ソースと画像のライセンスは、 Public Domain または CC0 ってことで。
[ ツッコむ ]
以上です。