Android手机游戏开发学习笔记
第一部分 其实游戏就是让状态机不断的让Canvas在View上画你想要的东西。这个状态机包括内部的执行,还包括外部的输入。
Android开发的MVC模式
1,通过View和SurfaceView来显示界面的视图。(处理界面与用户的交互事件,如,触笔点击,用户按键等。可通过View类的onKeyDown,onKeyUp,onTouchEvent等)。
2,用Activity来控制游戏的整体结构。
3,设计一个逻辑类,用来处理逻辑运算。
Android中任何一个View类都只有重写onDraw方法来实现界面显示。
Android中提供了onKeyUp,onKeyDown,onKeyMultiple,onKeyPreIme,onTouchEvent,onTrackballEvent等方法。可以用来处理游戏中的事件消息。所以继承View时,需要重载这些方法。
Android中提供了invalidate来刷新界面,但invalidate不能直接在线程中调用,违背单线程模型。
因此Android中最常用的方法是利用Handler来时更新UI界面。
第一部分 View 类
每个View类都有一个绘画的画布,在游戏中可以自定义视图View,任何一个View类都只需要重写onDraw方法来实现界面显示,可以是3D,也可以是文本。
游戏的核心就是不断的绘图和刷新,图我们可以通过onDraw方法绘制,刷新Android中可以用invalidate方法来刷新界面,注意:invalidate不能直接在线程中调用,因其违背了
违背单线程模型。因此Android中最常用的方法是利用Handler来时更新UI界面。下面这个例子中包含了两个刷新方法。
public class Game extends Activity{
public static final int REFRESH = 1;
public GameView gameView;
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
this.gameView = new GameView(this);//实例化GameView
setContentView(gameView);
new Thread(new GameThread()).start();
}
// Handler handler = new Handler(){//注释掉的为实例化Handler对象并重写handleMessage方法实现一个消息接受器,然后在线程中通过sendMessage方法发送更新界面的消息,
当接收器收到更新界面的消息时,便执行invalidate方法更新屏幕显示。
// public void handleMessage(Message msg){//接受消息
// switch (msg.what){
// case Game.REFRESH:
// gameView.invalidate();//更新界面
// break;
// }
// super.handleMessage(msg);
// }
// };
public boolean onTouchEvent (MotionEvent event){
return true;
}
public boolean onKeyDown(int keyCode,KeyEvent event){
return true;
}
public boolean onKeyUp(int keyCode,KeyEvent event){
switch (keyCode){
case KeyEvent.KEYCODE_DPAD_DOWN:
gameView.y+=6;
break;
case KeyEvent.KEYCODE_DPAD_UP:
gameView.y-=6;
break;
}
return true;
}
public boolean onKeyMultiple(int keyCode,int repeatCount,KeyEvent event){
return true;
}
// public class GameThread implements Runnable{//创建更新线程
// @Override
// public void run() {
// while(!Thread.currentThread().isInterrupted()){
// Message message = new Message();
// message.what = Game.REFRESH;
// Game.this.handler.sendMessage(message);//发送消息
// try{
// Thread.sleep(100);
// }catch(InterruptedException e){
// Thread.currentThread().interrupt();
// }
// }
// }
// }
public class GameThread implements Runnable{
public void run(){
while(!Thread.currentThread().isInterrupted()){
try{
Thread.sleep(100);
}catch(InterruptedException e){
Thread.currentThread().interrupt();
}
gameView.postInvalidate();//使用PostInvalidate可以直接在线程中更新界面 不需要Handler来传递消息
}
}
}
}
public class GameView extends View{
public int count = 0;
public int y =0;
public GameView(Context context) {
super(context);
}
public void onDraw(Canvas canvas){
if(count<8){
count++;
}else{
count=0;
}
Paint paint= new Paint();
switch(count%4){
case 0:
paint.setColor(Color.BLACK);
break;
case 1:
paint.setColor(Color.RED);
break;
case 2:
paint.setColor(Color.YELLOW);
break;
case 3:
paint.setColor(Color.GREEN);
break;
}
canvas.drawRect(y,y,y+40,y+40, paint);//绘制矩形
}
}
第二部分 SurfaceView类
1,开发复杂游戏,而且对程序的执行效率要求更高时用此类,因本身就是双缓冲机制的。
2,SurfaceView可以直接访问一个画布。
3,SurfaceView是提供给需要直接画像素而不是使用窗体部件的应用而使用的。
4,View即其子类(如TextVie,Button)要画在Surface上。每个Surface创建一个Canvas对象(属性时常改变)用来管理View在Surface上绘制操作。
5,使用SurfaceView绘图时,一般都是出现在最顶层。在使用时要对其创建,销毁,情况改变进行监视,这就需要实现SurfaceHolder.Callback接口,
如果要对被绘制的画布进行裁剪,控制其大小时都需要使用SurfaceHolder来完成处理。
6,在程序中,SurfaceHolder对象需要通过getHolder方法来获得,同时还需要addCallback方法来添加“回调函数”。
7,SurfaceView与View不同之处,在于SurfaceView不需要通过线程来更新视图,在绘制前必须使用lockCanvas方法锁定画布,并得到画布,然后在画布上绘制
绘制完成后,使用unlockCanvasAndPost方法来解锁画布。
8,addCallback:给SurfaceView添加一个回调函数;
removeCallback:从SurfaceView移除回调函数;
public class GameView2 extends SurfaceView implements SurfaceHolder.Callback, Runnable{
SurfaceHolder surfaceHolder = null;//定义对象
public boolean loop = false;
public int count = 1;
public GameView2(Context context) {
super(context);
surfaceHolder = this.getHolder();//实例化SurfaceHolder对象
surfaceHolder.addCallback(this);//添加回调函数
loop = true;
this.setFocusable(true);//不知道这句什么意思?
}
private void Draw() {
Canvas canvas = surfaceHolder.lockCanvas();//锁定画布
if(surfaceHolder ==null||canvas ==null){
return ;
}
if(count<100){
count++;
}else{
count= 0;
}
Paint paint = new Paint();//创建画笔
paint.setAntiAlias(true);//设置抗锯齿
paint.setColor(Color.GREEN);//设置画笔颜色
canvas.drawRect(0,0,320,480, paint);//绘制矩形
switch(count%4){
case 0:
paint.setColor(Color.BLUE);
break;
case 1:
paint.setColor(Color.YELLOW);
break;
case 2:
paint.setColor(Color.RED);
break;
case 3:
paint.setColor(Color.CYAN);
break;
default:
paint.setColor(Color.WHITE);
break;
}
canvas.drawCircle(130, 240, 500, paint);
surfaceHolder.unlockCanvasAndPost(canvas);//绘制后解锁,绘制后必须解锁才能显示
}
@Override//在Surface大小发生改变时激发
public void surfaceChanged(SurfaceHolder holder, int format, int width,int height) {
// TODO Auto-generated method stub
}
@Override//在Surface创建时激发
public void surfaceCreated(SurfaceHolder holder) {
// TODO Auto-generated method stub
new Thread(this).start();
}
@Override//在Surface销毁时调用
public void surfaceDestroyed(SurfaceHolder holder) {
// TODO Auto-generated method stub
loop=false;
}
public void run(){
while(loop){
try{
Thread.sleep(200);
synchronized (surfaceHolder) {
Draw();
}
}catch(Exception e){
}
}
}
}
第三部分 Paint类和Color类
在Android中通过graphic类来显示2D图形。graphic包括Canvas类(画布),Paint(画笔),Color(颜色),Bitmap(图像),2D几何图形等常用类。
package graphics;
Paint类常用的一些方法:
setAntiAlias 设置画笔的锯齿效果
setColor 设置画笔颜色
setARGB 设置画笔的a,r,g,b值。
setAlpha 设置透明度
setTextSize 设置字体大小
setStyle 设置画笔风格 空心或实心
setStrokeWidth设置空心的边框宽度
getColor 得到画笔的颜色
getAlpha 得到画笔的透明度
Color类中定义了一些颜色常量
Color.rgb方法将整型的颜色转换成Color类型。如Color.red方法可提取出红色的值。
/**
* 注意:在绘制时按从上到下的顺序绘制的 ,后面的如果和前面的重合的话会将前面的覆盖掉
*/
public class Graphics_Paint_Color extends View implements Runnable {
public Paint paint;
private String TAG="你好";
public Graphics_Paint_Color(Context context) {
super(context);
paint = new Paint();//创建画笔
new Thread(this).start();//开启线程
}
public void onDraw(Canvas canvas){
super.onDraw(canvas);
paint.setAntiAlias(true);//设置抗锯齿
paint.setColor(Color.RED);//设置画笔颜色
paint.setColor(Color.rgb(12, 234, 34));//设置画笔颜色 其中参数分别为red,green,blue三种颜色的值
Color.red(23);//提取颜色?
Color.blue(234);
paint.setARGB(255, 23,234,49);//设置Paint的颜色和Alpha值
paint.setAlpha(200);//设置透明度
//paint.set(new PAINT());//设置另外一个Paint对象
paint.setTextSize(14);//设置字体的尺寸
//得到Paint的一些属性
Log.i(TAG, "paint的颜色"+paint.getColor());
Log.i(TAG,"paint的Alpha"+paint.getAlpha());
paint.setStyle(Paint.Style.STROKE);//设置Paint为空心
paint.setStrokeWidth(5);//设置空心的外框宽度
canvas.drawRect(0,0,50,60, paint);//绘制空心矩形
paint.setStyle(Paint.Style.FILL);//设置Paint为实心
canvas.drawRect(60,70,120,140, paint);//绘制实心的矩形
}
public void run() {
while(!Thread.currentThread().isInterrupted()){
try{
Thread.sleep(200);
}catch(Exception e){
e.printStackTrace();
Thread.currentThread().interrupt();
}
postInvalidate();//使用postInvalidate可以直接在线程中更新界面
}
}
}在View或SurfaceView中绘制图形后,需要在Activtiy中通过setContentView()来让其显示出来
第四部分 Canvas类和ShadeDrawable类
Canvas方法:
Canvas() 创建画布,可以用setBitmap()方法设置绘制具体画布
Canvas(Bitmap bitmap) 以bitmap对象创建一个画布,则将内容都绘制在bitmap上,所以bitmap不得为null
Canvas(GL gl) 绘制3D效果时使用,与OpenGL
drawColor 设置画布背景色
setBitmap 设置具体画布
clipRect 设置显示区域,即设置裁剪区
isOpaque 检测是否支持透明
rotate 旋转画布 在游戏中我们需要对精灵旋转,缩放或其它操作就可以通过旋转画布来实现,但在旋转画布时会旋转画布上所以对象,而我们只需要旋转其中一个。这个时候
我们就需要用save方法来锁定需要操作的对象,在操作后通过restore方法来解锁。(例子有此方法的运用)
setViewport 设置画布中显示窗口
skew 设置便宜量
绘制几何图形的方法
drawRect 绘制矩形
drawCircle 绘制圆形
drawOval 绘制椭圆
drawLine 绘制直线
drawPoint 绘制点
public class CanvasView extends View implements Runnable{
private Paint paint ;
public ShapeDrawableView shapeDrawableView;
public CanvasView(Context context) {
super(context);
paint = new Paint();
shapeDrawableView= new ShapeDrawableView(context);//得到对象
new Thread(this).start();
}
public void onDraw(Canvas canvas){
super.onDraw(canvas);
canvas.drawColor(Color.BLACK);//设置画布颜色
paint.setAntiAlias(true);//取消抗锯齿效果
//canvas.clipRect(20,20,80,80);//设置裁剪区域
canvas.save();//锁定画布
//canvas.rotate(45.0f);//旋转画布
paint.setColor(Color.RED);//设置颜色及绘制矩形
canvas.drawRect(new Rect(10,10,80,80), paint);
canvas.restore();//解锁画布
paint.setStyle(Paint.Style.STROKE);{//设置为空心画笔
Rect rect = new Rect();
rect.left =85;
rect.bottom=80;
rect.right=150;
rect.top = 10;
paint.setColor(Color.BLUE);
canvas.drawRect(rect, paint);//绘制矩形
canvas.drawCircle(60,60,50,paint);//绘制圆形
RectF rectF = new RectF();//绘制椭圆
rectF.bottom=80;
rectF.left=160;
rectF.right= 300;
rectF.top = 10;
canvas.drawOval(rectF, paint);
Path path = new Path();//绘制多边形
path.moveTo(10, 90);
path.lineTo(80, 100);
path.lineTo(70,150);
path.lineTo(10, 200);
path.close();//封闭多边形
canvas.drawPath(path, paint);
paint.setStrokeWidth(5);
canvas.drawLine(50,50,300,200, paint);
}
paint.setStyle(Paint.Style.FILL);{//设置画笔为实心
paint.setColor(Color.YELLOW);
canvas.drawRect(20,250,300,300, paint);
}
shapeDrawableView.DrawShape(canvas);//绘制另一View里的图形到本View中来
}
public void run() {
while(!Thread.currentThread().isInterrupted()){
try{
Thread.sleep(200);
}catch(Exception e){
Thread.currentThread().interrupt();
}
postInvalidate();//直接在线程中更新
}
}
}
/**
*Android中可以通过ShapeDrawable来绘制图像。可以通过getPaint方法得到Paint对象,
setBounds 此方法可设置图形显示的区域
通过ShapeDrawable的Draw方法将图形画到屏幕上
*/
public class ShapeDrawableView extends View{
ShapeDrawable shapeDrawable;
Paint paint;
public ShapeDrawableView(Context context) {
super(context);
}
public void DrawShape(Canvas canvas){
shapeDrawable = new ShapeDrawable();//------用ShapeDrawable绘图必须要实例化对象
paint=shapeDrawable.getPaint();//得到画笔
paint.setColor(Color.GREEN);//设置画笔颜色
Rect bounds =new Rect(10,310,80,390);//绘制矩形
shapeDrawable.setBounds(bounds);//------用ShapeDrawable绘图必须要使用SetBounds显示区域
shapeDrawable.