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

迷路

  1. ステップ1: ゲームの枠組み

      迷路から脱出するゲームです.基本的に,「ゲーム枠の作成」で説明した方法とほぼ同じ方法で作成します.ただし,パネルのサイズは変更しています.また,ゲームオーバーの画面は存在しません.以下,各クラスに対して,「ゲーム枠の作成」の場合との違いについて説明していきます.

    1. Game クラス

        「ゲーム枠の作成」における Game クラスと全く同じプログラムです( Game.java ).

      /*************************/
      /* 迷路                  */
      /*   coded by Y.Suganuma */
      /*************************/
      import java.awt.*;
      import java.awt.event.*;
      import javax.swing.*;
      import main.*;
      
      public class Game {
      	public static void main (String[] args)
      	{
      		Win win = new Win("迷路");
      	}
      }
      
      class Win extends JFrame
      {
      	/******************/
      	/* コンストラクタ */
      	/******************/
      	Win(String name)
      	{
      					// JFrameクラスのコンストラクタ(Windowのタイトルを引き渡す)
      		super(name);
      					// Windowの大きさ
      		setSize(360, 490);   // 40+20, 70+20
      					// MainPanel の大きさを決定
      		Dimension size = getSize();
      		size.width  -=60;
      		size.height -=90;
      					// ContentPain を取得し,設定
      		Container CP = getContentPane();   // ContentPane を取得
      		CP.setLayout(null);   // レイアウトマネージャを停止
      		CP.setBackground(new Color(220, 255, 220));   // 背景色
      					// MainPanel を追加し,設定
      		MainPanel pn = new MainPanel(size);   // MainPanel オブジェクトの生成
      		CP.add(pn);   // MainPanel オブジェクトを ContentPane に追加
      		pn.setSize(size.width, size.height);
      		pn.setLocation(10, 10);
      					// ウィンドウを表示
      		setVisible(true);
      					// イベントリスナ
      		addWindowListener(new WinEnd());
      	}
      
      	/******************************/
      	/* 上,左,下,右の余白の設定 */
      	/******************************/
      	public Insets getInsets()
      	{
      		return new Insets(50, 20, 20, 20);
      	}
      
      	/************/
      	/* 終了処理 */
      	/************/
      	class WinEnd extends WindowAdapter
      	{
      		public void windowClosing(WindowEvent e) {
      			System.exit(0);
      		}
      	}
      }
      				

    2. MainPanel クラス

        「ゲーム枠の作成」における MainPanel クラスとほぼ同じですが,ゲームオーバーの状態を無くしている部分だけが異なっています( MainPanel.java ).

      package main;
      
      import java.awt.*;
      import java.awt.event.*;
      import javax.swing.*;
      import start.*;
      import game.*;
      import clear.*;
      
      public class MainPanel extends JPanel implements Runnable
      {
      	Dimension size;   // パネルの大きさ
      	boolean in_game = true;   // ゲーム実行中はtrue
      	public int state = 0;   // ゲーム状態(0:表紙,1:ゲーム,2:クリア,3:終了)
      	public int level = 1;   // ゲームレベル
      	int old_state = 0;   // 直前のゲーム状態
      	StartPanel sp;
      	GamePanel gp;
      	GameClearPanel gcp;
      	Thread td;
      			// コンストラクタ
      	public MainPanel(Dimension size1)
      	{
      		size = size1;
      					// グリッドレイアウト
      		setLayout(new GridLayout(1, 1, 0, 0));
      					// ゲームパネルの生成
      		sp = new StartPanel(size, this);   // スタート(タイトル)
      		add(sp);
      					// スレッドの生成
      		td = new Thread(this);
      		td.start();
      	}
      			// ゲームの状態を変更
      	public void run()
      	{
      		while (in_game) {
      			try {
      				td.sleep(100);   // 100 ms 毎の実施
      			}
      			catch (InterruptedException e) {}
      			if (state != old_state) {
      							// 前のパネルの削除
      				if (old_state == 0)
      					remove(sp);
      				else if (old_state == 1)
      					remove(gp);
      				else
      					remove(gcp);
      							// 新しいパネルの追加
      				if (state == 3)   // ゲーム終了
      					in_game = false;
      				else {
      					if (state == 0) {   // StartPanel
      						sp = new StartPanel(size, this);
      						add(sp);
      					}
      					else if (state == 1) {   // GamePanel
      						gp = new GamePanel(size, this);
      						add(gp);
      					}
      					else {   // GameClearPanel
      						gcp = new GameClearPanel(size, this);
      						add(gcp);
      					}
      					validate();
      					old_state = state;
      				}
      			}
      		}
      	}
      }
      				

    3. StartPanel クラス

        「ゲーム枠の作成」における StartPanel クラスでは,「 s 」キーを押すとゲームを開始するように設定していましたが,ここではマウスのダブルクリックによって開始するように変更してあります( 36 行目,73 行目~ 78 行目).当然のことながら,ゲームタイトル及び「遊び方」の内容を変更しています( StartPanel.java ).

      001	package start;
      002	
      003	import java.awt.*;
      004	import java.awt.event.*;
      005	import javax.swing.*;
      006	import main.*;
      007	
      008	public class StartPanel extends JPanel implements ActionListener
      009	{
      010		boolean in_game = true;
      011		Dimension size;   // パネルの大きさ
      012		MainPanel mp;
      013		JButton bt;
      014				// コンストラクタ
      015		public StartPanel(Dimension size1, MainPanel mp1)
      016		{
      017			size = size1;
      018			mp   = mp1;
      019						// レイアウトマネージャの停止
      020			setLayout(null);
      021						// 背景色の設定
      022			setBackground(Color.white);
      023						// ボタンの配置
      024			Font f = new Font("SansSerif", Font.BOLD, 20);
      025			FontMetrics fm = getFontMetrics(f);
      026			String str = "遊び方";
      027			int w = fm.stringWidth(str) + 40;
      028			int h = fm.getHeight() + 10;
      029			bt = new JButton(str);
      030			bt.setFont(f);
      031			bt.addActionListener(this);
      032			bt.setSize(w, h);
      033			bt.setLocation(size.width/2-w/2, 5);
      034			add(bt);
      035						// マウスリスナの追加
      036			addMouseListener(new Mouse());
      037		}
      038				// 描画
      039		public void paintComponent(Graphics g)
      040		{
      041			super.paintComponent(g);   // 親クラスの描画
      042			FontMetrics fm;
      043			Font f;
      044			String str;
      045			int w, h;
      046	
      047			f   = new Font("SansSerif", Font.BOLD, 40);
      048			fm  = g.getFontMetrics(f);
      049			str = "迷路";
      050			w   = fm.stringWidth(str);
      051			h   = fm.getHeight();
      052			g.setFont(f);
      053			g.drawString(str, size.width/2-w/2, size.height/2);
      054	
      055			f   = new Font("Serif", Font.PLAIN, 20);
      056			fm  = g.getFontMetrics(f);
      057			str = "ゲーム開始:ダブルクリック";
      058			w   = fm.stringWidth(str);
      059			h   = size.height - fm.getHeight() - 10;
      060			g.setFont(f);
      061			g.drawString(str, size.width/2-w/2, h);
      062		}
      063				// ボタンがクリックされたときの処理
      064		public void actionPerformed(ActionEvent e)
      065		{
      066			if (e.getSource() == bt) {
      067				Method db = new Method();
      068				db.setVisible(true);
      069				requestFocusInWindow();
      070			}
      071		}
      072				// ダブルクリックされたときの処理
      073		class Mouse extends MouseAdapter {
      074			public void mouseClicked(MouseEvent e) {
      075				if (e.getClickCount() == 2)
      076					mp.state = 1;
      077			}
      078		}
      079	}
      080	
      081	/******************/
      082	/* ゲームの遊び方 */
      083	/******************/
      084	class Method extends JDialog
      085	{
      086				// コンストラクタ
      087		Method()
      088		{
      089			setTitle("ゲームの遊び方");
      090					// ContetPain
      091			Container cp = getContentPane();
      092			cp.setLayout(new FlowLayout(FlowLayout.CENTER));
      093			cp.setBackground(new Color(220, 255, 220));   // 背景色
      094			Font f = new Font("MS 明朝", Font.PLAIN, 20);
      095			setSize(550, 160);
      096					// TextArea の追加
      097			JTextArea ta = new JTextArea(5, 50);
      098			ta.setFont(f);
      099			ta.setEditable(false);
      100			ta.setLineWrap(true);
      101			ta.setText("・ゲーム開始: 画面上でダブルクリック\n");
      102	
      103			ta.append("・主人公の移動: 「上」,「下」,「左」,「右」ボタンをクリックする\n");
      104			JScrollPane scroll = new JScrollPane(ta);
      105			cp.add(scroll);
      106					// Window を閉じるため
      107			addWindowListener(new WinEnd());
      108		}
      109					// 終了処理
      110		class WinEnd extends WindowAdapter
      111		{
      112			public void windowClosing(WindowEvent e) {
      113				setVisible(false);
      114			}
      115		}
      116	}
      				

    4. GamePanel クラス

        GamePanel クラスは,実際のゲームを実現するクラスです.従って,「ゲーム枠の作成」における GamePanel クラスとは,ゲームの種類によってその内容は大きく異なります.今後,このクラス及びその関連クラスを完成させていくことになりますが,ここでは, GamePanel クラス( GamePanel.java : 001 行目~ 027 行目)に,迷路と主人公を表示するパネル( Maze.java : 029 行目~ 045 行目)と主人公を操作するパネル( Operation.java : 047 行目~ 120 行目)を貼り付けています.

      001	package game;
      002	
      003	import java.awt.*;
      004	import javax.swing.*;
      005	import main.*;
      006	
      007	public class GamePanel extends JPanel
      008	{
      009				// コンストラクタ
      010		public GamePanel(Dimension size, MainPanel mp)
      011		{
      012						// レイアウトマネージャの停止
      013			setLayout(null);
      014						// 背景色の設定
      015			setBackground(new Color(220, 255, 220));   // 背景色
      016						// パネルの配置
      017			Maze mz = new Maze(size, mp);
      018			mz.setSize(size.width, size.width);
      019			mz.setLocation(0, 0);
      020			add(mz);
      021	
      022			Operation op = new Operation(size.width, size.height-size.width-10, mp, mz);
      023			op.setSize(size.width, size.height-size.width-10);
      024			op.setLocation(0, size.width+10);
      025			add(op);
      026		}
      027	}
      028	
      029	package game;
      030	
      031	import java.awt.*;
      032	import javax.swing.*;
      033	import main.*;
      034	
      035	class Maze extends JPanel
      036	{
      037				// コンストラクタ
      038		public Maze(Dimension size, MainPanel mp)
      039		{
      040						// レイアウトマネージャの停止
      041			setLayout(null);
      042						// 背景色の設定
      043			setBackground(Color.white);
      044		}
      045	}
      046	
      047	package game;
      048	
      049	import java.awt.*;
      050	import java.awt.event.*;
      051	import javax.swing.*;
      052	import main.*;
      053	
      054	public class Operation extends JPanel implements ActionListener
      055	{
      056		MainPanel mp;
      057		Maze mz;
      058		JButton left, right, up, down;
      059				// コンストラクタ
      060		public Operation(int width, int height, MainPanel mp1, Maze mz1)
      061		{
      062			mp = mp1;
      063			mz = mz1;
      064						// レイアウトマネージャの停止
      065			setLayout(null);
      066						// 背景色の設定
      067			setBackground(Color.white);
      068						// ボタンの配置
      069			Font f = new Font("SansSerif", Font.BOLD, 12);
      070			FontMetrics fm = getFontMetrics(f);
      071	
      072			String str1 = "左";
      073			int w1 = fm.stringWidth(str1) + 40;
      074			int h1 = fm.getHeight() + 10;
      075			left = new JButton(str1);
      076			left.setFont(f);
      077			left.addActionListener(this);   // アクションリスナ
      078			left.setSize(w1, h1);
      079	
      080			String str2 = "右";
      081			int w2 = fm.stringWidth(str2) + 40;
      082			int h2 = fm.getHeight() + 10;
      083			right = new JButton(str2);
      084			right.setFont(f);
      085			right.addActionListener(this);   // アクションリスナ
      086			right.setSize(w2, h2);
      087	
      088			String str3 = "上";
      089			int w3 = fm.stringWidth(str3) + 40;
      090			int h3 = fm.getHeight() + 10;
      091			up = new JButton(str3);
      092			up.setFont(f);
      093			up.addActionListener(this);   // アクションリスナ
      094			up.setSize(w3, h3);
      095	
      096			String str4 = "下";
      097			int w4 = fm.stringWidth(str4) + 40;
      098			int h4 = fm.getHeight() + 10;
      099			down = new JButton(str4);
      100			down.setFont(f);
      101			down.addActionListener(this);   // アクションリスナ
      102			down.setSize(w4, h4);
      103	
      104			int w = w1 + w2 + 5;
      105			left.setLocation(width/2-w/2, height/2-h1/2);
      106			add(left);
      107			right.setLocation(width/2-w/2+w1+5, height/2-h1/2);
      108			add(right);
      109			up.setLocation(width/2-w3/2, height/2-h1/2-h3-5);
      110			add(up);
      111			down.setLocation(width/2-w4/2, height/2+h1/2+5);
      112			add(down);
      113		}
      114				// ボタンがクリックされたときの処理
      115		public void actionPerformed(ActionEvent e)
      116		{
      117			if (e.getSource() == left)   // ゲームクリア
      118				mp.state = 2;
      119		}
      120	}
      				
      017 行目~ 025 行目

        「ゲーム枠の作成」における GamePanel クラスでは,GamePanel クラスに必要な部品を貼り付けていましたが,ここでは,GamePanel クラスに,異なるファイルに記述された Maze クラス( 029 行目~ 045 行目)と Operation クラス( 047 行目~ 120 行目)を貼り付け,Maze クラスには迷路と主人公を表示し,また,Operation クラスには主人公を操作するためのボタンを貼り付けることにします.

      029 行目~ 045 行目

        Maze クラスの定義です.現段階では,白紙を表示しているだけです.

      047 行目~ 120 行目

        Operation クラスの定義です.主人公を上下左右に動かすために 4 つのボタンを貼り付けています.ただし,この段階では,「左」ボタンをクリックしたときにゲームクリアの状態に移動するような設定をしているだけです( 115 行目~ 119 行目).

    5. GameClearPanel クラス

        「ゲーム枠の作成」における GameClearPanel クラスと,ほぼ同じです.違いは,レベルが 2 までしか無い点だけです( 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;
      			// コンストラクタ
      	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 str1 = "ゲーム終了";
      		int w1 = fm.stringWidth(str1) + 40;
      		int h1 = fm.getHeight() + 10;
      		bt1 = new JButton(str1);
      		bt1.setFont(f);
      		bt1.addActionListener(this);   // アクションリスナ
      		bt1.setSize(w1, h1);
      		bt1.setLocation(size.width/2-w1-5, size.height-h1-20);
      		add(bt1);
      
      		String str2;
      		if (mp.level == 2)
      			str2 = "最初から再開";
      		else
      			str2 = "次のレベル";
      		int w2 = fm.stringWidth(str2) + 40;
      		int h2 = fm.getHeight() + 10;
      		bt2 = new JButton(str2);
      		bt2.setFont(f);
      		bt2.addActionListener(this);   // アクションリスナ
      		bt2.setSize(w2, h2);
      		bt2.setLocation(size.width/2+5, size.height-h2-20);
      		add(bt2);
      	}
      			// 描画
      	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 = 3;
      			bt1.setEnabled(false);
      			bt2.setEnabled(false);
      		}
      		else {
      			mp.level++;
      			if (mp.level > 2) {   // 最初からゲーム再開
      				mp.state = 0;
      				mp.level = 1;
      			}
      			else   // レベルアップ
      				mp.state = 1;
      		}
      	}
      }
      				

  2. ステップ2: 主人公と迷路の配置

      迷路と主人公を描きます.game パッケージ内に,以下に示す Hero クラス( Hero.java )を新規作成します.主人公は直径 13 ピクセルの円として,Maze クラス画面のほぼ中央に描きます.なお,主人公の位置は,次の Maze クラスで説明する迷路上の位置を表しています.

    package game;
    
    import java.awt.*;
    
    class Hero
    {
    	Color color = new Color(0, 255, 0);   // 主人公の色
    	int x, y;   // 主人公の位置
    	int w = 13;   // 主人公の直径
    			// コンストラクタ
    	Hero()
    	{
    		x = 19;
    		y = 19;
    	}
    }
    			

      次に,Maze クラス( Maze.java )の修正を行います.迷路は,縦横各々 40 個の 15 ピクセル四方のブロックから構成されているものとします.また,ブロックの状態が 1 である時は壁,0 である時は道を構成するものとします.具体的には,ここで使用する迷路は以下に示すようなものです.
    001	package game;
    002	
    003	import java.awt.*;
    004	import java.awt.image.*;
    005	import javax.swing.*;
    006	import main.*;
    007	
    008	class Maze extends JPanel
    009	{
    010		int x, y;   // 迷路の位置
    011		int width = 40;   // 迷路の幅
    012		int height = 40;   // 迷路の高さ
    013		int blk = 15;   // 迷路におけるブロックのサイズ
    014		Image image;   // 迷路
    015		int map1[][] = 
    016			{{1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    017			{1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    018			{1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    019			{1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    020			{1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    021			{1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    022			{1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    023			{1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    024			{1,1,1,1,1,1,1,1,1,1,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,0,0,0,0,0},
    025			{1,1,1,1,1,1,1,1,1,1,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,0,0,0,0,0},
    026			{1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1},
    027			{1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1},
    028			{1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1},
    029			{1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1},
    030			{1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1},
    031			{1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1},
    032			{1,1,1,1,1,1,1,1,1,1,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,0,0,0,1,1},
    033			{1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,1,1},
    034			{1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,1,1},
    035			{1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,1,1},
    036			{0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,1,1},
    037			{0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,1,1},
    038			{0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,1,1},
    039			{0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,1,1},
    040			{1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,1,1},
    041			{1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,1,1},
    042			{1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,1,1},
    043			{1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,1,1},
    044			{1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1},
    045			{1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1},
    046			{1,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,0,0,0,0,0,0,0,0,0,0,0,0,1,1},
    047			{1,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,0,0,0,0,0,0,0,0,0,0,0,0,1,1},
    048			{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    049			{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    050			{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    051			{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    052			{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    053			{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    054			{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
    055			{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}};
    056		int map2[][] = 
    057			{{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1},
    058			{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1},
    059			{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1},
    060			{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1},
    061			{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1},
    062			{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1},
    063			{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1},
    064			{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1},
    065			{1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1},
    066			{1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1},
    067			{1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1},
    068			{1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1},
    069			{1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1},
    070			{1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1},
    071			{1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1},
    072			{1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1},
    073			{1,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,0,0,0,0,0,0,0,0,0,0,0,1,1,1},
    074			{1,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,0,0,0,0,1,1,1,1,1,1,0,1,1,1},
    075			{1,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,0,0,0,0,1,1,1,1,1,1,0,1,1,1},
    076			{1,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,0,0,0,0,1,1,1,1,1,1,0,1,1,1},
    077			{1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,1,1},
    078			{1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,1,1},
    079			{1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,1,1},
    080			{1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,1,1},
    081			{1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,1,1},
    082			{1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,1,1},
    083			{1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,1,1},
    084			{1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,0,0,1,1},
    085			{1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1},
    086			{1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1,1},
    087			{1,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,0,0,0,0,0,0,0,0,0,0,0,0,1,1},
    088			{1,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,0,0,0,0,0,0,0,0,0,0,0,0,1,1},
    089			{1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1},
    090			{1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1},
    091			{1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1},
    092			{1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1},
    093			{1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1},
    094			{1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1},
    095			{1,1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,1,1},
    096			{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}};
    097		int map[][];   // 迷路の地図(0:道,1:壁)
    098		Hero hr;   // 主人公
    099				// コンストラクタ
    100		public Maze(Dimension size, MainPanel mp)
    101		{
    102						// レイアウトマネージャの停止
    103			setLayout(null);
    104						// 背景色の設定
    105			setBackground(Color.white);
    106						// 迷路の生成
    107			if (mp.level == 1)
    108				map = map1;
    109			else
    110				map = map2;
    111			int w = blk * width, h = blk * height;
    112			int pixels[] = new int [w * h];
    113			for (int i1 = 0; i1 < height; i1++) {
    114				int k1 = i1 * blk	;
    115				for (int i2 = 0; i2 < width; i2++) {
    116					int k2 = i2 * blk;
    117					int cl = (map[i1][i2] > 0) ? 0xff404040 : 0xffffffff;
    118					for (int i3 = k1; i3 < k1+blk; i3++) {
    119						for (int i4 = k2; i4 < k2+blk; i4++) {
    120							int k     = i3 * w + i4;
    121							pixels[k] = cl;
    122						}
    123					}
    124				}
    125			}
    126			MemoryImageSource mis = new MemoryImageSource(w, h, pixels, 0, w);
    127			image = createImage(mis);
    128						// 迷路の初期位置
    129			x = -9;
    130			y = -9;
    131						// 主人公の生成
    132			hr = new Hero();
    133		}
    134				// 描画
    135		public void paintComponent(Graphics g)
    136		{
    137			super.paintComponent(g);   // 親クラスの描画
    138						// 迷路
    139			g.drawImage(image, x*blk, y*blk, this);
    140						// 主人公
    141			g.setColor(hr.color);
    142			int px = (hr.x + x) * blk + (blk - hr.w) / 2;
    143			int py = (hr.y + y) * blk + (blk - hr.w) / 2;
    144			g.fillOval(px, py, hr.w, hr.w);
    145		}
    146	}
    			
    011,012 行目

      縦横のブロックの数を表しています.

    013 行目

      ブロックの大きさを表しています(ピクセル).

    015 行目~ 096 行目

      各ブロックの状態を表す 2 次元配列を定義し,015 行目~ 055 行目ではフィールド map1,056 行目~ 096 行目ではフィールド map2 に対する初期設定を行っています.初期設定における内側の括弧は,各行における要素の値を表しています.この結果,各フィールドとも,40 行 40 列の大きさを持ち,1 ブロックの大きさが 15 ピクセルですので,迷路全体の大きさは( 600 ピクセル × 600 ピクセル)になります.

      一般に,多次元配列は,配列の各要素を配列として定義することによって可能です.例えば,2 行 3 列の配列 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};				
    のような方法で行います.

    097 行目

      レベル1の時は map1 が,また,レベル2の時は map2 が map に代入されます( 107 行目~ 110 行目,実際には,map1 または map2 のアドレス).「 Java と C/C++ 」における配列の章で説明していますように,配列変数 map1,map2,map などは,データを記憶している領域の先頭を指すポインタとみなすことができます.例えば,108 行目において map1 を map に代入していますが,map1 の全てのデータをコピーして map に代入しているわけではなく,map1 と map が同じデータ領域を指すことになるだけです.従って,map を介して各要素の値を変更すれば,map1 の対応する要素の値も変化します(逆も同様).

    111 行目

      迷路画像の幅と高さです.具体的には,縦横共 600 ピクセルになります.

    112 行目

      迷路画像の各ピクセルの色を代入するための配列です.各ピクセルに対する情報として,4 バイト(透明度RGB(赤緑青))使用しているため,配列のサイズは ( w * h * 4 ) バイトになります.画像自体は 2 次元配列ですが,ここではそれを 1 次元配列で表しています.例えば,3 行 4 列の配列 x[3][4] の内容を,12 個の要素からなる 1 次元配列 y[12] で表現したいような場合は,以下に示すような対応関係を使用します.なお,一般に,連続した領域に記憶されている 2 次元配列 x を 1 次元配列 y とみなしたとき,2 次元配列 x の要素 x[i][j] は,1 次元配列 y の要素 y[i * col + j] に対応します( col は,2 次元配列 x の列の数).
    	y[0] : x[0][0]
    	y[1] : x[0][1]
    	y[2] : x[0][2]
    	y[3] : x[0][3]
    	y[4] : x[1][0]
    	y[5] : x[1][1]
    	y[6] : x[1][2]
    	y[7] : x[1][3]
    	y[8] : x[2][0]
    	y[9] : x[2][1]
    	y[10] : x[2][2]
    	y[11] : x[2][3]				

    113 行目~ 125 行目

      上で述べた対応関係を使用して,map の値に従って各ブロックの色(灰色または白)を設定しています.117 行目の ? : は,条件演算子と呼ばれ,一般的には以下のように記述されます.論理式が評価され,その結果が真であれば,式1を評価した結果が変数に代入され,偽であれば,式2を評価した結果が変数に代入されます.
    	変数 = (論理式) ? 式1 : 式2 				
    従って,117 行目は,map[i1][i2] が正であれば,cl の値を 0xff404040 とし,そうでなければ,0xffffffff とするという意味になります.

    126,127 行目

      MemoryImageSource クラスを利用し,Image クラスのオブジェクトを生成しています.

    129,130 行目

      画面の大きさが 300 × 300 ピクセルであるのに対して,迷路画像の大きさは 600 × 600 ピクセルです.そのため,主人公(画面の中央に存在)を中心とした 300 ピクセルの範囲を表示するため,迷路画像の表示位置を負の値(ピクセル値ではなく,ブロック上の位置)で指定しています.実際の迷路画像の位置は,これらの値にブロックの大きさをかけたものになります.負の値は,画面の左上外側に相当します.

    132 行目

      主人公である Hero オブジェクトを生成しています.

    139 行目

      迷路を描画しています.

    141 行目~ 144 行目

      主人公を画面中央に描画しています.

  3. ステップ3: 完成

      ボタンを使用して主人公を動かせるようにします.そのため,Operation クラス( Operation.java )を修正します.

    001	package game;
    002	
    003	import java.awt.*;
    004	import java.awt.event.*;
    005	import javax.swing.*;
    006	import main.*;
    007	
    008	public class Operation extends JPanel implements ActionListener
    009	{
    010		MainPanel mp;
    011		Maze mz;
    012		JButton left, right, up, down;
    013				// コンストラクタ
    014		public Operation(int width, int height, MainPanel mp1, Maze mz1)
    015		{
    016			mp = mp1;
    017			mz = mz1;
    018						// レイアウトマネージャの停止
    019			setLayout(null);
    020						// 背景色の設定
    021			setBackground(Color.white);
    022						// ボタンの配置
    023			Font f = new Font("SansSerif", Font.BOLD, 12);
    024			FontMetrics fm = getFontMetrics(f);
    025	
    026			String str1 = "左";
    027			int w1 = fm.stringWidth(str1) + 40;
    028			int h1 = fm.getHeight() + 10;
    029			left = new JButton(str1);
    030			left.setFont(f);
    031			left.addActionListener(this);   // アクションリスナ
    032			left.setSize(w1, h1);
    033	
    034			String str2 = "右";
    035			int w2 = fm.stringWidth(str2) + 40;
    036			int h2 = fm.getHeight() + 10;
    037			right = new JButton(str2);
    038			right.setFont(f);
    039			right.addActionListener(this);   // アクションリスナ
    040			right.setSize(w2, h2);
    041	
    042			String str3 = "上";
    043			int w3 = fm.stringWidth(str3) + 40;
    044			int h3 = fm.getHeight() + 10;
    045			up = new JButton(str3);
    046			up.setFont(f);
    047			up.addActionListener(this);   // アクションリスナ
    048			up.setSize(w3, h3);
    049	
    050			String str4 = "下";
    051			int w4 = fm.stringWidth(str4) + 40;
    052			int h4 = fm.getHeight() + 10;
    053			down = new JButton(str4);
    054			down.setFont(f);
    055			down.addActionListener(this);   // アクションリスナ
    056			down.setSize(w4, h4);
    057	
    058			int w = w1 + w2 + 5;
    059			left.setLocation(width/2-w/2, height/2-h1/2);
    060			add(left);
    061			right.setLocation(width/2-w/2+w1+5, height/2-h1/2);
    062			add(right);
    063			up.setLocation(width/2-w3/2, height/2-h1/2-h3-5);
    064			add(up);
    065			down.setLocation(width/2-w4/2, height/2+h1/2+5);
    066			add(down);
    067	
    068			check();   // ボタンの有効性のチェック
    069		}
    070				// ボタンがクリックされたときの処理
    071		public void actionPerformed(ActionEvent e)
    072		{
    073						// 移動
    074			if (e.getSource() == left) {
    075				mz.hr.x--;
    076				mz.x++;
    077			}
    078			else if (e.getSource() == right) {
    079				mz.hr.x++;
    080				mz.x--;
    081			}
    082			else if (e.getSource() == up) {
    083				mz.hr.y--;
    084				mz.y++;
    085			}
    086			else {
    087				mz.hr.y++;
    088				mz.y--;
    089			}
    090						// ゲームクリア?
    091			if (mz.hr.x < 0 || mz.hr.x >= mz.width || mz.hr.y < 0 || mz.hr.y >= mz.height)
    092				mp.state = 2;
    093						// ボタンの設定
    094			else {
    095				left.setEnabled(true);
    096				right.setEnabled(true);
    097				up.setEnabled(true);
    098				down.setEnabled(true);
    099				check();   // ボタンの有効性のチェック
    100			}
    101						// 再描画
    102			mz.repaint();
    103		}
    104				// ボタンの有効性のチェック
    105		void check()
    106		{
    107			if (mz.hr.x > 0 && mz.map[mz.hr.y][mz.hr.x-1] > 0)
    108				left.setEnabled(false);
    109			if (mz.hr.x < mz.width-1 && mz.map[mz.hr.y][mz.hr.x+1] > 0)
    110				right.setEnabled(false);
    111			if (mz.hr.y > 0 && mz.map[mz.hr.y-1][mz.hr.x] > 0)
    112				up.setEnabled(false);
    113			if (mz.hr.y < mz.height-1 && mz.map[mz.hr.y+1][mz.hr.x] > 0)
    114				down.setEnabled(false);
    115		}
    116	}
    			
    068 行目

      ボタンの示す方向に壁の存在によって移動できない場合は,対応するボタンを無効にする処理を行うメソッド check を呼んでいます.メソッド check の具体的内容は 105 行目~ 115 行目です.

    074 行目~ 089 行目

      クリックされたボタンに従って主人公を移動させると共に.画面の中央に主人公が描かれるように,反対方向に迷路画面を移動させています.

    091,092 行目

      迷路の外に出たか否かの判定です.外に出た場合は,ゲームクリアになります.

    094 行目~ 100 行目

      迷路内にとどまっている場合の処理です.すべてのボタンを有効にした後,メソッド check を呼んでいます.

    105 行目~ 115 行目

      メソッド check の定義です.上下左右に移動できるかのチェックを行い(壁がある場合は移動できない),移動できなかった場合は,その方向を示すボタンを無効にしています.

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