如何運用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進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。