シューティングゲームの作り方3
1. 画像を表示する
2. 方向キーで画像を移動する
3. 自機クラスを作る
4. シーンクラスを作る
5. 弾を撃つ
6. 敵を作る
7. 当たり判定を作る
8. 敵に弾を撃たせる
9. 耐久力を持たせる
10. 敵を爆発させる
11. スコアを表示する
12. 背景を表示する
13. 表示優先順位を決める
14. 背景をスクロールさせる
15. 背景を多重スクロールさせる
16. 地面に当たり判定を付ける
17. タイトル画面を作る
18. スコアランキング画面を作る
19. ネームエントリー画面を作る
20. ランキングデータを読み書きする
8. 敵に弾を撃たせる
敵の弾用に新しいクラスを作ってもいいですが、ここではStg_bulletクラスを改造して
敵にも利用できるようにしてみましょう。
まず敵の弾を用意します。
Stg_bulletのinitializeの内容を弾の種類ごとに変数で分岐するように改造します。
def initialize(x, y, type = 0)
super()
@type = type
case @type
when 0
#自機が撃つ弾
self.bitmap = RPG::Cache.picture("bullet")
@dx = 8
@dy = 2
when 1
#敵が撃つ弾
self.bitmap = RPG::Cache.picture("e_bullet")
@dx = 8
@dy = 8
end
self.ox = self.bitmap.width / 2
self.oy = self.bitmap.height / 2
self.x = x
self.y = y
end
|
引数typeを追加しました。1が代入された時に敵の弾として処理します。
当たり判定は見た目よりやや小さくしてみます。
今はmoveで弾の動きを直接書いていますが、両方の弾に対応できるように
X軸方向、Y軸方向への移動量をそれぞれ変数で操作するようにします。
class Stg_bullet < Sprite
#--------------------------------------------------------------------------
# ● 公開インスタンス変数
#--------------------------------------------------------------------------
attr_reader :dx
attr_reader :dy
#--------------------------------------------------------------------------
# ● オブジェクト初期化
#--------------------------------------------------------------------------
def initialize(x, y, type = 0)
super()
@type = type
case @type
when 0
#自機が撃つ弾
self.bitmap = RPG::Cache.picture("bullet")
@dx = 8
@dy = 2
@spx = 12
@spy = 0
when 1
#敵が撃つ弾
self.bitmap = RPG::Cache.picture("e_bullet")
@dx = 8
@dy = 8
@spx = -6
@spy = 0
end
self.ox = self.bitmap.width / 2
self.oy = self.bitmap.height / 2
self.x = x
self.y = y
end
#--------------------------------------------------------------------------
# ● 弾の移動処理
#--------------------------------------------------------------------------
def move
self.x += @spx
self.y += @spy
if screenout?
self.dispose
return true
end
return false
end
#--------------------------------------------------------------------------
# ● 画面外判定
#--------------------------------------------------------------------------
def screenout?
return true if self.x > 640 + self.bitmap.width / 2
return true if self.x < 0 - self.bitmap.width / 2
return true if self.y > 480 + self.bitmap.height / 2
return true if self.y < 0 - self.bitmap.height / 2
return false
end
end
|
そしてStg_enemyに攻撃を判定するメソッドを作ります。
#--------------------------------------------------------------------------
# ● 攻撃判定
#--------------------------------------------------------------------------
def attack
case @type
when 1
#20フレームに1回弾を発射
if @count % 20 == 0
return 1
end
end
return 0
end
|
attackメソッドを追加しました。数字を返り値にしていますが、これは弾のtypeを指します。
ではシーンクラスに敵の弾の処理を追加します。
自機への当たり判定もついでにやってしまいましょう。
class Scene_stg
#--------------------------------------------------------------------------
# ● メイン処理
#--------------------------------------------------------------------------
def main
@shot = Array.new #自機の弾を格納
@shot_interval = 3 #弾の発射間隔(フレーム)
@shot_count = 0 #弾の発射間隔をカウントする変数
@enemy = Array.new #敵を格納
@enemyshot = Array.new #敵の弾を格納
Graphics.frame_count = 0 #フレームカウントの初期化
#自機表示
@player = Stg_player.new
#メインループ
loop do
#自機の操作
@player.move
#自機の弾を発射
player_shot()
#敵キャラの生成
make_enemy()
#弾と敵の接触処理
enemy_crash()
#敵のショット
enemy_shot()
#敵の弾と自機の接触処理
player_crash()
#各種更新
Input.update
Graphics.update
end
end
#--------------------------------------------------------------------------
# ● 自機のショット
#--------------------------------------------------------------------------
def player_shot
if Input.press?(Input::A) and @shot.size < 10
@shot_count += 1
if @shot_count == @shot_interval
@shot_count = 0
@shot.push Stg_bullet.new(@player.x + 32, @player.y)
end
end
#弾の移動&削除
@shot.reject!{|i|i.move}
end
#--------------------------------------------------------------------------
# ● 敵キャラの生成
#--------------------------------------------------------------------------
def make_enemy
if Graphics.frame_count % 100 == 0
@enemy.push Stg_enemy.new(672, rand(480),1)
end
if Graphics.frame_count % 250 == 0
@enemy.push Stg_enemy.new(672, rand(480),2)
end
if Graphics.frame_count % 550 == 0
@enemy.push Stg_enemy.new(672, rand(280)+100,3)
end
@enemy.reject!{|i|i.move}
end
#--------------------------------------------------------------------------
# ● 弾と敵の接触処理
#--------------------------------------------------------------------------
def enemy_crash
@shot.each{|i|
@enemy.each{|j|
if crash?(i, j)
i.dispose
j.dispose
@enemy.delete(j)
break
end
}
}
@shot.reject!{|i|i.disposed?}
end
#--------------------------------------------------------------------------
# ● 当たり判定
#--------------------------------------------------------------------------
def crash?(obj1, obj2)
if (obj1.x - obj2.x).abs < obj1.dx + obj2.dx and
(obj1.y - obj2.y).abs < obj1.dy + obj2.dy
return true
else
return false
end
end
#--------------------------------------------------------------------------
# ● 敵のショット
#--------------------------------------------------------------------------
def enemy_shot
@enemy.each{|i|
type = i.attack
if type > 0
@enemyshot.push Stg_bullet.new(i.x, i.y, type)
end
}
@enemyshot.reject!{|i|i.move}
end
#--------------------------------------------------------------------------
# ● 敵の弾と自機の接触処理
#--------------------------------------------------------------------------
def player_crash
@enemyshot.each{|i|
if crash?(i, @player)
i.dispose
@player.flash(Color.new(255, 255, 255, 255), 5)
end
}
@enemyshot.reject!{|i|i.disposed?}
@player.update
end
end
|
まだゲームオーバー画面等を作っていないのでとりあえず今は当たっても
フラッシュするだけにしました。
さあこの調子で自機狙い弾も作ってみましょう!
と、行きたいところですが…。ここからいきなり難しくなります。
自機を狙うということは位置関係によっては微妙な斜め方向に撃ったりしなければなりません。
しかし設定できるのは1フレームにx座標とy座標を何ドット動かすかだけです。
さあ、どうやって計算しましょうか?ここからちょっと数学の知識が必要になります。
一応解説してみますが、よく分からない人は結果だけ見て「なるもんはなる」と思ってください。
まずは敵と自機の距離と方向を調べなくてはなりません。
自機の座標を(a,b)、敵の座標を(c,d)とします。
するとX軸成分の距離は|a-c|、Y軸成分の距離は|b-d|になります。
では自機と敵の距離はいくらになるでしょうか?
ピタゴラスの定理が使えます。
「直角三角形の斜辺の2乗は他の辺の2乗の和に等しい」てなものです。
これを利用すると
距離の2乗 = (a-c)^2 + (b-d)^2
よって
距離 = √ {(a-c)^2 + (b-d)^2}
となります。RGSSでの表現に直すと
距離 = Math.sqrt((a-c) ** 2 + (b-d) ** 2)
です。
以上をまとめると図のようになります。
問題はspの方向へ進ませたいのですが、直接は指定できないので
X軸方向とY軸方向にそれぞれ進ませるドット数を計算しなければいけません。
実はspとMath.sqrt ((a-c) ** 2 + (b-d) ** 2)の関係はspxとa-c、spyとb-dの関係と同じです。
つまり
sp : Math.sqrt ((a-c) ** 2 + (b-d) ** 2) = spx : a-c
sp : Math.sqrt ((a-c) ** 2 + (b-d) ** 2) = spy : b-d
が成立するわけです。
さあ、あとは比の計算です。spxとspyは次のようになります。
spx = sp * (a-c) / Math.sqrt ((a-c) ** 2+ (b-d) ** 2)
spy = sp * (b-d) / Math.sqrt ((a-c) ** 2+ (b-d) ** 2)
あとはspに1フレーム分の移動量を代入すれば自機狙い弾の完成です。
と、思いきや…
なんだか命中精度が低いような気がしませんか?
それもそのはずでx,yは常に整数値しか取らないのです。
毎フレーム小数部分が削られるのでズレが出てしまいます。
そこで小数値として確保する座標変数@fx,@fyを別に用意しましょう。
class Stg_bullet < Sprite
#--------------------------------------------------------------------------
# ● 公開インスタンス変数
#--------------------------------------------------------------------------
attr_reader :dx #横方向の当たり判定
attr_reader :dy #縦方向の当たり判定
#--------------------------------------------------------------------------
# ● オブジェクト初期化
#--------------------------------------------------------------------------
def initialize(x, y, type = 0, target_x = 0, target_y = 0)
super()
@type = type
case @type
when 0
#自機が撃つ弾
self.bitmap = RPG::Cache.picture("bullet")
@dx = 8
@dy = 4
@spx = 12
@spy = 0
when 1
#敵が撃つ弾・左に直進する
self.bitmap = RPG::Cache.picture("e_bullet")
@dx = 8
@dy = 8
@spx = -6
@spy = 0
when 2
#敵が撃つ弾・自機に向かって直進する
self.bitmap = RPG::Cache.picture("e_bullet")
@dx = 8
@dy = 8
@spx = 6
@spy = 6
@spx = @spx*(target_x-x) / Math.sqrt((target_x-x)**2+(target_y-y)**2)
@spy = @spy*(target_y-y) / Math.sqrt((target_x-x)**2+(target_y-y)**2)
end
self.ox = self.bitmap.width / 2
self.oy = self.bitmap.height / 2
self.x = x
self.y = y
@fx = x #浮動少数x座標
@fy = y #浮動少数y座標
end
#--------------------------------------------------------------------------
# ● 弾の移動処理
#--------------------------------------------------------------------------
def move
#浮動少数座標に移動量を加算
@fx += @spx
@fy += @spy
#浮動少数座標を座標に代入(座標には整数値が入る)
self.x = @fx
self.y = @fy
#画面外に出たら消滅
if screenout?
self.dispose
return true
end
return false
end
#--------------------------------------------------------------------------
# ● 画面外判定
#--------------------------------------------------------------------------
def screenout?
return true if self.x > 640 + self.bitmap.width / 2
return true if self.x < 0 - self.bitmap.width / 2
return true if self.y > 480 + self.bitmap.height / 2
return true if self.y < 0 - self.bitmap.height / 2
return false
end
end
|
引数にtarget_x = 0, target_y = 0が追加しました。
呼び出すときに自機の座標@player.x, @player.yを代入すれば自機がターゲットになります。
当たりまくり…
それではついでにNウェイ弾と回転弾も追加してみましょう。
もう私には細かい解説はできませんので気になる方はその筋のサイトを訪ねてみてください。
def initialize(x, y, type = 0, target_x = 0, target_y = 0, rad = 0, rot = 0)
super()
@type = type
case @type
when 0
#自機が撃つ弾
self.bitmap = RPG::Cache.picture("bullet")
@dx = 8
@dy = 4
@spx = 12
@spy = 0
when 1
#敵が撃つ弾・左に直進する
self.bitmap = RPG::Cache.picture("e_bullet")
@dx = 8
@dy = 8
@spx = -6
@spy = 0
when 2
#敵が撃つ弾・自機に向かって直進する
self.bitmap = RPG::Cache.picture("e_bullet")
@dx = 8
@dy = 8
@spx = 6
@spy = 6
@spx = @spx*(target_x-x) / Math.sqrt((target_x-x)**2+(target_y-y)**2)
@spy = @spy*(target_y-y) / Math.sqrt((target_x-x)**2+(target_y-y)**2)
when 3
#敵が撃つ弾・Nウェイ弾
self.bitmap = RPG::Cache.picture("e_bullet")
@dx = 8
@dy = 8
@spx = 6
@spy = 6
@spx = @spx*(target_x-x) / Math.sqrt((target_x-x)**2+(target_y-y)**2)
@spy = @spy*(target_y-y) / Math.sqrt((target_x-x)**2+(target_y-y)**2)
@spx = @spx * Math.cos(rad) - @spy * Math.sin(rad)
@spy = @spx * Math.sin(rad) + @spy * Math.cos(rad)
when 4
#敵が撃つ弾・回転弾
self.bitmap = RPG::Cache.picture("e_bullet")
@dx = 8
@dy = 8
@spx = 6
@spy = 6
@spx = @spx * Math.cos(rot * Math::PI / 180)
@spy = @spy * Math.sin(rot * Math::PI / 180)
end
self.ox = self.bitmap.width / 2
self.oy = self.bitmap.height / 2
self.x = x
self.y = y
@fx = x
@fy = y
end
|
タイプ3はradに-1.0〜1.0の範囲で値を入れるとその角度だけ方向をずらすことができます。
次のようにすると自機を中心とした3方向を狙って撃ちます。
@enemyshot.push Stg_bullet.new(i.x, i.y, 3, @player.x, @player.y, -0.1)
@enemyshot.push Stg_bullet.new(i.x, i.y, 3, @player.x, @player.y, 0)
@enemyshot.push Stg_bullet.new(i.x, i.y, 3, @player.x, @player.y, 0.1)
|
タイプ4はrotに0〜360の値を入れて360度系で弾の移動方向を指定します。
次のようにすると敵を中心に12発の弾が円状に発射されます。
@enemyshot.push Stg_bullet.new(i.x, i.y, 4, 0,0,0,0)
@enemyshot.push Stg_bullet.new(i.x, i.y, 4, 0,0,0,30)
@enemyshot.push Stg_bullet.new(i.x, i.y, 4, 0,0,0,60)
@enemyshot.push Stg_bullet.new(i.x, i.y, 4, 0,0,0,90)
@enemyshot.push Stg_bullet.new(i.x, i.y, 4, 0,0,0,120)
@enemyshot.push Stg_bullet.new(i.x, i.y, 4, 0,0,0,150)
@enemyshot.push Stg_bullet.new(i.x, i.y, 4, 0,0,0,180)
@enemyshot.push Stg_bullet.new(i.x, i.y, 4, 0,0,0,210)
@enemyshot.push Stg_bullet.new(i.x, i.y, 4, 0,0,0,240)
@enemyshot.push Stg_bullet.new(i.x, i.y, 4, 0,0,0,270)
@enemyshot.push Stg_bullet.new(i.x, i.y, 4, 0,0,0,300)
@enemyshot.push Stg_bullet.new(i.x, i.y, 4, 0,0,0,330)
|
シーンクラスのenemy_shotメソッドに追加してみます。
#--------------------------------------------------------------------------
# ● 敵のショット
#--------------------------------------------------------------------------
def enemy_shot
@enemy.each{|i|
type = i.attack
case type
when 1
@enemyshot.push Stg_bullet.new(i.x, i.y, 1)
when 2
@enemyshot.push Stg_bullet.new(i.x, i.y, 2,@player.x, @player.y)
when 3
@enemyshot.push Stg_bullet.new(i.x, i.y, 3, @player.x, @player.y, -0.1)
@enemyshot.push Stg_bullet.new(i.x, i.y, 3, @player.x, @player.y, 0)
@enemyshot.push Stg_bullet.new(i.x, i.y, 3, @player.x, @player.y, 0.1)
when 4
for rot in 0..11
@enemyshot.push Stg_bullet.new(i.x, i.y, 4, 0,0,0,rot*30)
end
end
}
@enemyshot.reject!{|i|i.move}
end
|
せっかくですから残った2種類の敵にこの攻撃を設定してみましょう。
#--------------------------------------------------------------------------
# ● 攻撃判定
#--------------------------------------------------------------------------
def attack
case @type
when 1
return 2 if @count % 50 == 0
when 2
return 3 if @count % 50 == 0
when 3
return 4 if @count % 150 == 50
end
return 0
end
|
一応これらがシューティングでの基本形だと思います。
それぞれを組み合わせることで多彩な攻撃が可能となるでしょう。
メニューへ
9. 耐久力を持たせる
固い敵を演出するために耐久力の概念を加えます。
要するにヒットポイントを作ります。
class Stg_enemy < Sprite
#--------------------------------------------------------------------------
# ● 公開インスタンス変数
#--------------------------------------------------------------------------
attr_reader :dx #横方向の当たり判定
attr_reader :dy #縦方向の当たり判定
#--------------------------------------------------------------------------
# ● オブジェクト初期化
#--------------------------------------------------------------------------
def initialize(x, y, type)
super()
@type = type
self.bitmap = RPG::Cache.picture("enemy"+"#{@type}")
self.ox = self.bitmap.width / 2
self.oy = self.bitmap.height / 2
self.x = x
self.y = y
case @type
when 1
@spx = -2
@spy = 0
@life = 5
when 2
@spx = -4
@spy = 0
@life = 7
when 3
@spx = -3
@spy = 1
@life = 9
end
@dx = 32
@dy = 32
@count = 0 #生存時間
end
#--------------------------------------------------------------------------
# ● 敵キャラの移動処理
#--------------------------------------------------------------------------
def move
#このupdateはフラッシュ処理に必要
self.update
move_select()
self.x += @spx
self.y += @spy
#画面外に出たら消滅
if screenout?
self.dispose
return true
end
return false
end
#--------------------------------------------------------------------------
# ● 敵キャラの種類別移動
#--------------------------------------------------------------------------
def move_select
@count += 1
case @type
when 2
#途中で引き返す
@spx = 4 if @count == 100
when 3
#ジグザグ移動
@spy *= -1 if @count % 100 == 0
end
end
#--------------------------------------------------------------------------
# ● 画面外判定
#--------------------------------------------------------------------------
def screenout?
return true if self.x > 640 + self.bitmap.width / 2
return true if self.x < 0 - self.bitmap.width / 2
return true if self.y > 480 + self.bitmap.height / 2
return true if self.y < 0 - self.bitmap.height / 2
return false
end
#--------------------------------------------------------------------------
# ● 攻撃判定
#--------------------------------------------------------------------------
def attack
case @type
when 1
return 2 if @count % 50 == 0
when 2
return 3 if @count % 50 == 0
when 3
return 4 if @count % 150 == 50
end
return 0
end
#--------------------------------------------------------------------------
# ● 敵キャラのダメージ処理
#--------------------------------------------------------------------------
def damage
@life -= 1
#5フレームの間白くフラッシュ
self.flash(Color.new(255, 255, 255, 255), 5)
if @life == 0
return true
end
return false
end
end
|
ダメージメソッドを追加しました。
これに合わせてシーンクラスの当たり判定部分を書き換えましょう。
#--------------------------------------------------------------------------
# ● 弾と敵の接触処理
#--------------------------------------------------------------------------
def enemy_crash
@shot.each{|i|
@enemy.each{|j|
if crash?(i, j)
i.dispose
if j.damage
j.dispose
@enemy.delete(j)
end
break
end
}
}
@shot.reject!{|i|i.disposed?}
end
|
もうこのくらいは問題ないでしょう。
自機の耐久も同じように作れますね。
class Stg_player < Sprite
#--------------------------------------------------------------------------
# ● 公開インスタンス変数
#--------------------------------------------------------------------------
attr_reader :dx #横方向の当たり判定
attr_reader :dy #縦方向の当たり判定
attr_reader :life #ライフ
#--------------------------------------------------------------------------
# ● オブジェクト初期化
#--------------------------------------------------------------------------
def initialize
super()
self.bitmap = RPG::Cache.picture("fighter")
#原点を画像の中心に設定
self.ox = self.bitmap.width / 2
self.oy = self.bitmap.height / 2
self.x = 32
self.y = 240
@dx = 16
@dy = 4
@life = 5
@muteki = 0 #無敵時間を計る
end
#--------------------------------------------------------------------------
# ● 自機の移動処理
#--------------------------------------------------------------------------
def move
move_x = 0 #横方向の移動量
move_y = 0 #縦方向の移動量
#入力されたキーによって画像の移動量を決める
move_y = 8 if Input.press?(Input::DOWN)
move_x = -8 if Input.press?(Input::LEFT)
move_x = 8 if Input.press?(Input::RIGHT)
move_y = -8 if Input.press?(Input::UP)
#斜め入力のときは速度を落とす
if move_x * move_y != 0
move_x *= 0.7
move_y *= 0.7
end
#移動量を座標に加算する
self.x += move_x
self.y += move_y
#画面外に出たら座標を画面内に強制する
self.x = 620 if self.x > 620
self.x = 20 if self.x < 20
self.y = 470 if self.y > 470
self.y = 10 if self.y < 10
#無敵時間があればカウントを減らす
if @muteki > 0
@muteki -= 1
self.opacity = 255 if @muteki == 0
end
end
#--------------------------------------------------------------------------
# ● 自機のダメージ処理
#--------------------------------------------------------------------------
def damage
#無敵状態でなければライフを減らす
if @muteki == 0
@life -= 1
#無敵時間50フレーム
@muteki = 50
#自機を半透明にする
self.opacity = 127
if @life < 0
@life = 0
return true
end
end
return false
end
end
|
ついでに無敵時間を追加しました。
ダメージを受けると50フレームの間ダメージを受けません。
opacityは不透明度で、無敵の間は半透明になります。
シーンクラスでもここを呼ぶようにしましょう。
#--------------------------------------------------------------------------
# ● 敵の弾と自機の接触処理
#--------------------------------------------------------------------------
def player_crash
@enemyshot.each{|i|
if crash?(i, @player)
@player.damage
i.dispose
end
}
@enemyshot.reject!{|i|i.disposed?}
@player.update
end
|
ライフ5としていますが、現段階ではまだ死亡処理は作らないでおきます。
メニューへ
10. 敵を爆発させる
敵がただ消えるのも味気ないので爆発させてみましょう。
爆発させるといっても、敵がいた場所にアニメーションを表示させるだけです。
というわけで爆発画像を用意しました。
burst1.png / burst2.png / burst3.png / burst4.png / burst5.png / burst6.png
では爆発クラスを作ってみましょう。
class Stg_burst < Sprite
#--------------------------------------------------------------------------
# ● オブジェクト初期化
#--------------------------------------------------------------------------
def initialize(x,y)
super()
self.bitmap = RPG::Cache.picture("burst6")
self.ox = self.bitmap.width / 2
self.oy = self.bitmap.height / 2
self.x = x
self.y = y
@time = 0
#爆発の効果音
Audio.se_play("Audio/SE/death",90)
end
#--------------------------------------------------------------------------
# ● アニメーション進行
#--------------------------------------------------------------------------
def move
#2フレームに1回実行
if Graphics.frame_count % 2 == 0
@time += 1
if @time == 7
self.dispose
return true
end
self.bitmap = RPG::Cache.picture("burst"+"#{@time}")
end
return false
end
end
|
def moveでは2フレームに1回のペースで画像を変えています。
ここでは効果音を出すようにしました。
音楽や効果音関係はAudioモジュールを参照してください。
"ヘルプ→RGSSリファレンス→ゲームライブラリ→RGSS 組み込みモジュール→Audio"
ちなみに"death"は
これです。
XPのRTPにはどうも気軽に使える効果音がないので自作しました。
効果音の作成には
KanaWaveやSWaveがお勧めです。
結構楽しいですよ。
それではくどいようですがシーンクラスに処理を追加します。
ループの前に@burst = Array.newをやってから、当たり判定の部分を書き換えます。
#--------------------------------------------------------------------------
# ● 弾と敵の接触処理
#--------------------------------------------------------------------------
def enemy_crash
@shot.each{|i|
@enemy.each{|j|
if crash?(i, j)
i.dispose
if j.damage
@burst.push Stg_burst.new(j.x, j.y)
j.dispose
@enemy.delete(j)
end
break
end
}
}
@shot.reject!{|i|i.disposed?}
@burst.reject!{|i|i.move}
end
|
おしまい♪
爆発クラスは汎用アニメーションクラスとして使ってもいいですね。
メニューへ
11. スコアを表示する
シューティングのお約束、スコア表示です。
ここまでくればもう立派なゲームですね。
まずはStg_enemyクラスにスコア用変数を作って、外部から読めるようにします。
個々の敵キャラの設定は@lifeと同じように@score = 100などと代入すればOKです。
シーンクラスに表示部分も作りましょう。
class Scene_stg
#--------------------------------------------------------------------------
# ● メイン処理
#--------------------------------------------------------------------------
def main
@shot = Array.new #自機の弾を格納
@shot_interval = 3 #弾の発射間隔(フレーム)
@shot_count = 0 #弾の発射間隔をカウントする変数
@enemy = Array.new #敵を格納
@enemyshot = Array.new #敵の弾を格納
@burst = Array.new #爆発アニメーションを格納
Graphics.frame_count = 0 #フレームカウントの初期化
#スコアの表示
@score = 0
@prescore = 0
@scoreview = Sprite.new
@scoreview.bitmap = Bitmap.new(96,32)
@scoreview.bitmap.draw_text(0, 0, 96, 32, "0" ,2)
#自機表示
@player = Stg_player.new
#メインループ
loop do
#自機の操作
@player.move
#自機の弾を発射
player_shot()
#敵キャラの生成
make_enemy()
#弾と敵の接触処理
enemy_crash()
#敵のショット
enemy_shot()
#敵の弾と自機の接触処理
player_crash()
#スコア書き換え
change_score()
#各種更新
Input.update
Graphics.update
end
end
#--------------------------------------------------------------------------
# ● 自機のショット
#--------------------------------------------------------------------------
def player_shot
if Input.press?(Input::A) and @shot.size < 10
@shot_count += 1
if @shot_count == @shot_interval
@shot_count = 0
@shot.push Stg_bullet.new(@player.x + 32, @player.y)
end
end
#弾の移動&削除
@shot.reject!{|i|i.move}
end
#--------------------------------------------------------------------------
# ● 敵キャラの生成
#--------------------------------------------------------------------------
def make_enemy
if Graphics.frame_count % 100 == 0
@enemy.push Stg_enemy.new(672, rand(480),1)
end
if Graphics.frame_count % 250 == 0
@enemy.push Stg_enemy.new(672, rand(480),2)
end
if Graphics.frame_count % 550 == 0
@enemy.push Stg_enemy.new(672, rand(280)+100,3)
end
@enemy.reject!{|i|i.move}
end
#--------------------------------------------------------------------------
# ● 弾と敵の接触処理
#--------------------------------------------------------------------------
def enemy_crash
@shot.each{|i|
@enemy.each{|j|
if crash?(i, j)
i.dispose
if j.damage
@score += j.score
@burst.push Stg_burst.new(j.x, j.y)
j.dispose
@enemy.delete(j)
end
break
end
}
}
@shot.reject!{|i|i.disposed?}
@burst.reject!{|i|i.move}
end
#--------------------------------------------------------------------------
# ● 当たり判定
#--------------------------------------------------------------------------
def crash?(obj1, obj2)
if (obj1.x - obj2.x).abs < obj1.dx + obj2.dx and
(obj1.y - obj2.y).abs < obj1.dy + obj2.dy
return true
else
return false
end
end
#--------------------------------------------------------------------------
# ● 敵のショット
#--------------------------------------------------------------------------
def enemy_shot
@enemy.each{|i|
type = i.attack
case type
when 1
@enemyshot.push Stg_bullet.new(i.x, i.y, 1)
when 2
@enemyshot.push Stg_bullet.new(i.x, i.y, 2,@player.x, @player.y)
when 3
@enemyshot.push Stg_bullet.new(i.x, i.y, 3, @player.x, @player.y, -0.1)
@enemyshot.push Stg_bullet.new(i.x, i.y, 3, @player.x, @player.y, 0)
@enemyshot.push Stg_bullet.new(i.x, i.y, 3, @player.x, @player.y, 0.1)
when 4
for rot in 0..11
@enemyshot.push Stg_bullet.new(i.x, i.y, 4, 0,0,0,rot*30)
end
end
}
@enemyshot.reject!{|i|i.move}
end
#--------------------------------------------------------------------------
# ● 敵の弾と自機の接触処理
#--------------------------------------------------------------------------
def player_crash
@enemyshot.each{|i|
if crash?(i, @player)
@player.damage
i.dispose
end
}
@enemyshot.reject!{|i|i.disposed?}
@player.update
end
#--------------------------------------------------------------------------
# ● スコア書き換え
#--------------------------------------------------------------------------
def change_score
if @score != @prescore
@scoreview.bitmap.clear
@scoreview.bitmap.draw_text(0, 0, 96, 32, "#{@score}" ,2)
@prescore = @score
end
end
end
|
scoreとprescoreの2つの変数を使って値が変わったときだけ
文字を書き換えるようにしています。
こうしておかないと毎フレーム文字画像を埋め込む処理がされて重くなってしまうからです。
メニューへ
次のページへ
前のページへ
一覧へ戻る
トップページへ戻る