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

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

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

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

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

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

5. 弾を撃つ

弾を撃つとはどういうことでしょうか。
まず何かのキーが押されたことを調べます。
そして自機の前辺りの座標に弾の画像を表示します。
弾の画像は自動的に毎フレーム座標を右へ移動していきます。
画面外に出たら画像は消えます。
こう考えていくと自機を作成した時と同じような感覚で作れそうです。
でも大切なことは弾は1個だけでなく、画面内にたくさん存在できるということです。
それをどう管理していくか…

ひとまず弾の画像を用意します。

bullet
bullet.png

それから新しいセクションに弾クラスを作ります。
class Stg_bullet < Sprite
  #--------------------------------------------------------------------------
  # ● オブジェクト初期化
  #--------------------------------------------------------------------------
  def initialize(x,y)
    super()
    self.bitmap = RPG::Cache.picture("bullet") 
    self.ox = self.bitmap.width / 2
    self.oy = self.bitmap.height / 2
    #座標に引数の値を代入
    self.x = x
    self.y = y
  end
  
  #--------------------------------------------------------------------------
  # ● 弾の移動処理
  #--------------------------------------------------------------------------
  def move
    #座標を右へ移動
    self.x += 16
    #画面外に出たら消滅
    if screenout?
      self.dispose
    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
end

initializeメソッドに引数(x,y)が付いています。
このx,yはself.x,self.yに代入されているので、クラスを生成する時に
Stg_bullet.new(player.x, player.y)とかやれば自機と同じ座標に表示することができます。
moveメソッドでは画像が画面外に飛び出したら画像を開放(dispose)しています。
これをやらないとゲーム中に弾の画像がどんどん蓄積されて処理が重くなってしまいます。
screenout?は画像が画面外出たらtrueを返すメソッドです。

次に弾を発生させる命令が必要ですが、これをどこに持ってくるかが結構問題です。
自機の動作と考えるなら自機クラスのmoveメソッドに追加すればいいのですが、
自機クラスの中で弾クラスを管理するのは後のことを考えるとなんだか具合が悪いです。
敵も同じ弾クラスを使って弾を撃ったり、当たり判定を付けたりとすると
いろんな場所から弾クラスにアクセスすることになるので、分かりやすさを優先して
できれば近くに固めておきたいところです。新たに弾管理クラスを作ってもいいですが、
まあ今回は直接シーンクラスに書いてみましょう。
class Scene_stg
  #--------------------------------------------------------------------------
  # ● メイン処理
  #--------------------------------------------------------------------------
  def main
    #自機表示
    @player = Stg_player.new
    
    #メインループ
    loop do
      #自機の操作
      @player.move
      
      #自機の弾を発射
      player_shot()
      
      #各種更新
      Input.update 
      Graphics.update
    end
  end
  
  #--------------------------------------------------------------------------
  # ● 自機のショット
  #--------------------------------------------------------------------------
  def player_shot
    if Input.press?(Input::A)
      @shot = Stg_bullet.new(@player.x + 32, @player.y)
    end
  end
end

これで実行するとボタンA(Aキーではない)を押している間、自機の前辺りに弾の画像が
連続で表示されます。

ここで変数playerの名前の頭に"@"が付きました。これはインスタンス変数です。
mainメソッドで使われた普通の変数(ローカル変数)はmainメソッド外に持っていくことができません。
しかし新しく作ったplayer_shotメソッド内でも使いたいので、
インスタンス変数にして適用範囲をクラス内に広げました。
@shotも今後当たり判定の処理なんかに使うことを考えてインスタンス変数にしておきます。

さて、次はmoveメソッドを呼び出して弾を移動させたいのですが、
困ったことに変数@shotには新しく呼び出した弾1個の情報しか入っていません。
すべての弾に対してmoveを呼び出さなくてはいけないので、弾の個数だけ記憶領域が
必要です。そこでshotを配列変数にします。
class Scene_stg
  #--------------------------------------------------------------------------
  # ● メイン処理
  #--------------------------------------------------------------------------
  def main
    @shot = Array.new          #自機の弾を格納
    
    #自機表示
    @player = Stg_player.new
    
    #メインループ
    loop do
      #自機の操作
      @player.move
      
      #自機の弾を発射
      player_shot()
      
      #各種更新
      Input.update 
      Graphics.update
    end
  end
  
  #--------------------------------------------------------------------------
  # ● 自機のショット
  #--------------------------------------------------------------------------
  def player_shot
    if Input.press?(Input::A)
      @shot.push Stg_bullet.new(@player.x + 32, @player.y)
    end
  end
end

配列についてはスクリプト入門とArrayクラス
"ヘルプ→RGSSリファレンス→標準ライブラリ→組み込みクラス→Object→Array"
を一読しましょう。
pushメソッドは配列の末尾に新しい要素を自動追加するもので、新しい弾クラスが
作られるたびに配列に追加されていきます。

では配列を順番に参照しながらmoveを呼び出してみましょう。例えばこう。
      unless @shot.empty?
        for i in 0...@shot.size
          @shot[i].move
        end
      end

shot.empty?で配列が空でないことを確認してから、for i in 0...shot.lengthで
変数iに配列の0から要素数-1番目までを辿らせてmoveを呼び出しています。

この命令は1行で済ませることもできます。
@shot.each{|i|i.move}

each {|item| .... }は||の中の変数に配列の要素を順番に渡していくイテレータというものらしいです。
ま、要するに最初に示した方法と同じような動きをするものと思って構いません。

さて、moveの評価は終わりましたが、これだけだと弾が画面外に消えた時にエラーが出ます。
すでに開放された画像にアクセスしようとしたためです。
それを防ぐためにdisposed?で分岐してもいいのですが、時間とともに無駄が多くなってしまいます。
なので開放された画像を小まめに配列から取り除くようにしましょう。
またfor文で巡りながらdisposed?で調べて消去していってもいいのですが、
この辺も簡潔にまとめてみます。

まず準備として弾クラスのmoveメソッドに返り値を持たせます。
  #--------------------------------------------------------------------------
  # ● 弾の移動処理
  #--------------------------------------------------------------------------
  def move
    #座標を右へ移動
    self.x += 16
    #画面外に出たら消滅
    if screenout?
      self.dispose
      return true
    end
    return false
  end

これで画像が開放されたらtrue、それ以外はfalseが返ります。

次にシーンクラスのplayer_shotメソッドに1行追加します。
  #--------------------------------------------------------------------------
  # ● 自機のショット
  #--------------------------------------------------------------------------
  def player_shot
    if Input.press?(Input::A)
      shot.push Stg_bullet.new(player.x + 32, player.y)
    end
    #弾の移動&削除
    @shot.reject!{|i|i.move}
  end

eachがreject!に変わりました。
実はヘルプにも載っていない命令です。こんな情報をどこから仕入れてきたのかというと
RGSSの元となったRubyの公式サイトからです。
リファレンスマニュアル→組み込みクラス→Arrayに載っています。
とりあえずリファレンスマニュアルのArrayだけでもさっと読んでおくことをお勧めします。
eachと違うのは各要素を順次評価して、結果がtrueになった要素を削除することです。

これでようやく自機の弾が完成しました。

ところでちょっと弾が出過ぎているような気もしますね。
発射間隔を少し空けてみましょう。
class Scene_stg
  #--------------------------------------------------------------------------
  # ● メイン処理
  #--------------------------------------------------------------------------
  def main
    @shot = Array.new          #自機の弾を格納
    @shot_interval = 3         #弾の発射間隔(フレーム)
    @shot_count = 0            #弾の発射間隔をカウントする変数
    
    #自機表示
    @player = Stg_player.new

    #メインループ
    loop do
      #自機の操作
      @player.move
      
      #自機の弾を発射
      player_shot()
      
      #各種更新
      Input.update 
      Graphics.update
    end
  end
  
  #--------------------------------------------------------------------------
  # ● 自機のショット
  #--------------------------------------------------------------------------
  def player_shot
    if Input.press?(Input::A)
      @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
end

これで3フレームに1回の発射となりました。

また、画面内に存在できる弾の数を制限するという手法もあります。
弾の数は変数shotの要素数を見れば分かります。
  #--------------------------------------------------------------------------
  # ● 自機のショット
  #--------------------------------------------------------------------------
  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

これで一度に撃てる弾の数が最大10発までに制限されました。



メニューへ


6. 敵を作る

敵と言ってもいきなり複雑な動きをしたり自機に向かってガンガン
撃ってくるようなのは作りません。
まずは画面の右端から現れて、左端まで直進して消えるというものを作ります。
これだけならもう復習レベルですね。

それでは敵の画像を用意。

enemy1
ememy1.png

うはっ、我ながらすごい芸術的センス!

次に敵クラスの作成。
class Stg_enemy < Sprite
  #--------------------------------------------------------------------------
  # ● オブジェクト初期化
  #--------------------------------------------------------------------------
  def initialize(x,y)
    super()
    self.bitmap = RPG::Cache.picture("enemy1") 
    self.ox = self.bitmap.width / 2
    self.oy = self.bitmap.height / 2
    self.x = x
    self.y = y
  end
  
  #--------------------------------------------------------------------------
  # ● 敵キャラの移動処理
  #--------------------------------------------------------------------------
  def move
    #座標を左へ移動
    self.x -= 2
    #画面外に出たら消滅
    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

弾クラスをコピーしてちょちょいと手直ししただけです。
では発生の条件はどうしましょうか。
一番簡単なやり方として定期的にランダムな位置に発生させるとしましょう。
時間を測るのにはGraphics.frame_countが使えます。
これはフレームが1つ進行するたびに値が自動で+1されていきます。

ではシーンクラスに追加します。
class Scene_stg
  #--------------------------------------------------------------------------
  # ● メイン処理
  #--------------------------------------------------------------------------
  def main
    @shot = Array.new          #自機の弾を格納
    @shot_interval = 3         #弾の発射間隔(フレーム)
    @shot_count = 0            #弾の発射間隔をカウントする変数
    @enemy = Array.new         #敵を格納
    Graphics.frame_count = 0   #フレームカウントの初期化
    
    #自機表示
    @player = Stg_player.new

    #メインループ
    loop do
      #自機の操作
      @player.move
      
      #自機の弾を発射
      player_shot()
      
      #敵キャラの生成
      make_enemy()
      
      #各種更新
      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))
    end
    @enemy.reject!{|i|i.move}
  end
end

ほとんど弾の作り方をまねただけでできてしまいました。
if Graphics.frame_count % 100 == 0はフレームカウントの値が100で割り切れるとき、
つまり100フレームに1回敵クラスを生成させるということです。
rand(480)で毎回Y座標の位置を変化させています。
後はもう問題ないですね。

ところで1種類の敵ならこれでいいとして、他の種類の敵も登場させようと思ったらどうしましょう?
このまま敵の数だけクラスをコピーして作ってもいいですが、
できれば同じクラス内で済ましてしまいたいものです。

まずは敵の画像を用意してっと。
enemy2
ememy2.png
enemy3
ememy3.png

……まぁ、これはいいとして、敵クラスを改造してみます。
class Stg_enemy < Sprite
  #--------------------------------------------------------------------------
  # ● オブジェクト初期化
  #--------------------------------------------------------------------------
  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
    when 2
      @spx = -4
      @spy = 0
    when 3
      @spx = -3
      @spy = 1
    end
  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

まずは引数にtypeが加わりました。これで敵の種類を管理します。
例えば赤いのを1、黄色いのを2、青いのを3……と自分の中で決めておいて、
typeの値が2なら黄色い敵の行動を作る、というふうにします。

次に注目は"enemy"+"#{@type}"の表記です。これは変数@typeの値を文字列として展開して
別の文字列と合体させています。
仮に@typeに1が代入されていたら"enemy1"が呼ばれるという寸法です。
うまく使えばソースコードをグンと短くできます。
あとはcase文で分岐して1フレーム間に移動するx座標とy座標の両を@spx@spyで設定しています。

さっそくシーンクラスにて呼出し命令を書いてみましょう。
  #--------------------------------------------------------------------------
  # ● 敵キャラの生成
  #--------------------------------------------------------------------------
  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

直進だけではつまらないので、さらに動きを付けてみましょうか。
そんな場合も@typeが役に立ちます。
class Stg_enemy < Sprite
  #--------------------------------------------------------------------------
  # ● オブジェクト初期化
  #--------------------------------------------------------------------------
  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
    when 2
      @spx = -4
      @spy = 0
    when 3
      @spx = -3
      @spy = 1
    end
    @count = 0 #生存時間
  end
  
  #--------------------------------------------------------------------------
  # ● 敵キャラの移動処理
  #--------------------------------------------------------------------------
  def move
    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
end

もうこれはいちいち解説するよりもいろいろといじって遊んでみてください。
工夫次第でいろんな動きが作れます。



メニューへ


7. 当たり判定を作る

さあ、いよいよ来ました。シューティングの肝です!
これさえ分かってしまえば後のことは全部オマケみたいなものです。

でもその前に、当たり判定とは何でしょうか。
簡単に言えば画像と画像の接触判定です。
では画像のドット1個1個を調べて当たっているかどうかを判定するのでしょうか。
まずそんな大変なことはしませんし、処理落ちの原因になるでしょう。
一般には画像の座標と何らかの数値を使って計算してそれらしく見える状態を
当たったと判断します。
なので当たり判定の仕方自体がある種そのゲームのルールになります。
それだけ当たり判定にもいろいろなやり方がありますが、ここでは広く使えそうなものを
1つだけ紹介します。

まずは当たり判定の大きさを表す変数を用意しましょう。
ここでは画像の中心から横方向への大きさと縦方向への大きさを持った当たり判定を使います。
一般に矩形と呼ばれるものです。
Stg_player、Stg_bullet、Stg_enemyの各クラスのinitializeメソッドでインスタンス変数
@dx、@dyというのを作って適当な値を代入してみましょう。
もちろんdx,dyの名前は自分で好きに決めて構いません。
自機はやや小さめに@dx = 20,@dy = 4くらい。弾と敵は画像の大きさと合わせて(8,2),(32,32)
としときますか。

次に当たり判定の処理はどこに作ればいいかというと、やはりシーンクラスが良いと思います。
と、言うことは@dx,@dyをクラス外から読まなくてはなりません。
shot.dxと書いてもエラーが出てしまいますね。

そこでアクセサの出番です。スクリプト入門のクラス定義のところで触れられていますが
これを読んだだけではなんの役に立つのかピンときませんよね。
今みたいにクラスの中でしか通用しない変数をクラスの外から参照したい時に使うんです。
player.xとか何の気なしに使っていましたが、これもアクセサです。
変数をメソッドと同じように扱う方法ですね。
詳しい定義は
"ヘルプ→RGSSリファレンス→標準ライブラリ→組み込みクラス→Module"にあります。

さて、当たり判定の大きさは一度決めたら多分書き換えることはないと思うので、
読み取りのみのattr_readerを使いましょうか。書く位置はクラスの中で、メソッドの外です。
class Stg_bullet < Sprite
  #--------------------------------------------------------------------------
  # ● 公開インスタンス変数
  #--------------------------------------------------------------------------
  attr_reader :dx  #横方向の当たり判定
  attr_reader :dy  #縦方向の当たり判定
 
  #--------------------------------------------------------------------------
  # ● オブジェクト初期化
  #-------------------------------------------------------------------------- 
  def initialize(x,y)
    super()
    self.bitmap = RPG::Cache.picture("bullet") 
    self.ox = self.bitmap.width / 2
    self.oy = self.bitmap.height / 2
    self.x = x
    self.y = y
    @dx = 16
    @dy = 8
  end
  
  #--------------------------------------------------------------------------
  # ● 弾の移動処理
  #--------------------------------------------------------------------------
  def move
    #座標を右へ移動
    self.x += 16
    #画面外に出たら消滅
    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_playerとStg_enemyにも作りましょう。

次に当たり判定メソッドをScene_stgの中に定義します。
  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

absは絶対値にする命令です。簡単に言えば答えがマイナスだったら
マイナス記号をはずすということです。
このやたら長い条件は
「2つの座標の距離が双方の当たり判定値の合計より小さかったら当たったと判断する」
ということを言っています。

crash

こんな場合でも当たりと判断するわけですが、まあゲームに支障が無ければ良しとします。

それでは仕上げとして配列shotと配列enemyの要素を全部の組み合わせでcrash?を通す
処理を追加します。
class Scene_stg
  #--------------------------------------------------------------------------
  # ● メイン処理
  #--------------------------------------------------------------------------
  def main
    @shot = Array.new          #自機の弾を格納
    @shot_interval = 3         #弾の発射間隔(フレーム)
    @shot_count = 0            #弾の発射間隔をカウントする変数
    @enemy = Array.new         #敵を格納
    Graphics.frame_count = 0   #フレームカウントの初期化
    
    #自機表示
    @player = Stg_player.new

    #メインループ
    loop do
      #自機の操作
      @player.move
      
      #自機の弾を発射
      player_shot()
      
      #敵キャラの生成
      make_enemy()
      
      #弾と敵の接触処理
      enemy_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
end

ふぅ、やっとシューティングらしくなってきましたね。



メニューへ

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



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