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)
[ ツッコむ ]
以上です。







