您好,登錄后才能下訂單哦!
如何運用surfaceviewAndroid進行2d游戲貪吃蛇的開發,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。
我們先看一下這個同步框架,看看騷年你能不能自己領悟。
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的方法,在里面我封裝了上個游戲用到的滑動手勢,在這個挨踢圈里,人們最常說的一句話就是:不要重復造輪子。當我們看到寫了很多重復代碼時,就是我們需要精簡的時候了。
上代碼:
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,并由此改變游戲邏輯。這種就是同步機制,而用戶輸入事件游戲邏輯就隨之改變的就是異步機制。
下面我們用這個框架做一個貪吃蛇游戲,效果圖如下:
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)); } }
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); } } @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; } }
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; } }
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; else this.dir = dir; } public boolean isAlive(){ return isAlive; } public int getLength(){ return snake.size(); } }
NextEvent.java
就是剛開始介紹的那個類,這里就不重復貼出了
關于如何運用surfaceviewAndroid進行2d游戲貪吃蛇的開發問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。