飞机大战-StartView 开始界面的实现

本文介绍了飞机大战游戏的开始界面实现,重点讲解了使用剪切图动画的方法,通过改变图片位置产生动画效果。讨论了在Android应用中,当从Home键退出再进入游戏时可能出现的黑屏问题,原因是surface相关方法的执行顺序导致的异常。建议将线程开启操作放在适当位置,并在提交画布时进行异常判断,同时强调在获取屏幕尺寸时需确保在正确的方法中进行。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


                                                                                                      

这是项目的开始界面,由于没有背景图片,所以....

我们用到了剪切图动画,他的原理是先定义一块矩形区域,然后不断改变图片的位置来实现的。

注意:刚进入时执行顺序是:StartView构造方法——>surfaceCreated——>surfaceChanged——>surfaceDestroyed,楼主曾把线程的开启操作t = new Thread(this)放到

了构造方法中,运行项目的时候按Home键退出后再进入游戏会导致黑屏,为什么会这样呢?

这是因为按Home键退出后再进入游戏方法的执行顺序是:surfaceCreated——>surfaceChanged——>surfaceDestroyed,他不会运行构造方法,,而产生一个异常,小伙伴

们注意啰。

最后要注意的是提交画布的时候也可能产生异常,所以我们要if (canvas != null)进行判断后再进行提交操作,还有screen_width = this.getWidth()和screen_height =

 this.getHeight()这两行代码不要写在构造方法中,因为只有进入到surfaceCreated方法中才能得到屏幕的宽和高,大家养成良好的代码编写习惯哦。


package com.example.qgns;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.view.MotionEvent;
import android.view.SurfaceHolder;

public class StartView extends BasicView {
	private Bitmap logo;//logo图片
	private Bitmap fly;//动画图片
	private Bitmap button;//按钮图片

	private float logo_x;  //他们的x,y坐标,大家随便设置
	private float logo_y;
	private float fly_x;
	private float fly_y;
	private float button_x;
	private float button_y;

	private MainActivity mainActivity;
	private SoundPlay sound;//音效

	private String name = "作者:情过南山";//绘制的文字

	public StartView(Context context) {
		super(context);
		this.mainActivity = (MainActivity) context;
		sound=new SoundPlay(mainActivity);
		sound.initSound();//初始化音效
	}

	@SuppressLint("ClickableViewAccessibility")
	@Override
	public boolean onTouchEvent(MotionEvent event) {//点击按钮进入到游戏主界面
		if (event.getAction() == MotionEvent.ACTION_DOWN
				&& event.getPointerCount() == 1) {
			float x = event.getX();
			float y = event.getY();
			if (x > button_x && x < button_x + button.getWidth()//按钮的一个矩形区域
					&& y > button_y && y < button_y + button.getHeight()) {
				sound.play(1, 0);
				mainActivity.toMyView();
				
			}
		}
		return false;
	}

	@Override
	public void initBitmap() {//图片的初始化操作
		logo = BitmapFactory.decodeResource(getResources(),
				R.drawable.shoot_copyright);
		fly = BitmapFactory.decodeResource(getResources(), R.drawable.fly);
		button = BitmapFactory.decodeResource(getResources(),
				R.drawable.game_start);

		logo_x = screen_width / 2 - logo.getWidth() / 2;
		logo_y = screen_height / 5;
		fly_x = screen_width / 2 - fly.getWidth() / 2;
		fly_y = logo_y + logo.getHeight() + fly.getWidth() / 3;
		button_x = screen_width / 2 - button.getWidth() / 2;
		button_y = screen_height - 3 * button.getHeight();

	}

	@Override
	public void myDraw() {
		try {
			canvas = sur.lockCanvas();//锁定画布
			if (canvas != null) {
				canvas.drawColor(Color.WHITE);

				canvas.drawBitmap(logo, logo_x, logo_y, paint);//绘制logo
				canvas.drawBitmap(button, button_x, button_y, paint);//绘制按钮

				canvas.save();//剪切图动画
				canvas.clipRect(fly_x, fly_y, fly_x + fly.getWidth(), fly_y
						+ fly.getHeight() / 3);
				canvas.drawBitmap(fly, fly_x,
						fly_y - currentFrome * fly.getHeight() / 3, paint);
				currentFrome++;//不断的改变帧
				if (currentFrome >= 3) {
					currentFrome = 0;
				}
				canvas.restore();

				paint.setTextSize(30);//画笔大小
				paint.setAntiAlias(true);//抗锯齿
				float textLong = paint.measureText(name, 0, name.length());//标签长度
				canvas.drawText(name, screen_width / 2 - textLong / 2, 60,//绘制标签
						paint);
			}
		} catch (Exception e) {
		} finally {
			if (canvas != null) {
				sur.unlockCanvasAndPost(canvas);//解锁画布
			}
		}
	}

	@Override
	public void run() {
		while (threadFlag) {//通过threadFlag标志位的改变控制线程的开始与暂停
			long startTime = System.currentTimeMillis();//得到当前时间
			myDraw();
			long endTime = System.currentTimeMillis();
			try {
				if ((endTime - startTime) < 500) {
					Thread.sleep(500 - (endTime - startTime));//保证每次的刷新时间相同
				}
			} catch (Exception e) {
			}
		}
	}

	@Override
	public void surfaceCreated(SurfaceHolder holder) {
		screen_width = this.getWidth();//屏宽
		screen_height = this.getHeight();//屏高
		initBitmap();

		if (!threadFlag) {
			threadFlag = true;//改变标志位
			t = new Thread(this);//开启线程
			t.start();//开始线程的绘制
		}
	}

	@Override
	public void surfaceChanged(SurfaceHolder holder, int format, int width,
			int height) {
	}

	@Override
	public void surfaceDestroyed(SurfaceHolder holder) {
		release();
		threadFlag = false;//视图销毁改变标志位

	}

	@Override
	public void release() {//垃圾回收操作
		if (!logo.isRecycled()) {
			logo.recycle();
		}
		if (!fly.isRecycled()) {
			fly.recycle();
		}
		if (!button.isRecycled()) {
			button.recycle();
		}
	}

}


package cn.feike.shoot; import java.awt.Graphics; import java.awt.image.BufferedImage; public abstract class FlyingObject { protected double x;//物体的x坐标 protected double y;//物体的y坐标 protected double width;//物体的宽 protected double heigth;//物体的高 protected BufferedImage image;//当前正在显示的图片 protected int index = 0;//图片数组下标序号,子类中使用 protected double step;//飞行物每次(1/24秒)移动的距离 protected int life;//命 protected int state;//飞行物的状态 public static final int ACTIVE=0;//活着状态 public static final int DEAD=1;//死亡状态 public static final int REMOVE=2;//回收状态 //默认构造器 public FlyingObject() { life = 1; state = ACTIVE; } //有参构造器 public FlyingObject(double width,double heigth){ this();//调用无参数的构造器,必须写在第一行. this.x = (int)(Math.random()*(480-width)); this.y = -heigth; this.width = width; this.heigth = heigth; step = Math.random()*3+0.8;//初始化step为[0.8,3.8)之间的数 } //重写toString方法 public String toString() { return x+","+y+","+width+","+heigth+","+image; } //重写paint,方便子类对象的使用 public void paint(Graphics g) { g.drawImage(image, (int)x, (int)y, null);//绘制图片 } //飞行物移动的move方法 /** * 重构了move,方法实现播放销毁动画功能 */ public void move(){ if(state == ACTIVE){ y += step; return ; } if(state == DEAD){ //从子类对象中获取下一张照片 BufferedImage img = nextImage(); if(img == null){ state = REMOVE;//没有照片则回收 }else{ image = img;//否则把子类的图片传给image } //越界则销毁 if(y>=825){ state = REMOVE; } } } /** * 子类中必须有的方法,返回下一个要播放的照片引用, * 如果返回null表示没有可播放的照片了. */ protected abstract BufferedImage nextImage(); /** * 飞行物被打了一下 */ public void hit(){ if(life>0){ life--; } if(life==0){ state = DEAD; } } /** * 碰撞检测的方法 * 检测物体的位置是否在碰撞的范围内. * (子弹是否在飞行物的碰撞范围内) */ public boolean duang(FlyingObject obj){ //this(x,y,w,h) //obj(x,y,w,h) double x1 = this.x - obj.width; double x2 = this.x + this.width; double y1 = this.y - obj.width; double y2 = this.y + this.heigth; return x1<obj.x&&obj;.x<x2&&y1;<obj.y&&obj;.y<y2; } /** 重构FlyingObject,添加了状态检查方法 */ /** 检查飞行物死了吗 */ public boolean isDead(){ return state == DEAD; } /** 检查飞行物是否活动的 */ public boolean isActive(){ return state == ACTIVE; } /** 检查飞行是否可以被删除*/ public boolean canRemove(){ return state == REMOVE; } /** 飞行物添加"去死"方法*/ public void goDead(){ if(isActive()){ state = DEAD; } } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值