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

ぷよぷよ

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

      ぷよぷよ風のゲームです.同じ色のピースが指定した数(ここでは,4 個)以上繋がると消去されます.基本的に,「ゲーム枠の作成」で説明した方法とほぼ同じ方法で作成します.ただし,パネルのサイズは変更しています.また,ゲームクリアの画面やゲームのレベルは存在しません.以下,各クラスに対して,「ゲーム枠の作成」の場合との違いについて説明していきます.

    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(210, 550);   // 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 over.*;
      
      public class MainPanel extends JPanel implements Runnable
      {
      	Dimension size;   // パネルの大きさ
      	boolean in_game = true;   // ゲーム実行中はtrue
      	public int state = 0;   // ゲーム状態(0:表紙,1:ゲーム,2:オーバー,3:終了)
      	int old_state = 0;   // 直前のゲーム状態
      	StartPanel sp;
      	GamePanel gp;
      	GameOverPanel gop;
      	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(gop);
      							// 新しいパネルの追加
      				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 {   // GameOverPanel
      						gop = new GameOverPanel(size, this);
      						add(gop);
      					}
      					validate();
      					old_state = state;
      				}
      			}
      		}
      	}
      }
      				

    3. StartPanel クラス

        「ゲーム枠の作成」における StartPanel クラスでは,「 s 」キーを押すとゲームを開始するように設定していましたが,ここではマウスのダブルクリックによって開始するように変更してあります( 36 行目,078 行目~ 083 行目).当然のことながら,ゲームタイトル及び「遊び方」の内容を変更しています( 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-h/2);
      054			g.drawString(str, size.width/2-w/2, size.height/2+h/2);
      055	
      056			f   = new Font("Serif", Font.PLAIN, 20);
      057			fm  = g.getFontMetrics(f);
      058			str = "ゲーム開始";
      059			w   = fm.stringWidth(str);
      060			h   = size.height - 2 * fm.getHeight();
      061			g.setFont(f);
      062			g.drawString(str, size.width/2-w/2, h);
      063			str = "ダブルクリック";
      064			w   = fm.stringWidth(str);
      065			h   = size.height - fm.getHeight();
      066			g.drawString(str, size.width/2-w/2, h);
      067		}
      068				// ボタンがクリックされたときの処理
      069		public void actionPerformed(ActionEvent e)
      070		{
      071			if (e.getSource() == bt) {
      072				Method db = new Method();
      073				db.setVisible(true);
      074				requestFocusInWindow();
      075			}
      076		}
      077				// ダブルクリックされたときの処理
      078		class Mouse extends MouseAdapter {
      079			public void mouseClicked(MouseEvent e) {
      080				if (e.getClickCount() == 2)
      081					mp.state = 1;
      082			}
      083		}
      084	}
      085	
      086	/******************/
      087	/* ゲームの遊び方 */
      088	/******************/
      089	class Method extends JDialog
      090	{
      091				// コンストラクタ
      092		Method()
      093		{
      094			setTitle("ゲームの遊び方");
      095					// ContetPain
      096			Container cp = getContentPane();
      097			cp.setLayout(new FlowLayout(FlowLayout.CENTER));
      098			cp.setBackground(new Color(220, 255, 220));   // 背景色
      099			Font f = new Font("MS 明朝", Font.PLAIN, 20);
      100			setSize(550, 160);
      101					// TextArea の追加
      102			JTextArea ta = new JTextArea(5, 50);
      103			ta.setFont(f);
      104			ta.setEditable(false);
      105			ta.setLineWrap(true);
      106			ta.setText("・ゲーム開始: 画面上でダブルクリック\n");
      107	
      108			ta.append("・「左」,「右」,「回転」,「色交換」ぼタン: 左右への動,90 or -90 度回転,左右 or 上下の色を交換\n");
      109			JScrollPane scroll = new JScrollPane(ta);
      110			cp.add(scroll);
      111					// Window を閉じるため
      112			addWindowListener(new WinEnd());
      113		}
      114					// 終了処理
      115		class WinEnd extends WindowAdapter
      116		{
      117			public void windowClosing(WindowEvent e) {
      118				setVisible(false);
      119			}
      120		}
      121	}
      				

    4. GamePanel クラス

        GamePanel クラスは,実際のゲームを実現するクラスです.従って,「ゲーム枠の作成」における GamePanel クラスとは,ゲームの種類によってその内容は大きく異なります.今後,このクラス及びその関連クラスを完成させていくことになりますが,ここでは,GamePanel クラスに( GamePanel.java : 001 行目~ 028 行目),ピースを表示するパネル( Puyo.java : 030 行目~ 076 行目)とピースを操作するパネル( Operation.java : 078 行目~ 148 行目)を貼り付けています.なお,指定した位置にピースを 1 つだけ表示させています.

      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			int row = 12, col = 5, width = 30;
      013						// レイアウトマネージャの停止
      014			setLayout(null);
      015						// 背景色の設定
      016			setBackground(new Color(220, 255, 220));   // 背景色
      017						// パネルの配置
      018			Puyo py = new Puyo(row, col, width, mp);
      019			py.setSize(width*col, width*row);
      020			py.setLocation(0, 0);
      021			add(py);
      022	
      023			Operation op = new Operation(size.width, 90, py);
      024			op.setSize(size.width, 90);
      025			op.setLocation(0, width*row+10);
      026			add(op);
      027		}
      028	}
      029	
      030	package game;
      031	
      032	import java.awt.*;
      033	import javax.swing.*;
      034	import main.*;
      035	
      036	class Puyo extends JPanel
      037	{
      038		MainPanel mp;
      039		int p_x, p_y;   // 左または上のピースの座標
      040		int row, col;   // ゲーム画面の行と列数
      041		int width;   // ピースの幅と高さ
      042		int rot;   // 0:横,1:縦
      043		int p[][];   // 画面の状態(0:存在しない,1:赤,2:ピンク,3:緑,4:青)
      044		Color color[] = {Color.red, Color.pink, Color.green, Color.blue};   // ピースの色
      045				// コンストラクタ
      046		public Puyo(int row1, int col1, int width1, MainPanel mp1)
      047		{
      048			mp    = mp1;
      049			row   = row1;
      050			col   = col1;
      051			width = width1;
      052			p     = new int [row][col];
      053						// レイアウトマネージャの停止
      054			setLayout(null);
      055						// 背景色の設定
      056			setBackground(Color.white);
      057						// ピースの位置
      058			p_x = 1;
      059			p_y = 3;
      060			p[p_y][p_x]   = 2;
      061			p[p_y][p_x+1] = 3;
      062		}
      063				// 描画
      064		public void paintComponent(Graphics g)
      065		{
      066			super.paintComponent(g);   // 親クラスの描画
      067			for (int i1 = 0; i1 < row; i1++) {
      068				for (int i2 = 0; i2 < col; i2++) {
      069					if (p[i1][i2] > 0) {
      070						g.setColor(color[p[i1][i2]-1]);
      071						g.fillRect(i2*width, i1*width, width, width);
      072					}
      073				}
      074			}
      075		}
      076	}
      077	
      078	package game;
      079	
      080	import java.awt.*;
      081	import java.awt.event.*;
      082	import javax.swing.*;
      083	import main.*;
      084	
      085	public class Operation extends JPanel implements ActionListener
      086	{
      087		Puyo py;
      088		JButton left, right, rot, chg;
      089				// コンストラクタ
      090		public Operation(int width, int height, Puyo py1)
      091		{
      092			py = py1;
      093						// レイアウトマネージャの停止
      094			setLayout(null);
      095						// 背景色の設定
      096			setBackground(Color.white);
      097						// ボタンの配置
      098			Font f = new Font("SansSerif", Font.BOLD, 12);
      099			FontMetrics fm = getFontMetrics(f);
      100	
      101			String str1 = "左";
      102			int w1 = fm.stringWidth(str1) + 40;
      103			int h1 = fm.getHeight() + 10;
      104			left = new JButton(str1);
      105			left.setFont(f);
      106			left.addActionListener(this);   // アクションリスナ
      107			left.setSize(w1, h1);
      108	
      109			String str2 = "右";
      110			int w2 = fm.stringWidth(str2) + 40;
      111			int h2 = fm.getHeight() + 10;
      112			right = new JButton(str2);
      113			right.setFont(f);
      114			right.addActionListener(this);   // アクションリスナ
      115			right.setSize(w2, h2);
      116	
      117			String str3 = "回転";
      118			int w3 = fm.stringWidth(str3) + 40;
      119			int h3 = fm.getHeight() + 10;
      120			rot = new JButton(str3);
      121			rot.setFont(f);
      122			rot.addActionListener(this);   // アクションリスナ
      123			rot.setSize(w3, h3);
      124	
      125			String str4 = "色交換";
      126			int w4 = fm.stringWidth(str4) + 40;
      127			int h4 = fm.getHeight() + 10;
      128			chg = new JButton(str4);
      129			chg.setFont(f);
      130			chg.addActionListener(this);   // アクションリスナ
      131			chg.setSize(w4, h4);
      132	
      133			int w = w1 + w2 + 5;
      134			left.setLocation(width/2-w/2, height/2-h1/2);
      135			add(left);
      136			right.setLocation(width/2-w/2+w1+5, height/2-h1/2);
      137			add(right);
      138			rot.setLocation(width/2-w3/2, height/2-h1/2-h3-5);
      139			add(rot);
      140			chg.setLocation(width/2-w4/2, height/2+h1/2+5);
      141			add(chg);
      142		}
      143				// ボタンがクリックされたときの処理
      144		public void actionPerformed(ActionEvent e)
      145		{
      146			py.mp.state = 2;
      147		}
      148	}
      				
      018 行目~ 021 行目

        「ゲーム枠の作成」における GamePanel クラスでは,GamePanel クラスに必要な部品を直接貼り付けていましたが,このゲームでは,GamePanel クラスに,異なるファイルに記述された2 つのパネル Puyo クラス( 030 行目~ 076 行目)と Operation クラス( 078 行目~ 148 行目)を貼り付け,Puyo クラスにはピースを表示し,また,Operation クラスにはピース操作するためのボタンを貼り付けることにします.

        ここでは,Puyo クラスのオブジェクトを貼り付けています.画面を 12 行 5 列のセルで表現し,各セルに縦横の大きさが 30 ピクセルのピース( 012 行目)を表示できるようにしていますので,パネルの大きさは 019 行目のように設定されます.

      023 行目~ 026 行目

        Operation クラスのオブジェクトを貼り付けています.パネルの高さは 90 ピクセルとしています.

      039 行目

        現在操作対象としているピース(落下中のピース)が,セル上のどの位置にいるかを表します.横並びの場合は左側のピースの位置,縦並びの場合は上のピースの位置を示しています.

      042 行目

        2 つピースを横に並べた状態で落下を開始しますが,後ほど述べる操作によって縦に並べることも可能です.フィールド rot は,現在,横に並んでいるのか( rot = 0 ),または,縦に並んでいるのか( rot = 1 )を示します.

      043 行目

        このプログラムでは,各セルの状態を 12 行 5 列の 2 次元配列で表すことにします.その値の意味する所は,コメントに記述したとおりです.

        一般に,多次元配列は,配列の各要素を配列として定義することによって可能です.例えば,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}};   // 内側の括弧が各行に相当					
      または,
      i	nt x1[] = {1, 2, 3};
      	int x2[] = {4, 5, 6};
      	int x[][] = {x1, x2};					
      のような方法で行います.

      044 行目

        Color クラスのオブジェクトの配列を定義し,使用する色で初期設定しています.

      058 行目~ 061 行目

        ピースの表示を確認するために,適当な位置とピースの色を設定しています.後ほど,この部分は削除することになります.

      067 行目~ 074 行目

        各セルの状態を調べ,ピースが存在する場所には,指定された色で塗りつぶした矩形を描いています.

      098 行目~ 141 行目

        「左」(ピースを左へ移動),「右」(ピースを右へ移動),「回転」(横を縦に,または,縦を横に変更),「色交換」(左右または上下の色を交換)ボタンを貼り付けています.ただし,現段階では,ゲーム状態の変化を確認するために,いずれのボタンをクリックしてもゲームオーバーの状態に移行します( 144 行目~ 147 行目).

    5. 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;
      			// コンストラクタ
      	public GameOverPanel(Dimension size1, MainPanel mp1)
      	{
      		size = size1;
      		mp   = mp1;
      					// レイアウトマネージャの停止
      		setLayout(null);
      					// 背景色の設定
      		setBackground(Color.white);
      					// ボタンの配置
      		Font f = new Font("SansSerif", Font.BOLD, 15);
      		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);
      
      		String 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);
      
      		int w = size.width / 2 - (w1 + w2 + 5) / 2;
      		bt1.setLocation(w, size.height-h1-20);
      		add(bt1);
      		w += (w1 + 5);
      		bt2.setLocation(w, size.height-h1-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";
      		int w = fm.stringWidth(str);
      		int h = fm.getHeight();
      		g.setFont(f);
      		g.drawString(str, size.width/2-w/2, size.height/2-h/2);
      		str = "Over!";
      		w   = fm.stringWidth(str);
      		g.drawString(str, size.width/2-w/2, size.height/2+h/2);
      	}
      			// ボタンがクリックされたときの処理
      	public void actionPerformed(ActionEvent e)
      	{
      		if (e.getSource() == bt1) {   // ゲーム終了
      			mp.state = 3;
      			bt1.setEnabled(false);
      			bt2.setEnabled(false);
      		}
      		else   // 最初からゲーム再開
      			mp.state = 0;
      	}
      }
      				

  2. ステップ2: ピースの落下

      ここでは,ステップ1で表示したピースを落下させ,画面の一番下で停止させる処理を行います.修正するプログラムは,game パッケージ内の Puyo クラスです( Puyo.java ).

    01	package game;
    02	
    03	import java.awt.*;
    04	import javax.swing.*;
    05	import main.*;
    06	
    07	class Puyo extends JPanel implements Runnable
    08	{
    09		MainPanel mp;
    10		int p_x, p_y;   // 左または上のピースの座標
    11		int row, col;   // ゲーム画面の行と列数
    12		int width;   // ピースの幅と高さ
    13		int rot;   // 0:横,1:縦
    14		int p[][];   // 画面の状態(0:存在しない,1:赤,2:ピンク,3:緑,4:青)
    15		Color color[] = {Color.red, Color.pink, Color.green, Color.blue};   // ピースの色
    16		Thread th;
    17		boolean in_game = true;
    18				// コンストラクタ
    19		public Puyo(int row1, int col1, int width1, MainPanel mp1)
    20		{
    21			mp    = mp1;
    22			row   = row1;
    23			col   = col1;
    24			width = width1;
    25			p     = new int [row][col];
    26						// レイアウトマネージャの停止
    27			setLayout(null);
    28						// 背景色の設定
    29			setBackground(Color.white);
    30						// ピースの位置
    31			p_x = 1;
    32			p_y = 3;
    33			p[p_y][p_x]   = 2;
    34			p[p_y][p_x+1] = 3;
    35						// スレッドの生成
    36			th = new Thread(this);
    37			th.start();
    38		}
    39				// スレッドの実行
    40		public void run()
    41		{
    42			while (in_game) {
    43				try {
    44					th.sleep(500);
    45				}
    46				catch (InterruptedException e) {}
    47						// ピースの落下
    48				if (p_y < row-1) {
    49					p[p_y+1][p_x]   = p[p_y][p_x];
    50					p[p_y+1][p_x+1] = p[p_y][p_x+1];
    51					p[p_y][p_x]     = 0;
    52					p[p_y][p_x+1]   = 0;
    53					p_y++;
    54				}
    55						// 再描画
    56				repaint();
    57			}
    58		}
    59				// 描画
    60		public void paintComponent(Graphics g)
    61		{
    62			super.paintComponent(g);   // 親クラスの描画
    63			for (int i1 = 0; i1 < row; i1++) {
    64				for (int i2 = 0; i2 < col; i2++) {
    65					if (p[i1][i2] > 0) {
    66						g.setColor(color[p[i1][i2]-1]);
    67						g.fillRect(i2*width, i1*width, width, width);
    68					}
    69				}
    70			}
    71		}
    72	}
    			
    07 行目

      ピースを移動させるために Runnable インタフェースを継承しています.

    16 行目~ 17 行目,36 行目~ 37 行目

      スレッドを生成し,スタートしています.

    40 行目~ 58 行目

      Thread クラスのメソッド run に対するオーバーライドです.48 行目~ 54 行目において,画面の最下段に到達していない場合は,ピースの存在場所を 1 つ下に移動しています.

  3. ステップ3: ピースの生成と落下

      ここでは,画面の最上部(水平位置はランダム)にピースを生成し,そのピースを落下させ,画面の一番下で停止させる処理を行います.なお,ピースが停止すると,次のピースが生成され,落下が再び始まります.修正するプログラムは,game パッケージ内の Puyo クラスです( Puyo.java ).

    01	package game;
    02	
    03	import java.awt.*;
    04	import javax.swing.*;
    05	import java.util.Random;
    06	import main.*;
    07	
    08	class Puyo extends JPanel implements Runnable
    09	{
    10		MainPanel mp;
    11		int p_x, p_y;   // 左または上のピースの座標
    12		int row, col;   // ゲーム画面の行と列数
    13		int width;   // ピースの幅と高さ
    14		int rot;   // 0:横,1:縦
    15		int p[][];   // 画面の状態(0:存在しない,1:赤,2:ピンク,3:緑,4:青)
    16		Color color[] = {Color.red, Color.pink, Color.green, Color.blue};   // ピースの色
    17		Thread th;
    18		boolean in_game = true;
    19		Random rn;
    20				// コンストラクタ
    21		public Puyo(int row1, int col1, int width1, MainPanel mp1)
    22		{
    23			mp    = mp1;
    24			row   = row1;
    25			col   = col1;
    26			width = width1;
    27			p     = new int [row][col];
    28						// レイアウトマネージャの停止
    29			setLayout(null);
    30						// 背景色の設定
    31			setBackground(Color.white);
    32						// ランダム変数の初期化
    33			rn = new Random();
    34						// ピースの選択
    35			select();
    36						// スレッドの生成
    37			th = new Thread(this);
    38			th.start();
    39		}
    40				// スレッドの実行
    41		public void run()
    42		{
    43			while (in_game) {
    44				try {
    45					th.sleep(500);
    46				}
    47				catch (InterruptedException e) {}
    48						// ピースの落下
    49				if (p_y < row-1) {
    50					p[p_y+1][p_x]   = p[p_y][p_x];
    51					p[p_y+1][p_x+1] = p[p_y][p_x+1];
    52					p[p_y][p_x]     = 0;
    53					p[p_y][p_x+1]   = 0;
    54					p_y++;
    55				}
    56				else
    57					select();   // 次のピース
    58						// 再描画
    59				repaint();
    60			}
    61		}
    62				// 描画
    63		public void paintComponent(Graphics g)
    64		{
    65			super.paintComponent(g);   // 親クラスの描画
    66			for (int i1 = 0; i1 < row; i1++) {
    67				for (int i2 = 0; i2 < col; i2++) {
    68					if (p[i1][i2] > 0) {
    69						g.setColor(color[p[i1][i2]-1]);
    70						g.fillRect(i2*width, i1*width, width, width);
    71					}
    72				}
    73			}
    74		}
    75				// ピースの選択
    76		void select()
    77		{
    78			p_y         = 0;
    79			p_x         = rn.nextInt(col - 1);
    80			p[0][p_x]   = 1 + rn.nextInt(4);
    81			p[0][p_x+1] = 1 + rn.nextInt(4);
    82		}
    83	}
    			
    05 行目,19 行目,33 行目

      ランダム変数を使用するため,Random クラスのオブジェクトを生成しています.

    35 行目

      ピースの初期状態を決めるためのメソッド select( 76 行目~ 82 行目)を呼び,ピースを生成しています.

    56,57 行目

      ピースが最下段に到達した場合は,次のピースを生成しています.

    76 行目~ 82 行目

      新しく生成するピースを決めるためのメソッドです.縦位置は最上段( 78 行目),横位置はランダム( 79 行目)に決めています.また,2 つのピースの色もランダムに決めています( 80,81 行目).なお,生成する 2 つのピースは常に横並びです.

  4. ステップ4: ピースの移動・回転・色の交換

      ここでは,ピースの左右への移動,回転,色の交換の処理を行います.修正するプログラムは,game パッケージ内の Operation クラス( Operation.java ),及び,Puyo クラス( Puyo.java )です.まず,Operation クラスに対して,ボタンをクリックしたときの処理を追加します.

    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		Puyo py;
    011		JButton left, right, rot, chg;
    012				// コンストラクタ
    013		public Operation(int width, int height, Puyo py1)
    014		{
    015			py = py1;
    016						// レイアウトマネージャの停止
    017			setLayout(null);
    018						// 背景色の設定
    019			setBackground(Color.white);
    020						// ボタンの配置
    021			Font f = new Font("SansSerif", Font.BOLD, 12);
    022			FontMetrics fm = getFontMetrics(f);
    023	
    024			String str1 = "左";
    025			int w1 = fm.stringWidth(str1) + 40;
    026			int h1 = fm.getHeight() + 10;
    027			left = new JButton(str1);
    028			left.setFont(f);
    029			left.addActionListener(this);   // アクションリスナ
    030			left.setSize(w1, h1);
    031	
    032			String str2 = "右";
    033			int w2 = fm.stringWidth(str2) + 40;
    034			int h2 = fm.getHeight() + 10;
    035			right = new JButton(str2);
    036			right.setFont(f);
    037			right.addActionListener(this);   // アクションリスナ
    038			right.setSize(w2, h2);
    039	
    040			String str3 = "回転";
    041			int w3 = fm.stringWidth(str3) + 40;
    042			int h3 = fm.getHeight() + 10;
    043			rot = new JButton(str3);
    044			rot.setFont(f);
    045			rot.addActionListener(this);   // アクションリスナ
    046			rot.setSize(w3, h3);
    047	
    048			String str4 = "色交換";
    049			int w4 = fm.stringWidth(str4) + 40;
    050			int h4 = fm.getHeight() + 10;
    051			chg = new JButton(str4);
    052			chg.setFont(f);
    053			chg.addActionListener(this);   // アクションリスナ
    054			chg.setSize(w4, h4);
    055	
    056			int w = w1 + w2 + 5;
    057			left.setLocation(width/2-w/2, height/2-h1/2);
    058			add(left);
    059			right.setLocation(width/2-w/2+w1+5, height/2-h1/2);
    060			add(right);
    061			rot.setLocation(width/2-w3/2, height/2-h1/2-h3-5);
    062			add(rot);
    063			chg.setLocation(width/2-w4/2, height/2+h1/2+5);
    064			add(chg);
    065		}
    066				// ボタンがクリックされたときの処理
    067		public void actionPerformed(ActionEvent e)
    068		{
    069			int k;
    070			if (py.ok) {
    071						// 左移動
    072				if (e.getSource() == left) {
    073					if (py.p_x > 0) {
    074						if (py.rot == 0 && py.p[py.p_y][py.p_x-1] == 0) {
    075							py.p[py.p_y][py.p_x-1] = py.p[py.p_y][py.p_x];
    076							py.p[py.p_y][py.p_x]   = py.p[py.p_y][py.p_x+1];
    077							py.p[py.p_y][py.p_x+1] = 0;
    078							py.p_x--;
    079							py.repaint();
    080						}
    081						else if (py.p[py.p_y][py.p_x-1] == 0 && py.p[py.p_y+1][py.p_x-1] == 0) {
    082							py.p[py.p_y][py.p_x-1]   = py.p[py.p_y][py.p_x];
    083							py.p[py.p_y+1][py.p_x-1] = py.p[py.p_y+1][py.p_x];
    084							py.p[py.p_y][py.p_x]     = 0;
    085							py.p[py.p_y+1][py.p_x]   = 0;
    086							py.p_x--;
    087							py.repaint();
    088						}
    089					}
    090				}
    091						// 右移動
    092				else if (e.getSource() == right) {
    093					if (py.rot == 0) {
    094						if (py.p_x < py.col-2 && py.p[py.p_y][py.p_x+2] == 0) {
    095							py.p[py.p_y][py.p_x+2] = py.p[py.p_y][py.p_x+1];
    096							py.p[py.p_y][py.p_x+1] = py.p[py.p_y][py.p_x];
    097							py.p[py.p_y][py.p_x]   = 0;
    098							py.p_x++;
    099							py.repaint();
    100						}
    101					}
    102					else {
    103						if (py.p_x < py.col-1 && py.p[py.p_y][py.p_x+1] == 0 && py.p[py.p_y+1][py.p_x+1] == 0) {
    104							py.p[py.p_y][py.p_x+1]   = py.p[py.p_y][py.p_x];
    105							py.p[py.p_y+1][py.p_x+1] = py.p[py.p_y+1][py.p_x];
    106							py.p[py.p_y][py.p_x]     = 0;
    107							py.p[py.p_y+1][py.p_x]   = 0;
    108							py.p_x++;
    109							py.repaint();
    110						}
    111					}
    112				}
    113						// 上下または左右入れ替え
    114				else if (e.getSource() == chg) {
    115					if (py.rot == 0) {
    116						k                      = py.p[py.p_y][py.p_x];
    117						py.p[py.p_y][py.p_x]   = py.p[py.p_y][py.p_x+1];
    118						py.p[py.p_y][py.p_x+1] = k;
    119					}
    120					else {
    121						k                      = py.p[py.p_y][py.p_x];
    122						py.p[py.p_y][py.p_x]   = py.p[py.p_y+1][py.p_x];
    123						py.p[py.p_y+1][py.p_x] = k;
    124					}
    125					py.repaint();
    126				}
    127						// 90度または-90度回転
    128				else if (e.getSource() == rot) {
    129					if (py.rot == 0 && py.p[py.p_y+1][py.p_x] == 0) {
    130						if (py.p_y < py.row-1) {
    131							py.p[py.p_y+1][py.p_x] = py.p[py.p_y][py.p_x+1];
    132							py.p[py.p_y][py.p_x+1] = 0;
    133							py.rot                 = 1;
    134							py.repaint();
    135						}
    136					}
    137					else {
    138						if (py.p_x < py.col-1 && py.p[py.p_y][py.p_x+1] == 0) {
    139							py.p[py.p_y][py.p_x+1] = py.p[py.p_y+1][py.p_x];
    140							py.p[py.p_y+1][py.p_x] = 0;
    141							py.rot                 = 0;
    142							py.repaint();
    143						}
    144					}
    145				}
    146			}
    147		}
    148	}
    			
    070 行目

      ピースが最下段に到着した場合や他のピースの上に積み上がった場合(後ほどの処理)には,移動,回転,色の交換を禁止するため,Puyo クラスにフィールド ok を定義しました( Puyo クラスの 17 行目).ok が true の場合だけ,移動,回転,色の交換が可能です.

    072 行目~ 112 行目

      左右への移動処理を行っています.その際,画面の外に出ないか,また,移動先に他のピースが存在しないかのチェックを行っています.

    114 行目~ 126 行目

      現在のピースの並び方を調べ( 115 行目),左右または上下の色を交換しています.

    128 行目~ 145 行目

      現在のピースの並び方,ピースの位置,回転したときに障害となるピースの存在を調べ( 129,130,138 行目),90 度,または,-90 度の回転を行っています.その際,Puyo クラスの変数 rot の再設定も行っています.

      次は,Puyo クラスの修正です.Puyo クラスのソースコードは以下に示すとおりです.

    001	package game;
    002	
    003	import java.awt.*;
    004	import javax.swing.*;
    005	import java.util.Random;
    006	import main.*;
    007	
    008	class Puyo extends JPanel implements Runnable
    009	{
    010		MainPanel mp;
    011		int p_x, p_y;   // 左または上のピースの座標
    012		int row, col;   // ゲーム画面の行と列数
    013		int width;   // ピースの幅と高さ
    014		int rot;   // 0:横,1:縦
    015		int p[][];   // 画面の状態(0:存在しない,1:赤,2:ピンク,3:緑,4:青)
    016		Color color[] = {Color.red, Color.pink, Color.green, Color.blue};   // ピースの色
    017		boolean ok = true;   // 回転,色の交換が可能か?
    018		Thread th;
    019		boolean in_game = true;
    020		Random rn;
    021				// コンストラクタ
    022		public Puyo(int row1, int col1, int width1, MainPanel mp1)
    023		{
    024			mp    = mp1;
    025			row   = row1;
    026			col   = col1;
    027			width = width1;
    028			p     = new int [row][col];
    029						// レイアウトマネージャの停止
    030			setLayout(null);
    031						// 背景色の設定
    032			setBackground(Color.white);
    033						// ランダム変数の初期化
    034			rn = new Random();
    035						// ピースの選択
    036			select();
    037						// スレッドの生成
    038			th = new Thread(this);
    039			th.start();
    040		}
    041				// スレッドの実行
    042		public void run()
    043		{
    044			while (in_game) {
    045				try {
    046					th.sleep(500);
    047				}
    048				catch (InterruptedException e) {}
    049						// ピースの落下
    050				int ct = 0;   // 落下できたか否か
    051								// 横
    052				if (rot == 0) {
    053					if (p_y < row-1) {
    054						ct              = 1;
    055						p[p_y+1][p_x]   = p[p_y][p_x];
    056						p[p_y+1][p_x+1] = p[p_y][p_x+1];
    057						p[p_y][p_x]     = 0;
    058						p[p_y][p_x+1]   = 0;
    059						p_y++;
    060					}
    061					else
    062						ok = false;
    063				}
    064								// 縦
    065				else {
    066					if (p_y < row-2) {
    067						ct = 1;
    068						p[p_y+2][p_x] = p[p_y+1][p_x];
    069						p[p_y+1][p_x] = p[p_y][p_x];
    070						p[p_y][p_x]   = 0;
    071						p_y++;
    072					}
    073					else
    074						ok = false;
    075				}
    076						// 次のピース
    077				if (ct == 0)
    078					select();
    079						// 再描画
    080				repaint();
    081			}
    082		}
    083				// 描画
    084		public void paintComponent(Graphics g)
    085		{
    086			super.paintComponent(g);   // 親クラスの描画
    087			for (int i1 = 0; i1 < row; i1++) {
    088				for (int i2 = 0; i2 < col; i2++) {
    089					if (p[i1][i2] > 0) {
    090						g.setColor(color[p[i1][i2]-1]);
    091						g.fillRect(i2*width, i1*width, width, width);
    092					}
    093				}
    094			}
    095		}
    096				// ピースの選択
    097		void select()
    098		{
    099			ok          = true;
    100			rot         = 0;
    101			p_y         = 0;
    102			p_x         = rn.nextInt(col - 1);
    103			p[0][p_x]   = 1 + rn.nextInt(4);
    104			p[0][p_x+1] = 1 + rn.nextInt(4);
    105		}
    106	}
    			
    017 行目

      ok が true の場合だけ,移動,回転,色の交換が可能です.

    052 行目~ 075 行目

      横並びと縦並びの場合に分けて,ピースの落下処理を行っています.ct は落下できたか否かを表す変数(その値が 1 の時は落下できたことを示す)であり,落下できなかった場合(最下段に達した場合)は,次のピースを生成しています( 078 行目).また,落下できなかった場合は,フィールド ok を false に設定し,以後,移動,回転,色の交換を不可能にしています( 062 行目,074 行目).

    099,100 行目

      新しく生成するピースに対して,移動,回転,色の交換を可能にし,ピースを横並びとしています.

  5. ステップ5: ピースの積み上げ

      ここでは,ピースを他のピースの上に積み上げていきます.縦並びの場合はそれほど問題ではありませんが,横並びの場合は,例えば,左側のピースが他のピースの上に乗ったが,右側のピースの下には何もない場合,ピースを分離し,右側のピースだけを落下させる処理が必要となります.なお,ゲームオーバーの処理も追加してあります.修正するプログラムは,game パッケージ内の Puyo クラスです( Puyo.java ).

    001	package game;
    002	
    003	import java.awt.*;
    004	import javax.swing.*;
    005	import java.util.Random;
    006	import main.*;
    007	
    008	class Puyo extends JPanel implements Runnable
    009	{
    010		MainPanel mp;
    011		int p_x, p_y;   // 左または上のピースの座標
    012		int row, col;   // ゲーム画面の行と列数
    013		int width;   // ピースの幅と高さ
    014		int rot;   // 0:横,1:縦
    015		int p[][];   // 画面の状態(0:存在しない,1:赤,2:ピンク,3:緑,4:青)
    016		Color color[] = {Color.red, Color.pink, Color.green, Color.blue};   // ピースの色
    017		boolean ok = true;   // 回転,色の交換が可能か?
    018		Thread th;
    019		boolean in_game = true;
    020		Random rn;
    021				// コンストラクタ
    022		public Puyo(int row1, int col1, int width1, MainPanel mp1)
    023		{
    024			mp    = mp1;
    025			row   = row1;
    026			col   = col1;
    027			width = width1;
    028			p     = new int [row][col];
    029						// レイアウトマネージャの停止
    030			setLayout(null);
    031						// 背景色の設定
    032			setBackground(Color.white);
    033						// ランダム変数の初期化
    034			rn = new Random();
    035						// ピースの選択
    036			select();
    037						// スレッドの生成
    038			th = new Thread(this);
    039			th.start();
    040		}
    041				// スレッドの実行
    042		public void run()
    043		{
    044			while (in_game) {
    045				try {
    046					th.sleep(500);
    047				}
    048				catch (InterruptedException e) {}
    049						// ピースの落下
    050				int ct = 0;   // 落下できたか否か
    051								// 横
    052				if (rot == 0) {
    053					if (p_y < row-1) {
    054						if (ok) {
    055							if (p[p_y+1][p_x] == 0 && p[p_y+1][p_x+1] == 0) {
    056								ct              = 1;
    057								p[p_y+1][p_x]   = p[p_y][p_x];
    058								p[p_y+1][p_x+1] = p[p_y][p_x+1];
    059								p[p_y][p_x]     = 0;
    060								p[p_y][p_x+1]   = 0;
    061								p_y++;
    062							}
    063							else {
    064								ok = false;
    065								if (p[p_y+1][p_x] == 0) {
    066									ct            = 1;
    067									p[p_y+1][p_x] = p[p_y][p_x];
    068									p[p_y][p_x]   = 0;
    069									p_y++;
    070								}
    071								else if (p[p_y+1][p_x+1] == 0) {
    072									ct              = 1;
    073									p[p_y+1][p_x+1] = p[p_y][p_x+1];
    074									p[p_y][p_x+1]   = 0;
    075									p_x++;
    076									p_y++;
    077								}
    078							}
    079						}
    080						else {
    081							if (p[p_y+1][p_x] == 0) {
    082								ct            = 1;
    083								p[p_y+1][p_x] = p[p_y][p_x];
    084								p[p_y][p_x]   = 0;
    085								p_y++;
    086							}
    087						}
    088					}
    089					else
    090						ok = false;
    091				}
    092								// 縦
    093				else {
    094					if (p_y < row-2 && p[p_y+2][p_x] == 0) {
    095						ct = 1;
    096						p[p_y+2][p_x] = p[p_y+1][p_x];
    097						p[p_y+1][p_x] = p[p_y][p_x];
    098						p[p_y][p_x]   = 0;
    099						p_y++;
    100					}
    101					else
    102						ok = false;
    103				}
    104						// 次のピース
    105				if (ct == 0)
    106					select();
    107						// 再描画
    108				repaint();
    109			}
    110		}
    111				// 描画
    112		public void paintComponent(Graphics g)
    113		{
    114			super.paintComponent(g);   // 親クラスの描画
    115			for (int i1 = 0; i1 < row; i1++) {
    116				for (int i2 = 0; i2 < col; i2++) {
    117					if (p[i1][i2] > 0) {
    118						g.setColor(color[p[i1][i2]-1]);
    119						g.fillRect(i2*width, i1*width, width, width);
    120					}
    121				}
    122			}
    123		}
    124				// ピースの選択
    125		void select()
    126		{
    127			ok  = true;
    128			rot = 0;
    129			p_y = 0;
    130			p_x = rn.nextInt(col - 1);
    131			if (p[0][p_x] == 0 && p[0][p_x+1] == 0) {
    132				p[0][p_x]   = 1 + rn.nextInt(4);
    133				p[0][p_x+1] = 1 + rn.nextInt(4);
    134			}
    135			else {   // ゲームオーバー
    136				in_game  = false;
    137				mp.state = 2;
    138			}
    139		}
    140	}
    			
    054 行目

      ピースが横に並んでいるとき,1 つのピースだけが他のピースの上に乗った場合も,ok を false にして,移動,回転,色の交換を不可能にしています.従って,ok が true であることは,2 つのピースが横に並んでいる場合を表します.

    055 行目~ 062 行目

      横に並んだ 2 つのピースが,そろって落下できる場合の処理です.

    064 行目~ 077 行目

      左右いずれかのピースだけが他のピースの上に乗った場合の処理です.ok を false に設定し,乗らなかったピースを分離し,そのピースだけを落下させていきます.

    080 行目~ 087 行目

      分離された 1 つのピースに対する落下処理です.

    094 行目~ 102 行目

      縦に並んだピースに対する落下処理です.

    131 行目~ 138 行目

      新しく生成したピースの一部が,既に存在するピースと重なった場合,ゲームオーバーになります(ピースが,画面上部まで積み上がった場合に発生する).

  6. ステップ6: 完成

      指定された数(ここでは,4 )以上,同じ色のピースが繋がった場合,それらのピースを削除します.この処理を加え,ゲームは完成です.修正するプログラムは,game パッケージ内の Puyo クラスです( Puyo.java ).

    001	package game;
    002	
    003	import java.awt.*;
    004	import javax.swing.*;
    005	import java.util.Random;
    006	import main.*;
    007	
    008	class Puyo extends JPanel implements Runnable
    009	{
    010		MainPanel mp;
    011		int p_x, p_y;   // 左または上のピースの座標
    012		int row, col;   // ゲーム画面の行と列数
    013		int width;   // ピースの幅と高さ
    014		int rot;   // 0:横,1:縦
    015		int p[][];   // 画面の状態(0:存在しない,1:赤,2:ピンク,3:緑,4:青)
    016		Color color[] = {Color.red, Color.pink, Color.green, Color.blue};   // ピースの色
    017		int pp[][];   // ワーク
    018		int d_no = 4;   // 同じ色のピースがd_no以上連続すると消去
    019		boolean ok = true;   // 回転,色の交換が可能か?
    020		Thread th;
    021		boolean in_game = true;
    022		Random rn;
    023				// コンストラクタ
    024		public Puyo(int row1, int col1, int width1, MainPanel mp1)
    025		{
    026			mp    = mp1;
    027			row   = row1;
    028			col   = col1;
    029			width = width1;
    030			p     = new int [row][col];
    031			pp    = new int [row][col];
    032						// レイアウトマネージャの停止
    033			setLayout(null);
    034						// 背景色の設定
    035			setBackground(Color.white);
    036						// ランダム変数の初期化
    037			rn = new Random();
    038						// ピースの選択
    039			select();
    040						// スレッドの生成
    041			th = new Thread(this);
    042			th.start();
    043		}
    044				// スレッドの実行
    045		public void run()
    046		{
    047			while (in_game) {
    048				try {
    049					th.sleep(500);
    050				}
    051				catch (InterruptedException e) {}
    052						// ピースの落下
    053				int ct = 0;   // 落下できたか否か
    054								// 横
    055				if (rot == 0) {
    056					if (p_y < row-1) {
    057						if (ok) {
    058							if (p[p_y+1][p_x] == 0 && p[p_y+1][p_x+1] == 0) {
    059								ct              = 1;
    060								p[p_y+1][p_x]   = p[p_y][p_x];
    061								p[p_y+1][p_x+1] = p[p_y][p_x+1];
    062								p[p_y][p_x]     = 0;
    063								p[p_y][p_x+1]   = 0;
    064								p_y++;
    065							}
    066							else {
    067								ok = false;
    068								if (p[p_y+1][p_x] == 0) {
    069									ct            = 1;
    070									p[p_y+1][p_x] = p[p_y][p_x];
    071									p[p_y][p_x]   = 0;
    072									p_y++;
    073								}
    074								else if (p[p_y+1][p_x+1] == 0) {
    075									ct              = 1;
    076									p[p_y+1][p_x+1] = p[p_y][p_x+1];
    077									p[p_y][p_x+1]   = 0;
    078									p_x++;
    079									p_y++;
    080								}
    081							}
    082						}
    083						else {
    084							if (p[p_y+1][p_x] == 0) {
    085								ct            = 1;
    086								p[p_y+1][p_x] = p[p_y][p_x];
    087								p[p_y][p_x]   = 0;
    088								p_y++;
    089							}
    090						}
    091					}
    092					else
    093						ok = false;
    094				}
    095								// 縦
    096				else {
    097					if (p_y < row-2 && p[p_y+2][p_x] == 0) {
    098						ct = 1;
    099						p[p_y+2][p_x] = p[p_y+1][p_x];
    100						p[p_y+1][p_x] = p[p_y][p_x];
    101						p[p_y][p_x]   = 0;
    102						p_y++;
    103					}
    104					else
    105						ok = false;
    106				}
    107						// 消去と次のピース
    108				if (ct == 0) {   // 落下できなかった場合
    109					ct = d_no;
    110					while (ct >= d_no) {
    111						ct = 0;
    112						for (int i1 = row-1; i1 >= 0 && ct < d_no; i1--) {
    113							for (int i2 = 0; i2 < col && ct < d_no; i2++) {
    114								if (p[i1][i2] > 0) {
    115									for (int i3 = 0; i3 < row; i3++) {
    116										for (int i4 = 0; i4 < col; i4++)
    117											pp[i3][i4] = 0;
    118									}
    119									pp[i1][i2] = 1;
    120									ct         = search(i1, i2, 1);
    121								}
    122							}
    123						}
    124						if (ct >= d_no)
    125							del();
    126					}
    127					select();
    128				}
    129						// 再描画
    130				repaint();
    131			}
    132		}
    133				// 描画
    134		public void paintComponent(Graphics g)
    135		{
    136			super.paintComponent(g);   // 親クラスの描画
    137			for (int i1 = 0; i1 < row; i1++) {
    138				for (int i2 = 0; i2 < col; i2++) {
    139					if (p[i1][i2] > 0) {
    140						g.setColor(color[p[i1][i2]-1]);
    141						g.fillRect(i2*width, i1*width, width, width);
    142					}
    143				}
    144			}
    145		}
    146				// ピースの選択
    147		void select()
    148		{
    149			ok  = true;
    150			rot = 0;
    151			p_y = 0;
    152			p_x = rn.nextInt(col - 1);
    153			if (p[0][p_x] == 0 && p[0][p_x+1] == 0) {
    154				p[0][p_x]   = 1 + rn.nextInt(4);
    155				p[0][p_x+1] = 1 + rn.nextInt(4);
    156			}
    157			else {   // ゲームオーバー
    158				in_game  = false;
    159				mp.state = 2;
    160			}
    161		}
    162				// 同じ色のピースを探す
    163				//      k1,k2 : 対象としているピース
    164				//      c1 : 同じ色のピースの数
    165				//      return : 同じ色のピースの数
    166		int search(int k1, int k2, int c1)
    167		{
    168			int ct = c1;
    169	
    170			if (k1 > 0 && p[k1-1][k2] == p[k1][k2] && pp[k1-1][k2] == 0) {
    171				pp[k1-1][k2] = 1;
    172				ct           = search(k1-1, k2, ct+1);
    173			}
    174			if (k1 < row-1 && p[k1+1][k2] == p[k1][k2] && pp[k1+1][k2] == 0) {
    175				pp[k1+1][k2] = 1;
    176				ct           = search(k1+1, k2, ct+1);
    177			}
    178			if (k2 > 0 && p[k1][k2-1] == p[k1][k2] && pp[k1][k2-1] == 0) {
    179				pp[k1][k2-1] = 1;
    180				ct           = search(k1, k2-1, ct+1);
    181			}
    182			if (k2 < col-1 && p[k1][k2+1] == p[k1][k2] && pp[k1][k2+1] == 0) {
    183				pp[k1][k2+1] = 1;
    184				ct           = search(k1, k2+1, ct+1);
    185			}
    186	
    187			return ct;
    188		}
    189				// 同じ色のピースを削除
    190		void del()
    191		{
    192			int i1, i2, i3, k1, k2, k3;
    193						// 削除
    194			for (i1 = 0; i1 < row; i1++) {
    195				for (i2 = 0; i2 < col; i2++) {
    196					if (pp[i1][i2]  > 0)
    197						p[i1][i2] = 0;
    198				}
    199			}
    200						// 詰める
    201			for (i1 = 0; i1 < col; i1++) {
    202				k1 = 1;
    203				for (i2 = row-1; i2 > 0 && k1 >= 0; i2--) {
    204					if (p[i2][i1] == 0) {
    205						k1 = -1;
    206						for (i3 = i2-1; i3 >= 0 && k1 < 0; i3--) {
    207							if (p[i3][i1] > 0)
    208								k1 = i3;
    209						}
    210						if (k1 >= 0) {
    211							k2 = i2;
    212							k3 = k2 - k1;
    213							while (k1 >= 0) {
    214								p[k2][i1] = p[k1][i1];
    215								k1--;
    216								k2--;
    217							}
    218							k1++;
    219							for (i3 = 0; i3 < k3; i3++)
    220								p[i3][i1] = 0;
    221						}
    222					}
    223				}
    224			}
    225		}
    226	}
    			
    017 行目~ 018 行目,031 行目

      Puyo オブジェクトに対して,同じ色のピースが何個以上繋がったら削除するかを示すフィールド d_no と作業域として使用する配列 pp を定義しています.

    109 行目~ 126 行目

      d_no 個以上繋がったピースを削除するための処理です.この処理は,ピースの移動が停止した後( 108 行目)に行われ,削除処理の終了後,次のピースを生成します( 127 行目).実際に削除する部分は 111 行目~ 125 行目ですが,この処理だけですと,削除された後新たに d_no 個以上繋がったピースが現れた場合の処理を行えません.この点を考慮して,この部分を 110 行目の while 文で繰り返しています.

      あるセルにピースが存在した場合は( 114 行目),セルの状態を表すフィールド(配列) p と同じサイズの配列 pp のすべての値を 0 に設定した後,ピースが存在する場所だけを 1 に設定し,そのピースと繋がっている同じ色のピースの数をメソッド search を利用して数えます( 120 行目).この結果,繋がっているピースの数( ct )が戻されると共に,フィールド pp の繋がっているピースの位置に対応する値が 1 にセットされます.繋がったピースの数 ct が d_no 以上であった場合は,フィールド p,及び,pp の情報を使用して,それらのピースを削除します( 125 行目).

    166 行目~ 188 行目

      繋がった状態の同じ色のピースを数えるためのメソッドです.指定されたピースの上下左右のピースの色が,指定されたピースと同じ場合は,フィールド pp のその場所に対応する要素の値を 1 に設定し,再帰的に search (再帰呼び出し)を呼びます.

    190 行目~ 225 行目

      同じ色で繋がったピースを削除するためのメソッドです.まず,194 行目~ 199 行目において繋がったピースを削除した後,ピースの縦の並びの間に空いたセルがある場合は,上のピースを落下させて空いたセルがないようにします.

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