ポケモンバトル4[2.0ver]

未分類

こんにちはkazutoです。今回は、ポケモンバトルシリーズの続きになります。

実装内容の確認

まず初めに、実装のアウトラインを把握しましょう。仕様書は下記です。

上記のチャート図が今回、実装していくアウトラインになります。もし実装中で

  • コードの意味がわからない
  • 実装の全体像が掴めない
  • 行き詰まった場合

などの問題を抱えてしまった場合は、再度チャート図を見て、実装のアウトラインを把握しましょう。全体像が掴めないまま実装してしまうと後から大変になります。

今回は、「ポケモンバトル」で一番重要なバトルシステムのプログラムを組んでいきます。なおAttckクラスに処理を実装していきます。

Attackクラス

Attackクラスで実装していく手順は、

  • インスタンスを生成、initializeメソッドを用いてインスタンス変数を初期化
  • バトルシステムの実装(pokemon_attack,lifeメソッド)

インスタンスを生成、initializeメソッドを用いてインスタンス変数を初期化

class Attack
 def initialize(pokemon,pokemon_lists)
    @legendary_pokemon=pokemon
    @pokemon_lists=pokemon_on_hand
  end
end
# 伝説のポケモン定義
legendary_pokemon=Legendary_pokemon.new
pokemon=legendary_pokemon.select_pokemon
# 手持ちのポケモン定義
puts"たたかうポケモンは、#{pokemon[:name]}"
pokemon_on_hand =Pokemon_on_hand.new
pokemons=pokemon_on_hand.pokemon_on_hand
battle_pokemon=pokemon_on_hand.pokemon_list(pokemon_on_hand)
#追加
attack= Attack.new(battle_pokemon,pokemon, pokemon_on_hand,pokemons)

Attackクラスのinitializeメソッドで初期化しているインスタンス変数は、

  • @legendary_pokemon
    →伝説のポケモン
  • @pokemon_lists
    →Pokemon_on_handクラスのインスタンス

となります。

今回は、主にインスタンス変数の使い道として、バトルをした結果を格納しているローカル変数と比較して、ポケモンの状態によって処理を条件分岐をするという趣旨で3つのインスタンス変数を初期化しました。

要するにダミーみたいなものです。

バトルシステムの実装(pokemon_attack,lifeメソッド)

続いて、バトルシステムを実装していきましょう。バトルシステムを実装するにあたり下記のメソッドを作成します。

  • pokemon_attackメソッド
    →バトルの進行を担うメソッド
  • lifeメソッド
    →ダメージを計算and勝敗の判定

となります。

要するにpokemon_attackメソッドでバトルの進行をして、lifeメソッドでダメージを計算した結果によって条件分岐をするという流れです。

文章だけで説明されても理解が湧かないと思うので、下記のコードを見てください。

class Attack
...省略
 def pokemon_attack(attack_pokemon,pokemon,pokemon_on_hand)
      if attack_pokemon[:name]==@legendary_pokemon[:name]
      puts "#{attack_pokemon[:name]}が#{attack_pokemon[:techniques].sample}を繰り出した"
      puts  Random.rand(0..2) == 0 ?  "#{life(pokemon,pokemon_on_hand)}": "#{pokemon[:name]}に攻撃が当たらなかった"
     elsif attack_pokemon[:name]==  @pokemon_lists[0][0][:name] || @pokemon_lists[0][1][:name] ||@pokemon_lists[0][2][:name]
        puts"どの技を使う❓"
        attack_pokemon[:techniques].each_with_index {|technique,keys|puts "#{keys+1}:#{technique}"}
        input =gets.to_i-1
        puts "#{attack_pokemon[:name]}が#{attack_pokemon[:techniques][input]}を繰り出した"
        puts  Random.rand(0..2) == 0 ?  "#{life(pokemon,pokemon_on_hand)}": "#{pokemon[:name]}に攻撃が当たらなかった"
    end
  def life(pokemon,pokemon_on_hand)
      damages= [30,50,20,10]
      pokemon[:hp]-=damages.sample
      puts "#{pokemon[:name]}はダメージを受けた"
      if pokemon[:hp]<0&&pokemon[:name]==@pokemon[:name]
        puts"#{ pokemon[:name]}が倒れた" 
      elsif pokemon[:hp]<0 && pokemon[:name]==@legendary_pokemon[:name]
          puts"#{ pokemon[:name]}が倒れたあなたの勝ちですです"
          exit
      elsif (@pokemon_lists[0][0][:hp]<=0) && (@pokemon_lists[0][1][:hp]<=0 ) && (@pokemon_lists[0][2][:hp]<=0)
          puts "あなたの手持ちのポケモンは全て力付きました"
          puts "あなたの負けです。"
          exit
      end
    end
   end
end

ソースコードが長いので抜粋して解説していきます。ます初めにpokemon_attackの概要について解説します。

 def pokemon_attack(attack_pokemon,pokemon,pokemon_on_hand)
      if attack_pokemon[:name]==@legendary_pokemon[:name]
      puts "#{attack_pokemon[:name]}が#{attack_pokemon[:techniques].sample}を繰り出した"
      puts  Random.rand(0..2) == 0 ?  "#{life(pokemon,pokemon_on_hand)}": "#{pokemon[:name]}に攻撃が当たらなかった"
    elsif attack_pokemon[:name]==  @pokemon_lists[0][0][:name] || @pokemon_lists[0][1][:name] ||@pokemon_lists[0][2][:name]
        puts"どの技を使う❓"
        attack_pokemon[:techniques].each_with_index {|technique,keys|puts "#{keys+1}:#{technique}"}
        input =gets.to_i-1
        puts "#{attack_pokemon[:name]}が#{attack_pokemon[:techniques][input]}を繰り出した"
        puts  Random.rand(0..2) == 0 ?  "#{life(pokemon,pokemon_on_hand)}": "#{pokemon[:name]}に攻撃が当たらなかった"
    end
  end

pokemon_attackメソッドでは引数の値によって攻撃手法を変えています。具体的な内容として

if attack_pokemon[:name]==@legendary_pokemon[:name]
#ランダムで技を繰り出す
elsif attack_pokemon[:name]==@pokemon_lists[0][0][:name] || @pokemon_lists[0][1][:name] ||@pokemon_lists[0][2][:name]
#コマンド操作で技を繰り出す
ned
  • もしattack_pokemonに格納されている値が@legendary_pokemon(伝説のポケモン)の場合
    →ランダムに技を繰り出す
  • もしattack_pokemonに格納されている値が手持ちのポケモンと同じだった場合
    →コマンド操作で技を選択して攻撃をする

ポケモンゲームをイメージしてください。技を選択して攻撃できるのは、自分が所持しているポケモンだけですよね❓
野生のポケモンと戦う場合やジムリーダーと戦う場合、相手が繰り出してくる技は、わからないと思います。

なので上記の様に攻撃手法を変えています。

[もしattack_pokemonに格納されている値が@legendary_pokemon(伝説のポケモン)の場合]

 if attack_pokemon[:name]==@legendary_pokemon[:name]
      puts "#{attack_pokemon[:name]}が#{attack_pokemon[:techniques].sample}を繰り出した"
      puts  Random.rand(0..2) == 0 ?  "#{life(pokemon,pokemon_on_hand)}": "#{pokemon[:name]}に攻撃が当たらなかった"

こちらは、とてもシンプルです。:techniquesというキーからsampleメソッドを用いてバリューをランダムで取り出しています。
また、三項演算子とRandomを用いて、ランダムで生成された数字が0だった場合のみ、攻撃が相手に当たりlifeメソッドを呼び出します。

[もしattack_pokemonに格納されている値が手持ちのポケモンと同じだった場合]

 elsif attack_pokemon[:name]==  @pokemon_lists[0][0][:name] || @pokemon_lists[0][1][:name] ||@pokemon_lists[0][2][:name]
        puts"どの技を使う❓"
        attack_pokemon[:techniques].each_with_index {|technique,keys|puts "#{keys+1}:#{technique}"}
        input =gets.to_i-1
        puts "#{attack_pokemon[:name]}が#{attack_pokemon[:techniques][input]}を繰り出した"
        puts  Random.rand(0..2) == 0 ?  "#{life(pokemon,pokemon_on_hand)}": "#{pokemon[:name]}に攻撃が当たらなかった"

コマンド操作を再現するためにeach_with_indexを用いて:techniquesというキーからバリューを全て取り出しています。

 puts  Random.rand(0..2) == 0 ?  "#{life(pokemon,pokemon_on_hand)}": "#{pokemon[:name]}に攻撃が当たらなかった"

三項演算子とRandomを用いて、ランダムで生成された数字が0だった場合のみ、攻撃が相手に当たりlifeメソッドを呼び出します。

以上でpokemon_attackメソッドの解説を終わります。続いて、lifeメソッドの処理の内容について解説していきます。

def life(pokemon,pokemon_on_hand)
      damages= [30,50,20,10]
      pokemon[:hp]-=damages.sample
      puts "#{pokemon[:name]}はダメージを受けた"
      if pokemon[:hp]<0&&pokemon[:name]==@pokemon[:name]
        puts"#{ pokemon[:name]}が倒れた" 
      elsif pokemon[:hp]<0 && pokemon[:name]==@legendary_pokemon[:name]
          puts"#{ pokemon[:name]}が倒れたあなたの勝ちですです"
          exit
      elsif (@pokemon_lists[0][0][:hp]<=0) && (@pokemon_lists[0][1][:hp]<=0 ) && (@pokemon_lists[0][2][:hp]<=0)
          puts "あなたの手持ちのポケモンは全て力付きました"
          puts "あなたの負けです。"
          exit
      end
    end
   end
damages= [30,50,20,10]
      pokemon[:hp]-=damages.sample
      puts "#{pokemon[:name]}はダメージを受けた"

まず初めにpokemon_on_handメソッドから呼び出された際に、攻撃対象のポケモンに対してダメージを与えます。具体的な内容として、damagesというダメージ数を管理をする配列を作成し、対象となるポケモンのHPをsampleメソッドを用いて配列damagesからランダムに値を取り出し,値を引いた数をpokemon[:hp]に代入しています。

その後,

  • 攻撃対象のポケモンのHPの状態
  • 手持ちのポケモンか❓
  • 伝説のポケモンか❓

という3つの観点から条件分岐を行い、4つの挙動を再現をしていきます。

  • ポケモンのHPが0以下且つそのポケモンが手持ちのポケモンだった場合
    →ポケモンが倒れた事を出力
  • ポケモンのHPが0以下且つそのポケモンが伝説のポケモンだった場合
    →プログラムを終了(ユーザーの勝ち)
  • ポケモンのHPが0以下且つそのポケモンが手持ちのポケモンであり全て瀕死状態に陥った場合
    →プログラムを終了(ユーザの負け)
  • 該当しない場合
    →メニュー欄に戻る

と上記の様に条件分岐をしていきます。

[ポケモンのHPが0以下且つそのポケモンが手持ちのポケモンだった場合]

if pokemon[:hp]<0&&pokemon[:name]==@pokemon[:name]
        puts"#{ pokemon[:name]}が倒れた" 

ポケモンのHPが0以下且つそのポケモンが手持ちのポケモンだった場合は、シンプルに攻撃対象のポケモンの名前を出力をするだけです。

[ポケモンのHPが0以下且つそのポケモンが伝説のポケモンだった場合]

elsif pokemon[:hp]<0 && pokemon[:name]==@legendary_pokemon[:name]
          puts"#{ pokemon[:name]}が倒れたあなたの勝ちですです"
          exit

ポケモンのHPが0以下且つそのポケモンが伝説のポケモンだった場合は攻撃対象のポケモン(伝説のポケモン)の名前を出力をした後、ユーザーに勝った事を伝え、exitメソッドでプログラムを終了しています。

[ポケモンのHPが0以下且つそのポケモンが手持ちのポケモンであり全て瀕死状態に陥った場合]

elsif (@pokemon_lists[0][0][:hp]<=0) && (@pokemon_lists[0][1][:hp]<=0 ) && (@pokemon_lists[0][2][:hp]<=0)
          puts "あなたの手持ちのポケモンは全て力付きました"
          puts "あなたの負けです。"
          exit

ポケモンのHPが0以下且つそのポケモンが手持ちのポケモンであり全て瀕死状態に陥った場合

  • 手持ちのポケモンが全て力尽きた事
  • ユーザが負けた事

を出力します。
その後、exitメソッドでプログラムを終了します。

[該当しない場合]

def  life(pokemon,pokemon_on_hand)
 damages= [30,50,20,10]
      pokemon[:hp]-=damages.sample
      puts "#{pokemon[:name]}はダメージを受けた"#ここが返り値になります
end

先程、解説した3つの条件に該当しない場合は、if文は、読み込まれず、最終的に

 puts "#{pokemon[:name]}はダメージを受けた"#ここが返り値になります

上記がlifeメソッドの返り値になります。その後、メニュー欄に戻ります。

※Rubyは明示的にretuenメソッドを記述しなくとも、最後の行のプログラムがメソッドの返り値になります。

以上で、攻撃システムの解説を終了します。最後にメニュー欄に処理を追加していきましょう。

メニュー欄を構築しよう

先程、実装したバトルシステムを元にメニュー欄を構築していきましょう。ソースコードは下記です。

  while true do
      puts "HP:#{pokemon[:hp]}#{pokemon[:name]}"
      puts "HP:#{battle_pokemon[:hp]}#{battle_pokemon[:name]}"
      puts"[1]たたかう"
      puts"[2]バッグ"
      puts"[3]ポケモン"
      puts"[4]逃げる"
      input=gets.to_i
      case input
        when 1
            attack.pokemon_attack(battle_pokemon,pokemon,pokemon_on_hand)
            attack.pokemon_attack(pokemon,battle_pokemon,pokemon_on_hand)
            battle_pokemon =  pokemon_on_hand.pokemon_list(pokemon_on_hand)  if battle_pokemon[:hp] <=0
            
        when 2
          #アイテムの処理
       attack.pokemon_attack(pokemon,battle_pokemon,pokemon_on_hand)
            battle_pokemon =  pokemon_on_hand.pokemon_list(pokemon_on_hand)  if battle_pokemon[:hp] <=0
        when 3 
          battle_pokemon =pokemon_on_hand.pokemon_list(pokemon_on_hand)
          puts "ゆけ#{battle_pokemon[:name]}"
          attack.pokemon_attack(pokemon,battle_pokemon,pokemon_on_hand)
          battle_pokemon = pokemon_on_hand.pokemon_list(pokemon_on_hand)  if battle_pokemon[:hp] <=0
        when 4
          #逃げる処理
          puts "・・・"
          gets
          if Random.rand(0..2) == 0
            puts"上手く逃げ切れた"
            exit
          else
            puts "逃げられなかった"
          end
           attack.pokemon_attack(pokemon,battle_pokemon,pokemon_on_hand)
           battle_pokemon = pokemon_on_hand.pokemon_list(pokemon_on_hand)  if battle_pokemon[:hp] <=0
        end 
     end

量が多いですが、実際には同じ処理を記述しているので特に難しくないので安心してください。

  • 戦う
  • バッグ
  • ポケモン
  • 逃げる

とコマンドごとに解説していきます。

[たたかう]

  when 1
            attack.pokemon_attack(battle_pokemon,pokemon,pokemon_on_hand)
            attack.pokemon_attack(pokemon,battle_pokemon,pokemon_on_hand)
            battle_pokemon =  pokemon_on_hand.pokemon_list(pokemon_on_hand)  if battle_pokemon[:hp] <=0

コマンド操作でたたかうを選択し場合、pokemon_attackメソッド2回呼び必要があります。理由は、pokemon_attackメソッドの仕様で,引数の値によって攻撃対象のポケモンを決定しているためです。

 attack.pokemon_attack(battle_pokemon,pokemon,pokemon_on_hand)#上
 attack.pokemon_attack(pokemon,battle_pokemon,pokemon_on_hand)#下

上記のソースコードに注目してください。よく見ると第1引数と第2引数の順番が交互になっている事に気づくと思います。引数の順番を変える事で

  • 攻撃者
  • 攻撃対象者

を区別しています。

battle_pokemon =  pokemon_on_hand.pokemon_list(pokemon_on_hand)  if battle_pokemon[:hp] <=0  

上記のソースコードは、伝説のポケモンが攻撃を行った結果、バトルをしている手持ちのポケモンのHPが0以下になったら、pokemon_listメソッドを呼び出してポケモンを入れ替えるという処理です。

[バッグ]

when 2
          #アイテムの処理
       attack.pokemon_attack(pokemon,battle_pokemon,pokemon_on_hand)
            battle_pokemon =  pokemon_on_hand.pokemon_list(pokemon_on_hand)  if battle_pokemon[:hp] <=0

コマンド操作でバッグを選択した場合は、ユーザーサイドは、アイテムを使う挙動になります。ここでポイントとして抑えて欲しいのは、伝説のポケモンの行動です。

伝説のポケモンは、ユーザーがアイテムを使用するからという理由で、何もしないのはおかしいですよね❓
伝説のポケモンも何かしらアクションを起こさないとゲーム制が失われてよく分からない「ポケモンバトル」になってしまいます。

したがって、伝説のポケモンの行動も同時に実装していかなければなりません。

※アイテムに関しての処理は、他の記事にまとめます。

  attack.pokemon_attack(pokemon,battle_pokemon,pokemon_on_hand)

上記のソースコードは、伝説のポケモンの行動になります。伝説のポケモンはあくまでも野生のポケモンです。ポケモンゲームをプレイした事がある方は、なんとなくでも想像できると思いますが、ユーザーがアイテムを使い終わって、戦いが継続した場合、野生のポケモンは、攻撃を行ってくる事が大半です。

なので今回は、 ユーザーがどのコマンドを選択した場合でも、伝説のポケモンは攻撃を行うという仕様にしています。

[ポケモン]

when 3
  battle_pokemon = pokemon_on_hand.pokemon_list(pokemon_on_hand)
          puts "ゆけ#{battle_pokemon[:name]}"
          attack.pokemon_attack(pokemon,battle_pokemon,pokemon_on_hand)
          battle_pokemon = pokemon_on_hand.pokemon_list(pokemon_on_hand)  if battle_pokemon[:hp] <=0

コマンド操作でポケモンを選択した場合は、 ポケモンを入れ替えた後に伝説のポケモンが攻撃を行う仕様が、ポケモンゲームの仕様に最も近いと思うので、上記の様なソースコードになりました。

また、ポケモンを入れ替えた際に、入れ替えたポケモンのHPが0以下になる事象も考えられるので

 battle_pokemon = pokemon_on_hand.pokemon_list(pokemon_on_hand)  if battle_pokemon[:hp] <=0

再度、ポケモンを入れ替える事に対応できる仕様にするため、 上記のソースコードを記述しています。

[逃げる]

  when 4
            puts "・・・"
            gets
          if Random.rand(0..2) == 0
            puts"上手く逃げ切れた"
            exit
          else
            puts "逃げられなかった"
          end
           attack.pokemon_attack(pokemon,battle_pokemon,pokemon_on_hand)
           battle_pokemon = pokemon_on_hand.pokemon_list(pokemon_on_hand)  if battle_pokemon[:hp] <=0
end

コマンド操作で逃げるを選択した場合

  • 逃げ切れた
    →プログラムを終了する
  • 逃げきれなかった場合
    →伝説のポケモンの攻撃が行う

という流れです。

先程の解説と重複してしまうので解説は省略させていただきます。

以上で、メニュー欄の構築を終わります。

まとめ:ポケモンバトル4[2.0ver]

今回は、バトルシステム(Attckクラス)について実装していきました。バトルシステムは「ポケモンバトル2.0ver」の中で,とても大切な機能な部分なので,とても複雑なプログラムになってしまいました。また、文字数もとても多くなってしまいました。
しかし、バトルシステムを実装した事により、完成に近づいたのも事実です。あともう少しで「ポケモンバトル2.0ver」が完成をするので、もうひと踏ん張りをしてがんばっていきましょう。
以上、kazutoでした。

関連記事