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 ってことで。
[ ツッコむ ]
以上、1 日分です。
