Android 教你完成最简单的贪吃蛇示例

Android 教你完成最简单的贪吃蛇示例

先来看下实际效果图

在这里插入图片描述

先来初始化下

    init {
        biankuangPaint = Paint()
        biankuangPaint!!.strokeWidth = 1f
        biankuangPaint!!.color = context?.let { ContextCompat.getColor(it, R.color.w66) }!!
        foodPaint = Paint()
        foodPaint!!.color = ContextCompat.getColor(context, R.color.colorAccent)
        snackPaint = Paint()
        snackPaint!!.color = ContextCompat.getColor(context, R.color.snake_color)
        val bitmap = BitmapFactory.decodeResource(resources, R.mipmap.snake1)
        snakeHead = ImageUtils.resizeBitmap(bitmap, foodWidth - 2 * wcharJaine, foodWidth - 2 * wcharJaine)
        val bitmap2 = BitmapFactory.decodeResource(resources, R.mipmap.mushroom)
        val mushroom = ImageUtils.resizeBitmap(bitmap2, foodWidth - 2 * wcharJaine, foodWidth - 2 * wcharJaine)
        val bitmap3 = BitmapFactory.decodeResource(resources, R.mipmap.qingwa)
        val quinta = ImageUtils.resizeBitmap(bitmap3, foodWidth - 2 * wcharJaine, foodWidth - 2 * wcharJaine)
        val bitmap4 = BitmapFactory.decodeResource(resources, R.mipmap.mouse)
        val mouse = ImageUtils.resizeBitmap(bitmap4, foodWidth - 2 * wcharJaine, foodWidth - 2 * wcharJaine)
        foodBitmapArray[0] = mushroom
        foodBitmapArray[1] = quinta
        foodBitmapArray[2] = mouse
        currentFoodBitmap = foodBitmapArray[0]
        for (i in currentXIndex downTo 1) {
            pjPoints?.add(PjPoint(i, 1))
        }
    }

由于贪吃蛇是用手指点击操作的,所以要对屏幕点击做处理(判断当前手指距离蛇头点击是横向多点还是纵向多点)

   override fun onTouchEvent(event: MotionEvent): Boolean {
        val action = event.action
        if (action == MotionEvent.ACTION_DOWN) {
            val pressX = event.x.toInt()
            val pressY = event.y.toInt()
            val jiangeX = pressX - currentXIndex * foodWidth
            val jiangeY = pressY - currentYIndex * foodWidth
            direction = if (Math.abs(jiangeX) > Math.abs(jiangeY)) {
                if (jiangeX > 0) {
                    if (currentDirection == DirectEnum.leftEnum.ordinal) DirectEnum.leftEnum.ordinal else DirectEnum.rightEnum.ordinal
                } else {
                    if (currentDirection == DirectEnum.rightEnum.ordinal) DirectEnum.rightEnum.ordinal else DirectEnum.leftEnum.ordinal
                }
            } else {
                if (jiangeY > 0) {
                    if (currentDirection == DirectEnum.topEnum.ordinal) DirectEnum.topEnum.ordinal else DirectEnum.bottomEnum.ordinal
                } else {
                    if (currentDirection == DirectEnum.bottomEnum.ordinal) DirectEnum.bottomEnum.ordinal else DirectEnum.topEnum.ordinal
                }
            }
            currentDirection = direction
        }
        return true
    }

然后就是测量,主要是测量屏幕大小和横竖网格所占的多少

   override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        measureWidth = measuredWidth
        measureHeight = measuredHeight
        allXIndex = measureWidth / foodWidth
        measureWidth = allXIndex * foodWidth
        allYIndex = measureHeight / foodWidth
        measureHeight = allYIndex * foodWidth
    }

画边框,蛇,食物

   override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        //初始化边框
        for (i in 0..allXIndex) {
            biankuangPaint?.let {canvas.drawLine((foodWidth * i).toFloat(), 0f, (foodWidth * i).toFloat(), measureHeight.toFloat(),it)}
        }
        for (i in 0..allYIndex) {
            biankuangPaint?.let {canvas.drawLine(0f, (foodWidth * i).toFloat(), measureWidth.toFloat(), (foodWidth * i).toFloat(),it)}
        }
        //初始化蛇
        drawSnack(canvas)
        //画食物
        if (foodFlag) {
            drawFood(canvas, foodPjPoint)
        }
    }

    //画蛇
    private fun drawSnack(canvas: Canvas) {
        for (i in pjPoints!!.indices) {
            val pjPoint = pjPoints!![i]
            if (i == 0) { //画蛇头(图片)
                canvas.drawBitmap(snakeHead!!, (foodWidth * pjPoint.x).toFloat(), (pjPoint.y * foodWidth).toFloat(), foodPaint)
            } else {  //画蛇身(基础圆)
                canvas.drawCircle((pjPoint.x * foodWidth + foodWidth / 2).toFloat(), (pjPoint.y * foodWidth + foodWidth / 2).toFloat(), (foodWidth / 2).toFloat(), snackPaint!!)
            }
        }
    }

    //画食物
    private fun drawFood(canvas: Canvas, pjPoint: PjPoint) {
        canvas.drawBitmap(currentFoodBitmap!!, (foodWidth * pjPoint.x).toFloat(), (pjPoint.y * foodWidth).toFloat(), foodPaint)
    }

结束的标志,撞了自己和墙

   //判断蛇头是否撞向了自己
    private fun isContainPoint(pjPoint: PjPoint): Boolean {
        val size = pjPoints!!.size
        for (i in 0 until size) {
            val pjPoint1 = pjPoints!![i]
            if (isContainFood(pjPoint, pjPoint1)) {
                return true
            }
        }
        return false
    }

    //判断蛇头是否吃了食物
    private fun isContainFood(pjPoint: PjPoint, pjPoint1: PjPoint): Boolean {
        return pjPoint.x == pjPoint1.x && pjPoint.y == pjPoint1.y
    }

随机生成食物(如果生成的食物在蛇身上,则过滤继续生成食物)

  private val randomFoodSnackPoint: PjPoint
        get() {
            var pjPoint: PjPoint
            while (true) {
                val randomX = (Math.random() * allXIndex).toInt()
                val randomY = (Math.random() * allYIndex).toInt()
                pjPoint = PjPoint(randomX, randomY)
                if (isContainPoint(pjPoint)) {
                    continue
                } else {
                    break
                }
            }
            return pjPoint
        }

更新蛇集合的数据和食物的标志(这部分是贪吃蛇的核心代码)

   private fun updateSnackInfo() {
        val headPjPoint = PjPoint(currentXIndex, currentYIndex)
        if (isContainFood(headPjPoint, foodPjPoint)) {
            pjPoints!!.add(0, headPjPoint)
            foodFlag = false
            foodPjPoint = randomFoodSnackPoint
            val random = Random()
            currentFoodBitmap = foodBitmapArray[random.nextInt(3)]
            currentScore++
            onGetScore!!.onGetScoreInfo(currentScore)
        } else {
            foodFlag = true
            val size = pjPoints!!.size
            //1,2,3,4,5,6
            //x,1,2,3,4,5 (核心算法)
            for (i in size - 2 downTo 0) {
                pjPoints!![i + 1] = pjPoints!![i]
            }
            pjPoints!![0] = headPjPoint
        }
        invalidate()
    }

下面最精彩的部分来了,线程跑起来,不断的更新蛇的所以点的集合(遇到食物吃掉增加长度,撞墙或者自己结束战斗)

   //开始移动
    fun startMoveSnack() {
        currentScore = 0
        onGetScore!!.onGetScoreInfo(currentScore)
        initMoveSnack()
        snackThread = Thread(snackRunnable)
        snackThread!!.start()
    }

    private val snackRunnable = Runnable {
        while (true) {
            try {
                if (isStop) {
                    break
                }
                Thread.sleep(snackSpeed)
                when(direction){
                    DirectEnum.rightEnum.ordinal -> currentXIndex++
                    DirectEnum.bottomEnum.ordinal -> currentYIndex++
                    DirectEnum.leftEnum.ordinal -> currentXIndex--
                    DirectEnum.topEnum.ordinal -> currentYIndex--
                }
                mainHandler.sendEmptyMessage(DirectEnum.topEnum.ordinal)
            } catch (e: InterruptedException) {
                e.printStackTrace()
            }
        }
    }

主线程更新ui

  private val mainHandler: Handler = @SuppressLint("HandlerLeak")
    object : Handler(Looper.getMainLooper()) {
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            val what = msg.what
            if (what == DirectEnum.gameOverQiang.ordinal) {
                showToast("撞墙了游戏结束")
            } else if (what == DirectEnum.gameOverself.ordinal) {
                showToast("撞着自己了游戏结束")
            }
            updateSnackInfo()
        }
    }

其实贪吃蛇的原理的就是记录蛇头的位置就行了, 不断的改变蛇头, 然后把后面的部分覆盖前面的就行了, 说到底就是每次运动后的蛇所有点集合的改变, 如果对你感兴趣有用的可以点个赞,或者关注

  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值