シューティングゲームの作り方1

1. 画像を表示する
2. 方向キーで画像を移動する
3. 自機クラスを作る
4. シーンクラスを作る

5. 弾を撃つ
6. 敵を作る
7. 当たり判定を作る

8. 敵に弾を撃たせる
9. 耐久力を持たせる
10. 敵を爆発させる
11. スコアを表示する

12. 背景を表示する
13. 表示優先順位を決める
14. 背景をスクロールさせる
15. 背景を多重スクロールさせる
16. 地面に当たり判定を付ける

17. タイトル画面を作る
18. スコアランキング画面を作る
19. ネームエントリー画面を作る
20. ランキングデータを読み書きする

1. 画像を表示する

何はともあれ画面上に画像を表示しなければ始まりません。
画像の表示の仕方は"ヘルプ→スクリプト入門→基礎編→画像の表示"を参照しますと
次のように書かれています。
devil = Sprite.new
devil.bitmap = Bitmap.new("Graphics/Battlers/075-Devil01")

loop do
  Graphics.update
end

RTPの075-Devil01を呼んでから画面の書き換えを永遠に繰り返すという命令ですね。
画像の表示はこれが基本ではありますが、RGSSにはキャッシュというものが用意されています。
詳しくは"ヘルプ→RGSSリファレンス→ゲームライブラリ→RGSS 組み込みモジュール→RPG::Cache"
を見てみましょう。
Bitmap.newではファイルを直接読みに行くのに対して
RPG::Cacheは一度メモリに取り込んで、次回はそこから読み込むということのようですね。
つまり同じ画像なら2回目以降の表示が高速になります。

キャッシュを使う場合は上の命令文は次のように変わります。
devil = Sprite.new
devil.bitmap = RPG::Cache.battler("075-Devil01", 0) 

loop do
  Graphics.update
end

("075-Devil01", 0) の, 0の数値を変えると画像の色相を変えることができます。
指定したフォルダによっては, hueの部分が無いものもありますのでヘルプでよく確認しましょう。
命令文も簡単になりますから画像の表示は以後キャッシュで統一することにします。

それでは自分で用意した画像を画面に表示してみましょう。
とりあえず適当に絵を描いて、っと。

fighter.png

適当過ぎ!!
あ、いいのいいの。
画像なんて後からいくらでも差し替えられますから。

で、この画像のファイル名は"fighter.png"です。
Pictureフォルダにでも入れときますか。
では画面に表示してみましょう。
#自機表示
player = Sprite.new
player.bitmap = RPG::Cache.picture("fighter") 

#メインループ
loop do
  #各種更新
  Graphics.update
end

キャッシュの指定フォルダ名が違うのと色相指定がないことに注意しましょう。
ここまでは簡単ですね。



メニューへ


2. 方向キーで画像を移動する

画像を表示したら次はその画像を動かしたくなってきます。
考え方としてはまずキーが押されたかどうかを調べて、押されたキーの種類によって
画像の座標を動かせばいいんじゃないかということになります。

では押されたキーを調べるにはどうしたらいいでしょうか。
いきなり答えを言いますと、Inputモジュールがあります。
詳しくは"ヘルプ→RGSSリファレンス→ゲームライブラリ→RGSS 組み込みモジュール→Input"
これを使うにはInput.updateがまず必要みたいですね。loopの中に入れときましょう。
loop do
  #各種更新
  Input.update 
  Graphics.update
end

それから方向キーを受け取りたいわけですが、press?かな、trigger?かな?
シューティングで方向キーを連打する人は珍しいですよね。
押しっぱなしに対応するならInput.press?(num) を使います。
方向キーには定数としてDOWN LEFT RIGHT UPが用意されています。
#自機表示
player = Sprite.new
player.bitmap = RPG::Cache.picture("fighter") 

#メインループ
loop do
  #入力されたキーによって画像の移動量を決める
  player.y += 1 if Input.press?(Input::DOWN)
  player.x -= 1 if Input.press?(Input::LEFT)
  player.x += 1 if Input.press?(Input::RIGHT)
  player.y -= 1 if Input.press?(Input::UP)

  #各種更新
  Input.update
  Graphics.update
end

player.y += 1が何だか分からない人はいますか?
これは画像のY座標に1を加算するという命令です。
つまり1フレームごとに下へ1ドット移動します。

フレーム??という人もいるかも知れませんね。
200Xからのユーザーはここで考え方の転換が必要になるかも知れません。
200Xにあった「ピクチャー操作」は移動後の座標と移動にかける秒数を指定しますが、
XPではフレームごとの移動という考え方をします。

フレームとはアニメでいうコマのことです。
40fps(frame par second:秒間当たりのフレーム数)と言えば1秒間に40回画面が書き換えられます。
player.y += 1なら1回画面が切り替えられるたびに下へ1ドット移動、つまり1秒間に40ドット
下へ移動することになります。
それを制御しているのがGraphics.updateで、まあ単純にここのloop do〜endが1秒間に
40回転していると思って差し支えありません。
1ドットでは遅い感じがしますから+= 1の数字をいろいろ変えて動作を確認してみましょう。
ちなみに世の中の主流は60fps(1秒間に60回の画面書き換え)らしいですので、
最初から60fpsで設計してみましょうか。
一番最初の行に次の1文を入れてみましょう。
#フレームレート変更(1秒間に60回のペースで画面を書き換えます)
Graphics.frame_rate = 60

これで60fpsに設定されました。
テストプレー中にF2キーを押すとタイトルバーに現在のfpsが表示されます。
どうしても60fps出なくなったら値を下げるのも手です。
おっと、滑らかモードもONにしておいてくださいね。

さてさて、画像は動かせましたが、遊んでいるうちに画像が画面外の彼方に行って
見失ってしまった人はいませんか?
とりあえず画面外に出ないようにしてみましょう。

XPの標準画面は横が640ピクセル、縦が480ピクセルです。
なのでその範囲を超えた場合は強制的に座標を代入して画面内に留めましょう。
#フレームレート変更(1秒間に60回のペースで画面を書き換えます)
Graphics.frame_rate = 60
#自機表示
player = Sprite.new
player.bitmap = RPG::Cache.picture("fighter") 

#メインループ
loop do
  #入力されたキーによって画像の移動量を決める
  player.y += 8 if Input.press?(Input::DOWN) 
  player.x -= 8 if Input.press?(Input::LEFT) 
  player.x += 8 if Input.press?(Input::RIGHT) 
  player.y -= 8 if Input.press?(Input::UP) 

  #画面外に出たら座標を画面内に強制する
  player.x = 640 if player.x > 640
  player.x = 0 if player.x < 0
  player.y = 480 if player.y > 480
  player.y = 0 if player.y < 0

  #各種更新
  Input.update 
  Graphics.update
end

え?画像を下にやると画面内に留まらない?
そうですね。今は画像の原点座標が左上に設定されています。
これを画像の中心に持ってきます。
原点位置を設定するプロパティはox,oyです。
中心座標は縦横のサイズを取得して半分にすれば出てきます。
それを追加するとこんな感じです。
#フレームレート変更(1秒間に60回のペースで画面を書き換えます)
Graphics.frame_rate = 60
#自機表示
player = Sprite.new
player.bitmap = RPG::Cache.picture("fighter") 
#原点を画像の中心に設定
player.ox = player.bitmap.width / 2
player.oy = player.bitmap.height / 2

#メインループ
loop do
  #入力されたキーによって画像の移動量を決める
  player.y += 8 if Input.press?(Input::DOWN) 
  player.x -= 8 if Input.press?(Input::LEFT) 
  player.x += 8 if Input.press?(Input::RIGHT) 
  player.y -= 8 if Input.press?(Input::UP) 

  #画面外に出たら座標を画面内に強制する
  player.x = 640 if player.x > 640
  player.x = 0 if player.x < 0
  player.y = 480 if player.y > 480
  player.y = 0 if player.y < 0

  #各種更新
  Input.update 
  Graphics.update
end

えー、この辺何やってるか分からないという人は
ヘルプでSpriteやBitmapを調べてみてくださいね。

ところで、斜め方向にキーを入れると自機が速くなったような気がしませんが?
気のせいではなくて、X軸・Y軸に同じ量だけ進むため実際1.4倍くらい速くなっています。
そこで斜め方向の速度制限をしてみましょう。
とはいえ厳密に計算してもあまり意味はないので0.7を掛ける程度でいいでしょう。
#フレームレート変更(1秒間に60回のペースで画面を書き換えます)
Graphics.frame_rate = 60
#自機表示
player = Sprite.new
player.bitmap = RPG::Cache.picture("fighter") 
#原点を画像の中心に設定
player.ox = player.bitmap.width / 2
player.oy = player.bitmap.height / 2

#メインループ
loop do
  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

  #移動量を座標に加算する
  player.x += move_x
  player.y += move_y

  #画面外に出たら座標を画面内に強制する
  player.x = 640 if player.x > 640
  player.x = 0 if player.x < 0
  player.y = 480 if player.y > 480
  player.y = 0 if player.y < 0

  #各種更新
  Input.update 
  Graphics.update
end

これまでx,yに直接加算していましたが、今回はいったん変数move_x,move_yに代入して
加工してから使うようにしました。
move_x * move_y != 0とやっているように、
斜め方向に移動するということはmove_x,move_y両方が0でないということです。

これで移動処理はバッチリですね。



メニューへ


3. 自機クラスを作る

これまでスクリプトを実行される順にべた書きしてきましたが、
RGSSは仮にもオブジェクト指向言語らしいのでクラスでも作ってみましょう。
え、クラスを作ったら何か良いことがあるのかですって?

そんなこと、ど素人の私に聞かないでください。

…と言いたいところですが、まずはスクリプト全体が見やすくなります。
これから敵を作ったり弾を作ったりしていくわけですが、順番にずら〜っと書いていくと
後である部分を修正したくなったときに探すのが大変になります。
自機に関する動作はここ、敵に関する動作はここ、と分けておけば修正が楽です。
他にも敵や弾みたいに同じものをたくさん表示する必要があるときなんかに
クラスなら使い回しができて楽になります。
(これ覚えたらもう200Xで定番のピクチャーを駆使して画面を構成するのが嫌になります)

さて、クラスの作り方は"ヘルプ→スクリプト入門→基礎編→クラス定義"にあります。
class Person
end

これが基本ですが、このままでは画像を動かす時に使ったx,yというようなプロパティは
使えません。こういうプロパティもクラスの中で定義するものです。
では画像のx,yはどこで定義されているかというとSpriteクラスです。
なのでSpriteクラスの機能を引き継いで自機のクラスを作ると便利そうですね。
そんな場合はこうします。
class Stg_player < Sprite
end

これでStg_playerクラスがSpriteクラスのサブクラスとして定義されました。
つまりSpriteの機能を自由に使うことができます。
では自機に関する動作をこの中に移動させましょう。
class Stg_player < Sprite
  #--------------------------------------------------------------------------
  # ● オブジェクト初期化
  #--------------------------------------------------------------------------
  def initialize
    super()
    #自機表示
    self.bitmap = RPG::Cache.picture("fighter") 
    #原点を画像の中心に設定
    self.ox = self.bitmap.width / 2
    self.oy = self.bitmap.height / 2
  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 = 640 if self.x > 640
    self.x = 0 if self.x < 0
    self.y = 480 if self.y > 480
    self.y = 0 if self.y < 0
  end
end

#フレームレート変更(1秒間に60回のペースで画面を書き換えます)
Graphics.frame_rate = 60

#自機表示
player = Stg_player.new

#メインループ
loop do
  #自機の操作
  player.move

  #各種更新
  Input.update 
  Graphics.update
end

いきなり変わりすぎですか?
まずdef initializeとdef moveが入りました。
def initializeはStg_player.newみたいにクラスを作成したとき自動的に実行されるメソッドです。
すぐ下にsuper()とありますね。
これは親クラス(ここではSpriteクラス)の同名メソッド(initialize)を受け継ぐ命令です。
super()の()の中にはViewportが指定できるわけですが、今は気にしなくてもいいです。
あとは表示する画像と座標の中心点を指定しています。

ん、self?
これまでplayer.bitmapと書いてたものがself.bitmapに変わっています。
selfとは自分自身、つまり「このクラス」を意味します。
これまではplayerという変数を使って外から操作していたわけですが、
今度は内側から「自分自身をこう動かす」という風に命令をしています。

次にdef move。ここには自機の動きを書いています。
initializeは固有の名前ですが、moveは好きな名前に変えて構いません。

以上をまとめるとplayer = Stg_player.newで自機を表示して、
ループ中のplayer.moveでキー入力に合わて動かすスクリプトができました。



メニューへ


4. シーンクラスを作る

シーンクラスとは何か。
んー、ゲームでは普通タイトル画面とかメインゲーム画面とかゲームオーバー画面
なんかがありますので、それらを1つ1つの画面に分けて考えようというものらしいです。
オブジェクト指向らしくないという批判もあるようですが、私には細かいことは
判断できませんので、たぶん製作が楽になるんじゃないかということで作ってみます。

現在トップレベルに直接記述されてるスクリプトはこれだけあります。
#フレームレート変更(1秒間に60回のペースで画面を書き換えます)
Graphics.frame_rate = 60
#自機表示
player = Stg_player.new

#メインループ
loop do
  #自機の操作
  player.move

  #各種更新
  Input.update 
  Graphics.update
end

この部分をまとめてシーンクラスのmainメソッドに入れます。
class Scene_stg
  #--------------------------------------------------------------------------
  # ● メイン処理
  #--------------------------------------------------------------------------
  def main
    #フレームレート変更(1秒間に60回のペースで画面を書き換えます)
    Graphics.frame_rate = 60
    #自機表示
    player = Stg_player.new

    #メインループ
    loop do
      #自機の操作
      player.move

      #各種更新
      Input.update 
      Graphics.update
    end
  end
end

そしてScene_stgクラスを実行するスクリプトを書きます。
scene = Scene_stg.new
scene.main

この2行はかならずスクリプトの一番最後に書いてください。

何故?

試しに一番上に書いて実行してみましょう。
"uninitialized constant Scene_stg"と表示されましたね。
newは定義されたクラスからオブジェクトを生成する命令です。
クラスを導入したことで命令の流れが飛び飛びになったように見えますが、
スクリプトの基本は上から順番に読まれていきます。
クラス定義の部分は画面上に現れないからといって素通りしたわけではありません。
つまりnewの前にクラス定義が完了していなければならないわけです。

さて、現時点でStg_playerクラス、Scene_stgクラス、実行部、の3つの部分ができました。
そろそろこの3つを別のセクションに分割しましょう。
stgt2.PNG

長々と書きましたが、以上までがシューティングとはあんまり関係ない基本事項でした。(ぇ
これからシューティングっぽいことをやっていきます。



メニューへ

次のページへ
一覧へ戻る
トップページへ戻る



更新日時:2006/12/18 18:00
管理人:すっぴぃ ◆ADGYPSYxII