« 曲がるレーザーの実装実験(6/6) | トップページ | 【ココログ】ボタンを設置してみた »

2011年6月21日 (火)

タイルマップ座標のオフセットを求める

タイルマップ上のある一点の座標から、タイル単位の座標とタイル内のオフセットを求めるサンプルプログラム。

負の座標から正のオフセットを求めるシンプルなコードはC/C++でもそのまま使える。

  • カーソルキーで矢印の移動、または背景のスクロール
  • Zキーで矢印の移動と背景のスクロールを切り替え

TilemapOffset.java

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

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

	// タイルの大きさ(ピクセル単位)
	static final int TILE_WIDTH  = 16;
	static final int TILE_HEIGHT = 16;
	// マップの大きさ(タイル単位)
	static final int MAP_WIDTH  = 10;
	static final int MAP_HEIGHT = 10;
	// マップの大きさ(ピクセル単位)
	static final int WORLD_WIDTH  = TILE_WIDTH  * MAP_WIDTH;
	static final int WORLD_HEIGHT = TILE_HEIGHT * MAP_HEIGHT;

	int pad_states_last;

	VGTiledImage imgTiled;
	VGTilemap mapFloor, mapHigh, mapLow;
	boolean scroll;
	int scroll_x, scroll_y;
	int target_x, target_y;

	int tile_x, tile_y;
	int offs_x, offs_y;

	public TilemapOffset()
	{
		super(SCREEN_WIDTH, SCREEN_HEIGHT, 60);
		setBackground(Color.BLACK);

		Image img = getResourceImage("images.png");
		imgTiled = new VGTiledImage(img, TILE_WIDTH, TILE_HEIGHT, 10);
		mapFloor = new VGTilemap(imgTiled, MAP_WIDTH, MAP_HEIGHT);
		mapHigh  = new VGTilemap(imgTiled, MAP_WIDTH, MAP_HEIGHT);
		mapLow   = new VGTilemap(imgTiled, MAP_WIDTH, MAP_HEIGHT);
		for (int y=0; y<MAP_HEIGHT; ++y) {
			for (int x=0; x<MAP_WIDTH; ++x) {
				mapFloor.setTile(x, y, (x + y) % 2 + 1, 0);
				mapHigh.setTile(x, y, y % 10 + 20, 0);
				mapLow.setTile(x, y, x % 10 + 10, 0);
			}
		}
		scroll = true;
		target_x = SCREEN_WIDTH/2;
		target_y = SCREEN_HEIGHT/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;

		if (scroll) {
			if ((pad_states & (1<<PAD_LEFT))  != 0) --scroll_x;
			if ((pad_states & (1<<PAD_RIGHT)) != 0) ++scroll_x;
			if ((pad_states & (1<<PAD_UP))    != 0) --scroll_y;
			if ((pad_states & (1<<PAD_DOWN))  != 0) ++scroll_y;
			mapFloor.setOrigin(scroll_x, scroll_y);
			mapHigh.setOrigin(scroll_x, scroll_y);
			mapLow.setOrigin(scroll_x, scroll_y);
		}
		else {
			if ((pad_states & (1<<PAD_LEFT))  != 0) if (--target_x < 0) target_x = SCREEN_WIDTH-1;
			if ((pad_states & (1<<PAD_RIGHT)) != 0) if (++target_x >= SCREEN_WIDTH) target_x = 0;
			if ((pad_states & (1<<PAD_UP))    != 0) if (--target_y < 0) target_y = SCREEN_HEIGHT-1;
			if ((pad_states & (1<<PAD_DOWN))  != 0) if (++target_y >= SCREEN_HEIGHT) target_y = 0;
		}

		if ((pad_trigger & (1<<PAD_BUTTON1)) != 0) scroll = !scroll;

		// ワールド座標を求める
		int world_x = positive_remainder(scroll_x + target_x, WORLD_WIDTH);
		int world_y = positive_remainder(scroll_y + target_y, WORLD_HEIGHT);
		// タイルの大きさで割った値(整数未満切り捨て)がタイル座標
		tile_x = world_x / TILE_WIDTH;
		tile_y = world_y / TILE_HEIGHT;
		// タイルの大きさで割った余りがオフセット
		offs_x = world_x % TILE_WIDTH;
		offs_y = world_y % TILE_HEIGHT;
	}

	/**
	 *  被除数を除数で割った余りを求めます。<br>
	 *  ただし被除数が負の数のとき、正の数の延長として処理します。<br>
	 *  例えば除数が1024のとき、被除数-1の戻り値は1023、被除数-2の戻り値は1022となります。
	 *
	 *  @param dividend 被除数
	 *  @param divisor  除数 - 0より大きい値であること
	 *  @return 除数で割った余り。除数未満の正の数。除数が0以下のときは0
	 */
	private int positive_remainder(int dividend, int divisor)
	{
		if (divisor <= 0) return 0; // エラー
		if (dividend < 0) {
			dividend = -(dividend + 1);
			dividend %= divisor;
			dividend = divisor - 1 - dividend;
		}
		else {
			dividend %= divisor;
		}
		return dividend;
	}

	@Override
	protected void frameStretchRender(Graphics g)
	{
		g.clearRect(0,0,SCREEN_WIDTH,SCREEN_HEIGHT);
		mapFloor.paint(g, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0);
		mapLow.paint(g, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0);
		mapHigh.paint(g, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0);
		imgTiled.paint(g, 3, target_x, target_y, 0);
		imgTiled.paint(g, 3, target_x-SCREEN_WIDTH, target_y, 0);
		imgTiled.paint(g, 3, target_x, target_y-SCREEN_HEIGHT, 0);
		imgTiled.paint(g, 3, target_x-SCREEN_WIDTH, target_y-SCREEN_HEIGHT, 0);
		g.setFont(new Font("Monospaced", Font.BOLD, 12));
		String strLoc = String.format("Loc. (%2d, %2d)", tile_x, tile_y);
		String strOfs = String.format("Ofs. (%2d, %2d)", offs_x, offs_y);
		g.setColor(Color.BLACK);
		g.drawString(strLoc, 10+1, 16+1);
		g.drawString(strOfs, 10+1, 32+1);
		g.setColor(Color.WHITE);
		g.drawString(strLoc, 10, 16);
		g.drawString(strOfs, 10, 32);
	}

	public static void main(String[] args)
	{
		new VGFrame(new TilemapOffset(), 640, 480, "タイルマップのオフセット");
	}
}

|

« 曲がるレーザーの実装実験(6/6) | トップページ | 【ココログ】ボタンを設置してみた »

JavaSE」カテゴリの記事

コメント

コメントを書く



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




« 曲がるレーザーの実装実験(6/6) | トップページ | 【ココログ】ボタンを設置してみた »