如何运用surfaceviewAndroid进行2d游戏贪吃蛇的开发
发表于:2026-11-03 作者:千家信息网编辑
千家信息网最后更新 2026年11月03日,如何运用surfaceviewAndroid进行2d游戏贪吃蛇的开发,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。我们先看一下这个同
千家信息网最后更新 2026年11月03日如何运用surfaceviewAndroid进行2d游戏贪吃蛇的开发
如何运用surfaceviewAndroid进行2d游戏贪吃蛇的开发,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。
我们先看一下这个同步框架,看看骚年你能不能自己领悟。
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(); } @Override public 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); } //这里就是同步触发机制了 //每一个时钟周期,执行一次逻辑方法和绘图方法 @Override public void run() { while(flag){ mLogic(); mDraw(); try { Thread.sleep(500); }catch (Exception e){ e.printStackTrace(); } } } //SurfaceView创建时调用 @Override public void surfaceCreated(SurfaceHolder holder) { thread.start(); } //SurfaceView发生改变时调用 @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } //SurfaceView销毁时调用 @Override public 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 { @Override protected 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 nullList = new ArrayList(); 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); } } @Override public 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); } @Override public void run() { while(flag){ mLogic(); mDraw(); try { Thread.sleep(500); }catch (Exception e){ e.printStackTrace(); } } } @Override public void surfaceCreated(SurfaceHolder holder) { thread.start(); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { } @Override public 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 snake; private int dir; private boolean isAlive; public Snake(Tile head,Tile body,Tile tail,int dir){ snake = new ArrayList(); 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; else this.dir = dir; } public boolean isAlive(){ return isAlive; } public int getLength(){ return snake.size(); }} NextEvent.java
就是刚开始介绍的那个类,这里就不重复贴出了
关于如何运用surfaceviewAndroid进行2d游戏贪吃蛇的开发问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注行业资讯频道了解更多相关知识。
逻辑
就是
方法
框架
事件
控制
输入
代码
时调
机制
问题
同步
循环
开发
平滑
内容
句柄
只是
周期
屏幕
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
互联网科技企业
互联网科技海报
百单网互联网科技有限公司
西安纸贵互联网科技有限公司
苏州找大状互联网科技有限公司
互联网科技工作委员会毛军
互联网金融监管科技数据
与互联网科技公司
北京互联网科技公司
致骋互联网科技
天津摩尔互联网科技
猫创互联网科技
天扬科技互联网广告
互联网科技公司招聘
北京华百购互联网科技有限公司
米聚互联网科技南京有限公司排面
滨湖区互联网智能科技产品有哪些
互联网时代的科技公司
互联网科技收购价值
高科技和互联网的现代国家
科技互联网
易互联网科技有限公司
时间互联网络科技
掌上梦互联网科技
老年人脱离互联网科技的作文
电子科技大学互联网创业大赛
国大互联网教育科技有限公司工作
西安小鹰速贷互联网科技有限公司
三亚科技创新互联网基地
亚光科技低轨卫星互联网