千家信息网

如何运用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(){        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游戏贪吃蛇的开发问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注行业资讯频道了解更多相关知识。

0