整体实现思路
在SurfaceView上利用canvas的drawBitmap方法画背景图(两张背景图循环滚动),子弹(利用Vector数组存入子弹并循环打印 ),飞机(利用onTouchEvent方法移动飞机监听屏幕),以及加成道具,爆炸效果等
用MediaPlayer实现背景音乐的播放与循环
用SoundPool实现子弹,爆炸,道具等音效
如何绘制循环滚动的背景图
用两次drawBitmap方法绘制背景图
每次绘制后,让两张背景图的y坐标减10;
当第一张背景图移除屏幕时,立刻把这张背景图的移至另一张背景的上面
代码如下:
import android.graphics.Bitmap;
importandroid.graphics.Canvas;
importandroid.graphics.Paint;
public classBackGround {
private int y1;
private int y2;
private Bitmap bitmap;
public BackGround(Bitmap bitmap) {
this.bitmap = bitmap;
y1 = 0;
y2 = y1 - bitmap.getHeight();
}
public void draw(Canvas canvas,Paint paint){
logic();
canvas.drawBitmap(bitmap, 0, y1,paint);
canvas.drawBitmap(bitmap, 0, y2,paint);
}
public void logic() {
y1 += 10;
y2 += 10;
if (y1 >= MySurfaceView.height) {
y1 = y2 - bitmap.getHeight();
}
if (y2 >= MySurfaceView.height) {
y2 = y1 - bitmap.getHeight();
}
}
}
如何绘制飞机
绘制小飞机的方法就是canvas.drawBitmap,xy坐标默认为屏幕的最下方,之后的xy跟随手指移动
绘制BossPlane时,需要用到
canvas.save();//画布保存
canvas.clipRect();//画布裁剪
canvas.drawBitmap();
canvas.restore();//画布恢复
需要先裁剪画布为只显示图片的七分之一;
代码如下:
canvas.save();//画布保存和画布恢复
canvas.clipRect(x,y,x+frameW,y+frameH);
canvas.drawBitmap(bitmap,x,y,paint);
canvas.restore();
· 1
· 2
· 3
· 4
x,y为飞机的坐标,frameW为bitmap七分之一的宽度,frameH为bitmap的高度
如何绘制子弹
定义一个Vector数组bullet,存储Bullet的对象
在MySurfaceView中定义一个计数器count,每次运行run方法中的while中的内容时,就让这个计数器count++;
再在while中作判断,当count%50=0时,就往数组bullet中添加一个Bullet的对象,再用for循环
bullet.elementAt(i)调用Bullet中的draw方法,设置每次画出子弹后让子弹的y轴–,再判断子弹是否击中目标或者飞出屏幕,
选择是否remove该对象
Boss的子弹同理
代码如下:
while (isDrawing) {
count++;
try {
canvas =surfaceHolder.lockCanvas();
canvas.drawColor(Color.WHITE);
backGround.draw(canvas, paint);
plane.draw(canvas,paint);
bossplane.draw(canvas, paint);
if (count % 50 == 0) {
Bullet bullet =new Bullet(BitmapFactory.decodeResource(getResources(), R.mipmap.mybullet),plane.getX(), plane.getY(), 0);
Bullet bullet1 =new Bullet(BitmapFactory.decodeResource(getResources(), R.mipmap.mybullet),plane.getX() + plane.getWidth(), plane.getY(), 0);
bulletVector.add(bullet);
bulletVector.add(bullet1);
}
//移除消失的子弹
for (int i = 0; i< bulletVector.size(); i++) {
if(bulletVector.elementAt(i).isDead()) {
bulletVector.remove(i);
}
}
//绘制玩家子弹
for (int i = 0; i< bulletVector.size(); i++) {
bulletVector.elementAt(i).draw(canvas, paint);
if(bossplane.isCollision(bulletVector.elementAt(i))) {
gameSoundPool.playSound(2);
Boom boom =new Boom(BitmapFactory.decodeResource(getResources(), R.mipmap.boom),bossplane.getX(), bossplane.getY(), 7);
boomVector.add(boom);
}
}
for (int i = 0; i< boomVector.size(); i++) {
if(boomVector.elementAt(i).isEnd()) {
boomVector.remove(i);
} else {
boomVector.elementAt(i).draw(canvas, paint);
}
}
if (count % 40 == 0){
Bullet bullet =new Bullet(BitmapFactory.decodeResource(getResources(), R.mipmap.bossbullet),bossplane.getX(), bossplane.getFrameH(), 1);
Bullet bullet1 =new Bullet(BitmapFactory.decodeResource(getResources(), R.mipmap.bossbullet),bossplane.getX() + bossplane.getFrameW(), bossplane.getY() +bossplane.getFrameH(), 1);
bossBulletVector.add(bullet);
bossBulletVector.add(bullet1);
}
//移除消失的boss子弹
for (int i = 0; i< bossBulletVector.size(); i++) {
if(bossBulletVector.elementAt(i).isDead()) {
bossBulletVector.remove(i);
}
}
//绘制boss子弹
for (int i = 0; i< bossBulletVector.size(); i++) {
bossBulletVector.elementAt(i).draw(canvas, paint);
plane.isCollision(bossBulletVector.elementAt(i));
}
plane.isCollision(bossplane);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (canvas != null){
surfaceHolder.unlockCanvasAndPost(canvas);//解锁画布,显示到屏幕上
}
}
}
}
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
public class Bullet {
private Bitmap bitmap;
private int x, y;
private int speed = 10;
private boolean isDead;
private int type;
public Bullet(Bitmap bitmap, intx, int y, int type) {
this.bitmap = bitmap;
this.x = x;
this.y = y;
this.type = type;
}
public void draw(Canvas canvas,Paint paint) {
canvas.drawBitmap(bitmap, x,y, paint);
logic();
}
public void logic(){
switch (type){
case 0:
//玩家子弹
y-=speed;
if (y<0){
isDead = true;
}
break;
case 1:
//boss子弹
y+=speed;
if (y<0){
isDead = true;
}
break;
default:
break;
}
}
public boolean isDead(){
return isDead;
}
public Bitmap getBitmap() {
return bitmap;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setDead(booleandead) {
isDead = dead;
}
}如何判断碰撞
1、 子弹与飞机的碰撞
主要类MySurfaceView,这个类主要是要先实现继承和接口,然后碰撞主要的内容就是判断,当飞机的坐标和子弹的坐标重合的时候,玩家飞机的血量就会减少,来体现玩家飞机已经碰撞到了子弹,这需要好几个判断,首先Boss子弹和玩家飞机碰撞时的判断,当子弹的右边碰上飞机的左边并子弹的左边小于飞机的左边,子弹的右边小于飞机的右边,当子弹的右边大于飞机的左边并子弹的左边小于飞机的右边这个时候就是飞机与子弹的碰撞。
飞机与子弹的碰撞代码如下:
publicbooleanisCollision(Bullet bullet) {
if (noCollision) {
returnfalse;
}
if (bullet.getX() > x && bullet.getX() < x + width &&bullet.getY() > y && bullet.getY() < y + height) {
noCollision = true;
if (Hp > 0) {
Hp--;
}
returntrue;
}
returnfalse;
}
2、 飞机与飞机的碰撞
道理与子弹与飞机的碰撞相同
publicbooleanAttack(Bossplane bossplane) {
if (noCollision) {
returnfalse;
}
if(y
if (x < bossplane.getX() && x+width > bossplane.getX()) {//我方战机左边碰撞
noCollision = true;
if (Hp > 0) {
Hp--;
}
returntrue;
}
if (x > bossplane.getX() && x+width < bossplane.getX() +bossplane.getFrameW()) {//我方飞机中间碰撞
noCollision = true;
if (Hp > 0) {
Hp--;
}
returntrue;
}
if (x < bossplane.getX() && x+width >bossplane.getX()+bossplane.getFrameW()) {//我方飞机右边碰撞
noCollision = true;
if (Hp > 0) {
Hp--;
}
returntrue;
}
}
returnfalse;
如何绘制爆炸效果
爆炸效果的绘制与子弹的绘制差不多,都要用到Vector数组
Vector boom = new Vector<>();
只不过在绘制时,爆炸图片需要进行裁剪,再每次往左偏移七分之一的爆炸图片的宽度(我的爆炸图片是七帧的)
每次移动一帧,就给一个计数器++,当这个计数器++到7时,数组就移除该对象;
代码如下:
importandroid.graphics.Bitmap;
importandroid.graphics.Canvas;
importandroid.graphics.Paint;
public class Boom {
private Bitmap bitmap;
private int x,y;
private int totalFrame;
private int frameH,frameW;
private int currentFrame;
private boolean isEnd;
public Boom(Bitmap bitmap, int x, int y,int totalFrame){
this.bitmap = bitmap;
this.x = x;
this.y = y;
this.totalFrame = totalFrame;
frameW = bitmap.getWidth()/totalFrame;
frameH = bitmap.getHeight();
}
public void draw(Canvas canvas, Paintpaint){
canvas.save();
canvas.clipRect(x,y,x+frameW,y+frameH);
canvas.drawBitmap(bitmap,x-currentFrame*frameW,y,paint);
canvas.restore();
logic();
}
public void logic(){
if (currentFrame
currentFrame++;
}else {
isEnd = true;
}
}
public boolean isEnd() {
return isEnd;
}
}当isEnd为true时,删除这一Boom的对象
如何添加音效
建造一个GameSoundPool类
定义一个SoundPool对象
定义int对象 s
在构造方法中给s赋予初始值,音乐文件
用soundPool.play播放
只需再该音乐对应的事件发生时,用GameSoundPool的对象调用即可
代码如下:
publicclass GameSoundPool {
private SoundPool soundPool;
privateint s1,s2,s3,s4,s5,s6;
publicGameSoundPool(Context context){
this.soundPool = new SoundPool(2, AudioManager.STREAM_MUSIC,0);
s1 =soundPool.load(context,R.raw.shoot,1);
s2 =soundPool.load(context,R.raw.button,2);
// s3 =soundPool.load(context,R.raw.bgm_zhandou2,0);
s4 =soundPool.load(context,R.raw.explosion,4);
s5 =soundPool.load(context,R.raw.explosion2,3);
s6 =soundPool.load(context,R.raw.bigexplosion,5);
}
publicvoidplaySound(int type){
switch (type){
case1:
soundPool.play(s1,1,1,1,0,1);
break;
case2:
soundPool.play(s2,1,1,1,0,1);
break;
case3:
// soundPool.play(s3,1,1,1,-1,1);
break;
case4:
soundPool.play(s4,1,1,1,0,1);
break;
case5:
soundPool.play(s5,1,1,1,0,1);
break;
case6:
soundPool.play(s6,1,1,1,0,1);
}
}
}
BGM
BGM一般比较大
需要用mediaPlayer播放
代码如下:
private MediaPlayermediaPlayer;
mediaPlayer=MediaPlayer.create(getContext(),R.raw.meum);
mediaPlayer.setVolume(1,1);
mediaPlayer.setLooping(true);
mediaPlayer.start();
· 1
· 2
· 3
· 4
· 5
哪些地方用到封装,继承,多态,方法重载,接口等
继承
这个项目除了MySurfaceView类之外没有用到继承
public class MySurfaceView extends SurfaceView
接口
在MysufaceView中实现了两个接口
SurfaceHolder.Callback,与Runnable
public class MySurfaceView extends SurfaceView implementsSurfaceHolder.Callback, Runnable
我的感悟与心得
因为对AndroidStudio的不熟悉再加上AndroidStudio全是英文和对自己的要求不够严谨导致后两周的效果不及前两周的效果好。这次实训让我认识到,知识在于积累,平时不努力,临时是要付出代价的,以后应认真学习知识,不能临时抱佛脚。