静岡理工科大学 菅沼ホーム Java目次 索引

アニメーションとゲーム

      1. アニメーション
        1. 円の描画
        2. ランニング(外部複数画像の利用)
        3. ボールの運動(描画とイベント処理)
        4. ボールの運動(外部単一画像の利用とイベント処理)
        5. 花火(ピクセル操作)
        6. Web ページ(外部複数画像の利用とイベント処理,他ページへのリンク)
      2. ゲーム
        1. シューティングゲーム
        2. ぷよぷよ(テトリス)

  1. アニメーション

    1. 円の描画

        この例(「AWT の利用」,「Swing の利用」)では,半径の異なる円を順次描画する方法によってアニメーションを作成してみます.以下に示すのは Java のソース( Test.java )です.

      AWT を利用した場合

      01	import java.awt.*;
      02	import java.awt.event.*;
      03	import java.applet.*;
      04	
      05	/********************/
      06	/* クラスTestの定義 */
      07	/********************/
      08	public class Test extends Applet implements Runnable, ActionListener
      09	{
      10		boolean state;
      11		Button b_start, b_stop;
      12		D_Panel dp;
      13		Thread th;
      14	
      15		public void init() {
      16						// レイアウト,背景色,フォントなど
      17			setLayout(new BorderLayout(5, 10));
      18			setBackground(new Color(225, 255, 225));
      19			setFont(new Font("TimesRoman", Font.BOLD, 20));
      20						// 上のパネル
      21			Panel pn1 = new Panel();
      22			pn1.setLayout(new FlowLayout(FlowLayout.CENTER));
      23			add(pn1, BorderLayout.NORTH);
      24			b_start = new Button("開始");
      25			b_start.addActionListener(this);
      26			pn1.add(b_start);
      27			b_stop = new Button("停止");
      28			b_stop.addActionListener(this);
      29			pn1.add(b_stop);
      30						// 中央のパネル
      31			dp = new D_Panel();
      32			add(dp, BorderLayout.CENTER);
      33						// スレッドの生成
      34			th = new Thread(this);
      35		}
      36	
      37		public Insets getInsets()
      38		{
      39			return new Insets(10, 10, 10, 10);
      40		}
      41	
      42		public void stop()
      43		{
      44			state = false;
      45		}
      46	
      47		public void run()
      48		{
      49			while (state) {
      50				dp.count++;
      51				if (dp.count > 10)
      52					dp.count = 0;
      53				dp.repaint();   // 再描画
      54				try {
      55					th.sleep(100);   // 100 ms 毎の描画
      56				}
      57				catch (InterruptedException e) {}
      58			}
      59		}
      60	
      61		public void actionPerformed(ActionEvent e)
      62		{
      63			if (e.getSource() == b_start) {   // 開始
      64				state = true;
      65				th.start();
      66			}
      67			else {   // 停止
      68				state    = false;
      69				th       = new Thread(this);
      70				dp.count = 0;
      71				dp.repaint();
      72			}
      73		}
      74	}
      75	
      76	class D_Panel extends Panel
      77	{
      78		int count;
      79		D_Panel()
      80		{
      81			setBackground(Color.white);
      82			count = 0;
      83		}
      84		public void paint (Graphics g)   // 描画
      85		{
      86			int r = 10;
      87			for (int i1 = 0; i1 < count; i1++) {
      88				g.drawOval(0, 0, 2*r, 2*r);
      89				r = (int)(1.5 * r);
      90			}
      91		}
      92	}
      				
      08 行目

        Runnable インタフェースを継承している.Runnable インタフェースは,マルチスレッドを実現するために使用されるインタフェースである.アニメーションを作成する場合,必ずしも連続的に描画する必要はなく,ある一定時間間隔で描画を実行すれば,我々はそれを連続的に動いているように認識できる.そこで,現在動作しているスレッドとは別に,アニメーションの描画を行うスレッドを生成し,そのスレッドをある一定時間間隔で動作させるといった方法がよく使用される.その際に必要なのが,Runnable インタフェースである.

      21 行目~ 29 行目

        「開始」及び「停止」ボタンを追加している.

      31 行目~ 32 行目

        アニメーションを描画するためのパネルとして,D_Panel クラス( 76 行目~ 92 行目)のオブジェクトを追加している.

      34 行目

        Thread クラスのオブジェクトを生成している.

      42 行目~ 45 行目

        Applet クラスのメソッド stop に対するオーバーライドであり,他のページへ移動するとき自動的に呼び出される.この場合は,スレッドを停止している.

      47 行目~ 59 行目

        Thread クラスのメソッド run に対するオーバーライドである.Runnable インタフェースを継承してスレッドを利用する場合,必ずこのメソッドを定義してやる必要がある.
      • 49 行目: state が true である間,50 行目~ 57 行目の処理が実行される.
      • 50 行目~ 52 行目: D_Panel クラスのプロパティである count を 1 だけ増やし,その値が 10 を越えたら 0 に戻している.
      • 53 行目: D_Panel クラスのオブジェクトを再描画している.
      • 54 行目~ 57 行目: この設定により,このスレッドは 100 ms 毎に実行されることになる.

      63 行目~ 66 行目

        「開始」ボタンがクリックされたときの処理である.state を true に設定し,スレッドを開始している.

      67 行目~ 72 行目

        「停止」ボタンがクリックされたときの処理である.state を false に設定し(スレットの停止),次の開始のために新しいスレッドを生成し,カウンタ( D_Panel クラスのプロパティ count )の初期設定を行い,最後に,再描画している.

      76 行目~ 92 行目

        D_Panel クラスの定義である.
      • 79 行目~ 83 行目: コンストラクタであり,背景の設定とカウンタの初期設定を行っている.
      • 84 行目~ 91 行目: メソッド paint のオーバーライドであり,count 個だけ,半径の異なる円を描画している.

      Swing を利用した場合

      001	import java.awt.*;
      002	import java.awt.event.*;
      003	import javax.swing.*;
      004	
      005	/********************/
      006	/* クラスTestの定義 */
      007	/********************/
      008	public class Test extends JApplet implements Runnable, ActionListener
      009	{
      010		boolean state;
      011		JButton b_start, b_stop;
      012		Test_Panel dp;
      013		Thread th;
      014	
      015		public void init() {
      016			Container cP = getContentPane();
      017						// レイアウト,背景色,フォント
      018			JPanel pn = new JPanel();
      019			pn.setLayout(new BorderLayout(5, 10));
      020			pn.setBackground(new Color(225, 255, 225));
      021			cP.add(pn);
      022			Font f = new Font("TimesRoman", Font.BOLD, 20);
      023						// 上のパネル(ボタンの設定)
      024			JPanel pn1 = new JPanel();
      025			pn1.setLayout(new FlowLayout(FlowLayout.CENTER));
      026			pn1.setBackground(new Color(225, 255, 225));
      027			pn.add(pn1, BorderLayout.NORTH);
      028			b_start = new JButton("開始");
      029			b_start.addActionListener(this);
      030			b_start.setFont(f);
      031			pn1.add(b_start);
      032			b_stop = new JButton("停止");
      033			b_stop.addActionListener(this);
      034			b_stop.setFont(f);
      035			pn1.add(b_stop);
      036						// 中央のパネル(描画領域)
      037			JPanel pn2 = new JPanel();
      038			pn2.setBackground(new Color(225, 255, 225));
      039			pn2.setLayout(null);
      040			pn.add(pn2, BorderLayout.CENTER);
      041			dp = new Test_Panel();
      042			dp.setSize(380, 175);
      043			dp.setLocation(10, 10);
      044			pn2.add(dp);
      045						// スレッドの生成
      046			th = new Thread(this);
      047		}
      048	
      049		public void stop()   // 他ページへ移動の際,一時的にスレッドを停止
      050		{
      051			state = false;
      052		}
      053	
      054		public void run()
      055		{
      056			while (state) {
      057				dp.count++;
      058				if (dp.count > 10)
      059					dp.count = 0;
      060				dp.repaint();   // 再描画
      061				try {
      062					th.sleep(100);   // 100 ms 毎の描画
      063				}
      064				catch (InterruptedException e) {}
      065			}
      066		}
      067	
      068		public void actionPerformed(ActionEvent e)
      069		{
      070			if (e.getSource() == b_start) {   // 開始
      071				state = true;
      072				th.start();
      073			}
      074			else {   // 停止
      075				state    = false;
      076				th       = new Thread(this);
      077				dp.count = 0;
      078				dp.repaint();
      079			}
      080		}
      081	}
      082	
      083	class Test_Panel extends JPanel
      084	{
      085		int count;
      086		Test_Panel()
      087		{
      088			setBackground(Color.white);
      089			count = 0;
      090		}
      091		public void paintComponent (Graphics g)   // 描画
      092		{
      093			super.paintComponent(g);   // 親クラスの描画(必ず必要)
      094			int r = 10;
      095			for (int i1 = 0; i1 < count; i1++) {
      096				g.drawOval(0, 0, 2*r, 2*r);
      097				r = (int)(1.5 * r);
      098			}
      099		}
      100	}
      				
      008 行目

        Runnable インタフェースを継承している.Runnable インタフェースは,マルチスレッドを実現するために使用されるインタフェースである.アニメーションを作成する場合,必ずしも連続的に描画する必要はなく,ある一定時間間隔で描画を実行すれば,我々はそれを連続的に動いているように認識できる.そこで,現在動作しているスレッドとは別に,アニメーションの描画を行うスレッドを生成し,そのスレッドをある一定時間間隔で動作させるといった方法がよく使用される.その際に必要なのが,Runnable インタフェースである.

      024 行目~ 035 行目

        「開始」及び「停止」ボタンを追加している.

      037 行目~ 044 行目

        アニメーションを描画するためのパネルとして,Test_Panel クラス( 083 行目~ 100 行目)のオブジェクトを追加している.白い描画領域の回りに,薄い緑色の領域を作るために,Test_Panel クラスのオブジェクトを JPanel クラスのオブジェクト pn2 の上に配置している.

      046 行目

        Thread クラスのオブジェクトを生成している.

      049 行目~ 052 行目

        JApplet クラスのメソッド stop に対するオーバーライドであり,他のページへ移動するとき自動的に呼び出される.この場合は,スレッドを停止している.

      054 行目~ 066 行目

        Thread クラスのメソッド run に対するオーバーライドである.Runnable インタフェースを継承してスレッドを利用する場合,必ずこのメソッドを定義してやる必要がある.
      • 056 行目: state が true である間,057 行目~ 064 行目の処理が実行される.
      • 057 行目~ 059 行目: Test_Panel クラスのプロパティである count を 1 だけ増やし,その値が 10 を越えたら 0 に戻している.
      • 060 行目: D_Panel クラスのオブジェクトを再描画している.
      • 061 行目~ 064 行目: この設定により,このスレッドは 100 ms 毎に実行されることになる.

      070 行目~ 073 行目

        「開始」ボタンがクリックされたときの処理である.state を true に設定し,スレッドを開始している.

      074 行目~ 079 行目

        「停止」ボタンがクリックされたときの処理である.state を false に設定し(スレットの停止),次の開始のために新しいスレッドを生成し,カウンタ( Test_Panel クラスのプロパティ count )の初期設定を行い,最後に,再描画している.

      083 行目~ 100 行目

        Test_Panel クラスの定義である.
      • 086 行目~ 090 行目: コンストラクタであり,背景の設定とカウンタの初期設定を行っている.
      • 091 行目~ 099 行目: メソッド paintComponent のオーバーライドであり,count 個だけ,半径の異なる円を描画している.

    2. ランニング(外部複数画像の利用)

        ランニング(「AWT の利用」,「Swing の利用」)では,一定時間毎に,外部から読み込んだ異なる画像を異なる位置に描くことによってアニメーションを作成しています.以下に示すのは Java のソース( Test.java )です.

      AWT を利用した場合

      001	/****************************/
      002	/* ランニング               */
      003	/*      coded by Y.Suganuma */
      004	/****************************/
      005	import java.awt.*;
      006	import java.applet.*;
      007	
      008	/*********************/
      009	/* クラスAni_7の定義 */
      010	/*********************/
      011	public class Ani_7 extends Applet implements Runnable
      012	{
      013	
      014		boolean win_state = true;
      015		Image a[] = new Image [6];
      016		Image now_im;
      017		int now_id = 0, x, y;
      018		Thread load;
      019	
      020		/**********************/
      021		/* イメージの読み込み */
      022		/**********************/
      023		public void init() {
      024			setBackground(Color.white);
      025						// getCodeBase() : アプレットが置かれているディレクトリの取得
      026			a[0] = getImage(getCodeBase(), "fig0.gif");
      027			a[1] = getImage(getCodeBase(), "fig1.gif");
      028			a[2] = getImage(getCodeBase(), "fig2.gif");
      029			a[3] = getImage(getCodeBase(), "fig3.gif");
      030			a[4] = getImage(getCodeBase(), "fig4.gif");
      031			a[5] = getImage(getCodeBase(), "fig5.gif");
      032		}
      033	
      034		/**********************/
      035		/* スレッドのスタート */
      036		/**********************/
      037		public void start()
      038		{
      039			load = new Thread(this);
      040			load.start();
      041		}
      042	
      043		/******************/
      044		/* スレッドの停止 */
      045		/******************/
      046		public void stop()
      047		{
      048			win_state = false;
      049		}
      050	
      051		/******************/
      052		/* スレッドの実行 */
      053		/******************/
      054		public void run()
      055		{
      056			while (win_state) {
      057				now_im = a[now_id%6];
      058				if (now_id == 0) {
      059					x = -20;
      060					y = 200 - 238 / 2;
      061				}
      062				else if (now_id == 1) {
      063					x = 5;
      064					y = 200 - 249 / 2;
      065				}
      066				else if (now_id == 2) {
      067					x = 70;
      068					y = 200 - 258 / 2;
      069				}
      070				else if (now_id == 3) {
      071					x = 140;
      072					y = 200 - 258 / 2;
      073				}
      074				else if (now_id == 4) {
      075					x = 150;
      076					y = 200 - 258 / 2;
      077				}
      078				else if (now_id == 5) {
      079					x = 160;
      080					y = 200 - 247 / 2;
      081				}
      082				else if (now_id == 6) {
      083					x = 200;
      084					y = 200 - 238 / 2;
      085				}
      086				else if (now_id == 7) {
      087					x = 225;
      088					y = 200 - 249 / 2;
      089				}
      090				else if (now_id == 8) {
      091					x = 290;
      092					y = 200 - 258 / 2;
      093				}
      094				else if (now_id == 9) {
      095					x = 360;
      096					y = 200 - 258 / 2;
      097				}
      098				else if (now_id == 10) {
      099					x = 370;
      100					y = 200 - 258 / 2;
      101				}
      102				else if (now_id == 11) {
      103					x = 380;
      104					y = 200 - 247 / 2;
      105				}
      106				else if (now_id == 12) {
      107					x = 420;
      108					y = 200 - 238 / 2;
      109				}
      110				else if (now_id == 13) {
      111					x = 445;
      112					y = 200 - 249 / 2;
      113				}
      114				else if (now_id == 14) {
      115					x = 510;
      116					y = 200 - 258 / 2;
      117				}
      118				else if (now_id == 15) {
      119					x = 580;
      120					y = 200 - 258 / 2;
      121				}
      122				else if (now_id == 16) {
      123					x = 590;
      124					y = 200 - 258 / 2;
      125				}
      126				else {
      127					x = 600;
      128					y = 200 - 247 / 2;
      129				}
      130				repaint();
      131				try {
      132					load.sleep(100);
      133				}
      134				catch (InterruptedException e) {}
      135				now_id++;
      136				if (now_id > 17)
      137					now_id = 0;
      138			}
      139		}
      140	
      141		/********/
      142		/* 描画 */
      143		/********/
      144		public void paint (Graphics g)
      145		{
      146			if (now_im != null)
      147				g.drawImage(now_im, x, y, this);   // 位置(左上)
      148		}
      149	}
      				
      026 行目~ 031 行目

        Image クラスのオブジェクトを記憶する配列に,画像 fig0.gif ~ fig5.gif を読み込んでいる.

      037 行目~ 041 行目

        Applet クラスのメソッドに対するオーバーライドであり,ブラウザが init メソッドを呼び出したとき,または,他のページからアプレットのあるページへ戻ったときに自動的に呼び出される.この場合は,Thread クラスのオブジェクトを生成し,それをスタートさせている.

      046 行目~ 049 行目

        Applet クラスのメソッドに対するオーバーライドであり,他のページへ移動するとき自動的に呼び出される.この場合は,スレッドを停止している.

      057 行目

        表示する画像を,変数 now_im に設定している( 6 回毎に同じ画像を繰り返す).

      058 行目~ 129 行目

        now_id の値によって,画像を描くべき位置を設定している.

      135 行目~ 137 行目

        変数 now_id を増加させ,その値が 17 より大きくなった場合は,0 に戻している.

      147 行目

        画像を指定した位置に描画している.

      Swing を利用した場合

      001	/****************************/
      002	/* ランニング               */
      003	/*      coded by Y.Suganuma */
      004	/****************************/
      005	import java.awt.*;
      006	import javax.swing.*;
      007	
      008	public class Ani_7 extends JApplet
      009	{
      010		Ani_7_Panel pn;
      011		public void init() {
      012			Image a[] = new Image [6];
      013			a[0] = getImage(getDocumentBase(), "fig0.gif");
      014			a[1] = getImage(getDocumentBase(), "fig1.gif");
      015			a[2] = getImage(getDocumentBase(), "fig2.gif");
      016			a[3] = getImage(getDocumentBase(), "fig3.gif");
      017			a[4] = getImage(getDocumentBase(), "fig4.gif");
      018			a[5] = getImage(getDocumentBase(), "fig5.gif");
      019			pn = new Ani_7_Panel(a);
      020			getContentPane().add(pn);
      021		}
      022	
      023		public void stop()
      024		{
      025			pn.win_state = false;
      026		}
      027	}
      028	
      029	class Ani_7_Panel extends JPanel implements Runnable
      030	{
      031		boolean win_state = true;
      032		Image a[] = new Image [6];
      033		Image now_im;
      034		int now_id = 0, x, y;
      035		Thread load;
      036	
      037		Ani_7_Panel(Image a1[])
      038		{
      039			a = a1;
      040			setBackground(Color.white);
      041			load = new Thread(this);
      042			load.start();
      043		}
      044	
      045		public void run()
      046		{
      047			while (win_state) {
      048				now_im = a[now_id%6];
      049				if (now_id == 0) {
      050					x = -20;
      051					y = 200 - 238 / 2;
      052				}
      053				else if (now_id == 1) {
      054					x = 5;
      055					y = 200 - 249 / 2;
      056				}
      057				else if (now_id == 2) {
      058					x = 70;
      059					y = 200 - 258 / 2;
      060				}
      061				else if (now_id == 3) {
      062					x = 140;
      063					y = 200 - 258 / 2;
      064				}
      065				else if (now_id == 4) {
      066					x = 150;
      067					y = 200 - 258 / 2;
      068				}
      069				else if (now_id == 5) {
      070					x = 160;
      071					y = 200 - 247 / 2;
      072				}
      073				else if (now_id == 6) {
      074					x = 200;
      075					y = 200 - 238 / 2;
      076				}
      077				else if (now_id == 7) {
      078					x = 225;
      079					y = 200 - 249 / 2;
      080				}
      081				else if (now_id == 8) {
      082					x = 290;
      083					y = 200 - 258 / 2;
      084				}
      085				else if (now_id == 9) {
      086					x = 360;
      087					y = 200 - 258 / 2;
      088				}
      089				else if (now_id == 10) {
      090					x = 370;
      091					y = 200 - 258 / 2;
      092				}
      093				else if (now_id == 11) {
      094					x = 380;
      095					y = 200 - 247 / 2;
      096				}
      097				else if (now_id == 12) {
      098					x = 420;
      099					y = 200 - 238 / 2;
      100				}
      101				else if (now_id == 13) {
      102					x = 445;
      103					y = 200 - 249 / 2;
      104				}
      105				else if (now_id == 14) {
      106					x = 510;
      107					y = 200 - 258 / 2;
      108				}
      109				else if (now_id == 15) {
      110					x = 580;
      111					y = 200 - 258 / 2;
      112				}
      113				else if (now_id == 16) {
      114					x = 590;
      115					y = 200 - 258 / 2;
      116				}
      117				else {
      118					x = 600;
      119					y = 200 - 247 / 2;
      120				}
      121				repaint();
      122				try {
      123					load.sleep(100);
      124				}
      125				catch (InterruptedException e) {}
      126				now_id++;
      127				if (now_id > 17)
      128					now_id = 0;
      129			}
      130		}
      131	
      132		public void paintComponent (Graphics g)   // 描画
      133		{
      134			super.paintComponent(g);   // 親クラスの描画(必ず必要)
      135			if (now_im != null)
      136				g.drawImage(now_im, x, y, this);   // 位置(左上)
      137		}
      138	}
      				
      013 行目~ 018 行目

        Image クラスのオブジェクトを記憶する配列に,画像 fig0.gif ~ fig5.gif を読み込んでいる.

      023 行目~ 026 行目

        JApplet クラスのメソッドに対するオーバーライドであり,他のページへ移動するとき自動的に呼び出される.この場合は,スレッドを停止している.

      041 行目~ 042 行目

        Thread クラスのオブジェクトを生成し,それをスタートさせている.

      048 行目

        表示する画像を,変数 now_im に設定している( 6 回毎に同じ画像を繰り返す).

      049 行目~ 120 行目

        now_id の値によって,画像を描くべき位置を設定している.

      126 行目~ 128 行目

        変数 now_id を増加させ,その値が 17 より大きくなった場合は,0 に戻している.

      136 行目

        画像を指定した位置に描画している.

    3. ボールの運動(描画とイベント処理)

        ボールの落下(「AWT の利用」,「Swing の利用」)では,ボールをクッリクすると停止し,もう一度クリックすると再び動き出します.この例においても,円の描画の場合と同様に,一定時間毎に異なる位置にボールを描くことによってアニメーションを作成しています.以下に示すのは Java のソース( Ball.java )です.

      AWT を利用した場合

      01	import java.awt.*;
      02	import java.awt.event.*;
      03	import java.applet.*;
      04	
      05	public class Ball extends Applet implements Runnable
      06	{
      07		private boolean state = true;
      08		private double g = 9.8;   // y 軸方向の加速度
      09		private double v0 = 0;   // y 軸方向の初期速度
      10		private double v = 0;   // y 軸方向の現在速度
      11		private double t = 0;   // 時間の初期値
      12		private double h0;   // ボールの初期位置の y 座標(上が正,初期高さ)
      13		private double x, y;   // ボールの現在位置の x 座標,y 座標(上が正)
      14		private int sw = 1;   // ボールの状態( 0:停止している,1:動いている)
      15		private Dimension d;
      16		private Thread th;
      17	
      18		public void init() {
      19						// 背景色
      20			setBackground(new Color(238, 255, 238));
      21						// 初期設定
      22			d  = getSize();
      23			h0 = d.height + 40;
      24			x  = -40;
      25			y  = -40;
      26			addMouseListener(new Mouse());
      27						// スレッドの生成
      28			th = new Thread(this);
      29			th.start();
      30		}
      31	
      32		public Insets getInsets()
      33		{
      34			return new Insets(0, 0, 0, 0);
      35		}
      36	
      37		public void stop()
      38		{
      39			state = false;
      40		}
      41	
      42		public void run()
      43		{
      44			while (state) {
      45				try {
      46					th.sleep(33);
      47				}
      48				catch (InterruptedException e) {}
      49				if (x < d.width + 80 && sw > 0) {
      50					x += 1.5;
      51					t += 0.1;
      52					v  = -g * t + v0;
      53					y  = d.height - (-0.5 * g * t * t + v0 * t + h0);
      54					if (y >= d.height - 80 && v < 0) {
      55						y  = d.height - 80;
      56						v0 = -0.8 * v;
      57						h0 = 80;
      58						t  = 0;
      59					}
      60					System.out.println("position " + x + " " + y);
      61					repaint();
      62				}
      63			}
      64		}
      65	
      66		public void paint (Graphics g)
      67		{
      68			g.setColor(Color.green);
      69			g.fillOval((int)x, (int)y, 80, 80);
      70		}
      71	
      72		class Mouse extends MouseAdapter {
      73			public void mouseClicked(MouseEvent e)
      74			{
      75				int mx = e.getX();
      76				int my = e.getY();
      77				double x1 = x + 40 - mx;
      78				double y1 = y + 40 - my;
      79				double r  = Math.sqrt(x1 * x1 + y1 * y1);
      80				if (r < 40) {
      81					if (sw > 0)
      82						sw = 0;
      83					else
      84						sw = 1;
      85				}
      86			}
      87		}
      88	}
      				
      22 行目~ 26 行目

        ボールの初期位置を設定し,MouseListener を追加している.

      49 行目~ 62 行目

        ボールが動いており( sw > 0 ),かつ,画面内に存在したときの処理である.
      • 50 行目: ボールの x 座標の変更( 1.5 ピクセル / 33 ms )
      • 51 行目: 時間の変更( 0.1 秒 / 33 ms )
      • 52 行目: ボールの速度の変更
      • 53 行目: ボールの y 座標の変更.画面座標に合うように座標変換も行っている.
      • 54 行目~ 59 行目: ボールが地面に落ちたときの跳ね返り処理を行っている.その際,速度が 0.8 倍される.
      • 60 行目: ボールの位置を標準出力( Java コンソール)に出力している.
      • 61 行目: 再描画

      73 行目~ 86 行目

        画面をクリックすると実行されるメソッドである.ただし,クリックした場所がボールの外側である場合は何も行われない.ボールの内側であるときは,ボールが動いている場合は停止,停止している場合は再び動かす.

      Swing を利用した場合

      01	import java.awt.*;
      02	import java.awt.event.*;
      03	import javax.swing.*;
      04	
      05	public class Ball extends JApplet
      06	{
      07		Ball_Panel pn;
      08		public void init() {
      09			Dimension d = getSize();
      10			pn = new Ball_Panel(d);
      11			getContentPane().add(pn);
      12		}
      13	
      14		public void stop()   // 他ページへ移動の際,一時的にスレッドを停止
      15		{
      16			pn.state = false;
      17		}
      18	}
      19	
      20	class Ball_Panel extends JPanel implements Runnable
      21	{
      22		boolean state = true;
      23		private double g = 9.8;   // y 軸方向の加速度
      24		private double v0 = 0;   // y 軸方向の初期速度
      25		private double v = 0;   // y 軸方向の現在速度
      26		private double t = 0;   // 時間の初期値
      27		private double h0;   // ボールの初期位置の y 座標(上が正,初期高さ)
      28		private double x, y;   // ボールの現在位置の x 座標,y 座標(上が正)
      29		private int sw = 1;   // ボールの状態( 0:停止している,1:動いている)
      30		private Dimension d;
      31		private Thread th;
      32	
      33		Ball_Panel(Dimension d1)
      34		{
      35			d = d1;
      36						// 背景色
      37			setBackground(new Color(238, 255, 238));
      38						// 初期設定
      39			h0 = d.height + 40;
      40			x  = -40;
      41			y  = -40;
      42			addMouseListener(new Mouse());
      43						// スレッドの生成
      44			th = new Thread(this);
      45			th.start();
      46		}
      47	
      48		public void run()
      49		{
      50			while (state) {
      51				try {
      52					th.sleep(33);
      53				}
      54				catch (InterruptedException e) {}
      55				if (x < d.width + 80 && sw > 0) {
      56					x += 1.5;
      57					t += 0.1;
      58					v  = -g * t + v0;
      59					y  = d.height - (-0.5 * g * t * t + v0 * t + h0);
      60					if (y >= d.height - 80 && v < 0) {
      61						y  = d.height - 80;
      62						v0 = -0.8 * v;
      63						h0 = 80;
      64						t  = 0;
      65					}
      66					System.out.println("position " + x + " " + y);
      67					repaint();
      68				}
      69			}
      70		}
      71	
      72		public void paintComponent (Graphics g)   // 描画
      73		{
      74			super.paintComponent(g);   // 親クラスの描画(必ず必要)
      75			g.setColor(Color.green);
      76			g.fillOval((int)x, (int)y, 80, 80);
      77		}
      78	
      79		class Mouse extends MouseAdapter {
      80			public void mouseClicked(MouseEvent e)
      81			{
      82				int mx = e.getX();
      83				int my = e.getY();
      84				double x1 = x + 40 - mx;
      85				double y1 = y + 40 - my;
      86				double r  = Math.sqrt(x1 * x1 + y1 * y1);
      87				if (r < 40) {
      88					if (sw > 0)
      89						sw = 0;
      90					else
      91						sw = 1;
      92				}
      93			}
      94		}
      95	}
      				
      39 行目~ 42 行目

        ボールの初期位置を設定し,MouseListener を追加している.

      55 行目~ 68 行目

        ボールが動いており( sw > 0 ),かつ,画面内に存在したときの処理である.
      • 56 行目: ボールの x 座標の変更( 1.5 ピクセル / 33 ms )
      • 57 行目: 時間の変更( 0.1 秒 / 33 ms )
      • 58 行目: ボールの速度の変更
      • 59 行目: ボールの y 座標の変更.画面座標に合うように座標変換も行っている.
      • 60 行目~ 65 行目: ボールが地面に落ちたときの跳ね返り処理を行っている.その際,速度が 0.8 倍される.
      • 66 行目: ボールの位置を標準出力( Java コンソール)に出力している.
      • 67 行目: 再描画

      80 行目~ 93 行目

        画面をクリックすると実行されるメソッドである.ただし,クリックした場所がボールの外側である場合は何も行われない.ボールの内側であるときは,ボールが動いている場合は停止,停止している場合は再び動かす.

    4. ボールの運動(外部単一画像の利用とイベント処理)

        ボールの落下(「AWT の利用」,「Swing の利用」)は,上で示したアニメーションと同じものですが,ここではボールの画像( ball.gif )を読み込み,その画像を一定時間毎に異なる位置に表示することによってアニメーションを作成しています.以下に示すのは Java のソース( Ball.java )です.外部画像を読み込み表示する以外,上で述べたプログラムとほとんど同じですので説明は省略します.

      AWT を利用した場合

      import java.awt.*;
      import java.awt.event.*;
      import java.applet.*;
      
      public class Ball extends Applet implements Runnable
      {
      	private boolean state = true;
      	private double g = 9.8;   // y 軸方向の加速度
      	private double v0 = 0;   // y 軸方向の初期速度
      	private double v = 0;   // y 軸方向の現在速度
      	private double t = 0;   // 時間の初期値
      	private double h0;   // ボールの初期位置の y 座標(上が正,初期高さ)
      	private double x, y;   // ボールの現在位置の x 座標,y 座標(上が正)
      	private int sw = 1;   // ボールの状態( 0:停止している,1:動いている)
      	private Dimension d;
      	private Thread th;
      	private Image im;
      
      	public void init() {
      					// 背景色
      		setBackground(new Color(238, 255, 238));
      					// 初期設定
      		d  = getSize();
      		h0 = d.height + 40;
      		x  = -40;
      		y  = -40;
      		im = getImage(getDocumentBase(), "ball.gif");
      		addMouseListener(new Mouse());
      					// スレッドの生成
      		th = new Thread(this);
      		th.start();
      	}
      
      	public Insets getInsets()
      	{
      		return new Insets(0, 0, 0, 0);
      	}
      
      	public void stop()
      	{
      		state = false;
      	}
      
      	public void run()
      	{
      		while (state) {
      			try {
      				th.sleep(33);
      			}
      			catch (InterruptedException e) {}
      			if (x < d.width + 80 && sw > 0) {
      				x += 1.5;
      				t += 0.1;
      				v  = -g * t + v0;
      				y  = d.height - (-0.5 * g * t * t + v0 * t + h0);
      				if (y >= d.height - 80 && v < 0) {
      					y  = d.height - 80;
      					v0 = -0.8 * v;
      					h0 = 80;
      					t  = 0;
      				}
      				System.out.println("position " + x + " " + y);
      				repaint();
      			}
      		}
      	}
      
      	public void paint (Graphics g)
      	{
      		g.drawImage(im, (int)x, (int)y, this);
      	}
      
      	class Mouse extends MouseAdapter {
      		public void mouseClicked(MouseEvent e)
      		{
      			int mx = e.getX();
      			int my = e.getY();
      			double x1 = x + 40 - mx;
      			double y1 = y + 40 - my;
      			double r  = Math.sqrt(x1 * x1 + y1 * y1);
      			if (r < 40) {
      				if (sw > 0)
      					sw = 0;
      				else
      					sw = 1;
      			}
      		}
      	}
      }
      				

      Swing を利用した場合

      import java.awt.*;
      import java.awt.event.*;
      import javax.swing.*;
      
      public class Ball extends JApplet
      {
      	Ball_Panel pn;
      	public void init() {
      		Dimension d = getSize();
      		Image im = getImage(getDocumentBase(), "ball.gif");
      		pn = new Ball_Panel(d, im);
      		getContentPane().add(pn);
      	}
      
      	public void stop()
      	{
      		pn.state = false;
      	}
      }
      
      class Ball_Panel extends JPanel implements Runnable
      {
      	boolean state = true;
      	private double g = 9.8;   // y 軸方向の加速度
      	private double v0 = 0;   // y 軸方向の初期速度
      	private double v = 0;   // y 軸方向の現在速度
      	private double t = 0;   // 時間の初期値
      	private double h0;   // ボールの初期位置の y 座標(上が正,初期高さ)
      	private double x, y;   // ボールの現在位置の x 座標,y 座標(上が正)
      	private int sw = 1;   // ボールの状態( 0:停止している,1:動いている)
      	private Dimension d;
      	private Thread th;
      	private Image im;
      
      	Ball_Panel(Dimension d1, Image im1)
      	{
      		d  = d1;
      		im = im1;
      					// 背景色
      		setBackground(new Color(238, 255, 238));
      					// 初期設定
      		h0 = d.height + 40;
      		x  = -40;
      		y  = -40;
      		addMouseListener(new Mouse());
      					// スレッドの生成
      		th = new Thread(this);
      		th.start();
      	}
      
      	public void run()
      	{
      		while (state) {
      			try {
      				th.sleep(33);
      			}
      			catch (InterruptedException e) {}
      			if (x < d.width + 80 && sw > 0) {
      				x += 1.5;
      				t += 0.1;
      				v  = -g * t + v0;
      				y  = d.height - (-0.5 * g * t * t + v0 * t + h0);
      				if (y >= d.height - 80 && v < 0) {
      					y  = d.height - 80;
      					v0 = -0.8 * v;
      					h0 = 80;
      					t  = 0;
      				}
      				System.out.println("position " + x + " " + y);
      				repaint();
      			}
      		}
      	}
      
      	public void paintComponent (Graphics g)   // 描画
      	{
      		super.paintComponent(g);   // 親クラスの描画(必ず必要)
      		g.drawImage(im, (int)x, (int)y, this);
      	}
      
      	class Mouse extends MouseAdapter {
      		public void mouseClicked(MouseEvent e)
      		{
      			int mx = e.getX();
      			int my = e.getY();
      			double x1 = x + 40 - mx;
      			double y1 = y + 40 - my;
      			double r  = Math.sqrt(x1 * x1 + y1 * y1);
      			if (r < 40) {
      				if (sw > 0)
      					sw = 0;
      				else
      					sw = 1;
      			}
      		}
      	}
      }
      				

    5. 花火(ピクセル操作)

        ピクセル値を直接操作し,それを画像に変換し,表示位置を変化させることによってもアニメーションを作成することができます(もちろん,これらの方法を同時に使用する方法も考えられます).最初の例(「AWT の利用」,「Swing の利用」)は,ピクセル値を直接操作して描いた矩形を左から右に移動させているだけのものです.以下に示すのは Java のソース( Pixel.java )です. .

      AWT を利用した場合

      01	import java.awt.*;
      02	import java.awt.image.*;
      03	import java.applet.*;
      04	
      05	public class Pixel extends Applet implements Runnable
      06	{
      07		private boolean state = true;
      08		private int x, y;   // 位置
      09		private int w = 40, h = 40, size;   // 図形の大きさ
      10		private int pixels[];   // 図形
      11		private Dimension d;   // 画面の大きさ
      12		private Thread th;
      13		private MemoryImageSource mis;
      14		private Image im;
      15	
      16		public void init() {
      17						// 背景色
      18			setBackground(new Color(238, 255, 238));
      19						// 初期設定
      20			d      = getSize();
      21			x      = 0;
      22			y      = d.height / 2 - h / 2;
      23			size   = w * h;
      24			pixels = new int [size];
      25						// 画像の生成
      26			for (int i1 = 0; i1 < h; i1++) {
      27				for (int i2 = 0; i2 < w; i2++) {
      28					if (i1 < w/2)
      29						pixels[i1*w+i2] = 0xffff0000;
      30					else
      31						pixels[i1*w+i2] = 0xff00ff00;
      32				}
      33			}
      34			mis = new MemoryImageSource(w, h, pixels, 0, w);
      35			mis.setAnimated(true);
      36			im = createImage(mis);
      37						// スレッドの生成
      38			th = new Thread(this);
      39			th.start();
      40		}
      41	
      42		public void stop()
      43		{
      44			state = false;
      45		}
      46	
      47		public void run()
      48		{
      49			while (state) {
      50				try {
      51					th.sleep(100);   // 100ms 毎に表示
      52				}
      53				catch (InterruptedException e) {}
      54				repaint();
      55				x += 20;
      56				if (x >= d.width - w)
      57					x = 0;
      58			}
      59		}
      60	
      61		public void paint (Graphics g)
      62		{
      63			g.drawImage(im, x, y, this);   // 図形の描画
      64		}
      65	}
      				
      24 行目

        画像( 40 × 40 ピクセルの矩形)の大きさから必要なピクセル数を決定し,各ピクセルの情報を入れるための配列を定義

      26 行目~ 33 行目

        画像の上半分を赤,下半分を青に設定.各ピクセルの透明度,赤,緑,青に対する情報が,4 バイト 1 組になり,1 次元配列 pixels に設定される.本来,画像は,画像の高さが h ピクセル,幅が w ピクセルであった場合,h 行 w 列の 2 次元配列としてとらえた方が理解しやすいが,ここでは,それを 1 元配列として表現している.一般に,2 次元配列が連続した領域にとられた場合,2 次元配列の i 行 j 列は,1 次元配列の添え字 (i * w + j) の位置に相当する.

      34 行目

        ピクセルデータから,MemoryImageSource クラスのオブジェクトを生成

      35 行目

        アニメーションを実行するために必要

      36 行目

        MemoryImageSource クラスのオブジェクトを,Image クラスのオブジェクトに変換

      54 行目

        再描画

      55 行目~ 57 行目

        画像の位置を変更し,もし,画像が画面の右端に来たら,左端の戻す.

      Swing を利用した場合

      01	import java.awt.*;
      02	import java.awt.image.*;
      03	import javax.swing.*;
      04	
      05	public class Pixel extends JApplet
      06	{
      07		Draw_Panel pn;
      08		public void init() {
      09			Dimension d = getSize();
      10			pn = new Draw_Panel(d);
      11			getContentPane().add(pn);
      12		}
      13	
      14		public void stop()
      15		{
      16			pn.state = false;
      17		}
      18	}
      19	
      20	class Draw_Panel extends JPanel implements Runnable
      21	{
      22		public boolean state = true;
      23		private int x, y;   // 位置
      24		private int w = 40, h = 40, size;   // 図形の大きさ
      25		private int pixels[];   // 図形
      26		private Dimension d;   // 画面の大きさ
      27		private Thread th;
      28		private MemoryImageSource mis;
      29		private Image im;
      30	
      31		Draw_Panel(Dimension d1) {
      32			d = d1;
      33						// 背景色
      34			setBackground(new Color(238, 255, 238));
      35						// 初期設定
      36			x      = 0;
      37			y      = d.height / 2 - h / 2;
      38			size   = w * h;
      39			pixels = new int [size];
      40						// 画像の生成
      41			for (int i1 = 0; i1 < h; i1++) {
      42				for (int i2 = 0; i2 < w; i2++) {
      43					if (i1 < w/2)
      44						pixels[i1*w+i2] = 0xffff0000;
      45					else
      46						pixels[i1*w+i2] = 0xff00ff00;
      47				}
      48			}
      49			mis = new MemoryImageSource(w, h, pixels, 0, w);
      50			mis.setAnimated(true);
      51			im = createImage(mis);
      52						// スレッドの生成
      53			th = new Thread(this);
      54			th.start();
      55		}
      56	
      57		public void run()
      58		{
      59			while (state) {
      60				try {
      61					th.sleep(100);   // 100ms 毎に表示
      62				}
      63				catch (InterruptedException e) {}
      64				repaint();
      65				x += 20;
      66				if (x >= d.width - w)
      67					x = 0;
      68			}
      69		}
      70	
      71		public void paintComponent (Graphics g)
      72		{
      73			super.paintComponent(g);   // 親クラスの描画(必ず必要)
      74			g.drawImage(im, x, y, this);   // 図形の描画
      75		}
      76	}
      				
      39 行目

        画像( 40 × 40 ピクセルの矩形)の大きさから必要なピクセル数を決定し,各ピクセルの情報を入れるための配列を定義

      41 行目~ 48 行目

        画像の上半分を赤,下半分を青に設定.各ピクセルの透明度,赤,緑,青に対する情報が,4 バイト 1 組になり,1 次元配列 pixels に設定される.本来,画像は,画像の高さが h ピクセル,幅が w ピクセルであった場合,h 行 w 列の 2 次元配列としてとらえた方が理解しやすいが,ここでは,それを 1 元配列として表現している.一般に,2 次元配列が連続した領域にとられた場合,2 次元配列の i 行 j 列は,1 次元配列の添え字 (i * w + j) の位置に相当する.

      49 行目

        ピクセルデータから,MemoryImageSource クラスのオブジェクトを生成

      50 行目

        アニメーションを実行するために必要

      51 行目

        MemoryImageSource クラスのオブジェクトを,Image クラスのオブジェクトに変換

      64 行目

        再描画

      65 行目~ 67 行目

        画像の位置を変更し,もし,画像が画面の右端に来たら,左端の戻す.

        次の例(「AWT の利用」,「Swing の利用」)は,もう少し複雑な例です.花火と同じような情景を描いています.以下に示すのは Java のソース( Fire.java )です.

      AWT を利用した場合

      001	import java.awt.*;
      002	import java.awt.image.*;
      003	import java.applet.*;
      004	import java.util.Random;
      005	
      006	public class Fire extends Applet implements Runnable
      007	{
      008		private int max = 20;   // 花火の数
      009		private int m_pr = 7;   // 打ち上げ間隔の最大値
      010		private int m_cl = 10;   // 花火の色の最大値
      011		private int f_l = 300;   // 花火の直径
      012		private int count = 0;   // カウンタ
      013		private int next = 0;   // 次の打ち上げ時期
      014		private int x[];   // 花火のx座標
      015		private int y[];   // 花火のy座標
      016		private int cl[];   // 花火の色
      017		private int k[];   // 花火の状態
      018						   // (-1:打ち上げ前,0:打ち上げ当初,>0:描いた輪の数)
      019		private int height, width, size, f_size, pixels[][], color[];
      020		private boolean state = true;
      021		private Thread th;
      022		private MemoryImageSource mis[];
      023		private Image im[];
      024		private Random rn;
      025	
      026		public void init() {
      027			int i1;
      028						// 背景色
      029			setBackground(new Color(0, 0, 0));
      030						// 初期設定
      031			width  = getSize().width;
      032			height = getSize().height;
      033			size   = width * height;
      034			f_size = f_l * f_l;
      035			pixels = new int [max][f_size];
      036			k      = new int [max];
      037			x      = new int [max];
      038			y      = new int [max];
      039			cl     = new int [max];
      040			mis    = new MemoryImageSource [max];
      041			im     = new Image [max];
      042			for (i1 = 0; i1 < max; i1++)
      043				k[i1] = -1;
      044			rn    = new Random();
      045			color = new int [m_cl];
      046			color[0] = 0xffff0000;
      047			color[1] = 0xff00ff00;
      048			color[2] = 0xff0000ff;
      049			color[3] = 0xffffff00;
      050			color[4] = 0xffff00ff;
      051			color[5] = 0xff00ffff;
      052			color[6] = 0xffeeffee;
      053			color[7] = 0xffffaaaa;
      054			color[8] = 0xffaaffaa;
      055			color[9] = 0xffaaaaff;
      056						// スレッドの生成
      057			th = new Thread(this);
      058			th.start();
      059		}
      060	
      061		public Insets getInsets()
      062		{
      063			return new Insets(0, 0, 0, 0);
      064		}
      065	
      066		public void stop()
      067		{
      068			state = false;
      069		}
      070	
      071		public void run()
      072		{
      073			double ang, s;
      074			int i0, i1, i2, i3, kx, ky, kxy, sw;
      075	
      076			while (state) {
      077				try {
      078					th.sleep(200);
      079				}
      080				catch (InterruptedException e) {}
      081	
      082				sw = 0;
      083				for (i0 = 0; i0 < max; i0++) {
      084					if (k[i0] < 0) {
      085						if (count >= next && sw == 0) {
      086							sw     = 1;
      087							count  = 0;
      088							cl[i0] = (int)(m_cl * rn.nextDouble());
      089							if (cl[i0] >= m_cl)
      090								cl[i0] = m_cl - 1;
      091							for (i1 = 0; i1 < f_size; i1++)
      092								pixels[i0][i1] = 0x00000000;
      093							x[i0]   = (int)(width * rn.nextDouble()) - f_l / 2;
      094							y[i0]   = (int)(height * rn.nextDouble()) - f_l / 2;
      095							k[i0]   = 0;
      096							mis[i0] = new MemoryImageSource(f_l, f_l, pixels[i0], 0, f_l);
      097							mis[i0].setAnimated(true);
      098							im[i0] = createImage(mis[i0]);
      099							next   = (int)(m_pr * rn.nextDouble());
      100							if (next <= 0)
      101								next = 1;
      102						}
      103					}
      104					else {
      105						k[i0]++;
      106						if (k[i0] > m_pr)
      107							k[i0] = -1;
      108						else {
      109							s   = Math.PI / 6;
      110							ang = 0;
      111							for (i1 = 0; i1 < 12; i1++) {
      112								kx = f_l / 2 + (int)(20 * k[i0] * Math.cos(ang));
      113								ky = f_l / 2 + (int)(20 * k[i0] * Math.sin(ang));
      114								for (i2 = kx-5; i2 < kx+5; i2++) {
      115									for (i3 = ky-5; i3 < ky+5; i3++) {
      116										kxy = f_l * i2 + i3;
      117										if (kxy >= 0 && kxy < f_size)
      118											pixels[i0][kxy] = color[cl[i0]];
      119									}
      120								}
      121										// 以下は,四角を丸に近くするため行っている
      122								pixels[i0][f_l*(kx-1)+ky-6] = color[cl[i0]];
      123								pixels[i0][f_l*kx+ky-6] = color[cl[i0]];
      124								pixels[i0][f_l*(kx+1)+ky-6] = color[cl[i0]];
      125								pixels[i0][f_l*(kx-1)+ky+5] = color[cl[i0]];
      126								pixels[i0][f_l*kx+ky+5] = color[cl[i0]];
      127								pixels[i0][f_l*(kx+1)+ky+5] = color[cl[i0]];
      128								pixels[i0][f_l*(kx-6)+ky-1] = color[cl[i0]];
      129								pixels[i0][f_l*(kx-6)+ky] = color[cl[i0]];
      130								pixels[i0][f_l*(kx-6)+ky+1] = color[cl[i0]];
      131								pixels[i0][f_l*(kx+5)+ky-1] = color[cl[i0]];
      132								pixels[i0][f_l*(kx+5)+ky] = color[cl[i0]];
      133								pixels[i0][f_l*(kx+5)+ky+1] = color[cl[i0]];
      134								ang += s;
      135							}
      136							im[i0] = createImage(mis[i0]);
      137						}
      138					}
      139				}
      140				count++;
      141				repaint();
      142			}
      143		}
      144	
      145		public void paint (Graphics g)
      146		{
      147			int i0;
      148			for (i0 = 0; i0 < max; i0++) {
      149				if (k[i0] >= 0)
      150					g.drawImage(im[i0], x[i0], y[i0], this);
      151			}
      152		}
      153	}
      				
      031 行目~ 035 行目

        画面の大きさから必要なピクセル数を決定し,花火毎にその大きさを持った配列を定義

      040 行目

        MemoryImageSource クラスのオブジェクトが入る配列を定義

      041 行目

        Image クラスのオブジェクトが入る配列を定義

      042 行目~ 043 行目

        すべての花火を打ち上げ前の状態に設定

      044 行目

        乱数の初期設定

      045 行目~ 055 行目

        選択できる花火の 10 種類の色を 透明度とRGB 値で設定

      084 行目~ 103 行目

        花火が打ち上げ前の状態( k[i0] < 0 )であるときの処理である.カウンタ count の値が 次の打ち上げ時刻 next 以降であり,かつ,次の花火の打ち上げ時刻が設定されていない場合( sw == 0 )は,新しい花火を打ち上げるために,以下の処理が行われる.
      • 086 行目: 次の花火の打ち上げ時刻設定済み
      • 087 行目: カウンタの初期設定
      • 088 行目~ 090 行目: 花火の色をランダムに選択
      • 091 行目~ 092 行目: 画面の初期設定
      • 093 行目~ 094 行目: 花火の位置をランダムに選択
      • 095 行目: 花火を打ち上げた状態に設定
      • 096 行目: 設定されたピクセル値に基づき,MemoryImageSource クラスのオブジェクトを生成
      • 097 行目: アニメーションを実行するために必要
      • 098 行目: MemoryImageSource クラスのオブジェクトを,Image クラスのオブジェクトに変換
      • 099 行目~ 101 行目: 次の打ち上げ時刻をランダムに選択

      105 行目~ 107 行目

        花火を打ち上げた後の処理であり,105 行目において描画する輪の数を増加させ,その数が指定の数( m_pr )より大きくなった場合は,花火を消去する(描画されないようにする).

      109 行目~ 135 行目

        指定した輪の数( k[i0] )だけ,半径を 20 ピクセルずつ変化させ,かつ,円周を 12 等分した位置に 幅 9 ピクセル,高さ 9 ピクセルの矩形を描いている.なお,122 行目~ 133 行目は,上で描いた矩形に多少丸みを付けるための処理であり,必ずしも必要ない.

      136 行目

        MemoryImageSource クラスのオブジェクトを,Image クラスのオブジェクトに変換している.

      140 行目

        カウンタ(時間)の増加処理

      141 行目

        再描画

      Swing を利用した場合

      001	import java.awt.*;
      002	import java.awt.image.*;
      003	import javax.swing.*;
      004	import java.util.Random;
      005	
      006	public class Fire extends JApplet
      007	{
      008		Fire_Panel pn;
      009		public void init() {
      010			Dimension d = getSize();
      011			pn = new Fire_Panel(d);
      012			getContentPane().add(pn);
      013		}
      014	
      015		public void stop()
      016		{
      017			pn.state = false;
      018		}
      019	}
      020	
      021	class Fire_Panel extends JPanel implements Runnable
      022	{
      023		private int max = 20;   // 花火の数
      024		private int m_pr = 7;   // 打ち上げ間隔の最大値
      025		private int m_cl = 10;   // 花火の色の最大値
      026		private int f_l = 300;   // 花火の直径
      027		private int count = 0;   // カウンタ
      028		private int next = 0;   // 次の打ち上げ時期
      029		private int x[];   // 花火のx座標
      030		private int y[];   // 花火のy座標
      031		private int cl[];   // 花火の色
      032		private int k[];   // 花火の状態
      033						   // (-1:打ち上げ前,0:打ち上げ当初,>0:描いた輪の数)
      034		private int height, width, size, f_size, pixels[][], color[];
      035		boolean state = true;
      036		private Thread th;
      037		private MemoryImageSource mis[];
      038		private Image im[];
      039		private Random rn;
      040	
      041		Fire_Panel(Dimension d)
      042		{
      043			int i1;
      044						// 背景色
      045			setBackground(new Color(0, 0, 0));
      046						// 初期設定
      047			width  = d.width;
      048			height = d.height;
      049			size   = width * height;
      050			f_size = f_l * f_l;
      051			pixels = new int [max][f_size];
      052			k      = new int [max];
      053			x      = new int [max];
      054			y      = new int [max];
      055			cl     = new int [max];
      056			mis    = new MemoryImageSource [max];
      057			im     = new Image [max];
      058			for (i1 = 0; i1 < max; i1++)
      059				k[i1] = -1;
      060			rn    = new Random();
      061			color = new int [m_cl];
      062			color[0] = 0xffff0000;
      063			color[1] = 0xff00ff00;
      064			color[2] = 0xff0000ff;
      065			color[3] = 0xffffff00;
      066			color[4] = 0xffff00ff;
      067			color[5] = 0xff00ffff;
      068			color[6] = 0xffeeffee;
      069			color[7] = 0xffffaaaa;
      070			color[8] = 0xffaaffaa;
      071			color[9] = 0xffaaaaff;
      072						// スレッドの生成
      073			th = new Thread(this);
      074			th.start();
      075		}
      076	
      077		public void run()
      078		{
      079			double ang, s;
      080			int i0, i1, i2, i3, kx, ky, kxy, sw;
      081	
      082			while (state) {
      083				try {
      084					th.sleep(200);
      085				}
      086				catch (InterruptedException e) {}
      087	
      088				sw = 0;
      089				for (i0 = 0; i0 < max; i0++) {
      090					if (k[i0] < 0) {
      091						if (count >= next && sw == 0) {
      092							sw     = 1;
      093							count  = 0;
      094							cl[i0] = (int)(m_cl * rn.nextDouble());
      095							if (cl[i0] >= m_cl)
      096								cl[i0] = m_cl - 1;
      097							for (i1 = 0; i1 < f_size; i1++)
      098								pixels[i0][i1] = 0x00000000;
      099							x[i0]   = (int)(width * rn.nextDouble()) - f_l / 2;
      100							y[i0]   = (int)(height * rn.nextDouble()) - f_l / 2;
      101							k[i0]   = 0;
      102							mis[i0] = new MemoryImageSource(f_l, f_l, pixels[i0], 0, f_l);
      103							mis[i0].setAnimated(true);
      104							im[i0] = createImage(mis[i0]);
      105							next   = (int)(m_pr * rn.nextDouble());
      106							if (next <= 0)
      107								next = 1;
      108						}
      109					}
      110					else {
      111						k[i0]++;
      112						if (k[i0] > m_pr)
      113							k[i0] = -1;
      114						else {
      115							s   = Math.PI / 6;
      116							ang = 0;
      117							for (i1 = 0; i1 < 12; i1++) {
      118								kx = f_l / 2 + (int)(20 * k[i0] * Math.cos(ang));
      119								ky = f_l / 2 + (int)(20 * k[i0] * Math.sin(ang));
      120								for (i2 = kx-5; i2 < kx+5; i2++) {
      121									for (i3 = ky-5; i3 < ky+5; i3++) {
      122										kxy = f_l * i2 + i3;
      123										if (kxy >= 0 && kxy < f_size)
      124											pixels[i0][kxy] = color[cl[i0]];
      125									}
      126								}
      127								pixels[i0][f_l*(kx-1)+ky-6] = color[cl[i0]];
      128								pixels[i0][f_l*kx+ky-6] = color[cl[i0]];
      129								pixels[i0][f_l*(kx+1)+ky-6] = color[cl[i0]];
      130								pixels[i0][f_l*(kx-1)+ky+5] = color[cl[i0]];
      131								pixels[i0][f_l*kx+ky+5] = color[cl[i0]];
      132								pixels[i0][f_l*(kx+1)+ky+5] = color[cl[i0]];
      133								pixels[i0][f_l*(kx-6)+ky-1] = color[cl[i0]];
      134								pixels[i0][f_l*(kx-6)+ky] = color[cl[i0]];
      135								pixels[i0][f_l*(kx-6)+ky+1] = color[cl[i0]];
      136								pixels[i0][f_l*(kx+5)+ky-1] = color[cl[i0]];
      137								pixels[i0][f_l*(kx+5)+ky] = color[cl[i0]];
      138								pixels[i0][f_l*(kx+5)+ky+1] = color[cl[i0]];
      139								ang += s;
      140							}
      141							im[i0] = createImage(mis[i0]);
      142						}
      143					}
      144				}
      145				count++;
      146				repaint();
      147			}
      148		}
      149	
      150		public void paintComponent (Graphics g)   // 描画
      151		{
      152			super.paintComponent(g);   // 親クラスの描画(必ず必要)
      153			int i0;
      154			for (i0 = 0; i0 < max; i0++) {
      155				if (k[i0] >= 0)
      156					g.drawImage(im[i0], x[i0], y[i0], this);
      157			}
      158		}
      159	}
      				
      047 行目~ 051 行目

        画面の大きさから必要なピクセル数を決定し,花火毎にその大きさを持った配列を定義

      056 行目

        MemoryImageSource クラスのオブジェクトが入る配列を定義

      057 行目

        Image クラスのオブジェクトが入る配列を定義

      058 行目~ 059 行目

        すべての花火を打ち上げ前の状態に設定

      060 行目

        乱数の初期設定

      061 行目~ 071 行目

        選択できる花火の 10 種類の色を 透明度と RGB 値で設定

      090 行目~ 109 行目

        花火が打ち上げ前の状態( k[i0] < 0 )であるときの処理である.カウンタ count の値が 次の打ち上げ時刻 next 以降であり,かつ,次の花火の打ち上げ時刻が設定されていない場合( sw == 0 )は,新しい花火を打ち上げるために,以下の処理が行われる.
      • 092 行目: 次の花火の打ち上げ時刻設定済み
      • 093 行目: カウンタの初期設定
      • 094 行目~ 096 行目: 花火の色をランダムに選択
      • 097 行目~ 098 行目: 画面の初期設定
      • 099 行目~ 100 行目: 花火の位置をランダムに選択
      • 101 行目: 花火を打ち上げた状態に設定
      • 102 行目: 設定されたピクセル値に基づき,MemoryImageSource クラスのオブジェクトを生成
      • 103 行目: アニメーションを実行するために必要
      • 104 行目: MemoryImageSource クラスのオブジェクトを,Image クラスのオブジェクトに変換
      • 105 行目~ 107 行目: 次の打ち上げ時刻をランダムに選択

      111 行目~ 113 行目

        花火を打ち上げた後の処理であり,105 行目において描画する輪の数を増加させ,その数が指定の数( m_pr )より大きくなった場合は,花火を消去する(描画されないようにする).

      115 行目~ 140 行目

        指定した輪の数( k[i0] )だけ,半径を 20 ピクセルずつ変化させ,かつ,円周を 12 等分した位置に 幅 9 ピクセル,高さ 9 ピクセルの矩形を描いている.なお,127 行目~ 138 行目は,上で描いた矩形に多少丸みを付けるための処理であり,必ずしも必要ない.

      141 行目

        MemoryImageSource クラスのオブジェクトを,Image クラスのオブジェクトに変換している.

      145 行目

        カウンタ(時間)の増加処理

      146 行目

        再描画

    6. Web ページ(外部複数画像の利用とイベント処理,他ページへのリンク)

        ここでは,Java によってホームページにおけるトップページを作成した例(「AWT の利用」,「Swing の利用」)を示します.最初に表示された状態では,すべての画像が停止していますが,徐々に各画像が動き出します.まず,上部にある文字の部分をクリックすると,

      加速: 動きが少しずつ速くなります
      加速: 動きが少しずつ遅くなります
      情報学部: 情報学部への入り口に移動します

      といった動作を行います.

        画像部分をクリックすると,動きが止まり,対応する説明が表示されます.説明を閉じると再び動き出し,説明を閉じずに文字や画像以外の部分をクリックすると説明に対応する画像が削除された後,他の画像は再び動き出します.また,文字や画像以外の部分をクリックすると,動きが停止します.停止した状態で,文字や画像以外の部分をクリックすると,再び動き出します.最初に設定されている速度では,各画像をクリックすることは難しいかと思いますので,減速又は停止した後にクリックしてみてください.

      AWT を利用した場合

      Java のソース( Space.java )
      import java.applet.*;
      import java.util.*;
      import java.awt.*;
      import java.awt.event.*;
      import java.io.*;
      import java.net.*;
      
      public class Space extends Applet implements Runnable
      {
      	int p[], a[], b[];   // 周期,楕円の長径と短径
      	int x[], y[], z[];   // 画像の位置
      	int period = 100;   // 表示間隔
      	int w, h;   // 画面の大きさ
      	int t_x1, t_x2, t_y1, t_y2;   // タイトルの位置
      	int d_x1, d_x2, d_y1, d_y2;   // 減速の位置
      	int u_x1, u_x2, u_y1, u_y2;   // 加速の位置
      	int i_w[], i_wt[], i_h[], i_ht[];   // 画像の幅と高さ
      	String tt[];
      	Image Back, Buf, back, bun[], bun_t[];
      	int state = 0;
      	Graphics g_b;
      	Thread th;
      	Random rand;
      	Space sp = this;
      
      	/************/
      	/* 初期設定 */
      	/************/
      	public void init()
      	{
      		int i1;
      					// イメージの読み込み
      		bun   = new Image [9];
      		bun_t = new Image [9];
      		MediaTracker trk = new MediaTracker(this);
      		back = getImage(getCodeBase(), "space.jpg");
      		trk.addImage(back, 0);
      		bun[0]   = getImage(getCodeBase(), "computer.gif");
      		bun[1]   = getImage(getCodeBase(), "architecture.gif");
      		bun[2]   = getImage(getCodeBase(), "AI.gif");
      		bun[3]   = getImage(getCodeBase(), "life.gif");
      		bun[4]   = getImage(getCodeBase(), "education.gif");
      		bun[5]   = getImage(getCodeBase(), "media.gif");
      		bun[6]   = getImage(getCodeBase(), "social.gif");
      		bun[7]   = getImage(getCodeBase(), "business.gif");
      		bun[8]   = getImage(getCodeBase(), "communication.gif");
      		bun_t[0] = getImage(getCodeBase(), "computer_txt.gif");
      		bun_t[1] = getImage(getCodeBase(), "architecture_txt.gif");
      		bun_t[2] = getImage(getCodeBase(), "AI_txt.gif");
      		bun_t[3] = getImage(getCodeBase(), "life_txt.gif");
      		bun_t[4] = getImage(getCodeBase(), "education_txt.gif");
      		bun_t[5] = getImage(getCodeBase(), "media_txt.gif");
      		bun_t[6] = getImage(getCodeBase(), "social_txt.gif");
      		bun_t[7] = getImage(getCodeBase(), "business_txt.gif");
      		bun_t[8] = getImage(getCodeBase(), "communication_txt.gif");
      		for (i1 = 0; i1 < 9; i1++) {
      			trk.addImage(bun[i1], 2*i1+1);
      			trk.addImage(bun_t[i1], 2*i1+2);
      		}
      		try {
      			trk.waitForAll();
      		}
      		catch (InterruptedException e1) {}
      
      		tt    = new String [9];
      		tt[0] = "コンピュータ";
      		tt[1] = "情報基礎";
      		tt[2] = "人工知能・認知科学";
      		tt[3] = "生命情報";
      		tt[4] = "教育情報";
      		tt[5] = "メディア";
      		tt[6] = "社会情報";
      		tt[7] = "経営・ビジネス";
      		tt[8] = "コミュニケーション";
      
      		i_w = new int [9];
      		i_h = new int [9];
      		for (i1 = 0; i1 < 9; i1++) {
      			i_w[i1] = bun[i1].getWidth(this);
      			i_h[i1] = bun[i1].getHeight(this);
      		}
      		i_wt = new int [9];
      		i_ht = new int [9];
      		for (i1 = 0; i1 < 9; i1++) {
      			i_wt[i1] = bun_t[i1].getWidth(this);
      			i_ht[i1] = bun_t[i1].getHeight(this);
      		}
      					// スクリーンバッファの生成
      		w   = getSize().width;
      		h   = getSize().height;
      		Buf = createImage(w, h);     //バッファ生成
      		g_b = Buf.getGraphics();    //グラフィックコンテキスト取得
      					// 背景画像の生成
      		Back = createImage(w, h);
      		Graphics g = Back.getGraphics();
      		g.drawImage(back, 0, 0, w, h, this);
      		g.setColor(Color.cyan);
      		Font f = new Font("TimesRoman", Font.BOLD, 25);
      		g.setFont(f);
      		FontMetrics fm = g.getFontMetrics(f);
      		int c_w = fm.stringWidth("情報学部");
      		int c_h = fm.getHeight();
      		Insets insets = getInsets();
      		t_x1 = w / 2 - c_w / 2;
      		t_x2 = t_x1 + c_w;
      		t_y1 = insets.top;
      		t_y2 = t_y1 + c_h;
      		g.drawString("情報学部", t_x1, t_y2);
      		g.setColor(Color.yellow);
      		f = new Font("TimesRoman", Font.BOLD, 15);
      		g.setFont(f);
      		fm   = g.getFontMetrics(f);
      		c_w  = fm.stringWidth("<< 減速");
      		c_h  = fm.getHeight();
      		d_x1 = t_x1 - c_w - 10;
      		d_x2 = d_x1 + c_w;
      		d_y1 = t_y2 - c_h;
      		d_y2 = t_y2;
      		g.drawString("<< 減速", d_x1, d_y2);
      		u_x1 = t_x2 + 10;
      		u_x2 = u_x1 + c_w;
      		u_y1 = t_y2 - c_h;
      		u_y2 = t_y2;
      		g.drawString("加速 >>", u_x1, u_y2);
      		g.dispose();
      					// マウスリスナーの追加
              addMouseListener(new Mouse());
      					// ランダム変数の初期化
      		rand = new Random();
      					// 周期,楕円の長径と短径
      		p = new int [9];
      		a = new int [9];
      		b = new int [9];
      		x = new int [9];
      		y = new int [9];
      		z = new int [9];
      		for (i1 = 0; i1 < 9; i1++) {
      			p[i1] = (int)(20 + 20 * rand.nextDouble());
      			a[i1] = (int)(3 * w / 8 + (w / 8 + 50) * rand.nextDouble());
      			b[i1] = (int)(3 * h / 8 + (h / 8 + 50) * rand.nextDouble());
      		}
      					// スレッドの定義と開始
      		state = 1;
      		th    = new Thread(this);
      		th.start();
      	}
      
      	/******************/
      	/* スレッドの実行 */
      	/******************/
      	public void run()
      	{
      		int i1, i2, m = 0, c[], now[], s[], sw, sh;
      		double r, x1, x2, y1, y2, z1, z2, a45 = Math.PI / 4.0;
      					// 初期位置
      		now = new int [9];
      		s   = new int [9];
      		c   = new int [9];
      		for (i1 = 0; i1 < 9; i1++)
      			now[i1] = 0;
      		for (i1 = 1; i1 < 9; i1++)
      			c[i1] = (int)(100 * rand.nextDouble());
      		x[0] = w / 2;
      		y[0] = h / 2;
      		z[0] = 0;
      					// 描画
      		while (state > 0) {
      			try {
      				th.sleep(period);
      			}
      			catch (InterruptedException e) {}
      			if (state == 1) {
      				g_b.drawImage(Back, 0, 0, this);  //背景画像の描画
      						// アーキテクチャ
      				x1   = a[1] * Math.cos(2 * Math.PI / p[1] * now[1]);
      				y[1] = h / 2;
      				z1   = b[1] * Math.sin(2 * Math.PI / p[1] * now[1]);
      				x[1] = w / 2 + (int)(Math.cos(a45) * x1 - Math.sin(a45) * z1);
      				z[1] = (int)(Math.sin(a45) * x1 + Math.cos(a45) * z1);
      						// 人工知能
      				x1   = a[2] * Math.cos(-Math.PI + 2 * Math.PI / p[2] * now[2]);
      				y[2] = h / 2;
      				z1   = b[2] * Math.sin(-Math.PI + 2 * Math.PI / p[2] * now[2]);
      				x[2] = w / 2 + (int)(Math.cos(-a45) * x1 - Math.sin(-a45) * z1);
      				z[2] = (int)(Math.sin(-a45) * x1 + Math.cos(-a45) * z1);
      						// 生命
      				x[3] = w / 2;
      				y1   = a[3] * Math.cos(2 * Math.PI / p[3] * now[3]);
      				z1   = b[3] * Math.sin(2 * Math.PI / p[3] * now[3]);
      				y[3] = h / 2 + (int)(Math.cos(a45) * y1 - Math.sin(a45) * z1);
      				z[3] = (int)(Math.sin(a45) * y1 + Math.cos(a45) * z1);
      						// 教育
      				x[4] = w / 2;
      				y1   = a[4] * Math.cos(-Math.PI + 2 * Math.PI / p[4] * now[4]);
      				z1   = b[4] * Math.sin(-Math.PI + 2 * Math.PI / p[4] * now[4]);
      				y[4] = h / 2 + (int)(Math.cos(-a45) * y1 - Math.sin(-a45) * z1);
      				z[4] = (int)(Math.sin(-a45) * y1 + Math.cos(-a45) * z1);
      						// メディア
      				x1   = a[5] * Math.cos(2 * Math.PI / p[5] * now[5]);
      				y1   = 0;
      				z1   = b[5] * Math.sin(2 * Math.PI / p[5] * now[5]);
      				x2   = (int)(Math.cos(a45) * x1 - Math.sin(a45) * z1);
      				z[5] = (int)(Math.sin(a45) * x1 + Math.cos(a45) * z1);
      				x[5] = w / 2 + (int)(Math.cos(a45) * x2 - Math.sin(a45) * y1);
      				y[5] = h / 2 + (int)(Math.sin(a45) * x2 + Math.cos(a45) * y1);
      						// 社会
      				x1   = a[6] * Math.cos(2 * Math.PI / p[6] * now[6]);
      				y1   = 0;
      				z1   = b[6] * Math.sin(2 * Math.PI / p[6] * now[6]);
      				x2   = (int)(Math.cos(a45) * x1 - Math.sin(a45) * z1);
      				z[6] = (int)(Math.sin(a45) * x1 + Math.cos(a45) * z1);
      				x[6] = w / 2 + (int)(Math.cos(-a45) * x2 - Math.sin(-a45) * y1);
      				y[6] = h / 2 + (int)(Math.sin(-a45) * x2 + Math.cos(-a45) * y1);
      						// ビジネス
      				x1   = a[7] * Math.cos(Math.PI + 2 * Math.PI / p[7] * now[7]);
      				y1   = 0;
      				z1   = b[7] * Math.sin(Math.PI + 2 * Math.PI / p[7] * now[7]);
      				x2   = (int)(Math.cos(-a45) * x1 - Math.sin(-a45) * z1);
      				z[7] = (int)(Math.sin(-a45) * x1 + Math.cos(-a45) * z1);
      				x[7] = w / 2 + (int)(Math.cos(a45) * x2 - Math.sin(a45) * y1);
      				y[7] = h / 2 + (int)(Math.sin(a45) * x2 + Math.cos(a45) * y1);
      						// コミュニケーション
      				x1   = a[8] * Math.cos(Math.PI + 2 * Math.PI / p[8] * now[8]);
      				y1   = 0;
      				z1   = b[8] * Math.sin(Math.PI + 2 * Math.PI / p[8] * now[8]);
      				x2   = (int)(Math.cos(-a45) * x1 - Math.sin(-a45) * z1);
      				z[8] = (int)(Math.sin(-a45) * x1 + Math.cos(-a45) * z1);
      				x[8] = w / 2 + (int)(Math.cos(-a45) * x2 - Math.sin(-a45) * y1);
      				y[8] = h / 2 + (int)(Math.sin(-a45) * x2 + Math.cos(-a45) * y1);
      						// 表示
      				for (i1 = 0; i1 < 9; i1++)
      					s[i1] = 0;
      				for (i1 = 0; i1 < 9; i1++) {
      					m = -1;
      					for (i2 = 0; i2 < 9; i2++) {
      						if (s[i2] == 0 && (m < 0 || z[m] > z[i2]))
      							m = i2;
      					}
      					s[m] = 1;
      					if  (m == 0)
      						g_b.drawImage(bun[0], x[0]-i_w[0]/2, y[0]-i_h[0]/2, this);
      					else {
      						r  = (w / 2.0 + z[m]) / w;
      						sw = (int)(r * i_w[m]);
      						sh = (int)(r * i_h[m]);
      						g_b.drawImage(bun[m], x[m]-sw/2, y[m]-sh/2, sw, sh, this);
      						if (c[m] > 0)
      							c[m]--;
      						else {
      							now[m]++;
      							if (now[m] >= p[m])
      								now[m] = 0;
      						}
      					}
      				}
      				repaint();
      			}
      		}
      	}
      
      	/********/
      	/* 描画 */
      	/********/
      	public void paint (Graphics g)
      	{
      		g.drawImage(Buf, 0, 0, this);      //バッファを画面に描画
      	}
      
      	public void update(Graphics g)    //オーバーライドして最低限のことだけをする
      	{
      		paint(g);
      	}
      
      	/************************/
      	/* マウスイベントの処理 */
      	/************************/
          class Mouse extends MouseAdapter {
              public void mouseClicked(MouseEvent e)
              {
      			int i1, m, r, x1, x2, y1, y2;
      			Explanation exp;
      
                  x1 = e.getX();
                  y1 = e.getY();
      
      			if (x1 > d_x1 && x1 < d_x2 && y1 > d_y1 && y1 < d_y2) {
      				period += 10;
      			}
      			else if (x1 > u_x1 && x1 < u_x2 && y1 > u_y1 && y1 < u_y2) {
      				if (period > 10)
      					period -= 10;
      			}
      			else if (x1 > t_x1 && x1 < t_x2 && y1 > t_y1 && y1 < t_y2) {
      				URL url = null;
      				try {
      					url = new URL("http://ex-cs.sist.ac.jp/~suganuma/dep/index.html");
      				}
      				catch (MalformedURLException e1) {}
      				getAppletContext().showDocument(url, "_blank");
      			}
      			else {
      				m = -1;
      				for (i1 = 0; i1 < 9; i1++) {
      					x2 = x1 - x[i1];
      					y2 = y1 - y[i1];
      					r  = (int)Math.sqrt(x2 * x2 + y2 * y2);
      					if (r < 50 && (m < 0 || z[m] < z[i1]))
      						m = i1;
      				}
      				if (m >= 0)
      					exp = new Explanation(tt[m], i_wt[m], i_ht[m], bun_t[m], x1, y1, sp);
      				else {
      					if (state == 1)
      						state = 2;
      					else
      						state = 1;
      				}
      			}
              }
          }
      }
      				
      Java のソース( Explanation.java )
      /****************************/
      /* 各分野の説明             */
      /*      coded by Y.Suganuma */
      /****************************/
      import java.io.*;
      import java.awt.*;
      import java.awt.event.*;
      
      public class Explanation extends Frame implements Runnable
      {
      
      	int width, height;   // 画像の大きさ
      	int r_width, r_height;   // Windowの大きさ
      	int cx, cy, top, left;
      	int n_sp = 1, sp = 10;
      	Image img;
      	Thread th;
      	boolean state;
      	Insets insets;
      	Dimension scr;
      	Space space;
      
      	/****************************************/
      	/* コンストラクタ                       */
      	/*      title : タイトル                */
      	/*      width_i,height_i : 画像の大きさ */
      	/*      img_i : 画像                    */
      	/*      cx_i, cy_i : クリックした位置   */
      	/****************************************/
      	Explanation(String title, int width_i, int height_i, Image img_i, int cx_i, int cy_i, Space space_i)
      	{
      					// Frameクラスのコンストラクタの呼び出し
      		super(title);
      					// データの保存
      		width  = width_i;
      		height = height_i;
      		img    = img_i;
      		cx     = cx_i;
      		cy     = cy_i;
      		space  = space_i;
      		space.state = 2;
      					// Windowサイズの初期設定
      		int w, h;
      		setSize(0, 0);
      					// ウィンドウを表示
      		setVisible(true);
      		w            = getSize().width;
      		h            = getSize().height;
      		Toolkit tool = getToolkit();
      		scr          = tool.getScreenSize();
      		left         = (scr.width - space.w) / 2 - 10;
      		top          = scr.height - space.h - 30;
      		setLocation(left + cx - w / 2, top + cy - h / 2);
      		insets = getInsets();
      					// イベントアダプタ
      		addWindowListener(new WinEnd());
      		addComponentListener(new ComponentResize());
      					// スレッドの定義と開始
      		state = true;
      		th    = new Thread(this);
      		th.start();
      	}
      
      	/******************/
      	/* スレッドの実行 */
      	/******************/
      	public void run()
      	{
      		int w, h, x, y;
      
      		while (state) {
      			try {
      				th.sleep(50);
      			}
      			catch (InterruptedException e) {}
      			w = n_sp * width / sp + insets.left + insets.right;
      			h = n_sp * height / sp + insets.top + insets.bottom;
      			setSize(w, h);
      			w = getSize().width;
      			h = getSize().height;
      			if (cx > space.w/2)
      				x = left - n_sp * (cx - space.w / 2) / sp + cx - w / 2;
      			else
      				x = left + n_sp * (space.w / 2 - cx) / sp + cx - w / 2;
      			if (cy > space.h/2)
      				y = top - n_sp * (cy - space.h / 2) / sp + cy - h / 2;
      			else
      				y = top + n_sp * (space.h / 2 - cy) / sp + cy - h / 2;
      			setLocation(x, y);
      			n_sp++;
      			if (n_sp > sp)
      				state = false;
      		}
      	}
      
      	/********/
      	/* 描画 */
      	/********/
      	public void paint (Graphics g)
      	{
      		int w, h;
      
      		Dimension d = getSize();
      		h = d.height - (insets.top + insets.bottom);
      		w = (int)(width * (double)h / height);
      		g.drawImage(img, insets.left, insets.top, w, h, this);
      	}
      
      	/**********************/
      	/* Windowのサイズ変化 */
      	/**********************/
      	class ComponentResize extends ComponentAdapter
      	{
      		public void componentResized(ComponentEvent e)
      		{
      			repaint();
      		}
      	}
      
      	/************/
      	/* 終了処理 */
      	/************/
      	class WinEnd extends WindowAdapter
      	{
      		public void windowClosing(WindowEvent e) {
      			space.state = 1;
      			setVisible(false);
      		}
      	}
      }
      				

      Swing を利用した場合

      Java のソース( Space.java )
      import javax.swing.*;
      import java.util.*;
      import java.awt.*;
      import java.awt.event.*;
      import java.io.*;
      import java.net.*;
      
      public class Space extends JApplet
      {
      	Draw_m dm;
      
      	/************/
      	/* 初期設定 */
      	/************/
      	public void init()
      	{
      		int i1;
      					// イメージの読み込み
      		Image bun[]   = new Image [9];
      		Image bun_t[] = new Image [9];
      		MediaTracker trk = new MediaTracker(this);
      		Image back = getImage(getDocumentBase(), "space.jpg");
      		trk.addImage(back, 0);
      		bun[0]   = getImage(getDocumentBase(), "computer.gif");
      		bun[1]   = getImage(getDocumentBase(), "architecture.gif");
      		bun[2]   = getImage(getDocumentBase(), "AI.gif");
      		bun[3]   = getImage(getDocumentBase(), "life.gif");
      		bun[4]   = getImage(getDocumentBase(), "education.gif");
      		bun[5]   = getImage(getDocumentBase(), "media.gif");
      		bun[6]   = getImage(getDocumentBase(), "social.gif");
      		bun[7]   = getImage(getDocumentBase(), "business.gif");
      		bun[8]   = getImage(getDocumentBase(), "communication.gif");
      		bun_t[0] = getImage(getDocumentBase(), "computer_txt.gif");
      		bun_t[1] = getImage(getDocumentBase(), "architecture_txt.gif");
      		bun_t[2] = getImage(getDocumentBase(), "AI_txt.gif");
      		bun_t[3] = getImage(getDocumentBase(), "life_txt.gif");
      		bun_t[4] = getImage(getDocumentBase(), "education_txt.gif");
      		bun_t[5] = getImage(getDocumentBase(), "media_txt.gif");
      		bun_t[6] = getImage(getDocumentBase(), "social_txt.gif");
      		bun_t[7] = getImage(getDocumentBase(), "business_txt.gif");
      		bun_t[8] = getImage(getDocumentBase(), "communication_txt.gif");
      		for (i1 = 0; i1 < 9; i1++) {
      			trk.addImage(bun[i1], 2*i1+1);
      			trk.addImage(bun_t[i1], 2*i1+2);
      		}
      		try {
      			trk.waitForAll();
      		}
      		catch (InterruptedException e1) {}
      					// 描画パネル
      		Container cp = getContentPane();
      		dm = new Draw_m(bun, bun_t, back, this);
      		cp.add(dm);
      	}
      }
      
      class Draw_m extends JPanel implements Runnable {
      
      	int p[], a[], b[];   // 周期,楕円の長径と短径
      	int x[], y[], z[];   // 画像の位置
      	int draw[][];   // 描く図形とその位置
      	int nd;   // 描く図形の数
      	int period = 100;   // 表示間隔
      	int w, h;   // 画面の大きさ
      	int t_x1, t_x2, t_y1, t_y2;   // タイトルの位置
      	int d_x1, d_x2, d_y1, d_y2;   // 減速の位置
      	int u_x1, u_x2, u_y1, u_y2;   // 加速の位置
      	int i_w[], i_wt[], i_h[], i_ht[];   // 画像の幅と高さ
      	boolean dsp[];   // 画像の表示
      	String tt[];
      	Image back, bun[], bun_t[];
      	int state = 0;
      	Thread th;
      	Random rand;
      	Space sp;
      	Draw_m dm = this;
      
      	/******************/
      	/* コンストラクタ */
      	/******************/
      	Draw_m(Image bun1[], Image bun_t1[], Image back1, Space sp1)
      	{
      		bun   = bun1;
      		bun_t = bun_t1;
      		back  = back1;
      		sp    = sp1;
      		w     = sp.getSize().width;
      		h     = sp.getSize().height;
      
      		tt    = new String [9];
      		tt[0] = "コンピュータ";
      		tt[1] = "情報基礎";
      		tt[2] = "人工知能・認知科学";
      		tt[3] = "生命情報";
      		tt[4] = "教育情報";
      		tt[5] = "メディア";
      		tt[6] = "社会情報";
      		tt[7] = "経営・ビジネス";
      		tt[8] = "コミュニケーション";
      
      		i_w  = new int [9];
      		i_h  = new int [9];
      		i_wt = new int [9];
      		i_ht = new int [9];
      		dsp  = new boolean [9];
      		for (int i1 = 0; i1 < 9; i1++) {
      			i_w[i1]  = bun[i1].getWidth(this);
      			i_h[i1]  = bun[i1].getHeight(this);
      			i_wt[i1] = bun_t[i1].getWidth(this);
      			i_ht[i1] = bun_t[i1].getHeight(this);
      			dsp[i1]  = true;
      		}
      					// マウスリスナーの追加
              addMouseListener(new Mouse());
      					// ランダム変数の初期化
      		rand = new Random();
      					// 周期,楕円の長径と短径
      		p = new int [9];
      		a = new int [9];
      		b = new int [9];
      		x = new int [9];
      		y = new int [9];
      		z = new int [9];
      		draw = new int [9][6];
      		for (int i1 = 0; i1 < 9; i1++) {
      			p[i1] = (int)(10 + 70 * rand.nextDouble());
      			if (i1 == 3 || i1 == 4)
      				a[i1] = (int)(3 * h / 8 + (w / 8 + 50) * rand.nextDouble());
      			else
      				a[i1] = (int)(3 * w / 8 + (w / 8 + 50) * rand.nextDouble());
      			b[i1] = (int)(3 * h / 8 + (h / 8 + 50) * rand.nextDouble());
      		}
      					// スレッドの定義と開始
      		state = 1;
      		th    = new Thread(this);
      		th.start();
      	}
      
      	/******************/
      	/* スレッドの実行 */
      	/******************/
      	public void run()
      	{
      		int i1, i2, m = 0, c[], now[], s[], sw, sh;
      		double r, x1, x2, y1, y2, z1, z2, a45 = Math.PI / 4.0;
      					// 初期位置
      		now = new int [9];
      		s   = new int [9];
      		c   = new int [9];
      		for (i1 = 0; i1 < 9; i1++)
      			now[i1] = 0;
      		for (i1 = 1; i1 < 9; i1++)
      			c[i1] = (int)(100 * rand.nextDouble());
      		x[0] = w / 2;
      		y[0] = h / 2;
      		z[0] = 0;
      					// 描画
      		while (state > 0) {
      			try {
      				th.sleep(period);
      			}
      			catch (InterruptedException e) {}
      			if (state == 1) {
      						// アーキテクチャ
      				if (dsp[1]) {
      					x1   = a[1] * Math.cos(2 * Math.PI / p[1] * now[1]);
      					y[1] = h / 2;
      					z1   = b[1] * Math.sin(2 * Math.PI / p[1] * now[1]);
      					x[1] = w / 2 + (int)(Math.cos(a45) * x1 - Math.sin(a45) * z1);
      					z[1] = (int)(Math.sin(a45) * x1 + Math.cos(a45) * z1);
      				}
      						// 人工知能
      				if (dsp[2]) {
      					x1   = a[2] * Math.cos(-Math.PI + 2 * Math.PI / p[2] * now[2]);
      					y[2] = h / 2;
      					z1   = b[2] * Math.sin(-Math.PI + 2 * Math.PI / p[2] * now[2]);
      					x[2] = w / 2 + (int)(Math.cos(-a45) * x1 - Math.sin(-a45) * z1);
      					z[2] = (int)(Math.sin(-a45) * x1 + Math.cos(-a45) * z1);
      				}
      						// 生命
      				if (dsp[3]) {
      					x[3] = w / 2;
      					y1   = a[3] * Math.cos(2 * Math.PI / p[3] * now[3]);
      					z1   = b[3] * Math.sin(2 * Math.PI / p[3] * now[3]);
      					y[3] = h / 2 + (int)(Math.cos(a45) * y1 - Math.sin(a45) * z1);
      					z[3] = (int)(Math.sin(a45) * y1 + Math.cos(a45) * z1);
      				}
      						// 教育
      				if (dsp[4]) {
      					x[4] = w / 2;
      					y1   = a[4] * Math.cos(-Math.PI + 2 * Math.PI / p[4] * now[4]);
      					z1   = b[4] * Math.sin(-Math.PI + 2 * Math.PI / p[4] * now[4]);
      					y[4] = h / 2 + (int)(Math.cos(-a45) * y1 - Math.sin(-a45) * z1);
      					z[4] = (int)(Math.sin(-a45) * y1 + Math.cos(-a45) * z1);
      				}
      						// メディア
      				if (dsp[5]) {
      					x1   = a[5] * Math.cos(2 * Math.PI / p[5] * now[5]);
      					y1   = 0;
      					z1   = b[5] * Math.sin(2 * Math.PI / p[5] * now[5]);
      					x2   = (int)(Math.cos(a45) * x1 - Math.sin(a45) * z1);
      					z[5] = (int)(Math.sin(a45) * x1 + Math.cos(a45) * z1);
      					x[5] = w / 2 + (int)(Math.cos(a45) * x2 - Math.sin(a45) * y1);
      					y[5] = h / 2 + (int)(Math.sin(a45) * x2 + Math.cos(a45) * y1);
      				}
      						// 社会
      				if (dsp[6]) {
      					x1   = a[6] * Math.cos(2 * Math.PI / p[6] * now[6]);
      					y1   = 0;
      					z1   = b[6] * Math.sin(2 * Math.PI / p[6] * now[6]);
      					x2   = (int)(Math.cos(a45) * x1 - Math.sin(a45) * z1);
      					z[6] = (int)(Math.sin(a45) * x1 + Math.cos(a45) * z1);
      					x[6] = w / 2 + (int)(Math.cos(-a45) * x2 - Math.sin(-a45) * y1);
      					y[6] = h / 2 + (int)(Math.sin(-a45) * x2 + Math.cos(-a45) * y1);
      				}
      						// ビジネス
      				if (dsp[7]) {
      					x1   = a[7] * Math.cos(Math.PI + 2 * Math.PI / p[7] * now[7]);
      					y1   = 0;
      					z1   = b[7] * Math.sin(Math.PI + 2 * Math.PI / p[7] * now[7]);
      					x2   = (int)(Math.cos(-a45) * x1 - Math.sin(-a45) * z1);
      					z[7] = (int)(Math.sin(-a45) * x1 + Math.cos(-a45) * z1);
      					x[7] = w / 2 + (int)(Math.cos(a45) * x2 - Math.sin(a45) * y1);
      					y[7] = h / 2 + (int)(Math.sin(a45) * x2 + Math.cos(a45) * y1);
      				}
      						// コミュニケーション
      				if (dsp[8]) {
      					x1   = a[8] * Math.cos(Math.PI + 2 * Math.PI / p[8] * now[8]);
      					y1   = 0;
      					z1   = b[8] * Math.sin(Math.PI + 2 * Math.PI / p[8] * now[8]);
      					x2   = (int)(Math.cos(-a45) * x1 - Math.sin(-a45) * z1);
      					z[8] = (int)(Math.sin(-a45) * x1 + Math.cos(-a45) * z1);
      					x[8] = w / 2 + (int)(Math.cos(-a45) * x2 - Math.sin(-a45) * y1);
      					y[8] = h / 2 + (int)(Math.sin(-a45) * x2 + Math.cos(-a45) * y1);
      				}
      						// 表示
      				nd = 0;
      				for (i1 = 0; i1 < 9; i1++)
      					s[i1] = 0;
      				for (i1 = 0; i1 < 9; i1++) {
      					m = -1;
      					for (i2 = 0; i2 < 9; i2++) {
      						if (s[i2] == 0 && (m < 0 || z[m] > z[i2]))
      							m = i2;
      					}
      					s[m] = 1;
      					if (dsp[m]) {
      						if (m == 0) {
      							draw[nd][0] = 0;
      							draw[nd][1] = 0;
      							draw[nd][2] = x[0] - i_w[0] / 2;
      							draw[nd][3] = y[0] - i_h[0] / 2;
      						}
      						else {
      							r  = (w / 2.0 + z[m]) / w;
      							sw = (int)(r * i_w[m]);
      							sh = (int)(r * i_h[m]);
      							draw[nd][0] = 1;
      							draw[nd][1] = m;
      							draw[nd][2] = x[m] - sw / 2;
      							draw[nd][3] = y[m] - sh / 2;
      							draw[nd][4] = sw;
      							draw[nd][5] = sh;
      							if (c[m] > 0)
      								c[m]--;
      							else {
      								now[m]++;
      								if (now[m] >= p[m])
      									now[m] = 0;
      							}
      						}
      						nd++;
      					}
      				}
      				repaint();
      			}
      		}
      	}
      
      	/************************/
      	/* マウスイベントの処理 */
      	/************************/
          class Mouse extends MouseAdapter {
              public void mouseClicked(MouseEvent e)
              {
      			int i1, m, r, x1, x2, y1, y2;
      			Explanation exp;
      
                  x1 = e.getX();
                  y1 = e.getY();
      
      			if (x1 > d_x1 && x1 < d_x2 && y1 > d_y1 && y1 < d_y2) {
      				period += 10;
      			}
      			else if (x1 > u_x1 && x1 < u_x2 && y1 > u_y1 && y1 < u_y2) {
      				if (period > 10)
      					period -= 10;
      			}
      			else if (x1 > t_x1 && x1 < t_x2 && y1 > t_y1 && y1 < t_y2) {
      				URL url = null;
      				try {
      					url = new URL("http://ex-cs.sist.ac.jp/~suganuma/dep/index.html");
      				}
      				catch (MalformedURLException e1) {}
      				sp.getAppletContext().showDocument(url);
      			}
      			else {
      				m = -1;
      				for (i1 = 0; i1 < 9; i1++) {
      					if (dsp[i1]) {
      						x2 = x1 - x[i1];
      						y2 = y1 - y[i1];
      						r  = (int)Math.sqrt(x2 * x2 + y2 * y2);
      						if (r < 50 && (m < 0 || z[m] < z[i1]))
      							m = i1;
      					}
      				}
      				if (m >= 0) {
      					dsp[m] = false;
      					exp    = new Explanation(tt[m], m, i_wt[m], i_ht[m], bun_t[m], x1-w/2, y1-h/2, dm);
      				}
      				else {
      					if (state == 1)
      						state = 2;
      					else
      						state = 1;
      				}
      			}
              }
          }
      
      	/********/
      	/* 描画 */
      	/********/
      	public void paintComponent (Graphics g)
      	{
      		super.paintComponent(g);   // 親クラスの描画(必ず必要)
      					// 背景画像
      		g.drawImage(back, 0, 0, w, h, this);
      		g.setColor(Color.cyan);
      		Font f = new Font("TimesRoman", Font.BOLD, 25);
      		g.setFont(f);
      		FontMetrics fm = g.getFontMetrics(f);
      		int c_w = fm.stringWidth("情報学部");
      		int c_h = fm.getHeight();
      		Insets insets = getInsets();
      		t_x1 = w / 2 - c_w / 2;
      		t_x2 = t_x1 + c_w;
      		t_y1 = insets.top;
      		t_y2 = t_y1 + c_h;
      		g.drawString("情報学部", t_x1, t_y2);
      		g.setColor(Color.yellow);
      		f = new Font("TimesRoman", Font.BOLD, 15);
      		g.setFont(f);
      		fm   = g.getFontMetrics(f);
      		c_w  = fm.stringWidth("<< 減速");
      		c_h  = fm.getHeight();
      		d_x1 = t_x1 - c_w - 10;
      		d_x2 = d_x1 + c_w;
      		d_y1 = t_y2 - c_h;
      		d_y2 = t_y2;
      		g.drawString("<< 減速", d_x1, d_y2);
      		u_x1 = t_x2 + 10;
      		u_x2 = u_x1 + c_w;
      		u_y1 = t_y2 - c_h;
      		u_y2 = t_y2;
      		g.drawString("加速 >>", u_x1, u_y2);
      					// 対象物
      		for (int i1 = 0; i1 < nd; i1++) {
      			if (draw[i1][0] == 0)
      				g.drawImage(bun[draw[i1][1]], draw[i1][2], draw[i1][3], this);
      			else
      				g.drawImage(bun[draw[i1][1]], draw[i1][2], draw[i1][3], draw[i1][4], draw[i1][5], this);
      		}
      	}
      }
      				
      Java のソース( Explanation.java )
      /****************************/
      /* 各分野の説明             */
      /*      coded by Y.Suganuma */
      /****************************/
      import java.io.*;
      import java.awt.*;
      import javax.swing.*;
      import java.awt.event.*;
      
      class Explanation extends JFrame implements Runnable
      {
      
      	int width, height;   // 画像の大きさ
      	int r_width, r_height;   // Windowの大きさ
      	int cx, cy, s_x = 0, s_y = 0;
      	int m, n_sp = 1, sp = 10;
      	Image img;
      	Thread th;
      	boolean state;
      	Insets insets;
      	Dimension scr;
      	Draw_m dm;
      	Draw_e de;
      
      	/****************************************/
      	/* コンストラクタ                       */
      	/*      title : タイトル                */
      	/*      m_i : 画像番号                  */
      	/*      width_i,height_i : 画像の大きさ */
      	/*      img_i : 画像                    */
      	/*      cx_i, cy_i : クリックした位置   */
      	/****************************************/
      	Explanation(String title, int m_i, int width_i, int height_i, Image img_i, int cx_i, int cy_i, Draw_m dm_i)
      	{
      					// Frameクラスのコンストラクタの呼び出し
      		super(title);
      					// データの保存
      		m      = m_i;
      		width  = width_i;
      		height = height_i;
      		img    = img_i;
      		cx     = cx_i;
      		cy     = cy_i;
      		dm     = dm_i;
      		dm.state = 2;
      					// Windowサイズの初期設定
      		int w, h;
      		setSize(0, 0);
      					// ウィンドウを表示
      		setVisible(true);
      		w            = getSize().width;
      		h            = getSize().height;
      		Toolkit tool = getToolkit();
      		scr          = tool.getScreenSize();
      		if (cx < -100) {
      			if (cy < -100) {
      				s_x = 0;
      				s_y = 100;
      			}
      			else if (cy > 100) {
      				s_x = 0;
      				s_y = scr.height - 100;
      			}
      			else {
      				s_x = 0;
      				s_y = scr.height / 2;
      			}
      		}
      		else if (cx > 100) {
      			if (cy < -100) {
      				s_x = scr.width;
      				s_y = 100;
      			}
      			else if (cy > 100) {
      				s_x = scr.width;
      				s_y = scr.height - 100;
      			}
      			else {
      				s_x = scr.width;
      				s_y = scr.height / 2;
      			}
      		}
      		else {
      			if (cy < -100) {
      				s_x = scr.width / 2;
      				s_y = 100;
      			}
      			else if (cy > 100) {
      				s_x = scr.width / 2;
      				s_y = scr.height - 100;
      			}
      			else {
      				s_x = scr.width / 2;
      				s_y = scr.height / 2;
      			}
      		}
      		setLocation(s_x - w / 2, s_y - h / 2);
      		insets = getInsets();
      					// 描画パネル
      		Container cp = getContentPane();
      		de = new Draw_e(this);
      		cp.add(de);
      					// イベントアダプタ
      		addWindowListener(new WinEnd());
      		addComponentListener(new ComponentResize());
      					// スレッドの定義と開始
      		state = true;
      		th    = new Thread(this);
      		th.start();
      	}
      
      	/******************/
      	/* スレッドの実行 */
      	/******************/
      	public void run()
      	{
      		int w, h, x, y;
      
      		while (state) {
      			try {
      				th.sleep(50);
      			}
      			catch (InterruptedException e) {}
      			w = n_sp * width / sp + insets.left + insets.right;
      			h = n_sp * height / sp + insets.top + insets.bottom;
      			setSize(w, h);
      			w = getSize().width;
      			h = getSize().height;
      			if (cx > scr.width/2)
      				x = s_x - n_sp * (scr.width / 2 - s_x) / sp - w / 2;
      			else
      				x = s_x + n_sp * (scr.width / 2 - s_x) / sp - w / 2;
      			if (cy > scr.height/2)
      				y = s_y - n_sp * (scr.height / 2 - s_y) / sp - h / 2;
      			else
      				y = s_y + n_sp * (scr.height / 2 - s_y) / sp - h / 2;
      			setLocation(x, y);
      			n_sp++;
      			if (n_sp > sp)
      				state = false;
      		}
      	}
      
      	/**********************/
      	/* Windowのサイズ変化 */
      	/**********************/
      	class ComponentResize extends ComponentAdapter
      	{
      		public void componentResized(ComponentEvent e)
      		{
      			de.repaint();
      		}
      	}
      
      	/************/
      	/* 終了処理 */
      	/************/
      	class WinEnd extends WindowAdapter
      	{
      		public void windowClosing(WindowEvent e) {
      			dm.state  = 1;
      			dm.dsp[m] = true;
      			setVisible(false);
      		}
      	}
      }
      
      class Draw_e extends JPanel {
      
      	Explanation ex;
      
      	/******************/
      	/* コンストラクタ */
      	/******************/
      	Draw_e(Explanation ex_i)
      	{
      		ex = ex_i;
      		setBackground(Color.white);
      	}
      
      	/********/
      	/* 描画 */
      	/********/
      	public void paintComponent (Graphics g)
      	{
      		super.paintComponent(g);   // 親クラスの描画(必ず必要)
      		int w, h;
      
      		Dimension d = ex.getSize();
      		h = d.height - (ex.insets.top + ex.insets.bottom);
      		w = (int)(ex.width * (double)h / ex.height);
      		g.drawImage(ex.img, ex.insets.left, ex.insets.top/3, w, h, this);
      	}
      }
      				

  2. ゲーム

      ゲームに関しては,Java によるゲームプログラミングも参考にして下さい.より多くのゲームに対し,より詳細な説明をしています.

    1. シューティングゲーム

        このアプレット(「AWT の利用」,「Swing の利用」)は,キーイベントを使用した簡単なシューティング風ゲームです.左右の矢印キーによって下中央に描かれた黒い矩形(砲台)を左右に動かすことができます.Shift キーをクリックするとレーザ砲が発射され,ターゲット(緑の円)に命中すると,ターゲットの色が一時的にピンクに変化し消滅します.また,ターゲットが,黒い矩形に当たるとゲームオーバーになります.

        ここでは,複数人で作成する大きなプログラムではなく,一人で比較的小さなプログラムを作成する場合について,その作成手順について考えてみます.少なくとも,すべてのプログラムを作成してからコンパイルし,実行してみるといった方法はあまり良い方法ではありません.作成するプログラムにもよりますが,私は,部分的な機能を実現するプログラムを作成し,その機能を確認した後,新しい機能を追加していくといった方法をよく利用します.たとえば,この例の場合は,以下のような手順になります.

      1. ターゲットと砲台の表示(「AWT の利用」,「Swing の利用」):  どのような位置に表示しても構いませんが,後から,これらの位置を変更する必要がありますので,表示位置は変数((xt, yt) と (x, y))で与えておいた方がよいと思います.また,このプログラムでは,変数 game を false に設定することによって,ゲームオーバー画面も確認することができます.

        AWT を利用した場合

        01	/************************/
        02	/* シューティングゲーム */
        03	/************************/
        04	import java.awt.*;
        05	import java.applet.*;
        06	
        07	public class Game1 extends Applet
        08	{
        09		boolean game = true;   // ゲーム実行中か否か
        10		int xt = 20, yt = 50;   // ターゲットの位置
        11		int x, y;   // 砲台の位置
        12		int r = 25;   // ターゲットの半径
        13		Dimension d;
        14	
        15		public void init() {
        16						// 背景色
        17			setBackground(new Color(238, 255, 238));
        18						// 初期設定
        19			d  = getSize();
        20			x  = d.width / 2 - 10;
        21			y  = d.height - 20;
        22		}
        23	
        24		public Insets getInsets()
        25		{
        26			return new Insets(0, 0, 0, 0);
        27		}
        28	
        29		public void paint (Graphics g)
        30		{
        31							// ゲーム中
        32			if (game) {
        33									// 砲台の表示
        34				g.fill3DRect(x, y, 20, 20, true);
        35									// ターゲットの表示
        36				g.setColor(Color.green);
        37				g.fillOval(xt, yt, 2*r, 2*r);
        38			}
        39							// ゲームオーバ
        40			else {
        41				Font f = new Font("TimesRoman", Font.BOLD, 50);
        42				g.setFont(f);
        43				g.drawString("Game Over", d.width/2-130, d.height/2);
        44			}
        45		}
        46	}
        					
        34 行目

          20 行目~ 21 行目で指定した位置に,砲台(塗りつぶした黒の矩形)を描画

        36 行目~ 37 行目

          10 行目で指定した位置に,ターゲット(塗りつぶした緑の円)を描画

        41 行目~ 43 行目

          画面に「 Game Over 」を表示し,ゲームオーバーであることを示す.

        Swing を利用した場合

        01	/************************/
        02	/* シューティングゲーム */
        03	/************************/
        04	import java.awt.*;
        05	import javax.swing.*;
        06	
        07	public class Game1 extends JApplet
        08	{
        09		public void init() {
        10						// 背景色
        11			setBackground(new Color(238, 255, 238));
        12						// 初期設定
        13			Container cp = getContentPane();
        14			cp.setBackground(Color.white);
        15			Draw dp = new Draw(this);
        16			dp.setBackground(new Color(238, 255, 238));
        17			cp.add(dp, BorderLayout.CENTER);
        18		}
        19	}
        20	
        21	class Draw extends JPanel
        22	{
        23		boolean game = true;   // ゲーム実行中か否か
        24		int xt = 20, yt = 50;   // ターゲットの位置
        25		int x, y;   // 砲台の位置
        26		int r = 25;   // ターゲットの半径
        27		Dimension d;
        28		Insets in;
        29		Game1 gm;
        30	
        31		Draw(Game1 gm1) {
        32			gm = gm1;
        33						// 初期設定
        34			in = gm.getInsets();
        35			d  = gm.getSize();
        36			d.width  = d.width - in.left - in.right;
        37			d.height = d.height - in.top - in.bottom;
        38			x  = d.width / 2 - 10;
        39			y  = d.height - 20;
        40		}
        41	
        42		public void paintComponent (Graphics g)
        43		{
        44			super.paintComponent(g);   // 親クラスの描画(必ず必要)
        45	
        46							// ゲーム中
        47			if (game) {
        48									// 砲台の表示
        49				g.fill3DRect(x, y, 20, 20, true);
        50									// ターゲットの表示
        51				g.setColor(Color.green);
        52				g.fillOval(xt, yt, 2*r, 2*r);
        53			}
        54							// ゲームオーバ
        55			else {
        56				Font f = new Font("TimesRoman", Font.BOLD, 50);
        57				g.setFont(f);
        58				g.drawString("Game Over", d.width/2-130, d.height/2);
        59			}
        60		}
        61	}
        					
        49 行目

          38 行目~ 39 行目で指定した位置に,砲台(塗りつぶした黒の矩形)を描画

        51 行目~ 52 行目

          24 行目で指定した位置に,ターゲット(塗りつぶした緑の円)を描画

        56 行目~ 58 行目

          画面に「 Game Over 」を表示し,ゲームオーバーであることを示す.

      2. ターゲットの移動(「AWT の利用」,「Swing の利用」):  スレッドを利用して,33 ms 毎に,ターゲットを下方向へ移動してみます.Runnable インターフェースを継承し,run メソッドの中でターゲットの y 座標を変数 sp に設定されている値だけ増加していくことになります.前のステップで作成したプログラムを少し変更するだけですが,スレッドを利用したアニーションの機能を確認することができます.

        AWT を利用した場合

        01	/************************/
        02	/* シューティングゲーム */
        03	/************************/
        04	import java.awt.*;
        05	import java.applet.*;
        06	
        07	public class Game1 extends Applet implements Runnable
        08	{
        09		boolean state = true;   // スレッドが動作中か否か
        10		boolean game = true;   // ゲーム実行中か否か
        11		int xt = 20, yt = 50;   // ターゲットの位置
        12		int x, y;   // 砲台の位置
        13		int r = 25;   // ターゲットの半径
        14		int sp = 5;   // ターゲットの速さ
        15		Thread th;
        16		Dimension d;
        17	
        18		public void init() {
        19						// 背景色
        20			setBackground(new Color(238, 255, 238));
        21						// 初期設定
        22			d  = getSize();
        23			x  = d.width / 2 - 10;
        24			y  = d.height - 20;
        25						// スレッドの生成
        26			th = new Thread(this);
        27			th.start();
        28		}
        29	
        30		public Insets getInsets()
        31		{
        32			return new Insets(0, 0, 0, 0);
        33		}
        34	
        35		public void stop()
        36		{
        37			state = false;
        38		}
        39	
        40		public void run()
        41		{
        42			while (state) {
        43				try {
        44					th.sleep(33);
        45				}
        46				catch (InterruptedException e) {}
        47							// ターゲットの移動
        48				yt += sp;
        49							// 再描画
        50				repaint();
        51			}
        52		}
        53	
        54		public void paint (Graphics g)
        55		{
        56							// ゲーム中
        57			if (game) {
        58									// 砲台の表示
        59				g.fill3DRect(x, y, 20, 20, true);
        60									// ターゲットの表示
        61				g.setColor(Color.green);
        62				g.fillOval(xt, yt, 2*r, 2*r);
        63			}
        64							// ゲームオーバ
        65			else {
        66				Font f = new Font("TimesRoman", Font.BOLD, 50);
        67				g.setFont(f);
        68				g.drawString("Game Over", d.width/2-130, d.height/2);
        69			}
        70		}
        71	}
        					
        07 行目

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

        26 行目~ 27 行目

          Thread クラスのオブジェクトを生成し,それをスタートさせている.

        40 行目~ 52 行目

          Thread クラスのメソッドであり,スレッドを 33 ms 毎に実行することになる( 44 行目).実行する内容は,ピースの y 座標を変化させ( 48 行目),再描画しているだけである.

        Swing を利用した場合

        01	/************************/
        02	/* シューティングゲーム */
        03	/************************/
        04	import java.awt.*;
        05	import javax.swing.*;
        06	
        07	public class Game1 extends JApplet
        08	{
        09		Draw dp;
        10		public void init() {
        11						// 背景色
        12			setBackground(new Color(238, 255, 238));
        13						// 初期設定
        14			Container cp = getContentPane();
        15			cp.setBackground(Color.white);
        16			dp = new Draw(this);
        17			dp.setBackground(new Color(238, 255, 238));
        18			cp.add(dp, BorderLayout.CENTER);
        19		}
        20	
        21		public void stop()
        22		{
        23			dp.state = false;
        24		}
        25	}
        26	
        27	class Draw extends JPanel implements Runnable
        28	{
        29		boolean state = true;   // スレッドが動作中か否か
        30		boolean game = true;   // ゲーム実行中か否か
        31		int xt = 20, yt = 50;   // ターゲットの位置
        32		int x, y;   // 砲台の位置
        33		int r = 25;   // ターゲットの半径
        34		int sp = 5;   // ターゲットの速さ
        35		Thread th;
        36		Dimension d;
        37		Insets in;
        38		Game1 gm;
        39	
        40		Draw(Game1 gm1) {
        41			gm = gm1;
        42						// 初期設定
        43			in = gm.getInsets();
        44			d  = gm.getSize();
        45			d.width  = d.width - in.left - in.right;
        46			d.height = d.height - in.top - in.bottom;
        47			x  = d.width / 2 - 10;
        48			y  = d.height - 20;
        49						// スレッドの生成
        50			th = new Thread(this);
        51			th.start();
        52		}
        53	
        54		public void run()
        55		{
        56			while (state) {
        57				try {
        58					th.sleep(33);
        59				}
        60				catch (InterruptedException e) {}
        61							// ターゲットの移動
        62				yt += sp;
        63							// 再描画
        64				repaint();
        65			}
        66		}
        67	
        68		public void paintComponent (Graphics g)
        69		{
        70			super.paintComponent(g);   // 親クラスの描画(必ず必要)
        71	
        72							// ゲーム中
        73			if (game) {
        74									// 砲台の表示
        75				g.fill3DRect(x, y, 20, 20, true);
        76									// ターゲットの表示
        77				g.setColor(Color.green);
        78				g.fillOval(xt, yt, 2*r, 2*r);
        79			}
        80							// ゲームオーバ
        81			else {
        82				Font f = new Font("TimesRoman", Font.BOLD, 50);
        83				g.setFont(f);
        84				g.drawString("Game Over", d.width/2-130, d.height/2);
        85			}
        86		}
        87	}
        					
        27 行目

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

        50 行目~ 51 行目

          Thread クラスのオブジェクトを生成し,それをスタートさせている.

        54 行目~ 66 行目

          Thread クラスのメソッドであり,スレッドを 33 ms 毎に実行することになる( 58 行目).実行する内容は,ピースの y 座標を変化させ( 62 行目),再描画しているだけである.

      3. ターゲットの生成・移動・消滅(「AWT の利用」,「Swing の利用」):  ターゲットは,

        • 画面上に存在し,移動している状態( target = 1 )
        • 画面上に存在しない状態( target = 0 )
        • 命中し色が変わった状態( target = 2 )

        という 3 つの状態のいずれかになります.その状態を記憶しているのが変数 target です.ここでは,ターゲットを画面上部の任意の場所に生成し,画面の外に出たら消滅するように変更します.消滅すると,再び,新しいターゲットが生成されます.また,移動方向は,π/4 ~ 3π/4 の間の値からランダムに選択します.なお,位置や方向をランダムにするために,Random クラスを使用しています.

        AWT を利用した場合

        01	/************************/
        02	/* シューティングゲーム */
        03	/************************/
        04	import java.awt.*;
        05	import java.applet.*;
        06	import java.util.*;
        07	
        08	public class Game1 extends Applet implements Runnable
        09	{
        10		boolean state = true;   // スレッドが動作中か否か
        11		boolean game = true;   // ゲーム実行中か否か
        12		int xt = 20, yt = 50;   // ターゲットの位置
        13		int x, y;   // 砲台の位置
        14		int r = 25;   // ターゲットの半径
        15		int sp = 5;   // ターゲットの速さ
        16		int vx, vy;   // ターゲットの x 軸,及び,y 軸方向の速さ
        17		int target = 0;   // ターゲットの状態(0:存在しない,1:移動中,2:命中)
        18		Thread th;
        19		Dimension d;
        20		Random rn;
        21	
        22		public void init() {
        23						// 背景色
        24			setBackground(new Color(238, 255, 238));
        25						// 初期設定
        26			d  = getSize();
        27			x  = d.width / 2 - 10;
        28			y  = d.height - 20;
        29			rn = new Random();
        30						// スレッドの生成
        31			th = new Thread(this);
        32			th.start();
        33		}
        34	
        35		public Insets getInsets()
        36		{
        37			return new Insets(0, 0, 0, 0);
        38		}
        39	
        40		public void stop()
        41		{
        42			state = false;
        43		}
        44	
        45		public void run()
        46		{
        47			double ang;
        48	
        49			while (state) {
        50				try {
        51					th.sleep(33);
        52				}
        53				catch (InterruptedException e) {}
        54							// ターゲットの移動と消滅
        55				if (target == 1) {
        56					xt += vx;
        57					yt += vy;
        58					if (xt < -2*r || xt > d.width || yt > d.height)
        59						target = 0;
        60				}
        61							// ターゲットの生成
        62				else if (target == 0){
        63					target = 1;
        64					xt     = (int)((d.width - 2 * r) * rn.nextDouble());
        65					yt     = -r;
        66					ang    = 0.5 * Math.PI * rn.nextDouble() + 0.25 * Math.PI;
        67					vx     = (int)(sp * Math.cos(ang));
        68					vy     = (int)(sp * Math.sin(ang));
        69				}
        70							// 再描画
        71				repaint();
        72			}
        73		}
        74	
        75		public void paint (Graphics g)
        76		{
        77							// ゲーム中
        78			if (game) {
        79									// 砲台の表示
        80				g.fill3DRect(x, y, 20, 20, true);
        81									// ターゲットの表示
        82				g.setColor(Color.green);
        83				g.fillOval(xt, yt, 2*r, 2*r);
        84			}
        85							// ゲームオーバ
        86			else {
        87				Font f = new Font("TimesRoman", Font.BOLD, 50);
        88				g.setFont(f);
        89				g.drawString("Game Over", d.width/2-130, d.height/2);
        90			}
        91		}
        92	}
        					
        56 行目~ 59 行目

          ターゲットが存在する場合の処理である.各軸方向の速度を利用して,ターゲットの x 座標,及び,y 座標の値を変化させ,画面外に出た場合は消滅させる.

        62 行目~ 69 行目

          ターゲットが存在しないときの処理であり,新しいターゲットを生成している.63 行目において,変数 target の値を 1 に設定し,64 行目~ 65 行目において,ターゲットの初期位置を決めている.ただし,x 座標はランダムに選択される.また,66 行目において,ターゲットが進む方向がランダムに選択され,67 行目~ 68 行目において,その角度から各軸方向の速度を計算している.

        Swing を利用した場合

        001	/************************/
        002	/* シューティングゲーム */
        003	/************************/
        004	import java.awt.*;
        005	import javax.swing.*;
        006	import java.util.*;
        007	
        008	public class Game1 extends JApplet
        009	{
        010		Draw dp;
        011		public void init() {
        012						// 背景色
        013			setBackground(new Color(238, 255, 238));
        014						// 初期設定
        015			Container cp = getContentPane();
        016			cp.setBackground(Color.white);
        017			dp = new Draw(this);
        018			dp.setBackground(new Color(238, 255, 238));
        019			cp.add(dp, BorderLayout.CENTER);
        020		}
        021	
        022		public void stop()
        023		{
        024			dp.state = false;
        025		}
        026	}
        027	
        028	class Draw extends JPanel implements Runnable
        029	{
        030		boolean state = true;   // スレッドが動作中か否か
        031		boolean game = true;   // ゲーム実行中か否か
        032		int xt = 20, yt = 50;   // ターゲットの位置
        033		int x, y;   // 砲台の位置
        034		int r = 25;   // ターゲットの半径
        035		int sp = 5;   // ターゲットの速さ
        036		int vx, vy;   // ターゲットの x 軸,及び,y 軸方向の速さ
        037		int target = 0;   // ターゲットの状態(0:存在しない,1:移動中,2:命中)
        038		Thread th;
        039		Dimension d;
        040		Insets in;
        041		Random rn;
        042		Game1 gm;
        043	
        044		Draw(Game1 gm1) {
        045			gm = gm1;
        046						// 初期設定
        047			in = gm.getInsets();
        048			d  = gm.getSize();
        049			d.width  = d.width - in.left - in.right;
        050			d.height = d.height - in.top - in.bottom;
        051			x  = d.width / 2 - 10;
        052			y  = d.height - 20;
        053			rn = new Random();
        054						// スレッドの生成
        055			th = new Thread(this);
        056			th.start();
        057		}
        058	
        059		public void run()
        060		{
        061			double ang;
        062	
        063			while (state) {
        064				try {
        065					th.sleep(33);
        066				}
        067				catch (InterruptedException e) {}
        068							// ターゲットの移動と消滅
        069				if (target == 1) {
        070					xt += vx;
        071					yt += vy;
        072					if (xt < -2*r || xt > d.width || yt > d.height)
        073						target = 0;
        074				}
        075							// ターゲットの生成
        076				else if (target == 0){
        077					target = 1;
        078					xt     = (int)((d.width - 2 * r) * rn.nextDouble());
        079					yt     = -r;
        080					ang    = 0.5 * Math.PI * rn.nextDouble() + 0.25 * Math.PI;
        081					vx     = (int)(sp * Math.cos(ang));
        082					vy     = (int)(sp * Math.sin(ang));
        083				}
        084							// 再描画
        085				repaint();
        086			}
        087		}
        088	
        089		public void paintComponent (Graphics g)
        090		{
        091			super.paintComponent(g);   // 親クラスの描画(必ず必要)
        092	
        093							// ゲーム中
        094			if (game) {
        095									// 砲台の表示
        096				g.fill3DRect(x, y, 20, 20, true);
        097									// ターゲットの表示
        098				g.setColor(Color.green);
        099				g.fillOval(xt, yt, 2*r, 2*r);
        100			}
        101							// ゲームオーバ
        102			else {
        103				Font f = new Font("TimesRoman", Font.BOLD, 50);
        104				g.setFont(f);
        105				g.drawString("Game Over", d.width/2-130, d.height/2);
        106			}
        107		}
        108	}
        					
        069 行目~ 074 行目

          ターゲットが存在する場合の処理である.各軸方向の速度を利用して,ターゲットの x 座標,及び,y 座標の値を変化させ,画面外に出た場合は消滅させる.

        076 行目~ 083 行目

          ターゲットが存在しないときの処理であり,新しいターゲットを生成している.077 行目において,変数 target の値を 1 に設定し,078 行目~ 079 行目において,ターゲットの初期位置を決めている.ただし,x 座標はランダムに選択される.また,080 行目において,ターゲットが進む方向がランダムに選択され,081 行目~ 082 行目において,その角度から各軸方向の速度を計算している.

      4. ゲームオーバー(「AWT の利用」,「Swing の利用」):  このゲームでは,ターゲットと砲台が衝突するとゲームオーバーとなります.ここでは,その判定を追加します.まだ,砲台を移動できませんので,砲台に衝突するようなターゲットが現れるまでしばらく待ち,機能を確認します.

        AWT を利用した場合

        001	/************************/
        002	/* シューティングゲーム */
        003	/************************/
        004	import java.awt.*;
        005	import java.applet.*;
        006	import java.util.*;
        007	
        008	public class Game1 extends Applet implements Runnable
        009	{
        010		boolean state = true;   // スレッドが動作中か否か
        011		boolean game = true;   // ゲーム実行中か否か
        012		int xt = 20, yt = 50;   // ターゲットの位置
        013		int x, y;   // 砲台の位置
        014		int r = 25;   // ターゲットの半径
        015		int sp = 5;   // ターゲットの速さ
        016		int vx, vy;   // ターゲットの x 軸,及び,y 軸方向の速さ
        017		int target = 0;   // ターゲットの状態(0:存在しない,1:移動中,2:命中)
        018		Thread th;
        019		Dimension d;
        020		Random rn;
        021	
        022		public void init() {
        023						// 背景色
        024			setBackground(new Color(238, 255, 238));
        025						// 初期設定
        026			d  = getSize();
        027			x  = d.width / 2 - 10;
        028			y  = d.height - 20;
        029			rn = new Random();
        030						// スレッドの生成
        031			th = new Thread(this);
        032			th.start();
        033		}
        034	
        035		public Insets getInsets()
        036		{
        037			return new Insets(0, 0, 0, 0);
        038		}
        039	
        040		public void stop()
        041		{
        042			state = false;
        043		}
        044	
        045		public void run()
        046		{
        047			double ang, x1, y1;
        048	
        049			while (state) {
        050				try {
        051					th.sleep(33);
        052				}
        053				catch (InterruptedException e) {}
        054							// ターゲットの移動と消滅
        055				if (target == 1) {
        056									// ゲームオーバ
        057					x1 = xt + r - (x + 10);
        058					y1 = yt + r - (y + 10);
        059					if (Math.sqrt(x1*x1+y1*y1) < r+10)
        060						game = false;
        061									// ゲームオーバでない
        062					else {
        063						xt += vx;
        064						yt += vy;
        065						if (xt < -2*r || xt > d.width || yt > d.height)
        066							target = 0;
        067					}
        068				}
        069							// ターゲットの生成
        070				else if (target == 0){
        071					target = 1;
        072					xt     = (int)((d.width - 2 * r) * rn.nextDouble());
        073					yt     = -r;
        074					ang    = 0.5 * Math.PI * rn.nextDouble() + 0.25 * Math.PI;
        075					vx     = (int)(sp * Math.cos(ang));
        076					vy     = (int)(sp * Math.sin(ang));
        077				}
        078							// 再描画
        079				repaint();
        080			}
        081		}
        082	
        083		public void paint (Graphics g)
        084		{
        085							// ゲーム中
        086			if (game) {
        087									// 砲台の表示
        088				g.fill3DRect(x, y, 20, 20, true);
        089									// ターゲットの表示
        090				g.setColor(Color.green);
        091				g.fillOval(xt, yt, 2*r, 2*r);
        092			}
        093							// ゲームオーバ
        094			else {
        095				Font f = new Font("TimesRoman", Font.BOLD, 50);
        096				g.setFont(f);
        097				g.drawString("Game Over", d.width/2-130, d.height/2);
        098				state = false;
        099			}
        100		}
        101	}
        					
        057 行目~ 060 行目

          ターゲットが砲台に衝突したか否かの判定を行い,衝突した場合は,ゲームオーバーにしている.

        098 行目

          スレッドを停止している.

        Swing を利用した場合

        001	/************************/
        002	/* シューティングゲーム */
        003	/************************/
        004	import java.awt.*;
        005	import javax.swing.*;
        006	import java.util.*;
        007	
        008	public class Game1 extends JApplet
        009	{
        010		Draw dp;
        011		public void init() {
        012						// 背景色
        013			setBackground(new Color(238, 255, 238));
        014						// 初期設定
        015			Container cp = getContentPane();
        016			cp.setBackground(Color.white);
        017			dp = new Draw(this);
        018			dp.setBackground(new Color(238, 255, 238));
        019			cp.add(dp, BorderLayout.CENTER);
        020		}
        021	
        022		public void stop()
        023		{
        024			dp.state = false;
        025		}
        026	}
        027	
        028	class Draw extends JPanel implements Runnable
        029	{
        030		boolean state = true, game = true;
        031		int xt, yt, vx, vy, x, y, r = 25, sp = 5, target = 0;
        032		Thread th;
        033		Dimension d;
        034		Insets in;
        035		Random rn;
        036		Game1 gm;
        037	
        038		Draw(Game1 gm1) {
        039			gm = gm1;
        040						// 初期設定
        041			in = gm.getInsets();
        042			d  = gm.getSize();
        043			d.width  = d.width - in.left - in.right;
        044			d.height = d.height - in.top - in.bottom;
        045			x  = d.width / 2 - 10;
        046			y  = d.height - 20;
        047			rn = new Random();
        048						// スレッドの生成
        049			th = new Thread(this);
        050			th.start();
        051		}
        052	
        053		public void run()
        054		{
        055			double ang, x1, y1;
        056	
        057			while (state) {
        058				try {
        059					th.sleep(33);
        060				}
        061				catch (InterruptedException e) {}
        062							// ターゲットの移動と消滅
        063				if (target == 1) {
        064									// ゲームオーバ
        065					x1 = xt + r - (x + 10);
        066					y1 = yt + r - (y + 10);
        067					if (Math.sqrt(x1*x1+y1*y1) < r+10)
        068						game = false;
        069									// ゲームオーバでない
        070					else {
        071						xt += vx;
        072						yt += vy;
        073						if (xt < -2*r || xt > d.width || yt > d.height)
        074							target = 0;
        075					}
        076				}
        077							// ターゲットの生成
        078				else if (target == 0){
        079					target = 1;
        080					xt     = (int)((d.width - 2 * r) * rn.nextDouble());
        081					yt     = -r;
        082					ang    = 0.5 * Math.PI * rn.nextDouble() + 0.25 * Math.PI;
        083					vx     = (int)(sp * Math.cos(ang));
        084					vy     = (int)(sp * Math.sin(ang));
        085				}
        086							// 再描画
        087				repaint();
        088			}
        089		}
        090	
        091		public void paintComponent (Graphics g)
        092		{
        093			super.paintComponent(g);   // 親クラスの描画(必ず必要)
        094	
        095							// ゲーム中
        096			if (game) {
        097									// 砲台の表示
        098				g.fill3DRect(x, y, 20, 20, true);
        099									// ターゲットの表示
        100				g.setColor(Color.green);
        101				g.fillOval(xt, yt, 50, 50);
        102			}
        103							// ゲームオーバ
        104			else {
        105				Font f = new Font("TimesRoman", Font.BOLD, 50);
        106				g.setFont(f);
        107				g.drawString("Game Over", d.width/2-130, d.height/2);
        108				state = false;
        109			}
        110		}
        111	}
        					
        065 行目~ 068 行目

          ターゲットが砲台に衝突したか否かの判定を行い,衝突した場合は,ゲームオーバーにしている.

        108 行目

          スレッドを停止している.

      5. キーイベント(「AWT の利用」,「Swing の利用」:  最後に,キーイベントに対する処理を追加します.左矢印または右矢印キーを押すと,砲台が左または右に 20 ピクセルだけ移動します.また,Shift キーを押すと,レーザ光が発射されます(変数 fire を true にする).

        AWT を利用した場合

        001	/************************/
        002	/* シューティングゲーム */
        003	/************************/
        004	import java.awt.*;
        005	import java.awt.event.*;
        006	import java.applet.*;
        007	import java.util.*;
        008	
        009	public class Game1 extends Applet implements Runnable
        010	{
        011		boolean fire = false;   // レーザ砲が発射されているか否か
        012		boolean state = true;   // スレッドが動作中か否か
        013		boolean game = true;   // ゲーム実行中か否か
        014		int xt = 20, yt = 50;   // ターゲットの位置
        015		int x, y;   // 砲台の位置
        016		int r = 25;   // ターゲットの半径
        017		int sp = 5;   // ターゲットの速さ
        018		int vx, vy;   // ターゲットの x 軸,及び,y 軸方向の速さ
        019		int target = 0;   // ターゲットの状態(0:存在しない,1:移動中,2:命中)
        020		Dimension d;
        021		Thread th;
        022		Random rn;
        023	
        024		public void init() {
        025						// 背景色
        026			setBackground(new Color(238, 255, 238));
        027						// 初期設定
        028			d  = getSize();
        029			x  = d.width / 2 - 10;
        030			y  = d.height - 20;
        031			rn = new Random();
        032						// キーリスナの付加
        033			addKeyListener(new Key_e());
        034						// スレッドの生成
        035			th = new Thread(this);
        036			th.start();
        037		}
        038	
        039		public Insets getInsets()
        040		{
        041			return new Insets(0, 0, 0, 0);
        042		}
        043	
        044		public void stop()
        045		{
        046			state = false;
        047		}
        048	
        049		public void run()
        050		{
        051			double ang, x1, y1;
        052	
        053			while (state) {
        054				try {
        055					th.sleep(33);
        056				}
        057				catch (InterruptedException e) {}
        058							// ターゲットの移動と消滅
        059				if (target == 1) {
        060									// ゲームオーバ
        061					x1 = xt + r - (x + 10);
        062					y1 = yt + r - (y + 10);
        063					if (Math.sqrt(x1*x1+y1*y1) < r+10)
        064						game = false;
        065									// ゲームオーバでない
        066											// 命中
        067					else if (fire && xt <= x+10 && xt > x+10-2*r)
        068						target = 2;
        069											// 命中しない
        070					else {
        071						xt += vx;
        072						yt += vy;
        073						if (xt < -2*r || xt > d.width || yt > d.height)
        074							target = 0;
        075					}
        076				}
        077							// ターゲットの生成
        078				else if (target == 0){
        079					target = 1;
        080					xt     = (int)((d.width - 2 * r) * rn.nextDouble());
        081					yt     = -r;
        082					ang    = 0.5 * Math.PI * rn.nextDouble() + 0.25 * Math.PI;
        083					vx     = (int)(sp * Math.cos(ang));
        084					vy     = (int)(sp * Math.sin(ang));
        085				}
        086							// 再描画
        087				repaint();
        088			}
        089		}
        090	
        091		public void paint (Graphics g)
        092		{
        093							// ゲーム中
        094			if (game) {
        095									// 砲台の表示
        096				g.fill3DRect(x, y, 20, 20, true);
        097									// ターゲットの表示
        098				if (target > 0) {
        099					if (target == 1)
        100						g.setColor(Color.green);
        101					else {
        102						g.setColor(Color.pink);
        103						target = 0;
        104					}
        105					g.fillOval(xt, yt, 2*r, 2*r);
        106				}
        107									// レーザ砲の発射
        108				if (fire){
        109					g.setColor(Color.red);
        110					g.drawLine(x+9, 0, x+9, d.height-10);
        111					g.drawLine(x+10, 0, x+10, d.height-10);
        112					g.drawLine(x+11, 0, x+11, d.height-10);
        113					fire = false;
        114				}
        115			}
        116							// ゲームオーバ
        117			else {
        118				Font f = new Font("TimesRoman", Font.BOLD, 50);
        119				g.setFont(f);
        120				g.drawString("Game Over", d.width/2-130, d.height/2);
        121				state = false;
        122			}
        123		}
        124	
        125		public boolean isFocusable() { return true; }
        126	
        127		class Key_e extends KeyAdapter {
        128			public void keyPressed(KeyEvent e)
        129			{
        130				if (e.getKeyCode() == 37)  // 左矢印
        131					x -= 20;
        132				else if (e.getKeyCode() == 39)   // 右矢印
        133					x += 20;
        134				if (e.isShiftDown())   // Shift キー
        135					fire = true;
        136			}
        137		}
        138	}
        					
        033 行目

          キーイベントを受け付けるために,KeyListener を追加している.

        067 行目~ 068 行目

          レーザ砲が発射されており,かつ,それがターゲットに命中した場合は,変数 target の値を 2 に変更している.

        098 行目~ 106 行目

          変数 target の値が 1 の場合は緑で,また,2 の場合はピンクでターゲットを描画している.さらに,変数 target の値が 2 の場合は,その値を 0 に変更している.

        108 行目~ 114 行目

          レーザ砲が発射されている場合は,それを描画し,発射されていない状態に戻している( 113 行目).

        128 行目~ 136 行目

          キーが押されたときの処理である.左矢印キーのときは,砲台を 20 ピクセルだけ左に,また,右矢印キーのときは,砲台を 20 ピクセルだけ右に移動させる.さらに,シフトキーのときは,レーザ砲を発射状態に設定する.

        Swing を利用した場合

        001	/************************/
        002	/* シューティングゲーム */
        003	/************************/
        004	import java.awt.*;
        005	import java.awt.event.*;
        006	import javax.swing.*;
        007	import java.util.*;
        008	
        009	public class Game1 extends JApplet
        010	{
        011		Draw dp;
        012		public void init() {
        013						// 背景色
        014			setBackground(new Color(238, 255, 238));
        015						// 初期設定
        016			Container cp = getContentPane();
        017			cp.setBackground(Color.white);
        018			dp = new Draw(this);
        019			dp.setBackground(new Color(238, 255, 238));
        020			cp.add(dp, BorderLayout.CENTER);
        021		}
        022	
        023		public void stop()
        024		{
        025			dp.state = false;
        026		}
        027	}
        028	
        029	class Draw extends JPanel implements Runnable
        030	{
        031		boolean fire = false;   // レーザ砲が発射されているか否か
        032		boolean state = true;   // スレッドが動作中か否か
        033		boolean game = true;   // ゲーム実行中か否か
        034		int xt = 20, yt = 50;   // ターゲットの位置
        035		int x, y;   // 砲台の位置
        036		int r = 25;   // ターゲットの半径
        037		int sp = 5;   // ターゲットの速さ
        038		int vx, vy;   // ターゲットの x 軸,及び,y 軸方向の速さ
        039		int target = 0;   // ターゲットの状態(0:存在しない,1:移動中,2:命中)
        040		Dimension d;
        041		Insets in;
        042		Thread th;
        043		Random rn;
        044		Game1 gm;
        045	
        046		Draw(Game1 gm1) {
        047			gm = gm1;
        048						// 初期設定
        049			in = gm.getInsets();
        050			d  = gm.getSize();
        051			d.width  = d.width - in.left - in.right;
        052			d.height = d.height - in.top - in.bottom;
        053			x  = d.width / 2 - 10;
        054			y  = d.height - 20;
        055			rn = new Random();
        056						// キーリスナの付加
        057			addKeyListener(new Key_e());
        058						// スレッドの生成
        059			th = new Thread(this);
        060			th.start();
        061		}
        062	
        063		public void run()
        064		{
        065			double ang, x1, y1;
        066	
        067			while (state) {
        068				try {
        069					th.sleep(33);
        070				}
        071				catch (InterruptedException e) {}
        072							// ターゲットの移動と消滅
        073				if (target == 1) {
        074									// ゲームオーバ
        075					x1 = xt + r - (x + 10);
        076					y1 = yt + r - (y + 10);
        077					if (Math.sqrt(x1*x1+y1*y1) < r+10)
        078						game = false;
        079									// ゲームオーバでない
        080											// 命中
        081					else if (fire && xt <= x+10 && xt > x+10-2*r)
        082						target = 2;
        083											// 命中しない
        084					else {
        085						xt += vx;
        086						yt += vy;
        087						if (xt < -2*r || xt > d.width || yt > d.height)
        088							target = 0;
        089					}
        090				}
        091							// ターゲットの生成
        092				else if (target == 0){
        093					target = 1;
        094					xt     = (int)((d.width - 2 * r) * rn.nextDouble());
        095					yt     = -r;
        096					ang    = 0.5 * Math.PI * rn.nextDouble() + 0.25 * Math.PI;
        097					vx     = (int)(sp * Math.cos(ang));
        098					vy     = (int)(sp * Math.sin(ang));
        099				}
        100							// 再描画
        101				repaint();
        102			}
        103		}
        104	
        105		public void paintComponent (Graphics g)
        106		{
        107			super.paintComponent(g);   // 親クラスの描画(必ず必要)
        108	
        109							// ゲーム中
        110			if (game) {
        111									// 砲台の表示
        112				g.fill3DRect(x, y, 20, 20, true);
        113									// ターゲットの表示
        114				if (target > 0) {
        115					if (target == 1)
        116						g.setColor(Color.green);
        117					else {
        118						g.setColor(Color.pink);
        119						target = 0;
        120					}
        121					g.fillOval(xt, yt, 2*r, 2*r);
        122				}
        123									// レーザ砲の発射
        124				if (fire){
        125					g.setColor(Color.red);
        126					g.drawLine(x+9, 0, x+9, d.height-10);
        127					g.drawLine(x+10, 0, x+10, d.height-10);
        128					g.drawLine(x+11, 0, x+11, d.height-10);
        129					fire = false;
        130				}
        131			}
        132							// ゲームオーバ
        133			else {
        134				Font f = new Font("TimesRoman", Font.BOLD, 50);
        135				g.setFont(f);
        136				g.drawString("Game Over", d.width/2-130, d.height/2);
        137				state = false;
        138			}
        139		}
        140	
        141		public boolean isFocusable() { return true; }
        142	
        143		class Key_e extends KeyAdapter {
        144			public void keyPressed(KeyEvent e)
        145			{
        146				if (e.getKeyCode() == 37)  // 左矢印
        147					x -= 20;
        148				else if (e.getKeyCode() == 39)   // 右矢印
        149					x += 20;
        150				if (e.isShiftDown())   // Shift キー
        151					fire = true;
        152			}
        153		}
        154	}
        					
        057 行目

          キーイベントを受け付けるために,KeyListener を追加している.

        081 行目~ 082 行目

          レーザ砲が発射されており,かつ,それがターゲットに命中した場合は,変数 target の値を 2 に変更している.

        114 行目~ 122 行目

          変数 target の値が 1 の場合は緑で,また,2 の場合はピンクでターゲットを描画している.さらに,変数 target の値が 2 の場合は,その値を 0 に変更している.

        124 行目~ 130 行目

          レーザ砲が発射されている場合は,それを描画し,発射されていない状態に戻している( 129 行目).

        144 行目~ 152 行目

          キーが押されたときの処理である.左矢印キーのときは,砲台を 20 ピクセルだけ左に,また,右矢印キーのときは,砲台を 20 ピクセルだけ右に移動させる.さらに,シフトキーのときは,レーザ砲を発射状態に設定する.

    2. ぷよぷよ(テトリス)

        このアプレット(「AWT の利用」,「Swing の利用」)は,キーイベントを使用した簡単なぷよぷよ風ゲームです.左右の矢印キーによって落下してくるピースを左右に動かすことができます.また,下矢印キーで90度,または,-90度の回転,上矢印キーで左右,または,上下の色を交換できます.

        ここでは,複数人で作成する大きなプログラムではなく,一人で比較的小さなプログラムを作成する場合について,その作成手順について考えてみます.少なくとも,すべてのプログラムを作成してからコンパイルし,実行してみるといった方法はあまり良い方法ではありません.作成するプログラムにもよりますが,私は,部分的な機能を実現するプログラムを作成し,その機能を確認した後,新しい機能を追加していくといった方法をよく利用します.たとえば,この例の場合は,以下のような手順になります.

      1. ピースの表示(「AWT の利用」,「Swing の利用」):  このプログラムでは,画面を格子状に区切り,この格子を配列に対応させ,各要素の値によってピースの色を制御しています( 0 の場合は,ピースが存在しない).どのような位置に,どのような色で表示しても構いませんが,後からのことを考えると,位置や色に対する一般的な処理を行っておいた方がよいと思います.なお,変数 p_x 及び p_y はピースの位置を表すための変数です.また,このプログラムでは,変数 game を false に設定することによって,ゲームオーバー画面も確認することができます.

        AWT を利用した場合

        01	/********************************/
        02	/* ぷよぷよ(ゲーム全体の制御) */
        03	/********************************/
        04	import java.awt.*;
        05	import java.applet.*;
        06	
        07	public class Game2 extends Applet
        08	{
        09		Puyo py;
        10		boolean game = true;   // ゲーム中か否か
        11		int p_x = 1, p_y = 3;   // 動いているピースの位置(列と行)
        12		int row = 12, col = 5;   // ブロックの行数と列数(ブロックの大きさは 30 ピクセル)
        13		int p[][] = new int [row][col];   // 各ブロックに存在するピースの色( 0 の時は存在しない)
        14	
        15		/************/
        16		/* 初期設定 */
        17		/************/
        18		public void init() {
        19						// 背景色
        20			setBackground(new Color(238, 255, 238));
        21						// ゲームパネルの配置
        22			p[p_y][p_x]   = 2;
        23			p[p_y][p_x+1] = 3;
        24			setLayout(null);
        25			py = new Puyo(this);
        26			add(py);
        27			py.setSize(30*col, 30*row);
        28			py.setLocation(10, 10);
        29			py.setBackground(Color.white);
        30		}
        31	
        32		/******************/
        33		/* 左右上下の余裕 */
        34		/******************/
        35		public Insets getInsets()
        36		{
        37			return new Insets(10, 10, 10, 10);
        38		}
        39	}
        40	
        41	/****************/
        42	/* ゲームパネル */
        43	/****************/
        44	class Puyo extends Panel
        45	{
        46		Game2 gm;
        47	
        48		/******************/
        49		/* コンストラクタ */
        50		/******************/
        51		Puyo (Game2 gm_t)
        52		{
        53			gm = gm_t;
        54		}
        55	
        56		/********/
        57		/* 描画 */
        58		/********/
        59		public void paint (Graphics g)
        60		{
        61			int i1, i2;
        62								// ゲーム中
        63			if (gm.game) {
        64				for (i1 = 0; i1 < gm.row; i1++) {
        65					for (i2 = 0; i2 < gm.col; i2++) {
        66						if (gm.p[i1][i2] > 0) {
        67							switch (gm.p[i1][i2]) {
        68								case 1:
        69									g.setColor(Color.red);
        70									break;
        71								case 2:
        72									g.setColor(Color.pink);
        73									break;
        74								case 3:
        75									g.setColor(Color.green);
        76									break;
        77								case 4:
        78									g.setColor(Color.blue);
        79									break;
        80							}
        81							g.fillRect(i2*30, i1*30, 30, 30);
        82						}
        83					}
        84				}
        85			}
        86								// ゲームオーバー
        87			else {
        88				Font f = new Font("TimesRoman", Font.BOLD, 40);
        89				g.setFont(f);
        90				g.drawString("Game", 20, 200);
        91				g.drawString("Over!", 25, 250);
        92			}
        93		}
        94	}
        					
        22 行目~ 23 行目

          左側のピースをピンク,右側のピースを緑に設定している( 67 行目~ 80 行目参照).

        24 行目

          レイアウトマネージャを無効にしている.この結果,コンポーネントを任意の位置に,かつ,任意の大きさで追加できるようになる.

        25 行目~ 29 行目

          画面の左側にゲーム画面(ピースを描く画面)となる Puyo クラス( 44 行目~ 94 行目)のオブジェクトを追加している.

        64 行目~ 84 行目

          配列変数 p の値が 0 より大きい(色が指定してある)セルの位置に,指定された色で,30 ピクセル × 30 ピクセルの矩形を描画している.

        88 行目~ 91 行目

          ゲームオーバーになった場合に対する処理であり,画面に「 Game Over! 」を表示している.

        Swing を利用した場合

        01	/********************************/
        02	/* ぷよぷよ(ゲーム全体の制御) */
        03	/********************************/
        04	import java.awt.*;
        05	import java.awt.event.*;
        06	import javax.swing.*;
        07	import java.util.*;
        08	
        09	public class Game2 extends JApplet
        10	{
        11		int row = 12, col = 5;   // ブロックの行数と列数(ブロックの大きさは 30 ピクセル)
        12	
        13		/************/
        14		/* 初期設定 */
        15		/************/
        16		public void init() {
        17						// 背景色
        18			setBackground(new Color(238, 255, 238));
        19						// ゲームパネルの配置
        20			Container cp = getContentPane();
        21			cp.setBackground(new Color(238, 255, 238));
        22			cp.setLayout(null);
        23			Puyo py = new Puyo(this);
        24			cp.add(py);
        25			py.setSize(30*col, 30*row);
        26			py.setLocation(10, 10);
        27			py.setBackground(Color.white);
        28		}
        29	}
        30	
        31	/****************/
        32	/* ゲームパネル */
        33	/****************/
        34	class Puyo extends JPanel
        35	{
        36		int p_x = 1, p_y = 3;   // 動いているピースの位置(列と行)
        37		int p[][];   // 各ブロックに存在するピースの色( 0 の時は存在しない)
        38		boolean game = true;   // ゲーム中か否か
        39		Game2 gm;
        40	
        41		/******************/
        42		/* コンストラクタ */
        43		/******************/
        44		Puyo (Game2 gm_t)
        45		{
        46			gm = gm_t;
        47			p  = new int [gm.row][gm.col];
        48			p[p_y][p_x]   = 2;
        49			p[p_y][p_x+1] = 3;
        50		}
        51	
        52		/********/
        53		/* 描画 */
        54		/********/
        55		public void paintComponent (Graphics g)
        56		{
        57			super.paintComponent(g);   // 親クラスの描画(必ず必要)
        58	
        59			int i1, i2;
        60								// ゲーム中
        61			if (game) {
        62				for (i1 = 0; i1 < gm.row; i1++) {
        63					for (i2 = 0; i2 < gm.col; i2++) {
        64						if (p[i1][i2] > 0) {
        65							switch (p[i1][i2]) {
        66								case 1:
        67									g.setColor(Color.red);
        68									break;
        69								case 2:
        70									g.setColor(Color.pink);
        71									break;
        72								case 3:
        73									g.setColor(Color.green);
        74									break;
        75								case 4:
        76									g.setColor(Color.blue);
        77									break;
        78							}
        79							g.fillRect(i2*30, i1*30, 30, 30);
        80						}
        81					}
        82				}
        83			}
        84								// ゲームオーバー
        85			else {
        86				Font f = new Font("TimesRoman", Font.BOLD, 40);
        87				g.setFont(f);
        88				g.drawString("Game", 20, 200);
        89				g.drawString("Over!", 25, 250);
        90			}
        91		}
        92	}
        					
        22 行目

          レイアウトマネージャを無効にしている.この結果,コンポーネントを任意の位置に,かつ,任意の大きさで追加できるようになる.

        23 行目~ 27 行目

          画面の左側にゲーム画面(ピースを描く画面)となる Puyo クラス( 34 行目~ 92 行目)のオブジェクトを追加している.

        48 行目~ 49 行目

          左側のピースをピンク,右側のピースを緑に設定している( 65 行目~ 78 行目参照).

        62 行目~ 82 行目

          配列変数 p の値が 0 より大きい(色が指定してある)セルの位置に,指定された色で,30 ピクセル × 30 ピクセルの矩形を描画している.

        86 行目~ 89 行目

          ゲームオーバーになった場合に対する処理であり,画面に「 Game Over! 」を表示している.

      2. ピースの落下(「AWT の利用」,「Swing の利用」):  先ほど表示したピースを,一定時間毎( この例では,500 ms )に落下させてみます.Runnable インターフェースを継承し,run メソッドの中でピースの位置を配列の 1 要素分だけ下に移動させています(配列の外に出ないように注意すること).前のステップで作成したプログラムを少し変更するだけですが,スレッドを利用したアニーションの機能を確認することができます.

        AWT を利用した場合

        001	/********************************/
        002	/* ぷよぷよ(ゲーム全体の制御) */
        003	/********************************/
        004	import java.awt.*;
        005	import java.applet.*;
        006	
        007	public class Game2 extends Applet implements Runnable
        008	{
        009		Puyo py;
        010		boolean game = true;   // ゲーム中か否か
        011		boolean state = true;   // スレッドが動作中か否か
        012		int p_x = 1, p_y = 3;   // 動いているピースの位置(列と行)
        013		int row = 12, col = 5;   // ブロックの行数と列数(ブロックの大きさは 30 ピクセル)
        014		int p[][] = new int [row][col];   // 各ブロックに存在するピースの色( 0 の時は存在しない)
        015		Thread th;
        016	
        017		/************/
        018		/* 初期設定 */
        019		/************/
        020		public void init() {
        021						// 背景色
        022			setBackground(new Color(238, 255, 238));
        023						// ゲームパネルの配置
        024			p[p_y][p_x]   = 2;
        025			p[p_y][p_x+1] = 3;
        026			setLayout(null);
        027			py = new Puyo(this);
        028			add(py);
        029			py.setSize(30*col, 30*row);
        030			py.setLocation(10, 10);
        031			py.setBackground(Color.white);
        032						// スレッドの生成
        033			th = new Thread(this);
        034			th.start();
        035		}
        036	
        037		/******************/
        038		/* 左右上下の余裕 */
        039		/******************/
        040		public Insets getInsets()
        041		{
        042			return new Insets(10, 10, 10, 10);
        043		}
        044	
        045		/******************/
        046		/* スレッドの停止 */
        047		/******************/
        048		public void stop()
        049		{
        050			state = false;
        051		}
        052	
        053		/******************/
        054		/* スレッドの実行 */
        055		/******************/
        056		public void run()
        057		{
        058			while (state) {
        059				try {
        060					th.sleep(500);
        061				}
        062				catch (InterruptedException e) {}
        063							// ピースの落下
        064				if (p_y < row-1) {
        065					p[p_y+1][p_x]   = p[p_y][p_x];
        066					p[p_y+1][p_x+1] = p[p_y][p_x+1];
        067					p[p_y][p_x]     = 0;
        068					p[p_y][p_x+1]   = 0;
        069					p_y++;
        070				}
        071							// 再描画
        072				py.repaint();
        073			}
        074		}
        075	}
        076	
        077	/****************/
        078	/* ゲームパネル */
        079	/****************/
        080	class Puyo extends Panel
        081	{
        082		Game2 gm;
        083	
        084		/******************/
        085		/* コンストラクタ */
        086		/******************/
        087		Puyo (Game2 gm_t)
        088		{
        089			gm = gm_t;
        090		}
        091	
        092		/********/
        093		/* 描画 */
        094		/********/
        095		public void paint (Graphics g)
        096		{
        097			int i1, i2;
        098								// ゲーム中
        099			if (gm.game) {
        100				for (i1 = 0; i1 < gm.row; i1++) {
        101					for (i2 = 0; i2 < gm.col; i2++) {
        102						if (gm.p[i1][i2] > 0) {
        103							switch (gm.p[i1][i2]) {
        104								case 1:
        105									g.setColor(Color.red);
        106									break;
        107								case 2:
        108									g.setColor(Color.pink);
        109									break;
        110								case 3:
        111									g.setColor(Color.green);
        112									break;
        113								case 4:
        114									g.setColor(Color.blue);
        115									break;
        116							}
        117							g.fillRect(i2*30, i1*30, 30, 30);
        118						}
        119					}
        120				}
        121			}
        122								// ゲームオーバー
        123			else {
        124				Font f = new Font("TimesRoman", Font.BOLD, 40);
        125				g.setFont(f);
        126				g.drawString("Game", 20, 200);
        127				g.drawString("Over!", 25, 250);
        128				gm.state = false;
        129			}
        130		}
        131	}
        					
        007 行目

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

        033 行目~ 034 行目

          Thread クラスのオブジェクトを生成し,それをスタートさせている.

        056 行目~ 074 行目

          Thread クラスのメソッドであり,スレッドを 500 ms 毎に実行することになる( 060 行目).064 行目~ 070 行目において,ピースが床に達していないときは,一つ下のセルに移動している.

        128 行目

          ゲームオーバーになった場合は,スレッドを停止している.

        Swing を利用した場合

        001	/********************************/
        002	/* ぷよぷよ(ゲーム全体の制御) */
        003	/********************************/
        004	import java.awt.*;
        005	import java.awt.event.*;
        006	import javax.swing.*;
        007	import java.util.*;
        008	
        009	public class Game2 extends JApplet
        010	{
        011		int row = 12, col = 5;   // ブロックの行数と列数(ブロックの大きさは 30 ピクセル)
        012		Puyo py;
        013	
        014		/************/
        015		/* 初期設定 */
        016		/************/
        017		public void init() {
        018						// 背景色
        019			setBackground(new Color(238, 255, 238));
        020						// ゲームパネルの配置
        021			Container cp = getContentPane();
        022			cp.setBackground(new Color(238, 255, 238));
        023			cp.setLayout(null);
        024			py = new Puyo(this);
        025			cp.add(py);
        026			py.setSize(30*col, 30*row);
        027			py.setLocation(10, 10);
        028			py.setBackground(Color.white);
        029		}
        030	
        031		/******************/
        032		/* スレッドの停止 */
        033		/******************/
        034		public void stop()
        035		{
        036			py.state = false;
        037		}
        038	}
        039	
        040	/****************/
        041	/* ゲームパネル */
        042	/****************/
        043	class Puyo extends JPanel implements Runnable
        044	{
        045		int p_x = 1, p_y = 3;   // 動いているピースの位置(列と行)
        046		int p[][];   // 各ブロックに存在するピースの色( 0 の時は存在しない)
        047		boolean game = true;   // ゲーム中か否か
        048		boolean state = true;   // スレッドが動作中か否か
        049		Thread th;
        050		Game2 gm;
        051	
        052		/******************/
        053		/* コンストラクタ */
        054		/******************/
        055		Puyo (Game2 gm_t)
        056		{
        057			gm = gm_t;
        058			p  = new int [gm.row][gm.col];
        059			p[p_y][p_x]   = 2;
        060			p[p_y][p_x+1] = 3;
        061						// スレッドの生成
        062			th = new Thread(this);
        063			th.start();
        064		}
        065	
        066		/******************/
        067		/* スレッドの実行 */
        068		/******************/
        069		public void run()
        070		{
        071			while (state) {
        072				try {
        073					th.sleep(500);
        074				}
        075				catch (InterruptedException e) {}
        076							// ピースの落下
        077				if (p_y < gm.row-1) {
        078					p[p_y+1][p_x]   = p[p_y][p_x];
        079					p[p_y+1][p_x+1] = p[p_y][p_x+1];
        080					p[p_y][p_x]     = 0;
        081					p[p_y][p_x+1]   = 0;
        082					p_y++;
        083				}
        084							// 再描画
        085				repaint();
        086			}
        087		}
        088	
        089		/********/
        090		/* 描画 */
        091		/********/
        092		public void paintComponent (Graphics g)
        093		{
        094			super.paintComponent(g);   // 親クラスの描画(必ず必要)
        095	
        096			int i1, i2;
        097								// ゲーム中
        098			if (game) {
        099				for (i1 = 0; i1 < gm.row; i1++) {
        100					for (i2 = 0; i2 < gm.col; i2++) {
        101						if (p[i1][i2] > 0) {
        102							switch (p[i1][i2]) {
        103								case 1:
        104									g.setColor(Color.red);
        105									break;
        106								case 2:
        107									g.setColor(Color.pink);
        108									break;
        109								case 3:
        110									g.setColor(Color.green);
        111									break;
        112								case 4:
        113									g.setColor(Color.blue);
        114									break;
        115							}
        116							g.fillRect(i2*30, i1*30, 30, 30);
        117						}
        118					}
        119				}
        120			}
        121								// ゲームオーバー
        122			else {
        123				Font f = new Font("TimesRoman", Font.BOLD, 40);
        124				g.setFont(f);
        125				g.drawString("Game", 20, 200);
        126				g.drawString("Over!", 25, 250);
        127				state = false;
        128			}
        129		}
        130	}
        					
        043 行目

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

        062 行目~ 063 行目

          Thread クラスのオブジェクトを生成し,それをスタートさせている.

        069 行目~ 087 行目

          Thread クラスのメソッドであり,スレッドを 500 ms 毎に実行することになる( 073 行目).077 行目~ 083 行目において,ピースが床に達していないときは,一つ下のセルに移動している.

        127 行目

          ゲームオーバーになった場合は,スレッドを停止している.

      3. ピースの生成と落下(「AWT の利用」,「Swing の利用」):  ピースを画面上部(水平位置はランダム)に発生させ,落下させます.ランダムな値を設定するために,Random クラスを使用しています.

        AWT を利用した場合

        001	/********************************/
        002	/* ぷよぷよ(ゲーム全体の制御) */
        003	/********************************/
        004	import java.awt.*;
        005	import java.applet.*;
        006	import java.util.*;
        007	
        008	public class Game2 extends Applet implements Runnable
        009	{
        010		Puyo py;
        011		boolean game = true;   // ゲーム中か否か
        012		boolean state = true;   // スレッドが動作中か否か
        013		int p_x = 1, p_y = 3;   // 動いているピースの位置(列と行)
        014		int row = 12, col = 5;   // ブロックの行数と列数(ブロックの大きさは 30 ピクセル)
        015		int p[][] = new int [row][col];   // 各ブロックに存在するピースの色( 0 の時は存在しない)
        016		Random rn;
        017		Thread th;
        018	
        019		/************/
        020		/* 初期設定 */
        021		/************/
        022		public void init() {
        023						// 背景色
        024			setBackground(new Color(238, 255, 238));
        025						// ゲームパネルの配置
        026			setLayout(null);
        027			py = new Puyo(this);
        028			add(py);
        029			py.setSize(30*col, 30*row);
        030			py.setLocation(10, 10);
        031			py.setBackground(Color.white);
        032			rn = new Random();
        033						// ピースの選択
        034			select();
        035						// スレッドの生成
        036			th = new Thread(this);
        037			th.start();
        038		}
        039	
        040		/******************/
        041		/* 左右上下の余裕 */
        042		/******************/
        043		public Insets getInsets()
        044		{
        045			return new Insets(10, 10, 10, 10);
        046		}
        047	
        048		/******************/
        049		/* スレッドの停止 */
        050		/******************/
        051		public void stop()
        052		{
        053			state = false;
        054		}
        055	
        056		/******************/
        057		/* スレッドの実行 */
        058		/******************/
        059		public void run()
        060		{
        061			while (state) {
        062				try {
        063					th.sleep(500);
        064				}
        065				catch (InterruptedException e) {}
        066							// ピースの落下
        067				if (p_y < row-1) {
        068					p[p_y+1][p_x]   = p[p_y][p_x];
        069					p[p_y+1][p_x+1] = p[p_y][p_x+1];
        070					p[p_y][p_x]     = 0;
        071					p[p_y][p_x+1]   = 0;
        072					p_y++;
        073				}
        074				else
        075					select();
        076							// 再描画
        077				py.repaint();
        078			}
        079		}
        080	
        081		/****************/
        082		/* ピースの選択 */
        083		/****************/
        084		void select()
        085		{
        086			int color;
        087	
        088			p_y = 0;
        089			p_x = (int)(rn.nextDouble() * (col - 1));
        090			if (p_x > col-2)
        091				p_x = col - 2;
        092	
        093			color = (int)(rn.nextDouble() * 4) + 1;
        094			if (color > 4)
        095				color = 4;
        096			p[0][p_x] = color;
        097			color = (int)(rn.nextDouble() * 4) + 1;
        098			if (color > 4)
        099				color = 4;
        100			p[0][p_x+1] = color;
        101		}
        102	}
        103	
        104	/****************/
        105	/* ゲームパネル */
        106	/****************/
        107	class Puyo extends Panel
        108	{
        109		Game2 gm;
        110	
        111		/******************/
        112		/* コンストラクタ */
        113		/******************/
        114		Puyo (Game2 gm_t)
        115		{
        116			gm = gm_t;
        117		}
        118	
        119		/********/
        120		/* 描画 */
        121		/********/
        122		public void paint (Graphics g)
        123		{
        124			int i1, i2;
        125								// ゲーム中
        126			if (gm.game) {
        127				for (i1 = 0; i1 < gm.row; i1++) {
        128					for (i2 = 0; i2 < gm.col; i2++) {
        129						if (gm.p[i1][i2] > 0) {
        130							switch (gm.p[i1][i2]) {
        131								case 1:
        132									g.setColor(Color.red);
        133									break;
        134								case 2:
        135									g.setColor(Color.pink);
        136									break;
        137								case 3:
        138									g.setColor(Color.green);
        139									break;
        140								case 4:
        141									g.setColor(Color.blue);
        142									break;
        143							}
        144							g.fillRect(i2*30, i1*30, 30, 30);
        145						}
        146					}
        147				}
        148			}
        149								// ゲームオーバー
        150			else {
        151				Font f = new Font("TimesRoman", Font.BOLD, 40);
        152				g.setFont(f);
        153				g.drawString("Game", 20, 200);
        154				g.drawString("Over!", 25, 250);
        155				gm.state = false;
        156			}
        157		}
        158	}
        					
        032 行目

          乱数の初期化.

        034 行目

          ピースの初期位置や色をランダムに選択するメソッド select( 084 行目~ 101 行目)を呼んでいる.

        074 行目~ 075 行目

          ピースが最下段に到達した場合は,次のピースを選択するために,メソッド select( 084 行目~ 101 行目)を呼んでいる.

        089 行目~ 091 行目

          ピースの初期位置(水平方向)をランダムに選択

        093 行目~ 100 行目

          2 つのピースの色をランダムに選択

        Swing を利用した場合

        001	/********************************/
        002	/* ぷよぷよ(ゲーム全体の制御) */
        003	/********************************/
        004	import java.awt.*;
        005	import java.awt.event.*;
        006	import javax.swing.*;
        007	import java.util.*;
        008	
        009	public class Game2 extends JApplet
        010	{
        011		int row = 12, col = 5;   // ブロックの行数と列数(ブロックの大きさは 30 ピクセル)
        012		Puyo py;
        013	
        014		/************/
        015		/* 初期設定 */
        016		/************/
        017		public void init() {
        018						// 背景色
        019			setBackground(new Color(238, 255, 238));
        020						// ゲームパネルの配置
        021			Container cp = getContentPane();
        022			cp.setBackground(new Color(238, 255, 238));
        023			cp.setLayout(null);
        024			py = new Puyo(this);
        025			cp.add(py);
        026			py.setSize(30*col, 30*row);
        027			py.setLocation(10, 10);
        028			py.setBackground(Color.white);
        029		}
        030	
        031		/******************/
        032		/* スレッドの停止 */
        033		/******************/
        034		public void stop()
        035		{
        036			py.state = false;
        037		}
        038	}
        039	
        040	/****************/
        041	/* ゲームパネル */
        042	/****************/
        043	class Puyo extends JPanel implements Runnable
        044	{
        045		int p_x = 1, p_y = 3;   // 動いているピースの位置(列と行)
        046		int p[][];   // 各ブロックに存在するピースの色( 0 の時は存在しない)
        047		boolean game = true;   // ゲーム中か否か
        048		boolean state = true;   // スレッドが動作中か否か
        049		Thread th;
        050		Random rn;
        051		Game2 gm;
        052	
        053		/******************/
        054		/* コンストラクタ */
        055		/******************/
        056		Puyo (Game2 gm_t)
        057		{
        058			gm = gm_t;
        059			p  = new int [gm.row][gm.col];
        060			rn = new Random();
        061						// ピースの選択
        062			select();
        063						// スレッドの生成
        064			th = new Thread(this);
        065			th.start();
        066		}
        067	
        068		/******************/
        069		/* スレッドの実行 */
        070		/******************/
        071		public void run()
        072		{
        073			while (state) {
        074				try {
        075					th.sleep(500);
        076				}
        077				catch (InterruptedException e) {}
        078							// ピースの落下
        079				if (p_y < gm.row-1) {
        080					p[p_y+1][p_x]   = p[p_y][p_x];
        081					p[p_y+1][p_x+1] = p[p_y][p_x+1];
        082					p[p_y][p_x]     = 0;
        083					p[p_y][p_x+1]   = 0;
        084					p_y++;
        085				}
        086				else
        087					select();
        088							// 再描画
        089				repaint();
        090			}
        091		}
        092	
        093		/****************/
        094		/* ピースの選択 */
        095		/****************/
        096		void select()
        097		{
        098			int color;
        099	
        100			p_y = 0;
        101			p_x = (int)(rn.nextDouble() * (gm.col - 1));
        102			if (p_x > gm.col-2)
        103				p_x = gm.col - 2;
        104	
        105			color = (int)(rn.nextDouble() * 4) + 1;
        106			if (color > 4)
        107				color = 4;
        108			p[0][p_x] = color;
        109			color = (int)(rn.nextDouble() * 4) + 1;
        110			if (color > 4)
        111				color = 4;
        112			p[0][p_x+1] = color;
        113		}
        114	
        115		/********/
        116		/* 描画 */
        117		/********/
        118		public void paintComponent (Graphics g)
        119		{
        120			super.paintComponent(g);   // 親クラスの描画(必ず必要)
        121	
        122			int i1, i2;
        123								// ゲーム中
        124			if (game) {
        125				for (i1 = 0; i1 < gm.row; i1++) {
        126					for (i2 = 0; i2 < gm.col; i2++) {
        127						if (p[i1][i2] > 0) {
        128							switch (p[i1][i2]) {
        129								case 1:
        130									g.setColor(Color.red);
        131									break;
        132								case 2:
        133									g.setColor(Color.pink);
        134									break;
        135								case 3:
        136									g.setColor(Color.green);
        137									break;
        138								case 4:
        139									g.setColor(Color.blue);
        140									break;
        141							}
        142							g.fillRect(i2*30, i1*30, 30, 30);
        143						}
        144					}
        145				}
        146			}
        147								// ゲームオーバー
        148			else {
        149				Font f = new Font("TimesRoman", Font.BOLD, 40);
        150				g.setFont(f);
        151				g.drawString("Game", 20, 200);
        152				g.drawString("Over!", 25, 250);
        153				state = false;
        154			}
        155		}
        156	}
        					
        060 行目

          乱数の初期化.

        062 行目

          ピースの初期位置や色をランダムに選択するメソッド select( 096 行目~ 113 行目)を呼んでいる.

        086 行目~ 087 行目

          ピースが最下段に到達した場合は,次のピースを選択するために,メソッド select( 096 行目~ 113 行目)を呼んでいる.

        101 行目~ 103 行目

          ピースの初期位置(水平方向)をランダムに選択

        105 行目~ 112 行目

          2 つのピースの色をランダムに選択

      4. キーイベント(「AWT の利用」,「Swing の利用」):  キーイベントに対する処理を追加します.移動や回転を行う場合は,移動や回転が可能か否かのチェックが必要となります.変数 rot の値が 0 の時は横並び,1 の時は縦並びであることを意味します.当然,rot の値によって,一番下に到着したか否かのチェック方法も異なってきますので,ピースの落下を制御する部分も変更する必要があります.また,一番下に到着すると,変数 ok を false に設定し,キーイベントを受け付けないようにしています.

        AWT を利用した場合

        001	/********************************/
        002	/* ぷよぷよ(ゲーム全体の制御) */
        003	/********************************/
        004	import java.awt.*;
        005	import java.awt.event.*;
        006	import java.applet.*;
        007	import java.util.*;
        008	
        009	public class Game2 extends Applet implements Runnable
        010	{
        011		Puyo py;
        012		boolean game = true;   // ゲーム中か否か
        013		boolean state = true;   // スレッドが動作中か否か
        014		int p_x = 1, p_y = 3;   // 動いているピースの位置(列と行)
        015		int row = 12, col = 5;   // ブロックの行数と列数(ブロックの大きさは 30 ピクセル)
        016		int rot = 0;   // 横か縦か(0:横,1:縦)
        017		int p[][] = new int [row][col];   // 各ブロックに存在するピースの色( 0 の時は存在しない)
        018		boolean ok = true;   // キーイベントを受け付けるか否か
        019		Random rn;
        020		Thread th;
        021	
        022		/************/
        023		/* 初期設定 */
        024		/************/
        025		public void init() {
        026						// 背景色
        027			setBackground(new Color(238, 255, 238));
        028						// ゲームパネルの配置
        029			setLayout(null);
        030			py = new Puyo(this);
        031			add(py);
        032			py.setSize(30*col, 30*row);
        033			py.setLocation(10, 10);
        034			py.setBackground(Color.white);
        035			rn = new Random();
        036						// ピースの選択
        037			select();
        038						// スレッドの生成
        039			th = new Thread(this);
        040			th.start();
        041		}
        042	
        043		/******************/
        044		/* 左右上下の余裕 */
        045		/******************/
        046		public Insets getInsets()
        047		{
        048			return new Insets(10, 10, 10, 10);
        049		}
        050	
        051		/******************/
        052		/* スレッドの停止 */
        053		/******************/
        054		public void stop()
        055		{
        056			state = false;
        057		}
        058	
        059		/******************/
        060		/* スレッドの実行 */
        061		/******************/
        062		public void run()
        063		{
        064			int ct;
        065	
        066			while (state) {
        067				try {
        068					th.sleep(500);
        069				}
        070				catch (InterruptedException e) {}
        071							// ピースの落下
        072				ct = 0;   // 落下したか否かを示す
        073				if (rot == 0) {   // 横並び
        074					if (p_y < row-1) {
        075						ct              = 1;
        076						p[p_y+1][p_x]   = p[p_y][p_x];
        077						p[p_y+1][p_x+1] = p[p_y][p_x+1];
        078						p[p_y][p_x]     = 0;
        079						p[p_y][p_x+1]   = 0;
        080						p_y++;
        081					}
        082					else
        083						ok = false;
        084				}
        085				else {   // 縦並び
        086					if (p_y < row-2) {
        087						ct = 1;
        088						p[p_y+2][p_x] = p[p_y+1][p_x];
        089						p[p_y+1][p_x] = p[p_y][p_x];
        090						p[p_y][p_x]   = 0;
        091						p_y++;
        092					}
        093					else
        094						ok = false;
        095				}
        096							// 消去と次のピース
        097				if (ct == 0)
        098					select();
        099							// 再描画
        100				py.repaint();
        101			}
        102		}
        103	
        104		/****************/
        105		/* ピースの選択 */
        106		/****************/
        107		void select()
        108		{
        109			int color;
        110	
        111			ok  = true;
        112			rot = 0;
        113			p_y = 0;
        114			p_x = (int)(rn.nextDouble() * (col - 1));
        115			if (p_x > col-2)
        116				p_x = col - 2;
        117	
        118			color = (int)(rn.nextDouble() * 4) + 1;
        119			if (color > 4)
        120				color = 4;
        121			p[0][p_x] = color;
        122			color = (int)(rn.nextDouble() * 4) + 1;
        123			if (color > 4)
        124				color = 4;
        125			p[0][p_x+1] = color;
        126		}
        127	}
        128	
        129	/****************/
        130	/* ゲームパネル */
        131	/****************/
        132	class Puyo extends Panel
        133	{
        134		Game2 gm;
        135	
        136		/******************/
        137		/* コンストラクタ */
        138		/******************/
        139		Puyo (Game2 gm_t)
        140		{
        141			gm = gm_t;
        142			addKeyListener(new Key_e());
        143		}
        144	
        145		/********/
        146		/* 描画 */
        147		/********/
        148		public void paint (Graphics g)
        149		{
        150			int i1, i2;
        151								// ゲーム中
        152			if (gm.game) {
        153				for (i1 = 0; i1 < gm.row; i1++) {
        154					for (i2 = 0; i2 < gm.col; i2++) {
        155						if (gm.p[i1][i2] > 0) {
        156							switch (gm.p[i1][i2]) {
        157								case 1:
        158									g.setColor(Color.red);
        159									break;
        160								case 2:
        161									g.setColor(Color.pink);
        162									break;
        163								case 3:
        164									g.setColor(Color.green);
        165									break;
        166								case 4:
        167									g.setColor(Color.blue);
        168									break;
        169							}
        170							g.fillRect(i2*30, i1*30, 30, 30);
        171						}
        172					}
        173				}
        174			}
        175								// ゲームオーバー
        176			else {
        177				Font f = new Font("TimesRoman", Font.BOLD, 40);
        178				g.setFont(f);
        179				g.drawString("Game", 20, 200);
        180				g.drawString("Over!", 25, 250);
        181				gm.state = false;
        182			}
        183		}
        184	
        185		/************************/
        186		/* キーイベントの有効化 */
        187		/************************/
        188		public boolean isFocusable() { return true; }
        189	
        190		/**********************/
        191		/* キーイベントの処理 */
        192		/**********************/
        193		class Key_e extends KeyAdapter {
        194			public void keyPressed(KeyEvent e)
        195			{
        196				int k;
        197				if (gm.ok) {
        198					if (e.getKeyCode() == KeyEvent.VK_LEFT) {   // 左矢印(左移動)
        199						if (gm.p_x > 0) {
        200							if (gm.rot == 0 && gm.p[gm.p_y][gm.p_x-1] == 0) {
        201								gm.p[gm.p_y][gm.p_x-1] = gm.p[gm.p_y][gm.p_x];
        202								gm.p[gm.p_y][gm.p_x]   = gm.p[gm.p_y][gm.p_x+1];
        203								gm.p[gm.p_y][gm.p_x+1] = 0;
        204								gm.p_x--;
        205								repaint();
        206							}
        207							else if (gm.p[gm.p_y][gm.p_x-1] == 0 && gm.p[gm.p_y+1][gm.p_x-1] == 0) {
        208								gm.p[gm.p_y][gm.p_x-1]   = gm.p[gm.p_y][gm.p_x];
        209								gm.p[gm.p_y+1][gm.p_x-1] = gm.p[gm.p_y+1][gm.p_x];
        210								gm.p[gm.p_y][gm.p_x]     = 0;
        211								gm.p[gm.p_y+1][gm.p_x]   = 0;
        212								gm.p_x--;
        213								repaint();
        214							}
        215						}
        216					}
        217					else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {   // 右矢印(右移動)
        218						if (gm.rot == 0) {
        219							if (gm.p_x < gm.col-2 && gm.p[gm.p_y][gm.p_x+2] == 0) {
        220								gm.p[gm.p_y][gm.p_x+2] = gm.p[gm.p_y][gm.p_x+1];
        221								gm.p[gm.p_y][gm.p_x+1] = gm.p[gm.p_y][gm.p_x];
        222								gm.p[gm.p_y][gm.p_x]   = 0;
        223								gm.p_x++;
        224								repaint();
        225							}
        226						}
        227						else {
        228							if (gm.p_x < gm.col-1 && gm.p[gm.p_y][gm.p_x+1] == 0 && gm.p[gm.p_y+1][gm.p_x+1] == 0) {
        229								gm.p[gm.p_y][gm.p_x+1]   = gm.p[gm.p_y][gm.p_x];
        230								gm.p[gm.p_y+1][gm.p_x+1] = gm.p[gm.p_y+1][gm.p_x];
        231								gm.p[gm.p_y][gm.p_x]     = 0;
        232								gm.p[gm.p_y+1][gm.p_x]   = 0;
        233								gm.p_x++;
        234								repaint();
        235							}
        236						}
        237					}
        238					else if (e.getKeyCode() == KeyEvent.VK_UP) {   // 上矢印(上下または左右入れ替え)
        239						if (gm.rot == 0) {
        240							k                      = gm.p[gm.p_y][gm.p_x];
        241							gm.p[gm.p_y][gm.p_x]   = gm.p[gm.p_y][gm.p_x+1];
        242							gm.p[gm.p_y][gm.p_x+1] = k;
        243						}
        244						else {
        245							k                      = gm.p[gm.p_y][gm.p_x];
        246							gm.p[gm.p_y][gm.p_x]   = gm.p[gm.p_y+1][gm.p_x];
        247							gm.p[gm.p_y+1][gm.p_x] = k;
        248						}
        249						repaint();
        250					}
        251					else if (e.getKeyCode() == KeyEvent.VK_DOWN) {   // 下矢印(90度または-90度回転)
        252						if (gm.rot == 0 && gm.p[gm.p_y+1][gm.p_x] == 0) {
        253							if (gm.p_y < gm.row-1) {
        254								gm.p[gm.p_y+1][gm.p_x] = gm.p[gm.p_y][gm.p_x+1];
        255								gm.p[gm.p_y][gm.p_x+1] = 0;
        256								gm.rot                 = 1;
        257								repaint();
        258							}
        259						}
        260						else {
        261							if (gm.p_x < gm.col-1 && gm.p[gm.p_y][gm.p_x+1] == 0) {
        262								gm.p[gm.p_y][gm.p_x+1] = gm.p[gm.p_y+1][gm.p_x];
        263								gm.p[gm.p_y+1][gm.p_x] = 0;
        264								gm.rot                 = 0;
        265								repaint();
        266							}
        267						}
        268					}
        269				}
        270			}
        271		}
        272	}
        					
        086 行目~ 094 行目

          ピースが縦並びの場合の落下処理.横並びの場合も同様であるが,最下段に到達したときは,キーイベントを受け付けないようにしている( 094 行目)

        142 行目

          キーイベントを受け付けるために,KeyListener を追加している.

        188 行目

          キーイベントを有効にしている.

        201 行目~ 205 行目

          左矢印キーが押され,ピースが横並びであった場合の処理である.左側に他のピースが存在しない場合は,左側に移動する.

        208 行目~ 213 行目

          左矢印キーが押され,ピースが縦並びであった場合の処理である.左側に他のピースが存在しない場合は,左側に移動する.

        218 行目~ 236 行目

          右矢印キーによって右移動するための処理である.左矢印キーが押された場合と同様の処理を行っている.

        239 行目~ 249 行目

          上矢印キーが押された場合の処理であり,上下,または,左右の色を入れ替えている.

        252 行目~ 267 行目

          下矢印キーが押された場合の処理であり,回転可能な場合は,回転させる処理を行っている.

        Swing を利用した場合

        001	/********************************/
        002	/* ぷよぷよ(ゲーム全体の制御) */
        003	/********************************/
        004	import java.awt.*;
        005	import java.awt.event.*;
        006	import javax.swing.*;
        007	import java.util.*;
        008	
        009	public class Game2 extends JApplet
        010	{
        011		int row = 12, col = 5;   // ブロックの行数と列数(ブロックの大きさは 30 ピクセル)
        012		Puyo py;
        013	
        014		/************/
        015		/* 初期設定 */
        016		/************/
        017		public void init() {
        018						// 背景色
        019			setBackground(new Color(238, 255, 238));
        020						// ゲームパネルの配置
        021			Container cp = getContentPane();
        022			cp.setBackground(new Color(238, 255, 238));
        023			cp.setLayout(null);
        024			py = new Puyo(this);
        025			cp.add(py);
        026			py.setSize(30*col, 30*row);
        027			py.setLocation(10, 10);
        028			py.setBackground(Color.white);
        029		}
        030	
        031		/******************/
        032		/* スレッドの停止 */
        033		/******************/
        034		public void stop()
        035		{
        036			py.state = false;
        037		}
        038	}
        039	
        040	/****************/
        041	/* ゲームパネル */
        042	/****************/
        043	class Puyo extends JPanel implements Runnable
        044	{
        045		int p_x = 1, p_y = 3;   // 動いているピースの位置(列と行)
        046		int p[][];   // 各ブロックに存在するピースの色( 0 の時は存在しない)
        047		int rot = 0;   // 横か縦か(0:横,1:縦)
        048		boolean game = true;   // ゲーム中か否か
        049		boolean state = true;   // スレッドが動作中か否か
        050		boolean ok = true;   // キーイベントを受け付けるか否か
        051		Thread th;
        052		Random rn;
        053		Game2 gm;
        054	
        055		/******************/
        056		/* コンストラクタ */
        057		/******************/
        058		Puyo (Game2 gm_t)
        059		{
        060			gm = gm_t;
        061			rn = new Random();
        062			p  = new int [gm.row][gm.col];
        063			addKeyListener(new Key_e());
        064						// ピースの選択
        065			select();
        066						// スレッドの生成
        067			th = new Thread(this);
        068			th.start();
        069		}
        070	
        071		/******************/
        072		/* スレッドの実行 */
        073		/******************/
        074		public void run()
        075		{
        076			int ct;
        077	
        078			while (state) {
        079				try {
        080					th.sleep(500);
        081				}
        082				catch (InterruptedException e) {}
        083							// ピースの落下
        084				ct = 0;   // 落下したか否かを示す
        085				if (rot == 0) {   // 横並び
        086					if (p_y < gm.row-1) {
        087						ct              = 1;
        088						p[p_y+1][p_x]   = p[p_y][p_x];
        089						p[p_y+1][p_x+1] = p[p_y][p_x+1];
        090						p[p_y][p_x]     = 0;
        091						p[p_y][p_x+1]   = 0;
        092						p_y++;
        093					}
        094					else
        095						ok = false;
        096				}
        097				else {   // 縦並び
        098					if (p_y < gm.row-2) {
        099						ct = 1;
        100						p[p_y+2][p_x] = p[p_y+1][p_x];
        101						p[p_y+1][p_x] = p[p_y][p_x];
        102						p[p_y][p_x]   = 0;
        103						p_y++;
        104					}
        105					else
        106						ok = false;
        107				}
        108							// 消去と次のピース
        109				if (ct == 0)
        110					select();
        111							// 再描画
        112				repaint();
        113			}
        114		}
        115	
        116		/****************/
        117		/* ピースの選択 */
        118		/****************/
        119		void select()
        120		{
        121			int color;
        122	
        123			ok  = true;
        124			rot = 0;
        125			p_y = 0;
        126			p_x = (int)(rn.nextDouble() * (gm.col - 1));
        127			if (p_x > gm.col-2)
        128				p_x = gm.col - 2;
        129	
        130			color = (int)(rn.nextDouble() * 4) + 1;
        131			if (color > 4)
        132				color = 4;
        133			p[0][p_x] = color;
        134			color = (int)(rn.nextDouble() * 4) + 1;
        135			if (color > 4)
        136				color = 4;
        137			p[0][p_x+1] = color;
        138		}
        139	
        140		/********/
        141		/* 描画 */
        142		/********/
        143		public void paintComponent (Graphics g)
        144		{
        145			super.paintComponent(g);   // 親クラスの描画(必ず必要)
        146	
        147			int i1, i2;
        148								// ゲーム中
        149			if (game) {
        150				for (i1 = 0; i1 < gm.row; i1++) {
        151					for (i2 = 0; i2 < gm.col; i2++) {
        152						if (p[i1][i2] > 0) {
        153							switch (p[i1][i2]) {
        154								case 1:
        155									g.setColor(Color.red);
        156									break;
        157								case 2:
        158									g.setColor(Color.pink);
        159									break;
        160								case 3:
        161									g.setColor(Color.green);
        162									break;
        163								case 4:
        164									g.setColor(Color.blue);
        165									break;
        166							}
        167							g.fillRect(i2*30, i1*30, 30, 30);
        168						}
        169					}
        170				}
        171			}
        172								// ゲームオーバー
        173			else {
        174				Font f = new Font("TimesRoman", Font.BOLD, 40);
        175				g.setFont(f);
        176				g.drawString("Game", 20, 200);
        177				g.drawString("Over!", 25, 250);
        178				state = false;
        179			}
        180		}
        181	
        182		/************************/
        183		/* キーイベントの有効化 */
        184		/************************/
        185		public boolean isFocusable() { return true; }
        186	
        187		/**********************/
        188		/* キーイベントの処理 */
        189		/**********************/
        190		class Key_e extends KeyAdapter {
        191			public void keyPressed(KeyEvent e)
        192			{
        193				int k;
        194				if (ok) {
        195					if (e.getKeyCode() == KeyEvent.VK_LEFT) {   // 左矢印(左移動)
        196						if (p_x > 0) {
        197							if (rot == 0 && p[p_y][p_x-1] == 0) {
        198								p[p_y][p_x-1] = p[p_y][p_x];
        199								p[p_y][p_x]   = p[p_y][p_x+1];
        200								p[p_y][p_x+1] = 0;
        201								p_x--;
        202								repaint();
        203							}
        204							else if (p[p_y][p_x-1] == 0 && p[p_y+1][p_x-1] == 0) {
        205								p[p_y][p_x-1]   = p[p_y][p_x];
        206								p[p_y+1][p_x-1] = p[p_y+1][p_x];
        207								p[p_y][p_x]     = 0;
        208								p[p_y+1][p_x]   = 0;
        209								p_x--;
        210								repaint();
        211							}
        212						}
        213					}
        214					else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {   // 右矢印(右移動)
        215						if (rot == 0) {
        216							if (p_x < gm.col-2 && p[p_y][p_x+2] == 0) {
        217								p[p_y][p_x+2] = p[p_y][p_x+1];
        218								p[p_y][p_x+1] = p[p_y][p_x];
        219								p[p_y][p_x]   = 0;
        220								p_x++;
        221								repaint();
        222							}
        223						}
        224						else {
        225							if (p_x < gm.col-1 && p[p_y][p_x+1] == 0 && p[p_y+1][p_x+1] == 0) {
        226								p[p_y][p_x+1]   = p[p_y][p_x];
        227								p[p_y+1][p_x+1] = p[p_y+1][p_x];
        228								p[p_y][p_x]     = 0;
        229								p[p_y+1][p_x]   = 0;
        230								p_x++;
        231								repaint();
        232							}
        233						}
        234					}
        235					else if (e.getKeyCode() == KeyEvent.VK_UP) {   // 上矢印(上下または左右入れ替え)
        236						if (rot == 0) {
        237							k             = p[p_y][p_x];
        238							p[p_y][p_x]   = p[p_y][p_x+1];
        239							p[p_y][p_x+1] = k;
        240						}
        241						else {
        242							k             = p[p_y][p_x];
        243							p[p_y][p_x]   = p[p_y+1][p_x];
        244							p[p_y+1][p_x] = k;
        245						}
        246						repaint();
        247					}
        248					else if (e.getKeyCode() == KeyEvent.VK_DOWN) {   // 下矢印(90度または-90度回転)
        249						if (rot == 0 && p[p_y+1][p_x] == 0) {
        250							if (p_y < gm.row-1) {
        251								p[p_y+1][p_x] = p[p_y][p_x+1];
        252								p[p_y][p_x+1] = 0;
        253								rot           = 1;
        254								repaint();
        255							}
        256						}
        257						else {
        258							if (p_x < gm.col-1 && p[p_y][p_x+1] == 0) {
        259								p[p_y][p_x+1] = p[p_y+1][p_x];
        260								p[p_y+1][p_x] = 0;
        261								rot           = 0;
        262								repaint();
        263							}
        264						}
        265					}
        266				}
        267			}
        268		}
        269	}
        					
        063 行目

          キーイベントを受け付けるために,KeyListener を追加している.

        098 行目~ 106 行目

          ピースが縦並びの場合の落下処理.横並びの場合も同様であるが,最下段に到達したときは,キーイベントを受け付けないようにしている( 106 行目)

        185 行目

          キーイベントを有効にしている.

        198 行目~ 202 行目

          左矢印キーが押され,ピースが横並びであった場合の処理である.左側に他のピースが存在しない場合は,左側に移動する.

        205 行目~ 210 行目

          左矢印キーが押され,ピースが縦並びであった場合の処理である.左側に他のピースが存在しない場合は,左側に移動する.

        215 行目~ 233 行目

          右矢印キーによって右移動するための処理である.左矢印キーが押された場合と同様の処理を行っている.

        236 行目~ 246 行目

          上矢印キーが押された場合の処理であり,上下,または,左右の色を入れ替えている.

        249 行目~ 264 行目

          下矢印キーが押された場合の処理であり,回転可能な場合は,回転させる処理を行っている.

      5. ピースの積み上げ(「AWT の利用」,「Swing の利用」):  このままでは,ピースが積み上がっていきません.落下しているピースが,他のピースの上に乗ったらそこで止めるための処理が必要になります.特に,横並びの場合,一つのピースだけが他のピースの上に乗った場合,キーイベントの処理を受け付けないようにした後(変数 ok を false ),乗っていないピースだけを落下させる処理が必要になる点に注意して下さい.また,上まで積み上がるとゲームオーバーになる処理も追加しています.

        AWT を利用した場合

        001	/********************************/
        002	/* ぷよぷよ(ゲーム全体の制御) */
        003	/********************************/
        004	import java.awt.*;
        005	import java.awt.event.*;
        006	import java.applet.*;
        007	import java.util.*;
        008	
        009	public class Game2 extends Applet implements Runnable
        010	{
        011		Puyo py;
        012		boolean game = true;   // ゲーム中か否か
        013		boolean state = true;   // スレッドが動作中か否か
        014		int p_x = 1, p_y = 3;   // 動いているピースの位置(列と行)
        015		int row = 12, col = 5;   // ブロックの行数と列数(ブロックの大きさは 30 ピクセル)
        016		int rot = 0;   // 横か縦か(0:横,1:縦)
        017		int p[][] = new int [row][col];   // 各ブロックに存在するピースの色( 0 の時は存在しない)
        018		boolean ok = true;   // キーイベントを受け付けるか否か
        019		Random rn;
        020		Thread th;
        021	
        022		/************/
        023		/* 初期設定 */
        024		/************/
        025		public void init() {
        026						// 背景色
        027			setBackground(new Color(238, 255, 238));
        028						// ゲームパネルの配置
        029			setLayout(null);
        030			py = new Puyo(this);
        031			add(py);
        032			py.setSize(30*col, 30*row);
        033			py.setLocation(10, 10);
        034			py.setBackground(Color.white);
        035			rn = new Random();
        036						// ピースの選択
        037			select();
        038						// スレッドの生成
        039			th = new Thread(this);
        040			th.start();
        041		}
        042	
        043		/******************/
        044		/* 左右上下の余裕 */
        045		/******************/
        046		public Insets getInsets()
        047		{
        048			return new Insets(10, 10, 10, 10);
        049		}
        050	
        051		/******************/
        052		/* スレッドの停止 */
        053		/******************/
        054		public void stop()
        055		{
        056			state = false;
        057		}
        058	
        059		/******************/
        060		/* スレッドの実行 */
        061		/******************/
        062		public void run()
        063		{
        064			int i1, i2, i3, i4, ct;
        065			int pp[][] = new int [row][col];
        066	
        067			while (state) {
        068				try {
        069					th.sleep(500);
        070				}
        071				catch (InterruptedException e) {}
        072							// ピースの落下
        073				ct = 0;   // 落下したか否かを示す
        074				if (rot == 0) {   // 横並び
        075					if (p_y < row-1) {
        076						if (ok) {
        077							if (p[p_y+1][p_x] == 0 && p[p_y+1][p_x+1] == 0) {
        078								ct              = 1;
        079								p[p_y+1][p_x]   = p[p_y][p_x];
        080								p[p_y+1][p_x+1] = p[p_y][p_x+1];
        081								p[p_y][p_x]     = 0;
        082								p[p_y][p_x+1]   = 0;
        083								p_y++;
        084							}
        085							else {
        086								ok = false;
        087								if (p[p_y+1][p_x] == 0) {
        088									ct            = 1;
        089									p[p_y+1][p_x] = p[p_y][p_x];
        090									p[p_y][p_x]   = 0;
        091									p_y++;
        092								}
        093								else if (p[p_y+1][p_x+1] == 0) {
        094									ct              = 1;
        095									p[p_y+1][p_x+1] = p[p_y][p_x+1];
        096									p[p_y][p_x+1]   = 0;
        097									p_x++;
        098									p_y++;
        099								}
        100							}
        101						}
        102						else {
        103							if (p[p_y+1][p_x] == 0) {
        104								ct            = 1;
        105								p[p_y+1][p_x] = p[p_y][p_x];
        106								p[p_y][p_x]   = 0;
        107								p_y++;
        108							}
        109						}
        110					}
        111				}
        112				else {   // 縦並び
        113					if (p_y < row-2 && p[p_y+2][p_x] == 0) {
        114						ct = 1;
        115						p[p_y+2][p_x] = p[p_y+1][p_x];
        116						p[p_y+1][p_x] = p[p_y][p_x];
        117						p[p_y][p_x]   = 0;
        118						p_y++;
        119					}
        120					else
        121						ok = false;
        122				}
        123							// 消去と次のピース
        124				if (ct == 0)
        125					select();
        126							// 再描画
        127				py.repaint();
        128			}
        129		}
        130	
        131		/****************/
        132		/* ピースの選択 */
        133		/****************/
        134		void select()
        135		{
        136			int color;
        137	
        138			ok  = true;
        139			rot = 0;
        140			p_y = 0;
        141			p_x = (int)(rn.nextDouble() * (col - 1));
        142			if (p_x > col-2)
        143				p_x = col - 2;
        144	
        145			if (p[0][p_x] == 0 && p[0][p_x+1] == 0) {
        146				color = (int)(rn.nextDouble() * 4) + 1;
        147				if (color > 4)
        148					color = 4;
        149				p[0][p_x] = color;
        150				color = (int)(rn.nextDouble() * 4) + 1;
        151				if (color > 4)
        152					color = 4;
        153				p[0][p_x+1] = color;
        154			}
        155			else
        156				game = false;
        157		}
        158	}
        159	
        160	/****************/
        161	/* ゲームパネル */
        162	/****************/
        163	class Puyo extends Panel
        164	{
        165		Game2 gm;
        166	
        167		/******************/
        168		/* コンストラクタ */
        169		/******************/
        170		Puyo (Game2 gm_t)
        171		{
        172			gm = gm_t;
        173			addKeyListener(new Key_e());
        174		}
        175	
        176		/********/
        177		/* 描画 */
        178		/********/
        179		public void paint (Graphics g)
        180		{
        181			int i1, i2;
        182								// ゲーム中
        183			if (gm.game) {
        184				for (i1 = 0; i1 < gm.row; i1++) {
        185					for (i2 = 0; i2 < gm.col; i2++) {
        186						if (gm.p[i1][i2] > 0) {
        187							switch (gm.p[i1][i2]) {
        188								case 1:
        189									g.setColor(Color.red);
        190									break;
        191								case 2:
        192									g.setColor(Color.pink);
        193									break;
        194								case 3:
        195									g.setColor(Color.green);
        196									break;
        197								case 4:
        198									g.setColor(Color.blue);
        199									break;
        200							}
        201							g.fillRect(i2*30, i1*30, 30, 30);
        202						}
        203					}
        204				}
        205			}
        206								// ゲームオーバー
        207			else {
        208				Font f = new Font("TimesRoman", Font.BOLD, 40);
        209				g.setFont(f);
        210				g.drawString("Game", 20, 200);
        211				g.drawString("Over!", 25, 250);
        212				gm.state = false;
        213			}
        214		}
        215	
        216		/************************/
        217		/* キーイベントの有効化 */
        218		/************************/
        219		public boolean isFocusable() { return true; }
        220	
        221		/**********************/
        222		/* キーイベントの処理 */
        223		/**********************/
        224		class Key_e extends KeyAdapter {
        225			public void keyPressed(KeyEvent e)
        226			{
        227				int k;
        228				if (gm.ok) {
        229					if (e.getKeyCode() == KeyEvent.VK_LEFT) {   // 左矢印(左移動)
        230						if (gm.p_x > 0) {
        231							if (gm.rot == 0 && gm.p[gm.p_y][gm.p_x-1] == 0) {
        232								gm.p[gm.p_y][gm.p_x-1] = gm.p[gm.p_y][gm.p_x];
        233								gm.p[gm.p_y][gm.p_x]   = gm.p[gm.p_y][gm.p_x+1];
        234								gm.p[gm.p_y][gm.p_x+1] = 0;
        235								gm.p_x--;
        236								repaint();
        237							}
        238							else if (gm.p[gm.p_y][gm.p_x-1] == 0 && gm.p[gm.p_y+1][gm.p_x-1] == 0) {
        239								gm.p[gm.p_y][gm.p_x-1]   = gm.p[gm.p_y][gm.p_x];
        240								gm.p[gm.p_y+1][gm.p_x-1] = gm.p[gm.p_y+1][gm.p_x];
        241								gm.p[gm.p_y][gm.p_x]     = 0;
        242								gm.p[gm.p_y+1][gm.p_x]   = 0;
        243								gm.p_x--;
        244								repaint();
        245							}
        246						}
        247					}
        248					else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {   // 右矢印(右移動)
        249						if (gm.rot == 0) {
        250							if (gm.p_x < gm.col-2 && gm.p[gm.p_y][gm.p_x+2] == 0) {
        251								gm.p[gm.p_y][gm.p_x+2] = gm.p[gm.p_y][gm.p_x+1];
        252								gm.p[gm.p_y][gm.p_x+1] = gm.p[gm.p_y][gm.p_x];
        253								gm.p[gm.p_y][gm.p_x]   = 0;
        254								gm.p_x++;
        255								repaint();
        256							}
        257						}
        258						else {
        259							if (gm.p_x < gm.col-1 && gm.p[gm.p_y][gm.p_x+1] == 0 && gm.p[gm.p_y+1][gm.p_x+1] == 0) {
        260								gm.p[gm.p_y][gm.p_x+1]   = gm.p[gm.p_y][gm.p_x];
        261								gm.p[gm.p_y+1][gm.p_x+1] = gm.p[gm.p_y+1][gm.p_x];
        262								gm.p[gm.p_y][gm.p_x]     = 0;
        263								gm.p[gm.p_y+1][gm.p_x]   = 0;
        264								gm.p_x++;
        265								repaint();
        266							}
        267						}
        268					}
        269					else if (e.getKeyCode() == KeyEvent.VK_UP) {   // 上矢印(上下または左右入れ替え)
        270						if (gm.rot == 0) {
        271							k                      = gm.p[gm.p_y][gm.p_x];
        272							gm.p[gm.p_y][gm.p_x]   = gm.p[gm.p_y][gm.p_x+1];
        273							gm.p[gm.p_y][gm.p_x+1] = k;
        274						}
        275						else {
        276							k                      = gm.p[gm.p_y][gm.p_x];
        277							gm.p[gm.p_y][gm.p_x]   = gm.p[gm.p_y+1][gm.p_x];
        278							gm.p[gm.p_y+1][gm.p_x] = k;
        279						}
        280						repaint();
        281					}
        282					else if (e.getKeyCode() == KeyEvent.VK_DOWN) {   // 下矢印(90度または-90度回転)
        283						if (gm.rot == 0 && gm.p[gm.p_y+1][gm.p_x] == 0) {
        284							if (gm.p_y < gm.row-1) {
        285								gm.p[gm.p_y+1][gm.p_x] = gm.p[gm.p_y][gm.p_x+1];
        286								gm.p[gm.p_y][gm.p_x+1] = 0;
        287								gm.rot                 = 1;
        288								repaint();
        289							}
        290						}
        291						else {
        292							if (gm.p_x < gm.col-1 && gm.p[gm.p_y][gm.p_x+1] == 0) {
        293								gm.p[gm.p_y][gm.p_x+1] = gm.p[gm.p_y+1][gm.p_x];
        294								gm.p[gm.p_y+1][gm.p_x] = 0;
        295								gm.rot                 = 0;
        296								repaint();
        297							}
        298						}
        299					}
        300				}
        301			}
        302		}
        303	}
        					
        077 行目~ 084 行目

          ピースが横並びで,かつ,2 つのピースが同時に落下する場合の処理である.

        086 行目

          087 行目~ 099 行目は,左右いずれかのピースが他のピースの上に乗った場合の処理である.ここでは,変数 ok を false に設定し,イベントを受け付けないようにしている.従って,ok が false であることは,ピースが最下段に到達するか,または,横並びの 2 つのピースが分離し,1 つのピースだけが落下している状態であることを意味する.

        087 行目~ 092 行目

          右側のピースが他のピースの上に乗り,左側のピースだけが落下する状態を開始する処理である.

        093 行目~ 099 行目

          上と同様,左側のピースが他のピースの上に乗り,右側のピースだけが落下する状態を開始する処理である.

        103 行目~ 108 行目

          1 つのピースだけが落下している場合に対する処理である.

        113 行目~ 121 行目

          ピースが縦に並んでいる場合に対する落下処理である.

        145 行目,155 行目~ 156 行目

          ピースを生成した位置に,他のピースが存在した場合は,ゲームオーバーにしている.

        Swing を利用した場合

        001	/********************************/
        002	/* ぷよぷよ(ゲーム全体の制御) */
        003	/********************************/
        004	import java.awt.*;
        005	import java.awt.event.*;
        006	import javax.swing.*;
        007	import java.util.*;
        008	
        009	public class Game2 extends JApplet
        010	{
        011		int row = 12, col = 5;   // ブロックの行数と列数(ブロックの大きさは 30 ピクセル)
        012		Puyo py;
        013	
        014		/************/
        015		/* 初期設定 */
        016		/************/
        017		public void init() {
        018						// 背景色
        019			setBackground(new Color(238, 255, 238));
        020						// ゲームパネルの配置
        021			Container cp = getContentPane();
        022			cp.setBackground(new Color(238, 255, 238));
        023			cp.setLayout(null);
        024			py = new Puyo(this);
        025			cp.add(py);
        026			py.setSize(30*col, 30*row);
        027			py.setLocation(10, 10);
        028			py.setBackground(Color.white);
        029		}
        030	
        031		/******************/
        032		/* スレッドの停止 */
        033		/******************/
        034		public void stop()
        035		{
        036			py.state = false;
        037		}
        038	}
        039	
        040	/****************/
        041	/* ゲームパネル */
        042	/****************/
        043	class Puyo extends JPanel implements Runnable
        044	{
        045		int p_x = 1, p_y = 3;   // 動いているピースの位置(列と行)
        046		int p[][];   // 各ブロックに存在するピースの色( 0 の時は存在しない)
        047		int rot = 0;   // 横か縦か(0:横,1:縦)
        048		boolean game = true;   // ゲーム中か否か
        049		boolean state = true;   // スレッドが動作中か否か
        050		boolean ok = true;   // キーイベントを受け付けるか否か
        051		Thread th;
        052		Random rn;
        053		Game2 gm;
        054	
        055		/******************/
        056		/* コンストラクタ */
        057		/******************/
        058		Puyo (Game2 gm_t)
        059		{
        060			gm = gm_t;
        061			rn = new Random();
        062			p  = new int [gm.row][gm.col];
        063			addKeyListener(new Key_e());
        064						// ピースの選択
        065			select();
        066						// スレッドの生成
        067			th = new Thread(this);
        068			th.start();
        069		}
        070	
        071		/******************/
        072		/* スレッドの実行 */
        073		/******************/
        074		public void run()
        075		{
        076			int ct;
        077	
        078			while (state) {
        079				try {
        080					th.sleep(500);
        081				}
        082				catch (InterruptedException e) {}
        083							// ピースの落下
        084				ct = 0;   // 落下したか否かを示す
        085				if (rot == 0) {   // 横並び
        086					if (p_y < gm.row-1) {
        087						if (ok) {
        088							if (p[p_y+1][p_x] == 0 && p[p_y+1][p_x+1] == 0) {
        089								ct              = 1;
        090								p[p_y+1][p_x]   = p[p_y][p_x];
        091								p[p_y+1][p_x+1] = p[p_y][p_x+1];
        092								p[p_y][p_x]     = 0;
        093								p[p_y][p_x+1]   = 0;
        094								p_y++;
        095							}
        096							else {
        097								ok = false;
        098								if (p[p_y+1][p_x] == 0) {
        099									ct            = 1;
        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 if (p[p_y+1][p_x+1] == 0) {
        105									ct              = 1;
        106									p[p_y+1][p_x+1] = p[p_y][p_x+1];
        107									p[p_y][p_x+1]   = 0;
        108									p_x++;
        109									p_y++;
        110								}
        111							}
        112						}
        113						else {
        114							if (p[p_y+1][p_x] == 0) {
        115								ct            = 1;
        116								p[p_y+1][p_x] = p[p_y][p_x];
        117								p[p_y][p_x]   = 0;
        118								p_y++;
        119							}
        120						}
        121					}
        122				}
        123				else {   // 縦並び
        124					if (p_y < gm.row-2 && p[p_y+2][p_x] == 0) {
        125						ct = 1;
        126						p[p_y+2][p_x] = p[p_y+1][p_x];
        127						p[p_y+1][p_x] = p[p_y][p_x];
        128						p[p_y][p_x]   = 0;
        129						p_y++;
        130					}
        131					else
        132						ok = false;
        133				}
        134							// 消去と次のピース
        135				if (ct == 0)
        136					select();
        137							// 再描画
        138				repaint();
        139			}
        140		}
        141	
        142		/****************/
        143		/* ピースの選択 */
        144		/****************/
        145		void select()
        146		{
        147			int color;
        148	
        149			ok  = true;
        150			rot = 0;
        151			p_y = 0;
        152			p_x = (int)(rn.nextDouble() * (gm.col - 1));
        153			if (p_x > gm.col-2)
        154				p_x = gm.col - 2;
        155	
        156			if (p[0][p_x] == 0 && p[0][p_x+1] == 0) {
        157				color = (int)(rn.nextDouble() * 4) + 1;
        158				if (color > 4)
        159					color = 4;
        160				p[0][p_x] = color;
        161				color = (int)(rn.nextDouble() * 4) + 1;
        162				if (color > 4)
        163					color = 4;
        164				p[0][p_x+1] = color;
        165			}
        166			else
        167				game = false;
        168		}
        169	
        170		/********/
        171		/* 描画 */
        172		/********/
        173		public void paintComponent (Graphics g)
        174		{
        175			super.paintComponent(g);   // 親クラスの描画(必ず必要)
        176	
        177			int i1, i2;
        178								// ゲーム中
        179			if (game) {
        180				for (i1 = 0; i1 < gm.row; i1++) {
        181					for (i2 = 0; i2 < gm.col; i2++) {
        182						if (p[i1][i2] > 0) {
        183							switch (p[i1][i2]) {
        184								case 1:
        185									g.setColor(Color.red);
        186									break;
        187								case 2:
        188									g.setColor(Color.pink);
        189									break;
        190								case 3:
        191									g.setColor(Color.green);
        192									break;
        193								case 4:
        194									g.setColor(Color.blue);
        195									break;
        196							}
        197							g.fillRect(i2*30, i1*30, 30, 30);
        198						}
        199					}
        200				}
        201			}
        202								// ゲームオーバー
        203			else {
        204				Font f = new Font("TimesRoman", Font.BOLD, 40);
        205				g.setFont(f);
        206				g.drawString("Game", 20, 200);
        207				g.drawString("Over!", 25, 250);
        208				state = false;
        209			}
        210		}
        211	
        212		/************************/
        213		/* キーイベントの有効化 */
        214		/************************/
        215		public boolean isFocusable() { return true; }
        216	
        217		/**********************/
        218		/* キーイベントの処理 */
        219		/**********************/
        220		class Key_e extends KeyAdapter {
        221			public void keyPressed(KeyEvent e)
        222			{
        223				int k;
        224				if (ok) {
        225					if (e.getKeyCode() == KeyEvent.VK_LEFT) {   // 左矢印(左移動)
        226						if (p_x > 0) {
        227							if (rot == 0 && p[p_y][p_x-1] == 0) {
        228								p[p_y][p_x-1] = p[p_y][p_x];
        229								p[p_y][p_x]   = p[p_y][p_x+1];
        230								p[p_y][p_x+1] = 0;
        231								p_x--;
        232								repaint();
        233							}
        234							else if (p[p_y][p_x-1] == 0 && p[p_y+1][p_x-1] == 0) {
        235								p[p_y][p_x-1]   = p[p_y][p_x];
        236								p[p_y+1][p_x-1] = p[p_y+1][p_x];
        237								p[p_y][p_x]     = 0;
        238								p[p_y+1][p_x]   = 0;
        239								p_x--;
        240								repaint();
        241							}
        242						}
        243					}
        244					else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {   // 右矢印(右移動)
        245						if (rot == 0) {
        246							if (p_x < gm.col-2 && p[p_y][p_x+2] == 0) {
        247								p[p_y][p_x+2] = p[p_y][p_x+1];
        248								p[p_y][p_x+1] = p[p_y][p_x];
        249								p[p_y][p_x]   = 0;
        250								p_x++;
        251								repaint();
        252							}
        253						}
        254						else {
        255							if (p_x < gm.col-1 && p[p_y][p_x+1] == 0 && p[p_y+1][p_x+1] == 0) {
        256								p[p_y][p_x+1]   = p[p_y][p_x];
        257								p[p_y+1][p_x+1] = p[p_y+1][p_x];
        258								p[p_y][p_x]     = 0;
        259								p[p_y+1][p_x]   = 0;
        260								p_x++;
        261								repaint();
        262							}
        263						}
        264					}
        265					else if (e.getKeyCode() == KeyEvent.VK_UP) {   // 上矢印(上下または左右入れ替え)
        266						if (rot == 0) {
        267							k             = p[p_y][p_x];
        268							p[p_y][p_x]   = p[p_y][p_x+1];
        269							p[p_y][p_x+1] = k;
        270						}
        271						else {
        272							k             = p[p_y][p_x];
        273							p[p_y][p_x]   = p[p_y+1][p_x];
        274							p[p_y+1][p_x] = k;
        275						}
        276						repaint();
        277					}
        278					else if (e.getKeyCode() == KeyEvent.VK_DOWN) {   // 下矢印(90度または-90度回転)
        279						if (rot == 0 && p[p_y+1][p_x] == 0) {
        280							if (p_y < gm.row-1) {
        281								p[p_y+1][p_x] = p[p_y][p_x+1];
        282								p[p_y][p_x+1] = 0;
        283								rot           = 1;
        284								repaint();
        285							}
        286						}
        287						else {
        288							if (p_x < gm.col-1 && p[p_y][p_x+1] == 0) {
        289								p[p_y][p_x+1] = p[p_y+1][p_x];
        290								p[p_y+1][p_x] = 0;
        291								rot           = 0;
        292								repaint();
        293							}
        294						}
        295					}
        296				}
        297			}
        298		}
        299	}
        					
        088 行目~ 095 行目

          ピースが横並びで,かつ,2 つのピースが同時に落下する場合の処理である.

        097 行目

          098 行目~ 110 行目は,左右いずれかのピースが他のピースの上に乗った場合の処理である.ここでは,変数 ok を false に設定し,イベントを受け付けないようにしている.従って,ok が false であることは,ピースが最下段に到達するか,または,横並びの 2 つのピースが分離し,1 つのピースだけが落下している状態であることを意味する.

        098 行目~ 103 行目

          右側のピースが他のピースの上に乗り,左側のピースだけが落下する状態を開始する処理である.

        104 行目~ 110 行目

          上と同様,左側のピースが他のピースの上に乗り,右側のピースだけが落下する状態を開始する処理である.

        114 行目~ 119 行目

          1 つのピースだけが落下している場合に対する処理である.

        124 行目~ 132 行目

          ピースが縦に並んでいる場合に対する落下処理である.

        156 行目,166 行目~ 167 行目

          ピースを生成した位置に,他のピースが存在した場合は,ゲームオーバーにしている.

      6. ピースの削除(「AWT の利用」,「Swing の利用」):  最後に行わなければならないのがピースの削除です.同じ色のピースが縦横 4 個以上並んだ場合,それらを削除し,それらの上に乗っていたピースを可能なところまで落下させます.メソッド search で同じ色のピースを数え,メソッド delete で削除しています.

        AWT を利用した場合

        001	/********************************/
        002	/* ぷよぷよ(ゲーム全体の制御) */
        003	/********************************/
        004	import java.awt.*;
        005	import java.awt.event.*;
        006	import java.applet.*;
        007	import java.util.*;
        008	
        009	public class Game2 extends Applet implements Runnable
        010	{
        011		Puyo py;
        012		boolean game = true;   // ゲーム中か否か
        013		boolean state = true;   // スレッドが動作中か否か
        014		int p_x = 1, p_y = 3;   // 動いているピースの位置(列と行)
        015		int row = 12, col = 5;   // ブロックの行数と列数(ブロックの大きさは 30 ピクセル)
        016		int rot = 0;   // 横か縦か(0:横,1:縦)
        017		int p[][] = new int [row][col];   // 各ブロックに存在するピースの色( 0 の時は存在しない)
        018		boolean ok = true;   // キーイベントを受け付けるか否か
        019		Random rn;
        020		Thread th;
        021	
        022		/************/
        023		/* 初期設定 */
        024		/************/
        025		public void init() {
        026						// 背景色
        027			setBackground(new Color(238, 255, 238));
        028						// ゲームパネルの配置
        029			setLayout(null);
        030			py = new Puyo(this);
        031			add(py);
        032			py.setSize(30*col, 30*row);
        033			py.setLocation(10, 10);
        034			py.setBackground(Color.white);
        035			rn = new Random();
        036						// ピースの選択
        037			select();
        038						// スレッドの生成
        039			th = new Thread(this);
        040			th.start();
        041		}
        042	
        043		/******************/
        044		/* 左右上下の余裕 */
        045		/******************/
        046		public Insets getInsets()
        047		{
        048			return new Insets(10, 10, 10, 10);
        049		}
        050	
        051		/******************/
        052		/* スレッドの停止 */
        053		/******************/
        054		public void stop()
        055		{
        056			state = false;
        057		}
        058	
        059		/******************/
        060		/* スレッドの実行 */
        061		/******************/
        062		public void run()
        063		{
        064			int i1, i2, i3, i4, ct;
        065			int pp[][] = new int [row][col];
        066	
        067			while (state) {
        068				try {
        069					th.sleep(500);
        070				}
        071				catch (InterruptedException e) {}
        072							// ピースの落下
        073				ct = 0;   // 落下したか否かを示す
        074				if (rot == 0) {   // 横並び
        075					if (p_y < row-1) {
        076						if (ok) {
        077							if (p[p_y+1][p_x] == 0 && p[p_y+1][p_x+1] == 0) {
        078								ct              = 1;
        079								p[p_y+1][p_x]   = p[p_y][p_x];
        080								p[p_y+1][p_x+1] = p[p_y][p_x+1];
        081								p[p_y][p_x]     = 0;
        082								p[p_y][p_x+1]   = 0;
        083								p_y++;
        084							}
        085							else {
        086								ok = false;
        087								if (p[p_y+1][p_x] == 0) {
        088									ct            = 1;
        089									p[p_y+1][p_x] = p[p_y][p_x];
        090									p[p_y][p_x]   = 0;
        091									p_y++;
        092								}
        093								else if (p[p_y+1][p_x+1] == 0) {
        094									ct              = 1;
        095									p[p_y+1][p_x+1] = p[p_y][p_x+1];
        096									p[p_y][p_x+1]   = 0;
        097									p_x++;
        098									p_y++;
        099								}
        100							}
        101						}
        102						else {
        103							if (p[p_y+1][p_x] == 0) {
        104								ct            = 1;
        105								p[p_y+1][p_x] = p[p_y][p_x];
        106								p[p_y][p_x]   = 0;
        107								p_y++;
        108							}
        109						}
        110					}
        111				}
        112				else {   // 縦並び
        113					if (p_y < row-2 && p[p_y+2][p_x] == 0) {
        114						ct = 1;
        115						p[p_y+2][p_x] = p[p_y+1][p_x];
        116						p[p_y+1][p_x] = p[p_y][p_x];
        117						p[p_y][p_x]   = 0;
        118						p_y++;
        119					}
        120					else
        121						ok = false;
        122				}
        123							// 消去と次のピース
        124				if (ct == 0) {
        125					ct = 4;
        126					while (ct >= 4) {
        127						ct = 0;
        128						for (i1 = row-1; i1 >= 0 && ct < 4; i1--) {
        129							for (i2 = 0; i2 < col && ct < 4; i2++) {
        130								for (i3 = 0; i3 < row; i3++) {
        131									for (i4 = 0; i4 < col; i4++)
        132										pp[i3][i4] = 0;
        133								}
        134								if (p[i1][i2] > 0) {
        135									pp[i1][i2] = 1;
        136									ct         = search(pp, i1, i2, 1);
        137								}
        138							}
        139						}
        140						if (ct >= 4)
        141							delete(p, pp);
        142					}
        143					select();
        144				}
        145							// 再描画
        146				py.repaint();
        147			}
        148		}
        149	
        150		/****************/
        151		/* ピースの選択 */
        152		/****************/
        153		void select()
        154		{
        155			int color;
        156	
        157			ok  = true;
        158			rot = 0;
        159			p_y = 0;
        160			p_x = (int)(rn.nextDouble() * (col - 1));
        161			if (p_x > col-2)
        162				p_x = col - 2;
        163	
        164			if (p[0][p_x] == 0 && p[0][p_x+1] == 0) {
        165				color = (int)(rn.nextDouble() * 4) + 1;
        166				if (color > 4)
        167					color = 4;
        168				p[0][p_x] = color;
        169				color = (int)(rn.nextDouble() * 4) + 1;
        170				if (color > 4)
        171					color = 4;
        172				p[0][p_x+1] = color;
        173			}
        174			else
        175				game = false;
        176		}
        177	
        178		/*************************************/
        179		/* 同じ色のピースを探す              */
        180		/*      pp : 同じ色のピース位置      */
        181		/*      k1,k2 : 対象としているピース */
        182		/*      c1 : 同じ色のピースの数      */
        183		/*      return : 同じ色のピースの数  */
        184		/*************************************/
        185		int search(int pp[][], int k1, int k2, int c1)
        186		{
        187			int ct = c1;
        188	
        189			if (k1 > 0 && p[k1-1][k2] == p[k1][k2] && pp[k1-1][k2] == 0) {
        190				pp[k1-1][k2] = 1;
        191				ct           = search(pp, k1-1, k2, ct+1);
        192			}
        193			if (k1 < row-1 && p[k1+1][k2] == p[k1][k2] && pp[k1+1][k2] == 0) {
        194				pp[k1+1][k2] = 1;
        195				ct           = search(pp, k1+1, k2, ct+1);
        196			}
        197			if (k2 > 0 && p[k1][k2-1] == p[k1][k2] && pp[k1][k2-1] == 0) {
        198				pp[k1][k2-1] = 1;
        199				ct           = search(pp, k1, k2-1, ct+1);
        200			}
        201			if (k2 < col-1 && p[k1][k2+1] == p[k1][k2] && pp[k1][k2+1] == 0) {
        202				pp[k1][k2+1] = 1;
        203				ct           = search(pp, k1, k2+1, ct+1);
        204			}
        205	
        206			return ct;
        207		}
        208	
        209		/********************************/
        210		/* 同じ色のピースを削除         */
        211		/*      p : ピース位置          */
        212		/*      pp : 同じ色のピース位置 */
        213		/********************************/
        214		void delete(int p[][], int pp[][])
        215		{
        216			int i1, i2, i3, k1, k2, k3;
        217						// 削除
        218			for (i1 = 0; i1 < row; i1++) {
        219				for (i2 = 0; i2 < col; i2++) {
        220					if (pp[i1][i2]  > 0)
        221						p[i1][i2] = 0;
        222				}
        223			}
        224						// 詰める
        225			for (i1 = 0; i1 < col; i1++) {
        226				k1 = 1;
        227				for (i2 = row-1; i2 > 0 && k1 >= 0; i2--) {
        228					if (p[i2][i1] == 0) {
        229						k1 = -1;
        230						for (i3 = i2-1; i3 >= 0 && k1 < 0; i3--) {
        231							if (p[i3][i1] > 0)
        232								k1 = i3;
        233						}
        234						if (k1 >= 0) {
        235							k2 = i2;
        236							k3 = k2 - k1;
        237							while (k1 >= 0) {
        238								p[k2][i1] = p[k1][i1];
        239								k1--;
        240								k2--;
        241							}
        242							k1++;
        243							for (i3 = 0; i3 < k3; i3++)
        244								p[i3][i1] = 0;
        245						}
        246					}
        247				}
        248			}
        249		}
        250	}
        251	
        252	
        253	
        254	/****************/
        255	/* ゲームパネル */
        256	/****************/
        257	class Puyo extends Panel
        258	{
        259		Game2 gm;
        260	
        261		/******************/
        262		/* コンストラクタ */
        263		/******************/
        264		Puyo (Game2 gm_t)
        265		{
        266			gm = gm_t;
        267			addKeyListener(new Key_e());
        268		}
        269	
        270		/********/
        271		/* 描画 */
        272		/********/
        273		public void paint (Graphics g)
        274		{
        275			int i1, i2;
        276								// ゲーム中
        277			if (gm.game) {
        278				for (i1 = 0; i1 < gm.row; i1++) {
        279					for (i2 = 0; i2 < gm.col; i2++) {
        280						if (gm.p[i1][i2] > 0) {
        281							switch (gm.p[i1][i2]) {
        282								case 1:
        283									g.setColor(Color.red);
        284									break;
        285								case 2:
        286									g.setColor(Color.pink);
        287									break;
        288								case 3:
        289									g.setColor(Color.green);
        290									break;
        291								case 4:
        292									g.setColor(Color.blue);
        293									break;
        294							}
        295							g.fillRect(i2*30, i1*30, 30, 30);
        296						}
        297					}
        298				}
        299			}
        300								// ゲームオーバー
        301			else {
        302				Font f = new Font("TimesRoman", Font.BOLD, 40);
        303				g.setFont(f);
        304				g.drawString("Game", 20, 200);
        305				g.drawString("Over!", 25, 250);
        306				gm.state = false;
        307			}
        308		}
        309	
        310		/************************/
        311		/* キーイベントの有効化 */
        312		/************************/
        313		public boolean isFocusable() { return true; }
        314	
        315		/**********************/
        316		/* キーイベントの処理 */
        317		/**********************/
        318		class Key_e extends KeyAdapter {
        319			public void keyPressed(KeyEvent e)
        320			{
        321				int k;
        322				if (gm.ok) {
        323					if (e.getKeyCode() == KeyEvent.VK_LEFT) {   // 左矢印(左移動)
        324						if (gm.p_x > 0) {
        325							if (gm.rot == 0 && gm.p[gm.p_y][gm.p_x-1] == 0) {
        326								gm.p[gm.p_y][gm.p_x-1] = gm.p[gm.p_y][gm.p_x];
        327								gm.p[gm.p_y][gm.p_x]   = gm.p[gm.p_y][gm.p_x+1];
        328								gm.p[gm.p_y][gm.p_x+1] = 0;
        329								gm.p_x--;
        330								repaint();
        331							}
        332							else if (gm.p[gm.p_y][gm.p_x-1] == 0 && gm.p[gm.p_y+1][gm.p_x-1] == 0) {
        333								gm.p[gm.p_y][gm.p_x-1]   = gm.p[gm.p_y][gm.p_x];
        334								gm.p[gm.p_y+1][gm.p_x-1] = gm.p[gm.p_y+1][gm.p_x];
        335								gm.p[gm.p_y][gm.p_x]     = 0;
        336								gm.p[gm.p_y+1][gm.p_x]   = 0;
        337								gm.p_x--;
        338								repaint();
        339							}
        340						}
        341					}
        342					else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {   // 右矢印(右移動)
        343						if (gm.rot == 0) {
        344							if (gm.p_x < gm.col-2 && gm.p[gm.p_y][gm.p_x+2] == 0) {
        345								gm.p[gm.p_y][gm.p_x+2] = gm.p[gm.p_y][gm.p_x+1];
        346								gm.p[gm.p_y][gm.p_x+1] = gm.p[gm.p_y][gm.p_x];
        347								gm.p[gm.p_y][gm.p_x]   = 0;
        348								gm.p_x++;
        349								repaint();
        350							}
        351						}
        352						else {
        353							if (gm.p_x < gm.col-1 && gm.p[gm.p_y][gm.p_x+1] == 0 && gm.p[gm.p_y+1][gm.p_x+1] == 0) {
        354								gm.p[gm.p_y][gm.p_x+1]   = gm.p[gm.p_y][gm.p_x];
        355								gm.p[gm.p_y+1][gm.p_x+1] = gm.p[gm.p_y+1][gm.p_x];
        356								gm.p[gm.p_y][gm.p_x]     = 0;
        357								gm.p[gm.p_y+1][gm.p_x]   = 0;
        358								gm.p_x++;
        359								repaint();
        360							}
        361						}
        362					}
        363					else if (e.getKeyCode() == KeyEvent.VK_UP) {   // 上矢印(上下または左右入れ替え)
        364						if (gm.rot == 0) {
        365							k                      = gm.p[gm.p_y][gm.p_x];
        366							gm.p[gm.p_y][gm.p_x]   = gm.p[gm.p_y][gm.p_x+1];
        367							gm.p[gm.p_y][gm.p_x+1] = k;
        368						}
        369						else {
        370							k                      = gm.p[gm.p_y][gm.p_x];
        371							gm.p[gm.p_y][gm.p_x]   = gm.p[gm.p_y+1][gm.p_x];
        372							gm.p[gm.p_y+1][gm.p_x] = k;
        373						}
        374						repaint();
        375					}
        376					else if (e.getKeyCode() == KeyEvent.VK_DOWN) {   // 下矢印(90度または-90度回転)
        377						if (gm.rot == 0 && gm.p[gm.p_y+1][gm.p_x] == 0) {
        378							if (gm.p_y < gm.row-1) {
        379								gm.p[gm.p_y+1][gm.p_x] = gm.p[gm.p_y][gm.p_x+1];
        380								gm.p[gm.p_y][gm.p_x+1] = 0;
        381								gm.rot                 = 1;
        382								repaint();
        383							}
        384						}
        385						else {
        386							if (gm.p_x < gm.col-1 && gm.p[gm.p_y][gm.p_x+1] == 0) {
        387								gm.p[gm.p_y][gm.p_x+1] = gm.p[gm.p_y+1][gm.p_x];
        388								gm.p[gm.p_y+1][gm.p_x] = 0;
        389								gm.rot                 = 0;
        390								repaint();
        391							}
        392						}
        393					}
        394				}
        395			}
        396		}
        397	}
        					
        065 行目

          作業域として使用するため,ピースの存在を示す 2 次元配列 p と同じ大きさの配列 pp を定義している.

        125 行目~ 143 行目

          ピースが再下端に達するか,または,他のピースの上に乗って停止した場合に対する処理である.127 行目~ 141 行目において,メソッド search( 185 行目~ 207 行目)によって,ピース p[i1][i2] と隣り合っている同じ色のピースの数を調べ,その値が 4 以上である場合は,メソッド delete( 214 行目~ 249 行目)によってそれらのピースを削除している.これら全体を 126 行目の while 文によって繰り返しているのは,削除の結果,再び同じ色のピースが繋がる可能性があるからである.

        185 行目~ 207 行目

          ピース p[k1][k2] の上下左右に同じ色のピースがあるか否かを,再帰呼び出しを利用して調べている.最終的に,隣り合った同じ色のピースの数が返される.

        218 行目~ 223 行目

          隣り合った同じ色のピースを削除している.

        225 行目~ 248 行目

          ピースが存在しない空白を詰める処理である.
        • 228 行目: p[i2][i1] が空白のとき以下の処理が行われる
        • 229 行目~ 234 行目: 空白の上にピースが存在するか否かを調べている.存在する場合は,その行番号が変数 k1 に設定され,235 行目以降が実行される.
        • 235 行目~ 244 行目: k2 行,及び,k2 行から k1 行の間にある空白を詰める.

        Swing を利用した場合

        001	/********************************/
        002	/* ぷよぷよ(ゲーム全体の制御) */
        003	/********************************/
        004	import java.awt.*;
        005	import java.awt.event.*;
        006	import javax.swing.*;
        007	import java.util.*;
        008	
        009	public class Game2 extends JApplet
        010	{
        011		int row = 12, col = 5;   // ブロックの行数と列数(ブロックの大きさは 30 ピクセル)
        012		Puyo py;
        013	
        014		/************/
        015		/* 初期設定 */
        016		/************/
        017		public void init() {
        018						// 背景色
        019			setBackground(new Color(238, 255, 238));
        020						// ゲームパネルの配置
        021			Container cp = getContentPane();
        022			cp.setBackground(new Color(238, 255, 238));
        023			cp.setLayout(null);
        024			py = new Puyo(this);
        025			cp.add(py);
        026			py.setSize(30*col, 30*row);
        027			py.setLocation(10, 10);
        028			py.setBackground(Color.white);
        029		}
        030	
        031		/******************/
        032		/* スレッドの停止 */
        033		/******************/
        034		public void stop()
        035		{
        036			py.state = false;
        037		}
        038	}
        039	
        040	/****************/
        041	/* ゲームパネル */
        042	/****************/
        043	class Puyo extends JPanel implements Runnable
        044	{
        045		int p_x = 1, p_y = 3;   // 動いているピースの位置(列と行)
        046		int p[][];   // 各ブロックに存在するピースの色( 0 の時は存在しない)
        047		int rot = 0;   // 横か縦か(0:横,1:縦)
        048		boolean game = true;   // ゲーム中か否か
        049		boolean state = true;   // スレッドが動作中か否か
        050		boolean ok = true;   // キーイベントを受け付けるか否か
        051		Random rn;
        052		Thread th;
        053		Game2 gm;
        054	
        055		/******************/
        056		/* コンストラクタ */
        057		/******************/
        058		Puyo (Game2 gm_t)
        059		{
        060			gm = gm_t;
        061			rn = new Random();
        062			addKeyListener(new Key_e());
        063			p = new int [gm.row][gm.col];
        064						// ピースの選択
        065			select();
        066						// スレッドの生成
        067			th = new Thread(this);
        068			th.start();
        069		}
        070	
        071		/******************/
        072		/* スレッドの実行 */
        073		/******************/
        074		public void run()
        075		{
        076			int i1, i2, i3, i4, ct;
        077			int pp[][] = new int [gm.row][gm.col];
        078	
        079			while (state) {
        080				try {
        081					th.sleep(500);
        082				}
        083				catch (InterruptedException e) {}
        084							// ピースの落下
        085				ct = 0;   // 落下したか否かを示す
        086				if (rot == 0) {   // 横並び
        087					if (p_y < gm.row-1) {
        088						if (ok) {
        089							if (p[p_y+1][p_x] == 0 && p[p_y+1][p_x+1] == 0) {
        090								ct              = 1;
        091								p[p_y+1][p_x]   = p[p_y][p_x];
        092								p[p_y+1][p_x+1] = p[p_y][p_x+1];
        093								p[p_y][p_x]     = 0;
        094								p[p_y][p_x+1]   = 0;
        095								p_y++;
        096							}
        097							else {
        098								ok = false;
        099								if (p[p_y+1][p_x] == 0) {
        100									ct            = 1;
        101									p[p_y+1][p_x] = p[p_y][p_x];
        102									p[p_y][p_x]   = 0;
        103									p_y++;
        104								}
        105								else if (p[p_y+1][p_x+1] == 0) {
        106									ct              = 1;
        107									p[p_y+1][p_x+1] = p[p_y][p_x+1];
        108									p[p_y][p_x+1]   = 0;
        109									p_x++;
        110									p_y++;
        111								}
        112							}
        113						}
        114						else {
        115							if (p[p_y+1][p_x] == 0) {
        116								ct            = 1;
        117								p[p_y+1][p_x] = p[p_y][p_x];
        118								p[p_y][p_x]   = 0;
        119								p_y++;
        120							}
        121						}
        122					}
        123				}
        124				else {   // 縦並び
        125					if (p_y < gm.row-2 && p[p_y+2][p_x] == 0) {
        126						ct = 1;
        127						p[p_y+2][p_x] = p[p_y+1][p_x];
        128						p[p_y+1][p_x] = p[p_y][p_x];
        129						p[p_y][p_x]   = 0;
        130						p_y++;
        131					}
        132					else
        133						ok = false;
        134				}
        135							// 消去と次のピース
        136				if (ct == 0) {
        137					ct = 4;
        138					while (ct >= 4) {
        139						ct = 0;
        140						for (i1 = gm.row-1; i1 >= 0 && ct < 4; i1--) {
        141							for (i2 = 0; i2 < gm.col && ct < 4; i2++) {
        142								for (i3 = 0; i3 < gm.row; i3++) {
        143									for (i4 = 0; i4 < gm.col; i4++)
        144										pp[i3][i4] = 0;
        145								}
        146								if (p[i1][i2] > 0) {
        147									pp[i1][i2] = 1;
        148									ct         = search(pp, i1, i2, 1);
        149								}
        150							}
        151						}
        152						if (ct >= 4)
        153							delete(p, pp);
        154					}
        155					select();
        156				}
        157							// 再描画
        158				repaint();
        159			}
        160		}
        161	
        162		/****************/
        163		/* ピースの選択 */
        164		/****************/
        165		void select()
        166		{
        167			int color;
        168	
        169			ok  = true;
        170			rot = 0;
        171			p_y = 0;
        172			p_x = (int)(rn.nextDouble() * (gm.col - 1));
        173			if (p_x > gm.col-2)
        174				p_x = gm.col - 2;
        175	
        176			if (p[0][p_x] == 0 && p[0][p_x+1] == 0) {
        177				color = (int)(rn.nextDouble() * 4) + 1;
        178				if (color > 4)
        179					color = 4;
        180				p[0][p_x] = color;
        181				color = (int)(rn.nextDouble() * 4) + 1;
        182				if (color > 4)
        183					color = 4;
        184				p[0][p_x+1] = color;
        185			}
        186			else
        187				game = false;
        188		}
        189	
        190		/*************************************/
        191		/* 同じ色のピースを探す              */
        192		/*      pp : 同じ色のピース位置      */
        193		/*      k1,k2 : 対象としているピース */
        194		/*      c1 : 同じ色のピースの数      */
        195		/*      return : 同じ色のピースの数  */
        196		/*************************************/
        197		int search(int pp[][], int k1, int k2, int c1)
        198		{
        199			int ct = c1;
        200	
        201			if (k1 > 0 && p[k1-1][k2] == p[k1][k2] && pp[k1-1][k2] == 0) {
        202				pp[k1-1][k2] = 1;
        203				ct           = search(pp, k1-1, k2, ct+1);
        204			}
        205			if (k1 < gm.row-1 && p[k1+1][k2] == p[k1][k2] && pp[k1+1][k2] == 0) {
        206				pp[k1+1][k2] = 1;
        207				ct           = search(pp, k1+1, k2, ct+1);
        208			}
        209			if (k2 > 0 && p[k1][k2-1] == p[k1][k2] && pp[k1][k2-1] == 0) {
        210				pp[k1][k2-1] = 1;
        211				ct           = search(pp, k1, k2-1, ct+1);
        212			}
        213			if (k2 < gm.col-1 && p[k1][k2+1] == p[k1][k2] && pp[k1][k2+1] == 0) {
        214				pp[k1][k2+1] = 1;
        215				ct           = search(pp, k1, k2+1, ct+1);
        216			}
        217	
        218			return ct;
        219		}
        220	
        221		/********************************/
        222		/* 同じ色のピースを削除         */
        223		/*      p : ピース位置          */
        224		/*      pp : 同じ色のピース位置 */
        225		/********************************/
        226		void delete(int p[][], int pp[][])
        227		{
        228			int i1, i2, i3, k1, k2, k3;
        229						// 削除
        230			for (i1 = 0; i1 < gm.row; i1++) {
        231				for (i2 = 0; i2 < gm.col; i2++) {
        232					if (pp[i1][i2]  > 0)
        233						p[i1][i2] = 0;
        234				}
        235			}
        236						// 詰める
        237			for (i1 = 0; i1 < gm.col; i1++) {
        238				k1 = 1;
        239				for (i2 = gm.row-1; i2 > 0 && k1 >= 0; i2--) {
        240					if (p[i2][i1] == 0) {
        241						k1 = -1;
        242						for (i3 = i2-1; i3 >= 0 && k1 < 0; i3--) {
        243							if (p[i3][i1] > 0)
        244								k1 = i3;
        245						}
        246						if (k1 >= 0) {
        247							k2 = i2;
        248							k3 = k2 - k1;
        249							while (k1 >= 0) {
        250								p[k2][i1] = p[k1][i1];
        251								k1--;
        252								k2--;
        253							}
        254							k1++;
        255							for (i3 = 0; i3 < k3; i3++)
        256								p[i3][i1] = 0;
        257						}
        258					}
        259				}
        260			}
        261		}
        262	
        263		/********/
        264		/* 描画 */
        265		/********/
        266		public void paintComponent (Graphics g)
        267		{
        268			super.paintComponent(g);   // 親クラスの描画(必ず必要)
        269	
        270			int i1, i2;
        271								// ゲーム中
        272			if (game) {
        273				for (i1 = 0; i1 < gm.row; i1++) {
        274					for (i2 = 0; i2 < gm.col; i2++) {
        275						if (p[i1][i2] > 0) {
        276							switch (p[i1][i2]) {
        277								case 1:
        278									g.setColor(Color.red);
        279									break;
        280								case 2:
        281									g.setColor(Color.pink);
        282									break;
        283								case 3:
        284									g.setColor(Color.green);
        285									break;
        286								case 4:
        287									g.setColor(Color.blue);
        288									break;
        289							}
        290							g.fillRect(i2*30, i1*30, 30, 30);
        291						}
        292					}
        293				}
        294			}
        295								// ゲームオーバー
        296			else {
        297				Font f = new Font("TimesRoman", Font.BOLD, 40);
        298				g.setFont(f);
        299				g.drawString("Game", 20, 200);
        300				g.drawString("Over!", 25, 250);
        301				state = false;
        302			}
        303		}
        304	
        305		/************************/
        306		/* キーイベントの有効化 */
        307		/************************/
        308		public boolean isFocusable() { return true; }
        309	
        310		/**********************/
        311		/* キーイベントの処理 */
        312		/**********************/
        313		class Key_e extends KeyAdapter {
        314			public void keyPressed(KeyEvent e)
        315			{
        316				int k;
        317				if (ok) {
        318					if (e.getKeyCode() == KeyEvent.VK_LEFT) {   // 左矢印(左移動)
        319						if (p_x > 0) {
        320							if (rot == 0 && p[p_y][p_x-1] == 0) {
        321								p[p_y][p_x-1] = p[p_y][p_x];
        322								p[p_y][p_x]   = p[p_y][p_x+1];
        323								p[p_y][p_x+1] = 0;
        324								p_x--;
        325								repaint();
        326							}
        327							else if (p[p_y][p_x-1] == 0 && p[p_y+1][p_x-1] == 0) {
        328								p[p_y][p_x-1]   = p[p_y][p_x];
        329								p[p_y+1][p_x-1] = p[p_y+1][p_x];
        330								p[p_y][p_x]     = 0;
        331								p[p_y+1][p_x]   = 0;
        332								p_x--;
        333								repaint();
        334							}
        335						}
        336					}
        337					else if (e.getKeyCode() == KeyEvent.VK_RIGHT) {   // 右矢印(右移動)
        338						if (rot == 0) {
        339							if (p_x < gm.col-2 && p[p_y][p_x+2] == 0) {
        340								p[p_y][p_x+2] = p[p_y][p_x+1];
        341								p[p_y][p_x+1] = p[p_y][p_x];
        342								p[p_y][p_x]   = 0;
        343								p_x++;
        344								repaint();
        345							}
        346						}
        347						else {
        348							if (p_x < gm.col-1 && p[p_y][p_x+1] == 0 && p[p_y+1][p_x+1] == 0) {
        349								p[p_y][p_x+1]   = p[p_y][p_x];
        350								p[p_y+1][p_x+1] = p[p_y+1][p_x];
        351								p[p_y][p_x]     = 0;
        352								p[p_y+1][p_x]   = 0;
        353								p_x++;
        354								repaint();
        355							}
        356						}
        357					}
        358					else if (e.getKeyCode() == KeyEvent.VK_UP) {   // 上矢印(上下または左右入れ替え)
        359						if (rot == 0) {
        360							k             = p[p_y][p_x];
        361							p[p_y][p_x]   = p[p_y][p_x+1];
        362							p[p_y][p_x+1] = k;
        363						}
        364						else {
        365							k             = p[p_y][p_x];
        366							p[p_y][p_x]   = p[p_y+1][p_x];
        367							p[p_y+1][p_x] = k;
        368						}
        369						repaint();
        370					}
        371					else if (e.getKeyCode() == KeyEvent.VK_DOWN) {   // 下矢印(90度または-90度回転)
        372						if (rot == 0 && p[p_y+1][p_x] == 0) {
        373							if (p_y < gm.row-1) {
        374								p[p_y+1][p_x] = p[p_y][p_x+1];
        375								p[p_y][p_x+1] = 0;
        376								rot           = 1;
        377								repaint();
        378							}
        379						}
        380						else {
        381							if (p_x < gm.col-1 && p[p_y][p_x+1] == 0) {
        382								p[p_y][p_x+1] = p[p_y+1][p_x];
        383								p[p_y+1][p_x] = 0;
        384								rot           = 0;
        385								repaint();
        386							}
        387						}
        388					}
        389				}
        390			}
        391		}
        392	}
        					
        077 行目

          作業域として使用するため,ピースの存在を示す 2 次元配列 p と同じ大きさの配列 pp を定義している.

        137 行目~ 155 行目

          ピースが再下端に達するか,または,他のピースの上に乗って停止した場合に対する処理である.139 行目~ 153 行目において,メソッド search( 197 行目~ 219 行目)によって,ピース p[i1][i2] と隣り合っている同じ色のピースの数を調べ,その値が 4 以上である場合は,メソッド delete( 226 行目~ 261 行目)によってそれらのピースを削除している.これら全体を 138 行目の while 文によって繰り返しているのは,削除の結果,再び同じ色のピースが繋がる可能性があるからである.

        197 行目~ 219 行目

          ピース p[k1][k2] の上下左右に同じ色のピースがあるか否かを,再帰呼び出しを利用して調べている.最終的に,隣り合った同じ色のピースの数が返される.

        230 行目~ 235 行目

          隣り合った同じ色のピースを削除している.

        230 行目~ 260 行目

          ピースが存在しない空白を詰める処理である.
        • 240 行目: p[i2][i1] が空白のとき以下の処理が行われる
        • 241 行目~ 246 行目: 空白の上にピースが存在するか否かを調べている.存在する場合は,その行番号が変数 k1 に設定され,247 行目以降が実行される.
        • 247 行目~ 256 行目: k2 行,及び,k2 行から k1 行の間にある空白を詰める.

前の章 静岡理工科大学 菅沼ホーム Java目次 索引