2014/06/07(土) [n年前の日記]
#1 [dxruby] DXRubyでラスタースクロール処理に再チャレンジ
_2014/01/12
に、DXRuby の Shader (HLSL)を使ってラスタースクロールの実験を ―― ラスタースクロールだけで疑似3Dっぽい表現ができないか実験していたのですが。ラスタースクロールに使える画像の作り方が分からなくて、諦めてしまったのでした。
ふと、今頃になって、「こういう画像を作ればよかったのではないか?」とピンと来たので試してみたり。
結果的には、こんな感じの画面になりました。 うむ。ストIIですな。
ふと、今頃になって、「こういう画像を作ればよかったのではないか?」とピンと来たので試してみたり。
結果的には、こんな感じの画面になりました。 うむ。ストIIですな。
- 天井と床は、y値に応じて、x方向のスクロール量を変える。
- 上辺(下辺)が1ドットスクロールしたら、下辺(上辺)は2ドットスクロールするようにした。
- 横方向のスクロールは、64ドットでループ。
◎ 画像を作る際のコツ。 :
たぶん、こういう画像を作ればいいんじゃないかな…。
ピンク色のところが、最終的に欲しい画像。
例えば、GIMPで作業するなら、
グリッドに合わせて線を引いていくのは面倒臭かったので、下地画像を作成するスクリプトを書いてみたり。
_mk_ras_img.rb
そして、これらの下地画像を参考にしつつ、GIMPで画像を作成。以下のような感じに。
ついでに、柱の画像もテキトーに作成。テクスチャを適当に作って、フィルター → マップ → オブジェクトにマップ → 円柱、とかそんな感じ。
例えば、GIMPで作業するなら、
- 変形ツールの遠近法?だかで、上辺896ドット、下辺1792ドットの台形画像を作成。
- 真ん中あたりから、896 x 160ドットぐらいをクリップ。
- 台形画像を作る際、下辺の長さを、上辺の2倍の長さにすると、処理や計算が楽。
- 最終的に必要な画像の横幅は、ウインドウサイズ+グリッドサイズ(64ドット) x 4 にしておくと楽。
グリッドに合わせて線を引いていくのは面倒臭かったので、下地画像を作成するスクリプトを書いてみたり。
_mk_ras_img.rb
# # ラスタースクロール用BGの下絵画像を生成して保存する。 # 天井、壁、床、の3つの画像を保存する。 # require 'dxruby' # ---------------------------------------- # 定義部分 wdw_w = 640 # ウインドウ横幅 wdw_h = 480 # ウインドウ縦幅 cw = 128 / 2 # 1マス分のドットサイズ h0 = 32 * 5 h2 = 32 * 5 h1 = wdw_h - h0 - h2 w = wdw_w + cw * 4 # 画像横幅 # 出力ファイル名 output_fname = [ "bg_ras_a_base.png", "bg_ras_b_base.png", "bg_ras_c_base.png", ] # 定義部分ここまで # ---------------------------------------- # 新規画像作成 img0 = Image.new(w, h0, [0, 0, 0, 0]) img1 = Image.new(w, h1, [0, 0, 0, 0]) img2 = Image.new(w, h2, [0, 0, 0, 0]) # 線を引く cx = w / 2 0.step(cx, cw) do |d| a0 = d a1 = d * 2 img0.line(cx + a1, 0, cx + a0, h0, C_WHITE) img0.line(cx - a1, 0, cx - a0, h0, C_WHITE) img1.line(cx + a0, 0, cx + a0, h1, C_WHITE) img1.line(cx - a0, 0, cx - a0, h1, C_WHITE) img2.line(cx + a0, 0, cx + a1, h2, C_WHITE) img2.line(cx - a0, 0, cx - a1, h2, C_WHITE) end # 保存する img0.save(output_fname[0]) img1.save(output_fname[1]) img2.save(output_fname[2]) font = Font.new(14) # 表示して確認 Window.loop do break if Input.keyPush?(K_ESCAPE) x = (Window.width - w) / 2 y = 0 Window.draw(x, y, img0) y += img0.height Window.draw(x, y, img1) y += img1.height Window.draw(x, y, img2) y += img2.height [ output_fname[0], output_fname[1], output_fname[2], "を保存しました" ].each_with_index do |s, i| Window.drawFont(4, 4 + i * 20, s, font) end end実行すると、以下の3つの画像が保存される。
そして、これらの下地画像を参考にしつつ、GIMPで画像を作成。以下のような感じに。
- 128x128の画像を作成。これが1マス分。
- フィルタ → マップ → 並べる、で、1792 x 640 ドットの画像を作成。
- 遠近法ツールで、遠近感がついた台形に変形。ガイドを作って、頂点をガイドに合わせるようにすれば作業が少し楽。x=448,x=1344の位置にガイドを作ればいいのかな。
- ガイドの作り方は、画像 → ガイド → 新規ガイド。それと、表示 → ガイドにスナップ、にチェックが入っていること。
- 変形できたら、矩形選択ツールで、必要な部分だけコピー。
- ファイル → 画像の生成 → クリップボードから、を選べば新規画像ができる。
- ブレンドツール(グラデーションを塗るツール)や、色調整で、見た目を修正。
ついでに、柱の画像もテキトーに作成。テクスチャを適当に作って、フィルター → マップ → オブジェクトにマップ → 円柱、とかそんな感じ。
◎ スクリプトはこんな感じに。 :
スクリプトはこんな感じに。
_bgrasterscroll3.rb.txt
実行すれば、前述のスクリーンショットのような画面になるはず。マウスカーソルのx座標で、スクロール速度が変わります。
他のスクリプトから呼んで使えるようにもしておきました。以下は使用例。
画像も含めて、一式をzipにして置いときます。ライセンスは、Public Domain ってことで。
_bg_raster_scroll3_20140607.zip (2.9MB)
_bgrasterscroll3.rb.txt
# Shaderを使ってラスタスクロールのテスト # 天井、壁、床をラスタースクロールさせるっぽい感じの処理 # 画像を読み込んで表示する版 require 'dxruby' class BgRasterScroll3 attr_accessor :gridsize attr_accessor :imgs attr_accessor :shaders # コンストラクタ # # @param [Number] gridsize 1マス分のドットサイズ # @param [Array] imgs 天井、壁、床のImageオブジェクトを配列に入れて渡す # def initialize(gridsize, imgs) hlsl = <<EOS float2 size; float d; float dd; texture tex0; sampler Samp0 = sampler_state { Texture =<tex0>; // こっちだと画面がチラチラする (´・ω・`) // MinFilter = LINEAR; // MagFilter = LINEAR; // MipFilter = LINEAR; // こっちならチラチラしない MinFilter = POINT; MagFilter = POINT; MipFilter = NONE; AddressU = WRAP; AddressV = WRAP; }; struct PixelIn { float2 UV : TEXCOORD0; }; struct PixelOut { float4 Color : COLOR0; }; PixelOut PS1(PixelIn input) { PixelOut output; input.UV.x = input.UV.x + ((d + (d * input.UV.y * dd)) / size.x); output.Color = tex2D( Samp0, input.UV ); return output; } PixelOut PS2(PixelIn input) { PixelOut output; input.UV.x = input.UV.x + ((d + (d * (1.0 - input.UV.y) * dd)) / size.x); output.Color = tex2D( Samp0, input.UV ); return output; } PixelOut PS3(PixelIn input) { PixelOut output; input.UV.x = input.UV.x + d / size.x; output.Color = tex2D( Samp0, input.UV ); return output; } // float4 PS(float2 input : TEXCOORD0) : COLOR0 // { // float4 output; // // input.x -= ((d / size.x) + ((d / size.x) * input.y * dd)); // input.x = input.x - ((d + (d * input.y * dd)) / size.x); // output = tex2D( Samp0, input); // return output; // } technique FloorScroll { pass P0 { PixelShader = compile ps_2_0 PS1(); } } technique CeilingScroll { pass P0 { PixelShader = compile ps_2_0 PS2(); } } technique WallScroll { pass P0 { PixelShader = compile ps_2_0 PS3(); } } EOS core = Shader::Core.new(hlsl, {:size=>:float, :d=>:float, :dd=>:float}) self.shaders = [] self.imgs = [] self.gridsize = gridsize shader_names = [ ["CeilingScroll", 1.0], ["WallScroll", 1.0], ["FloorScroll", 1.0] ] shader_names.each_with_index do |dt, i| sname, dd = dt img = imgs[i] self.imgs.push(img) shr = Shader.new(core, sname) shr.size = [img.width, img.height] shr.d = 0 # スクロール量 shr.dd = dd # 上辺と下辺で、x方向に何ドットずれるか self.shaders.push(shr) end end # スクロール量を更新 # # @param [Number] d スクロール量 # def update(d) d = (d % self.gridsize) self.shaders.each do |shr| shr.d = d end end # 描画 # # @param [Number] bx 描画位置 x # @param [Number] by 描画位置 y # def draw(bx, by) x, y = bx - self.gridsize * 2, by 3.times do |i| Window.drawShader(x, y, self.imgs[i], self.shaders[i]) y += self.imgs[i].height end end end if __FILE__ == $0 # ---------------------------------------- # 動作確認 imgs = [] # 画像ファイル名一覧に従って画像ロード [ "bg_ras_a.png", # 天井 "bg_ras_b.png", # 壁 "bg_ras_c.png" # 床 ].each {|fn| imgs.push(Image.load(fn)) } # ラスタースクロール用オブジェクトを発生 ras = BgRasterScroll3.new(64, imgs) # 柱スプライトを発生 pole = Image.load("pole.png") sprs = [] 4.times do |i| spr = Sprite.new(i * (96 * 2) + 72, 480 - (160 /2) - 128 + 8, pole) sprs.push(spr) end font = Font.new(14) x = 0 Window.loop do break if Input.keyPush?(K_ESCAPE) # マウス位置に応じてスクロール量を変化 dx = ((Window.width / 2) - Input.mousePosX).to_f / Window.width * 32 x -= dx ras.update(x) # スクロール量を指定 ras.draw(0, 0) # 描画 # 柱を描画 tdx = dx + dx * 0.5 sprs.each do |spr| spr.x += tdx spr.x += Window.width + 128 if dx < 0 and spr.x + 64 < 0 spr.x -= (Window.width + 128) if dx > 0 and spr.x > Window.width end Sprite.update(sprs) Sprite.draw(sprs) Window.drawFont(4, 4, "dx = #{dx} , x = #{x}", font) end end
実行すれば、前述のスクリーンショットのような画面になるはず。マウスカーソルのx座標で、スクロール速度が変わります。
他のスクリプトから呼んで使えるようにもしておきました。以下は使用例。
# BgRasterScroll3.rb の呼び出しテスト require 'dxruby' require_relative 'bgrasterscroll3' imgs = [] [ "bg_ras_a_base.png", # 天井 "bg_ras_b_base.png", # 壁 "bg_ras_c_base.png" # 床 ].each {|fn| imgs.push(Image.load(fn)) } ras = BgRasterScroll3.new(64, imgs) x = 0 Window.loop do break if Input.keyPush?(K_ESCAPE) ras.update(x) # スクロール量を指定 ras.draw(0, 0) # 描画 x += 3 end
画像も含めて、一式をzipにして置いときます。ライセンスは、Public Domain ってことで。
_bg_raster_scroll3_20140607.zip (2.9MB)
[ ツッコむ ]
以上です。