美文网首页
canvas小游戏-贪吃蛇

canvas小游戏-贪吃蛇

作者: 你怀中的猫 | 来源:发表于2022-05-28 16:18 被阅读0次

    使用canvas完成贪吃蛇小游戏

    • 可以设置速度
    • 可以设置有墙或无墙状态
    • 可以修改蛇身的皮肤


      image.png

    html代码

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>贪吃蛇</title>
        <link rel="stylesheet" href="./index.css">
        <script src="http://code.jquery.com/jquery-1.12.4.js"></script>
    </head>
    
    <body>
        <div class="top">
            <div class="left">Snake</div>
            <div class="right">Score:<span class="score">0</span></div>
        </div>
        <canvas id="canvas"></canvas>
        <!-- 开始游戏盒子 -->
        <div class="begin">
            <p>SNAKE</p>
            <div class="start">start</div>
            <br>
            <div class="set">setting</div>
        </div>
        <!-- 设置盒子 -->
        <div class="setBox">
            <p>Settings</p>
            <div class="start">start</div>
            <div class="speed">Speed: <div class="slow">Slow</div>
                <div class="normal">Normal</div>
                <div class="fast">Fast</div>
            </div>
            <div class="wall">Wall: <div class="on">On</div>
                <div class="off">Off</div>
            </div>
        </div>
        <!-- 结束盒子 -->
        <div class="end">
            <p>Game Over</p>
            <div class="endText">游戏结束</div>
            <div class="start">start</div>
            <br>
            <div class="set">setting</div>
        </div>
        <audio id="audio"
            src="https://dl.stream.qqmusic.qq.com/C400000uYx7S4VMZm0.m4a?guid=3943565997&vkey=D44390A154E3DC9A97128581F9652304787C70CB45A7C298ED80BB455C7C21128025EF7F3D3E2F134784118817FB0EE265DB174397269D32&uin=2497208832&fromtag=120002"></audio>
            
        <audio src="./audio/game_over.mp3" id="audio1"></audio>
    </body>
    <script src="./index.js"></script>
    
    </html>
    

    CSS代码

    @font-face {
      font-family: 'VT323';
      font-style: normal;
      font-weight: 400;
      src: url(https://fonts.gstatic.com/s/vt323/v17/pxiKyp0ihIEF2isfFJA.ttf) format('truetype');
    }
    * {
      margin: 0px;
      padding: 0px;
    }
    a {
      text-decoration: none;
      color: #000;
    }
    ul,
    ol {
      list-style-type: none;
    }
    table {
      border-collapse: collapse;
    }
    input {
      outline: none;
      border-width: 1px;
    }
    textarea {
      outline: none;
      resize: none;
      overflow: auto;
    }
    .clear {
      clear: both;
    }
    .clear::after {
      content: '';
      clear: both;
    }
    body {
      background-color: #000;
    }
    * {
      color: #fff;
      font-family: "VT323";
    }
    .top {
      margin: 0 auto;
      width: 340px;
      overflow: hidden;
      font-size: 26px;
      padding-top: 10px;
      margin-bottom: 20px;
    }
    .top .left {
      float: left;
    }
    .top .right {
      float: right;
    }
    body > div {
      width: 340px;
      margin: 0 auto;
    }
    #canvas {
      border: 10px solid #fff;
      display: block;
      margin: 0 auto;
      display: none;
    }
    .begin,
    .end {
      text-align: center;
    }
    .end{
      display: none;
    }
    .begin > p,
    .end > p {
      text-align: center;
      font-size: 50px;
      padding-top: 30px;
      animation: logo-ani 1000ms linear infinite;
      margin-bottom: 20px;
    }
    .begin .start,
    .end .start,
    .begin .set,
    .end .set {
      font-size: 26px;
      display: inline-block;
      line-height: 40px;
      cursor: pointer;
    }
    .begin .start::before,
    .end .start::before,
    .begin .set::before,
    .end .set::before {
      content: ">";
      padding-right: 10px;
      display: none;
    }
    .begin .start:hover::before,
    .end .start:hover::before,
    .begin .set:hover::before,
    .end .set:hover::before {
      display: inline-block;
    }
    .begin .endText,
    .end .endText {
      font-size: 30px;
    }
    @keyframes logo-ani {
      50% {
        transform: scale(1.3, 1.3);
      }
      100% {
        transform: scale(1, 1);
      }
    }
    .setBox {
      text-align: center;
      display: none;
    }
    .setBox > p {
      text-align: center;
      font-size: 50px;
      padding-top: 30px;
      margin-bottom: 20px;
    }
    .setBox .start {
      font-size: 26px;
      display: inline-block;
      line-height: 40px;
      cursor: pointer;
      padding: 0px !important;
    }
    .setBox .start::before {
      content: ">";
      padding-right: 10px;
      display: none;
    }
    .setBox .start:hover::before {
      display: inline-block;
    }
    .setBox > div {
      text-align: center;
      font-size: 26px;
      line-height: 30px;
      padding: 5px 0;
    }
    .setBox > div > div {
      display: inline-block;
      cursor: pointer;
      padding: 0 5px;
    }
    .setBox > div > .active {
      background-color: #fff;
      color: #000;
    }
    
    

    js代码

    /*
        键盘控制
        上  下  左  右
    */
    
    //设置画布的宽高
    canvas.width = 640;
    canvas.height = 640;
    var ctx = canvas.getContext('2d');
    
    //定义有没有墙
    var hasWall = true;
    //定义速度
    var speed = 60;
    
    //定义一个数组保存所有的格子
    var arr = [];
    //定义一个数组,保存蛇身所有的格子
    var snake = [512, 513, 514, 515, 516, 517];
    
    //声明一个变量 表示当前行进状态
    var fx = 39;  // 37 : 左 , 38 : 上  , 39 : 右 , 40 : 下 
    
    //定义一个变量,保存食物 的格子对象
    var foods = null;
    
    //定义一个变量,保存分数
    var score = 0;
    
    //定义一个蛇身渲染 和 键盘事件的开关
    var flog = false;
    
    //蛇头坐标
    var gird = null;
    
    
    
    //给设置墙的按钮绑定点击方法
    $('.wall>div').click(function () {
        var text = $(this).text();
        // console.log(text);
        switch (text) {
            case 'On':
                hasWall = true;
                break;
            case 'Off':
                hasWall = false;
                break;
        }
        // console.log(hasWall);
        //点击添加calss名  并删除兄弟标签的class名
        $(this).addClass('active').siblings().removeClass('active');
    })
    
    //模拟让 on 按钮被点击
    $('.wall>div').eq(0).click();
    
    
    
    //给设置速度的按钮绑定点击方法
    $('.speed>div').click(function () {
        var text = $(this).text();
        // console.log(text);
        switch (text) {
            case 'Slow':
                speed = 80;
                break;
            case 'Normal':
                speed = 60;
                break;
            case 'Fast':
                speed = 40;
                break;
        }
        // console.log(hasWall);
        //点击添加calss名  并删除兄弟标签的class名
        $(this).addClass('active').siblings().removeClass('active');
        console.log(speed);
    })
    
    //模拟让 on 按钮被点击
    $('.speed>div').eq(1).click();
    
    
    
    
    //给set按钮的点击方法  游戏设置按钮
    $('.set').click(function () {
        //关掉所有的盒子
        $('.begin').hide();
        $('.end').hide();
        $('#canvas').hide();
        //显示setBox盒子
        $('.setBox').show();
    })
    
    
    
    //定义一个格子的类
    function Rect(x, y, n,f) {
        this.x = x;
        this.y = y;
        this.index = n;
        // this.c = c || true;  //默认为黑色  true 代表黑色   false代表白色
        this.fx = f;
    }
    
    //添加一个绘制黑色格子的方法
    Rect.prototype.drawB = function () {
        ctx.beginPath();
        ctx.fillStyle = 'block';
        ctx.fillRect(this.x, this.y, 20, 20);
        ctx.closePath();
        ctx.stroke();
    }
    //添加一个绘制白色格子的方法
    Rect.prototype.drawW = function () {
        ctx.beginPath();
        ctx.fillStyle = '#fff';
        ctx.fillRect(this.x, this.y, 20, 20);
        ctx.closePath();
        ctx.stroke();
    }
    
    //添加一个绘制食物的方法
    Rect.prototype.drawFood = function () {
        ctx.beginPath();
        ctx.arc(this.x + 10, this.y + 10, 10, 0, 360)
        ctx.fillStyle = 'gold';
        ctx.fill();
        ctx.closePath();
        ctx.stroke();
    }
    
    
    //添加绘制蛇身皮肤的方法
    Rect.prototype.drawBody = function (img) {
        var that = this;
        img.onload = function () {
            var deg = Math.PI / 180;
            ctx.save();
            switch (that.fx) {
                case 37:
                    ctx.translate(that.x, that.y + 20);
                    ctx.rotate(270 * deg);
                    break;
                case 38:
                    ctx.translate(that.x, that.y);
                    ctx.rotate(0);
                    break;
                case 39:
                    ctx.translate(that.x + 20, that.y);
                    ctx.rotate(90 * deg);
                    break;
                case 40:
                    ctx.translate(that.x + 20, that.y + 20);
                    ctx.rotate(180 * deg);
                    break;
            }
            ctx.drawImage(this, 0, 0, 20, 20);
            ctx.restore();
        }
    }
    
    
    //生成棋盘的方法
    function createBox() {
        var x = 0;
        var y = 0;
        arr = [];
        for (var i = 0; i < 32 * 32; i++) {
            var obj = new Rect(x, y, i, 39);
            obj.drawB();
            arr.push(obj);
            x += 20;
            if ((i + 1) % 32 == 0) {
                y += 20;
                x = 0;
            }
        }
    
        //设置一下墙
        if (hasWall) {
            canvas.style.borderColor = '#fff';
        } else {
            canvas.style.borderColor = '#333';
        }
    }
    
    
    
    //绘制蛇身的方法
    function drawSnake() {
        for (var i = 0; i < snake.length; i++) {
            var a = snake[i];
    
            // if(a == gird){
            //     arr[a].fx = fx;
            // }
    
            //蛇身皮肤
            // if (i == snake.length - 1) {
            //     //执行画头部的方法
            //     var img = new Image();
            //     img.src = './img/1.png';
            //     arr[a].drawBody(img);
            // } else if (i == 0) {
            //     //执行画尾部的方法
            //     var img = new Image();
            //     img.src = './img/3.png';
            //     arr[a].drawBody(img);
            // } else {
            //     //执行画身体的的方法
            //     var img = new Image();
            //     img.src = './img/2.png';
            //     arr[a].drawBody(img);
            // }
    
            //正常小白快蛇身
            arr[a].drawW();
        }
        //
        flog = false;
    }
    
    
    //定义一个蛇身移动的方法
    function move() {
        var timer = setInterval(function () {
            canvas.width = canvas.width;
    
            //调用生成格子的方法
            createBox();
            //调用绘制蛇身的方法
            drawSnake();
    
            //绘制食物
            arr[foods].drawFood();
    
            //判断蛇是否吃到食物
            // eat() 返回值  如果吃到食物返回true  如果没吃到返回false
            if (!eat()) {
                //删掉蛇身数组中的第一个元素
                snake.shift();
            }
            //根据行进状态,改变蛇身数组
            var num = null;
            switch (fx) {
                case 37://左
                    num = snake[snake.length - 1] - 1;
                    break;
                case 38://上
                    num = snake[snake.length - 1] - 32;
                    break;
                case 39://右
                    num = snake[snake.length - 1] + 1;
                    break;
                case 40://下
                    num = snake[snake.length - 1] + 32;
                    break;
    
    
                    
            }
    
            //碰撞判断
            if (wall(num)) {
                //判断有没有墙壁
                if (hasWall) {
                    //有墙,撞上就结束游戏
                    clearInterval(timer)
                    // alert('游戏结束');
                    end('你个老六,你撞墙了!!!')
                    audio.pause();
                    audio1.play();
                } else {
                    //无墙 撞上之后从反方向出现
                    var head = snake[snake.length - 1];
                    //右墙
                    if ((head + 1) % 32 == 0 && fx == 39) {
                        num = head - 31;
                    }
                    //左墙
                    if (head % 32 == 0 && fx == 37) {
                        num = head + 31;
                    }
                    //上墙
                    if (head < 32 && fx == 38) {
                        num = head + 31 * 32;
                    }
                    //下墙
                    if (head >= 32 * 31 && fx == 40) {
                        num = head - 31 * 32;
                    }
                }
            }
    
            //判断是否咬到自己
            if (eatSelf()) {
                clearInterval(timer)
                audio.pause();
                audio1.play();
                // alert('游戏结束');
                end('你个老六,咬到自己了!!!')
            }
    
            snake.push(num);
            // console.log(snake);
        }, speed)
    }
    
    
    //定义 碰撞检测的方法
    function wall(n) {
        //上下墙的碰撞检测
        if (n < 0 || n > (32 * 32 - 1)) {
            return true;  //撞到墙壁
        }
        //蛇头坐标
        var head = snake[snake.length - 1];
        //右墙的碰撞检测
        if ((head + 1) % 32 == 0 && n == head + 1) {
            return true;
        }
        //左边墙壁的碰撞检测
        if (head % 32 == 0 && n == head - 1) {
            return true;
        }
    }
    
    
    
    //定义一个生成食物的方法
    function food() {
        var num = randNum(0, arr.length - 1);
        if (snake.indexOf(num) == -1) {
            // console.log(arr[num]);
            return num;
        } else {
            return food();
        }
    }
    
    
    //判断蛇吃食物
    function eat() {
        //获取蛇头
        var head = snake[snake.length - 1];
    
        if (head == foods) {
            //蛇吃到食物
            //重新生成食物
            foods = food();
            arr[foods].drawFood();
            score++;
            $('.score').text(score);
            return true;
        } else {
            //没吃到
            return false;
        }
    }
    
    
    //判断蛇是否咬到自己
    function eatSelf() {
        var head = snake[snake.length - 1];
        // console.log(snake);
        //先将蛇头从 蛇身数组中去掉
        snake.pop();
        // console.log(snake);
    
        if (snake.indexOf(head) == -1) {
            snake.push(head);
            return false;
        } else {
            //咬到自己
            snake.push(head);
            return true;
        }
    }
    
    
    
    
    //给start按钮的点击方法  开始游戏按钮
    $('.start').click(function () {
        //关掉所有的盒子
        $('.begin').hide();
        $('.end').hide();
        $('.setBox').hide();
        //显示canvas盒子
        $('#canvas').css('display', 'block');
    
        audio.play();
        audio1.pause();
        //调用绘制蛇身的方法
        // drawSnake()
    
        //将蛇身初始化
        snake = [512, 513, 514, 515, 516];
        //清空画布
        canvas.width = canvas.width;
        //修改键值方向
        fx = 39;
        //清空分数
        score = 0;
        $('.score').text(score);
    
        //调用生成格子的方法
        createBox();
    
        //生成食物
        foods = food();
        //绘制食物
        arr[foods].drawFood();
    
    
        move();
    })
    
    
    
    //键盘事件
    window.onkeydown = function (e) {
        var code = e.keyCode;
        // 向哪一个方向行进时,不能向反方向移动
        if (fx == 39 && code == 37) {
            return;
        }
        if (fx == 37 && code == 39) {
            return;
        }
        if (fx == 38 && code == 40) {
            return;
        }
        if (fx == 40 && code == 38) {
            return;
        }
        //蛇身渲染 和 键盘事件的开关
        if (flog) {
            return;
        }
        //如果不是这四个键,不执行
        if (code == 37 || code == 38 || code == 39 || code == 40) {
            gird = snake[snake.length - 1];
            fx = code;
            flog = true;
        }
    }
    
    
    //游戏结束的方法
    function end(t) {
        //关掉所有的盒子
        $('.begin').hide();
        $('#canvas').hide();
        $('.setBox').hide();
        //显示end盒子
        $('.end').show();
    
        $('.endText').text(t)
    }
    
    
    //封装随机数
    function randNum(min, max) {
        return Math.floor(Math.random() * (max - min + 1) + min);
    }
    
    
    

    相关文章

      网友评论

          本文标题:canvas小游戏-贪吃蛇

          本文链接:https://www.haomeiwen.com/subject/vpiiprtx.html