2014/01/23(木) [n年前の日記]
#1 [dxruby] 砲台はなんとかなってきた
とりあえず、砲台を動かすために書いた処理を、別ファイルにまとめたり。
_gamemath.rb
単体でも動くけど、他のファイルから呼んで使うこともできるはず。Public Domain ってことで。
ruby gamemath.rb 0 とか ruby gamemath.rb 3 とか打てば、下のような画面が出るので、マウスカーソルを動かして動作確認できるかと。0〜3 まで指定可能。
以下のページが参考になりました。ありがたや。
_[ Azure Contrail ] アズールコントレイル - Tips
_Flashゲーム講座&ASサンプル集【狙撃の計算方法について】
ちなみに。DXRuby開発版なら、外積とか内積とかそのへん求めるメソッドもあるのですが…。開発版は、常時最新版しか公開されてなくて、入手にちょっと難があるので、そのあたり、DXRuby 1.4.1 に取り込まれることを期待。
_gamemath.rb
# ゲーム関係の計算用メソッド # # ruby gamemath.rb 1 といった指定で、テスト種類を変えられる require 'dxruby' # 計算用メソッド class GameMath class << self # # 度からラジアンへの変換 # # @param [float] deg 角度。単位は度 # @retrun [float] 角度。単位はラジアン # def deg2rad(deg) return (deg * Math::PI / 180.0) end # # ラジアンから度への変換 # # @param [float] rad 角度。単位はラジアン # @retrun [float] 角度。単位は度 # def rad2deg(rad) return (rad * 180.0 / Math::PI) end # # 基準座標から目標座標への角度を返す # # @param [int] tgt_x 目標座標x # @param [int] tgt_y 目標座標y # @param [int] base_x 基準座標x # @param [int] base_y 基準座標y # @return [float] 目標座標までの角度。単位はラジアン # def get_dir_rad(tgt_x, tgt_y, base_x, base_y) tx = tgt_x - base_x ty = tgt_y - base_y return Math.atan2(ty, tx) end # # 角度をn方向に制限して返す # # @param [float] ang 元の角度 # @param [float] rest 何方向にするか。デフォルトは16 # @return [float] rest方向に制限した角度 # def get_dir_rest(ang, rest=16) d = 360.0 / rest dir = ((ang + (d / 2)) % 360.0).div(d) return dir * d end # # 角度と速度(距離)からx,y方向の移動量を求める(度を与える版) # # @param [float] ang 角度。単位は度 # @param [float] spd 速度(距離) # @param [float] bx 原点となる座標x。省略時は0 # @param [float] by 原点となる座標y。省略時は0 # @return [Array] 移動量x,y # def get_vec(ang, spd, bx=0, by=0) return GameMath.get_vec_rad(GameMath.deg2rad(ang), spd, bx, by) end # # 角度と速度(距離)からx,y方向の移動量を求める(ラジアンを与える版) # # @param [float] ang 角度。単位はラジアン # @param [float] spd 速度(距離) # @param [float] bx 原点となる座標x。省略時は0 # @param [float] by 原点となる座標y。省略時は0 # @return [Array] 移動量x,y # def get_vec_rad(rad, spd, bx=0, by=0) x = spd * Math.cos(rad) + bx y = spd * Math.sin(rad) + by return x, y end # # 外積を返す # # @param [int] tgt_x 目標ベクトルx # @param [int] tgt_y 目標ベクトルy # @param [int] base_x 基準ベクトルx # @param [int] base_y 基準ベクトルy # @return [float] 外積。 # 外積 > 0 なら、目標は右にあり、 # 外積 < 0 なら、目標は左にある # 外積 = 0 なら、同じ方向 # def get_cross_product(tgt_x, tgt_y, base_x, base_y) return base_x * tgt_y - base_y * tgt_x end # # 徐々に目標方向を向く角度を求める # # @param [float] ang 現在の角度。単位は度 # @param [float] ang_spd 角度変化量。単位は度 # @param [float] tgt_x 目標座標x # @param [float] tgt_y 目標座標y # @param [float] base_x 基準座標x # @param [float] base_y 基準座標y # @return [float] 新しい角度。単位は度 # def get_chase_angle(ang, ang_spd, tgt_x, tgt_y, base_x, base_y) ang %= 360 tgt_ang = GameMath.rad2deg(GameMath.get_dir_rad(tgt_x, tgt_y, base_x, base_y)) tgt_ang %= 360 d = tgt_ang - ang d += 360 if d < -180 d -= 360 if d > 180 if d > ang_spd ang += ang_spd elsif d < -ang_spd ang -= ang_spd else ang = tgt_ang end return ang end # # 指定範囲に収まった角度を返す # # @param [float] now_ang 現在の角度。単位は度 # @param [float] base_ang 基準角度。単位は度 # @param [float] min_d 角度範囲の最小値算出用。基準角度に加算する値 # @param [float] max_d 角度範囲の最大値算出用。基準角度に加算する値 # @return [float] 範囲に収まった角度。単位は度 # def get_dir_range(now_ang, base_ang, min_d, max_d) base_ang %= 360 now_ang %= 360 # 現在角度が、基準角度の正反対の角度を超える角度だったら補正する if now_ang > base_ang + 180 now_ang -= 360 elsif now_ang <= base_ang - 180 now_ang += 360 end min_ang = base_ang + min_d max_ang = base_ang + max_d now_ang = min_ang if now_ang < min_ang now_ang = max_ang if now_ang > max_ang return now_ang end # # 2点の座標間の距離が指定距離より小さいかを返す # # @param [int] tx 目標座標x # @param [int] ty 目標座標y # @param [int] bx 基準座標x # @param [int] by 基準座標y # @param [int] dist 判定距離 # @return {Boolean] trueなら、判定距離内に入ってる。falseなら、判定距離外 # def check_dist(tx, ty, bx, by, dist) w = tx - bx h = ty - by return (w * w + h * h <= dist * dist)? true : false end end end if $0 == __FILE__ # 動作確認 # 動作種類を変える if ARGV.length >= 1 test_kind = ARGV[0].to_i else test_kind = 0 end font = Font.new(16) fh = 20 bx = 320 by = 240 ang = 0 base_ang = 0 Window.loop do break if Input.keyPush?(K_ESCAPE) px, py = Input.mousePosX, Input.mousePosY case test_kind when 0 # 方向(角度)に制限を加えるテスト # n方向に制限する n = 12 rad = GameMath.get_dir_rad(px, py, bx, by) # 本来の角度 new_deg = GameMath.get_dir_rest(GameMath.rad2deg(rad), n) # 制限した角度 # 描画 d = 360 / n s = d / 2 n.times do |i| nx, ny = GameMath.get_vec(s + d * i, 80, bx, by) Window.drawLine(bx, by, nx, ny, C_RED) end nx, ny = GameMath.get_vec(GameMath.rad2deg(rad), 200, bx, by) Window.drawLine(bx, by, nx, ny, C_CYAN) rad = GameMath.deg2rad(new_deg) nx, ny = GameMath.get_vec(GameMath.rad2deg(rad), 150, bx, by) Window.drawLine(bx, by, nx, ny, C_YELLOW) Window.drawFont(0, 0, "#{n}方向にする制限を加える例", font) when 1 # 外積を使って目標を追尾するように角度を変えるテスト # プルプル震えてしまう ang_spd = 3 # 目標ベクトル tx = px - bx ty = py - by # 基準ベクトル vx = Math.cos(GameMath.deg2rad(ang)) vy = Math.sin(GameMath.deg2rad(ang)) # 外積を求める cp = GameMath.get_cross_product(tx, ty, vx, vy) # 外積の符号を見て、目標が右にあるか左にあるか判別 ang += ang_spd if cp > 0 ang -= ang_spd if cp < 0 ang %= 360 # 描画 nx, ny = GameMath.get_vec(ang, 200, bx, by) Window.drawLine(bx, by, nx, ny, C_CYAN) Window.drawFont(0, 0, "外積の符号で追尾する例", font) when 2 # 目標への角度を求めて、追尾するように角度を変えるテスト ang = GameMath.get_chase_angle(ang, 2, px, py, bx, by) # 描画 nx, ny = GameMath.get_vec(ang, 200, bx, by) Window.drawLine(bx, by, nx, ny, C_CYAN) Window.drawFont(0, 0, "角度を見て追尾する例", font) when 3 # 角度に制限を加える例 base_ang += Input.x * 2 # 基準角度を左右キーで変更 rad = GameMath.get_dir_rad(px, py, bx, by) # 本来の角度 ang = GameMath.rad2deg(rad) # 基準角度からプラスマイナス aw の範囲に角度を制限する aw = 90 ang = GameMath.get_dir_range(ang, base_ang, -aw, aw) # 描画 nx, ny = GameMath.get_vec(base_ang, 250, bx, by) Window.drawLine(bx, by, nx, ny, C_RED) nx, ny = GameMath.get_vec(base_ang - aw, 150, bx, by) Window.drawLine(bx, by, nx, ny, C_YELLOW) nx, ny = GameMath.get_vec(base_ang + aw, 150, bx, by) Window.drawLine(bx, by, nx, ny, C_YELLOW) nx, ny = GameMath.get_vec(ang, 200, bx, by) Window.drawLine(bx, by, nx, ny, C_CYAN) Window.drawFont(0, 0, "角度に制限を加える例 : 左右キーで角度変更", font) end end end各スクリプトに直接書いてしまってもいいぐらいのソレばかりですが…。
単体でも動くけど、他のファイルから呼んで使うこともできるはず。Public Domain ってことで。
ruby gamemath.rb 0 とか ruby gamemath.rb 3 とか打てば、下のような画面が出るので、マウスカーソルを動かして動作確認できるかと。0〜3 まで指定可能。
以下のページが参考になりました。ありがたや。
_[ Azure Contrail ] アズールコントレイル - Tips
_Flashゲーム講座&ASサンプル集【狙撃の計算方法について】
ちなみに。DXRuby開発版なら、外積とか内積とかそのへん求めるメソッドもあるのですが…。開発版は、常時最新版しか公開されてなくて、入手にちょっと難があるので、そのあたり、DXRuby 1.4.1 に取り込まれることを期待。
◎ DXRubyの便利なところ。 :
ここ数日、DXRubyを触っていて「便利だなー」と思った点が。メインループが数行で済む点は、ありがたい。おかげで、各ファイルの動作テストが書きやすいなと。
フツー、スクロールゲーム用のアレコレを書く場合は…。
DXRubyなら、敵のプログラムが書いてあるスクリプトファイルに、ちょこっと動作確認用のメインループを書いちゃえば、そのスクリプトだけで動きの確認がそこそこできるわけで。
特に Ruby の場合、以下のような書き方が可能で。
ということで、Ruby + DXRuby は、プロトタイプ作成に便利だなと再認識。
フツー、スクロールゲーム用のアレコレを書く場合は…。
- ゲーム本体のプログラムを動かして、
- 目的の場所までスクロールさせて、
- そこで出てくる敵その他の動きを確認して、
- 敵のプログラムを調整して、
DXRubyなら、敵のプログラムが書いてあるスクリプトファイルに、ちょこっと動作確認用のメインループを書いちゃえば、そのスクリプトだけで動きの確認がそこそこできるわけで。
特に Ruby の場合、以下のような書き方が可能で。
if $0 == __FILE__ # このスクリプトを単体で動かした時だけ、この部分を通る endまあ、Python なども、同じことができますけど…。何にせよ、この部分に動作確認用のソレを書いとけば、そのスクリプトファイルをそっくりそのまま、本体プログラム側で使うこともできてしまう。
ということで、Ruby + DXRuby は、プロトタイプ作成に便利だなと再認識。
[ ツッコむ ]
以上、1 日分です。