ゲームを作りながら学ぶ Java実践③

マインクラフト ゲーム開発

  * 要件の中には、検証してみないとできるかわからない要件も含む

設計

機能要件

  • 体力や空腹ゲージは最大化されること
    • 前提条件を合わせるため
  • 一定のエリア内でしか敵は発生しないこと
    • エリア外で発生しても倒せない
  • 敵の種類はランダムであること
  • 装備や武器はプレイするたびにおなじになること
    • 今の装備を取得して保存しといて、ゲームが始まったら指定の装備にして、終わったらもとの装備に戻す等
    • 対象のプレイヤーのインベントリの中身を直接書き換えることで実現する。
    • コマンド実行時に差し替えて、最終的にはコマンド実行前の装備の状況を保存しておき、ゲーム終了後戻したい
  • スコア(合計点数)ボードが表示できること
    • 画面上にバーンと出るようにする
    • スコアボードにするなど
  • 時間制限を設定できること
  • 敵を倒すと点数が手に入ること
  • 敵の種類によって手に入る点数が異なること
    • ゲーム性を高めるため
    • ゲーム性をしてどうあるべきか
  • 時間制限が来たらエリア内の敵は消滅すること
    • 終わったことをはっきりさせるため
  • 時間制限が来たら合計の点数が保存されること
    • できればデータベースなんかに保存して取り出せるようにしておきたい
  • 保存する情報はスコアとプレイヤー名と日時
  • 新しい情報が入った場合は上書きではなく、すべて保存すること
    • 追加されていく

非機能要件

  • コマンドでゲームを開始できる
    • コマンドでできる方が簡単だから
  • ゲーム中のエリア内のブロックは何があっても破壊されない
    • ゲームモードの変更をしなくてもプラグインで制御できればというもの
  • ゲーム中のオプションでプレイヤーの強さ、敵の種類をある程度コントロールできる
  • 敵の出現数が一定数を超えたときにゲームが重たくならないようにする。
    • マシンのスペックは人によって違うので考慮する
    • 敵の数を制御する
    • 超えたら前に出現したものから消える
  • プラグインを導入すればSpigotを使っていればどのサーバーでも導入できる
  • プログラムへの変更を加えずに、時間やスコアの項目などの設定値をある程度変更できる
    • お客さんはプログラムをいじることができない前提
    • プログラムをいじるのではなく、プログラム内の設定などのできることのモードを作っておく
  • 複数のプレイヤーが同時に実行しても動作すること

機能要件

  • 体力や空腹ゲージは最大化されること
    • コマンドを実行したら、体力と空腹値に20を設定する
  • 敵を倒すと点数が手に入ること
    EntityのSpawnの仕組みを使って敵を出現させる
    Entityが倒れたときのイベントを使って、点数を設定する。

ゲームスタート時の装備変更

実装

今回は、強制上書きとする。
(今回は、時間制限が設定できていないので、時間が来たらもとに戻るという設定ができないため)

まず、playerの情報を取るために

 player.setHealth(20);
 player.setFoodLevel(20);

の下に

 player.getInventory();

を入力

よく使うため変数を取っておくために

「getInventory」にカーソルをあて、右クリック「リファクタリング」→「変数の導入」→「エンター」

また、上の部分にわかりやすくなるように「//」を入力し、その後に「プレイヤーの状態を初期化する。体力と空腹度を最大値にする)」と解説を付けておく。

今後のための参考

そこで、インベントリに対しては、「set」というものができるが、今後のためにできることを示しておくと

inventory.getHelmet();

つづいて、これを入力。
これにより、装備を上書きできるようになる。

ここで、「getHelmet」にカーソルをあて、「リファクタリング」→「変数の導入」→「itemStack」を選択

ItemStack itemStack = inventory.getHelmet();

となり、ここで、「元の状態の装備を残しておく

そして、その下に

      inventory.setHelmet();
      
      // ゲーム終了
      inventory.setHelmet(helmet);

を入力して

      ItemStack itemStack = inventory.getHelmet();

      inventory.setHelmet();

      // ゲーム終了
      inventory.setHelmet(helmet);

こうしておくと、装備を変更したあと、ゲームが終了したら装備をもとに戻すということができる。

続きからの実装

しかし、ここでは時間制限がないので、上書きするコードを実装していく

そこで

 inventory.setHelmet();

だけ残し

 inventory.setHelmet(new ItemStack(Material.DIAMOND_HELMET));

とします。
(おそらく、新しい装備として「DIAMOND」という頭の装備を取ってきてつけるようにしなさいという意味)

そして、その下に

inventory.setChestplate(new ItemStack(Material.DIAMOND_CHESTPLATE));

を追加。
(おそらく、新しい装備として「DIAMOND」という防具の装備を取ってきてつけるようにしなさいという意味)

そして、次に

 inventory.setLeggings(new ItemStack(Material.DIAMOND_LEGGINGS));

を追加。

 inventory.setBoots(new ItemStack(Material.DIAMOND_BOOTS));

を追加。

inventory.setItemInMainHand(new ItemStack(Material.DIAMOND_SWORD));

を追加。
これは、下のアイテムバーの選択しているものが、強制的に変わるので、保存しておかないと大事に取っておいた武器なんかが上書きされてなくなってしまうということが起こってしまうので、気をつけるようにしとくほうが良い。

そこで、全体としては

      // プレイヤーの状態を初期化する。(体力と空腹度を最大値にする)
     player.setHealth(20);
     player.setFoodLevel(20);
      PlayerInventory inventory = player.getInventory();
      inventory.setHelmet(new ItemStack(Material.DIAMOND_HELMET));
      inventory.setChestplate(new ItemStack(Material.DIAMOND_CHESTPLATE));
      inventory.setLeggings(new ItemStack(Material.DIAMOND_LEGGINGS));
      inventory.setBoots(new ItemStack(Material.DIAMOND_BOOTS));
      inventory.setItemInMainHand(new ItemStack(Material.DIAMOND_SWORD));

となる。

実証!!

成功!!!

ちょっと攻撃されて矢が刺さってますが・・・😂

最後に、最強とされる

   player.setHealth(20);
     player.setFoodLevel(20);
      PlayerInventory inventory = player.getInventory();
      inventory.setHelmet(new ItemStack(Material.NETHERITE_HELMET));
      inventory.setChestplate(new ItemStack(Material.NETHERITE_CHESTPLATE));
      inventory.setLeggings(new ItemStack(Material.NETHERITE_LEGGINGS));
      inventory.setBoots(new ItemStack(Material.DIAMOND_BOOTS));
      inventory.setItemInMainHand(new ItemStack(Material.NETHERITE_SWORD));

に変更しメソッド抽出しておく。全体を指定し右クリック「リファクタリング」→「メソッド抽出」

  private static void initPlayerStatus(Player player) {
    // プレイヤーの状態を初期化する。(体力と空腹度を最大値にする)
    player.setHealth(20);
    player.setFoodLevel(20);
    PlayerInventory inventory = player.getInventory();
    inventory.setHelmet(new ItemStack(Material.NETHERITE_HELMET));
    inventory.setChestplate(new ItemStack(Material.NETHERITE_CHESTPLATE));
    inventory.setLeggings(new ItemStack(Material.NETHERITE_LEGGINGS));
    inventory.setBoots(new ItemStack(Material.DIAMOND_BOOTS));
    inventory.setItemInMainHand(new ItemStack(Material.NETHERITE_SWORD));
  }

が抽出され、

上の部分を「任意の名前として」

initPlayerStatus(player);

と変更しておく。

そして、説明文として

  /**
   * ゲームを始める前にプレイヤーの状態を設定する。
   * 体力と空腹度を最大にして、装備はネザライト一式になる。
   *
   * @param player コマンドを実行したプレイヤー
   */

を挿入。

全体としては

package plugin.enemydown.command;

import java.util.List;
import java.util.SplittableRandom;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.PlayerInventory;

public class EnemyDownCommand implements CommandExecutor {

  @Override
  public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
    if(sender instanceof Player player){

      //前提条件
      World world = player.getWorld();

      initPlayerStatus(player);

      world.spawnEntity(getEnemySpawnlocation(player, world), getEnemy());
    }
      return false;
  }

  /**
   * ゲームを始める前にプレイヤーの状態を設定する。
   * 体力と空腹度を最大にして、装備はネザライト一式になる。
   *
   * @param player コマンドを実行したプレイヤー
   */
  private void initPlayerStatus(Player player) {

    player.setHealth(20);
    player.setFoodLevel(20);

    PlayerInventory inventory = player.getInventory();
    inventory.setHelmet(new ItemStack(Material.NETHERITE_HELMET));
    inventory.setChestplate(new ItemStack(Material.NETHERITE_CHESTPLATE));
    inventory.setLeggings(new ItemStack(Material.NETHERITE_LEGGINGS));
    inventory.setBoots(new ItemStack(Material.DIAMOND_BOOTS));
    inventory.setItemInMainHand(new ItemStack(Material.NETHERITE_SWORD));
  }

  /**
   *敵の出現場所を取得します。
   * 出現エリアはX軸とZ軸は、自分の一からプラス、ランダムで−10〜9の値がsettingされます。
   * y軸はプレイヤーと同じ位置になります。
   *
   * @param player コマンドを実行したプレイヤー
   * @param world コマンドを実行したプレヤーが所属するワールド。
   * @return 敵の出現場所
   */

  private Location getEnemySpawnlocation(Player player, World world) {
    Location playerLocation = player.getLocation();
    int randomX= new SplittableRandom().nextInt(20) - 10;
    int randomZ= new SplittableRandom().nextInt(20) - 10;

    double x = playerLocation.getX()+randomX;
    double y = playerLocation.getY();
    double z = playerLocation.getZ()+randomZ;

    return new Location(world, x, y, z);
  }

  /**
   * ランダムで敵を抽出して、その結果の敵を取得します。
   * <p>
   * @return 敵
   */
  private EntityType getEnemy() {
    List<EntityType> enemyList =  List.of(EntityType.ZOMBIE, EntityType.SKELETON);
    int random= new SplittableRandom().nextInt(2);
    return enemyList.get(random);
  }
}

となりましたので、再度、実証!!

成功!!!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

CAPTCHA


目次