kotlin版贪吃蛇小游戏

kotlin版贪吃蛇小游戏

效果图

TIM截图20190903095435.png

最近一直在搞小程序,之前学过的kotlin都快忘光了。借此采用kotlin写了个贪吃蛇的demo,来回顾下kotlin的语法。

分析

贪吃蛇游戏由食物(红色小块)和小蛇(绿色小块)组成。其中需要明确的问题如下:

  1. 小蛇吃到食物后,食物在新的坐标重新出现(坐标需在显示区内)

  2. 小蛇吃到食物后身体变长(长度为食物的长度)

  3. 蛇身应由一个个小正方形组成大小与食物一样

  4. 蛇头和蛇尾不能调转即蛇头方向向左则无法再向右移动

  5. 小蛇在拐弯时,蛇身跟随蛇头转向即蛇身到达蛇头所在位置时如果与蛇头方向不一直则改变蛇身方向

  6. 小蛇碰撞到墙壁(蛇头不在显示区域内)或蛇头碰撞到蛇身则游戏结束

代码


class Food() : View{
    companion object {
        val widthAndHeight = 20
    }

    var isShow: Boolean = true
    var color: Color = Color.RED
    var x: Int = 0;
    var y: Int = 0;


    override fun draw(g: Graphics?) {
        g!!.color = if(isShow) color else null
        isShow = !isShow
        g.fillRect(x,y,widthAndHeight,widthAndHeight)
    }

    /**
     * 生成食物
     */
    fun generateFood(cellList: ArrayList<Snake.Cell>){
        while(true){
            val point = GameUtils.generatePosition()
            var isSuccess = true;
            for(cell in cellList){
                if(cell.x == point.x && cell.y == point.y){ //排除小蛇的坐标
                    isSuccess = false
                }
            }
            if(isSuccess){
                x = point.x
                y = point.y
                break
            }
        }
    }

}

class Snake() : View{
    enum class Direction{ //方向枚举
        TOP,RIGHT,BOTTOM,LEFT
    }

    var cellList: ArrayList<Cell> = ArrayList() //组成蛇的小正方形列表
    var color: Color = Color.GREEN //蛇的颜色

    private var inflectionPoint: ArrayList<Cell> = ArrayList() //记录发生转向的蛇头

    init {
        initCellList()
    }

    fun initCellList(){
        cellList.clear()
        cellList.add(Cell(0,0, Direction.LEFT)) //添加蛇头
    }

    /**
     * 检查是否成功进食,是=增加蛇身长度,否则不做处理
     */
    fun tryEat(food: Food): Boolean{
        if(cellList[0].x == food.x && cellList[0].y == food.y){
            val lastCell = cellList[cellList.size - 1];
            when(lastCell.direction){
                Direction.LEFT-> cellList.add(Cell(lastCell.x + Food.widthAndHeight,lastCell.y,lastCell.direction))
                Direction.RIGHT-> cellList.add(Cell(lastCell.x - Food.widthAndHeight,lastCell.y,lastCell.direction))
                Direction.TOP-> cellList.add(Cell(lastCell.x,lastCell.y + Food.widthAndHeight,lastCell.direction))
                Direction.BOTTOM-> cellList.add(Cell(lastCell.x,lastCell.y - Food.widthAndHeight,lastCell.direction))
            }
            return true
        }
        return false
    }

    fun walk(){
        for(index in cellList.indices){
            val cell:Cell = cellList[index]
            if(index != 0){
                for(pCell in inflectionPoint){//判断蛇身的每一个正方形是否经过蛇头转向点,是改变方向
                    if(cell.x == pCell.x && cell.y == pCell.y){
                        cell.direction = pCell.direction
                        println("change direction")
                        if(index == cellList.size-1){//当蛇尾也经过蛇头转向点时,从列表删除转向信息
                            inflectionPoint.remove(pCell)
                        }
                        break;
                    }
                }
            }


            when(cell.direction){//根据方向修改坐标,进行移动操作
                Snake.Direction.TOP-> cell.y -= Food.widthAndHeight
                Snake.Direction.RIGHT-> cell.x += Food.widthAndHeight
                Snake.Direction.BOTTOM-> cell.y += Food.widthAndHeight
                Snake.Direction.LEFT-> cell.x -= Food.widthAndHeight
            }
        }
    }

    /**
     * 根据输入的#{switchDirection}进行转向操作
     * @param switchDirection 方向枚举
     */
    fun switchDirection(switchDirection:Snake.Direction){
        val cDirection = cellList[0].direction
        when(switchDirection){
            Snake.Direction.LEFT->{
                if(cDirection != Snake.Direction.RIGHT){
                    cellList[0].direction = Snake.Direction.LEFT
                }
            }

            Snake.Direction.TOP->{
                if(cDirection != Snake.Direction.BOTTOM){
                    cellList[0].direction = Snake.Direction.TOP
                }
            }

            Snake.Direction.RIGHT->{
                if(cDirection != Snake.Direction.LEFT){
                    cellList[0].direction = Snake.Direction.RIGHT
                }
            }

            Snake.Direction.BOTTOM->{
                if(cDirection != Snake.Direction.TOP){
                    cellList[0].direction = Snake.Direction.BOTTOM
                }
            }
        }
        if(cellList.size > 1){
            inflectionPoint.add(Cell(cellList[0].x,cellList[0].y,cellList[0].direction))
        }
    }

    /**
     * 获取蛇头
     */
    fun getHeaderCell(): Cell{
        return cellList[0]
    }

    fun getCellCount(): Int{
        return cellList.size
    }

    /**
     * 是否咬到自己(蛇头碰撞到蛇身)
     */
    fun biteYourself(): Boolean{
        for(index in cellList.indices){
            if(index != 0){
                val cell = cellList[index]
                if(getHeaderCell().x == cell.x && getHeaderCell().y == cell.y){
                   return true
                }
            }
        }
        return false
    }

    fun generatePosition(){
        val point = GameUtils.generatePosition()
        cellList[0].x = point.x
        cellList[0].y = point.y
        if(point.x <= Food.widthAndHeight * 2){
            cellList[0].direction = Direction.RIGHT
        }
    }

    override fun draw(g: Graphics?) {
        g!!.color = color
        for(cell in cellList){
            g.fillRect(cell.x,cell.y,cell.width,cell.height)
        }
    }

    /**
     * 蛇的每一节,每吃一个食物涨一节
     */
    data class Cell(var x:Int,var y:Int,var direction: Direction,var width: Int = Food.widthAndHeight,var height: Int = Food.widthAndHeight)
}


/**
 *  游戏主界面
 */
class MainFrame(width:Int,height:Int) : DoubleBufferFrame(width,height), KeyListener {
    var isConfirmStart = false; //是否确认开始
    var food: Food = Food()
    var snake: Snake = Snake()

    init {
        initData()
        title = "贪吃蛇"
        addKeyListener(this)
        if(JOptionPane.showOptionDialog(this,"点击确定开始游戏","提示",JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE,null,null,null) == 0){
            isConfirmStart = true
        }
    }

    fun initData(){
        food.generateFood(snake.cellList)
        snake.generatePosition()
    }

    override fun updateAttr() {
        if(!isConfirmStart){
            return
        }
        if(isOver()){
            isStopDrawThread = true;
            if(JOptionPane.showOptionDialog(this,"游戏结束,得分${(snake.getCellCount()-1) * SCORE},是否重新开始","提示",JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE,null,null,null) == 0){
                snake.initCellList()
                initData()
                isStopDrawThread = false;
            }
            return
        }
        if(snake.tryEat(food)){
            food.generateFood(snake.cellList)
        }
        snake.walk()
    }

    /**
     * 蛇撞墙或着咬到自己则返回true,游戏结束
     */
    fun isOver(): Boolean{
        return isCrashWall() || snake.biteYourself()
    }

    /**
     * 蛇是否碰撞到墙
     */
    fun isCrashWall(): Boolean{
        val cell:Snake.Cell = snake.getHeaderCell();
        return cell.x < contentPane.x || cell.x + cell.width > contentPane.width || cell.y < contentPane.y || cell.y + cell.height > contentPane.height
    }


    override fun dPaint(g: Graphics?) {
        food.draw(g)
        snake.draw(g)
    }

    override fun keyTyped(e: KeyEvent?) {

    }

    override fun keyPressed(e: KeyEvent?) {
        isStopDrawThread = true

        when(e!!.keyCode){
            37-> snake.switchDirection(Snake.Direction.LEFT)
            38-> snake.switchDirection(Snake.Direction.TOP)
            39-> snake.switchDirection(Snake.Direction.RIGHT)
            40-> snake.switchDirection(Snake.Direction.BOTTOM)
        }
        repaint()
    }

    override fun keyReleased(e: KeyEvent?) {
        isStopDrawThread = false
    }

}

DoubleBufferFrame类采用双缓冲技术解决闪烁问题,主要思路是先将需绘制的场景绘制到内存,再从内存绘制到窗体上

const val BLOCK_LENGTH:Int = 20
const val SCORE:Int = 10 //每吃一个食物的得分
const val DIFFICULTY:Int = 5 //难度 1-10

fun main(args: Array<String>){
    MainFrame(Food.widthAndHeight * BLOCK_LENGTH,Food.widthAndHeight * BLOCK_LENGTH)
}

总结

贪吃蛇的实现并不难,几个类就能实现了。估计也就只有分析中的第5点需要稍微思考下。有兴趣的朋友可以拓展下。附上源码地址: https://github.com/aii1991/GluttonousSnake

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值