今天继续昨天的代码继续完善坦克大战这个小游戏:
主要完成如下的功能:
5:让坦克可以开火。
* 6: 现在的子弹的处理方式是否有问题??把飞出屏幕的子弹移除掉。子弹飞出屏幕就不再绘制。
* 7:添加地图,绘制地图
* 8:解决闪屏:双缓冲
* 9:作业:坦克出生在屏幕的左下角、尝试,在屏幕的右上角和左上角,添加两个坦克。
5、第一步我们完成让坦克的开炮功能,首先,我们先定义一个炮弹类Bullet,初步完成子弹的飞行轨迹。
代码如下:
/**
* 炮弹类
* 属性:坐标,速度,方向,伤害,大小,炮弹的敌我,颜色
* @author gc
*
*/
public class Bullet {
private int x,y;
private int speed = 8;
private int dir;
private int atk;
private int diameter = 4;//diameter
private boolean isFriendly;//炮弹的敌我属性
private Color color;//bullet's color
//子弹是否可见
private boolean visible = true;
public Bullet(int x, int y, int dir, int atk, boolean isFriendly, Color color) {
super();
this.x = x;
this.y = y;
this.speed = speed;
this.dir = dir;
this.atk = atk;
this.diameter = diameter;
this.isFriendly = isFriendly;
this.color = color;
}
public void draw(Graphics g) {
//一旦子弹飞出屏幕,这代码就不应该再被执行
//如果不可见
if (!visible)return;
g.setColor(color);
int w = diameter>>1;
g.fillArc(x-w, y-w, diameter, diameter, 0, 360);
//子弹的飞行轨迹
logic();
}
//子弹的飞行轨迹
private void logic() {
switch (dir) {
case Tank.DIR_UP:
y -= speed;
if(y < 0)visible = false;
break;
case Tank.DIR_DOWN:
y += speed;
if(y > Constant.FRAME_HEIGHT)visible = false;
break;
case Tank.DIR_LEFT:
x -= speed;
if (x < 0)visible = false;
break;
case Tank.DIR_RIGHT:
x += speed;
if(x > Constant.FRAME_WIDTH)visible = false;
break;
}
}
/////////////////////////////////////
//增加在其他的类访问子弹是否可见的属性
public boolean isVisible() {
return visible;
}
public void setVisible(boolean visible) {
this.visible = visible;
}
}
接着,在Tank的类中,完成让坦克发射炮弹的功能,代码如下:
//坦克发射炮弹功能
public Bullet fire(){
int w = 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);
}
6、优化一下炮弹处理方式,防止容器中的炮弹数量过大,把飞出屏幕的子弹移除掉。子弹飞出屏幕就不再绘制。
代码如下:
public void draw(Graphics g) {
//一旦子弹飞出屏幕,这代码就不应该再被执行
//如果不可见
if (!visible)return;
g.setColor(color);
int w = diameter>>1;
g.fillArc(x-w, y-w, diameter, diameter, 0, 360);
//子弹的飞行轨迹
logic();
7、完成了Tank的发射炮弹的功能,接着我们开始绘制游戏的地图。在制作2d游戏地图时,我们要注意地图有如下的几层:/**
* 地图类。
* 1:第一层,地表层,没有碰撞
* 2:碰撞层。
* 3:精灵层
* 4:遮挡层
今天,我们仅仅完成了,第一层,地表层和第二层碰撞层的绘制,首先我们根据定义的窗口,自定义地图的数据,放在MapData类中。具体的数据数组如下:
package com.hbust.entity;
/**
* 地图碰撞层的数据的类
* @author gc
*
*/
public class MapData {
// 800*600 25 cols 32 rows 24
//0 代表没有碰撞的块。1代表可以碰撞的地图块
public static int[][] mapDate0 = {
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,1,0,0,0},
{0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,1,0,0,0},
{0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,1,0,0,0},
{0,0,1,1,1,0,0,0,1,1,1,0,0,1,1,1,0,1,1,1,0,0,0,1,1,1,0,0,1,0,0,0},
{0,0,1,1,1,0,0,0,1,1,1,0,0,1,1,1,0,1,1,1,0,0,0,1,1,1,0,0,1,0,0,0},
{0,0,1,1,1,0,0,0,1,1,1,0,0,1,1,1,0,1,1,1,0,0,0,1,1,1,0,0,1,0,0,0},
{0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,1,0,0,0},
{0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,1,0,0,0},
{0,0,1,1,1,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,1,0,0,0},
{0,0,1,1,1,0,0,0,1,1,1,0,0,1,1,1,0,1,1,1,0,0,0,1,1,1,0,0,1,0,0,0},
{0,0,1,1,1,0,0,0,1,1,1,0,0,1,1,1,0,1,1,1,0,0,0,1,1,1,0,0,1,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,1,1,1,1,1,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,1,1,1,1,1,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,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,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,0,0,0},
{0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,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,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
};
}
效果如下图所示:
(这是完成地图绘制之后的成品图!)
接着,我们开始绘制地图中的砖块以及相应颜色填充,创建一个map类,具体代码如下:
/**
* 地图类。
* 1:第一层,地表层,没有碰撞
* 2:碰撞层。
* 3:精灵层
* 4:遮挡层
* @author yhl
* 属性:宽度高度,单元格的宽度和高度tile,地图数据
*
*/
public class Map {
//red block data
public static final int RED_BLOCK_DATA = 1;
// map width and height
private int width , height;
//地图单元格的宽度,正方形
public static final int TILE_WIDTH = 25;
//地图单元格的行数和列数
private int rowTileCount,colTileCount;
public Map(int rowTileCount, int colTileCount) {
super();
this.rowTileCount = rowTileCount;
this.colTileCount = colTileCount;
width = colTileCount * TILE_WIDTH;
height = rowTileCount * TILE_WIDTH;
}
public void draw(Graphics g) {
drawLayer0(g);
drawLayer1(g);
}
//绘制第一层,地表层
//单色填充, 应该有地表层的数据
private void drawLayer0(Graphics g) {
g.setColor(Color.GREEN);
g.fillRect(0, Constant.TITLE_BAR_HEIGHT, width, height);
}
//绘制碰撞层
private void drawLayer1(Graphics g) {
for(int i=0;i
8、在运行的过程中,我们发现生成的窗口一直在闪烁,为什么呢?大概是因为绘制的东西太多,而全部刷新地时间不够,于是这里提出一种双缓冲的技术来解决这个问题。具体代码如下:
//地图对象
private Map map = new Map(Constant.FRAME_HEIGHT/Map.TILE_WIDTH,
Constant.FRAME_WIDTH/Map.TILE_WIDTH);
//双缓冲技术:定义一张图片和地图的大小一致。用图片的画笔将需要绘制的内容全部绘制到图片上来。
//然后,再用系统画笔,将图片一次性的绘制到窗口上来。
private BufferedImage bufImg = new BufferedImage(Constant.FRAME_WIDTH,
Constant.FRAME_HEIGHT, BufferedImage.TYPE_3BYTE_BGR);
private Graphics ImgG;
修改绘图刷新的方法(注意使用update方法):
public void update(Graphics g) {
if (ImgG == null) {
ImgG = bufImg.getGraphics(); }
// map.draw(g);
// myTank.draw(g);
// drawBullets(g);
map.draw(ImgG);
myTank.draw(ImgG);
Tank1.draw(ImgG);
Tank2.draw(ImgG);
drawBullets(ImgG);
//将图片一次性的会知道窗口上使用系统画笔
g.drawImage(bufImg, 0, 0, null);
// g.drawString("子弹数量 = "+ bullets.size(),20, 100);
}
9、今天的作业,调整坦克出生的位置好处理,主要我们生成另外的坦克,其实也不难,理解了坦克这么生成的就行,主要修改GameFrame类中的initMytank方法,
myTank = new Tank(20,Constant.FRAME_HEIGHT-Constant.TITLE_BAR_HEIGHT-20,
Tank.DIR_UP);
Tank1 = new Tank(20,20,Tank.DIR_DOWN);
Tank2 = new Tank(Constant.FRAME_WIDTH-Constant.TITLE_BAR_HEIGHT-20,20,
Tank.DIR_DOWN);
接着,在更新的方法中更新创建的坦克:
myTank.draw(ImgG);
Tank1.draw(ImgG);
Tank2.draw(ImgG);
今天学得东西,比较多,比如双缓冲技术还是不是非常理解,另外对2D游戏地图的制作有了相应的了解。
希望明天能继续学到相应的知识。