情報学部 菅沼ホーム Java 目次 基礎技術目次 索引

背景(マップ)

  1. マップの使用
      アニメーションやゲームにおいて,背景画像上で様々なターゲットを移動させたいような場合が存在します.その際,ターゲットと共に背景画像自体を移動するような場合も少なくありません.それを実現する一つの方法は,大きな背景画像を準備し,それを背景として使用することです.しかし,そのようにすると,ターゲットと背景画像の位置関係を明確にするのが面倒になります.

      そこでよく使用されるのが,全体を格子状に区切り,区切られた各ブロックに適切な画像を配置したり,画像を描画することによって背景を構成する方法です(マップmap ).このようにすれば,ターゲットが格子内のどこに存在するかによって,背景画像との位置関係を知ることができ,障害物等に対する処理も容易になります.

      背景の例は,12 行 × 26 列の格子内に 2 種類の画像を配置し,横方向に移動した例です.以下に,そのプログラム( Test.java,Map.java )を示します.

    ー Test.java ー

    001	import java.awt.*;
    002	import java.awt.event.*;
    003	import javax.swing.*;
    004	
    005	public class Test {
    006		public static void main (String[] args)
    007		{
    008			Win win = new Win("背景(マップ)1");
    009		}
    010	}
    011	
    012	class Win extends JFrame
    013	{
    014		/******************/
    015		/* コンストラクタ */
    016		/******************/
    017		Win(String name)
    018		{
    019						// JFrameクラスのコンストラクタ(Windowのタイトルを引き渡す)
    020			super(name);
    021						// Windowの大きさ
    022			setSize(440, 370);   // +40, +70
    023						// 画像の読み込み
    024			Image im[] = new Image [2];   // 画像ファイル
    025			im[0] = getToolkit().getImage("obj1.jpg");   // イメージの読み込み
    026			im[1] = getToolkit().getImage("obj2.jpg");   // イメージの読み込み
    027						// ContentPane の取得と MainPanel の追加
    028			Dimension d  = getSize();   // Windowの大きさ
    029			d.width     -= 40;
    030			d.height    -= 70;
    031			MainPanel pn = new MainPanel(d, im);   // MainPanel オブジェクトの生成
    032			getContentPane().add(pn);   // MainPanel オブジェクトを ContentPane に追加
    033						// ウィンドウを表示
    034			setVisible(true);
    035						// イベントアダプタ
    036			addWindowListener(new WinEnd());
    037		}
    038	
    039		/******************************/
    040		/* 上,左,下,右の余白の設定 */
    041		/******************************/
    042		public Insets getInsets()
    043		{
    044			return new Insets(50, 20, 20, 20);
    045		}
    046	
    047		/************/
    048		/* 終了処理 */
    049		/************/
    050		class WinEnd extends WindowAdapter
    051		{
    052			public void windowClosing(WindowEvent e) {
    053				System.exit(0);
    054			}
    055		}
    056	}
    057	
    058	class MainPanel extends JPanel implements Runnable
    059	{
    060		Thread td;
    061		Map mp;   // 背景(マップ)
    062		Dimension size;
    063		Image im[];
    064		boolean state = true;
    065				// コンストラクタ
    066		public MainPanel(Dimension d, Image im1[])
    067		{
    068			size = d;
    069			im = im1;
    070						// レイアウトマネージャの停止
    071			setLayout(null);
    072						// 背景色の設定
    073			setBackground(Color.white);
    074						// 背景の生成
    075			mp = new Map();
    076						// スレッドの生成
    077			td = new Thread(this);
    078			td.start();
    079		}
    080				// スレッドの実行
    081		public void run()
    082		{
    083			while (state) {
    084				try {
    085					td.sleep(30);
    086				}
    087				catch (InterruptedException e) {}
    088				mp.x += (int)mp.v_x;
    089				repaint();
    090			}
    091		}
    092				// 描画
    093		public void paintComponent(Graphics g)
    094		{
    095			super.paintComponent(g);   // 親クラスの描画
    096						// 背景の描画
    097			for (int i1 = 0; i1 < mp.col; i1++) {
    098				int x = mp.x + mp.width * i1;
    099				if (x + mp.width >= 0 && x <= size.width) {
    100					for (int i2 = 0; i2 < mp.row; i2++) {
    101						if (mp.map[i2][i1] > 0) {
    102							int y = i2 * mp.height;
    103							g.drawImage(im[mp.map[i2][i1]-1], x, y, this);
    104						}
    105					}
    106				}
    107			}
    108		}
    109	}
    			
    024 行目~ 026 行目

      マップに必要な画像を読み込んでいます.

    075 行目

      Map クラスのオブジェクトを生成しています.

    088 行目

      マップを,30 ms 毎に 2 ピクセルずつ,左に移動させています.

    097 行目

      この for 文によって,マップの各列に対して 098 行目~ 106 行目の処理が繰り返されます.

    098 行目

      i1 列にあるブロックの左端の x 座標です.

    099 行目

      この if 文で,i1 列にあるブロックがキャンバス内に存在するか否かをチェックしています.存在する場合は,以下の処理が実行されます.

    100 行目

      この for 文によって,マップの各行に対して 101 行目~ 104 行目の処理が繰り返されます.097 行目及び 100 行目で定義された変数 i1 と i2 は,ここで( for 文の中で)宣言されています.そのため,097 行目で宣言された変数 i1 の有効範囲は対応する for ブロック( 097 行目~ 107 行目)の中だけ,さらに,100 行目で宣言された変数 i2 の有効範囲は対応する for ブロック( 100 行目~ 105 行目)の中だけになります.同様に,098 行目で宣言されている変数 x は,それ以降で,かつ,変数 x が入っている for ブロック内( 098 行目~ 106 行目),また,102 行目で宣言されている変数 y は,それ以降で,かつ,変数 y が入っている if ブロック内( 102,103 行目)だけで有効になります.このように,メソッド内やブロック内だけで有効な変数をロ-カル変数と呼びます.複数の関数やクラスから参照可能な変数は,関数の外で定義され,グローバル変数と呼ばれます.なお,「変数の有効範囲」も参照してください.

    101 行目~ 104 行目

      マップのブロックの値が正である箇所に,指定された画像を描画しています.

    ー Map.java ー

    01	public class Map
    02	{
    03		int map[][] = {
    04			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    05			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    06			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    07			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    08			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    09			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    10			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    11			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    12			{1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2},
    13			{1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2},
    14			{1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2},
    15			{1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2}};
    16		int row = 12;   // ブロックの行数
    17		int col = 26;   // ブロックの列数
    18		int x;   // 背景の位置
    19		double v_x = -2;   // 背景の水平方向移動速度
    20		int width = 25;   // ブロックの幅
    21		int height = 25;   // ブロックの高さ
    22				// コンストラクタ
    23		public Map()
    24		{
    25			x = 0;
    26		}
    27	}
    			
    03 行目~ 15 行目

      12 行 26 列のマップを定義しています.Test.java の説明で述べたように,値が 0 の箇所には何も表示されず,1 または 2 の箇所には,画像が表示されます.この例のように,初期設定によって 2 次元配列を定義することも可能ですが,一般に,多次元配列は,配列の各要素を配列として定義することによって可能です.例えば,2 行 3 列の配列 x は,
    	int x[][] = new int [2][3];				
    または,
    	int x[][] = new int [2][];
    	x[0] = new int [3];   // 以下,繰り返し文を使用可能
    	x[1] = new int [3];				
    のような形で定義可能です.初期設定を行いたい場合は,
    	int x[][] = {{1, 2, 3}, {4, 5, 6}};   // 内側の括弧が各行に相当				
    または,
    	int x1[] = {1, 2, 3};
    	int x2[] = {4, 5, 6};
    	int x[][] = {x1, x2};				
    のような方法で行います.

    16 行目~ 17 行目

      マップの行数と列数です.

    18 行目

      マップの左端の x 座標です.

    19 行目

      マップの移動速度です.

    20 行目~ 21 行目

      マップを構成するブロックの大きさです.

  2. もう一つの例

      この例では,マップを,繰り返し,切れ目なく表示しています.基本的には,同じマップを 2 つ用意して,それらの表示・非表示,及び,その位置を制御することによって実現しています.なお,ここでは,上に示した例と基本的に同じ Map.java を使用していますが,Map.java の 23 行目~ 26 行目のコンストラクタによって,背景の位置を設定できるようにしています.
    001	import java.awt.*;
    002	import java.awt.event.*;
    003	import javax.swing.*;
    004	
    005	public class Test {
    006		public static void main (String[] args)
    007		{
    008			Win win = new Win("背景(マップ)2");
    009		}
    010	}
    011	
    012	class Win extends JFrame
    013	{
    014		/******************/
    015		/* コンストラクタ */
    016		/******************/
    017		Win(String name)
    018		{
    019						// JFrameクラスのコンストラクタ(Windowのタイトルを引き渡す)
    020			super(name);
    021						// Windowの大きさ
    022			setSize(440, 370);   // +40, +70
    023						// 画像の読み込み
    024			Image im[] = new Image [2];   // 画像ファイル
    025			im[0] = getToolkit().getImage("obj1.jpg");   // イメージの読み込み
    026			im[1] = getToolkit().getImage("obj2.jpg");   // イメージの読み込み
    027						// ContentPane の取得と MainPanel の追加
    028			Dimension d  = getSize();   // Windowの大きさ
    029			d.width     -= 40;
    030			d.height    -= 70;
    031			MainPanel pn = new MainPanel(d, im);   // MainPanel オブジェクトの生成
    032			getContentPane().add(pn);   // MainPanel オブジェクトを ContentPane に追加
    033						// ウィンドウを表示
    034			setVisible(true);
    035						// イベントアダプタ
    036			addWindowListener(new WinEnd());
    037		}
    038	
    039		/******************************/
    040		/* 上,左,下,右の余白の設定 */
    041		/******************************/
    042		public Insets getInsets()
    043		{
    044			return new Insets(50, 20, 20, 20);
    045		}
    046	
    047		/************/
    048		/* 終了処理 */
    049		/************/
    050		class WinEnd extends WindowAdapter
    051		{
    052			public void windowClosing(WindowEvent e) {
    053				System.exit(0);
    054			}
    055		}
    056	}
    057	
    058	class MainPanel extends JPanel implements Runnable
    059	{
    060		Thread td;
    061		Map mp[] = new Map[2];   // 背景(マップ)
    062		Dimension size;
    063		Image im[];
    064		boolean state = true;
    065		int dsp[] = {1, 0};
    066				// コンストラクタ
    067		public MainPanel(Dimension d, Image im1[])
    068		{
    069			size = d;
    070			im   = im1;
    071						// レイアウトマネージャの停止
    072			setLayout(null);
    073						// 背景色の設定
    074			setBackground(Color.white);
    075						// 背景の生成
    076			mp[0] = new Map(0);
    077			mp[1] = new Map(d.width);
    078						// スレッドの生成
    079			td = new Thread(this);
    080			td.start();
    081		}
    082				// スレッドの実行
    083		public void run()
    084		{
    085			while (state) {
    086				try {
    087					td.sleep(30);
    088				}
    089				catch (InterruptedException e) {}
    090				for (int i1 = 0; i1 < 2; i1++) {
    091					int k = (i1 + 1) % 2;
    092					if (dsp[i1] > 0) {
    093						mp[i1].x += (int)mp[i1].v_x;
    094						if (mp[i1].x + mp[i1].width * mp[i1].col <= size.width) {
    095							mp[k].x = mp[i1].x + mp[i1].width * mp[i1].col;
    096							dsp[k]  = 1;
    097						}
    098						else if (mp[i1].x + mp[i1].width * mp[i1].col <= 0)
    099							dsp[i1] = 0;
    100					}
    101				}
    102				repaint();
    103			}
    104		}
    105				// 描画
    106		public void paintComponent(Graphics g)
    107		{
    108			super.paintComponent(g);   // 親クラスの描画
    109						// 背景の描画
    110			for (int i0 = 0; i0 < 2; i0 ++) {
    111				if (dsp[i0] > 0) {
    112					for (int i1 = 0; i1 < mp[i0].col; i1++) {
    113						int x = mp[i0].x + mp[i0].width * i1;
    114						if (x + mp[i0].width >= 0 && x <= size.width) {
    115							for (int i2 = 0; i2 < mp[i0].row; i2++) {
    116								if (mp[i0].map[i2][i1] > 0) {
    117									int y = i2 * mp[i0].height;
    118									g.drawImage(im[mp[i0].map[i2][i1]-1], x, y, this);
    119								}
    120							}
    121						}
    122					}
    123				}
    124			}
    125		}
    126	}
    			
    061 行目

      2 つのマップを記憶するための配列です.

    065 行目

      各マップが表示されているか否かを表すための配列です(各要素を,1 と 0 で初期設定).1 が表示,0 が非表示状態であることを表します.初期状態では,ここに示すように,1 番目のマップだけが表示状態となっています.

    076 行目~ 077 行目

      同じ内容の 2 つの Map オブジェクトを生成し,配列 mp の各要素に代入し,引数によって,2 番目のマップの位置をキャンバスの右外に設定しています.同じ内容のマップであるからと言って,

      mp[0] = new Map();
      mp[1] = mp[0];

    のようには記述しないでください.mp[0] に代入されるのは,生成されたオブジェクトのアドレスです.従って,上のように記述すると,mp[0] と mp[1] は,全く同じマップを示すことになってしまいます.ここで記憶したいのは,内容は同じであっても,異なるマップ(データ)である必要があります.さもないと,各マップの表示・非表示や位置を独立に制御できません.

    090 行目

      この for 文によって,各マップの表示・非表示や位置を制御しています.

    091 行目

      変数 k の値は,i1 が 0 の時は 1,1 の時は 0 になります.

    092 行目

      この if 文によって,マップ i1 が表示されているとき,093 行目~ 099 行目の処理が実行されます.

    094 行目~ 097 行目

      表示されているマップの右端がキャンバス内に入ったとき,あと一つのマップの位置をキャンバスの右端とし,表示状態にします.

    098 行目~ 099 行目

      表示されているマップの右端がキャンバスの左外に出たとき,マップを非表示状態にします.

    110 行目~ 124 行目

      表示されているマップに対してだけ,先ほどの例と同様,各ブロックに指定された画像を表示します.

    ー Map.java ー

    01	public class Map
    02	{
    03		int map[][] = {
    04			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    05			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    06			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    07			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    08			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    09			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    10			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    11			{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
    12			{1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2},
    13			{1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2},
    14			{1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2},
    15			{1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2}};
    16		int row = 12;   // ブロックの行数
    17		int col = 26;   // ブロックの列数
    18		int x;   // 背景の位置
    19		double v_x = -2;   // 背景の水平方向移動速度
    20		int width = 25;   // ブロックの幅
    21		int height = 25;   // ブロックの高さ
    22				// コンストラクタ
    23		public Map(int x)
    24		{
    25			this.x = x;
    26		}
    27	}
    			

情報学部 菅沼ホーム Java 目次 基礎技術目次 索引