Android实现贪吃蛇游戏一:游戏界面及控制

贪吃蛇游戏是一款经典休闲游戏,本文将在android上实现经典的贪吃蛇游戏,操作界面和以前那些游戏机一样。


游戏空间界面

该游戏的实现思路主要是在一个View中将贪吃蛇的游戏界面绘制出来,游戏空间由16*32个小方块组成,使用一个LinkedList记录当前蛇的位置和用一个变量记录食物的位置。

private int snakeFood; // 记录当前食物的位置,横坐标*100+纵坐标
private LinkedList<Integer> snakeArray; // 记录蛇的位置信息,每一节的坐标和食物的一致,横坐标*100+纵坐标
新建SnakeSpace继承自View。在onDraw方法中绘制游戏空间的界面,如下代码:
    @Override
    protected void onDraw(Canvas canvas) {
        int width = getWidth();
        int height = getHeight();
        int dx = width / 16;
        Paint paint = new Paint();
        paint.setStrokeWidth(4);
        canvas.drawColor(0xffFCBD00);
        canvas.drawLine(0, 0, width, 0, paint);
        canvas.drawLine(width, 0, width, height, paint);
        canvas.drawLine(0, height, width, height, paint);
        canvas.drawLine(0, 0, 0, height, paint);
        Paint paint2 = new Paint();
        paint2.setColor(0x50000000);

        for (int i = 0; i < 16; i++){
            for (int j = 0; j < 32; j++) {
                if (snakeArray.contains(i*100+j) || snakeFood == (i*100+j)){
                    canvas.drawRect((dx * i) + 1, (dx * j) + 1, (dx * i) + dx - 1, (dx * j) + dx - 1, paint);
                }else{
                    canvas.drawRect((dx * i) + 1, (dx * j) + 1, (dx * i) + dx - 1, (dx * j) + dx - 1, paint2);
                }
            }
        }

        super.onDraw(canvas);
    }

在代码中,首先获取width和height以便后续绘图使用,绘制空间周围的小黑边,然后双重循环绘制控制中的26*32个小方块,这里判断绘制的坐标如果是蛇或者食物,则绘制全黑的方块,否则绘制有透明度的方块。这样的话,在游戏过程中,改变蛇和食物变量的值,即可改变界面显示。

移动蛇

按下界面上的上下左右按键可以让蛇移动,这里将蛇移动的方法暴露出来,通过Dir参数设置移动的方向。

    public static final int DIRECTION_UP = 1;
    public static final int DIRECTION_DOWN = 2;
    public static final int DIRECTION_LEFT = 3;
    public static final int DIRECTION_RIGHT = 4;
    private int direction;
    public void move(int dir){
        if ((dir <= 2 && direction <= 2) || (dir > 2 && direction > 2)){
            return;
        }
        direction = dir;
    }
    public void move(){
        int firstIndex = snakeArray.getFirst();
        switch (direction){
            case DIRECTION_LEFT:
                if (firstIndex >= 100){
                    firstIndex -= 100;
                }else{
                    return;
                }
                break;
            case DIRECTION_UP:
                if (firstIndex%100 > 0){
                    firstIndex -= 1;
                }else{
                    return;
                }
                break;
            case DIRECTION_RIGHT:
                if (firstIndex < 1500){
                    firstIndex += 100;
                }else{
                    return;
                }
                break;
            case DIRECTION_DOWN:
                if (firstIndex%100 < 31){
                    firstIndex += 1;
                }else{
                    return;
                }
                break;
        }
        snakeArray.addFirst(firstIndex);
        if (firstIndex == snakeFood){
            showFood();
        }else{
            snakeArray.removeLast();
        }
        postInvalidate();
    }

代码中主要是设置蛇头和蛇尾的位置,因为每一次移动只移动一步,所以都是只需要改变蛇头和蛇尾而已。在该方法中做了食物处理,即蛇吃到食物后,随机显示下一个食物,蛇身变长。

随机显示食物

    private void showFood(){
        int x = (int) Math.round(Math.random() * 16);
        int y = (int) Math.round(Math.random() * 32);
        // 该方法存在一个很大的BUG,即可能在生成食物时会占用大量的时间甚至进入死循环,即游戏空间上已经没有合适的位置了。后续的文章中将修改这个。
        while (snakeArray.contains(x*100 + y)){ // 判断直到随机产生的位置是一个空位置,
            x = (int) Math.round(Math.random() * 16);
            y = (int) Math.round(Math.random() * 32);
        }
        Log.e("SnakeSpace", "food pos = " + snakeFood);
        snakeFood = x*100 + y;
    }

使用贪吃蛇View

在MainActivity的布局文件中引入我们创建的贪吃蛇View,和上下左右四个按键。

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.qcymall.snake.MainActivity">

    <!--<SurfaceView-->
        <!--android:id="@+id/glv_main_demo"-->
        <!--android:layout_width="match_parent"-->
        <!--android:layout_height="match_parent" />-->


    <com.qcymall.snake.SnakeSpace
        android:id="@+id/snake_view"
        android:layout_width="128dp"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>

    <Button
        android:id="@+id/up_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="上"
        android:onClick="upMove"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintBottom_toTopOf="@id/down_btn"/>
    <Button
        android:id="@+id/down_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="下"
        android:onClick="downMove"
        app:layout_constraintLeft_toLeftOf="@id/up_btn"
        app:layout_constraintBottom_toBottomOf="parent"/>
    <Button
        android:id="@+id/left_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="左"
        android:onClick="leftMove"
        app:layout_constraintTop_toTopOf="@id/down_btn"
        app:layout_constraintRight_toLeftOf="@id/down_btn"/>
    <Button
        android:id="@+id/right_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="右"
        android:onClick="rightMove"
        app:layout_constraintTop_toTopOf="@id/down_btn"
        app:layout_constraintLeft_toRightOf="@id/down_btn"/>
</android.support.constraint.ConstraintLayout>

在MainActivity中的使用代码如下:

class MainActivity : AppCompatActivity() {
    private val glRenderer: GLRenderer? = null
    private lateinit var snake: SnakeSpace

    private var mTimer: Timer? = null
    private var mTimerTask: TimerTask? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        snake = findViewById(R.id.snake_view)
        startTimer()
    }

    private fun startTimer(){
        stopTimer()
        mTimer = Timer()
        mTimerTask = object : TimerTask(){
            override fun run() {
                snake.move()
            }
        }
        mTimer!!.schedule(mTimerTask, 1000, 500)
    }

    private fun stopTimer(){
        if (mTimer != null){
            mTimer!!.cancel()
            mTimer = null
        }
        if (mTimerTask != null){
            mTimerTask!!.cancel()
            mTimerTask = null
        }
    }
    public fun upMove(view: View){
        snake.move(SnakeSpace.DIRECTION_UP)
    }
    public fun downMove(view: View){
        snake.move(SnakeSpace.DIRECTION_DOWN)
    }
    public fun leftMove(view: View){
        snake.move(SnakeSpace.DIRECTION_LEFT)
    }
    public fun rightMove(view: View){
        snake.move(SnakeSpace.DIRECTION_RIGHT)
    }
代码主要思路是使用定时器控制游戏速度,上下左右按键改变蛇的移动方向。

至此,已经实现了一个简单的可以移动的贪吃蛇游戏雏形了,项目源码可参考Github项目。

https://github.com/dgutkai/RetroSnake.git

https://github.com/dgutkai/RetroSnake.gi

猜你喜欢

转载自blog.csdn.net/linzekai100/article/details/80582116