mieki256's diary



2014/06/22() [n年前の日記]

#1 [dxruby] DXRubyで通路の奥に進むようなソレ

DXRubyを使って、通路の奥に進んでいくようなソレを実験。以下のような感じに。

passageway_ss.gif
うむ。レイフォースですな。あるいはギャラクシーフォース。

やってることは、真ん中に穴が開いた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 日分です。

過去ログ表示

Prev - 2014/06 - 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

カテゴリで表示

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


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

Powered by hns-2.19.6, HyperNikkiSystem Project