李华明Himi 原创,转载务必在明显处注明:
,css
此章节为正在创做的游戏开发书籍中的一部分,因为写书的缘故好久没有更新了,挺对不起你们的;那么今天放出书中的一部分,让你们先睹为快吧;html
在Android系统的手机,有的根本没有实体的上下左右导航按键,因此不少游戏都会有利用Android手机都具备触屏的特性,制做360度摇杆来取代游戏方向键,这样不只能使界面UI变得很美观,并且更加的方便操做;java
下面先来看效果吧:android
下面开始实现:canvas
首先,确定是绘制两个圆形,不容置疑;圆心点重合,为了区分 ,因此设置了不一样颜色;ide
灰色:固定不动的摇杆背景(也意味着摇杆的活动范围);函数
红色:摇杆;this
而后考虑:红色摇杆确定跟随手指触屏的位置而移动,那么这个很easy啦,只要在触屏事件中处理,将获取的触屏XY坐标赋值与摇杆XY坐标便可;这个没问题;可是紧接着在思考一个问题:spa
通常状况下,咱们不可能但愿摇杆一直跟随手指位置,因此须要一个摇杆的活动区域,也就如同上图中的灰色区域,在灰色区域内摇杆能够随着用户的触屏位置移动,可是一旦用户触屏位置在活动区域以外,摇杆就不该该跑出灰色区域;因此具体实现步骤以下:.net
1) 获得经过摇杆的坐标与触屏点的坐标获得所造成的角度Angle
2) 根据Angle,以及已知所在圆的半径,算出摇杆所在灰色圆形上作圆周运动的当前X,Y坐标;
首先第一步: 算出摇杆坐标与触屏坐标造成的角度
咱们确定已知摇杆当前坐标,而且当用户触屏时的坐标也能够在触屏按键中获得,那么获取的方法就能够写成一个方法,方法以下:
/*** * 获得两点之间的弧度 */ public double getRad(float px1, float py1, float px2, float py2) { //获得两点X的距离 float x = px2 - px1; //获得两点Y的距离 float y = py1 - py2; //算出斜边长 float xie = (float) Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); //获得这个角度的余弦值(经过三角函数中的定理 :邻边/斜边=角度余弦值) float cosAngle = x / xie; //经过反余弦定理获取到其角度的弧度 float rad = (float) Math.acos(cosAngle); //注意:当触屏的位置Y坐标<摇杆的Y坐标咱们要取反值-0~-180 if (py2 < py1) { rad = -rad; } return rad; }
在Java中 Math类中的反余弦函数返回的不是角度是弧度,这一点要格外注意;
另一点就是,由于三角函数角度范围是0~180度,因此反之应该是-0~-180度;
经过此函数获取到摇杆与用户触屏位置所造成的角度以后,咱们就能够经过圆周公式来获得其摇杆的XY坐标了;方法以下:
/** * * @param R * 圆周运动的旋转点 * @param centerX * 旋转点X * @param centerY * 旋转点Y * @param rad * 旋转的弧度 */ public void getXY(float centerX, float centerY, float R, double rad) { //获取圆周运动的X坐标 SmallRockerCircleX = (float) (R * Math.cos(rad)) + centerX; //获取圆周运动的Y坐标 SmallRockerCircleY = (float) (R * Math.sin(rad)) + centerY; }
圆周运动公式:经过三角函数定理得出:
X坐标:所在圆的半径*角度的余弦值
Y坐标:所在圆形半径*角度的正弦值
圆周的大小,由所在圆的半径R的大小来决定;
经过以上的公式咱们就可让摇杆在灰色圆形上作圆周运动,固然除此以外咱们还要注意三点:
1:作圆周运动的大小,应该跟灰色区域的半径相同;
2:触屏事件中应该首先断定用户触屏的位置是否在灰色区域中,若是不在,咱们就应该获取摇杆与触屏点的角度而后获取摇杆应该在圆周运动上的XY坐标;若是在,就没有处理了,只要将摇杆位置随着用户点击位置就行了;
3:在触屏事件中,当用户手指离开屏幕后,应该让摇杆的位置恢复到初始的位置状态;
下面是整个项目的MySurfaceView中所有代码:
package com.rp; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.Log; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.SurfaceHolder.Callback; public class MySurfaceView extends SurfaceView implements Callback, Runnable { private Thread th; private SurfaceHolder sfh; private Canvas canvas; private Paint paint; private boolean flag; //固定摇杆背景圆形的X,Y坐标以及半径 private int RockerCircleX = 100; private int RockerCircleY = 100; private int RockerCircleR = 50; //摇杆的X,Y坐标以及摇杆的半径 private float SmallRockerCircleX = 100; private float SmallRockerCircleY = 100; private float SmallRockerCircleR = 20; public MySurfaceView(Context context) { super(context); Log.v("Himi", "MySurfaceView"); this.setKeepScreenOn(true); sfh = this.getHolder(); sfh.addCallback(this); paint = new Paint(); paint.setAntiAlias(true); setFocusable(true); setFocusableInTouchMode(true); } public void surfaceCreated(SurfaceHolder holder) { th = new Thread(this); flag = true; th.start(); } /*** * 获得两点之间的弧度 */ public double getRad(float px1, float py1, float px2, float py2) { //获得两点X的距离 float x = px2 - px1; //获得两点Y的距离 float y = py1 - py2; //算出斜边长 float xie = (float) Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); //获得这个角度的余弦值(经过三角函数中的定理 :邻边/斜边=角度余弦值) float cosAngle = x / xie; //经过反余弦定理获取到其角度的弧度 float rad = (float) Math.acos(cosAngle); //注意:当触屏的位置Y坐标<摇杆的Y坐标咱们要取反值-0~-180 if (py2 < py1) { rad = -rad; } return rad; } @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN || event.getAction() == MotionEvent.ACTION_MOVE) { // 当触屏区域不在活动范围内 if (Math.sqrt(Math.pow((RockerCircleX - (int) event.getX()), 2) + Math.pow((RockerCircleY - (int) event.getY()), 2)) >= RockerCircleR) { //获得摇杆与触屏点所造成的角度 double tempRad = getRad(RockerCircleX, RockerCircleY, event.getX(), event.getY()); //保证内部小圆运动的长度限制 getXY(RockerCircleX, RockerCircleY, RockerCircleR, tempRad); } else {//若是小球中心点小于活动区域则随着用户触屏点移动便可 SmallRockerCircleX = (int) event.getX(); SmallRockerCircleY = (int) event.getY(); } } else if (event.getAction() == MotionEvent.ACTION_UP) { //当释放按键时摇杆要恢复摇杆的位置为初始位置 SmallRockerCircleX = 100; SmallRockerCircleY = 100; } return true; } /** * * @param R * 圆周运动的旋转点 * @param centerX * 旋转点X * @param centerY * 旋转点Y * @param rad * 旋转的弧度 */ public void getXY(float centerX, float centerY, float R, double rad) { //获取圆周运动的X坐标 SmallRockerCircleX = (float) (R * Math.cos(rad)) + centerX; //获取圆周运动的Y坐标 SmallRockerCircleY = (float) (R * Math.sin(rad)) + centerY; } public void draw() { try { canvas = sfh.lockCanvas(); canvas.drawColor(Color.WHITE); //设置透明度 paint.setColor(0x70000000); //绘制摇杆背景 canvas.drawCircle(RockerCircleX, RockerCircleY, RockerCircleR, paint); paint.setColor(0x70ff0000); //绘制摇杆 canvas.drawCircle(SmallRockerCircleX, SmallRockerCircleY, SmallRockerCircleR, paint); } catch (Exception e) { // TODO: handle exception } finally { try { if (canvas != null) sfh.unlockCanvasAndPost(canvas); } catch (Exception e2) { } } } public void run() { // TODO Auto-generated method stub while (flag) { draw(); try { Thread.sleep(50); } catch (Exception ex) { } } } public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { Log.v("Himi", "surfaceChanged"); } public void surfaceDestroyed(SurfaceHolder holder) { flag = false; Log.v("Himi", "surfaceDestroyed"); } }
若是你们想美化摇杆,那就让大家的美工给出两张圆形图吧;还在等什么,马上为你的游戏加上摇杆吧~娃哈哈
好啦,其实这里只是给你们介绍思路,具体的书中,我已经将此封装成了一个摇杆类,这样更加的OOP,至于如何封装,你们能够根据须要自由来设计;今天的博文写的可能不是很清晰,由于我如今脑子昏昏沉沉的 - - 。
从写书开始到如今,天天都3-4点睡觉,唉、不过值得高兴的是,若是没有特殊状况,6月底书籍就要交稿了,你们期待下吧;我也会继续努力这最后两个月的;
项目下载地址: http://www.himigame.com/android-game/384.html
还对了,之前上传的项目资源分都将资源调整成0分了,所有设置为免费下载,方便你们;