※上記の広告は60日以上更新のないWIKIに表示されています。更新することで広告が下部へ移動します。

入門編・10
シューティングゲームもどきを作る

敵キャラを表示する1・敵キャラクラスの設計

今回から、敵キャラを作成していきます。が、この部分はかなり管理人のオリジナルな色が強く、
この方法が効率的であるかかなり疑問があります。
詳しい方、よろしければご教授下さいませ……!

敵キャラに求められるものと、配列の使い方

自機と敵の一番の違いは(特にプログラム上の違いは)、敵キャラは複数存在する、ということです。
プログラムをやったことのある方なら聞いたことがあると思いますが、敵キャラを作る際には
敵キャラの『配列』を作ることが好ましいのです。
配列というのは、(1,1,2,4)のように、 要素(ここでは数字)を、いくつか並べたもの
例えば、(1,1,2,4)は、要素数4の配列であると言い、3番目の要素は2である、というような使い方をします。
そして、配列の何が便利かというと、配列にすると、 通し番号を使って繰り返しで効率的に処理できる という一点が最も重要です。
例えば、敵キャラを5体出す場合、配列を使わないと、処理内容としては
(敵キャラAの処理)
(敵キャラBの処理)
(敵キャラCの処理)
(敵キャラDの処理)
(敵キャラEの処理)
と、敵キャラの数だけ繰り返して記述する必要があります。これでは、記述が面倒だし、敵キャラ数が変わった場合への対応も明らかに難しくなります。
では、配列を使うとどうなるでしょうか。敵キャラが5体なら、次のような配列を作ります。
(敵キャラ[0],敵キャラ[1],敵キャラ[2],敵キャラ[3],敵キャラ[4],)
[]の中の数字は通し番号。要素には、それぞれ敵キャラクラスのインスタンスが入ります。
そして、処理内容はこうです。
nを0から4(敵の数-1)まで変化させて、以下の処理を行う。
 敵キャラ[n]の処理
ここまでを繰り返す
日本語で書いていますが、こんな感じです。まず、n=0、すなわち敵キャラ[0]の処理が行われ、『ここまで繰り返す』まで行ったら次はn=1となり、敵キャラ[1]の処理。と、敵の数だけ繰り返し、結果、先ほどの単に敵の数だけ処理を書いた場合と同じになります。
冗長な繰り返しが無くなり、さらに敵の数をいくつに変えても応用可能です。
敵キャラの数が100体とかになった場合、どちらが有利かは明らかでしょう。
というわけで、今回、敵キャラクラスの他に、敵キャラの配列を管理する、敵全体クラスを作成します。このクラスには、繰り返し用に、敵が全部で何人いるかといった情報も持たせるようにします。
では、クラスを設計しましょう。

敵キャラクラスを作る

自機と基本的に同じなので、ここでは簡単にいきましょう。
#==============================================================================
# ■ Game_Ememy
#------------------------------------------------------------------------------
#  シューティングゲームの敵キャラを扱うクラスです。
#==============================================================================

class Game_Enemy
 #--------------------------------------------------------------------------
 # ● 公開インスタンス変数
 #--------------------------------------------------------------------------
 attr_accessor   :x                        # 左上 X 座標
 attr_accessor   :y                        # 左上 Y 座標
 attr_accessor   :alive                    # 生きているか否か

 #--------------------------------------------------------------------------
 # ● 定数
 #--------------------------------------------------------------------------
 WND_X = 544 #ウィンドウのサイズ
 WND_Y = 416
 SIZE_X = 32 #敵キャラのサイズ
 SIZE_Y = 32
 #--------------------------------------------------------------------------
 # ● オブジェクト初期化
 #--------------------------------------------------------------------------
 def initialize(x,y)
   @x=x
   @y=y
   @alive = true
 end
 #--------------------------------------------------------------------------
 # ● 更新
 #--------------------------------------------------------------------------
 def update
 end
end
まあ、こんなところでしょうか。インスタンス変数には、自機と同様x座標、y座標のほか、生きているか否かを表す@aliveを用意しました。ここに入る値はtrue(正しい)かfalse(間違っている)で、生きている時trueになります。
これにより、例えばfalseの時は表示しない、あたり判定が無いという使い方をします。
また、敵の位置は敵ごとに自由に決めたいので、initializeの際に座標を渡し、それによって位置が決まるようにしました。
updateは後で必要になりそうだから作っただけで、今はとりあえず何もしません。

敵キャラのスプライトのクラスを作る

これも、自機の時と同様です。
#==============================================================================
# ■ Sprite_Enemy
#------------------------------------------------------------------------------
#  敵キャラ表示用のスプライトです。Game_Enemy クラスのインスタンスを
# 監視し、スプライトの状態を自動的に変化させます。
#==============================================================================

class Sprite_Enemy < Sprite_Base
 #--------------------------------------------------------------------------
 # ● 公開インスタンス変数
 #--------------------------------------------------------------------------
 attr_accessor :enemy
 #--------------------------------------------------------------------------
 # ● オブジェクト初期化
 #     viewport  : ビューポート
 #     z         : スプライト・ビューポートのz座標
 #     myMachine : キャラクター (Game_Enemy)
 #--------------------------------------------------------------------------
 def initialize(viewport,z,enemy)
   super(viewport)
   @enemy = enemy
   self.bitmap = Cache.character("Enemy")
   self.z=z
   self.viewport.z=z
   update
 end
 #--------------------------------------------------------------------------
 # ● 解放
 #--------------------------------------------------------------------------
 def dispose
   super
 end
 #--------------------------------------------------------------------------
 # ● フレーム更新
 #--------------------------------------------------------------------------
  def update
     self.x = @enemy.x
     self.y = @enemy.y
 end
end
というか、MyMachineがEnemyになっただけです。あ、あとグラフィックがenemyに。よかったら下の画像を使って下さい。名前をEnemyとしてインポートして下さい。
ここでは、簡略のため大きさを32×32と定数で決めてしまっているので、オリジナルのものを使う場合もこのサイズでお願いします。
大きさの違う色んな敵を出す場合は、敵キャラの大きさを変数にしたり、画像のファイル名をnewの際に渡したりする必要があるでしょう。あるいは、敵の種類ごとに種類idを振って、それで管理するとか。
使い方も、基本的には同じです。ただ、敵キャラごとにビューポートを作成するのは面倒なので、敵キャラのビューポートは全敵キャラで共通のものを使います。
敵キャラのz座標も、敵キャラごとに変えずに同じ値にします。この場合、後に作成したオブジェクトほど上に表示されます。

敵全体クラスを作る

さて、ここが新しい部分です。敵全体クラスを作りましょう。
必要最低限のものは、敵キャラの配列と、敵キャラの数、すなわち配列の要素数です。
今回は、便利かと思って生きている敵の数も一応インスタンス変数にしました。
クラス名は、Game_Enemiesとしました。
#==============================================================================
# ■ Game_Ememies
#------------------------------------------------------------------------------
#  シューティングゲームの敵キャラ全体を扱うクラスです。
#==============================================================================

class Game_Enemies
 #--------------------------------------------------------------------------
 # ● 公開インスタンス変数
 #--------------------------------------------------------------------------
 attr_accessor   :enemy_num                 # 敵の総数
 attr_accessor   :alive_num                 # 存在している(alive)敵の数
 attr_accessor   :enemies                   # Game_Enemyの配列
 attr_accessor   ;s_enemies                 # Sprite_Enemyの配列
 #--------------------------------------------------------------------------
 # ● オブジェクト初期化
 #--------------------------------------------------------------------------
 def initialize(enemy_num) # 敵キャラの総数をenemy_numとして渡す
   @enemy_num = enemy_num
   @alive_num = enemy_num
   @enemies = Array.new(enemy_num)
   @s_enemies = Array.new(enemy_num)
   viewport = Viewport.new(0,0,544,416)
   viewport.z = 200
   for i in 0..enemy_num-1 do
     @enemies[i] = Game_Enemy.new(200+rand(312),rand(384))
     @s_enemies[i] = Sprite_Enemy.new(viewport,200,@enemies[i])
   end
 end
 #--------------------------------------------------------------------------
 # ● 更新
 #--------------------------------------------------------------------------
 def update
   for i in 0..enemy_num-1 do
     @enemies[i].update
     @s_enemies[i].update
   end
 end
end
さて、微妙に複雑ですが一つずつ見ていきましょう。
まずはinitialize。
newの際は、数字を渡し、これを敵の総数とします。本当は、変な値が入っていた時にチェックできたりするといいのですが、
今回は面倒なのでそれはしていません。余力があれば挑戦しましょう。
そして、最初敵は全員生きているものとして、enemy_numとalive_numに渡した数、つまり敵の数を入れます。
   @enemies = Array.new(enemy_num)
   @s_enemies = Array.new(enemy_num)
問題が、これです。このArrayというのが、配列という意味です。Array.newによって、配列を作り出しています。()の中に渡すのは、想像できると思いますが、敵キャラの数。敵キャラの配列の要素数ですね。これによって、enemy_numの要素を持つ配列が作られます。@s_enemiesも同様で、こちらは敵キャラのスプライトを配列にしています。
なお、この段階では要素は何も入っておらず、これが敵キャラの配列であることすら未確定です。
続いて、ビューポートを作成し、このz座標を200としています。この値は敵キャラ全体で共通になります。
   for i in 0..enemy_num-1 do
     @enemies[i] = Game_Enemy.new(200+rand(312),rand(384))
     @s_enemies[i] = Sprite_Enemy.new(viewport,200,@enemies[i])
   end
続くこれが、配列のさっき言った『効率のいい繰り返し』の記述です。
先ほど、配列にはまだ何も入っていないと書きましたが、ここで配列に中身を入れています。すなわち、newによって作成した、Game_EnemyクラスのインスタンスとSprite_Enemyクラスのインスタンス。
iが、0からenemy_num-1まで変化して、この2行を実行します。
"enemy_num-1"になるのは、配列の添え字が0から始まるため、最後の要素の添え字が全体の要素数の数字より1小さくなることによります。
1行目で敵キャラを、2行目で敵キャラのスプライトを、それぞれ割り当てている(自機のcreate_myMachineの場合と似ている)わけですが、微妙に見慣れないものがあります。それは、rand(312)など、rand(数字)という命令。
これは、乱数です。実行のたびに、0以上数字未満(rand(100)なら0から99)の値を返します。
Game_Enemyのinitializeでは、敵の位置を渡すことにしていましたね。
つまり、ここでは敵の位置を乱数を使って決め、配置しているのです(x座標では、定数に乱数を足し、ある程度右の位置に行くようにしています)。
繰り返しの度にrandの値は変わるので、敵にはそれぞれ別のx座標、y座標が割り当てられることになります。
 def update
   for i in 0..enemy_num-1 do
     @enemies[i].update
     @s_enemies[i].update
   end
 end
updateの中でも繰り返しが使われています。(誤りがあったため、4行目を追記しました。)
敵全体クラスのインスタンスのupdateを行うことで、そこに所属する敵全てがupdateされます。
シーン中の記述は、敵全体のupdateのみを行えばいいことになります。

では、次回この敵キャラのクラスをシーンに組み込み、敵キャラを表示してみましょう。



  • すごくわかりやすいです!!
    わかりやすい講座を探していたのでありがたいです。
    続き楽しみにしています! -- (名無しさん) 2009-04-11 17:41:26
  • コメントありがとうございます。
    こんなに早く反応があるとは……。
    ヘルプの解説を含め、シーンの構造などを説明しているものが
    ほとんどなかったのが不満で、作ってみました。
    本当に実はあんまり分かっていないので、至らないところも
    多いかと思いますが、よろしくお願いします。 -- (管理人) 2009-04-11 18:58:22
  • すいません、『自機を表示する』の記述に誤りがありました。
    updateの中身を書かないと、自機の位置が正しく反映されません。
    記事を修正しておきます。 -- (管理人) 2009-04-14 21:59:20
  • ツクール古参ですがRGSSに四苦八苦でした(^^;)
    が、シーンの基本構造を解説した記事にガチで救われました
    応援してるんで、がんばってください!!続き楽しみです。

    -- (うきぶくろ) 2009-04-27 12:17:15
  • コメントありがとうございます。
    一番書きたかったそのあたりは書いたもののなんだか忙しい+
    自分でもRGSSの解読に手間取っているためになかなか更新できていません。
    どの程度まで書けるか分かりませんがよろしくお願いします。 -- (管理人) 2009-04-28 08:40:35
  • 敵キャラを作成する・2の最後の方に、
    updateの際に敵キャラを扱うスプライトの更新を行うのを忘れていたのを修正しました。
    イタリック体で修正が入っている部分です。 -- (管理人) 2009-05-02 22:10:23
  • UFOが見にくかったので画像に縁取りをつけました。 -- (管理人) 2009-05-04 08:56:56
  • ホントにスゴイと思います。尊敬します。
    大変だと思いますが少しずつでいいんで頑張ってください。 -- (ユウ) 2009-05-12 00:07:40
  • コメントありがとうございます。
    恐らくこれを見て作る側はすぐなんでたいそうもどかしいと思いますが……。
    ところで、今のところはちゃんとこの講座の通りで動くようになっているのでしょうか?
    一応チェックはしてますが、多少ポカはすると思ってたので指摘が無いのがむしろ意外です。 -- (管理人) 2009-05-13 21:11:52
  • 大丈夫です。ちゃんと動いています。
    頑張ってください。続きが楽しみです。 -- (ユウ) 2009-05-18 01:06:59
  • 1段目と言われている部分は『RGSS2』
    2段目と言われている部分は『プリセットのスクリプト』
    と言われると、より解り易い表現なのでは? -- (通行人) 2009-11-05 12:48:40
  • @enemies[i] = Game_Enemy.new(200 + rand(312), rand(384))
    ↑argumentErrorが発生しました。wrong number of arguments(2 for 0)
    どうすればいいでしょうか、頼みます。
    -- (help me!!) 2010-01-05 00:22:50
  • マウス入力はできますぜ。
    Win32とかなんやらをつかって。
    キーボードもできるみたいですが見つかりません。
    探してます。 -- (シャミラ) 2010-03-24 05:28:28
  • 勉強させてもらってます、とても参考になります。
    入門編・10 にあるGame_Ememiesのクラスの
    attr_accessor ;s_enemies ですが、;を:に変えないとエラーを吐きますので
    コピペでやった人は修正した方がよさそうです
    -- (邯鄲) 2010-09-04 17:27:18
  • @enemies[i] = Game_Enemy.new(200 + rand(312), rand(384))
    ↑argumentErrorが発生しました。wrong number of arguments(2 for 0)

    Game_Enemyクラスって最初からありますよね。
    それが原因では? -- (ぽん) 2010-10-03 23:11:42
  • 製作初心者なのでとても助かります!! -- (ありがとうございます!) 2011-02-05 12:44:03
  • セーブ/ロードまわりはMarshalを使ってスクリプトで記述されていますし、
    ファイルの読み書きもRubyと同様にできますから、
    セーブやロードをいじるのはスクリプト操作でできることでは? -- (ななし) 2011-05-20 13:04:19
  • ダンジョンなら3D化できると、下記サイトに書いてありました。 -- (だれかさん) 2011-08-18 07:26:19
  • アルバイトはじめましたd(´∀`*)グッ♪ http://64n.co/ -- (私だ) 2012-01-09 04:49:41
  • 風俗店を探よりココ!!男性は報酬あり!d(´∀`*)→ http://www.44m4.net/ -- (江梨子) 2012-08-20 17:11:02
名前:
コメント:

すべてのコメントを見る
添付ファイル