« 【JavaSE】サイドビューの実験(11/17) | トップページ | 【JavaSE】フレームワークのタイルマップ実装 »

2010年10月 6日 (水)

【JavaSE】サイドビューの実験(12/17)

横に移動するブロックから横に移動するブロックへ渡るときの操作性について実験する。

今回は確認のためにとりあえず何も対策しないままステージを用意した。次回以降で対策を行っていく。

ついでに、本来見えない、当たり判定領域を可視化したものを非表示にできるようにした。

  • カーソルキーで左右移動
  • Zキーでジャンプ
  • Xキーで当たり判定を可視化した補助線の描画のオン・オフを切り替え

SideViewExp12.java

import isle.videogame.*;
import isle.videogame.image.*;
import java.awt.*;

@SuppressWarnings("serial")
public class SideViewExp12 extends VGStretchCanvas
{
	static final int SCREEN_WIDTH  = 240;
	static final int SCREEN_HEIGHT = 240;

	int pad_states_last;

	byte[][] map = {
		{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
		{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
		{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
		{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,2 },
		{ 1,1,2,2,0,0,2,2,1,1,1,1,1,2,2 },
		{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
		{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
		{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
		{ 1,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
		{ 1,1,0,0,0,0,0,0,0,0,0,0,0,1,1 },
		{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 },
		{ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,1 },
		{ 0,0,0,0,0,0,0,2,0,0,0,0,0,0,1 },
		{ 2,0,0,0,0,0,0,2,0,0,0,0,0,1,2 },
		{ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 },
	};
	VGImage[] BG_imgs;

	VGImage[] Girl_imgs;
	Rectangle Girl_rc = new Rectangle();
	int Girl_nImg;
	int Girl_cWalk;
	boolean Girl_bLeft;
	int Girl_ga;
	int Girl_borderHead; // 当たり判定の頭上の境界
	int Girl_borderFoot; // 当たり判定の足元の境界
	boolean Girl_bStand; // 立っているか
	boolean Girl_bOnFix; // 移動ブロックに追従しない

	static final int FLOATBLOCK_NUM = 2;
	int[][][] FloatBlockMovements = {
		{{ 0, 0, 50 }, { 1, 0, 56 }, { 0, 0, 50 }, { -1, 0, 56 }},
		{{ 0, 0, 50 }, { -1, 0, 56 }, { 0, 0, 50 }, { 1, 0, 56 }},
	};
	int[] FloatBlock_kind = { 1, 2 };
	Point[] FloatBlock_pt = new Point[FLOATBLOCK_NUM];
	int[] FloatBlock_index = new int[FLOATBLOCK_NUM];
	int[] FloatBlock_count = new int[FLOATBLOCK_NUM];
	int[] FloatBlockSortedIndex = new int[FLOATBLOCK_NUM];

	Point[] FixMark_pt = { new Point(20, 128), new Point(220, 128) };

	Rectangle rcBump = new Rectangle(); // 当たり判定対象用

	boolean bDrawAux = true; // 補助線などを描画するかどうか

	public SideViewExp12()
	{
		super(SCREEN_WIDTH, SCREEN_HEIGHT, 60);
		setBackground(Color.DARK_GRAY);

		Image img = getResourceImage("buruma.png");
		Girl_imgs = VGImage.createImages(img, 0, 0, 16, 24, 6);
		Girl_rc.setSize(32-3*2, 48); // 当たり判定基準
		Girl_rc.setLocation(3, 0);

		img = getResourceImage("bg.png");
		BG_imgs = VGImage.createImages(img, 0, 0, 16, 16, 3);

		FloatBlock_pt[0] = new Point(32, 144);
		FloatBlock_index[0] = 0;
		FloatBlock_count[0] = FloatBlockMovements[0][FloatBlock_index[0]][2];
		FloatBlock_pt[1] = new Point(176, 144);
		FloatBlock_index[1] = 0;
		FloatBlock_count[1] = FloatBlockMovements[1][FloatBlock_index[1]][2];
	}

	@Override
	protected void frameUpdate(int skipped)
	{
		int pad_states = getPadStates();
		int pad_trigger = (pad_states_last ^ pad_states) & pad_states;
		pad_states_last = pad_states;
		// 移動前の座標で頭上と足元の当たり判定の境界ラインを設定
		Girl_borderHead = Girl_rc.y;
		Girl_borderFoot = Girl_rc.y + Girl_rc.height;
		// ユーザー操作によるキャラクタの移動処理
		if (Girl_bStand) {
			Girl_nImg = 4;
			if ((pad_states & (1<<PAD_LEFT))  != 0) {
				--Girl_rc.x;
				++Girl_cWalk;
				Girl_cWalk %= 16;
				Girl_nImg = Girl_cWalk / 4;
				Girl_bLeft = true;
			}
			if ((pad_states & (1<<PAD_RIGHT)) != 0) {
				++Girl_rc.x;
				++Girl_cWalk;
				Girl_cWalk %= 16;
				Girl_nImg = Girl_cWalk / 4;
				Girl_bLeft = false;
			}
			if ((pad_trigger & (1<<PAD_BUTTON1)) != 0) {
				Girl_ga = -12;
			}
		}
		else {
			Girl_nImg = 5;
			if (Girl_ga < 8) ++Girl_ga;
			Girl_rc.y += Girl_ga;
			if ((pad_states & (1<<PAD_LEFT))  != 0) {
				--Girl_rc.x;
			}
			if ((pad_states & (1<<PAD_RIGHT)) != 0) {
				++Girl_rc.x;
			}
		}
		if ((pad_trigger & (1<<PAD_BUTTON2)) != 0) {
			bDrawAux = !bDrawAux; // 補助線などを描画する・しないを切り替え
		}

		// 固定マークとキャラクタが接触しているかチェック
		Girl_bOnFix = false;
		for (int i=0; i<FixMark_pt.length; ++i) {
			if (Girl_rc.contains(FixMark_pt[i])) Girl_bOnFix = true;
		}
		// 浮動ブロックの移動
		for (int i=0; i<FLOATBLOCK_NUM; ++i) {
			int vx = FloatBlockMovements[i][FloatBlock_index[i]][0];
			int vy = FloatBlockMovements[i][FloatBlock_index[i]][1];
			boolean bJoin = false; // キャラクターが乗っているかどうか
			rcBump.setBounds(FloatBlock_pt[i].x, FloatBlock_pt[i].y-1, 32, 1);
			if (FloatBlock_pt[i].y >= Girl_borderFoot && rcBump.intersects(Girl_rc)) bJoin = true;
			// X方向に移動する場合
			if (vx != 0) {
				// キャラクタが乗っていれば同じく移動
				// ただし固定マークに接触しているなら追従しない
				if (bJoin && !Girl_bOnFix) {
					Girl_rc.x += vx;
				}
				FloatBlock_pt[i].x += vx;
			}
			// Y方向に移動する場合
			if (vy != 0) {
				// キャラクタが乗っていれば同じく移動
				if (bJoin) {
					Girl_rc.y += vy;
					// 下降時に他の床に引っ掛かるよう足元の境界はそのまま
					if (vy < 0) Girl_borderFoot += vy;
				}
				FloatBlock_pt[i].y += vy;
			}
			if (--FloatBlock_count[i] <= 0) {
				FloatBlock_index[i] = (FloatBlock_index[i] + 1) % FloatBlockMovements[i].length;
				FloatBlock_count[i] = FloatBlockMovements[i][FloatBlock_index[i]][2];
			}
		}

		Girl_bStand = false;
		// 固定ブロックとの当たり判定
		for (int i=0,y=0; i<15; ++i,y+=16) {
			for (int j=0,x=0; j<15; ++j,x+=16) {
				doCollideBlock(x, y, map[i][j]);
			}
		}
		// 浮動ブロックの位置で上から下へ順にソートした索引を作る
		for (int i=0; i<FLOATBLOCK_NUM; ++i) {
			FloatBlockSortedIndex[i] = i;
			for (int j=i-1; j>=0; --j) {
				if (FloatBlock_pt[FloatBlockSortedIndex[i]].y < FloatBlock_pt[FloatBlockSortedIndex[j]].y) {
					int tmp = FloatBlockSortedIndex[j];
					FloatBlockSortedIndex[j] = FloatBlockSortedIndex[i];
					FloatBlockSortedIndex[i] = tmp;
				}
			}
		}
		// 浮動ブロックとの当たり判定
		for (int i=0; i<FLOATBLOCK_NUM; ++i) {
			int ii = FloatBlockSortedIndex[i]; // ソートした順番
			doCollideBlock(FloatBlock_pt[ii].x   , FloatBlock_pt[ii].y, FloatBlock_kind[ii]);
			doCollideBlock(FloatBlock_pt[ii].x+16, FloatBlock_pt[ii].y, FloatBlock_kind[ii]);
		}

		// 最終座標補正
		if (Girl_rc.x < 0) Girl_rc.x = 0;
		if (Girl_rc.x > SCREEN_WIDTH-Girl_rc.width) Girl_rc.x = SCREEN_WIDTH-Girl_rc.width;
	}

	void doCollideBlock(int x, int y, int kind)
	{
		// 天井の判定
		switch (kind) {
		case 2:
			// 横に吹っ飛ばないための除外
			if (y + 16 > Girl_borderHead) break;
			// 天井の当たり判定はブロックの下端だけ
			rcBump.setBounds(x, y+16-1, 16, 1);
			if (!Girl_rc.intersects(rcBump)) break;
			Girl_rc.y = y + 16;
			Girl_ga = 0;
			// 当たり判定境界をリセット
			Girl_borderHead = Girl_rc.y;
			Girl_borderFoot = Girl_rc.y + Girl_rc.height;
		}
		// 床の判定
		switch (kind) {
		case 1: case 2:
			if (Girl_ga < 0) break;
			// 引っ張り上げられないための除外
			if (y < Girl_borderFoot) break;
			// 床の当たり判定はブロックの上端だけ
			// 足元の当たり判定を1ピクセル伸ばすことで
			// 座標補正後もブロックの上に居る状態を維持
			rcBump.setBounds(x, y-1, 16, 2);
			if (!Girl_rc.intersects(rcBump)) break;
			Girl_rc.y = y - Girl_rc.height;
			Girl_ga = 0;
			// 当たり判定境界をリセット
			Girl_borderHead = Girl_rc.y;
			Girl_borderFoot = Girl_rc.y + Girl_rc.height;
			Girl_bStand = true;
		}
		// 壁の判定
		switch (kind) {
		case 2:
			rcBump.setBounds(x, y, 16, 16);
			if (!Girl_rc.intersects(rcBump)) break;
			// 壁にめり込んでいる
			if (Girl_rc.x < x) {
				// ブロックより左寄りなら左に座標補正
				Girl_rc.x = x - Girl_rc.width;
			}
			else {
				// 右に座標補正
				Girl_rc.x = x + 16;
			}
		}
	}

	@Override
	protected void frameStretchRender(Graphics g)
	{
		// 固定ブロックを描画
		for (int i=0,y=0; i<15; ++i,y+=16) {
			for (int j=0,x=0; j<15; ++j,x+=16) {
				BG_imgs[map[i][j]].paint(g, x, y, 0);
			}
		}
		// 浮動ブロックを描画
		for (int i=0; i<FLOATBLOCK_NUM; ++i) {
			BG_imgs[FloatBlock_kind[i]].paint(g, FloatBlock_pt[i].x   , FloatBlock_pt[i].y, 0);
			BG_imgs[FloatBlock_kind[i]].paint(g, FloatBlock_pt[i].x+16, FloatBlock_pt[i].y, 0);
		}

		if (bDrawAux) {
			g.setColor(Color.YELLOW);
			g.drawLine(0, Girl_borderHead, SCREEN_WIDTH-1, Girl_borderHead);
			g.drawLine(0, Girl_borderFoot-1, SCREEN_WIDTH-1, Girl_borderFoot-1);
		}
		Girl_imgs[Girl_nImg].paint(g, Girl_rc.x-3, Girl_rc.y, 32, 48, Girl_bLeft ? 0 : VGImage.FLIP_HORIZONTAL);
		if (bDrawAux) {
			g.setColor(Color.RED);
			g.drawRect(Girl_rc.x, Girl_rc.y, Girl_rc.width-1, Girl_rc.height-1);
		}

		// 固定マークを描画
		if (bDrawAux) {
			g.setColor(Color.ORANGE);
			for (int i=0; i<FixMark_pt.length; ++i) {
				g.fillRect(FixMark_pt[i].x, FixMark_pt[i].y, 1, 1);
				g.drawRect(FixMark_pt[i].x-2, FixMark_pt[i].y-2, 5-1, 5-1);
			}
		}
	}

	// $> java -cp SideViewExp.jar SideViewExp12
	public static void main(String[] args)
	{
		new VGFrame(new SideViewExp12(), 640, 480, "サイドビュー実験");
	}
}

|

« 【JavaSE】サイドビューの実験(11/17) | トップページ | 【JavaSE】フレームワークのタイルマップ実装 »

JavaSE」カテゴリの記事

コメント

コメントを書く



(ウェブ上には掲載しません)




« 【JavaSE】サイドビューの実験(11/17) | トップページ | 【JavaSE】フレームワークのタイルマップ実装 »