mieki256's diary



2013/12/31(火) [n年前の日記]

#1 [dxruby] DXRubyのSpriteを勉強中

Spriteクラスの衝突判定について簡単なサンプルを書きながら勉強中。とりあえず、Sprite.check(攻撃側Spriteの配列、守備側Spriteの配列) を呼べば、Spriteを継承したクラスの、shot()、hit() が呼ばれるらしいと分かったり。その中に、当たった時の処理を書けばどうにかなるらしい。

スプライトの基準座標が左上座標に固定されてるあたりがしっくりこなかったのだけど。DXRuby開発版なら、self.offset_sync = true を設定してやることで、画像の拡大縮小回転の中心位置を基準座標として扱えることを今頃知ったり。コレ、正式公開版に反映されたら嬉しいのだけど…。とりあえず、DXRuby 1.4.0 に該当機能は無いので、手元のサンプルでは基準座標+オフセット値を表示座標としてわざわざ設定したり。…そういや、開発版のソレって、アタリ範囲はどうなるんだろう。

さらにDXRuby開発版には、Sprite#flush(色配列) で、フラッシュ画像を生成できるメソッドも追加されていて。先日、自前でフラッシュさせようとしてたソレを、メソッド一発でできるようになるのはありがたいなと。コレも、正式公開版に反映されたら以下略。

とりあえず以下のような感じに。

_sprite_test2.rb
# スプライトの衝突判定動作テスト

require 'dxruby'

font = Font.new(12)

# 画像読み込み
base64_text = ""
DATA.each {|l| base64_text += l.chomp}
bin_data = base64_text.unpack('m')[0]
$imgs = Image.loadFromFileInMemory(bin_data).sliceTiles(8, 1)

# enemyshotimgs = [$imgs[3], $imgs[4], $imgs[5]]

# 度→ラジアン変換
def deg2rad(deg)
  return deg * Math::PI / 180.0
end

# ----------------------------------------
# プレイヤーキャラ
class Player < Sprite
  attr_accessor :bx, :by, :shottimer, :hit_timer, :flush_image

  # 初期化処理
  def initialize(x, y)
    super
    self.bx = x
    self.by = y
    self.image = $imgs[1]
    self.flush_image = $imgs[7]
    self.shottimer = 0
    self.collision = [16, 16, 4] # アタリ範囲を設定
    self.hit_timer = 0

    # self.offset_sync = true # DXRuby開発版でのみ使えるプロパティ
  end

  def update
    w, h = self.image.width, self.image.height

    # マウスカーソル座標を自機座標にする
    self.bx = Input.mousePosX
    self.by = Input.mousePosY

    xmin, ymin = 0, 0
    xmax, ymax = Window.width, Window.height
    self.bx = xmin if self.bx < xmin
    self.by = ymin if self.by < ymin
    self.bx = xmax if self.bx > xmax
    self.by = ymax if self.by > ymax

    if self.shottimer % 10 == 0
      # 自機ショットを発射
      [270, 30, 150].each do |i|
        spr = Shot.new(self.bx, self.by, 16, i + self.bx / 4)
        $shots.push(spr)
      end
    end

    self.shottimer += 1
    self.angle += 8

    if self.hit_timer > 0
      self.hit_timer -= 1
      self.hit_timer = 0 if self.hit_timer <= 0
    else
      self.hit_timer = 0
    end

    # 基準座標+オフセット値を表示座標とする。
    # DXRuby開発版なら、self.offset_sync = true で済んでしまうのだけど、
    # 開発版は Ruby 1.8 や 1.9 に対応してないので…
    self.x = self.bx - w / 2
    self.y = self.by - h / 2
  end

  # 雑魚敵と当たった時に呼ばれる処理
  def hit(o)
    self.hit_timer = 4
  end

  def draw
    super
    if self.hit_timer > 0
      Window.drawScale(self.x, self.y, self.flush_image, 3, 3)
    end
  end
end

# ----------------------------------------
# プレイヤーの弾
class Shot < Sprite
  attr_accessor :bx, :by, :dx, :dy

  def initialize(x, y, spd, angle)
    self.bx = x
    self.by = y
    self.image = $imgs[0]
    self.dx = spd * Math.cos(deg2rad(angle))
    self.dy = spd * Math.sin(deg2rad(angle))
    self.angle = angle
    self.collision = [0, 13, 31, 18]
    self.collision_enable = true
    self.collision_sync = true
  end

  def update
    w, h = self.image.width, self.image.height
    self.bx += self.dx
    self.by += self.dy

    # 画面外に出たら自分を消滅させる
    xmin = - w / 2
    ymin = - h / 2
    xmax = Window.width + w / 2
    ymax = Window.height + h / 2
    if self.x < xmin or self.x > xmax or self.y < ymin or self.y > ymax
      self.vanish
    end

    self.x = self.bx - w / 2
    self.y = self.by - h / 2
  end

  # 敵に当たった場合に呼ばれるメソッド
  def shot(d)
    self.vanish # 自分を消滅させる
  end
end

# ----------------------------------------
# 雑魚敵
class Enemy < Sprite
  attr_accessor :bx, :by, :dir, :dx, :dy
  attr_accessor :hit_timer, :spd, :org_image, :flush_image

  def initialize(spd)
    self.org_image = $imgs[2]
    self.image = self.org_image
    self.flush_image =  $imgs[3]

    # DXRuby開発版でのみ利用可能。フラッシュ画像を作れる
    # self.flush_image = self.org_image.flush(C_WHITE)

    self.spd = spd
    self.collision = [0, 0, 31, 31]
    self.init
  end

  # 発生時の初期化処理
  def init
    self.bx = rand(Window.width)
    self.by = rand(Window.height)
    self.collision_enable = false
    self.collision_sync = false
    self.hit_timer = 0
    self.alpha = 0
  end

  # 更新処理
  def update
    w, h = self.image.width, self.image.height

    if self.alpha < 255
      # 出現中
      self.collision_enable = false
      self.alpha += 5
      if self.alpha >= 255
        self.alpha = 255

        # プレイヤーを狙った速度を決める
        ply = $players[0]
        self.dir = Math.atan2(ply.by - self.by, ply.bx - self.bx)
        self.dx = spd * Math.cos(self.dir)
        self.dy = spd * Math.sin(self.dir)
      end
    else
      # 移動中

      if self.hit_timer > 0
        # 弾が当たっているなら一定時間フラッシュさせる
        self.hit_timer -= 1
        self.collision_enable = false

        # フラッシュ時間が終わったら再発生
        self.init if self.hit_timer <= 0
      else
        # 弾は当たってない
        self.hit_timer = 0
        self.collision_enable = true

        # 移動
        self.bx += self.dx
        self.by += self.dy

        # 画面外に出たら再発生
        xmin = - w / 2
        ymin = - h / 2
        xmax = Window.width + w / 2
        ymax = Window.height + h / 2
        if self.x < xmin or self.x > xmax or self.y < ymin or self.y > ymax
          self.init
        end
      end
    end

    self.image = (self.hit_timer <= 0)? self.org_image : self.flush_image

    self.x = self.bx - w / 2
    self.y = self.by - h / 2
  end

  # プレイヤーの弾と当たった時に呼ばれるメソッド
  def hit(o)
    self.hit_timer = 4
  end

  # プレイヤーと当たった時に呼ばれるメソッド
  def shot(d)
  end
end

# ----------------------------------------
# メイン

# Input.mouseEnable = false # マウスカーソル非表示

srand(0)
$players = []
$shots = []
$enemys = []
$players.push(Player.new(0, 0))
8.times {|i| $enemys.push(Enemy.new(2))}

in_pause = false

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

  update_enable = false
  if in_pause
    # ポーズ中

    # Nキー押しで1フレーム進める
    update_enable = true if Input.keyPush?(K_N)

    # Pキーを押したらポーズ解除
    in_pause = false if Input.keyPush?(K_P)
  else
    # 通常処理
    in_pause = true if Input.keyPush?(K_P) # Pキーを押したらポーズ
    update_enable = true unless in_pause
  end

  if update_enable
    # プレイヤーの弾と雑魚敵の衝突判定
    Sprite.check($shots, $enemys)

    # 雑魚敵とプレイヤーの衝突判定
    Sprite.check($enemys, $players)

    Sprite.update($players)
    Sprite.update($shots)
    Sprite.update($enemys)

    Sprite.clean($shots)
    Sprite.clean($enemys)
  end

  Sprite.draw($enemys)
  Sprite.draw($players)
  Sprite.draw($shots)

  l = $players.length + $shots.length + $enemys.length
  Window.drawFont(0, 0, "Sprs: " + ('■' * l), font)
  Window.drawFont(0, 16, "PAUSE", font) if in_pause == 0
end

__END__
iVBORw0KGgoAAAANSUhEUgAAAQAAAAAgCAMAAADKd1bWAAAAwFBMVEV/AACg
AADAAADgAAD/HBz/cHD/qKj/xMT/4OAAwAAMdwBN/03/////Ziv/hk3/p3D/
x5OROADaLAAwMDBgYGCUlJSwsLDg4ODExMT///+goKSyAKwAAAAAAAAAAAAA
AAD/////ior/KyubAAD/////u4n/mSuacwDLy8vvdi7EUgCJBgD/Kyv/biv/
dCv/uSsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAACCWjOjAAAAHHRSTlP/////////////////////////////////
//8AF7Li1wAAA0tJREFUeJzlmd2SokAMhbVquNhdB91tcKW4USj1wkLe/+02
P40ESDN2iyNbnuJiTKYczjdJaLoXuYfSdAdK005wJ6UEMinMDwLfpsJKxhY+
X2ASIJAmRsa20q4xg0CW9QwPAt8ksv4J6jLwAoAIuvbB7zZtRQC6gZkAYPcV
iRnYhBcArQXArzE6AIjPBQDZv8anGHSKr4SAM4+3AADYOQDs5gIA/V/jeL0+
gtbrGBA0BN6iBfDfD/aPG/64OQICLAL88HAL5InpKukEBoZf478C+zIECCom
8HAL5H/FyG/UhmYAAOu/558IcBdMMQOaajdmScKfXC0wTgCnc1m6bwBn+OXi
zteg87kX1Pw3BCYEQPY/UIwgAECzTnEhqKxc+dqqm6f5N/APBGgSegGA/say
Towk0AJYfkQoQhACoCgOh8MIgaqC/AiBuob8kAAVgPb7XAIeAND/T1CXgAVA
/rOsT8ADAPrfjxBA//sRAuh/rxBwFIAtga8BpIYvQ/5/gIgAGMzpagFE6IcJ
BAGA+99bAioAm3cCsPkeACqAjeZsQyWgA9iw8PbShC8Q+P8FQgKgNKdrawc/
FUCnBFj3AoAC2KNcJQAFwHlHCUABcL5XAkVRndQOgB44VQoAXC+B2D/c344W
dLjWI/+rFRHAj4auvAVgW6AD4O7HYCErQAfQVoAOoK0ACQDWAGoHYA/AWmDx
m/QHhG9KnzGJCPAdwtCjCwQAViAEQMOQLv6u2wgUAOTfGvVuAYgZoAIQM0AF
IGbAqwFEYQBuTwHwrwG4PQUqJV/X7VOgLj0A9KPeLWAXgbAGuPnPIgSgbYi4
KZSFkGLwUgkp+XMt9AgAks8QbAA0BKABomUD4P4h+DQAvkOwr/sfgx+s0HVA
Oeof8qP+Ia/6D3wMqvpqIcQEoP55MewNoBT+NYOl8K/mhf8JF0I9AmNLYUYQ
LZdhS+GSEZS6P8o39l35xv5zlsJ3vAw1CnsZKm/S/3xYfsKXIX0/QO5/tK/D
YRsiY/aC809+HX6rDRHHrrDcAhSbIQEbIs/RdFtib78p6toWn/25wFTb4q6D
kdmfC0x1MPK/tkA+1dGY2gLdU4FBYCYApjkcVQ9Gev4HgYHd1/hHacfj/wAC
/D82nEMmSwAAAABJRU5ErkJggg==

実行結果。
実行結果


オブジェクトについて、画面外に消えるたびに破棄したり、一定時間毎に生成してるあたりが、ちょっと気になる…。オブジェクトの生成と破棄って時間がかかるらしいから、最初に使う分を確保して、それを使い回したほうがいいのかなと…。

このあたり、双方向リストで管理すれば、実行するオブジェクトと、実行せずに溜めておく空きオブジェクトを、即座に登録・解除できるのだけど…。実は DXRuby の内部でソレに近いことをやってたりしないのかな、という疑問も。タスクマネージャでプロセスを見ていても、メモリ使用量が増えているようには見えないし…。

この記事へのツッコミ

Re: DXRubyのSpriteを勉強中 by takaranotou    2017/07/10 17:17
getyerdrgkhuykhim;

以上、1 日分です。

過去ログ表示

Prev - 2013/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