场景绘制
前面我已经完成了直升飞机,现在到了整个游戏中最关键的部分,场景生成。
有专门的场景类Layer,其派生类TileLayer可以完成用图块拼合成场景的工作。先看看TiledLayer是怎么使用的
使用的一般步骤
1。先调用creataAnimatedTile构造函数,columns 和rows指的是屏幕上横向和纵向分别由多少图块,这点一定要记住,image是png图片,用相同大小图块构成,tileWidht和tileHeight则定义了图块的大小。
在我们这个游戏里,场景要用的图块只要一种就可以了。我们重复往tileLayer里面填这个图块,就可以构成复杂的场景,用随机来模拟地形的变化
2。构造一场景矩阵。为了模拟场景滚动,需要一个矩阵来完成这个工作,这样定义它int[][] gameMap=new int[rows][columns],该矩阵中存储图块的编号,对应image中的图块,从1开始,0则代表此单元在tiledLayer上是透明的。然后我们使用一个二重循环,调用setCell(int col,int row,int tileIndex)来完成将场景矩阵映射到屏幕上去的工作。我们可以发现,如果按一定规律对矩阵里的内容进行移动,就可以达到场景滚动的效果,这在我下面给出的源代码中可以看到,同时,如果我们只要按行或者按列来滚动场景,实际上只要专门准备一个对应于 行或者列的下标数组,循环的滚动下标就可以了,而不用非常低效地一点一点交换矩阵中的元素。我在Terrain.java中用了int mapIndex[]来做这个工作。
3.每一个游戏周期调用TiledLayer的paint方法,绘出矩阵。
我扩展了TiledLayer类为Terrain类,以满足我们这个游戏的需要。
附加模块:碰撞检测,每个周期都检测飞机所占据的单元是否跟砖块所占据的单元重叠即可。使用public boolean collides()实现。
源代码如下:
import javax.microedition.lcdui.game.*;
import javax.microedition.lcdui.Image;
import java.util.Random;
public class Terrain extends TiledLayer{
public int milemeter;
public int speed; // it is public so we could set the speed in main menu;
private int timestamp; // 时间戳,用来控制调整场景更新周期
private int tileWidth,tileHeight;
private int [][] gameMap = null;
private int screenWidth,screenHeight;
private int wallHeight;
private int Rows, Cols;
private Random random = null; // 随机数类
private int layerTop; // 保存场景在屏幕上的相对位置,用于平滑滚动场景。
private int [] indexMap = null;
private boolean up_down; // 地势是上升还是下降
private int alterSpeed; //地形变化速度
private int alterExtent; //地形变化持续周期
private int upBound,lowBound,wallPos; //当前地形上界和下界,墙壁的位置
private Helicopter player; //保存helicopter的引用,检测碰撞时用到
public Terrain(int columns,int rows,Image image,int tileWidth,int tileHeight,Helicopter player){
super(columns,rows,image,tileWidth,tileHeight); //TiledLayer构造函数
this.player = player;
speed = 0 ; //
random = new Random();
layerTop = -1* tileHeight;
speed = 0;
timestamp = 0;
this.tileHeight = tileHeight;
this.tileWidth = tileWidth;
screenWidth = getWidth();
screenHeight = getHeight();
wallHeight = screenWidth / 4 /tileWidth;
Rows = rows;
Cols = columns;
indexMap = new int[Rows];
for(int i=0;i<Rows;i++)indexMap[i] = i;
gameMap = new int[rows][columns];
for(int i=0;i<Rows;i++){
for(int j=0;j<Cols;j++){
if(j<(Cols/7) || (Cols-j)<=(Cols/7)) // 初始化场景矩阵,初始状况下上下边都有等厚度的墙壁
gameMap[i][j]=1;
}
}
for(int i=0;i<Rows;i++){
for(int j=0;j<Cols;j++){
this.setCell(j,i,gameMap[indexMap[i]][j]); // 将场景矩阵映射到TiledLayer上
}
}
alterSpeed = alterExtent = 0;
upBound = Cols/7;
lowBound = Cols-Cols/7;
}
public void update(){ // 更新场景
if (++timestamp > speed) { // 判断是否达到跟新周期
timestamp = 0;
if(layerTop == 0){
milemeter += 1;
for(int i=upBound+1;i<lowBound;i++)gameMap[indexMap[Rows-1]][i] = 0;
if(alterExtent > 0){
if(up_down == true){upBound -= alterSpeed;lowBound -= alterSpeed;}
else {upBound += alterSpeed; lowBound += alterSpeed;}
if(upBound<0){upBound += alterSpeed;lowBound+= alterSpeed; }
if(lowBound >= Cols-1){upBound -= alterSpeed;lowBound -= alterSpeed; }
for(int i=0;i<Cols;i++){
if(i<=upBound || i>=lowBound)
gameMap[indexMap[Rows-1]][i] = 1;
}
alterExtent--;
}
else { //产生新场景,存到已经滚出屏幕的那一列中,然后滚动下标,使其映射到屏幕最上一列
alterExtent = random.nextInt()%10; //产生地势变化持续时间,为正则地势上升,反之下降
if(alterExtent == 0)alterExtent=5; //避免时间为0的情况
if(alterExtent > 0){
up_down = true;
}
else {
up_down = false;
alterExtent *= -1;
}
alterSpeed = Math.abs(random.nextInt())%3+1; // 随即生成地势变化速度, 1~3
return;
}
// produces the wall barrier
if(milemeter % 10 == 0){ //隔定长距离产生一个墙壁
wallPos = Math.abs(random.nextInt())%(Cols-wallHeight);
for(int i=0;i<Cols;i++){
if(i==wallPos){
for(int j=0;j<wallHeight;j++)gameMap[indexMap[Rows-1]][i+j]=1;
break;
}
}
}
//:~ produce the barrier
// 调整场景矩阵的下标数组,实现滚屏
for(int i=0;i<Rows;i++){
indexMap[i]=indexMap[i]-1;
if(indexMap[i] == -1)indexMap[i] = Rows-1;
}
//:~ row mapping
// apply the new matrix to the tiledLayer
for(int i=0;i<Rows;i++){
for(int j=0;j<Cols;j++){
this.setCell(j,i,gameMap[indexMap[i]][j]);
}
}
//:~ apply
//将滚动映射到TiledLayer上
layerTop = -1 * tileHeight;
this.setPosition(0,layerTop);
//:~ update
}
else this.setPosition(0,layerTop++);
}
}
public boolean collides(){ //碰撞检测
for(int i=player.getPlayerRect().top / tileHeight;i<=player.getPlayerRect().bottom / tileHeight;i++){
for(int j=player.getPlayerRect().left / tileWidth;j<=player.getPlayerRect().right / tileWidth;j++){
if( gameMap[indexMap[i]][j] == 1){
return true;
}
}
}
return false;
}
}
前面我已经完成了直升飞机,现在到了整个游戏中最关键的部分,场景生成。
有专门的场景类Layer,其派生类TileLayer可以完成用图块拼合成场景的工作。先看看TiledLayer是怎么使用的
Constructor Summary | |
TiledLayer(int columns, int rows, Image image, int tileWidth, int tileHeight) Creates a new TiledLayer. |
int | createAnimatedTile(int staticTileIndex) Creates a new animated tile and returns the index that refers to the new animated tile. |
void | fillCells(int col, int row, int numCols, int numRows, int tileIndex) Fills a region cells with the specific tile. |
int | getAnimatedTile(int animatedTileIndex) Gets the tile referenced by an animated tile. |
int | getCell(int col, int row) Gets the contents of a cell. |
int | getCellHeight() Gets the height of a single cell, in pixels. |
int | getCellWidth() Gets the width of a single cell, in pixels. |
int | getColumns() Gets the number of columns in the TiledLayer grid. |
int | getRows() Gets the number of rows in the TiledLayer grid. |
void | paint(Graphics g) Draws the TiledLayer. |
void | setAnimatedTile(int animatedTileIndex, int staticTileIndex) Associates an animated tile with the specified static tile. |
void | setCell(int col, int row, int tileIndex) Sets the contents of a cell. |
void | setStaticTileSet(Image image, int tileWidth, int tileHeight) Change the static tile set. |
使用的一般步骤
1。先调用creataAnimatedTile构造函数,columns 和rows指的是屏幕上横向和纵向分别由多少图块,这点一定要记住,image是png图片,用相同大小图块构成,tileWidht和tileHeight则定义了图块的大小。
在我们这个游戏里,场景要用的图块只要一种就可以了。我们重复往tileLayer里面填这个图块,就可以构成复杂的场景,用随机来模拟地形的变化
2。构造一场景矩阵。为了模拟场景滚动,需要一个矩阵来完成这个工作,这样定义它int[][] gameMap=new int[rows][columns],该矩阵中存储图块的编号,对应image中的图块,从1开始,0则代表此单元在tiledLayer上是透明的。然后我们使用一个二重循环,调用setCell(int col,int row,int tileIndex)来完成将场景矩阵映射到屏幕上去的工作。我们可以发现,如果按一定规律对矩阵里的内容进行移动,就可以达到场景滚动的效果,这在我下面给出的源代码中可以看到,同时,如果我们只要按行或者按列来滚动场景,实际上只要专门准备一个对应于 行或者列的下标数组,循环的滚动下标就可以了,而不用非常低效地一点一点交换矩阵中的元素。我在Terrain.java中用了int mapIndex[]来做这个工作。
3.每一个游戏周期调用TiledLayer的paint方法,绘出矩阵。
我扩展了TiledLayer类为Terrain类,以满足我们这个游戏的需要。
附加模块:碰撞检测,每个周期都检测飞机所占据的单元是否跟砖块所占据的单元重叠即可。使用public boolean collides()实现。
源代码如下:
import javax.microedition.lcdui.game.*;
import javax.microedition.lcdui.Image;
import java.util.Random;
public class Terrain extends TiledLayer{
public int milemeter;
public int speed; // it is public so we could set the speed in main menu;
private int timestamp; // 时间戳,用来控制调整场景更新周期
private int tileWidth,tileHeight;
private int [][] gameMap = null;
private int screenWidth,screenHeight;
private int wallHeight;
private int Rows, Cols;
private Random random = null; // 随机数类
private int layerTop; // 保存场景在屏幕上的相对位置,用于平滑滚动场景。
private int [] indexMap = null;
private boolean up_down; // 地势是上升还是下降
private int alterSpeed; //地形变化速度
private int alterExtent; //地形变化持续周期
private int upBound,lowBound,wallPos; //当前地形上界和下界,墙壁的位置
private Helicopter player; //保存helicopter的引用,检测碰撞时用到
public Terrain(int columns,int rows,Image image,int tileWidth,int tileHeight,Helicopter player){
super(columns,rows,image,tileWidth,tileHeight); //TiledLayer构造函数
this.player = player;
speed = 0 ; //
random = new Random();
layerTop = -1* tileHeight;
speed = 0;
timestamp = 0;
this.tileHeight = tileHeight;
this.tileWidth = tileWidth;
screenWidth = getWidth();
screenHeight = getHeight();
wallHeight = screenWidth / 4 /tileWidth;
Rows = rows;
Cols = columns;
indexMap = new int[Rows];
for(int i=0;i<Rows;i++)indexMap[i] = i;
gameMap = new int[rows][columns];
for(int i=0;i<Rows;i++){
for(int j=0;j<Cols;j++){
if(j<(Cols/7) || (Cols-j)<=(Cols/7)) // 初始化场景矩阵,初始状况下上下边都有等厚度的墙壁
gameMap[i][j]=1;
}
}
for(int i=0;i<Rows;i++){
for(int j=0;j<Cols;j++){
this.setCell(j,i,gameMap[indexMap[i]][j]); // 将场景矩阵映射到TiledLayer上
}
}
alterSpeed = alterExtent = 0;
upBound = Cols/7;
lowBound = Cols-Cols/7;
}
public void update(){ // 更新场景
if (++timestamp > speed) { // 判断是否达到跟新周期
timestamp = 0;
if(layerTop == 0){
milemeter += 1;
for(int i=upBound+1;i<lowBound;i++)gameMap[indexMap[Rows-1]][i] = 0;
if(alterExtent > 0){
if(up_down == true){upBound -= alterSpeed;lowBound -= alterSpeed;}
else {upBound += alterSpeed; lowBound += alterSpeed;}
if(upBound<0){upBound += alterSpeed;lowBound+= alterSpeed; }
if(lowBound >= Cols-1){upBound -= alterSpeed;lowBound -= alterSpeed; }
for(int i=0;i<Cols;i++){
if(i<=upBound || i>=lowBound)
gameMap[indexMap[Rows-1]][i] = 1;
}
alterExtent--;
}
else { //产生新场景,存到已经滚出屏幕的那一列中,然后滚动下标,使其映射到屏幕最上一列
alterExtent = random.nextInt()%10; //产生地势变化持续时间,为正则地势上升,反之下降
if(alterExtent == 0)alterExtent=5; //避免时间为0的情况
if(alterExtent > 0){
up_down = true;
}
else {
up_down = false;
alterExtent *= -1;
}
alterSpeed = Math.abs(random.nextInt())%3+1; // 随即生成地势变化速度, 1~3
return;
}
// produces the wall barrier
if(milemeter % 10 == 0){ //隔定长距离产生一个墙壁
wallPos = Math.abs(random.nextInt())%(Cols-wallHeight);
for(int i=0;i<Cols;i++){
if(i==wallPos){
for(int j=0;j<wallHeight;j++)gameMap[indexMap[Rows-1]][i+j]=1;
break;
}
}
}
//:~ produce the barrier
// 调整场景矩阵的下标数组,实现滚屏
for(int i=0;i<Rows;i++){
indexMap[i]=indexMap[i]-1;
if(indexMap[i] == -1)indexMap[i] = Rows-1;
}
//:~ row mapping
// apply the new matrix to the tiledLayer
for(int i=0;i<Rows;i++){
for(int j=0;j<Cols;j++){
this.setCell(j,i,gameMap[indexMap[i]][j]);
}
}
//:~ apply
//将滚动映射到TiledLayer上
layerTop = -1 * tileHeight;
this.setPosition(0,layerTop);
//:~ update
}
else this.setPosition(0,layerTop++);
}
}
public boolean collides(){ //碰撞检测
for(int i=player.getPlayerRect().top / tileHeight;i<=player.getPlayerRect().bottom / tileHeight;i++){
for(int j=player.getPlayerRect().left / tileWidth;j<=player.getPlayerRect().right / tileWidth;j++){
if( gameMap[indexMap[i]][j] == 1){
return true;
}
}
}
return false;
}
}