Android之传感器小游戏

滚球小游戏–加速传感器的小应用

在这里插入图片描述
一个通过传感器的简单小游戏(明白逻辑就好)
关于如何使用传感器的代码我就不细说了 上篇博文已经谈过 套路都是一样的
SensorManager创建对象引用->获取对象->获取传感器
->给传感器弄监听->注册监听(onResume)->取消监听(onPause)
简记 Sensor七步骤

为了学习的方便我还是添上Activity的代码(可以看看这种写法)

Activity代码

public class SensorBallActivity extends Activity {
	
	//SensorManager对象引用
	SensorManager mySensorManager;	
	Sensor myGravity; 	//传感器类型
	static int bx=100;//用于程序界面不在最前时记录小球位置X坐标 以便恢复执行
	static int by=80;//用于程序界面不在最前时记录小球位置Y坐标 以便恢复执行
	
	MyGameView msv;
	//开发实现了SensorEventListener接口的传感器监听器
	private SensorEventListener mySensorListener = new SensorEventListener(){
		@Override
		public void onAccuracyChanged(Sensor sensor, int accuracy) {}
		@Override
		public void onSensorChanged(SensorEvent event) {			
			//计算出重力在屏幕上的投影方向
			float[] values=event.values;
			//从加速度传感器XY轴读数折算小球运动速度X分量
			msv.dx=-(int)(values[0]*msv.dLength);
			//从加速度传感器XY轴读数折算小球运动速度X分量
			msv.dy=(int)(values[1]*msv.dLength);
		}		  
	};  
	  
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        //全屏
		requestWindowFeature(Window.FEATURE_NO_TITLE); 
		getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN ,  
		              WindowManager.LayoutParams.FLAG_FULLSCREEN);		
		//设置为竖屏模式
		setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
		//=========================sensor begin============================
		//获得SensorManager对象
        mySensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);	        
        myGravity=mySensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);//TYPE_ORIENTATION
        msv = new MyGameView(this);
        this.setContentView(msv);       
        
    }
    
	@Override
	protected void onResume() {						//重写onResume方法
		mySensorManager.registerListener(			//注册监听器
			mySensorListener, 					//监听器对象
			myGravity,							//传感器类型
			SensorManager.SENSOR_DELAY_UI		//传感器事件传递的频度
		);
		msv.gvdt.pauseFlag=false;
		//恢复小球位置
		msv.ballX=bx;
		msv.ballY=by;
		super.onResume();
	}
	
	@Override
	protected void onPause() {									//重写onPause方法
		mySensorManager.unregisterListener(mySensorListener);	//取消注册监听器
		msv.gvdt.pauseFlag=true;
		//记录小球位置
		bx=msv.ballX;
		by=msv.ballY;
		super.onPause();
	}
}

一丶SurfaceView的实现

SurfaceView简介
surfaceView是视图(View)的继承类,这个视图里内嵌了一个专门用于绘制的Surface,可以控制这个Surface的格式和尺寸。Surfaceview控制这个Surface的绘制位置。

1.SurfaceView分析 surface是纵深排序(Z-ordered)的,这表明它总在自己所在窗口的后面。
2.surfaceview提供了一个可见区域,只有在这个可见区域内 surface 部分内容才可见,可见区域外的部分不可见。 surface的排版显示受到视图层级关系的影响,它的兄弟视图结点会在顶端显示。这意味者
3.surface的内容会被它的兄弟视图遮挡,这一特性可以用来放置遮盖物(overlays)(例如,文本和按钮等控件)。
4. 注意,如果surface 上面有透明控件,那么它的每次变化都会引起框架重新计算它和顶层控件之间的透明效果,这会影响性能。
5.SurfaceView类的目的在于提供一个可以使用绘图线程渲染到屏幕的surface。
6.所有的SurfaceView和surfaceHolder.Callback方法都应该在主线程调用,所以对于绘图线程使用的变量应该做同步处理。
从androidN开始,SurfaceView窗口的位置与其他View同步更新,意味着对屏幕上的SurfaceView做转换和缩放时,不会再出现重影。
7. 一定要保证绘图线程仅在surface可用时对其访问,即在SurfaceHolder.Callback.surfaceCreated()与SurfaceHolder.Callback.surfaceDestroyed()之间。
作者:昵称真难选 链接:https://www.jianshu.com/p/312f32885f31 來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
在这里插入图片描述
在这里插入图片描述

总结一下如果在线程画图肯定少不了Holder 其作用是操控SurfaceView的
步骤:1.SurfaceView子类下实现SurfaceHolder这个接口并且调用Callback( )方法(获取对象)
->2.在surfaceCreated(SurfaceHolder holder)方法下 使用Holder获取画布
->3.在线程中上锁synchronized(Holder)和解锁
再总结 需要Holder的地方有 实现接口,线程上锁和解锁,获取画布

主要谈画面的绘制
在这里插入图片描述
1.要想能够画出界面来 类必须继承SurfaceView
并且实现SurfaceHolder.Callback 生命周期回调接口

在这里插入图片描述
1.十分重要 :在surface子类的构造器中需要传入一个参数 即Activity
2.由于前面实现了生命周期回调接口 所以在构造器中必须要实现该接口
3.加载图片 然后获取图片的高度 然后创建画笔 打开抗锯齿
4.创建两个线程 一个小球运动的线程 一个画面的线程

在这里插入图片描述
下面开始画画了自定义的方法 onMyDraw(Canvas canvas) 调用此方法 传入参数Canvas(画布)
1.铺砖 canvas.drawBitmap(tableBM, tableBM.getWidth()*i, tableBM.getHeight()*j, paint);
四个参数->( 图片 坐标x 坐标y 画笔)

在这里插入图片描述
在这里插入图片描述
1.使用三个surface的方法 分别对应 改变,创建,销毁
当创建的时候就调用其创建的方法 (传入了SurfaceHolder对象)
并用该对象获取画布
2.给SurfaceHolder对象加锁synchronized(holder) 保证安全
3.if(canvas != null){
holder.unlockCanvasAndPost(canvas);
} 然后当画布创建完的时候 解锁
4.启动两个线程

public class MyGameView extends SurfaceView 
implements SurfaceHolder.Callback  //实现生命周期回调接口
{
	SensorBallActivity activity;
	BallGoThread bgt;
	GameViewDrawThread gvdt;
	Paint paint;//画笔
	Bitmap tableBM;
	Bitmap ballBM;
	
	int ballSize;//球的尺寸
	float dLength=2.5f;//小球的运动速度系数
	int ballX=100;//当前小球的X坐标
	int ballY=80;//当前小球的Y坐标
	int dx=0;//当前小球的运动速度X分量
	int dy=0;//当前小球的运动速度Y分量
	
	public MyGameView(SensorBallActivity activity) {
		super(activity);
		this.activity = activity;
		this.getHolder().addCallback(this);//设置生命周期回调接口的实现者
		
		//加载图片
		tableBM=BitmapFactory.decodeResource(activity.getResources(), R.drawable.table);
		ballBM=BitmapFactory.decodeResource(activity.getResources(), R.drawable.ball);
		ballSize=ballBM.getHeight();
		paint = new Paint();//创建画笔
		paint.setAntiAlias(true);//打开抗锯齿
		
		bgt=new BallGoThread(this);
		gvdt=new GameViewDrawThread(this);
	}

	public void onMyDraw(Canvas canvas)
	{
		//贴底纹3x3
		for(int i=0;i<9;i++)
		{
			for(int j=0;j<9;j++)
			{
				canvas.drawBitmap(tableBM, tableBM.getWidth()*i, tableBM.getHeight()*j, paint);	
			}
		}
		//贴球
		canvas.drawBitmap(ballBM, ballX, ballY, paint);
	}

	public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
		
	}

	public void surfaceCreated(SurfaceHolder holder) {//创建时被调用
		Canvas canvas = holder.lockCanvas();//获取画布
		try{
			synchronized(holder){
				onMyDraw(canvas);//绘制
			}			
		}
		catch(Exception e){
			e.printStackTrace();
		}
		finally{
			if(canvas != null){
				holder.unlockCanvasAndPost(canvas);
			}
		}
		//启动球定时根据重力移动的线程
		bgt.start();
		//启动定时重新绘制画面的线程
		gvdt.start();
	}

	public void surfaceDestroyed(SurfaceHolder arg0) {//销毁时被调用
         
	}
}

=====================================================================
下面一起来看两个线程的具体实现

二丶小球运动线程Thread的实现

具体来说
主要是获取了surface的子类
然后无限循环启用线程 设定每30ms线程sleep一次 为了节约系统资源同时也可以给用户很好的体验。
获取的数据主要由surface子类提供 而surface子类的数据则是Activity中的传感器来获取的

//球定时根据重力移动的线程
public class BallGoThread extends Thread
{
	MyGameView mgv;
	boolean flag=true;
	
	public BallGoThread(MyGameView mgv)
	{
		this.mgv=mgv;
	}
	
	public void run()
	{
		while(flag)
		{
			//计算球的新位置
			int dx=mgv.dx;
			int dy=mgv.dy;			
			mgv.ballX=mgv.ballX+dx;
			mgv.ballY=mgv.ballY+dy;
			
			//判断X方向是否碰壁,若碰壁则恢复
			if(mgv.ballX<0||mgv.ballX>mgv.getWidth()-mgv.ballSize)
			{
				mgv.ballX=mgv.ballX-dx;
			}
			//判断Y方向是否碰壁,若碰壁则恢复
			if(mgv.ballY<0||mgv.ballY>mgv.getHeight()-mgv.ballSize)
			{
				mgv.ballY=mgv.ballY-dy;
			}
			
			try
			{
				Thread.sleep(30);
			}
			catch(Exception e)
			{
				e.printStackTrace();
			}
		}
	}
}

三丶绘制画面的线程

1.首先看全局变量
有两个标志位(用于run中的判断 )
声明SurfaceView的子类
得到surfaceHolder的引用来操作SurfaceView的子类
2.构造方法不多说
在这里插入图片描述
3.线程我就不多说了
步骤前面提过 获取画布->上锁(绘制)->解锁

//定时重新绘制画面的线程
public class GameViewDrawThread extends Thread{
	boolean flag = true;
	boolean pauseFlag=false;
	int sleepSpan = 12;
	MyGameView gameView;
	SurfaceHolder surfaceHolder;

	public GameViewDrawThread(MyGameView gameView){
		this.gameView = gameView;
		this.surfaceHolder = gameView.getHolder();
	}
	public void run(){
		Canvas c;
        while (this.flag) {
            c = null;
            if(!pauseFlag)
            {
            	try {
	            	// 锁定整个画布,在内存要求比较高的情况下,建议参数不要为null
	                c = this.surfaceHolder.lockCanvas(null);
	                synchronized (this.surfaceHolder) {
	                	gameView.onMyDraw(c);//绘制
	                }
	            } finally {
	                if (c != null) {
	                	//并释放锁
	                    this.surfaceHolder.unlockCanvasAndPost(c);
	                }
	            }
            }
	            
            try{
            	Thread.sleep(sleepSpan);//睡眠指定毫秒数
            }
            catch(Exception e){
            	e.printStackTrace();//打印堆栈信息
            }
        }
	}
}

猜你喜欢

转载自blog.csdn.net/qq_41910103/article/details/82901517
今日推荐