1、今天的东西感觉比较难,主要完成了以下几个功能,
10:坦克和墙壁的碰撞
11:子弹和墙体的碰撞
12:敌人坦克的AI,增加敌人坦克的处理控制。
但是一些方法写的比较复杂,不废话了,下面是今天完成的任务。
10、首先我们完成坦克碰到墙壁会停止的功能,大体的思路是这样:
坦克是否碰上了砖墙:
将坦克的坐标找到地图的某一个单元格,或者是某几个单元格,然后根据相应的坐标和方向,得到三个点的坐标,然后使用这三个点的坐标,依次检测是否产生碰撞了。只要有一个点产生了碰撞,那么就坐标还原,后续的碰撞检测就不需要做了。也就是说这就是一个检测坦克的坐标和设置的砖块为“1”的坐标是否一致,一致则停止,我们这里要设置四个点,我在这里以设置X坐标为例子:
/**
* 增加了向右的坐标的-1
* 设置坦克的X坐标的方法
* @param x
*/
public void setX(int x){
if(x < 0){//坦克到了左边界
x = 0;
}
//右边界
if(x >= Constant.FRAME_WIDTH-TANK_WIDTH){
//坦克的x 坐标 x = Constant.FRAME_WIDTH-TANK_WIDTH 最大值。
x = Constant.FRAME_WIDTH-TANK_WIDTH-1;
}
//备份没撞墙之前的x 坐标
int oldX = this.x;
this.x = x;
//根据方向,得到三个点的坐标,然后使用这三个点的坐标,依次检测是否产生碰撞了。
//只要有一个点产生了碰撞,那么就坐标还原,后续的碰撞检测就不需要做了
int collideX,collideY;
boolean isCollide;
switch(dir){
case DIR_UP:
//第一个点
collideX = this.x;
collideY = this.y;
isCollide = collideWall(collideX, collideY);
if(isCollide){
this.x = oldX;
return;
}
//第二个点
collideX = this.x+TANK_WIDTH/2;
collideY = this.y;
isCollide = collideWall(collideX, collideY);
if(isCollide){
this.x = oldX;
return;
}
//第三个点
collideX = this.x+TANK_WIDTH;
collideY = this.y;
isCollide = collideWall(collideX, collideY);
if(isCollide){
this.x = oldX;
return;
}
break;
case DIR_DOWN:
//第一个点
collideX = this.x;
collideY = this.y+TANK_WIDTH;
isCollide = collideWall(collideX, collideY);
if(isCollide){
this.x = oldX;
return;
}
//第二个点
collideX = this.x+TANK_WIDTH/2;
collideY = this.y+TANK_WIDTH;
isCollide = collideWall(collideX, collideY);
if(isCollide){
this.x = oldX;
return;
}
//第三个点
collideX = this.x+TANK_WIDTH;
collideY = this.y+TANK_WIDTH;
isCollide = collideWall(collideX, collideY);
if(isCollide){
this.x = oldX;
return;
}
break;
case DIR_LEFT:
//第一个点
collideX = this.x;
collideY = this.y;
isCollide = collideWall(collideX, collideY);
if(isCollide){
//不让坦克的坐标还原了,而是设置为碰撞块的边缘的坐标
//是否能知道碰撞的块的行号和列号??
//地图的列单元
int mapColTile = collideX/Map.TILE_WIDTH;
int mapColTileX = (mapColTile+1)*Map.TILE_WIDTH;
this.x = mapColTileX;
return;
}
//第二个点
collideX = this.x;
collideY = this.y+TANK_WIDTH/2;
isCollide = collideWall(collideX, collideY);
if(isCollide){
int mapColTile = collideX/Map.TILE_WIDTH;
int mapColTileX = (mapColTile+1)*Map.TILE_WIDTH;
this.x = mapColTileX;
return;
}
//第三个点
collideX = this.x;
collideY = this.y+TANK_WIDTH;
isCollide = collideWall(collideX, collideY);
if(isCollide){
int mapColTile = collideX/Map.TILE_WIDTH;
int mapColTileX = (mapColTile+1)*Map.TILE_WIDTH;
this.x = mapColTileX;
return;
}
break;
case DIR_RIGHT:
//第一个点
collideX = this.x+TANK_WIDTH;
collideY = this.y;
isCollide = collideWall(collideX, collideY);
if(isCollide){
int mapColTile = collideX/Map.TILE_WIDTH;
int mapColTileX = mapColTile*Map.TILE_WIDTH;
this.x = mapColTileX-Tank.TANK_WIDTH-1;
return;
}
//第二个点
collideX = this.x+TANK_WIDTH;
collideY = this.y+TANK_WIDTH/2;
isCollide = collideWall(collideX, collideY);
if(isCollide){
int mapColTile = collideX/Map.TILE_WIDTH;
int mapColTileX = mapColTile*Map.TILE_WIDTH;
this.x = mapColTileX-Tank.TANK_WIDTH-1;
return;
}
//第三个点
collideX = this.x+TANK_WIDTH;
collideY = this.y+TANK_WIDTH;
isCollide = collideWall(collideX, collideY);
if(isCollide){
int mapColTile = collideX/Map.TILE_WIDTH;
int mapColTileX = mapColTile*Map.TILE_WIDTH;
this.x = mapColTileX-Tank.TANK_WIDTH-1;
return;
}
break;
}
}
因为涉及到后面的功能,所以部分代码是后面功能的,所以后面设置X,Y,就不做重复地粘贴了!
11、接着,我们要完成子弹和墙壁的碰撞,以及碰撞时的特效。其实子弹的碰撞与坦克和墙体的碰撞是一样的方法,唯一的区别是当子弹接触墙体时,砖块要消失,那么怎么使得墙壁小时呢?
if (visible) {
//子弹和墙壁碰撞
//先求子弹的坐标对应的单元格的行和列
int mapRowTile = (y-Constant.TITLE_BAR_HEIGHT)/Map.TILE_WIDTH;
//地图的列单元
int mapColTile = x /Map.TILE_WIDTH;
if (MapData.mapDate0[mapRowTile][mapColTile]== Map.RED_BLOCK_DATA) {
visible = false;
MapData.mapDate0[mapRowTile][mapColTile] = 0;
//将产生一个爆炸效果,放到容器中
Explode explode = new Explode(x, y);
GameFrame.explodes.add(explode);
}
}
}
//如果子弹可见
/
//增加在其他的类访问子弹是否可见的属性
public boolean isVisible() {
return visible;
}
public void setVisible(boolean visible) {
this.visible = visible;
}
}
这就是今天上午完成的一些功能了,下午的话主要是完成地方坦克的生成以及其AI的实现。
12、在游戏中我们需要敌方的坦克来增加游戏的趣味性,生成敌方的坦克的方式与生成自己的坦克一样,都是调用相同的方法,不过就是要设置敌方坦克的生成数量以及样式,还有要让敌方坦克自己移动开炮而已,这部分的代码理解起来不难,大部分也都是运用随机函数,不过这里出现了一个数组越界的错误,其实也容易解决。不过是加1减1的问题而已!
创建敌坦克的方式和建立自己的一样:
//创建敌人坦克的构造方法
public Tank() {
super();
//随机在左上角,或者右上角
if(MyUtil.isHappened(0.5)){//左上角
this.x = 0;
}else{//右上角
this.x = Constant.FRAME_WIDTH-1-Tank.TANK_WIDTH;
}
this.y = Constant.TITLE_BAR_HEIGHT;
this.dir = MyUtil.getRandomNumber(DIR_UP, DIR_RIGHT+1);
isGood = false;
this.color = MyUtil.getRandomColor();
}
然后,定义控制部分:
//敌人坦克的控制部分
private int randomInterval;
public static final int MIN_INTERVAL = 1000;
public static final int MAX_INTERVAL = 3000;
//用于记录本次随机的开始时间
private long startTime;
接着就是写敌方坦克的AI部分了,这部分写的敌方的坦克是随机方向运动,随机的一直行动下去,并且在行动的过程中也是随机一边移动,一边开炮。具体代码如下:
/**
* 敌人的AI逻辑
*/
private void enemyAi(){
//敌人的坦克一直是行走的状态,随机的方向,时间随机[1000~5000].
//时间到达之后,继续随机 随机的方向,时间随机[1000~5000].
//随机控制控制部分
long time = System.currentTimeMillis();
if(time - startTime >= randomInterval){
this.dir = MyUtil.getRandomNumber(DIR_UP, DIR_RIGHT+1);
startTime = time;
randomInterval = MyUtil.getRandomNumber(MIN_INTERVAL, MAX_INTERVAL);
}
switch(dir){
case DIR_UP:
setY(y-speed);
break;
case DIR_DOWN:
setY(y+speed);
break;
case DIR_LEFT:
setX(x-speed);
break;
case DIR_RIGHT:
setX(x+speed);
break;
}
//随机发射子弹
if(Math.random() < 0.05){
GameFrame.enemyBullets.add(fire());
}
}
//坦克发射炮弹功能
public Bullet fire(){
int w = TANK_WIDTH >> 1;
int bulletX = 0;
int bulletY = 0;
//根据坦克(根据方向)的坐标来计算炮弹的坐标
switch(dir){
case DIR_UP:
bulletX = x + w;
bulletY = y - w;
break;
case DIR_DOWN:
bulletX = x + w;
bulletY = y + 3*w;
break;
case DIR_LEFT:
bulletX = x - w;
bulletY = y + w;
break;
case DIR_RIGHT:
bulletX = x + 3*w;
bulletY = y + w;
break;
}
//生成的子弹的方向和攻击力还有颜色都和坦克一致
return new Bullet(bulletX, bulletY, dir, atk, true, color);
}
好了,今天的主要任务大体上就是这么多了!今天主要解决的问题比较多,学习起来其实是比较枯燥的,但是完成之后的效果其实是比较有成就和比较有趣的!明天继续加油吧!