Android游戏开发教程—-游戏控制

游戏控制是我们的游戏技术基础的最后一部分,在Android 游戏中我们可以通过按键,触摸屏还有传感器来控制游戏。

按键控制

按键并非收有安卓手机都拥有,实际上Android平板和新版本的Android操作系统中,我们能发现实体按键正有逐渐消失的趋势。

按键事件包括onKeyUp和onKeyDown等事件,下面通过一个按键事件的使用案例来了解按键控制技术。

 

[java][/java] view plaincopy

  1. public class KeyInputActivity extends Activity {
  2.     @Override
  3.     protected void onCreate(Bundle savedInstanceState) {
  4.         super.onCreate(savedInstanceState);
  5.         setContentView(new TextView(this));
  6.     }
  7. @Override
  8.  public boolean onKeyDown(int keyCode, KeyEvent event) {
  9.     switch(keyCode){
  10.     case KeyEvent.KEYCODE_DPAD_CENTER:
  11.         Log.v(“onKeyDown”, “按下:中键”);
  12.         break;
  13.     case KeyEvent.KEYCODE_DPAD_UP:
  14.          Log.v(“onKeyDown”, “按下:上方向键”);
  15.         break;
  16.     case KeyEvent.KEYCODE_DPAD_DOWN:
  17.         Log.v(“onKeyDown”, “按下:下方向键”);
  18.         break;
  19.     case KeyEvent.KEYCODE_DPAD_LEFT:
  20.          Log.v(“onKeyDown”, “按下:左方向键”);
  21.         break;
  22.     case KeyEvent.KEYCODE_DPAD_RIGHT:
  23.         Log.v(“onKeyDown”, “按下:右方向键”);
  24.         break;
  25.  }
  26.     return super.onKeyDown(keyCode, event);
  27. }
  28. @Override
  29.  public boolean onKeyUp(int keyCode, KeyEvent event) {
  30.        switch(keyCode){
  31.         case KeyEvent.KEYCODE_DPAD_CENTER:
  32.             Log.v(“onKeyDown”, “释放:中键”);
  33.             break;
  34.         case KeyEvent.KEYCODE_DPAD_UP:
  35.              Log.v(“onKeyDown”, “释放:上方向键”);
  36.             break;
  37.         case KeyEvent.KEYCODE_DPAD_DOWN:
  38.             Log.v(“onKeyDown”, “释放:下方向键”);
  39.             break;
  40.         case KeyEvent.KEYCODE_DPAD_LEFT:
  41.              Log.v(“onKeyDown”, “释放:左方向键”);
  42.             break;
  43.         case KeyEvent.KEYCODE_DPAD_RIGHT:
  44.             Log.v(“onKeyDown”, “释放:右方向键”);
  45.             break;
  46.      }
  47.     return super.onKeyUp(keyCode, event);
  48. }
  49. }

 

 

说明:

keyCode为键值,手机中每一个按钮都拥有一个完全独立的键值,通过按键键值就可以确定当前按下的是哪一个按键。

KeyEvent为按键事件,该对象中保存着当前按键的所有信息。比如:按键发生的时间,按键发生的次数,按键发生的类型等等。

 

触摸控制

在Android平台中,触摸控制是基础,这也是移动平台优势所在,因为触摸能提供直观的更加人性化的操作。触摸就分为单点触摸控制与多点触摸控制,下面通过实例代码来分别演示如何使用触摸控制技术。

演示:单点触摸事件

[java][/java] view plaincopy

  1. public class STouchActivity extends Activity {
  2.     @Override
  3.     protected void onCreate(Bundle savedInstanceState) {
  4.         super.onCreate(savedInstanceState);
  5.         setContentView(new TextView(this));
  6.     }
  7. @Override
  8.  public boolean onTouchEvent(MotionEvent event) {
  9.    int action = event.getAction();
  10.    float mPosX = event.getX();//触摸点X坐标
  11.    float mPosY = event.getY();//触摸点y坐标
  12.     switch (action) {
  13.     // 触摸按下的事件
  14.     case MotionEvent.ACTION_DOWN:
  15.         Log.v(“test”, “ACTION_DOWN”);
  16.         break;
  17.     // 触摸移动的事件
  18.     case MotionEvent.ACTION_MOVE:
  19.         Log.v(“test”, “ACTION_MOVE”);
  20.         break;
  21.     // 触摸抬起的事件
  22.     case MotionEvent.ACTION_UP:
  23.         Log.v(“test”, “ACTION_UP”);
  24.         break;
  25.     }
  26.     return true;
  27. }

覆盖Activity的onTouchEvent方法,实现触摸事件处理

 

多点触摸

多点触摸并不是所有手机都支持,有些手机支持很多点触摸,有些手机可能只支持单点触摸。

 

Android2.2版本后对多点触摸做了修改,添加了新的方法和常量,甚至重命名了常量。这些改变可能会让处理多点触摸容易些。不过只支持Android2.2以后的版本,为了支持Android2.0–Android2.21版本,我们使用Android2.0的API。

当处理多点触摸事件时,我们使用重载的方法,它们带有一个所谓的指针索引,如event.getX(pointerIndex);

pointIndex是MotionEvent的内部数组中的一个索引,它包含特定手指触摸屏幕事件的坐标值。而真正识别屏幕上的一根手指是指针ID。指针ID是一个任意数字,可以唯一标识触摸屏幕的一个指针的实例。有一个方法MotionEvent.getPointerIdentifier(int pointerIndex),它返回一个基于指针索引的指针ID。只要手指还触摸在屏幕上,一个指针ID就与一根手指保持相同,而指针索引就不一定这样了。先来看看是如何获取指针索引:

int pointerIndex = (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >> MotionEvent.ACTION_POINTER_ID_SHIFT;

ACTION_POINTER_ID_MASK的常量的值是0xff00,因此低8位为0,高8位为15,用于保存事件的指针索引。整数的低8位可从event.getAction()方法返回得到,用于保存事件类型的值。我们通过MotionEvent.ACTION_POINTER_ID_SHIFT来移位,该值为8,因此实际上是将第15位移到第8位,第7位移到第0位。注意我们是获取pointerIndex而常量却是XXX_POINTER_ID_XXX而不是XXX_POINTER_INDEX_XXX

获取事件类型,我们只需屏蔽指针索引:

int action = event.getAction() & MotionEvent.ACTION_MASK;

这里我们会遇到新的事件类型,

MotionEvent.ACTION_POINTER_DOWN:除了第一根手指外的任何手指触摸屏幕,都将发生该事件,而第一根手指仍然产生MotionEvent.ACTION_DOWN事件。

MotionEvent.ACTION_POINTER_UP:多根手指触摸屏幕而一根手指离开屏幕时,将发生该事件。最后一根手指离开屏幕将产生MotionEvent.ACTION_UP事件,而该手指不一定是第一个触摸屏幕的手指。为了检查单个MotionEvent中包含几个事件,可使用MotionEvent.getPointerCount()方法,它会告诉我们MotionEvent中包含多少根手指的坐标。然后我们可通过MotionEvent.getX()、MotionEvent.getY()和MotionEvent.getPointerId()方法来得到指针ID和从指针索引0到MotionEvent.getPointerCount() – 1的坐标值。

好吧,如果上述理论介绍让你抓狂,不必担心,你只需明白如何应用就行了,请看实例代码.

多点触摸案例:

以下代码支持最多10个点的触摸,其实我们也正好只有10根手指,触摸坐标被保存在数组x和y里面,触摸状态被保存在touched 数组里面。

 

[java][/java] view plaincopy

  1. public class MTouchActivity extends Activity {
  2. float[] x = new float[10];//保存10个触摸点的x坐标
  3. float[] y = new float[10];//保存10个触摸点的y坐标
  4. boolean[] touched = new boolean[10];//保存10个触摸点的按下状态
  5. StringBuilder builder = new StringBuilder();
  6.     @Override
  7.     protected void onCreate(Bundle savedInstanceState) {
  8.         super.onCreate(savedInstanceState);
  9.         setContentView(new TextView(this));
  10.     }
  11. @Override
  12. public boolean onTouchEvent(MotionEvent event) {
  13. int action = event.getAction() & MotionEvent.ACTION_MASK;
  14. int pointerIndex =
  15. (event.getAction() & MotionEvent.ACTION_POINTER_ID_MASK) >>
  16. MotionEvent.ACTION_POINTER_ID_SHIFT;
  17. int pointerId = event.getPointerId(pointerIndex);
  18. switch (action) {
  19. case MotionEvent.ACTION_DOWN:
  20. case MotionEvent.ACTION_POINTER_DOWN://第二根手指按下时才会触发
  21. touched[pointerId] = true;
  22. x[pointerId] = (int)event.getX(pointerIndex);
  23. y[pointerId] = (int)event.getY(pointerIndex);
  24. break;
  25. case MotionEvent.ACTION_UP:
  26. case MotionEvent.ACTION_POINTER_UP://最后一根手指抬起时不会触发
  27. case MotionEvent.ACTION_CANCEL:
  28. touched[pointerId] = false;
  29. x[pointerId] = (int)event.getX(pointerIndex);
  30. y[pointerId] = (int)event.getY(pointerIndex);
  31. break;
  32. case MotionEvent.ACTION_MOVE:
  33. int pointerCount = event.getPointerCount();
  34. for (int i = 0; i < pointerCount; i++) {
  35. pointerIndex = i;
  36. pointerId = event.getPointerId(pointerIndex);
  37. x[pointerId] = (int)event.getX(pointerIndex);
  38. y[pointerId] = (int)event.getY(pointerIndex);
  39. }
  40. break;
  41. }
  42. //生成触摸日志
  43. builder.setLength(0);
  44. for(int i = 0; i < 10; i++) {
  45. builder.append(touched[i]);
  46. builder.append(“, “);
  47. builder.append(x[i]);
  48. builder.append(“, “);
  49. builder.append(y[i]);
  50. builder.append(“\n”);
  51. }
  52. //打印触摸日志
  53.  Log.v(“test”, builder.toString());
  54. return true;
  55. }
  56. }

 

 

传感器控制技术

 

加速器

游戏中一个有趣的输入方法是加速计,所有的Android设备都要求有一个3D加速计。

为了获取加速计信息,我们注册一个侦听器,需要实现的接口名为SensorEventListener,它具有两个方法:

public void onSensorChanged(SensorEvent event);

public void onAccuracyChanged(Sensor sensor, int accuracy);

当一个新的加速计事件发生时会调用第一个方法,而当加速计的精度发生变化时会调用第二个方法。一般我们可以放心的忽略第二个方法。为了实现这个功能,首先我们需要确认设备上是否具有一个加速计。尽管现在所有的Android设备都应该有一个加速计,但也许将来会有变化。我们必须百分之百确保该输入方法是可用的。首先我们需要做的是获得一个SensorManager实例,它将会表明设备是否安装了加速计,以及在哪里注册侦听器。我们可以通过Context接口的一个方法来获取SensorManager实例:

SensorManage manager =

(SensorManager)context.getSystemService(Context.SENSOR_SERVICE);

SensorManager是Android系统提供的一个系统服务,Android具有多个系统服务,每一个服务可向我们提供不同的系统信息。一旦获取SensorManager实例,就能检查加速计是否可用:

 

boolean hasAccel = manager.getSensorList(Sensor.TYPE_ACCELEROMETER).size() > 0;

通过这段代码,我们查询管理器以了解所有安装的accelerometer类型加速计。这就意味着一个设备可具有多个加速计,不过,实际上只返回一个加速计传感器。

如果已经安装了一个加速计,我们可通过SensorManager来获取它并向其注册SensorEventListener,如下所示:

Sensor sensor = manager.getSensorList(Sensor.TYPE_ACCELEROMETER).get(0);

boolean sucess =

manager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_GAME);

参数SensorManager.SENSOR_DELAY_GAME用于指定侦听器更新的频率,其更新内容来自加速计的最新状态。

SensorManager.registerListener()方法返回一个Boolean变量,以表明注册过程是否成功。

一旦成功注册侦听器,我们就可以通过SensorEventListener.onSensorChanged()方法来接收SensorEvent。该方法只有在传感器的状态发生变化时才会被调用。

现在该处理SensorEvent了。该SensorEvent有一个公有浮点型数组成员变量SensorEvent.values,它存储了加速计当前3个轴的加速度值。SensorEvent.values[0]保存X轴的值,SensorEvent.values[1]保存Y轴的值,SensorEvent.values[2]保存Z轴的值。

总体来说X、Y轴方向位于你的手机屏幕所在的平面,而Z轴垂直于手机屏幕所在平面,X轴一般位于手机短的那条边的方向,其他的坐标轴大家可以充分发挥你的空间想象能力。

 

说了这么多,其实看看代码就一切都简单明了。

 

加速计的使用案例:

以下代码将在textView中实时显示出加速传感器x,y,z的三个值

[java][/java] view plaincopy

  1. public class AccelerometerActivity extends Activity implements SensorEventListener{
  2.     TextView textView;
  3.     StringBuilder builder = new StringBuilder();
  4.     @Override
  5.     protected void onCreate(Bundle savedInstanceState) {
  6.         // TODO Auto-generated method stub
  7.         super.onCreate(savedInstanceState);
  8.         textView = new TextView(this);
  9.         setContentView(textView);
  10.         SensorManager manager = (SensorManager)getSystemService(
  11.                 Context.SENSOR_SERVICE);
  12.     if(manager.getSensorList(Sensor.TYPE_ACCELEROMETER).size() == 0){
  13.             textView.setText(“加速器未安装”);
  14.         }else{
  15.             Sensor accelerometer = manager.getSensorList(
  16.                     Sensor.TYPE_ACCELEROMETER).get(0);
  17.             if(!manager.registerListener(this, accelerometer,
  18.                     SensorManager.SENSOR_DELAY_GAME)){
  19.                 textView.setText(“申请监听器失败”);
  20.             }
  21.         }
  22.     }
  23.     @Override
  24.     public void onAccuracyChanged(Sensor sensor, int accuracy) {
  25.     }
  26.     @Override
  27.     public void onSensorChanged(SensorEvent event) {
  28.         // TODO Auto-generated method stub
  29.         builder.setLength(0);
  30.         builder.append(“x: “);
  31.         builder.append(event.values[0]);
  32.         builder.append(“, y: “);
  33.         builder.append(event.values[1]);
  34.         builder.append(“, z: “);
  35.         builder.append(event.values[2]);
  36.         textView.setText(builder.toString());
  37.     }
  38. }

 

方向传感器

方向传感器是用加速计和磁力计虚拟出来的,用于计算手机的俯仰角度。

方向传感器包括下面三个角度:

Roll:左右倾斜角度,也叫滚转角 范围为-90°至90°

Pitch:前后倾斜,也叫俯仰角  范围为-180°至180°

Yaw:左右摇摆,也叫偏航角  范围为0°至360°。

 

方向传感器的使用案例:

以下代码将在textView中实时显示出方向传感器roll,pitch,yaw的三个值

[java][/java] view plaincopy

  1. public class OrientationActivity extends Activity
  2.  implements SensorEventListener{
  3.     TextView textView;
  4.     StringBuilder builder = new StringBuilder();
  5.     float yaw;
  6.     float pitch;
  7.     float roll;
  8.     @Override
  9.     protected void onCreate(Bundle savedInstanceState) {
  10.         // TODO Auto-generated method stub
  11.         super.onCreate(savedInstanceState);
  12.         textView = new TextView(this);
  13.         setContentView(textView);
  14. nsorManager manager = (SensorManager)getSystemService(
  15.                 Context.SENSOR_SERVICE);
  16.         if(manager.getSensorList(Sensor.TYPE_ORIENTATION).size() == 0){
  17.             textView.setText(“方向传感器未安装”);
  18.         }else{
  19.             Sensor compass = manager.getDefaultSensor(
  20.                     Sensor.TYPE_ORIENTATION);
  21.             if(!manager.registerListener(this, compass,
  22.                     SensorManager.SENSOR_DELAY_GAME)){
  23.                 textView.setText(“申请监听器失败”);
  24.             }
  25.         }
  26.     }
  27.     @Override
  28.     public void onAccuracyChanged(Sensor sensor, int accuracy) {
  29.         // TODO Auto-generated method stub
  30.     }
  31.     @Override
  32.     public void onSensorChanged(SensorEvent event) {
  33.         // TODO Auto-generated method stub
  34.         yaw = event.values[0];
  35.         pitch = event.values[1];
  36.         roll = event.values[2];
  37.         calculateOrientation();
  38.     }
  39.     public void calculateOrientation(){
  40.         builder.setLength(0);
  41.         builder.append(“yaw: “);
  42.         builder.append(yaw + “\n”);
  43.         builder.append(“pitch: ” );
  44.         builder.append(pitch + “\n”);
  45.         builder.append(“roll: “);
  46.         builder.append(roll);
  47.         textView.setText(builder.toString());
  48.     }
  49. }

标签