情報学部 菅沼ホーム Java 目次 索引

タワーディフェンスゲーム

  1. ステップ1: マップの表示

      基本的に,「ゲーム枠の作成」で説明した方法とほぼ同じ方法で作成します.以下,各クラスに対して,「ゲーム枠の作成」の場合との違いについて説明していきます.

    1. Game クラス

        基本的に,「ゲーム枠の作成」における Game クラスと同じです( Game.java ).しかし,このゲームでは,アクションゲーム(その2)と同様,画面を格子状に区切り,各格子内に小さな画像を配置し,それらの組合せによって背景や障害物を描いています.さらに,各格子の状態(マップ)を編集可能にしています.ここでは,マップの編集に必要な画像を読み込んでします( 38 行目~ 39 行目).

      01	/****************************/
      02	/* タワーディフェンスゲーム */
      03	/*   coded by Y.Suganuma    */
      04	/****************************/
      05	import java.awt.*;
      06	import java.awt.event.*;
      07	import javax.swing.*;
      08	import main.*;
      09	import map.*;
      10	
      11	public class Game {
      12		public static void main (String[] args)
      13		{
      14			Win win = new Win("タワーディフェンスゲーム");
      15		}
      16	}
      17	
      18	class Win extends JFrame
      19	{
      20		/******************/
      21		/* コンストラクタ */
      22		/******************/
      23		Win(String name)
      24		{
      25						// JFrameクラスのコンストラクタ(Windowのタイトルを引き渡す)
      26			super(name);
      27						// Windowの大きさ
      28			setSize(960, 590);   // 40+20, 70+20
      29						// MainPanel の大きさを決定
      30			Dimension size = getSize();   // パネルの大きさの取得
      31			size.width  -= 60;
      32			size.height -= 90;
      33						// ContentPain を取得し,設定
      34			Container CP = getContentPane();   // ContentPane を取得
      35			CP.setLayout(null);   // レイアウトマネージャを停止
      36			CP.setBackground(new Color(220, 255, 220));   // 背景色
      37						// マップ画像の読み込み
      38			Map.block_image[0] = getToolkit().getImage("game/image/barrier1.jpg");
      39			Map.block_image[1] = getToolkit().getImage("game/image/barrier2.jpg");
      40						// MainPanel オブジェクトの追加
      41			MainPanel pn = new MainPanel(size);   // MainPanel オブジェクトの生成
      42			CP.add(pn);   // MainPanel オブジェクトを ContentPane に追加
      43			pn.setSize(size.width, size.height);
      44			pn.setLocation(10, 10);
      45						// ウィンドウを表示
      46			setVisible(true);
      47						// イベントリスナ
      48			addWindowListener(new WinEnd());
      49		}
      50	
      51		/******************************/
      52		/* 上,左,下,右の余白の設定 */
      53		/******************************/
      54		public Insets getInsets()
      55		{
      56			return new Insets(50, 20, 20, 20);
      57		}
      58	
      59		/************/
      60		/* 終了処理 */
      61		/************/
      62		class WinEnd extends WindowAdapter
      63		{
      64			public void windowClosing(WindowEvent e) {
      65				System.exit(0);
      66			}
      67		}
      68	}
      				

    2. MainPanel クラス

        このクラスに関しても,「ゲーム枠の作成」における MainPanel クラスとほとんど同じプログラムです( MainPanel.java ).ただし,ゲームレベルの設定をなくす代わりに,敵や味方の数をゲーム毎に修正することができるようにしたため,ゲームレベルに関する変数を削除し,17 及び 18 行目を追加してあります..

      01	package main;
      02	
      03	import java.awt.*;
      04	import java.awt.event.*;
      05	import javax.swing.*;
      06	import start.*;
      07	import game.*;
      08	import clear.*;
      09	import over.*;
      10	
      11	public class MainPanel extends JPanel implements Runnable
      12	{
      13		Dimension size;   // パネルの大きさ
      14		boolean in_game = true;   // ゲーム実行中はtrue
      15		public int state = 0;   // ゲーム状態(0:表紙,1:ゲーム,2:クリア,3:オーバー,4:終了)
      16		int old_state = 0;   // 直前のゲーム状態
      17		public int n_enemy[] = new int [2];   // 敵の数
      18		public int n_friend[] = new int [2];   // 味方の数
      19		StartPanel sp;
      20		GamePanel gp;
      21		GameClearPanel gcp;
      22		GameOverPanel gop;
      23		Thread td;
      24				// コンストラクタ
      25		public MainPanel(Dimension size1)
      26		{
      27			size = size1;
      28						// グリッドレイアウト
      29			setLayout(new GridLayout(1, 1, 0, 0));
      30						// ゲームパネルの生成
      31			sp = new StartPanel(size, this);   // スタート(タイトル)
      32			add(sp);
      33						// スレッドの生成
      34			td = new Thread(this);
      35			td.start();
      36		}
      37				// ゲームの状態を変更
      38		public void run()
      39		{
      40			while (in_game) {
      41				try {
      42					td.sleep(100);   // 100 ms 毎の実施
      43				}
      44				catch (InterruptedException e) {}
      45				if (state != old_state) {
      46								// 前のパネルの削除
      47					if (old_state == 0)
      48						remove(sp);
      49					else if (old_state == 1)
      50						remove(gp);
      51					else if (old_state == 2)
      52						remove(gcp);
      53					else
      54						remove(gop);
      55								// 新しいパネルの追加
      56					if (state == 4)   // ゲーム終了
      57						in_game = false;
      58					else {
      59						if (state == 0) {   // StartPanel
      60							sp = new StartPanel(size, this);
      61							add(sp);
      62						}
      63						else if (state == 1) {   // GamePanel
      64							gp = new GamePanel(size, this);
      65							add(gp);
      66						}
      67						else if (state == 2) {   // GameClearPanel
      68							gcp = new GameClearPanel(size, this);
      69							add(gcp);
      70						}
      71						else {   // GameOverPanel
      72							gop = new GameOverPanel(size, this);
      73							add(gop);
      74						}
      75						validate();
      76						old_state = state;
      77					}
      78				}
      79			}
      80		}
      81	}
      				

    3. Map クラス

        アクションゲーム(その2)においては,マップを GamePanel クラス内に記述していましたが,ここでは,マップを編集可能にするため,新たに Map クラスとして作成しました( Map.java ).以下に示すように,すべての変数に対して public,かつ,static 宣言を行っているため,他のクラスから「クラス名.変数名(例えば,Map.block_width )」の形で参照することができます.static 宣言された変数やメソッド(スタティック変数スタティックメソッド)の参照は,「オブジェクト.変数名」「オブジェクト.メソッド名」でないことに注意してください.

      001	package map;
      002	
      003	import java.awt.*;
      004	
      005	public class Map
      006	{
      007						// ブロック画像
      008		public static Image block_image[] = new Image [2];
      009						// ブロックの大きさ
      010		public static int block_width = 25;   // ブロックの幅
      011		public static int block_height = 25;   // ブロックの高さ
      012						// マップ
      013		public static int map_row = 20;   // マップの行数
      014		public static int map_col = 30;   // マップの列数
      015						// マップ1
      016		public static int map1[][][] = {{
      017			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      018			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0},
      019			{0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0},
      020			{0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0},
      021			{0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      022			{0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0},
      023			{0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0},
      024			{0, 0, 0, 0, 1, 0, 1, 0, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0},
      025			{0, 0, 2, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 2, 0, 2, 1, 0, 0, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0},
      026			{0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, 2, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0},
      027			{0, 0, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 1, 0, 0},
      028			{0, 0, 0, 0, 2, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 0, 0, 0, 0},
      029			{0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 2, 2, 2, 0, 0, 0, 0, 1, 0, 0},
      030			{0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0},
      031			{0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0},
      032			{0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0},
      033			{0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 2, 0, 0, 0, 0, 1, 0, 1, 0, 0},
      034			{0, 0, 0, 0, 1, 1, 0, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0},
      035			{0, 0, 1, 0, 0, 1, 0, 0, 0, 2, 2, 2, 0, 0, 0, 1, 0, 1, 0, 0, 2, 0, 0, 0, 0, 1, 0, 1, 0, 0},
      036			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
      037								// マップ2
      038			{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      039			{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      040			{0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 1, 0, 2, 2, 0, 0},
      041			{0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 2, 2, 0, 1, 0, 1, 0, 0, 2, 2, 0, 0},
      042			{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      043			{0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 0, 1, 0, 0, 1, 0, 2, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
      044			{0, 0, 0, 0, 2, 0, 2, 0, 0, 0, 0, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0},
      045			{0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 2, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 2, 0, 0, 0},
      046			{0, 0, 0, 0, 0, 2, 0, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      047			{0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0},
      048			{0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 2, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0},
      049			{0, 0, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
      050			{0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 2, 2, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0},
      051			{0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 0, 1, 0, 0},
      052			{0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0},
      053			{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0},
      054			{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0},
      055			{0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      056			{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0},
      057			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
      058								// マップ3
      059			{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      060			{0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
      061			{0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0},
      062			{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0, 1, 1, 0, 1, 0, 0, 0},
      063			{0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0},
      064			{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0},
      065			{0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 1, 0, 0},
      066			{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 1, 1, 1, 0, 2, 0, 0, 1, 0, 0, 0, 0, 0, 0},
      067			{0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 2, 0, 0, 0},
      068			{0, 0, 0, 0, 0, 2, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
      069			{0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0},
      070			{0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 2, 0, 2, 2, 2, 0, 0},
      071			{0, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      072			{0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0},
      073			{0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0},
      074			{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
      075			{0, 0, 0, 2, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0},
      076			{0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 2, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0},
      077			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0},
      078			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
      079								// 新規
      080			{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      081			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      082			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      083			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      084			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      085			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      086			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      087			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      088			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      089			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      090			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      091			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      092			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      093			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      094			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      095			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      096			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      097			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      098			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
      099			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}};
      100								// 使用するマップ
      101		public static int map[][];
      102	}
      				
      008 行( public static 変数)

        障害物を構成する画像です.ここでは,2 種類の画像を使用し,Game クラスにおいて読み込まれます.

      010,011 行( public static 変数)

        マップを構成するブロックの幅と高さです.

      013,014 行( public static 変数)

        マップの行数及び列数です.

      016 行目~ 099 行( public static 変数)

        4 種類のマップを多次元配列3 次元配列) map1[4][20][30] を使用して定義しています.最初の添え字が,マップの種類を表し,次の 2 つの添え字が実際のマップに対応します.0 は障害物が存在しない,また,それ以外の数字は障害物の種類を表しています.4 番目のマップは,障害物が全く存在しないマップです.

        一般に,多次元配列は,配列の各要素を配列として定義することによって可能です.例えば,2 行 3 列の 2 次元配列 x は,
      	int x[][] = new int [2][3];					
      または,
      	int x[][] = new int [2][];
      	x[0] = new int [3];   // 以下,繰り返し文を使用可能
      	x[1] = new int [3];					
      のような形で定義可能です.初期設定を行いたい場合は,
      	int x[][] = {{1, 2, 3}, {4, 5, 6}};   // 内側の括弧が各行に相当					
      または,
      	int x1[] = {1, 2, 3};
      	int x2[] = {4, 5, 6};
      	int x[][] = {x1, x2};					
      のような方法で行います.

        例えば,3 次元配列 x[4][2][3] のイメージは以下に示す図のようになります.まず,x が 4 個のポインタ配列の先頭を指すアドレスであり,4 個の各ポインタは,各々,2 個のポインタ配列の先頭アドレスを指しています.2 個の各ポインタは,各々,3 個のデータが入る先頭アドレスを指しています.この結果,x[i] は 2 次元配列として,また,x[i][j] は 1 次元配列として扱うことができます.
      101 行目( public static 変数)

        map は,ゲームで使用するマップを指定する変数です.

    4. StartPanel クラス

        基本的な考え方は,「ゲーム枠の作成」における StartPanel クラスと同じですが,マップの編集に入るための入り口設定やゲームパラメータの設定を行うため,いくつかの要素を追加しています( StartPanel.java ).当然のことながら,ゲームタイトル及び「遊び方」の内容を変更しています.

      001	package start;
      002	
      003	import java.awt.*;
      004	import java.awt.event.*;
      005	import javax.swing.*;
      006	import main.*;
      007	import map.*;
      008	
      009	public class StartPanel extends JPanel implements ActionListener
      010	{
      011		boolean in_game = true;
      012		Dimension size;   // パネルの大きさ
      013		MainPanel mp;
      014		JButton b_use, b_map, b_game;
      015		JTextField em1, em2, fr1, fr2, map;
      016				// コンストラクタ
      017		public StartPanel(Dimension size1, MainPanel mp1)
      018		{
      019			size = size1;
      020			mp   = mp1;
      021						// レイアウトマネージャの停止
      022			setLayout(null);
      023						// 背景色の設定
      024			setBackground(Color.white);
      025						// 実行条件設定エリア
      026			Font f = new Font("SansSerif", Font.BOLD, 20);
      027			FontMetrics fm = getFontMetrics(f);
      028			String str;
      029			int x, y, w1, w2, w3, h;
      030								// 敵
      031			x = 0;
      032			str = "敵の数: 敵1:";
      033			JLabel lb1 = new JLabel(str, JLabel.CENTER);
      034			lb1.setFont(f);
      035			w1 = fm.stringWidth(str);
      036			h = fm.getHeight();
      037			lb1.setSize(w1, h);
      038			y = size.height - h - 10;
      039			x += w1;
      040	
      041			em1 = new JTextField("10", 2);
      042			em1.setFont(f);
      043			em1.setSize(30, h);
      044			em1.setHorizontalAlignment(JTextField.RIGHT);
      045			x += 30;
      046	
      047			str = " 敵2:";
      048			w2 = fm.stringWidth(str);
      049			JLabel lb2 = new JLabel(str, JLabel.CENTER);
      050			lb2.setFont(f);
      051			lb2.setSize(w2, h);
      052			x += w2;
      053	
      054			em2 = new JTextField("5", 2);
      055			em2.setFont(f);
      056			em2.setSize(30, h);
      057			em2.setHorizontalAlignment(JTextField.RIGHT);
      058			x += 30;
      059	
      060			x = size.width / 2 - x / 2;
      061			lb1.setLocation(x, y);
      062			add(lb1);
      063			x += w1;
      064			em1.setLocation(x, y);
      065			add(em1);
      066			x += 30;
      067			lb2.setLocation(x, y);
      068			add(lb2);
      069			x += w2;
      070			em2.setLocation(x, y);
      071			add(em2);
      072								// 味方
      073			x = 0;
      074			str = "味方の数制限:  味方1:";
      075			JLabel lb3 = new JLabel(str, JLabel.CENTER);
      076			lb3.setFont(f);
      077			w1 = fm.stringWidth(str);
      078			h = fm.getHeight();
      079			lb3.setSize(w1, h);
      080			y -= (h + 10);
      081			x += w1;
      082	
      083			fr1 = new JTextField("10", 2);
      084			fr1.setFont(f);
      085			fr1.setSize(30, h);
      086			fr1.setHorizontalAlignment(JTextField.RIGHT);
      087			x += 30;
      088	
      089			str = " 味方2:";
      090			w2 = fm.stringWidth(str);
      091			JLabel lb4 = new JLabel(str, JLabel.CENTER);
      092			lb4.setFont(f);
      093			lb4.setSize(w2, h);
      094			x += w2;
      095	
      096			fr2 = new JTextField("4", 2);
      097			fr2.setFont(f);
      098			fr2.setSize(30, h);
      099			fr2.setHorizontalAlignment(JTextField.RIGHT);
      100			x += 30;
      101	
      102			x = size.width / 2 - x / 2;
      103			lb3.setLocation(x, y);
      104			add(lb3);
      105			x += w1;
      106			fr1.setLocation(x, y);
      107			add(fr1);
      108			x += 30;
      109			lb4.setLocation(x, y);
      110			add(lb4);
      111			x += w2;
      112			fr2.setLocation(x, y);
      113			add(fr2);
      114								// マップ
      115			x = 0;
      116			str = "使用するマップ:";
      117			JLabel lb5 = new JLabel(str, JLabel.CENTER);
      118			lb5.setFont(f);
      119			w1 = fm.stringWidth(str);
      120			h = fm.getHeight();
      121			lb5.setSize(w1, h);
      122			y -= (h + 10);
      123			x += w1;
      124	
      125			map = new JTextField("1", 2);
      126			map.setFont(f);
      127			map.setSize(30, h);
      128			map.setHorizontalAlignment(JTextField.RIGHT);
      129			x += 30;
      130	
      131			x = size.width / 2 - x / 2;
      132			lb5.setLocation(x, y);
      133			add(lb5);
      134			x += w1;
      135			map.setLocation(x, y);
      136			add(map);
      137						// ボタンの配置
      138			x = 0;
      139			str = "遊び方";
      140			w1 = fm.stringWidth(str) + 40;
      141			h = fm.getHeight() + 10;
      142			b_use = new JButton(str);
      143			b_use.setFont(f);
      144			b_use.addActionListener(this);
      145			b_use.setSize(w1, h);
      146			y -= (h + 10);
      147			x += w1;
      148	
      149			str = "マップ編集";
      150			w2 = fm.stringWidth(str) + 40;
      151			b_map = new JButton(str);
      152			b_map.setFont(f);
      153			b_map.addActionListener(this);
      154			b_map.setSize(w2, h);
      155			x += w2;
      156	
      157			str = "ゲーム開始";
      158			w3 = fm.stringWidth(str) + 40;
      159			b_game = new JButton(str);
      160			b_game.setFont(f);
      161			b_game.addActionListener(this);
      162			b_game.setSize(w3, h);
      163			x += w3;
      164	
      165			x = size.width / 2 - x / 2 - 5;
      166			b_use.setLocation(x, y);
      167			add(b_use);
      168			x += (w1 + 5);
      169			b_map.setLocation(x, y);
      170			add(b_map);
      171			x += (w2 + 5);
      172			b_game.setLocation(x, y);
      173			add(b_game);
      174		}
      175				// 描画
      176		public void paintComponent(Graphics g)
      177		{
      178			super.paintComponent(g);   // 親クラスの描画
      179			FontMetrics fm;
      180			Font f;
      181			String str;
      182			int w, h;
      183	
      184			f   = new Font("SansSerif", Font.BOLD, 40);
      185			fm  = g.getFontMetrics(f);
      186			str = "タワーディフェンスゲーム";
      187			w   = fm.stringWidth(str);
      188			h   = fm.getHeight();
      189			g.setFont(f);
      190			g.drawString(str, size.width/2-w/2, size.height/2);
      191		}
      192				// ボタンがクリックされたときの処理
      193		public void actionPerformed(ActionEvent e)
      194		{
      195						// 遊び方
      196			if (e.getSource() == b_use) {
      197				Method db = new Method();
      198				db.setVisible(true);
      199				requestFocusInWindow();
      200			}
      201						// ゲームクリア画面とゲームオーバー画面の確認
      202			else if (e.getSource() == b_map) {
      203				in_game  = false;
      204				mp.state = 2;   // ゲームクリア
      205	//			mp.state = 3;   // ゲームオーバー
      206			}
      207						// ゲーム開始
      208			else if (e.getSource() == b_game) {
      209				int k = Integer.parseInt(map.getText()) - 1;
      210				if (k < 0 || k > 3)
      211					k = 0;
      212				Map.map = Map.map1[k];   // 使用するマップ
      213				mp.n_friend[0] = Integer.parseInt(fr1.getText());   // 味方1の数
      214				mp.n_friend[1] = Integer.parseInt(fr2.getText());   // 味方2の数
      215				mp.n_enemy[0] = Integer.parseInt(em1.getText());   // 敵1の数
      216				mp.n_enemy[1] = Integer.parseInt(em2.getText());   // 敵2の数
      217				mp.state = 1;
      218			}
      219		}
      220	}
      221	
      222	/******************/
      223	/* ゲームの遊び方 */
      224	/******************/
      225	class Method extends JDialog
      226	{
      227				// コンストラクタ
      228		Method()
      229		{
      230			setTitle("ゲームの遊び方");
      231					// ContetPain
      232			Container cp = getContentPane();
      233			cp.setLayout(new FlowLayout(FlowLayout.CENTER));
      234			cp.setBackground(new Color(220, 255, 220));   // 背景色
      235			Font f = new Font("MS 明朝", Font.PLAIN, 20);
      236			setSize(850, 370);
      237					// TextArea の追加
      238			JTextArea ta = new JTextArea(15, 80);
      239			ta.setFont(f);
      240			ta.setEditable(false);
      241			ta.setLineWrap(true);
      242			ta.setText(" 左からランダムに現れる敵に対して,味方を適当な位置に配置することによって,敵が領域の右端に達するのを防ぐ.すべての敵を消滅させることができればゲームクリア,敵の一つでも領域の右端に達すればゲームオーバーとなる.\n");
      243	
      244			ta.append("\n");
      245			ta.append("Ⅰ.味方\n");
      246			ta.append(" a.味方1: 配置したい位置をマウスでクリックした後,1 のキーを押せば( 3 のキーを押せばキャンセル)配置され,左方向に動き出す.敵と接触することによって敵を消滅させることができる.配置できる上限数のデフォルトは 10 である.\n");
      247			ta.append(" b.味方2: 配置したい位置をマウスでクリックした後,2 のキーを押せば( 3 のキーを押せばキャンセル)配置される.その位置に停止したままであるが,その近傍を通過した敵を消滅させることができる.配置できる上限数のデフォルトは 4 である.\n");
      248			ta.append("\n");
      249			ta.append("Ⅱ.敵\n");
      250			ta.append(" a.敵1: 左端から現れ,右方向に,障害物を避けながら動く.敵1の数のデフォルトは 10 である.\n");
      251			ta.append(" b.敵2: 左端から現れ,右方向に,障害物を無視して動く.敵2の数のデフォルトは 5 である.\n");
      252			ta.append("\n");
      253			ta.append("Ⅲ.マップの編集: 既に存在する 3 種類のマップを編集したり,新しいマップを作成することができるが,マップの保存機能は持っていない.使用するマップを 1 から 4 で指定する( 4 は障害物が何も無いマップ).マップを編集したい場合は,「マップ編集」ボタンをクリックした後,障害物を配置したい位置をマウスでクリックし,1,または,2 のキーを押せば配置される.また,0 のキーを押せば障害物が無くなる.\n");
      254			JScrollPane scroll = new JScrollPane(ta);
      255			cp.add(scroll);
      256					// Window を閉じるため
      257			addWindowListener(new WinEnd());
      258		}
      259					// 終了処理
      260		class WinEnd extends WindowAdapter
      261		{
      262			public void windowClosing(WindowEvent e) {
      263				setVisible(false);
      264			}
      265		}
      266	}
      				
      031 行目~ 071 行目

        このゲームでは,2 種類の敵を使用します.敵1及び敵2の数を指定するエリアを設定しています.

      073 行目~ 113 行目

        このゲームでは,2 種類の味方を使用します.味方1及び味方2に対する最大使用可能数を指定するエリアを設定しています.

      115 行目~ 136 行目

        使用するマップを指定するエリアを設定しています.1 ~ 4 までの番号を指定でき,マップ4は初期状態において障害物が何も無い状態です.

      138 行目~ 173 行目

        「遊び方」,「マップ編集」,及び,「ゲーム開始」ボタンを配置しています.

      193 行目~ 219 行目

        各ボタンがクリックされたときの処理です.ただし,「マップ編集」ボタンをクリックした場合は,ゲームクリア状態に移行します.204 行目をコメントにし,205 行目のコメントを外せば,ゲームオーバー状態に移行します.現段階では,敵や味方に対する情報が設定されていませんので,ゲームクリア画面やゲームオーバー画面の「もう一度」ボタンは正しく動作しません.これらは,ゲームクリア及びゲームオーバー状態を確認するための処理であり,次のステップでは,マップの編集状態に移行するように修正します.

        「ゲーム開始」ボタンをクリックした場合は,テキストフィールドに入力されたゲーム条件を,対応する変数に設定しています( 209 行目~ 216 行目,MainPanel クラス及び Map クラス参照).Map.map には,Map.map1[k] が代入されますので,Map.map は,20 行 30 列の 2 次元配列とみなせます.配列の場合,アドレスの代入と同等ですので,Map.map と Map.map1[k] は同じ場所を指すことになります.Map.map を介して各要素の値を変更すれば,Map.map1[k] の対応する要素の値も変化することに注意してください.

        209 行目,213 行目~ 216 行目における parseInt は,テキストフィールドに入力された文字列を JTextField クラスのメソッド getText によって取得し,それを整数に変換する Integer クラスのスタティックメソッドです.実数値に変換したい場合は,Double クラスのスタティックメソッド parseDouble を使用します.

    5. GamePanel クラス

        GamePanel クラスは,実際のゲームを実現する部分です( GamePanel.java ).従って,「ゲーム枠の作成」における GamePanel クラス とは,ゲームの種類によってその内容は大きく異なります.今後,このプログラムを完成させていくことになりますが,ここでは,マップを表示しているだけです( 28 行目~ 36 行目).
      01	package game;
      02	
      03	import java.awt.*;
      04	import java.awt.event.*;
      05	import javax.swing.*;
      06	import main.*;
      07	import map.*;
      08	
      09	public class GamePanel extends JPanel
      10	{
      11		Dimension size;   // パネルの大きさ
      12		MainPanel mp;
      13				// コンストラクタ
      14		public GamePanel(Dimension size1, MainPanel mp1)
      15		{
      16			size = size1;
      17			mp   = mp1;
      18						// レイアウトマネージャの停止
      19			setLayout(null);
      20						// 背景色の設定
      21			setBackground(Color.white);
      22		}
      23				// 描画
      24		public void paintComponent(Graphics g)
      25		{
      26			super.paintComponent(g);   // 親クラスの描画
      27						// 背景の描画
      28			for (int i1 = 0; i1 < Map.map_col; i1++) {
      29				int x = i1 * Map.block_width;
      30				for (int i2 = 0; i2 < Map.map_row; i2++) {
      31					if (Map.map[i2][i1] > 0) {
      32						int y = i2 * Map.block_height;
      33						g.drawImage(Map.block_image[Map.map[i2][i1]-1], x, y, Map.block_width, Map.block_height, this);
      34					}
      35				}
      36			}
      37		}
      38	}
      				

    6. GameClearPanel クラス

        このクラスに関しても,「ゲーム枠の作成」における GameClearPanel クラスとほとんど同じです( GameClearPanel.java ).ただし,このゲームではレベルを設定せず,スタート画面においてパラメータを設定し直すことによってゲームレベルに相当する設定を行っています.そのため,同じパラメータでもう一度実行するか,または,スタート画面に戻るかの選択になります.

      package clear;
      
      import java.awt.*;
      import java.awt.event.*;
      import javax.swing.*;
      import main.*;
      
      public class GameClearPanel extends JPanel implements ActionListener
      {
      	Dimension size;   // パネルの大きさ
      	MainPanel mp;
      	JButton bt1, bt2, bt3;
      			// コンストラクタ
      	public GameClearPanel(Dimension size1, MainPanel mp1)
      	{
      		size = size1;
      		mp   = mp1;
      					// レイアウトマネージャの停止
      		setLayout(null);
      					// 背景色の設定
      		setBackground(Color.white);
      					// ボタンの配置
      		Font f = new Font("SansSerif", Font.BOLD, 20);
      		FontMetrics fm = getFontMetrics(f);
      		String str;
      		int x, y, w1, w2, w3, h;
      
      		x = 0;
      		str = "もう一度";
      		w1 = fm.stringWidth(str) + 40;
      		h = fm.getHeight() + 10;
      		bt1 = new JButton(str);
      		bt1.setFont(f);
      		bt1.addActionListener(this);
      		bt1.setSize(w1, h);
      		y = size.height - h - 10;
      		x += w1;
      
      		str = "スタート画面";
      		w2 = fm.stringWidth(str) + 40;
      		bt2 = new JButton(str);
      		bt2.setFont(f);
      		bt2.addActionListener(this);
      		bt2.setSize(w2, h);
      		x += w2;
      
      		str = "ゲーム終了";
      		w3 = fm.stringWidth(str) + 40;
      		bt3 = new JButton(str);
      		bt3.setFont(f);
      		bt3.addActionListener(this);
      		bt3.setSize(w3, h);
      		x += w3;
      
      		x = size.width / 2 - x / 2 - 5;
      		bt1.setLocation(x, y);
      		add(bt1);
      		x += (w1 + 5);
      		bt2.setLocation(x, y);
      		add(bt2);
      		x += (w2 + 5);
      		bt3.setLocation(x, y);
      		add(bt3);
      	}
      			// 描画
      	public void paintComponent(Graphics g)
      	{
      		super.paintComponent(g);   // 親クラスの描画
      		Font f = new Font("SansSerif", Font.BOLD, 40);
      		FontMetrics fm = g.getFontMetrics(f);
      		String str = "Game Clear!";
      		int w = fm.stringWidth(str);
      		g.setFont(f);
      		g.drawString(str, size.width/2-w/2, size.height/2);
      	}
      			// ボタンがクリックされたときの処理
      	public void actionPerformed(ActionEvent e)
      	{
      		if (e.getSource() == bt1)   // もう一度同じゲームを実行
      			mp.state = 1;
      		else if (e.getSource() == bt2)   // スタート画面
      			mp.state = 0;
      		else if (e.getSource() == bt3) {   // ゲーム終了
      			mp.state = 4;
      			bt1.setEnabled(false);
      			bt2.setEnabled(false);
      			bt3.setEnabled(false);
      		}
      	}
      }
      				

    7. GameOverPanel クラス

        このクラスに関しても,「ゲーム枠の作成」における GameOverPanel クラスとほとんど同じです( GameOverPanel.java ).ボタンの制御部分が異なっているだけです.

      package over;
      
      import java.awt.*;
      import java.awt.event.*;
      import javax.swing.*;
      import main.*;
      
      public class GameOverPanel extends JPanel implements ActionListener
      {
      	Dimension size;   // パネルの大きさ
      	MainPanel mp;
      	JButton bt1, bt2, bt3;
      			// コンストラクタ
      	public GameOverPanel(Dimension size1, MainPanel mp1)
      	{
      		size = size1;
      		mp   = mp1;
      					// レイアウトマネージャの停止
      		setLayout(null);
      					// 背景色の設定
      		setBackground(Color.white);
      					// ボタンの配置
      		Font f = new Font("SansSerif", Font.BOLD, 20);
      		FontMetrics fm = getFontMetrics(f);
      		String str;
      		int x, y, w1, w2, w3, h;
      
      		x = 0;
      		str = "もう一度";
      		w1 = fm.stringWidth(str) + 40;
      		h = fm.getHeight() + 10;
      		bt1 = new JButton(str);
      		bt1.setFont(f);
      		bt1.addActionListener(this);
      		bt1.setSize(w1, h);
      		y = size.height - h - 10;
      		x += w1;
      
      		str = "スタート画面";
      		w2 = fm.stringWidth(str) + 40;
      		bt2 = new JButton(str);
      		bt2.setFont(f);
      		bt2.addActionListener(this);
      		bt2.setSize(w2, h);
      		x += w2;
      
      		str = "ゲーム終了";
      		w3 = fm.stringWidth(str) + 40;
      		bt3 = new JButton(str);
      		bt3.setFont(f);
      		bt3.addActionListener(this);
      		bt3.setSize(w3, h);
      		x += w3;
      
      		x = size.width / 2 - x / 2 - 5;
      		bt1.setLocation(x, y);
      		add(bt1);
      		x += (w1 + 5);
      		bt2.setLocation(x, y);
      		add(bt2);
      		x += (w2 + 5);
      		bt3.setLocation(x, y);
      		add(bt3);
      	}
      			// 描画
      	public void paintComponent(Graphics g)
      	{
      		super.paintComponent(g);   // 親クラスの描画
      		Font f = new Font("SansSerif", Font.BOLD, 40);
      		FontMetrics fm = g.getFontMetrics(f);
      		String str = "Game Over!";
      		int w = fm.stringWidth(str);
      		g.setFont(f);
      		g.drawString(str, size.width/2-w/2, size.height/2);
      	}
      			// ボタンがクリックされたときの処理
      	public void actionPerformed(ActionEvent e)
      	{
      		if (e.getSource() == bt1)   // もう一度同じゲームを実行
      			mp.state = 1;
      		else if (e.getSource() == bt2)   // スタート画面
      			mp.state = 0;
      		else if (e.getSource() == bt3) {   // ゲーム終了
      			mp.state = 4;
      			bt1.setEnabled(false);
      			bt2.setEnabled(false);
      			bt3.setEnabled(false);
      		}
      	}
      }
      				

  2. ステップ2: マップの編集

      マップの編集機能を追加します.スタート画面において編集したいマップを指定した後,「マップ編集」ボタンをクリックすると,MapEdit クラスによりマップの編集が可能になります.マップを保存する機能はありませんので,ゲームを再読込すれば編集した結果は失われますが,スタート画面等から行うゲームの再実行の際には,編集結果は保持されます.

      以上述べたことを可能にするためには,今まで存在した画面に加え,マップ編集画面を用意し,それらの画面間の移動を可能にする必要があります.そこで,まず,MainPanel クラスを以下のように修正します.

    01	package main;
    02	
    03	import java.awt.*;
    04	import java.awt.event.*;
    05	import javax.swing.*;
    06	import start.*;
    07	import game.*;
    08	import clear.*;
    09	import over.*;
    10	import map.*;
    11	
    12	public class MainPanel extends JPanel implements Runnable
    13	{
    14		Dimension size;   // パネルの大きさ
    15		boolean in_game = true;   // ゲーム実行中はtrue
    16		public int state = 0;   // ゲーム状態(0:表紙,1:ゲーム,2:クリア,3:オーバー,4:終了,5:マップ編集)
    17		int old_state = 0;   // 直前のゲーム状態
    18		public int n_enemy[] = new int [2];   // 敵の数
    19		public int n_friend[] = new int [2];   // 味方の数
    20		StartPanel sp;
    21		GamePanel gp;
    22		GameClearPanel gcp;
    23		GameOverPanel gop;
    24		MapEdit map_edit;
    25		Thread td;
    26				// コンストラクタ
    27		public MainPanel(Dimension size1)
    28		{
    29			size = size1;
    30						// グリッドレイアウト
    31			setLayout(new GridLayout(1, 1, 0, 0));
    32						// ゲームパネルの生成
    33			sp = new StartPanel(size, this);   // スタート(タイトル)
    34			add(sp);
    35						// スレッドの生成
    36			td = new Thread(this);
    37			td.start();
    38		}
    39				// ゲームの状態を変更
    40		public void run()
    41		{
    42			while (in_game) {
    43				try {
    44					td.sleep(100);   // 100 ms 毎の実施
    45				}
    46				catch (InterruptedException e) {}
    47				if (state != old_state) {
    48								// 前のパネルの削除
    49					if (old_state == 0)
    50						remove(sp);
    51					else if (old_state == 1)
    52						remove(gp);
    53					else if (old_state == 2)
    54						remove(gcp);
    55					else if (old_state == 3)
    56						remove(gop);
    57					else
    58						remove(map_edit);
    59								// 新しいパネルの追加
    60					if (state == 4)   // ゲーム終了
    61						in_game = false;
    62					else {
    63						if (state == 0) {   // StartPanel
    64							sp = new StartPanel(size, this);
    65							add(sp);
    66						}
    67						else if (state == 1) {   // GamePanel
    68							gp = new GamePanel(size, this);
    69							add(gp);
    70						}
    71						else if (state == 2) {   // GameClearPanel
    72							gcp = new GameClearPanel(size, this);
    73							add(gcp);
    74						}
    75						else if (state == 3) {   // GameOverPanel
    76							gop = new GameOverPanel(size, this);
    77							add(gop);
    78						}
    79						else {   // MapEdit
    80							map_edit = new MapEdit(size, this);
    81							add(map_edit);
    82						}
    83						validate();
    84						old_state = state;
    85					}
    86				}
    87			}
    88		}
    89	}
    			

    10,16,24 行目

      9 行目の後ろに 10 行目を付加し,ゲーム状態の数が一つ増加しますので,ステップ1における 15 行目を 16 行目のように変更します.さらに,24 行目を追加します.

    55 行目,57 行目~ 58 行目

      前の状態がマップ編集画面であったときは,そのオブジェクトを削除します.

    75 行目,79 行目~ 82 行目

      マップの編集状態に変化したとき,マップ編集画面( MapEdit クラスのオブジェクト)に移行します.

      次に,スタート画面において,「マップ編集」ボタンをクリックしたときマップ編集画面に移動できるように,StartPanel クラスのステップ1における 201 行目~ 206 行目を削除し,以下のように修正します( 201 行目~ 208 行目).

    001	package start;
    002	
    003	import java.awt.*;
    004	import java.awt.event.*;
    005	import javax.swing.*;
    006	import main.*;
    007	import map.*;
    008	
    009	public class StartPanel extends JPanel implements ActionListener
    010	{
    011		boolean in_game = true;
    012		Dimension size;   // パネルの大きさ
    013		MainPanel mp;
    014		JButton b_use, b_map, b_game;
    015		JTextField em1, em2, fr1, fr2, map;
    016				// コンストラクタ
    017		public StartPanel(Dimension size1, MainPanel mp1)
    018		{
    019			size = size1;
    020			mp   = mp1;
    021						// レイアウトマネージャの停止
    022			setLayout(null);
    023						// 背景色の設定
    024			setBackground(Color.white);
    025						// 実行条件設定エリア
    026			Font f = new Font("SansSerif", Font.BOLD, 20);
    027			FontMetrics fm = getFontMetrics(f);
    028			String str;
    029			int x, y, w1, w2, w3, h;
    030								// 敵
    031			x = 0;
    032			str = "敵の数: 敵1:";
    033			JLabel lb1 = new JLabel(str, JLabel.CENTER);
    034			lb1.setFont(f);
    035			w1 = fm.stringWidth(str);
    036			h = fm.getHeight();
    037			lb1.setSize(w1, h);
    038			y = size.height - h - 10;
    039			x += w1;
    040	
    041			em1 = new JTextField("10", 2);
    042			em1.setFont(f);
    043			em1.setSize(30, h);
    044			em1.setHorizontalAlignment(JTextField.RIGHT);
    045			x += 30;
    046	
    047			str = " 敵2:";
    048			w2 = fm.stringWidth(str);
    049			JLabel lb2 = new JLabel(str, JLabel.CENTER);
    050			lb2.setFont(f);
    051			lb2.setSize(w2, h);
    052			x += w2;
    053	
    054			em2 = new JTextField("5", 2);
    055			em2.setFont(f);
    056			em2.setSize(30, h);
    057			em2.setHorizontalAlignment(JTextField.RIGHT);
    058			x += 30;
    059	
    060			x = size.width / 2 - x / 2;
    061			lb1.setLocation(x, y);
    062			add(lb1);
    063			x += w1;
    064			em1.setLocation(x, y);
    065			add(em1);
    066			x += 30;
    067			lb2.setLocation(x, y);
    068			add(lb2);
    069			x += w2;
    070			em2.setLocation(x, y);
    071			add(em2);
    072								// 味方
    073			x = 0;
    074			str = "味方の数制限:  味方1:";
    075			JLabel lb3 = new JLabel(str, JLabel.CENTER);
    076			lb3.setFont(f);
    077			w1 = fm.stringWidth(str);
    078			h = fm.getHeight();
    079			lb3.setSize(w1, h);
    080			y -= (h + 10);
    081			x += w1;
    082	
    083			fr1 = new JTextField("10", 2);
    084			fr1.setFont(f);
    085			fr1.setSize(30, h);
    086			fr1.setHorizontalAlignment(JTextField.RIGHT);
    087			x += 30;
    088	
    089			str = " 味方2:";
    090			w2 = fm.stringWidth(str);
    091			JLabel lb4 = new JLabel(str, JLabel.CENTER);
    092			lb4.setFont(f);
    093			lb4.setSize(w2, h);
    094			x += w2;
    095	
    096			fr2 = new JTextField("4", 2);
    097			fr2.setFont(f);
    098			fr2.setSize(30, h);
    099			fr2.setHorizontalAlignment(JTextField.RIGHT);
    100			x += 30;
    101	
    102			x = size.width / 2 - x / 2;
    103			lb3.setLocation(x, y);
    104			add(lb3);
    105			x += w1;
    106			fr1.setLocation(x, y);
    107			add(fr1);
    108			x += 30;
    109			lb4.setLocation(x, y);
    110			add(lb4);
    111			x += w2;
    112			fr2.setLocation(x, y);
    113			add(fr2);
    114								// マップ
    115			x = 0;
    116			str = "使用するマップ:";
    117			JLabel lb5 = new JLabel(str, JLabel.CENTER);
    118			lb5.setFont(f);
    119			w1 = fm.stringWidth(str);
    120			h = fm.getHeight();
    121			lb5.setSize(w1, h);
    122			y -= (h + 10);
    123			x += w1;
    124	
    125			map = new JTextField("1", 2);
    126			map.setFont(f);
    127			map.setSize(30, h);
    128			map.setHorizontalAlignment(JTextField.RIGHT);
    129			x += 30;
    130	
    131			x = size.width / 2 - x / 2;
    132			lb5.setLocation(x, y);
    133			add(lb5);
    134			x += w1;
    135			map.setLocation(x, y);
    136			add(map);
    137						// ボタンの配置
    138			x = 0;
    139			str = "遊び方";
    140			w1 = fm.stringWidth(str) + 40;
    141			h = fm.getHeight() + 10;
    142			b_use = new JButton(str);
    143			b_use.setFont(f);
    144			b_use.addActionListener(this);
    145			b_use.setSize(w1, h);
    146			y -= (h + 10);
    147			x += w1;
    148	
    149			str = "マップ編集";
    150			w2 = fm.stringWidth(str) + 40;
    151			b_map = new JButton(str);
    152			b_map.setFont(f);
    153			b_map.addActionListener(this);
    154			b_map.setSize(w2, h);
    155			x += w2;
    156	
    157			str = "ゲーム開始";
    158			w3 = fm.stringWidth(str) + 40;
    159			b_game = new JButton(str);
    160			b_game.setFont(f);
    161			b_game.addActionListener(this);
    162			b_game.setSize(w3, h);
    163			x += w3;
    164	
    165			x = size.width / 2 - x / 2 - 5;
    166			b_use.setLocation(x, y);
    167			add(b_use);
    168			x += (w1 + 5);
    169			b_map.setLocation(x, y);
    170			add(b_map);
    171			x += (w2 + 5);
    172			b_game.setLocation(x, y);
    173			add(b_game);
    174		}
    175				// 描画
    176		public void paintComponent(Graphics g)
    177		{
    178			super.paintComponent(g);   // 親クラスの描画
    179			FontMetrics fm;
    180			Font f;
    181			String str;
    182			int w, h;
    183	
    184			f   = new Font("SansSerif", Font.BOLD, 40);
    185			fm  = g.getFontMetrics(f);
    186			str = "タワーディフェンスゲーム";
    187			w   = fm.stringWidth(str);
    188			h   = fm.getHeight();
    189			g.setFont(f);
    190			g.drawString(str, size.width/2-w/2, size.height/2);
    191		}
    192				// ボタンがクリックされたときの処理
    193		public void actionPerformed(ActionEvent e)
    194		{
    195						// 遊び方
    196			if (e.getSource() == b_use) {
    197				Method db = new Method();
    198				db.setVisible(true);
    199				requestFocusInWindow();
    200			}
    201						// マップの編集
    202			else if (e.getSource() == b_map) {
    203				int k = Integer.parseInt(map.getText()) - 1;
    204				if (k < 0 || k > 3)
    205					k = 0;
    206				Map.map = Map.map1[k];   // 使用するマップ
    207				mp.state = 5;
    208			}
    209						// ゲーム開始
    210			else if (e.getSource() == b_game) {
    211				int k = Integer.parseInt(map.getText()) - 1;
    212				if (k < 0 || k > 3)
    213					k = 0;
    214				Map.map = Map.map1[k];   // 使用するマップ
    215				mp.n_friend[0] = Integer.parseInt(fr1.getText());   // 味方1の数
    216				mp.n_friend[1] = Integer.parseInt(fr2.getText());   // 味方2の数
    217				mp.n_enemy[0] = Integer.parseInt(em1.getText());   // 敵1の数
    218				mp.n_enemy[1] = Integer.parseInt(em2.getText());   // 敵2の数
    219				mp.state = 1;
    220			}
    221		}
    222	}
    223	
    224	/******************/
    225	/* ゲームの遊び方 */
    226	/******************/
    227	class Method extends JDialog
    228	{
    229				// コンストラクタ
    230		Method()
    231		{
    232			setTitle("ゲームの遊び方");
    233					// ContetPain
    234			Container cp = getContentPane();
    235			cp.setLayout(new FlowLayout(FlowLayout.CENTER));
    236			cp.setBackground(new Color(220, 255, 220));   // 背景色
    237			Font f = new Font("MS 明朝", Font.PLAIN, 20);
    238			setSize(850, 370);
    239					// TextArea の追加
    240			JTextArea ta = new JTextArea(15, 80);
    241			ta.setFont(f);
    242			ta.setEditable(false);
    243			ta.setLineWrap(true);
    244			ta.setText(" 左からランダムに現れる敵に対して,味方を適当な位置に配置することによって,敵が領域の右端に達するのを防ぐ.すべての敵を消滅させることができればゲームクリア,敵の一つでも領域の右端に達すればゲームオーバーとなる.\n");
    245	
    246			ta.append("\n");
    247			ta.append("Ⅰ.味方\n");
    248			ta.append(" a.味方1: 配置したい位置をマウスでクリックした後,1 のキーを押せば( 3 のキーを押せばキャンセル)配置され,左方向に動き出す.敵と接触することによって敵を消滅させることができる.配置できる上限数のデフォルトは 10 である.\n");
    249			ta.append(" b.味方2: 配置したい位置をマウスでクリックした後,2 のキーを押せば( 3 のキーを押せばキャンセル)配置される.その位置に停止したままであるが,その近傍を通過した敵を消滅させることができる.配置できる上限数のデフォルトは 4 である.\n");
    250			ta.append("\n");
    251			ta.append("Ⅱ.敵\n");
    252			ta.append(" a.敵1: 左端から現れ,右方向に,障害物を避けながら動く.敵1の数のデフォルトは 10 である.\n");
    253			ta.append(" b.敵2: 左端から現れ,右方向に,障害物を無視して動く.敵2の数のデフォルトは 5 である.\n");
    254			ta.append("\n");
    255			ta.append("Ⅲ.マップの編集: 既に存在する 3 種類のマップを編集したり,新しいマップを作成することができるが,マップの保存機能は持っていない.使用するマップを 1 から 4 で指定する( 4 は障害物が何も無いマップ).マップを編集したい場合は,「マップ編集」ボタンをクリックした後,障害物を配置したい位置をマウスでクリックし,1,または,2 のキーを押せば配置される.また,0 のキーを押せば障害物が無くなる.\n");
    256			JScrollPane scroll = new JScrollPane(ta);
    257			cp.add(scroll);
    258					// Window を閉じるため
    259			addWindowListener(new WinEnd());
    260		}
    261					// 終了処理
    262		class WinEnd extends WindowAdapter
    263		{
    264			public void windowClosing(WindowEvent e) {
    265				setVisible(false);
    266			}
    267		}
    268	}
    			

      最後に,MapEdit クラスを新規に作成します( MapEdit.java ).

    001	package map;
    002	
    003	import java.awt.*;
    004	import java.awt.event.*;
    005	import javax.swing.*;
    006	import main.*;
    007	
    008	public class MapEdit extends JPanel implements ActionListener
    009	{
    010		Dimension size;   // パネルの大きさ
    011		MainPanel mp;
    012		boolean mouse = false;   // マウスがクリックされたか否か
    013		int k1, k2;   // マウスでクリックされた位置
    014		JButton bt;
    015				// コンストラクタ
    016		public MapEdit(Dimension size1, MainPanel mp1)
    017		{
    018			size = size1;
    019			mp   = mp1;
    020						// レイアウトマネージャの停止
    021			setLayout(null);
    022						// 背景色の設定
    023			setBackground(Color.white);
    024						// リスナの追加
    025			addMouseListener(new Mouse());   // マウスリスナ
    026			addKeyListener(new Key());   // キーリスナ
    027						// ボタンの配置
    028			Font f = new Font("SansSerif", Font.BOLD, 20);
    029			FontMetrics fm = getFontMetrics(f);
    030			String str = "OK";
    031			int w = fm.stringWidth(str) + 40;
    032			int h = fm.getHeight();
    033			bt = new JButton(str);
    034			bt.setFont(f);
    035			bt.addActionListener(this);
    036			bt.setSize(w, h);
    037			int x = size.width - w - 10;
    038			int y = size.height / 2 - h / 2;
    039			bt.setLocation(x, y);
    040			add(bt);
    041		}
    042				// 描画
    043		public void paintComponent(Graphics g)
    044		{
    045			super.paintComponent(g);   // 親クラスの描画
    046						// 背景の描画
    047			for (int i1 = 0; i1 < Map.map_col; i1++) {
    048				int x = i1 * Map.block_width;
    049				for (int i2 = 0; i2 < Map.map_row; i2++) {
    050					if (Map.map[i2][i1] > 0) {
    051						int y = i2 * Map.block_height;
    052						g.drawImage(Map.block_image[Map.map[i2][i1]-1], x, y, Map.block_width, Map.block_height, this);
    053					}
    054				}
    055				g.drawLine(x, 0, x, Map.map_row * Map.block_height);
    056				if (i1 == Map.map_col-1)
    057					g.drawLine(x+Map.block_width, 0, x+Map.block_width, Map.map_row * Map.block_height);
    058			}
    059			for (int i1 = 0; i1 < Map.map_row; i1++) {
    060				int y = i1 * Map.block_height;
    061				g.drawLine(0, y, Map.map_col * Map.block_width, y);
    062			}
    063						// マウスがクリックされた位置を表示
    064			if (mouse) {
    065				int y = k1 * Map.block_width + 17;
    066				int x = k2 * Map.block_height + 2;
    067				g.drawString("Now", x, y);
    068			}
    069						// この Component が入力フォーカスを取得することを要求
    070			requestFocusInWindow();
    071		}
    072				// マウスイベントの処理
    073		class Mouse extends MouseAdapter {
    074			public void mousePressed(MouseEvent e) {
    075				k1 = e.getY() / Map.block_height;
    076				k2 = e.getX() / Map.block_width;
    077				if (k1 >= 0 && k1 < Map.map_row && k2 >= 0 && k2 < Map.map_col) {
    078					mouse = true;
    079					repaint();
    080				}
    081				else
    082					mouse = false;
    083			}
    084		}
    085				// キーイベントの処理
    086		class Key extends KeyAdapter {
    087			public void keyPressed(KeyEvent e)
    088			{
    089				if(mouse && e.getKeyCode() >= 48 && e.getKeyCode() <= 50) {
    090					if (e.getKeyCode() == 48)   // キー 0
    091						Map.map[k1][k2] = 0;
    092					else if (e.getKeyCode() == 49)   // キー 1
    093						Map.map[k1][k2] = 1;
    094					else if (e.getKeyCode() == 50)   // キー 2
    095						Map.map[k1][k2] = 2;
    096					mouse = false;
    097					repaint();
    098				}
    099			}
    100		}
    101				// ボタンがクリックされたときの処理
    102		public void actionPerformed(ActionEvent e)
    103		{
    104			if (e.getSource() == bt)
    105				mp.state = 0;
    106		}
    107	}
    			

    012 行目

      マウスでクリックされると true になり,障害物が指定されると false に戻ります.

    025,026 行目

      マウスリスナ及びキーリスナを追加します.

    028 行目~ 040 行目

      「OK」ボタンを追加します.編集を終了したとき,このボタンをクリックするとスタート画面に移動します.

    048 行目~ 054 行目

      マップ情報に基づき,障害物を描画しています.

    055 行目~ 057 行目

      ブロックを区切る縦線を描画しています.56,57 行目は,一番右の縦線の描画です.

    059 行目~ 062 行目

      ブロックを区切る横線を描画しています.

    064 行目~ 068 行目

      マウスがクリックされた位置に「 Now 」という文字列を表示しています.

    070 行目

      この Component が入力フォーカスを取得することを要求しています.キーイベントを受け付けるために必要です.

    074 行目~ 083 行目

      マウスで画面上をクリックしたときの処理です.マウスがクリックされた位置を,マップ上の行と列に変換しています( 075,076 行目).その後,その行と列がマップ内の値であった場合は,変数 mouse の値を true にする(キーイベントを受け付ける状態)と共に,その位置に「 Now 」という文字列を表示しています( repaint ).そうでない場合は,false に設定しています.

    087 行目~ 099 行目

      マウスがクリックされた後,数字キーが押された場合の処理です.089 行目の記述により,キー 0 ~ キー 2 が押された場合だけ処理を行います.キー 0 の場合は障害物を除き,キー 1 または 2 の場合は,対応する障害物をマップ上の指定されたブロックに設定した後,再描画しています( 097 行目).

    102 行目~ 106 行目

      「OK」ボタンがクリックされたときの処理であり,画面状態をスタート画面に変更しています.

  3. ステップ3: 敵の生成

      ここでは,敵を生成し動かします.敵は画面左側のランダムな位置から出現し,右方向へ一定の速度で動いていきます.敵が画面右側のマップの外に達するとゲームオーバーになります.敵には 2 種類あり,敵1は障害物を避けながら移動しますが,敵2は障害物を無視して移動します.game パッケージ内に Enemy クラス( Enemy.java )を新規に作成すると共に,新しい変数や処理を追加するなど,GamePanel クラス( GamePanel.java )を大きく修正します.

    Enemy.java

    01	package game;
    02	
    03	import java.awt.*;
    04	import java.util.Random;
    05	import main.*;
    06	import map.*;
    07	
    08	public class Enemy
    09	{
    10		public Image image;   // 敵画像
    11		public int x = 0;   // 敵のx座標の初期値
    12		public int y;   // 敵のy座標の初期値
    13		public int v = 2;   // 移動速度
    14		public int type;   // 敵のタイプ
    15		public int state = 0;   // 0:右へ移動,1:下へ移動,-1:上へ移動
    16				// コンストラクタ
    17		public Enemy(int sw, Random rn, MainPanel mp)
    18		{
    19			if (sw == 0)
    20				image = mp.getToolkit().getImage("game/image/enemy1.jpg");
    21			else
    22				image = mp.getToolkit().getImage("game/image/enemy2.jpg");
    23	
    24			type = sw;   // 敵のタイプ
    25	
    26			y = (int)(Map.map_row * rn.nextDouble());
    27			if (y >= Map.map_row)
    28				y = Map.map_row - 1;
    29			y *= Map.block_height;
    30		}
    31	}
    			
    19 行目~ 22 行目( Enemy 関数)

      対応する敵の画像を読み込んでいます.

    26 行目~ 29 行目( Enemy 関数)

      画面左側のランダムな位置から敵を出現させるため,y 座標の初期値をランダムに設定しています.nextDouble は,Random クラスのメソッドであり,[0, 1] 区間の一様乱数を返します.この値を変数 Map.map_row に乗じてマップ上の縦方向の位置を求めるわけですが,そのままでは,実数になってしまうため,キャスト演算子型変換( cast 演算子))によって整数に変換しています.

    GamePanel.java

    001	package game;
    002	
    003	import java.awt.*;
    004	import java.awt.event.*;
    005	import javax.swing.*;
    006	import java.util.Random;
    007	import main.*;
    008	import map.*;
    009	
    010	public class GamePanel extends JPanel implements Runnable
    011	{
    012		Dimension size;   // パネルの大きさ
    013		MainPanel mp;
    014		Thread td;
    015		boolean in_game = true;
    016		int count = 0;   // カウンタ
    017		int n_e_now;   // 出現していない敵の数
    018		int e_state[];   // 敵の状態(0:存在,1:動作中,-1:消滅)
    019		Enemy enemy[];   // 敵
    020		int next_time;   // 次の敵の出現時間
    021		int next;   // 次の敵
    022		Random rn;
    023				// コンストラクタ
    024		public GamePanel(Dimension size1, MainPanel mp1)
    025		{
    026			size = size1;
    027			mp   = mp1;
    028			rn   = new Random();
    029						// レイアウトマネージャの停止
    030			setLayout(null);
    031						// 背景色の設定
    032			setBackground(Color.white);
    033						// 敵
    034								// 敵の数
    035			n_e_now = mp.n_enemy[0] + mp.n_enemy[1];   // 出現していない敵の数
    036								// 敵の初期状態
    037			e_state = new int [n_e_now];
    038			enemy = new Enemy [n_e_now];
    039			int k = 0;
    040			for (int i1 = 0; i1 < mp.n_enemy.length; i1++) {
    041				for (int i2 = 0; i2 < mp.n_enemy[i1]; i2++) {
    042					e_state[k] = 0;
    043					enemy[k] = new Enemy(i1, rn, mp);
    044					k++;
    045				}
    046			}
    047								// 次の敵
    048			next_time = (int)(30 * rn.nextDouble());
    049			next = (int)(e_state.length * rn.nextDouble());
    050			if (next >= e_state.length)
    051				next = e_state.length - 1;
    052						// スレッドの生成
    053			td = new Thread(this);
    054			td.start();
    055		}
    056				// スレッドの実行
    057		public void run()
    058		{
    059			while (in_game) {
    060				try {
    061					td.sleep(33);
    062				}
    063				catch (InterruptedException e) {}
    064						// 敵の位置
    065				for (int i1 = 0; i1 < e_state.length; i1++) {
    066					if (e_state[i1] > 0) {
    067						int sw = 0;
    068								// 敵1
    069						if (enemy[i1].type == 0) {
    070										// 右へ移動
    071							if (enemy[i1].state == 0) {
    072								enemy[i1].x += enemy[i1].v;
    073								int k2 = (int)((enemy[i1].x + Map.block_width) / Map.block_width);
    074								if (k2 >= Map.map_col)
    075									sw = 1;
    076								else {
    077									int k1 = (int)(enemy[i1].y / Map.block_height);
    078									if (Map.map[k1][k2] > 0) {
    079										enemy[i1].x = (k2 - 1) * Map.block_width;
    080										double r = Math.random();
    081										if (k1 == Map.map_row-1 && Map.map[k1-1][k2-1] == 0 || k1 > 0 && k1 < Map.map_row-1 && (Map.map[k1+1][k2-1] > 0 || r > 0.5 && Map.map[k1+1][k2-1] == 0 && Map.map[k1-1][k2-1] == 0))
    082											enemy[i1].state = -1;   // 上へ
    083										else if (k1 == 0 && Map.map[k1+1][k2-1] == 0 || k1 > 0 && k1 < Map.map_row-1 && (Map.map[k1-1][k2-1] > 0 || r <= 0.5 && Map.map[k1+1][k2-1] == 0 && Map.map[k1-1][k2-1] == 0))
    084											enemy[i1].state = 1;   // 下へ
    085									}
    086								}
    087							}
    088										// 上へ移動
    089							else if (enemy[i1].state < 0) {
    090								enemy[i1].y -= enemy[i1].v;
    091								int k1 = (int)((enemy[i1].y + Map.block_height) / Map.block_height);
    092								int k2 = (int)(enemy[i1].x / Map.block_width) + 1;
    093								if (Map.map[k1][k2] == 0) {
    094									enemy[i1].y = k1 * Map.block_height;
    095									enemy[i1].state = 0;   // 右へ
    096								}
    097								else if (k1 == 0 || Map.map[k1-1][k2-1] > 0)
    098									enemy[i1].state = 1;   // 下へ
    099							}
    100										// 下へ移動
    101							else {
    102								enemy[i1].y += enemy[i1].v;
    103								int k1 = (int)(enemy[i1].y / Map.block_height);
    104								int k2 = (int)(enemy[i1].x / Map.block_width) + 1;
    105								if (k1 >= Map.map_row-1) {
    106									enemy[i1].y = k1 * Map.block_height;
    107									if (Map.map[k1][k2] == 0)
    108										enemy[i1].state = 0;   // 右へ
    109									else
    110										enemy[i1].state = -1;   // 上へ
    111								}
    112								else {
    113									if (Map.map[k1][k2] == 0) {
    114										enemy[i1].y = k1 * Map.block_height;
    115										enemy[i1].state = 0;   // 右へ
    116									}
    117									else if (Map.map[k1+1][k2-1] > 0)
    118										enemy[i1].state = -1;   // 上へ
    119								}
    120							}
    121						}
    122								// 敵2
    123						else if (enemy[i1].type == 1) {
    124							enemy[i1].x += enemy[i1].v;
    125							int k2 = (int)((enemy[i1].x + Map.block_width) / Map.block_width);
    126							if (k2 >= Map.map_col)
    127								sw = 1;
    128						}
    129								// ゲームオーバーか否か
    130						if (sw > 0) {
    131	//						in_game  = false;
    132	//						mp.state = 3;   // ゲームオーバー
    133						}
    134					}
    135				}
    136						// 次の敵
    137				count++;
    138				if (count == next_time) {
    139					n_e_now--;
    140					count = 0;
    141					e_state[next] = 1;
    142					if (n_e_now > 0) {
    143						next_time = (int)(5 * 30 - 30 * rn.nextDouble());
    144						next = -1;
    145						while (next < 0) {
    146							next = (int)(e_state.length * rn.nextDouble());
    147							if (next >= e_state.length)
    148								next = e_state.length - 1;
    149							if (e_state[next] != 0)
    150								next = -1;
    151						}
    152					}
    153					else
    154						next_time = -1;
    155				}
    156						// 再描画
    157	//			if (sw == 0)
    158					repaint();
    159			}
    160		}
    161				// 描画
    162		public void paintComponent(Graphics g)
    163		{
    164			super.paintComponent(g);   // 親クラスの描画
    165						// 背景の描画
    166			for (int i1 = 0; i1 < Map.map_col; i1++) {
    167				int x = i1 * Map.block_width;
    168				for (int i2 = 0; i2 < Map.map_row; i2++) {
    169					if (Map.map[i2][i1] > 0) {
    170						int y = i2 * Map.block_height;
    171						g.drawImage(Map.block_image[Map.map[i2][i1]-1], x, y, Map.block_width, Map.block_height, this);
    172					}
    173				}
    174			}
    175						// 敵の描画
    176			for (int i1 = 0; i1 < e_state.length; i1++) {
    177				if (e_state[i1] > 0)
    178					g.drawImage(enemy[i1].image, enemy[i1].x, enemy[i1].y, Map.block_width, Map.block_height, this);
    179			}
    180		}
    181	}
    			
    006 行目

      乱数を仕様するため,Random クラスを読み込んでいます.

    010 行目

      マルチスレッドを利用するため,Runnable インターフェースを継承しています.

    016 行目

      敵をランダムな時間間隔で出現させるために使用されるカウンタです.

    017 行目

      まだ画面上に出現していない敵の数です.

    018 行目

      敵の状態を表します.0 は存在するがまだ画面上に現れていない状態,1 は画面上に表示されている状態,また,-1 は消滅した状態を表します.

    020 行目

      次の敵が画面上に現れる時間です(カウンタの値).基本的に,4 ~ 5 秒間隔で現れます.

    021 行目

      次に画面上に現れる敵の番号です.

    022,028 行目

      乱数を使用するため,Random オブジェクトを定義し,生成しています.

    035 行目

      まだ画面上に出現していない敵の数の初期設定です.スタート画面において,敵の数に入力された値になります.

    037 行目~ 046 行目

      敵( Enemy オブジェクト)を生成( 043 行目)すると共に,それらの初期設定( 042 行目)を行っています.040 行目の length は,配列のサイズ(大きさ)を表しています.

    048 行目~ 051 行目

      次に敵が現れる時間( next_time ),及び,敵の番号 next ( [0, n_e_now-1] 区間の一様乱数)をランダムに設定しています.この設定の結果,最初の敵は 1 秒( next_time × 061 行目の描画周期)以内に画面上に現れることになります.

    053,054 行目

      スレッドを生成し,スタートしています.この結果,057 行目~ 160 行目の run メソッドが,変数 in_game が true の間実行されることになります.061 行目の記述により,実際の処理は 33 ms 毎に行われます.

    065 行目~ 135 行目

      065 行目の for 文によって,066 行目~ 134 行目が,敵の数だけ繰り返されます.066 行目の if 文により,移動中の敵(画面に表示されている敵,つまり,e_state[i1] = 1 である敵)に対して以下の処理が実行されます.067 行目の変数 sw は,敵がマップの外に出ると 1 に設定され,ゲームオーバーとなります.069 行目~ 121 行目が敵1,また,123 行目~ 128 行目が敵2に対する処理です.

    • 071 行目~ 087 行目: 敵1が右に進んでいる場合の処理です.右側のマップの外に出た場合はゲームオーバーにしています( 075 行目).そうでない場合は,障害物に衝突したか否かの判定( 078 行目)を行い,衝突した場合は回避行動に移らせています.上下に障害物が無い場合は,上または下がランダムに選択されますが,上または下に障害物が存在した場合は,障害物の無い方向に移動します.なお,ここでは,[0, 1] 区間の一様乱数を生成するために,Math クラスrandom メソッドを利用しています( 080 行目).

    • 089 行目~ 099 行目: 敵1が障害物を避け上に進んでいる場合に対する処理です.右側に障害物が無い場合は右への移動を開始します.また,上にある障害物と衝突した場合は,下に向かって進み始めます.

    • 101 行目~ 120 行目: 敵1が障害物を避け下に進んでいる場合に対する処理です.右側に障害物が無い場合は右への移動を開始します.また,下にある障害物と衝突した場合は,上に向かって進み始めます.

    • 123 行目~ 128 行目: 敵2は障害物を無視して移動しますので,単に右に移動させ,ゲームオーバー判定を行っているだけです.

    • 130 行目~ 133 行目: ゲームオーバーの場合( sw == 1 )は,変数 in_game を false にし,ゲームオーバー画面に移動しています.ただし,デバッグ等の関係から,この処理はコメントにしてあります(コメントを外せば確認できます).

    137 行目

      カウントアップしています.061 行目から,カウンタの値 30 がほぼ 1 秒に相当します.

    138 行目~ 155 行目

      カウンタの値が次の敵出現時刻( next_time )に一致した場合は,指定された敵を画面上に出現させます( 141 行目).その後,まだ画面上に出現していない敵が残っている場合は,次の敵の出現時刻と敵の番号を決定します( 143 行目~ 151 行目).143 行目の指定により,次の敵は 4 ~ 5 秒後に現れることになります.まだ画面上に出現していない敵が残っていない場合は( 153,154 行目),next_time を -1 に設定します.

    157,158 行目

      ゲームオーバーでない場合は,再描画します.ただし,現時点では,デバッグ等のため 157 行目をコメントにしてありますので,常に再描画されます.

    176 行目~ 179 行目

      敵を描画しています.

  4. ステップ4: 味方の生成

      ここでは,味方を生成し動かします.味方は,マウスでクリックすることによって,最初に設定された数の制限内であれば,マップ上の任意の位置に置くことができます.味方には 2 種類あり,味方1は障害物を避けながら左に移動しますが,味方2は最初に置かれた位置に止まります.ただし,味方1は敵と衝突することによって敵を消滅させることができますが,味方2は,その近傍を通った敵を消滅させることができます.すべての敵を消滅させることができれば,ゲームクリアになります.ただし,この段階では,まだ,敵の消滅を行ってはいません.game パッケージ内に Friend クラス( Friend.java )を新規に作成すると共に,GamePanel クラス( GamePanel.java )を修正します.なお,Friend クラスは,Enemy クラスとほとんど同じです.

    Friend.java

    package game;
    
    import java.awt.*;
    import main.*;
    import map.*;
    
    public class Friend
    {
    	public Image image;   // 味方画像
    	public int x = 0;   // 味方のx座標の初期値
    	public int y = 0;   // 味方のy座標の初期値
    	public int v = 2;   // 移動速度
    	public int type;   // 味方のタイプ
    	public int state = 0;   // 0:左へ移動,1:下へ移動,-1:上へ移動
    			// コンストラクタ
    	public Friend(int sw, MainPanel mp)
    	{
    		if (sw == 0)
    			image = mp.getToolkit().getImage("game/image/friend1.jpg");
    		else
    			image = mp.getToolkit().getImage("game/image/friend2.jpg");
    
    		type = sw;   // 味方のタイプ
    	}
    }
    			

    GamePanel.java

    001	package game;
    002	
    003	import java.awt.*;
    004	import java.awt.event.*;
    005	import javax.swing.*;
    006	import java.util.Random;
    007	import main.*;
    008	import map.*;
    009	
    010	public class GamePanel extends JPanel implements Runnable
    011	{
    012		Dimension size;   // パネルの大きさ
    013		MainPanel mp;
    014		Thread td;
    015		boolean in_game = true;
    016		int count = 0;   // カウンタ
    017		int n_e_now;   // 出現していない敵の数
    018		int e_state[];   // 敵の状態(0:存在,1:動作中,-1:消滅)
    019		Enemy enemy[];   // 敵
    020		int next_time;   // 次の敵の出現時間
    021		int next;   // 次の敵
    022		Random rn;
    023		public Image mark;   // マーカー
    024		boolean mouse = false;
    025		int mark_x = 0;   // マーカーのx座標
    026		int mark_y = 0;   // マーカーのy座標
    027		int f_state[][];   // 味方の状態(0:存在,1:動作中,-1:消滅)
    028		Friend friend[][];   // 味方
    029				// コンストラクタ
    030		public GamePanel(Dimension size1, MainPanel mp1)
    031		{
    032			size = size1;
    033			mp   = mp1;
    034			rn   = new Random();
    035						// レイアウトマネージャの停止
    036			setLayout(null);
    037						// リスナの追加
    038			addMouseListener(new Mouse());   // マウスリスナ
    039			addKeyListener(new Key());   // キーリスナ
    040						// 背景色の設定
    041			setBackground(Color.white);
    042						// マーカー
    043			mark = mp.getToolkit().getImage("game/image/mark.jpg");
    044						// 味方
    045								// 味方の初期状態
    046			f_state = new int [mp.n_friend.length][];
    047			friend = new Friend [mp.n_friend.length][];
    048			for (int i1 = 0; i1 < mp.n_friend.length; i1++) {
    049				f_state[i1] = new int [mp.n_friend[i1]];
    050				friend[i1] = new Friend [mp.n_friend[i1]];
    051				for (int i2 = 0; i2 < mp.n_friend[i1]; i2++) {
    052					f_state[i1][i2] = 0;
    053					friend[i1][i2] = new Friend(i1, mp);
    054				}
    055			}
    056						// 敵
    057								// 敵の数
    058			n_e_now = mp.n_enemy[0] + mp.n_enemy[1];   // 出現していない敵の数
    059								// 敵の初期状態
    060			e_state = new int [n_e_now];
    061			enemy = new Enemy [n_e_now];
    062			int k = 0;
    063			for (int i1 = 0; i1 < mp.n_enemy.length; i1++) {
    064				for (int i2 = 0; i2 < mp.n_enemy[i1]; i2++) {
    065					e_state[k] = 0;
    066					enemy[k] = new Enemy(i1, rn, mp);
    067					k++;
    068				}
    069			}
    070								// 次の敵
    071			next_time = (int)(30 * rn.nextDouble());
    072			next = (int)(e_state.length * rn.nextDouble());
    073			if (next >= e_state.length)
    074				next = e_state.length - 1;
    075						// スレッドの生成
    076			td = new Thread(this);
    077			td.start();
    078		}
    079				// スレッドの実行
    080		public void run()
    081		{
    082			while (in_game) {
    083				try {
    084					td.sleep(33);
    085				}
    086				catch (InterruptedException e) {}
    087				int sw = 0;
    088						// 味方の位置
    089								// 味方1
    090				for (int i1 = 0; i1 < f_state[0].length; i1++) {
    091					if (f_state[0][i1] > 0) {
    092										// 左へ移動
    093						if (friend[0][i1].state == 0) {
    094							friend[0][i1].x -= friend[0][i1].v;
    095							if (friend[0][i1].x < 0)
    096								f_state[0][i1] = -1;
    097							else {
    098								int k1 = (int)(friend[0][i1].y / Map.block_height);
    099								int k2 = (int)(friend[0][i1].x / Map.block_width);
    100								if (Map.map[k1][k2] > 0) {
    101									friend[0][i1].x = (k2 + 1) * Map.block_width;
    102									double r = rn.nextDouble();
    103									if (k1 == Map.map_row-1 && Map.map[k1-1][k2+1] == 0 || k1 > 0 && k1 < Map.map_row-1 && (Map.map[k1+1][k2+1] > 0 || r > 0.5 && Map.map[k1+1][k2+1] == 0 && Map.map[k1-1][k2+1] == 0))
    104										friend[0][i1].state = -1;   // 上へ
    105									else if (k1 == 0 && Map.map[k1+1][k2+1] == 0 || k1 > 0 && k1 < Map.map_row-1 && (Map.map[k1-1][k2+1] > 0 || r <= 0.5 && Map.map[k1+1][k2+1] == 0 && Map.map[k1-1][k2+1] == 0))
    106										friend[0][i1].state = 1;   // 下へ
    107								}
    108							}
    109						}
    110										// 上へ移動
    111						else if (friend[0][i1].state < 0) {
    112							friend[0][i1].y -= friend[0][i1].v;
    113							int k1 = (int)((friend[0][i1].y + Map.block_height) / Map.block_height);
    114							int k2 = (int)(friend[0][i1].x / Map.block_width) - 1;
    115							if (Map.map[k1][k2] == 0) {
    116								friend[0][i1].y = k1 * Map.block_height;
    117								friend[0][i1].state = 0;   // 左へ
    118							}
    119							else if (k1 == 0 || Map.map[k1-1][k2+1] > 0)
    120								friend[0][i1].state = 1;   // 下へ
    121						}
    122										// 下へ移動
    123						else {
    124							friend[0][i1].y += friend[0][i1].v;
    125							int k1 = (int)(friend[0][i1].y / Map.block_height);
    126							int k2 = (int)(friend[0][i1].x / Map.block_width) - 1;
    127							if (k1 >= Map.map_row-1) {
    128								friend[0][i1].y = k1 * Map.block_height;
    129								if (Map.map[k1][k2] == 0)
    130									friend[0][i1].state = 0;   // 左へ
    131								else
    132									friend[0][i1].state = -1;   // 上へ
    133							}
    134							else {
    135								if (Map.map[k1][k2] == 0) {
    136									friend[0][i1].y = k1 * Map.block_height;
    137									friend[0][i1].state = 0;   // 右へ
    138								}
    139								else if (Map.map[k1+1][k2+1] > 0)
    140									friend[0][i1].state = -1;   // 上へ
    141							}
    142						}
    143					}
    144				}
    145						// 敵の位置
    146				for (int i1 = 0; i1 < e_state.length; i1++) {
    147					if (e_state[i1] > 0) {
    148								// 敵1
    149						if (enemy[i1].type == 0) {
    150										// 右へ移動
    151							if (enemy[i1].state == 0) {
    152								enemy[i1].x += enemy[i1].v;
    153								int k2 = (int)((enemy[i1].x + Map.block_width) / Map.block_width);
    154								if (k2 >= Map.map_col)
    155									sw = 1;
    156								else {
    157									int k1 = (int)(enemy[i1].y / Map.block_height);
    158									if (Map.map[k1][k2] > 0) {
    159										enemy[i1].x = (k2 - 1) * Map.block_width;
    160										double r = Math.random();
    161										if (k1 == Map.map_row-1 && Map.map[k1-1][k2-1] == 0 || k1 > 0 && k1 < Map.map_row-1 && (Map.map[k1+1][k2-1] > 0 || r > 0.5 && Map.map[k1+1][k2-1] == 0 && Map.map[k1-1][k2-1] == 0))
    162											enemy[i1].state = -1;   // 上へ
    163										else if (k1 == 0 && Map.map[k1+1][k2-1] == 0 || k1 > 0 && k1 < Map.map_row-1 && (Map.map[k1-1][k2-1] > 0 || r <= 0.5 && Map.map[k1+1][k2-1] == 0 && Map.map[k1-1][k2-1] == 0))
    164											enemy[i1].state = 1;   // 下へ
    165									}
    166								}
    167							}
    168										// 上へ移動
    169							else if (enemy[i1].state < 0) {
    170								enemy[i1].y -= enemy[i1].v;
    171								int k1 = (int)((enemy[i1].y + Map.block_height) / Map.block_height);
    172								int k2 = (int)(enemy[i1].x / Map.block_width) + 1;
    173								if (Map.map[k1][k2] == 0) {
    174									enemy[i1].y = k1 * Map.block_height;
    175									enemy[i1].state = 0;   // 右へ
    176								}
    177								else if (k1 == 0 || Map.map[k1-1][k2-1] > 0)
    178									enemy[i1].state = 1;   // 下へ
    179							}
    180										// 下へ移動
    181							else {
    182								enemy[i1].y += enemy[i1].v;
    183								int k1 = (int)(enemy[i1].y / Map.block_height);
    184								int k2 = (int)(enemy[i1].x / Map.block_width) + 1;
    185								if (k1 >= Map.map_row-1) {
    186									enemy[i1].y = k1 * Map.block_height;
    187									if (Map.map[k1][k2] == 0)
    188										enemy[i1].state = 0;   // 右へ
    189									else
    190										enemy[i1].state = -1;   // 上へ
    191								}
    192								else {
    193									if (Map.map[k1][k2] == 0) {
    194										enemy[i1].y = k1 * Map.block_height;
    195										enemy[i1].state = 0;   // 右へ
    196									}
    197									else if (Map.map[k1+1][k2-1] > 0)
    198										enemy[i1].state = -1;   // 上へ
    199								}
    200							}
    201						}
    202								// 敵2
    203						else if (enemy[i1].type == 1) {
    204							enemy[i1].x += enemy[i1].v;
    205							int k2 = (int)((enemy[i1].x + Map.block_width) / Map.block_width);
    206							if (k2 >= Map.map_col)
    207								sw = 1;
    208						}
    209								// ゲームオーバーか否か
    210						if (sw > 0) {
    211	//						in_game  = false;
    212	//						mp.state = 3;   // ゲームオーバー
    213						}
    214					}
    215				}
    216						// 次の敵
    217				count++;
    218				if (count == next_time) {
    219					n_e_now--;
    220					count = 0;
    221					e_state[next] = 1;
    222					if (n_e_now > 0) {
    223						next_time = (int)(5 * 30 - 30 * rn.nextDouble());
    224						next = -1;
    225						while (next < 0) {
    226							next = (int)(e_state.length * rn.nextDouble());
    227							if (next >= e_state.length)
    228								next = e_state.length - 1;
    229							if (e_state[next] != 0)
    230								next = -1;
    231						}
    232					}
    233					else
    234						next_time = -1;
    235				}
    236						// 再描画
    237	//			if (sw == 0)
    238					repaint();
    239			}
    240		}
    241				// 描画
    242		public void paintComponent(Graphics g)
    243		{
    244			super.paintComponent(g);   // 親クラスの描画
    245						// 背景の描画
    246			for (int i1 = 0; i1 < Map.map_col; i1++) {
    247				int x = i1 * Map.block_width;
    248				for (int i2 = 0; i2 < Map.map_row; i2++) {
    249					if (Map.map[i2][i1] > 0) {
    250						int y = i2 * Map.block_height;
    251						g.drawImage(Map.block_image[Map.map[i2][i1]-1], x, y, Map.block_width, Map.block_height, this);
    252					}
    253				}
    254			}
    255						// マーカーの描画
    256			g.drawImage(mark, mark_x, mark_y, Map.block_width, Map.block_height, this);
    257						// 味方の描画
    258			for (int i1 = 0; i1 < mp.n_friend.length; i1++) {
    259				for (int i2 = 0; i2 < f_state[i1].length; i2++) {
    260					if (f_state[i1][i2] > 0)
    261						g.drawImage(friend[i1][i2].image, friend[i1][i2].x, friend[i1][i2].y, Map.block_width, Map.block_height, this);
    262				}
    263			}
    264						// 敵の描画
    265			for (int i1 = 0; i1 < e_state.length; i1++) {
    266				if (e_state[i1] > 0)
    267					g.drawImage(enemy[i1].image, enemy[i1].x, enemy[i1].y, Map.block_width, Map.block_height, this);
    268			}
    269						// この Component が入力フォーカスを取得することを要求
    270			requestFocusInWindow();
    271		}
    272				// マウスイベントの処理
    273		class Mouse extends MouseAdapter {
    274			public void mousePressed(MouseEvent e) {
    275				int k1 = e.getY() / Map.block_height;
    276				int k2 = e.getX() / Map.block_width;
    277				if (k1 >= 0 && k1 < Map.map_row && k2 >= 0 && k2 < Map.map_col && Map.map[k1][k2] == 0) {
    278					mark_x = k2 * Map.block_width;
    279					mark_y = k1 * Map.block_height;
    280					mouse = true;
    281				}
    282			}
    283		}
    284				// キーイベントの処理
    285		class Key extends KeyAdapter {
    286			public void keyPressed(KeyEvent e)
    287			{
    288				if(mouse && e.getKeyCode() >= 49 && e.getKeyCode() <= 51) {
    289					if (e.getKeyCode() == 49) {   // キー 1
    290						if (mp.n_friend[0] > 0) {
    291							mp.n_friend[0]--;
    292							f_state[0][mp.n_friend[0]] = 1;
    293							friend[0][mp.n_friend[0]].x = mark_x;
    294							friend[0][mp.n_friend[0]].y = mark_y;
    295						}
    296					}
    297					else if (e.getKeyCode() == 50) {   // キー 2
    298						if (mp.n_friend[1] > 0) {
    299							mp.n_friend[1]--;
    300							f_state[1][mp.n_friend[1]] = 1;
    301							friend[1][mp.n_friend[1]].x = mark_x;
    302							friend[1][mp.n_friend[1]].y = mark_y;
    303						}
    304					}
    305					mark_x = 0;
    306					mark_y = 0;
    307					mouse = false;
    308				}
    309			}
    310		}
    311	}
    			
    024 行目

      味方を置きたい場所をマウスでクリックすると,マーカーがその場所に移動すると共に,変数 mouse の値が true になり,味方を設置できる状態になります.

    025,026 行目

      マーカーの位置です.

    027 行目

      味方の状態を表します.0 は存在するがまだ画面上に現れていない状態,1 は画面上に表示されている状態,また,-1 は消滅した状態を表します.敵の場合は,2 種類の敵をランダムに出現させたいため,1 次元配列に入れていましたが,味方の場合は,2 種類の味方を指定して処理したいため,2 次元配列で処理しています.f_state[0][i] には味方1の,また,f_state[1][i] には味方2に対する情報が入ります.friend についても同様です.

    038,039 行目

      マウスダウン及ぶキーダウンに対するイベントリスナを追加しています.味方の配置は,味方を設置したい場所(ブロック)をクリックし,その後,そこへ設置したい味方をキーで指定することによって実行されます.そのため,これらのイベントリスナが必要になります.

    043 行目

      マーカーの画像を設定しています.

    046 行目~ 055 行目

      味方( Friend オブジェクト)を生成( 053 行目)すると共に,それらの初期設定( 052 行目)を行っています.

    090 行目~ 144 行目

      画面に表示されている味方1( f_state[0][i1] = 1 の場合)に対する処理です.左へ進む以外,基本的に,敵1に対する処理と同様です.ただし,左側のマップの外に出た場合は味方1を消滅させています( 096 行目).

    256 行目

      マーカーを表示しています.

    258 行目~ 263 行目

      味方を描画しています.

    273 行目~ 283 行目

      マウスがクリックされたときの処理です.マウスがクリックされた位置をマップ上の行と列に変換しています( 275,276 行目).その後,その行と列がマップ内の値であり,かつ,そこに障害物がない場合は,その値を使用してマーカーの座標を設定すると共に,変数 mouse の値を true にしています.

    285 行目~ 310 行目

      マウスがクリックされた後,数字キーが押された場合の処理です.キー 1 の場合は味方1を,キー 2 の場合は味方2を配置しています.その後,マーカーを画面左上に戻しています.

  5. ステップ5: 完成

      味方により敵を消滅させる処理を加えて,ゲームを完成させます.GamePanel クラスに,衝突判定を行う部分を追加すると共に,コメントを外し,ゲームオーバーに移行できるようにします.

    001	package game;
    002	
    003	import java.awt.*;
    004	import java.awt.event.*;
    005	import javax.swing.*;
    006	import java.util.Random;
    007	import main.*;
    008	import map.*;
    009	
    010	public class GamePanel extends JPanel implements Runnable
    011	{
    012		Dimension size;   // パネルの大きさ
    013		MainPanel mp;
    014		Thread td;
    015		boolean in_game = true;
    016		int count = 0;   // カウンタ
    017		int n_e_now;   // 出現していない敵の数
    018		int n_e_now_ex;   // 存在している敵の数
    019		int e_state[];   // 敵の状態(0:存在,1:動作中,-1:消滅)
    020		Enemy enemy[];   // 敵
    021		int next_time;   // 次の敵の出現時間
    022		int next;   // 次の敵
    023		Random rn;
    024		public Image mark;   // マーカー
    025		boolean mouse = false;
    026		int mark_x = 0;   // マーカーのx座標
    027		int mark_y = 0;   // マーカーのy座標
    028		int f_state[][];   // 味方の状態(0:存在,1:動作中,-1:消滅)
    029		Friend friend[][];   // 味方
    030				// コンストラクタ
    031		public GamePanel(Dimension size1, MainPanel mp1)
    032		{
    033			size = size1;
    034			mp   = mp1;
    035			rn   = new Random();
    036						// レイアウトマネージャの停止
    037			setLayout(null);
    038						// リスナの追加
    039			addMouseListener(new Mouse());   // マウスリスナ
    040			addKeyListener(new Key());   // キーリスナ
    041						// 背景色の設定
    042			setBackground(Color.white);
    043						// マーカー
    044			mark = mp.getToolkit().getImage("game/image/mark.jpg");
    045						// 味方
    046								// 味方の初期状態
    047			f_state = new int [mp.n_friend.length][];
    048			friend = new Friend [mp.n_friend.length][];
    049			for (int i1 = 0; i1 < mp.n_friend.length; i1++) {
    050				f_state[i1] = new int [mp.n_friend[i1]];
    051				friend[i1] = new Friend [mp.n_friend[i1]];
    052				for (int i2 = 0; i2 < mp.n_friend[i1]; i2++) {
    053					f_state[i1][i2] = 0;
    054					friend[i1][i2] = new Friend(i1, mp);
    055				}
    056			}
    057						// 敵
    058								// 敵の数
    059			n_e_now = mp.n_enemy[0] + mp.n_enemy[1];   // 出現していない敵の数
    060			n_e_now_ex = n_e_now;   // 存在している敵の数
    061								// 敵の初期状態
    062			e_state = new int [n_e_now];
    063			enemy = new Enemy [n_e_now];
    064			int k = 0;
    065			for (int i1 = 0; i1 < mp.n_enemy.length; i1++) {
    066				for (int i2 = 0; i2 < mp.n_enemy[i1]; i2++) {
    067					e_state[k] = 0;
    068					enemy[k] = new Enemy(i1, rn, mp);
    069					k++;
    070				}
    071			}
    072								// 次の敵
    073			next_time = (int)(30 * rn.nextDouble());
    074			next = (int)(e_state.length * rn.nextDouble());
    075			if (next >= e_state.length)
    076				next = e_state.length - 1;
    077						// スレッドの生成
    078			td = new Thread(this);
    079			td.start();
    080		}
    081				// スレッドの実行
    082		public void run()
    083		{
    084			while (in_game) {
    085				try {
    086					td.sleep(33);
    087				}
    088				catch (InterruptedException e) {}
    089				int sw = 0;
    090						// 味方の位置
    091								// 味方1
    092				for (int i1 = 0; i1 < f_state[0].length; i1++) {
    093					if (f_state[0][i1] > 0) {
    094										// 左へ移動
    095						if (friend[0][i1].state == 0) {
    096							friend[0][i1].x -= friend[0][i1].v;
    097							if (friend[0][i1].x < 0)
    098								f_state[0][i1] = -1;
    099							else {
    100								int k1 = (int)(friend[0][i1].y / Map.block_height);
    101								int k2 = (int)(friend[0][i1].x / Map.block_width);
    102								if (Map.map[k1][k2] > 0) {
    103									friend[0][i1].x = (k2 + 1) * Map.block_width;
    104									double r = rn.nextDouble();
    105									if (k1 == Map.map_row-1 && Map.map[k1-1][k2+1] == 0 || k1 > 0 && k1 < Map.map_row-1 && (Map.map[k1+1][k2+1] > 0 || r > 0.5 && Map.map[k1+1][k2+1] == 0 && Map.map[k1-1][k2+1] == 0))
    106										friend[0][i1].state = -1;   // 上へ
    107									else if (k1 == 0 && Map.map[k1+1][k2+1] == 0 || k1 > 0 && k1 < Map.map_row-1 && (Map.map[k1-1][k2+1] > 0 || r <= 0.5 && Map.map[k1+1][k2+1] == 0 && Map.map[k1-1][k2+1] == 0))
    108										friend[0][i1].state = 1;   // 下へ
    109								}
    110							}
    111						}
    112										// 上へ移動
    113						else if (friend[0][i1].state < 0) {
    114							friend[0][i1].y -= friend[0][i1].v;
    115							int k1 = (int)((friend[0][i1].y + Map.block_height) / Map.block_height);
    116							int k2 = (int)(friend[0][i1].x / Map.block_width) - 1;
    117							if (Map.map[k1][k2] == 0) {
    118								friend[0][i1].y = k1 * Map.block_height;
    119								friend[0][i1].state = 0;   // 左へ
    120							}
    121							else if (k1 == 0 || Map.map[k1-1][k2+1] > 0)
    122								friend[0][i1].state = 1;   // 下へ
    123						}
    124										// 下へ移動
    125						else {
    126							friend[0][i1].y += friend[0][i1].v;
    127							int k1 = (int)(friend[0][i1].y / Map.block_height);
    128							int k2 = (int)(friend[0][i1].x / Map.block_width) - 1;
    129							if (k1 >= Map.map_row-1) {
    130								friend[0][i1].y = k1 * Map.block_height;
    131								if (Map.map[k1][k2] == 0)
    132									friend[0][i1].state = 0;   // 左へ
    133								else
    134									friend[0][i1].state = -1;   // 上へ
    135							}
    136							else {
    137								if (Map.map[k1][k2] == 0) {
    138									friend[0][i1].y = k1 * Map.block_height;
    139									friend[0][i1].state = 0;   // 右へ
    140								}
    141								else if (Map.map[k1+1][k2+1] > 0)
    142									friend[0][i1].state = -1;   // 上へ
    143							}
    144						}
    145										// 衝突判定
    146						if (f_state[0][i1] > 0) {
    147							int k1 = (int)(friend[0][i1].y / Map.block_height);
    148							int k2 = (int)(friend[0][i1].x / Map.block_width);
    149							for (int i2 = 0; i2 < e_state.length; i2++) {
    150								if (e_state[i2] > 0) {
    151									int t1 = (int)(enemy[i2].y / Map.block_height);
    152									int t2 = (int)(enemy[i2].x / Map.block_width);
    153									if (k1 == t1 && k2 == t2) {
    154										e_state[i2] = -1;
    155										n_e_now_ex--;
    156										if (n_e_now_ex == 0) {
    157											sw = -1;
    158											in_game  = false;
    159											mp.state = 2;   // ゲームクリア
    160										}
    161									}
    162								}
    163							}
    164						}
    165					}
    166				}
    167								// 味方2との衝突判定
    168				for (int i1 = 0; i1 < f_state[1].length; i1++) {
    169					if (f_state[1][i1] > 0) {
    170						int k1 = (int)(friend[1][i1].y / Map.block_height);
    171						int k2 = (int)(friend[1][i1].x / Map.block_width);
    172						for (int i2 = 0; i2 < e_state.length; i2++) {
    173							if (e_state[i2] > 0) {
    174								int t1 = (int)(enemy[i2].y / Map.block_height);
    175								int t2 = (int)(enemy[i2].x / Map.block_width);
    176								if (Math.abs(k1-t1) + Math.abs(k2-t2) <= 2) {
    177									e_state[i2] = -1;
    178									n_e_now_ex--;
    179									if (n_e_now_ex == 0) {
    180										sw = -1;
    181										in_game  = false;
    182										mp.state = 2;   // ゲームクリア
    183									}
    184								}
    185							}
    186						}
    187					}
    188				}
    189						// 敵の位置
    190				for (int i1 = 0; i1 < e_state.length; i1++) {
    191					if (e_state[i1] > 0) {
    192								// 敵1
    193						if (enemy[i1].type == 0) {
    194										// 右へ移動
    195							if (enemy[i1].state == 0) {
    196								enemy[i1].x += enemy[i1].v;
    197								int k2 = (int)((enemy[i1].x + Map.block_width) / Map.block_width);
    198								if (k2 >= Map.map_col)
    199									sw = 1;
    200								else {
    201									int k1 = (int)(enemy[i1].y / Map.block_height);
    202									if (Map.map[k1][k2] > 0) {
    203										enemy[i1].x = (k2 - 1) * Map.block_width;
    204										double r = Math.random();
    205										if (k1 == Map.map_row-1 && Map.map[k1-1][k2-1] == 0 || k1 > 0 && k1 < Map.map_row-1 && (Map.map[k1+1][k2-1] > 0 || r > 0.5 && Map.map[k1+1][k2-1] == 0 && Map.map[k1-1][k2-1] == 0))
    206											enemy[i1].state = -1;   // 上へ
    207										else if (k1 == 0 && Map.map[k1+1][k2-1] == 0 || k1 > 0 && k1 < Map.map_row-1 && (Map.map[k1-1][k2-1] > 0 || r <= 0.5 && Map.map[k1+1][k2-1] == 0 && Map.map[k1-1][k2-1] == 0))
    208											enemy[i1].state = 1;   // 下へ
    209									}
    210								}
    211							}
    212										// 上へ移動
    213							else if (enemy[i1].state < 0) {
    214								enemy[i1].y -= enemy[i1].v;
    215								int k1 = (int)((enemy[i1].y + Map.block_height) / Map.block_height);
    216								int k2 = (int)(enemy[i1].x / Map.block_width) + 1;
    217								if (Map.map[k1][k2] == 0) {
    218									enemy[i1].y = k1 * Map.block_height;
    219									enemy[i1].state = 0;   // 右へ
    220								}
    221								else if (k1 == 0 || Map.map[k1-1][k2-1] > 0)
    222									enemy[i1].state = 1;   // 下へ
    223							}
    224										// 下へ移動
    225							else {
    226								enemy[i1].y += enemy[i1].v;
    227								int k1 = (int)(enemy[i1].y / Map.block_height);
    228								int k2 = (int)(enemy[i1].x / Map.block_width) + 1;
    229								if (k1 >= Map.map_row-1) {
    230									enemy[i1].y = k1 * Map.block_height;
    231									if (Map.map[k1][k2] == 0)
    232										enemy[i1].state = 0;   // 右へ
    233									else
    234										enemy[i1].state = -1;   // 上へ
    235								}
    236								else {
    237									if (Map.map[k1][k2] == 0) {
    238										enemy[i1].y = k1 * Map.block_height;
    239										enemy[i1].state = 0;   // 右へ
    240									}
    241									else if (Map.map[k1+1][k2-1] > 0)
    242										enemy[i1].state = -1;   // 上へ
    243								}
    244							}
    245						}
    246								// 敵2
    247						else if (enemy[i1].type == 1) {
    248							enemy[i1].x += enemy[i1].v;
    249							int k2 = (int)((enemy[i1].x + Map.block_width) / Map.block_width);
    250							if (k2 >= Map.map_col)
    251								sw = 1;
    252						}
    253								// ゲームオーバーか否か
    254						if (sw > 0) {
    255							in_game  = false;
    256							mp.state = 3;   // ゲームオーバー
    257						}
    258					}
    259				}
    260						// 次の敵
    261				count++;
    262				if (count == next_time) {
    263					n_e_now--;
    264					count = 0;
    265					e_state[next] = 1;
    266					if (n_e_now > 0) {
    267						next_time = (int)(5 * 30 - 30 * rn.nextDouble());
    268						next = -1;
    269						while (next < 0) {
    270							next = (int)(e_state.length * rn.nextDouble());
    271							if (next >= e_state.length)
    272								next = e_state.length - 1;
    273							if (e_state[next] != 0)
    274								next = -1;
    275						}
    276					}
    277					else
    278						next_time = -1;
    279				}
    280						// 再描画
    281				if (sw == 0)
    282					repaint();
    283			}
    284		}
    285				// 描画
    286		public void paintComponent(Graphics g)
    287		{
    288			super.paintComponent(g);   // 親クラスの描画
    289						// 背景の描画
    290			for (int i1 = 0; i1 < Map.map_col; i1++) {
    291				int x = i1 * Map.block_width;
    292				for (int i2 = 0; i2 < Map.map_row; i2++) {
    293					if (Map.map[i2][i1] > 0) {
    294						int y = i2 * Map.block_height;
    295						g.drawImage(Map.block_image[Map.map[i2][i1]-1], x, y, Map.block_width, Map.block_height, this);
    296					}
    297				}
    298			}
    299						// マーカーの描画
    300			g.drawImage(mark, mark_x, mark_y, Map.block_width, Map.block_height, this);
    301						// 味方の描画
    302			for (int i1 = 0; i1 < mp.n_friend.length; i1++) {
    303				for (int i2 = 0; i2 < f_state[i1].length; i2++) {
    304					if (f_state[i1][i2] > 0)
    305						g.drawImage(friend[i1][i2].image, friend[i1][i2].x, friend[i1][i2].y, Map.block_width, Map.block_height, this);
    306				}
    307			}
    308						// 敵の描画
    309			for (int i1 = 0; i1 < e_state.length; i1++) {
    310				if (e_state[i1] > 0)
    311					g.drawImage(enemy[i1].image, enemy[i1].x, enemy[i1].y, Map.block_width, Map.block_height, this);
    312			}
    313						// この Component が入力フォーカスを取得することを要求
    314			requestFocusInWindow();
    315		}
    316				// マウスイベントの処理
    317		class Mouse extends MouseAdapter {
    318			public void mousePressed(MouseEvent e) {
    319				int k1 = e.getY() / Map.block_height;
    320				int k2 = e.getX() / Map.block_width;
    321				if (k1 >= 0 && k1 < Map.map_row && k2 >= 0 && k2 < Map.map_col && Map.map[k1][k2] == 0) {
    322					mark_x = k2 * Map.block_width;
    323					mark_y = k1 * Map.block_height;
    324					mouse = true;
    325				}
    326			}
    327		}
    328				// キーイベントの処理
    329		class Key extends KeyAdapter {
    330			public void keyPressed(KeyEvent e)
    331			{
    332				if(mouse && e.getKeyCode() >= 49 && e.getKeyCode() <= 51) {
    333					if (e.getKeyCode() == 49) {   // キー 1
    334						if (mp.n_friend[0] > 0) {
    335							mp.n_friend[0]--;
    336							f_state[0][mp.n_friend[0]] = 1;
    337							friend[0][mp.n_friend[0]].x = mark_x;
    338							friend[0][mp.n_friend[0]].y = mark_y;
    339						}
    340					}
    341					else if (e.getKeyCode() == 50) {   // キー 2
    342						if (mp.n_friend[1] > 0) {
    343							mp.n_friend[1]--;
    344							f_state[1][mp.n_friend[1]] = 1;
    345							friend[1][mp.n_friend[1]].x = mark_x;
    346							friend[1][mp.n_friend[1]].y = mark_y;
    347						}
    348					}
    349					mark_x = 0;
    350					mark_y = 0;
    351					mouse = false;
    352				}
    353			}
    354		}
    355	}
    			
    018,060 行目

      存在する敵の数の初期設定です.消滅する毎に 1 ずつ減少し,0 になるとゲームクリアになります.

    146 行目~ 164 行目

      画面に表示されている敵に対して,味方1と同じブロックに入った敵を消滅させます.すべての敵を消滅させると,変数 in_game を false に設定し,ゲームクリア画面に移動します.

    168 行目~ 188 行目

      画面に表示されている敵に対して,味方2との距離(行の差と列の差の和,Math クラスの abs メソッドの利用)が 2 以下であるブロックに入った敵を消滅させます.すべての敵を消滅させると,変数 in_game を false に設定し,ゲームクリア画面に移動します.

    255 行目~ 256 行目,281 行目

      最後に,ステップ4までコメントにしていた部分のコメントを外します.

情報学部 菅沼ホーム Java 目次 索引