如何运用surfaceviewAndroid进行2d游戏贪吃蛇的开发
发表于:2025-01-20 作者:千家信息网编辑
千家信息网最后更新 2025年01月20日,如何运用surfaceviewAndroid进行2d游戏贪吃蛇的开发,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。我们先看一下这个同
千家信息网最后更新 2025年01月20日如何运用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(){ ArrayListnullList = 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 ArrayListsnake; 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安全错误
数据库的锁怎样保障安全
配送中心分拣软件开发
交个朋友武汉互联网科技有限公司
如何用数据库求一年
数据库mpp选型
宁夏青鹏网络技术服务有限公司
数据库表名有用户名
城阳区ios软件开发企业
吞吐数据库
软件开发参考文献m ol
泸县游戏软件开发
cbm数据库简答题
上海正规网络技术分类五星服务
在日本无服务器
佰悦网络技术发展有限公司
GE软件开发
网络安全宣传句
mysql数据库运用例子
软件开发时间估算表
小鸟云服务器怎么获取
以c语言为主的软件开发
盐城网络技术渠道招商
数据库中创建表的指令是什么
kettle建数据库连接
杭州计算机应用软件开发公司
河南软件开发者单位
国际服服务器1.17.10推荐
网络安全应具备四个特征
教育部小学生网络安全知识
在数据库查看目录
闵行区通讯网络技术五星服务