mieki256's diary



2016/12/22(木) [n年前の日記]

#1 [ruby][dxruby] 疑似3Dの初歩の基礎を(Ruby+DXRubyを使いつつ)書いてみる

_Ruby Game Developing Advent Calendar 2016 の12/19が空欄だったので、「何も入ってないよりは何か入ってたほうがええんじゃないか…」的に、この記事を登録してみました。「Rubyでゲーム制作」というテーマから全然ずれてる内容だよなと思わないでもないですが、DXRubyを使って実験してるし…いいよね? ダメ? Advent Calendar って後からこうしてアップ・登録してもいいのかどうかも分かりませんが…。

さておき。なんとなく、2Dゲームにおける疑似3Dの初歩というか基礎をメモしておこうかなと思い立ったりして。

いやまあ、「あらゆるゲームが3DCGで描画されてるこの御時勢にwww疑似3Dってwwwちょおまwww」と笑われそうな気もしますが。なんかその手の記事をぼんやり眺めてたら、式を求めるあたりで「三角形の相似」の一言で終わらせてる事例が結構あって。「説明がちょっと足りてないんじゃ」「図解しとけばすぐ分かるのに」と思えてきたので、せめてそこだけでもメモしておこうかなと。

疑似3Dと言っても、一般的には…。内部では x,y,z の3次元座標で管理していて、描画に使える何かしらが当時はスプライトかBGしかなかったからスプライト or BGで描画、みたいなことをやってたんだろうと思うわけですよ。もっとも中にはコレ全然3D計算してないだろうなって思えるアレなタイトルもありますが…。それっぽく見えてたタイトルは、内部では3次元座標で管理して、ということを多少はしてただろうと。

で、3次元空間に点があるとして、その点は画面上のどのへんに描かれるのか、ソレさえ分かれば疑似3Dはできるのですが。ソレを求める処理というか、図法のことを、 _透視投影 と呼ぶわけで。

まあ、近くのモノも遠くのモノも同じ大きさに見える _平行投影 てのもあるし、平行投影を使ってるゲームタイトルもありますが、ソレは奥行き情報であるz座標値を無視して描画すれば済んじゃうから横に置いとくとして。

透視投影をしたいなー、と思った時、実際どういう計算式になるかは、下の図を見てもらえば分かるかなと。ちなみに、画像をクリックすれば原寸大で見れます。

fake3d.png

分かりやすくするために、見ている人の目の位置を原点(0,0,0)として考えてます。また、この図では画面上のy座標しか求めてませんが、x座標も同じように考えて式を求めることができます。

ということで結局は、巷の解説記事でフツーに見かける…
画面上のx座標 sx = 画面までの距離 sz * 点のx座標 px / 点のz座標 pz
画面上のy座標 sy = 画面までの距離 sz * 点のy座標 py / 点のz座標 pz
この式になるわけですね。

実践その1。 :

本当にこの式でそれらしくなるのか、実際に試してみましょう。Ruby + DXRuby でサンプルを書いてみましたよ。

_fake3d_a.rb
require "dxruby"

img = Image.load("tmp_dot.png")

# 点の座標群を作成
py = 600.0
scale = 200.0
pos = []
10.times do |z|
  10.times do |x|
    pos.push([x * scale, py, z * scale])
    pos.push([x * -scale, py, z * scale])
  end
end

# 画面までの距離
sz = 75

Window.loop do
  break if Input.keyPush?(K_ESCAPE)

  # ウインドウの中心位置を取得
  cw = Window.width / 2
  ch = Window.height / 2

  # 画像横幅、縦幅を取得
  iw = img.width
  ih = img.height

  # マウス座標を点群の移動量にする
  mx = (Input.mousePosX - cw) * 5
  my = (Input.mousePosY - ch) * 5

  # 点の座標を透視投影で求めて描画
  pos.each do |px, py, pz|
    px += mx
    pz += my
    if pz > 0
      sx = sz * px / pz
      sy = sz * py / pz
      x = sx - iw / 2 + cw
      y = sy - ih / 2 + ch
      Window.draw(x, y, img, -pz.to_i)
    end
  end
end

画像は _tmp_dot.png を使ってください。

ruby fake3d_a.rb で実行したら、こういう結果になりました。



一見それらしく見えてますね。座標の変換は上手く行ってるみたい。

ただ、ちょっと問題が。

実践その2。 :

前述のスクリプトでは、各スプライト相当がどれも同じ大きさなので、なんだか不自然ですな。描画サイズも、z座標に応じて変化させてみませう。

_fake3d_b.rb
require "dxruby"

img = Image.load("tmp_dot.png")

# 点の座標群を作成
py = 600.0
scale = 200.0
pos = []
10.times do |z|
  10.times do |x|
    pos.push([x * scale, py, z * scale])
    pos.push([x * -scale, py, z * scale])
  end
end

# 画面までの距離
sz = 75

Window.loop do
  break if Input.keyPush?(K_ESCAPE)

  # ウインドウの中心位置を取得
  cw = Window.width / 2
  ch = Window.height / 2

  # 画像横幅、縦幅を取得
  iw = img.width
  ih = img.height

  # マウス座標を点群の移動量にする
  mx = (Input.mousePosX - cw) * 5
  my = (Input.mousePosY - ch) * 5

  # 点の座標を透視投影で求めて描画
  pos.each do |px, py, pz|
    px += mx
    pz += my
    if pz > 0
      ssz = sz / pz
      sx = px * ssz
      sy = py * ssz
      scale = 5.0 * ssz
      x = sx + cw - iw / 2
      y = sy + ch - ih / 2
      Window.drawEx( x, y, img,
                    :scalex => scale, :scaley => scale,
                    :center_x => iw / 2, :center_y => ih / 2,
                    :z => -pz.to_i)
    end
  end
end

ruby fake3d_b.rb で実行してみると…。



これならイイ感じじゃないですかね。

その他色々。 :

たったこれだけのことが分かってるだけでも、色々な見せ方ができます。

_mieki256's diary - DXRubyで通路の奥に進むようなソレ
passageway_ss.gif

_mieki256's diary - DXRubyで円柱っぽいBG描画をしてみたり
pipe_bg_ss_2.gif

_mieki256's diary - DXRuby+Shaderで床ラスタースクロールっぽいことができたような気がする
床ラスタースクロール

_mieki256's diary - 床ラスター処理をもうちょっと修正
壁として描画
壁として描画その2
壁として描画その3

それぞれの処理を書く際に使った知識(?)は、基本的には一番最初に乗せた図のソレだけです。ソレの応用で全部やってます。

fake3d.png

ということで、スプライトとBG描画しかできない2Dゲームライブラリでも、基礎というか初歩というかそのへん分かってるだけでも、こういうことができますよ、てな話でした。

OpenGL使ったほうが早いんじゃないかという気もする。 :

DXRubyというライブラリは2Dゲーム用のライブラリなんで、ちょっと変わった見た目にしたいなと思ったら、昔の2Dゲームで活用してたこういうソレを使うことになったりするんですけど。

これが、Ruby + gosu だったら、そのものズバリの、opengl というライブラリを追加して使えるようでして。

_gosu-examples というサンプル群の中に、 _opengl_integration.rb というサンプルがあるんですよ。以下に動作画面のキャプチャを貼ってみますが。



ちょっと分かりづらいけど、背景が3Dで描画されてますよね。

なので、「手前のオブジェクト群は分かりやすい2Dゲームのノリで」「でも背景だけは今風っぽく3Dで」てな希望がある時は、Ruby + gosu + opengl で作ってみるのも手かもしれないな、とも思ったりしました。

まあ、最初から3D描画をガシガシ使いたいならUnityあたりを選ぶのが妥当かも、とも思うんですけど。

以上、1 日分です。

過去ログ表示

Prev - 2016/12 - Next
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31

カテゴリで表示

検索機能は Namazu for hns で提供されています。(詳細指定/ヘルプ


注意: 現在使用の日記自動生成システムは Version 2.19.6 です。
公開されている日記自動生成システムは Version 2.19.5 です。

Powered by hns-2.19.6, HyperNikkiSystem Project