您的位置:首页 > 移动开发 > Android开发

Android游戏开发中常用的三种视图

2015-09-19 21:30 295 查看

简介

Android游戏开发中常用的三种视图是View、SurfaceView和GLSurfaceView

下面简单介绍这三种视图:

View:显示视图,内置画布,提供图形绘制函数、触屏事件、按键事件函数等;

SurfaceView:基于View视图进行拓展的视图类,更适用于2D游戏开发;

GLSurfaceView:基于SurfaceView视图类再次拓展的视图类,用于3D游戏开发的视图。

在2D游戏开发中,大致分为两种游戏框架:View游戏框架和SurfaceView游戏框架。两者的主要区别有:

1) 更新画布

View是由系统主UI线程进行更新,通过调用View提供的postInvalidate()和invalidate()这两个函数重新绘制的。棋牌类游戏,其画面更新属于被动更新,这类游戏适用于View游戏框架。

SurfaceView可在UI线程或者新的线程中更新。RPG、飞行射击类游戏,其画面更新属于主动更新,需要不断重绘,这类游戏实用于SurfaceView游戏框架。

2)视图机制

View视图没有双缓冲机制,而SurfaceView视图有。

实例

View游戏框架,此Demo跟踪用户的方向键点击事件,以及触屏事件,具体步骤如下:

1.新建Android项目“ViewDemo”,此处略去若干字

2.创建一个类MyView,此类继承View,代码如下:

package com.cpxiao.viewdemo;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;

/**
* Created by cpxiao on 15/9/19.
*/
public class MyView extends View {
private Paint mPaint;

/**
* 重写构造方法
*/
public MyView(Context context) {
super(context);
init();
}

public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}

private void init() {
//设置焦点
setFocusable(true);
mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setTextSize(30);
}

/**
* 重写绘图方法
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

canvas.drawText("MyView", textX, textY, mPaint);

}

int textX = 100;
int textY = 100;

/**
* 重写按键按下事件的方法
*/
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
//判断按下的是否为方向键(上下左右)
if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
textY -= 10;
} else if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
textY += 10;
} else if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
textX -= 10;
} else if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
textX += 10;
}
//重绘画布的函数有invalidate()和postInvalidate(),区别在于invalidate()不能在子线程中循环调用,而postInvalidate()可以。
invalidate();
//      postInvalidate();
return super.onKeyDown(keyCode, event);

}

/**
* 重写按键抬起事件的方法
*/
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
return super.onKeyUp(keyCode, event);
}

/**
* 重写触屏事件方法
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
if (event.getAction() == MotionEvent.ACTION_DOWN) {
textX = x;
textY = y;
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
textX = x;
textY = y;
} else if (event.getAction() == MotionEvent.ACTION_UP) {
textX = x;
textY = y;
}
invalidate();
return true;
//      return super.onTouchEvent(event);
}
}


3.MainActivity代码如下:

package com.cpxiao.viewdemo;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View view = new MyView(this);

//隐藏标题栏部分(程序名字)
requestWindowFeature(Window.FEATURE_NO_TITLE);
//隐藏状态栏部分(电池电量、时间等部分)
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(view);
}
}


SurfaceView游戏框架

1.新建Android项目“SurfaceViewDemo”,此处略去若干字

2.新建类“MySurfaceView”,此类继承SurfaceView,并且实现android.view.SurfaceHolder.Callback接口,代码如下:

package com.cpxiao.surfaceviewdemo;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

/**
* Created by cpxiao on 15/9/19.
*/
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback {

//SurfaceHolder用于控制SurfaceView的大小、格式等,用于监听SurfaceView的状态。
private SurfaceHolder mSurfaceHolder;
private Paint mPaint;

//初始化文本坐标
private int textX = 100;
private int textY = 100;

public MySurfaceView(Context context) {
super(context);
init();
}

public MySurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

public MySurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}

private void init() {
//实例SurfaceHolder
mSurfaceHolder = getHolder();
//为SurfaceView添加状态监听
mSurfaceHolder.addCallback(this);
//实例一个画笔
mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setTextSize(30);
}

/**
* 重写SurfaceHolder.Callback接口的三个方法surfaceCreated()、surfaceChanged()、surfaceDestroyed()
*/

/**
* 当SurfaceView被创建完成后响应的方法
*/
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
myDraw();
}

/**
* 当SurfaceView状态发生改变时响应的方法
*/
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {

}

/**
* 当SurfaceView状态Destroyed时响应的方法
*/
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {

}

/**
* 自定义绘图方法
*/
private void myDraw() {
//使用SurfaceHolder.lockCanvas()获取SurfaceView的Canvas对象,并对画布加锁.
Canvas canvas = mSurfaceHolder.lockCanvas();
//得到自定义大小的画布,因为局部绘制,效率更高
//      Canvas canvas = mSurfaceHolder.lockCanvas(new Rect(0,0,200,200));

/**
* 在绘制之前需要将画布清空,否则画布上会显示之前绘制的内容,以下三种方法效果一致*/
canvas.drawRect(0,0,getWidth(),getHeight(),new Paint());
canvas.drawColor(Color.WHITE);
canvas.drawRGB(255, 255, 255);

//通过在Canvas上绘制内容来修改SurfaceView中的数据
canvas.drawText("mySurfaceView", textX, textY, mPaint);
//用于解锁画布和提交
mSurfaceHolder.unlockCanvasAndPost(canvas);
}

/**
* 重写触屏监听方法
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
textX = (int) event.getX();
textY  = (int) event.getY();
myDraw();
return true;
//      return super.onTouchEvent(event);
}
}


3.MainActivity代码如下:

package com.cpxiao.surfaceviewdemo;

import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
MySurfaceView view = new MySurfaceView(this);
setContentView(view);
}
}


到此步骤为止,实现了和ViewDemo一致的效果。但是游戏中不会等用户按键或者触屏之后才绘制画布,往往会每隔一段时间刷新画布,比如游戏中的计时器、背景中的流水、动物等等,这些元素虽然不与用户交互但却是动态的。所以游戏开发中需要有一个线程实时更新游戏元素的状态。

完整的MySurfaceView代码如下:

package com.cpxiao.surfaceviewdemo;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

/**
* Created by cpxiao on 15/9/19.
*/
public class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback, Runnable {

//SurfaceHolder用于控制SurfaceView的大小、格式等,用于监听SurfaceView的状态。
private SurfaceHolder mSurfaceHolder;
private Paint mPaint;

//初始化文本坐标
private int textX = 100;
private int textY = 100;

//声明一个线程
private Thread mThread;
//线程消亡的标志位
private boolean flag = false;

//声明一个画布
private Canvas mCanvas;
//声明屏幕的宽高,获取视图的宽高一定要在视图创建之后才可获取,即surfaceCreated之后获取,否则一直为0
private int screenWidth, screenHeight;

public MySurfaceView(Context context) {
super(context);
init();
}

public MySurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}

public MySurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}

private void init() {
//实例SurfaceHolder
mSurfaceHolder = getHolder();
//为SurfaceView添加状态监听
mSurfaceHolder.addCallback(this);
//实例一个画笔
mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setTextSize(30);

//设置焦点
setFocusable(true);
}

/**
* 重写SurfaceHolder.Callback接口的三个方法surfaceCreated()、surfaceChanged()、surfaceDestroyed()
*/

/**
* 当SurfaceView被创建完成后响应的方法
*/
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
screenWidth = getWidth();
screenHeight = getHeight();
Log.d("CPXIAO", "screenWidth = " + screenWidth);
Log.d("CPXIAO", "screenHeight = " + screenHeight);
flag = true;
//实例线程
mThread = new Thread(this);
mThread.start();
}

/**
* 当SurfaceView状态发生改变时响应的方法
*/
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {

}

/**
* 当SurfaceView状态Destroyed时响应的方法
*/
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
flag = false;
}

/**
* 自定义绘图方法
*/
private void myDraw() {
try {
//使用SurfaceHolder.lockCanvas()获取SurfaceView的Canvas对象,并对画布加锁.
mCanvas = mSurfaceHolder.lockCanvas();
//得到自定义大小的画布,因为局部绘制,效率更高
//      Canvas canvas = mSurfaceHolder.lockCanvas(new Rect(0,0,200,200));

if (mCanvas != null) {
/**
* 在绘制之前需要将画布清空,否则画布上会显示之前绘制的内容,以下三种方法效果一致*/
mCanvas.drawRect(0, 0, getWidth(), getHeight(), new Paint());
mCanvas.drawColor(Color.WHITE);
mCanvas.drawRGB(255, 255, 255);

//通过在Canvas上绘制内容来修改SurfaceView中的数据
mCanvas.drawText("mySurfaceView", textX, textY, mPaint);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (mSurfaceHolder != null) {
//用于解锁画布和提交
mSurfaceHolder.unlockCanvasAndPost(mCanvas);
}
}
}

/**
* 重写触屏监听方法
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
textX = (int) event.getX();
textY = (int) event.getY();
myDraw();
return true;
//      return super.onTouchEvent(event);
}

private int moveX = 10;
private int moveY = 10;

/**
* 程序逻辑代码
*/
private void logic() {
if (textX < 0) {
moveX = 10;
} else if (textX > screenWidth) {
moveX = -10;
}

if (textY < 0) {
moveY = 10;
} else if (textY > screenHeight) {
moveY = -10;
}

textX += moveX;
textY += moveY;

}

//设置刷新时间为50毫秒
private static final int REFRESH_TIME = 50;

@Override
public void run() {
while (flag) {
long start = System.currentTimeMillis();
myDraw();
logic();
long end = System.currentTimeMillis();
try {
long use_time = end - start;
if (use_time < REFRESH_TIME) {
mThread.sleep(REFRESH_TIME - use_time);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}


代码说明:

1)线程标志位flag

便于消亡线程

防止重复创建线程,避免程序异常

2)获取视图的宽高

getWidth()和getHeight()

获取视图的宽高一定要在视图创建之后才可获取,即surfaceCreated之后获取,否则一直为0。因为视图还未创建之前是没有宽高值的

3)绘制时try catch finally

判断Canvas是否为null

为防止绘制出错无法运行到解锁提交这一步,将unlockCanvasAndPost放在finally中执行,执行前先判断一下SurfaceHolder是否为空

4)刷新时间尽可能保持一直,保证帧数

根据逻辑处理用时,计算出每次绘制休眠时间

Demo下载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 开发