大家好,今天来共同探讨一下,飞机大战这个游戏,本篇章主要讲解飞机大战的背景绘制,用户飞机的绘制,敌机的绘制,子弹的绘制,本次游戏设计和前两次是一样的,都是采用的是SurfaceView+BitMap二级缓存实现的。见下图
首先来看看先,下面这个接口
public interface ImageErji {
float getX();
float getY();
Bitmap draw();
//销毁BitMap
void recycle();
}
一共四个方法,分别是获取坐标的x,y,绘制图片以及销毁BitMap的方法。该接口适用于所有的游戏中出现的图片。
class Bg implements ImageErji {
private Bitmap newBitMap = null;
private int hight = 0;
private float x;
private float y;
public Bg() {
newBitMap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Bitmap.Config.ARGB_8888);
}
public void setX(float x) {
this.x = x;
}
public void setY(float y) {
this.y = y;
}
@Override
public float getX() {
return x;
}
@Override
public float getY() {
return y;
}
@Override
public Bitmap draw() {
mCanvas.setBitmap(newBitMap);
mCanvas.drawBitmap(mBackGroungBitMap, new Rect(0, 0, mBackGroungBitMap.getWidth(), mBackGroungBitMap.getHeight())
, new Rect(0, -getMeasuredHeight() + hight, getMeasuredWidth(), hight), mPaint);
mCanvas.drawBitmap(mBackGroungBitMap, new Rect(0, 0, mBackGroungBitMap.getWidth(), mBackGroungBitMap.getHeight()),
new Rect(0, hight, getMeasuredWidth(), hight + getMeasuredHeight()), mPaint);
hight+=10;
if (hight >= getMeasuredHeight()) {
hight = 0;
}
return newBitMap;
}
public void reset(){
hight = 0;
}
@Override
public void recycle() {
newBitMap.recycle();
}
}
该类draw() 实现的功能是,背景图片的从上至下移动,线程没循环一次就要绘制一次
接下来看用户飞机的绘制
class UserPlane implements ImageErji {
private Bitmap newBitMap;
private int flag = 0;
private float x;
private float y;
public UserPlane() {
newBitMap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Bitmap.Config.ARGB_8888);
//设置初始位置
y = getMeasuredHeight() - mUserBitMap.getHeight();
x = (getMeasuredWidth() - mUserBitMap.getWidth() / 4) / 2;
}
public void setX(float x) {
this.x = x;
}
public void setY(float y) {
this.y = y;
}
@Override
public float getX() {
return x;
}
@Override
public float getY() {
return y;
}
public boolean check() {
if (startTuchPoint.x < x || startTuchPoint.x > (x + mUserBitMap.getWidth() / 4)) {
return false;
}
if (startTuchPoint.y < y || startTuchPoint.y > y + mUserBitMap.getHeight()) {
return false;
}
return true;
}
//设置userPlane的坐标
public void resetXY(float x, float y) {
float xx = x - mUserBitMap.getWidth() / 8;
float yy = y - mUserBitMap.getHeight() / 2;
//边界检查
if (xx < 0 || xx > getMeasuredWidth() - mUserBitMap.getWidth() / 4) {
return;
}
if (yy < 0 || yy > getMeasuredHeight() - mUserBitMap.getHeight()) {
return;
}
//设置
setX(xx);
setY(yy);
}
@Override
public Bitmap draw() {
mCanvas.setBitmap(newBitMap);
Bitmap b = mUserLisrt.get(flag / 10);
mCanvas.drawBitmap(b, new Rect(0, 0, b.getWidth(), b.getHeight())
, new Rect(0, 0, b.getWidth(), b.getHeight()), mPaint);
flag++;
if (flag == 40) {
flag = 0;
}
return newBitMap;
}
@Override
public void recycle() {
newBitMap.recycle();
}
public void reset() {
y = getMeasuredHeight() - mUserBitMap.getHeight();
x = (getMeasuredWidth() - mUserBitMap.getWidth() / 4) / 2;
}
}
该类主要是负责用户飞机的绘制,检查,和销毁,背景和用户飞机只有一个实体类,所以作为字段,在urfaceCreated()方法中初始化,还要提供一个集合来承载背景和用户飞机,在线程方法中依次绘制背景和用户飞机,还有一点,用户飞机和敌机是有动画效果的,所以要在一次线程循环中绘制不同的图片,敌机也是如此,如下代码块
//循环绘制图片,将图片贴到mPicture上
for (ImageErji imageErji : listErji) {
Bitmap b = imageErji.draw();
mCanvas.setBitmap(mPicture);
mCanvas.drawBitmap(b, imageErji.getX(), imageErji.getY(), mPaint);
}
绘制玩背景和用户飞机后,还要能拖动一起移动,这样我们还要重写OnTuchEvent()方法,这里我控制飞机开始是在屏幕的底部中间的位置
@Override
public boolean onTouchEvent(MotionEvent event) {
isUserUseful = true;
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startTuchPoint.set(event.getX(), event.getY());
if (mUserPlane.check()) {
isUserUseful = true;
} else {
isUserUseful = false;
}
break;
case MotionEvent.ACTION_MOVE:
if (isUserUseful) {
//选中User的飞机,让飞机在点击位置的中间位置
mUserPlane.resetXY(event.getX(), event.getY());
}
break;
case MotionEvent.ACTION_UP:
break;
}
return true;
}
接下来,我们来绘制敌人的飞机
class Enemy implements ImageErji {
private Bitmap newBitMap;
private int flag = 0;
private float x;
private float y;
public Enemy() {
newBitMap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Bitmap.Config.ARGB_8888);
}
public void setX(float x) {
this.x = x;
}
public void setY(float y) {
this.y = y;
}
@Override
public float getX() {
return x;
}
@Override
public float getY() {
return y;
}
@Override
public Bitmap draw() {
mCanvas.setBitmap(newBitMap);
Bitmap b = mEnemyBitMapList.get(flag / 10);
mCanvas.drawBitmap(b, new Rect(0, 0, b.getWidth(), b.getHeight())
, new Rect(0, 0, b.getWidth(), b.getHeight()), mPaint);
flag++;
if (flag == 40) {
flag = 0;
}
return newBitMap;
}
public void reset(){
flag = 0;
}
@Override
public void recycle() {
newBitMap.recycle();
}
}
值得注意的是,为了不让随机的飞机重叠在一起,我先划分了n快区域,再在每块区域中取随机数,自认为这种方法是最简单的
private void creatEnemyPoint() {
boolean isNeed = false;
//找最近的
if (mEnemyList.size() == 0) {
isNeed = true;
} else {
Enemy e = mEnemyList.get(mEnemyList.size() - 1);
if (e.getY() > mEnemyBitMap.getHeight()) {
isNeed = true;
}
}
if (isNeed) {
//最大飞机的数量
int num = mRandom.nextInt(getMeasuredWidth() / mEnemyBitMap.getWidth());
int countW;
//把横向高度分成num份,产生的随机数加上前面的num份距离
if (num == 0) {
countW = getMeasuredWidth() - mEnemyBitMap.getWidth();
} else {
countW = getMeasuredWidth() / num;
}
for (int i = 0; i < num; i++) {
//减去一个飞机的宽度
int x = mRandom.nextInt(countW - mEnemyBitMap.getWidth()) + i * countW;
Enemy ee = new Enemy();
ee.setX(x);
ee.setY(-mEnemyBitMap.getHeight());
mEnemyList.add(ee);
}
}
}
接下来,要绘制子弹了
//子弹
class ZiDan implements ImageErji {
private Bitmap newBitMap;
private float x;
private float y;
private boolean isDelete = false;
public boolean isDelete() {
return isDelete;
}
public void setDelete(boolean delete) {
isDelete = delete;
}
public ZiDan() {
newBitMap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Bitmap.Config.ARGB_8888);
}
@Override
public float getX() {
return x;
}
@Override
public float getY() {
return y;
}
public void setX(float x) {
this.x = x;
}
public void setY(float y) {
this.y = y;
}
@Override
public Bitmap draw() {
mCanvas.setBitmap(newBitMap);
mCanvas.drawBitmap(mZiDuanBitMap, 0, 0, mPaint);
return newBitMap;
}
@Override
public void recycle() {
newBitMap.recycle();
}
private boolean CheckPengZhuang(List<ZiDan> list){
for (int i = 0; i <mEnemyList.size() ; i++) {
Enemy e = mEnemyList.get(i);
if (x+mZiDuanBitMap.getWidth()<e.getX()||x>e.getX()+mEnemyBitMap.getWidth()/4){
continue;
}
if (y-e.getY()+mEnemyBitMap.getHeight()<=0){
mEnemyList.remove(i);
e.recycle();
list.remove(this);
recycle();
return true;
}
}
return false;
}
}
这里子弹的绘制就不在过多的阐述了 ,方法基本一样,然后我们还要生产子弹,当最后的子弹移动了一个子弹的高度了,就要生产一个子弹
private void creatZiDan() {
boolean isNeed = true;
if (mZiDanList.size()>0) {
ZiDan zz = mZiDanList.get(mZiDanList.size() - 1);
if (zz.getY()<mUserPlane.getY()-mZiDuanBitMap.getHeight()){
isNeed = true;
}else {
isNeed = false;
}
}
if (isNeed) {
//生产子弹
float xx = mUserPlane.getX();
float yy = mUserPlane.getY();
float positionX =xx+(mUserBitMap.getWidth()/4-mZiDuanBitMap.getWidth())/2;
ZiDan z = new ZiDan();
z.setX(positionX);
z.setY(yy - mZiDuanBitMap.getHeight());
mZiDanList.add(z);
}
}
现在背景,用户飞机,敌机,子弹都有了,我们就要考虑碰撞问题,好啦,下一篇我们继续讲,github地址:https://github.com/yzzAndroid/PlaneWar