From Gossip@caterpillar

Computer Graphics: 平面地圖

 

 


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

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

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


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

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



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


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

  • Maze1.java
package onlyfun.caterpillar.graphics.animation;

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

public class Maze1 extends JApplet
implements KeyListener {
private int appletWidth, appletHeight,
blockWidth, blockHeight;
private Image character, floor, block1, block2;
private Image backGround, offScreen;

private Graphics drawOffScreen;
private SpriteAndMaze spriteAndMaze;

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 characterImIndexX, characterImIndexY;
private int characterWidth, characterHeight;

public void init() {
addKeyListener(this);

setBackground(Color.white);

//取得Applet的高度、寬度
appletWidth = getSize().width;
appletHeight = getSize().height;

//取得影像
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();
}

spriteAndMaze = new SpriteAndMaze(0, 1, maze);

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

//建立次畫面
offScreen = createImage(appletWidth, appletHeight);
drawOffScreen = offScreen.getGraphics();

// 建立地圖
backGround = createImage(appletWidth, appletHeight);
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) {
// 在背後繪地圖
drawOffScreen.drawImage(backGround, 0, 0, this);
// 貼人物
drawOffScreen.drawImage(character,
spriteAndMaze.getLocationX()*blockWidth,
spriteAndMaze.getLocationY()*blockHeight,
(spriteAndMaze.getLocationX()+1)*blockWidth,
(spriteAndMaze.getLocationY()+1)*blockHeight,
characterImIndexX,
characterImIndexY,
characterImIndexX+characterWidth,
characterImIndexY+characterHeight,
this);

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

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

//=====實作KeyListener介面 =====

public void keyTyped(KeyEvent e) { }

public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();

characterImIndexX =
characterImIndexX + characterWidth;

switch(key) {
case KeyEvent.VK_RIGHT:
spriteAndMaze.moveRight();
characterImIndexY = characterHeight;
break;
case KeyEvent.VK_LEFT:
spriteAndMaze.moveLeft();
characterImIndexY = characterHeight * 3;
break;
case KeyEvent.VK_UP:
spriteAndMaze.moveUp();
characterImIndexY = 0;
break;
case KeyEvent.VK_DOWN:
spriteAndMaze.moveDown();
characterImIndexY = characterHeight * 2;
break;
}

if(characterImIndexX >= character.getWidth(null))
characterImIndexX = 0;

repaint();
}

public void keyReleased(KeyEvent e) {}
}

class SpriteAndMaze {
private int i, j;
private int maze[][];

public SpriteAndMaze() {}

public SpriteAndMaze(int x, int y) {
this.i = y;
this.j = x;
}

public SpriteAndMaze(int x, int y, int[][] maze) {
this(x, y);
this.maze = maze;
}

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

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

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

public 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;
else
return false;
}

public int getLocationX() {
return j;
}
public int getLocationY() {
return i;
}
public void setLocationX(int locationX) {
this.j = locationX;
}
public void setLocationY(int locationY) {
this.i= locationY;
}
public int[][] getMaze() {
return maze;
}
public void setMaze(int[][] maze) {
this.maze = maze;
}
}