记得在红白机(FC)年代,还刚刚上小学的我对马里奥、冒险岛、洛克人、魂斗罗等游戏几乎可说是痴迷,每天放学回家就是想去游戏,就是要通关,就是想和关底论个胜负高低。
许多年过去了,沧海桑田,FC曾经的荣耀早已不再,只留下我们对曾经少年时的点点记忆。即使当时在FC上看上去多么复杂,多么高不可攀的游戏,在当今,即使最普通的程序员都可以轻易实现。
本着向经典学习、向经典致敬的心情,我也准备用Java在PC机再现当年马里奥的风采。
下面我在代码中所演示的,是一个简单的ACT游戏动作及地图构成原型。
Map.java
package org.test.mario;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;


/** *//**
* <p>
* Title: LoonFramework
* </p>
* <p>
* Description:地图绘制及描述用类
* </p>
* <p>
* Copyright: Copyright (c) 2008
* </p>
* <p>
* Company: LoonFramework
* </p>
*
* @author chenpeng
* @email:ceponline@yahoo.com.cn
* @version 0.1
*/

public class Map ...{

// 在以前的blog文章中我介绍过,游戏开发中通常以数组描述地图
// 此处1描绘为一个障碍物,0描绘为一个可通行空间

final static private int[][] map = ...{

...{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },

...{ 1, 0, 0, 0, 0, 0, 0, 0, 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, 1 },

...{ 1, 0, 0, 0, 0, 0, 0, 0, 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, 1, 1, 1, 1 },

...{ 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 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, 1 },

...{ 1, 0, 0, 0, 0, 0, 0, 0, 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, 1 },

...{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1 },

...{ 1, 0, 0, 0, 0, 0, 0, 0, 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, 1 },

...{ 1, 0, 0, 0, 0, 0, 0, 0, 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, 1 },

...{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } };

// 地面瓦片的宽度
final static private int TILE_SIZE = 32;

// 行
final static private int ROW = 15;

// 列
final static private int COL = 20;


/** *//**
* 构造函数
*
*/

public Map() ...{
}


public void draw(Graphics g) ...{
g.setColor(Color.ORANGE);

for (int i = 0; i < ROW; i++) ...{

for (int j = 0; j < COL; j++) ...{

switch (map[i][j]) ...{
case 1:
g.fillRect(tilesToPixels(j), tilesToPixels(i), TILE_SIZE,
TILE_SIZE);
break;
}
}
}
}


/** *//**
* 换算角色与地板的撞击,并返回Point用以描述新的x,y
*
* @param player
* @param newX
* @param newY
* @return
*/

public Point getTileHit(Role player, double newX, double newY) ...{
// 取最小的整数但不能小于自身,用以换算坐标
newX = Math.ceil(newX);
newY = Math.ceil(newY);

double fromX = Math.min(player.getX(), newX);
double fromY = Math.min(player.getY(), newY);
double toX = Math.max(player.getX(), newX);
double toY = Math.max(player.getY(), newY);

int fromTileX = pixelsToTiles(fromX);
int fromTileY = pixelsToTiles(fromY);
int toTileX = pixelsToTiles(toX + Role.WIDTH - 1);
int toTileY = pixelsToTiles(toY + Role.HEIGHT - 1);

// 返回Point,用以描述x,y坐标点

for (int x = fromTileX; x <= toTileX; x++) ...{

for (int y = fromTileY; y <= toTileY; y++) ...{

if (x < 0 || x >= COL) ...{
return new Point(x, y);
}

if (y < 0 || y >= ROW) ...{
return new Point(x, y);
}

if (map[y][x] == 1) ...{
return new Point(x, y);
}
}
}

return null;
}


/** *//**
* 将Tiles转为Pixels
*
* @param pixels
* @return
*/

public static int pixelsToTiles(double pixels) ...{
return (int) Math.floor(pixels / TILE_SIZE);
}


/** *//**
* 将Pixels转为Tiles
*
* @param pixels
* @return
*/

public static int tilesToPixels(int tiles) ...{
return tiles * TILE_SIZE;
}
}

Role.java
package org.test.mario;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;


![]()