前两个游戏是基于View游戏框架的,View游戏框架只适合做静止的,异步触发的游戏,如果做一直在动的游戏,View的效率就不高了,我们需要一种同步触发的游戏框架,也就是surfaceview游戏框架,你可能会问,什么乱七八糟的,啥叫同步?啥叫异步?。。。我就不告诉你。。。我们先看一下这个同步框架,看看骚年你能不能自己领悟。

GameView.java(继承自SurfaceView)

package com.next.eatsnake;import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnTouchListener;import java.util.Random;/*** Created by Next on 2016/3/24 0024.*/
public class GameView extends SurfaceView implements SurfaceHolder.Callback,OnTouchListener,Runnable {enum GameState{Menu,Playing,Over;}GameState gameState;//游戏状态Thread thread;//游戏线程Boolean flag;//游戏循环控制标志Canvas canvas;//画布Paint paint;//画笔SurfaceHolder holder;//SurfaceView控制句柄Random random;//随机数NextEvent nextEvent;//游戏输入事件int scrW,scrH;//屏幕的宽和高public GameView(Context context) {super(context);gameState = GameState.Menu;flag = true;paint = new Paint();paint.setAntiAlias(true);//笔迹平滑thread = new Thread(this);random = new Random();nextEvent = new NextEvent();holder = this.getHolder();holder.addCallback(this);this.setOnTouchListener(this);setKeepScreenOn(true);scrW = ((MainActivity)context).getWindowManager().getDefaultDisplay().getWidth();scrH = ((MainActivity)context).getWindowManager().getDefaultDisplay().getHeight();}@Overridepublic boolean onTouch(View v, MotionEvent event) {if (event.getAction() == MotionEvent.ACTION_DOWN) {nextEvent.setDownX((int) event.getX());nextEvent.setDownY((int) event.getY());}else if (event.getAction() == MotionEvent.ACTION_UP) {nextEvent.setUpX((int) event.getX());nextEvent.setUpY((int) event.getY());}return true;}private void mLogic(){switch (gameState){case Menu:menuLogic();break;case Playing:playingLogic();break;case Over:overLogic();break;}nextEvent.init();//每次逻辑循环后,清空事件}private void menuLogic(){if(nextEvent.getUpX() > 0){gameState = GameState.Playing;}}private void playingLogic(){if(nextEvent.getDir() == NextEvent.DOWN){gameState = GameState.Over;}}private void overLogic(){if(nextEvent.getDir() == NextEvent.RIGHT){gameState = GameState.Menu;}}private void mDraw(){try {canvas = holder.lockCanvas();canvas.drawColor(Color.WHITE);switch (gameState){case Menu:menuDraw(canvas);break;case Playing:playingDraw(canvas);break;case Over:overDraw(canvas);break;}}catch (Exception e){e.printStackTrace();}finally {holder.unlockCanvasAndPost(canvas);}}private void menuDraw(Canvas canvas){paint.setColor(Color.RED);paint.setTextSize(50);canvas.drawText("I am in menu.Touch me to next scence",100,100,paint);}private void playingDraw(Canvas canvas){paint.setColor(Color.RED);paint.setTextSize(50);canvas.drawText("I am in playing,Slide down to next scence ",100,100,paint);}private void overDraw(Canvas canvas){paint.setColor(Color.RED);paint.setTextSize(50);canvas.drawText("I am in over,Slide right to next scence",100,100,paint);}//这里就是同步触发机制了//每一个时钟周期,执行一次逻辑方法和绘图方法@Overridepublic void run() {while(flag){mLogic();mDraw();try {Thread.sleep(500);}catch (Exception e){e.printStackTrace();}}}//SurfaceView创建时调用@Overridepublic void surfaceCreated(SurfaceHolder holder) {thread.start();}//SurfaceView发生改变时调用@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}//SurfaceView销毁时调用@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {flag = false;}}

这只是一个游戏框架,还没有往里写任何内容,但根据我的经验(虽然我也没有很多经验),这里已经包括了所有游戏(这里特指小游戏-_-)的主体框架代码,有了这个框架,我们就只需要写游戏的逻辑和绘图方法了,不用纠结怎么搭建游戏框架了。
这里我还加了一个NextEvent的方法,在里面我封装了上个游戏用到的滑动手势,在这个挨踢圈里,人们最常说的一句话就是:不要重复造轮子。当我们看到写了很多重复代码时,就是我们需要精简的时候了。
上代码:

NextEvent.java

package com.next.eatsnake;/*** Created by Next on 2016/3/24 0024.*/
public class NextEvent {public static final int LEFT = 1;public static final int RIGHT = 2;public static final int UP = 3;public static final int DOWN = 4;public static final int QUIET = -1;//没有滑动private int dir;private int downX,downY,upX,upY;public NextEvent(){init();}public void init(){this.dir = QUIET;this.downX = -1;this.downY = -1;this.upX = -1;this.upY = -1;}public int getDir(){float offsetX,offsetY;offsetX = upX - downX;offsetY = upY - downY;if (Math.abs(offsetX) > Math.abs(offsetY)) {if (offsetX > 5 ) {dir = RIGHT;}else if (offsetX < -5) {dir = LEFT;}}else {if (offsetY > 5) {dir = DOWN;}else if (offsetY < -5) {dir = UP;}}return dir;}public int getDownX() {return downX;}public void setDownX(int downX) {this.downX = downX;}public int getDownY() {return downY;}public void setDownY(int downY) {this.downY = downY;}public int getUpX() {return upX;}public void setUpX(int upX) {this.upX = upX;}public int getUpY() {return upY;}public void setUpY(int upY) {this.upY = upY;}
}

这个NextEvent是用来存储用户输入事件的,我们只是存储,而没有直接触发游戏逻辑。那么什么时候用到读取这个NextEvent呢?如果你仔细看了第一段代码,应该已经知道了——在每一个时钟周期的逻辑方法里判断NextEvent,并由此改变游戏逻辑。这种就是同步机制,而用户输入事件游戏逻辑就随之改变的就是异步机制。

下面我们用这个框架做一个贪吃蛇游戏,效果图如下:



MainActivity.java

package com.next.eatsnake;import android.app.Activity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;public class MainActivity extends Activity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);setContentView(new GameView(this));}
}

GameView.java

package com.next.eatsnake;import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnTouchListener;import java.util.ArrayList;
import java.util.Random;/*** Created by Administrator on 2016/3/24 0024.*/
public class GameView extends SurfaceView implements SurfaceHolder.Callback,OnTouchListener,Runnable {enum GameState{Menu,Playing,Over;}GameState gameState;//游戏状态Thread thread;//游戏线程Boolean flag;//游戏循环控制标志Canvas canvas;//画布Paint paint;//画笔SurfaceHolder holder;//SurfaceView控制句柄public static Random random;//随机数NextEvent nextEvent;//游戏输入事件int scrW,scrH;//屏幕的宽和高final int MAX_X = 15;int MAX_Y;//竖向tile数量根据MAX_X动态计算,保证tile是正方形public static Tile[][] tiles;Snake snake;public static boolean isEatFood;public GameView(Context context) {super(context);gameState = GameState.Menu;flag = true;paint = new Paint();paint.setAntiAlias(true);//笔迹平滑paint.setTextAlign(Paint.Align.CENTER);//文字中间对齐thread = new Thread(this);random = new Random();nextEvent = new NextEvent();holder = this.getHolder();holder.addCallback(this);this.setOnTouchListener(this);setKeepScreenOn(true);scrW = ((MainActivity)context).getWindowManager().getDefaultDisplay().getWidth();scrH = ((MainActivity)context).getWindowManager().getDefaultDisplay().getHeight();Tile.width = scrW/MAX_X;MAX_Y = scrH/Tile.width;tiles = new Tile[MAX_X][MAX_Y];isEatFood = false;}private void newGame(){for (int x = 0;x < MAX_X;x++){for (int y = 0;y < MAX_Y;y++){if (x == 0||y == 0||x == MAX_X-1||y == MAX_Y-1){tiles[x][y] = new Tile(x,y,Tile.TYPE_WALL);}else {tiles[x][y] = new Tile(x,y,Tile.TYPE_NULL);}}}snake = new Snake(tiles[4][4],tiles[5][4],tiles[6][4], NextEvent.DOWN);addFood();addFood();addFood();}public void addFood(){ArrayList<Tile> nullList = new ArrayList<Tile>();for (int x = 0;x < MAX_X;x++){for (int y = 0;y < MAX_Y;y++){if (tiles[x][y].getType() == Tile.TYPE_NULL){nullList.add(tiles[x][y]);}}}if (nullList.size()!=0){nullList.get(random.nextInt(nullList.size())).setType(Tile.TYPE_FOOD);}}@Overridepublic boolean onTouch(View v, MotionEvent event) {if (event.getAction() == MotionEvent.ACTION_DOWN) {nextEvent.setDownX((int) event.getX());nextEvent.setDownY((int) event.getY());}else if (event.getAction() == MotionEvent.ACTION_UP) {nextEvent.setUpX((int) event.getX());nextEvent.setUpY((int) event.getY());}return true;}private void mLogic(){switch (gameState){case Menu:menuLogic();break;case Playing:playingLogic();break;case Over:overLogic();break;}nextEvent.init();//每次逻辑循环后,清空事件}private void menuLogic(){if(nextEvent.getUpX() > 0){gameState = GameState.Playing;newGame();}}private void playingLogic(){if (nextEvent.getDir()!=NextEvent.QUIET){snake.setDir(nextEvent.getDir());}snake.move();if (isEatFood){addFood();isEatFood = false;}if(!snake.isAlive()){gameState = GameState.Over;}}private void overLogic(){if(nextEvent.getUpX() > 0){gameState = GameState.Playing;newGame();}}private void mDraw(){try {canvas = holder.lockCanvas();canvas.drawColor(Color.WHITE);switch (gameState){case Menu:menuDraw(canvas);break;case Playing:playingDraw(canvas);break;case Over:overDraw(canvas);break;}}catch (Exception e){e.printStackTrace();}finally {holder.unlockCanvasAndPost(canvas);}}private void menuDraw(Canvas canvas){paint.setColor(Color.BLACK);paint.setTextSize(50);canvas.drawText("Eat Snake,Touch me and start",scrW/2,scrH/2,paint);}private void playingDraw(Canvas canvas){for (int x = 0;x < MAX_X;x++){for (int y = 0;y < MAX_Y;y++){tiles[x][y].draw(canvas,paint);}}}private void overDraw(Canvas canvas){paint.setColor(Color.BLACK);paint.setTextSize(50);canvas.drawText("Your score is:"+snake.getLength(),scrW/2,scrH/4,paint);canvas.drawText("Touch me and try again",scrW/2,scrH/2,paint);}@Overridepublic void run() {while(flag){mLogic();mDraw();try {Thread.sleep(500);}catch (Exception e){e.printStackTrace();}}}@Overridepublic void surfaceCreated(SurfaceHolder holder) {thread.start();}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {flag = false;}}

Tile.java

package com.next.eatsnake;import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;/*** Created by Next on 2016/3/26 0026.*/
public class Tile {public static final int TYPE_NULL = 0;//空public static final int TYPE_WALL = 1;//墙public static final int TYPE_HEAD = 2;//蛇头public static final int TYPE_BODY = 3;//蛇身public static final int TYPE_TAIL = 4;//蛇尾public static final int TYPE_FOOD = 5;//食物private int x,y;private int type;public static int width;public Tile(int x, int y, int type) {this.x = x;this.y = y;this.type = type;}public void draw(Canvas canvas,Paint paint){switch (type){case TYPE_NULL:break;case TYPE_WALL:paint.setColor(Color.BLACK);canvas.drawCircle(x*width+width/2,y*width+width/2,width/2,paint);break;case TYPE_HEAD:paint.setColor(Color.MAGENTA);canvas.drawCircle(x*width+width/2,y*width+width/2,width/2,paint);paint.setColor(Color.WHITE);canvas.drawCircle(x*width+width/2,y*width+width/2,width/8,paint);break;case TYPE_BODY:paint.setColor(Color.MAGENTA);canvas.drawCircle(x*width+width/2,y*width+width/2,width/2,paint);break;case TYPE_TAIL:paint.setColor(Color.MAGENTA);paint.setStrokeWidth(10);canvas.drawLine(x * width, y * width + width / 2, x * width + width / 2, y * width, paint);canvas.drawLine(x*width+ width / 2,y*width,x*width+width,y*width+width/2,paint);canvas.drawLine(x*width+width,y*width+width/2,x*width+width/2,y*width+width,paint);canvas.drawLine(x*width+width/2,y*width+width,x*width,y*width+ width / 2,paint);break;case TYPE_FOOD:switch (GameView.random.nextInt(3)){case 0:paint.setColor(Color.YELLOW);break;case 1:paint.setColor(Color.GREEN);break;case 2:paint.setColor(Color.CYAN);break;}canvas.drawCircle(x*width+width/2,y*width+width/2,width/2,paint);break;}}public int getX() {return x;}public void setX(int x) {this.x = x;}public int getY() {return y;}public void setY(int y) {this.y = y;}public int getType() {return type;}public void setType(int type) {this.type = type;}
}

Snake.java

package com.next.eatsnake;import java.util.ArrayList;/*** Created by Administrator on 2016/3/26 0026.*/
public class Snake {private ArrayList<Tile> snake;private int dir;private boolean isAlive;public Snake(Tile head,Tile body,Tile tail,int dir){snake = new ArrayList<Tile>();head.setType(Tile.TYPE_HEAD);body.setType(Tile.TYPE_BODY);tail.setType(Tile.TYPE_TAIL);snake.add(head);snake.add(body);snake.add(tail);isAlive = true;this.dir = dir;}public void move(){if (!isAlive)return;switch (dir){case NextEvent.LEFT:switch (GameView.tiles[snake.get(0).getX()-1][snake.get(0).getY()].getType()){case Tile.TYPE_WALL:case Tile.TYPE_BODY:case Tile.TYPE_TAIL:isAlive = false;break;case Tile.TYPE_FOOD:GameView.tiles[snake.get(0).getX()-1][snake.get(0).getY()].setType(Tile.TYPE_HEAD);GameView.tiles[snake.get(0).getX()][snake.get(0).getY()].setType(Tile.TYPE_BODY);snake.add(0, GameView.tiles[snake.get(0).getX() - 1][snake.get(0).getY()]);GameView.isEatFood = true;break;case Tile.TYPE_NULL:GameView.tiles[snake.get(0).getX()-1][snake.get(0).getY()].setType(Tile.TYPE_HEAD);GameView.tiles[snake.get(0).getX()][snake.get(0).getY()].setType(Tile.TYPE_BODY);snake.add(0, GameView.tiles[snake.get(0).getX() - 1][snake.get(0).getY()]);snake.get(snake.size()-1).setType(Tile.TYPE_NULL);snake.remove(snake.size()-1);snake.get(snake.size()-1).setType(Tile.TYPE_TAIL);break;}break;case NextEvent.RIGHT:switch (GameView.tiles[snake.get(0).getX()+1][snake.get(0).getY()].getType()){case Tile.TYPE_WALL:case Tile.TYPE_BODY:case Tile.TYPE_TAIL:isAlive = false;break;case Tile.TYPE_FOOD:GameView.tiles[snake.get(0).getX()+1][snake.get(0).getY()].setType(Tile.TYPE_HEAD);GameView.tiles[snake.get(0).getX()][snake.get(0).getY()].setType(Tile.TYPE_BODY);snake.add(0, GameView.tiles[snake.get(0).getX() + 1][snake.get(0).getY()]);GameView.isEatFood = true;break;case Tile.TYPE_NULL:GameView.tiles[snake.get(0).getX()+1][snake.get(0).getY()].setType(Tile.TYPE_HEAD);GameView.tiles[snake.get(0).getX()][snake.get(0).getY()].setType(Tile.TYPE_BODY);snake.add(0, GameView.tiles[snake.get(0).getX() + 1][snake.get(0).getY()]);snake.get(snake.size()-1).setType(Tile.TYPE_NULL);snake.remove(snake.size() - 1);snake.get(snake.size()-1).setType(Tile.TYPE_TAIL);break;}break;case NextEvent.UP:switch (GameView.tiles[snake.get(0).getX()][snake.get(0).getY()-1].getType()){case Tile.TYPE_WALL:case Tile.TYPE_BODY:case Tile.TYPE_TAIL:isAlive = false;break;case Tile.TYPE_FOOD:GameView.tiles[snake.get(0).getX()][snake.get(0).getY()-1].setType(Tile.TYPE_HEAD);GameView.tiles[snake.get(0).getX()][snake.get(0).getY()].setType(Tile.TYPE_BODY);snake.add(0, GameView.tiles[snake.get(0).getX()][snake.get(0).getY()-1]);GameView.isEatFood = true;break;case Tile.TYPE_NULL:GameView.tiles[snake.get(0).getX()][snake.get(0).getY()-1].setType(Tile.TYPE_HEAD);GameView.tiles[snake.get(0).getX()][snake.get(0).getY()].setType(Tile.TYPE_BODY);snake.add(0, GameView.tiles[snake.get(0).getX()][snake.get(0).getY()-1]);snake.get(snake.size()-1).setType(Tile.TYPE_NULL);snake.remove(snake.size()-1);snake.get(snake.size()-1).setType(Tile.TYPE_TAIL);break;}break;case NextEvent.DOWN:switch (GameView.tiles[snake.get(0).getX()][snake.get(0).getY()+1].getType()){case Tile.TYPE_WALL:case Tile.TYPE_BODY:case Tile.TYPE_TAIL:isAlive = false;break;case Tile.TYPE_FOOD:GameView.tiles[snake.get(0).getX()][snake.get(0).getY()+1].setType(Tile.TYPE_HEAD);GameView.tiles[snake.get(0).getX()][snake.get(0).getY()].setType(Tile.TYPE_BODY);snake.add(0, GameView.tiles[snake.get(0).getX()][snake.get(0).getY() + 1]);GameView.isEatFood = true;break;case Tile.TYPE_NULL:GameView.tiles[snake.get(0).getX()][snake.get(0).getY()+1].setType(Tile.TYPE_HEAD);GameView.tiles[snake.get(0).getX()][snake.get(0).getY()].setType(Tile.TYPE_BODY);snake.add(0, GameView.tiles[snake.get(0).getX()][snake.get(0).getY()+1]);snake.get(snake.size()-1).setType(Tile.TYPE_NULL);snake.remove(snake.size()-1);snake.get(snake.size()-1).setType(Tile.TYPE_TAIL);break;}break;}}public void setDir(int dir){if (this.dir == dir||this.dir == -dir||dir == NextEvent.QUIET)return;elsethis.dir = dir;}public boolean isAlive(){return isAlive;}public int getLength(){return snake.size();}
}

NextEvent.java

就是刚开始介绍的那个类,这里就不重复贴出了

下一个写俄罗斯方块,有兴趣的可以添加我的公众号xinshouit,更新会在里面通知。

【Android 2d游戏开发(3)】——贪吃蛇(基于surfaceview框架,精讲)相关推荐

  1. Win32 游戏开发:贪吃蛇 上篇

    1)贪吃蛇游戏介绍(摘抄自百度百科) 1976年,Gremlin平台推出了一款经典街机游戏Blockade.游戏中,两名玩家分别控制一个角色在屏幕上移动,所经之处砌起围栏.角色只能向左.右方向90度转 ...

  2. laya游戏开发之贪吃蛇大作战(一)

    laya游戏开发之贪吃蛇大作战 一.背景 二.引擎选择 三.整体架构 3.1 玩法分析 3.2 游戏架构 3.3 技术选型 一.背景 需要快速实现一个贪吃蛇的 demo 以验证功能,非传统贪吃蛇玩法, ...

  3. 【Android 2d 游戏开发(2)】——2048

    实现效果 : 代码结构: 完整代码: MainActivity: package com.next.next2048;import android.support.v7.app.AppCompatAc ...

  4. Win32 游戏开发:贪吃蛇 下篇

    因为个人原因更新来晚了,向各位关注的读者道个歉... 前排提示: 文章非常的长!!!(如有不懂的请在文章下方评论) 6)开始码代码啦 经过上面的前期工作,我们应该更清楚我们要做怎么做了(至少有一个方向 ...

  5. laya游戏开发之贪吃蛇大作战(二)—— 贪吃蛇客户端

    文章目录 一 功能分析 二 实现方案 1. 代码结构 2. 关键函数实现 2.1 游戏主循环(GameLoop) 2.2 数据层(Model) 2.3 画面绘制层(View) 帧同步的困难与解决方法 ...

  6. c语言游戏开发之贪吃蛇

    一.建立背景 这里可以运用界面函数initgraph(a,b)来建立,其头文件为<grapgics.h>. 注意:为了防止卡退需要使用while(1){ }来一直循坏. #include& ...

  7. 【Android 2D 游戏开发(5)】——九宫格拼图(苍老师版)

    效果: 开始界面: 3x3: 4x4: 5x5: 拼图成功后苍老师对你表示爱意.. 图片切割方法 有些朋友看到这篇文章可能会想知道怎么切割图片(虽然我做这个游戏并没有切割图片..),我就把切割图片的方 ...

  8. android 实现 3d 文字,android 3d 游戏 开发 基础 第10课-2D文字显示.ppt

    <android 3d 游戏 开发 基础 第10课-2D文字显示.ppt>由会员分享,可在线阅读,更多相关<android 3d 游戏 开发 基础 第10课-2D文字显示.ppt(1 ...

  9. 开发Windows贪吃蛇游戏——(二)代码实现

    目录 前言 窗体部分 Frame部分 声明全局变量 Panel的初始化 startGame方法 paintComponent方法 drawGame方法 newSnake方法 newFood方法 mov ...

最新文章

  1. 获取基目录,它由程序集冲突解决程序用来探测程序集
  2. java 转型概念_转型(java)(.net)
  3. Trigger4Orchard
  4. c++输出lpbyte类型_变量类型摸不清脉络?用typeid()试试,用一次就上瘾
  5. 网件使用计算机mac地址吗,找回网件R7000消失的无线MAC过滤功能
  6. 2012年1月份第2周51Aspx源码发布详情
  7. phpUnit 安装,实例和简单部署
  8. c++ 按键暂停继续 程序_加工中心程序代码 M30 M03 M04 M05 M07 M08 M09 S F R IJK
  9. H5调用手机摄像头拍照,如何压缩后上传
  10. 微软拿下美国国防部100亿美元云合同,亚马逊不干了...
  11. 你经历过最奇特的梦境是怎样的?
  12. Bailian4071 查找出现了k次的字符【字符串】
  13. python中用正则表达式检查密码是否安全
  14. 微信小程序地图点聚合
  15. 30种大脑训练方法:提高你的注意力(修订本) - 目录
  16. 整理一波常用中文文字集合(去重的文字最多中文:20902个文字+ 常用的中文文字3900+)
  17. 三思推荐的育儿书~~~
  18. 老闪创业那些事儿(55)——员工的私生活问题
  19. Unity统计代码行数脚本
  20. java获取剩余手机电池容量_怎样判断手机电池的剩余容量

热门文章

  1. Python函数之六:递归函数
  2. 编译flink-cdc-connectors
  3. 毕业生新手如何入门新媒体运营岗位
  4. mac 大型垃圾手动清理 无需安装软件
  5. 如何利用计算机,怎样利用计算机学习
  6. react-native 错误
  7. 2022年最高效,踏实的Python学习笔记以及Python学习规划
  8. API——常见的互联网实名认证方式
  9. 安卓网apk安装器_安卓APP推荐:假日时间规划师,定时音乐播放器,最小的APK提取器...
  10. 连接共享打印机输入用户名和密码——解决办法