JS贪吃蛇小游戏实现

一、实现功能

配置化制定,小蛇移动,小蛇死亡,分数统计,暂停游戏,继续游戏。

二、结果展示

在这里插入图片描述

三、开始制作

1、配置并计算数据

初始化配置数据,如游戏界面宽高,小蛇初始长度,游戏移动时间间隔,并按照界面宽高计算出小蛇上下左右移动以及穿墙步进值。

配置数据

    //蛇蛇初始长度
    let snake_initial_length = 3;
    //游戏界面宽度
    let view_width = 40;
    //游戏界面高度
    let view_height = 30;
    // 移动时间毫秒
    let move_time = 500;

按照配置数据计算各个步进值

   //上下行动每列距离
    let top_size = view_width;
    //左右行动每列距离
    let left_size = 1;
    //上下穿墙距离
    let top_through_size = view_width * view_height - view_width;
    //左右穿墙距离
    let left_through_size = view_width - 1;

2、搭建页面

按照配置页面宽高生成小蛇移动网格。

  //界面初始化
    function game_view_init() {
        let str = '';
        for (var i = 1; i <= view_height; i++) {

            str += "<div class='div_line'>"
            for (var j = 1; j <= view_width; j++) {
                str += "<div class='view_list'></div>"
            }
            str += "</div>"
        }
        $('#game_view_box').html(str)
    }

3、开始游戏

随机获取小蛇移动方向(1上 2右 3下 4左),并初始化游戏分数,按照移动方向个配置小蛇长度初始化小蛇数据,开启定时器开始移动小蛇和生成食物。

 //开始游戏
    function start_game() {
        //初始定时器
        if (stop_obj) {
            stop_game()
        }
        //初始分数
        game_score = 0;
        $('#game_score').text(game_score);
        //游戏状态
        game_status = true;
        //随机获取移动方向
        move_direction = Math.floor(Math.random() * 4 + 1)
        //获取初始小蛇数据
        let base_number = Math.floor(Math.random() * 600 + 1)
        snake_data = [];
        snake_data.push(base_number)
        if (move_direction == 1) {
            for (let i = 1; i <= snake_initial_length; i++) {
                snake_data.push(base_number + (top_size * i))
            }
        }
        if (move_direction == 2) {
            for (let i = 1; i <= snake_initial_length; i++) {
                snake_data.push(base_number - (left_size * i))
            }
        }
        if (move_direction == 3) {
            for (let i = 1; i <= snake_initial_length; i++) {
                snake_data.push(base_number - (top_size * i))
            }
        }
        if (move_direction == 4) {
            for (let i = 1; i <= snake_initial_length; i++) {
                snake_data.push(base_number + (left_size * i))
            }
        }
        //修改颜色
        edit_color();
        //移动小蛇
        move_snack();
        //创建食物
        create_food();
    }
    //修改小蛇颜色
    function edit_color() {
        $('.view_list').each(function (k, v) {
            let key = k + 1
            if (snake_data.indexOf(key) != -1) {
                $(this).addClass('snack_sel')
            } else {
                if ($(this).hasClass('snack_sel')) {
                    $(this).removeClass('snack_sel')
                }
            }
        })
    }

    //创建食物
    function create_food() {
        if (!food_number) {
            flag = true;
            while (flag) {
                food_number = Math.floor(Math.random() * 900 + 1)
                if (snake_data.indexOf(food_number) == -1) {
                    flag = false;
                }
            }
            $('.view_list').each(function (k, v) {
                let key = k + 1
                if (food_number == key) {
                    $(this).addClass('food_sel')
                } else {
                    if ($(this).hasClass('food_sel')) {
                        $(this).removeClass('food_sel')
                    }
                }
            })

        }
    }

4、移动小蛇并判断吃到食物,判断游戏结束

4.1 判断吃到食物

当小蛇所在数组包含食物所在数时判断吃到食物,并销毁当前食物数据,重新生成食物数据,并且小蛇最后加一位(加的一位为异动前小蛇数据的最后的一位)(这里逻辑有点绕)
判断

4.2 判断游戏结束

当小蛇咬到自己的时候游戏结束,判断逻辑为将小蛇数组排序,判断数组内有相同数的时候游戏结束。

4.3 代码实现

 //移动蛇蛇
    function move_snack() {
        stop_obj = setInterval(function () {

            let old_snack_data = JSON.parse(JSON.stringify(snake_data))

            let next_number = 0
            switch (move_direction) {
                case 1:
                    if (snake_data[0] <= top_size) {
                        next_number = snake_data[0] + top_through_size
                    } else {
                        next_number = snake_data[0] - top_size
                    }
                    break;
                case 2:
                    if (snake_data[0] % view_width == 0) {
                        next_number = snake_data[0] - left_through_size
                    } else {
                        next_number = snake_data[0] + left_size
                    }
                    break;
                case 3:
                    if (snake_data[0] >= top_through_size) {
                        next_number = snake_data[0] - top_through_size
                    } else {
                        next_number = snake_data[0] + top_size
                    }

                    break;
                case 4:
                    if (snake_data[0] % view_width == 1) {
                        next_number = snake_data[0] + left_through_size
                    } else {
                        next_number = snake_data[0] - left_size
                    }
                    break;
            }
            snake_data = snake_data.reverse();
            $.each(snake_data, function (k, v) {
                if (k == snake_data.length - 1) {
                    snake_data[k] = next_number
                } else {
                    snake_data[k] = snake_data[k + 1]
                }
            })
            snake_data = snake_data.reverse();
            if (snake_data.indexOf(food_number) != -1) {
                food_number = ''
                snake_data.push(old_snack_data[old_snack_data.length - 1])
                game_score += 1
                $('#game_score').text(game_score);
                create_food()
            }
            let order_snake_data = JSON.parse(JSON.stringify(snake_data))
            order_snake_data = positive_sort(order_snake_data)
            $.each(order_snake_data, function (k, v) {
                if (k != order_snake_data.length - 1 && order_snake_data[k] == order_snake_data[k + 1]) {
                    alert('游戏结束!最终分数为' + game_score + '!')
                    //清除定时器
                    clearInterval(stop_obj)
                    stop_obj = null
                    //修改游戏状态
                    game_status = false
                    //保持小蛇上一个状态
                    snake_data = old_snack_data
                }
            })

            edit_color()
        }, move_time)
    }
      //一维数组正序排序
    function positive_sort(data) {
        data.sort((x, y) => {
            return x - y
        })
        return data;
    }

    //一维数组倒序排序
    function reverse_sort(data) {
        data.sort((x, y) => {
            return y - x
        })
        return data;
    }

5、操作栏监听以及键盘快捷键监听

暂停游戏,继续游戏,页面方向监听,上下左右键监听以及WASD键监听

    //暂停游戏
    function stop_game() {
        clearInterval(stop_obj)
        stop_obj = null
    }

    //继续游戏
    function continue_game() {
        if (!game_status) {
            alert('游戏已结束!请重新开始!')
            return false;
        }
        move_snack()
    }
//修改方向
    function edit_direction(direction) {
        if (move_direction == 1 && direction == 3) {
            return false;
        }
        if (move_direction == 2 && direction == 4) {
            return false;
        }
        if (move_direction == 3 && direction == 1) {
            return false;
        }
        if (move_direction == 4 && direction == 2) {
            return false;
        }
        move_direction = direction
    }
//监听键盘
    $(document).keydown(function (event) {
        console.log(event.keyCode)
        if ((event.keyCode == 38 || event.keyCode == 87) && move_direction != 3) {
            //上
            move_direction = 1;
        } else if ((event.keyCode == 39 || event.keyCode == 68) && move_direction != 4) {
            //右
            move_direction = 2;
        } else if ((event.keyCode == 40 || event.keyCode == 83) && move_direction != 1) {
            //下
            move_direction = 3;
        } else if ((event.keyCode == 37 || event.keyCode == 65) && move_direction != 2) {
            //左
            move_direction = 4;
        }

    });

三、完整源码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>贪吃蛇--超级小胖子的博客</title>
    <style>
        body {
            display: flex;
            flex-wrap: wrap;
            justify-content: left;
            padding-top: 25px;
            min-width: 1400px;
        }
        .game_view_box {
            width: 1000px;
            display: flex;
            flex-wrap: wrap;
            justify-content: center;
        }
        .operation_view_box {
            width: 380px;
            margin-left: 20px;
        }
        .view_list {
            width: 18px;
            border: 1px solid black;
            height: 18px;
            font-size: 10px;
        }
        .snack_sel {
            background-color: #00ac78;
        }
        .food_sel {
            background-color: #ff4100;
        }
        .div_line {
            width: 100%;
            display: flex;
            flex-wrap: nowrap;
            justify-content: end;
        }
        .game_btn {
            width: 100px;
            height: 30px;
            text-align: center;
            line-height: 30px;
            border-radius: 5px;
            -moz-user-select: none;
            -webkit-user-select: none;
            -ms-user-select: none;
            -khtml-user-select: none;
            user-select: none;
        }
        .game_score {
            width: 80%;
            margin-top: 20px;
        }
        .game_score span {
            font-weight: bold;
            font-size: 20px;
            color: #ff273f;
        }
        .direction_box {
            width: 50%;
            text-align: center;
            margin-top: 100px;
        }
        .direction_box_list {
            width: 100%;
            display: flex;
            flex-wrap: wrap;
            justify-content: center;
            margin-top: 15px;
            -moz-user-select: none;
            -webkit-user-select: none;
            -ms-user-select: none;
            -khtml-user-select: none;
            user-select: none;
        }
    </style>
</head>
<body>
<div class="game_view_box" id="game_view_box">

</div>
<div class="operation_view_box" id="operation_view_box">
    <div class="direction_box">
        <div class="direction_box_list">
            <div style="width: 50%;">
                <div onclick="edit_direction(1)"
                     style="line-height:50px;width: 50px;height: 50px;background-color: #00ac78"></div>
            </div>
        </div>
        <div class="direction_box_list">
            <div style="width: 50%">
                <div onclick="edit_direction(4)"
                     style="line-height:50px;width: 50px;height: 50px;background-color: #00ac78"></div>
            </div>
            <div style="width: 50%">
                <div onclick="edit_direction(2)"
                     style="line-height:50px;width: 50px;height: 50px;background-color: #00ac78"></div>
            </div>
        </div>
        <div class="direction_box_list">
            <div style="width: 50%">
                <div onclick="edit_direction(3)"
                     style="line-height:50px;width: 50px;height: 50px;background-color: #00ac78"></div>
            </div>
        </div>


    </div>
    <div class="game_score">游戏分数:<span id="game_score">0</span></div>
    <div class="game_btn" style="background-color: #00ac78;margin-top: 20px;" onclick="start_game()">开始游戏</div>
    <div class="game_btn" style="background-color: #ff4100;margin-top: 20px;" onclick="stop_game()">停止游戏</div>
    <div class="game_btn" style="background-color: #1806ff;margin-top: 20px;" onclick="continue_game()">继续游戏</div>
</div>

</body>
<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script>
    //1 上  2 右  3  下  4 左
    let move_direction = 1;
    //蛇蛇数据
    let snake_data = [];
    //定时器对象
    let stop_obj = '';
    //食物数据
    let food_number = '';
    //获得分数
    let game_score = 0;

    //蛇蛇初始长度
    let snake_initial_length = 3;

    //游戏界面宽度
    let view_width = 40;
    //游戏界面高度
    let view_height = 30;

    // 移动时间毫秒
    let move_time = 500;

    //上下行动每列距离
    let top_size = view_width;
    //左右行动每列距离
    let left_size = 1;
    //上下穿墙距离
    let top_through_size = view_width * view_height - view_width;
    //左右穿墙距离
    let left_through_size = view_width - 1;

    //游戏状态
    let game_status = false;

    //界面初始化
    function game_view_init() {
        let str = '';
        for (var i = 1; i <= view_height; i++) {

            str += "<div class='div_line'>"
            for (var j = 1; j <= view_width; j++) {
                str += "<div class='view_list'></div>"
            }
            str += "</div>"
        }
        $('#game_view_box').html(str)
    }

    game_view_init();
    //开始游戏
    function start_game() {
        //初始定时器
        if (stop_obj) {
            stop_game()
        }
        //初始分数
        game_score = 0;
        $('#game_score').text(game_score);
        //游戏状态
        game_status = true;
        //随机获取移动方向
        move_direction = Math.floor(Math.random() * 4 + 1)
        //获取初始小蛇数据
        let base_number = Math.floor(Math.random() * 600 + 1)
        snake_data = [];
        snake_data.push(base_number)
        if (move_direction == 1) {
            for (let i = 1; i <= snake_initial_length; i++) {
                snake_data.push(base_number + (top_size * i))
            }
        }
        if (move_direction == 2) {
            for (let i = 1; i <= snake_initial_length; i++) {
                snake_data.push(base_number - (left_size * i))
            }
        }
        if (move_direction == 3) {
            for (let i = 1; i <= snake_initial_length; i++) {
                snake_data.push(base_number - (top_size * i))
            }
        }
        if (move_direction == 4) {
            for (let i = 1; i <= snake_initial_length; i++) {
                snake_data.push(base_number + (left_size * i))
            }
        }
        //修改颜色
        edit_color();
        //移动小蛇
        move_snack();
        //创建食物
        create_food();
    }
    //暂停游戏
    function stop_game() {
        clearInterval(stop_obj)
        stop_obj = null
    }
    //继续游戏
    function continue_game() {
        if (!game_status) {
            alert('游戏已结束!请重新开始!')
            return false;
        }
        move_snack()
    }
    //修改方向
    function edit_direction(direction) {
        if (move_direction == 1 && direction == 3) {
            return false;
        }
        if (move_direction == 2 && direction == 4) {
            return false;
        }
        if (move_direction == 3 && direction == 1) {
            return false;
        }
        if (move_direction == 4 && direction == 2) {
            return false;
        }
        move_direction = direction
    }
    //移动蛇蛇
    function move_snack() {
        stop_obj = setInterval(function () {

            let old_snack_data = JSON.parse(JSON.stringify(snake_data))

            let next_number = 0
            switch (move_direction) {
                case 1:
                    if (snake_data[0] <= top_size) {
                        next_number = snake_data[0] + top_through_size
                    } else {
                        next_number = snake_data[0] - top_size
                    }
                    break;
                case 2:
                    if (snake_data[0] % view_width == 0) {
                        next_number = snake_data[0] - left_through_size
                    } else {
                        next_number = snake_data[0] + left_size
                    }
                    break;
                case 3:
                    if (snake_data[0] >= top_through_size) {
                        next_number = snake_data[0] - top_through_size
                    } else {
                        next_number = snake_data[0] + top_size
                    }

                    break;
                case 4:
                    if (snake_data[0] % view_width == 1) {
                        next_number = snake_data[0] + left_through_size
                    } else {
                        next_number = snake_data[0] - left_size
                    }
                    break;
            }
            snake_data = snake_data.reverse();
            $.each(snake_data, function (k, v) {
                if (k == snake_data.length - 1) {
                    snake_data[k] = next_number
                } else {
                    snake_data[k] = snake_data[k + 1]
                }
            })
            snake_data = snake_data.reverse();
            if (snake_data.indexOf(food_number) != -1) {
                food_number = ''
                snake_data.push(old_snack_data[old_snack_data.length - 1])
                game_score += 1
                $('#game_score').text(game_score);
                create_food()
            }
            let order_snake_data = JSON.parse(JSON.stringify(snake_data))
            order_snake_data = positive_sort(order_snake_data)
            $.each(order_snake_data, function (k, v) {
                if (k != order_snake_data.length - 1 && order_snake_data[k] == order_snake_data[k + 1]) {
                    alert('游戏结束!最终分数为' + game_score + '!')
                    //清除定时器
                    clearInterval(stop_obj)
                    stop_obj = null
                    //修改游戏状态
                    game_status = false
                    //保持小蛇上一个状态
                    snake_data = old_snack_data
                }
            })

            edit_color()
        }, move_time)
    }
    //修改小蛇颜色
    function edit_color() {
        $('.view_list').each(function (k, v) {
            let key = k + 1
            if (snake_data.indexOf(key) != -1) {
                $(this).addClass('snack_sel')
            } else {
                if ($(this).hasClass('snack_sel')) {
                    $(this).removeClass('snack_sel')
                }
            }
        })
    }
    //创建食物
    function create_food() {
        if (!food_number) {
            flag = true;
            while (flag) {
                food_number = Math.floor(Math.random() * 900 + 1)
                if (snake_data.indexOf(food_number) == -1) {
                    flag = false;
                }
            }
            $('.view_list').each(function (k, v) {
                let key = k + 1
                if (food_number == key) {
                    $(this).addClass('food_sel')
                } else {
                    if ($(this).hasClass('food_sel')) {
                        $(this).removeClass('food_sel')
                    }
                }
            })

        }
    }
    //监听键盘
    $(document).keydown(function (event) {
        console.log(event.keyCode)
        if ((event.keyCode == 38 || event.keyCode == 87) && move_direction != 3) {
            //上
            move_direction = 1;
        } else if ((event.keyCode == 39 || event.keyCode == 68) && move_direction != 4) {
            //右
            move_direction = 2;
        } else if ((event.keyCode == 40 || event.keyCode == 83) && move_direction != 1) {
            //下
            move_direction = 3;
        } else if ((event.keyCode == 37 || event.keyCode == 65) && move_direction != 2) {
            //左
            move_direction = 4;
        }

    });
    //一维数组正序排序
    function positive_sort(data) {
        data.sort((x, y) => {
            return x - y
        })
        return data;
    }
    //一维数组倒序排序
    function reverse_sort(data) {
        data.sort((x, y) => {
            return y - x
        })
        return data;
    }
</script>
</html>

四、结语

一个比较简单但也比较有趣的贪吃蛇DEMO,大家有兴趣可以按照这个DEMO进行延伸,如不同关卡难度设置,难度设置如不可穿墙,设置障碍物,撞到障碍物即游戏结束,移动时间加快等等,有什么问题欢迎大家讨论和留言,喜欢的同学们点赞关注收藏哦!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值