平面地圖


這邊的平面地圖所指的是地形沒有高低,只有障礙物的地圖,人物在遇到障礙物時將無法前進,問題是如何判斷障礙物?

學過資料結構的話,大家應該都知道老鼠走迷宮這個例子,使用陣列,並在當中填入1表示迷宮的牆,0表示可以前進行的格子,而平面地圖也是利用這個方式來製作。

並不是由繪圖區域上的障礙物座標來判斷人物是否能前進,而是在一個陣列中判斷,將陣列元素設定為1表示障礙物,如下所示:


利用兩個變數(i, j)來表示人物於陣列中的位置,當人物上下左右移動前,先判斷下一個位置是否為0,如果是才可以前進(改變i, j的值),否則就停留在原地。

在繪圖時,只要根據陣列將指定的圖片一格格繪製上去就可以了,如下圖所示:



當然障礙物並不一定只有一種,您也可以用其它的元素值表示不同的障礙物,例如2表示樹木、3表示水池等等;如果地圖較大,可以只繪製指定範圍的圖形,這並不難,指定陣列中的起始與終止範圍就可以了,如下圖所示:


這個平面地圖的製作方式,可以使用於俯視、斜角地圖,下面這個程式是個實作的例子,您可以先看看 效果 程式中使用了事件處理,程式碼長了一些,有興趣的就耐心看完吧!
  • Maze1.java
package cc.openhome;

import java.awt.*;
import java.awt.event.*;
import javax.swing.JApplet;

public class Maze1 extends JApplet implements KeyListener {
private int[][] maze = {
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 1, 1, 0, 0, 0, 2, 2},
{2, 0, 1, 0, 1, 0, 0, 0, 2, 0, 1, 0, 0, 1, 1, 0, 2, 2},
{2, 0, 0, 0, 0, 0, 1, 1, 2, 0, 1, 0, 1, 0, 0, 0, 0, 2},
{2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 2},
{2, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 2},
{2, 1, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 2, 0, 2, 2},
{2, 1, 0, 0, 2, 0, 1, 1, 1, 2, 0, 1, 0, 0, 2, 0, 1, 2},
{2, 1, 1, 1, 1, 0, 0, 0, 1, 2, 0, 0, 0, 0, 1, 0, 0, 2},
{2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 2, 1, 0, 2},
{2, 0, 2, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 2},
{2, 1, 2, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 2, 1, 0, 2},
{2, 0, 0, 0, 2, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0},
{2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}};

private int blockWidth, blockHeight;
private Image character, floor, block1, block2;
private Image backGround, offScreen;
private Graphics gOffScreen;
private Sprite sprite;
private int characterStartX, characterStartY;
private int characterWidth, characterHeight;

public void init() {
addKeyListener(this);
setBackground(Color.white);

//取得影像
MediaTracker mediaTracker = new MediaTracker(this);
character = getImage(getDocumentBase(), "character.gif");
floor = getImage(getDocumentBase(), "floor.jpg");
block1 = getImage(getDocumentBase(), "block1.gif");
block2 = getImage(getDocumentBase(), "block2.gif");
mediaTracker.addImage(character, 0);
mediaTracker.addImage(floor, 0);
mediaTracker.addImage(block1, 0);
mediaTracker.addImage(block2, 0);

try {
showStatus("影像載入中(Loading Images)...");
mediaTracker.waitForAll();
} catch (InterruptedException e) {
e.printStackTrace();
}

sprite = new Sprite(0, 1, maze);

blockWidth = getWidth() / maze[0].length;
blockHeight = getHeight() / maze.length;
characterWidth = character.getWidth(this) / 3;
characterHeight = character.getHeight(this) / 4;
characterStartY = characterHeight;

//建立次畫面
offScreen = createImage(getWidth(), getHeight());
gOffScreen = offScreen.getGraphics();

// 建立地圖
backGround = createImage(getWidth(), getHeight());
Graphics g = backGround.getGraphics();
// 繪地版
for (int i = 0; i < maze.length; i++) {
for (int j = 0; j < maze[0].length; j++) {
g.drawImage(floor, j * blockWidth, i * blockHeight,
blockWidth, blockHeight, this);
}
}
// 繪障礙物
for (int i = 0; i < maze.length; i++) {
for (int j = 0; j < maze[0].length; j++) {
switch (maze[i][j]) {
case 1:
g.drawImage(block1, j * blockWidth, i * blockHeight,
blockWidth, blockHeight, this);
break;
case 2:
g.drawImage(block2, j * blockWidth, i * blockHeight,
blockWidth, blockHeight, this);
break;
default:
g.drawImage(floor, j * blockWidth, i * blockHeight,
blockWidth, blockHeight, this);
}
}
}
}

public void paint(Graphics g) {
// 在背後繪地圖
gOffScreen.drawImage(backGround, 0, 0, this);
// 貼人物
gOffScreen.drawImage(character,
sprite.getX() * blockWidth,
sprite.getY() * blockHeight,
(sprite.getX() + 1) * blockWidth,
(sprite.getY() + 1) * blockHeight,
characterStartX, characterStartY,
characterStartX + characterWidth,
characterStartY + characterHeight,
this);

//將次畫面貼到主畫面中
g.drawImage(offScreen, 0, 0, this);
}

public void update(Graphics g) {
paint(g);
}

//=====實作KeyListener介面 =====
public void keyPressed(KeyEvent e) {
characterStartX = characterStartX + characterWidth;
switch (e.getKeyCode()) {
case KeyEvent.VK_RIGHT:
sprite.moveRight();
characterStartY = characterHeight;
break;
case KeyEvent.VK_LEFT:
sprite.moveLeft();
characterStartY = characterHeight * 3;
break;
case KeyEvent.VK_UP:
sprite.moveUp();
characterStartY = 0;
break;
case KeyEvent.VK_DOWN:
sprite.moveDown();
characterStartY = characterHeight * 2;
break;
}

if (characterStartX >= character.getWidth(null)) {
characterStartX = 0;
}

repaint();
}

public void keyTyped(KeyEvent e) {}
public void keyReleased(KeyEvent e) {}
}

class Sprite {
private int i, j;
private int maze[][];
Sprite(int x, int y, int[][] maze) {
this.i = y;
this.j = x;
this.maze = maze;
}

void moveUp() {
if (isMovable(i - 1, j)) {
i--;
}
}

void moveDown() {
if (isMovable(i + 1, j)) {
i++;
}
}

void moveRight() {
if (isMovable(i, j + 1)) {
j++;
}
}

void moveLeft() {
if (isMovable(i, j - 1)) {
j--;
}
}

private boolean isMovable(int i, int j) {
if (i < 0 || j < 0 ||
j >= maze[0].length || i >= maze.length) {
return false;
}
if (maze[i][j] == 0) {
return true;
}
return false;
}

int getX() { return j; }
int getY() { return i; }
}

以下是使用HTML5 Canvas的方式(如果瀏覽器支援HTML5 Canvas,例如最新版的Firexfox、Chrome、IE9等,可以直接將下面的內容存為HTML或按下檔名連結,直接載入瀏覽器執行觀看結果:
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=Big5" http-equiv="content-type">
<script type="text/javascript">
window.onload = function() {
function Sprite(x, y, maze) {
var i = y;
var j = x;

function isMovable(i, j) {
if (i < 0 || j < 0 ||
j >= maze[0].length || i >= maze.length) {
return false;
}

if (maze[i][j] === 0) {
return true;
}

return false;
}

this.moveUp = function() {
if(isMovable(i - 1, j)) {
i--;
}
};

this.moveDown = function() {
if(isMovable(i + 1, j)) {
i++;
}
};

this.moveRight = function() {
if(isMovable(i, j + 1)) {
j++;
}
};

this.moveLeft = function() {
if(isMovable(i, j - 1)) {
j--;
}
};

this.getX = function() {
return j;
};

this.getY = function() {
return i;
};
}

var maze = [
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
[0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 1, 1, 0, 0, 0, 2, 2],
[2, 0, 1, 0, 1, 0, 0, 0, 2, 0, 1, 0, 0, 1, 1, 0, 2, 2],
[2, 0, 0, 0, 0, 0, 1, 1, 2, 0, 1, 0, 1, 0, 0, 0, 0, 2],
[2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 2],
[2, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 2],
[2, 1, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 0, 0, 2, 0, 2, 2],
[2, 1, 0, 0, 2, 0, 1, 1, 1, 2, 0, 1, 0, 0, 2, 0, 1, 2],
[2, 1, 1, 1, 1, 0, 0, 0, 1, 2, 0, 0, 0, 0, 1, 0, 0, 2],
[2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 2, 1, 0, 2],
[2, 0, 2, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 2],
[2, 1, 2, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 2, 1, 0, 2],
[2, 0, 0, 0, 2, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]];

var sprite = new Sprite(0, 1, maze);

var character = document.getElementById('character');
var floor = document.getElementById('floor');
var block1 = document.getElementById('block1');
var block2 = document.getElementById('block2');
document.body.removeChild(character);
document.body.removeChild(floor);
document.body.removeChild(block1);
document.body.removeChild(block2);

var canvas1 = document.getElementById('canvas1');
var canvas2 = document.getElementById('canvas2');
var context1 = canvas1.getContext('2d');
var context2 = canvas2.getContext('2d');

document.body.removeChild(canvas2);

var blockWidth = canvas1.width / maze[0].length;
var blockHeight = canvas1.height / maze.length;
var characterWidth = 108 / 3; // 人物圖片寬
var characterHeight = 186 / 4; // 人物圖片高
var characterStartX = 0;
var characterStartY = characterHeight;

var context = context1;

// 繪地版
function drawFloor() {
for(var i = 0; i < maze.length; i++) {
for(var j = 0; j < maze[0].length; j++) {
context.drawImage(
floor, j * blockWidth, i * blockHeight,
blockWidth, blockHeight);
}
}
}

// 繪障礙物
function drawBlock() {
for(var i = 0; i < maze.length; i++) {
for(var j = 0; j < maze[0].length; j++) {
switch (maze[i][j]) {
case 1:
context.drawImage(
block1, j * blockWidth, i * blockHeight,
blockWidth, blockHeight);
break;
case 2:
context.drawImage(
block2, j * blockWidth, i * blockHeight,
blockWidth, blockHeight);
break;
default:
context.drawImage(
floor, j * blockWidth, i * blockHeight,
blockWidth, blockHeight);
}
}
}
}

// 繪人物
function drawCharacter() {
context.drawImage(character,
characterStartX,
characterStartY,
characterWidth,
characterHeight,
sprite.getX() * blockWidth,
sprite.getY() * blockHeight,
blockWidth,
blockHeight);
}

drawFloor();
drawBlock();
drawCharacter();

context = context2;
drawFloor();
drawBlock();

document.onkeydown = function(event) {
characterStartX = characterStartX + characterWidth;
switch (event.which) {
case 39: // 右
sprite.moveRight();
characterStartY = characterHeight;
break;
case 37: // 左
sprite.moveLeft();
characterStartY = characterHeight * 3;
break;
case 38: // 上
sprite.moveUp();
characterStartY = 0;
break;
case 40: // 下
sprite.moveDown();
characterStartY = characterHeight * 2;
break;
}

if (characterStartX >= character.width) {
characterStartX = 0;
}

drawCharacter();

if(context === context2) {
document.body.replaceChild(canvas2, canvas1);
context = context1;
}
else {
document.body.replaceChild(canvas1, canvas2);
context = context2;
}

context.clearRect(0, 0, canvas1.width, canvas1.height);
drawFloor();
drawBlock();
};
};
</script>
</head>
<body>
<canvas id="canvas1" width="900" height="600"></canvas>
<canvas id="canvas2" width="900" height="600"></canvas>
<img id="character" src="images/character.gif"
style="visibility: hidden"/>
<img id="floor" src="images/floor.jpg"
style="visibility: hidden"/>
<img id="block1" src="images/block1.gif"
style="visibility: hidden"/>
<img id="block2" src="images/block2.gif"
style="visibility: hidden"/>
</body>
</html>