历史回放变色WEB贪吃蛇(众志成城)

开源共享,希望大家在评论区共享想法以及添加游戏功能的代码(比如会逃跑的食物围剿才能吃)现在网上还没有发现其他共享的。

实现效果

点击按钮会有波纹特效,在css中有定义,蛇吃食物会变成食物相似的颜色,点击贪吃蛇会重新开始,点击历史回放会开始回放。

目录

开源共享,希望大家在评论区共享想法以及添加游戏功能的代码(比如会逃跑的食物围剿才能吃)现在网上还没有发现其他共享的。

实现效果

1.index.html

2.index.css

3.game.js

4.snake.js

5.food.js

总结


基本思路:创建一个table表格作为蛇运行的地图,创建body作为蛇的数组,通过定时器不断的先清屏,然后更新蛇这个的数组,然后渲染蛇在table表格中来实现贪吃蛇。

细节:在制作过程中进行了类的拆分,分别为game类,snake类,food3种。

通过中介类game类去调用snakefood类的方法。

Game类的方法:initclearsetcolorsethtmlbindeventstarthistoryplay

Snake类的方法:updatechangedirection,render.

Food类的方法:render

(上面省略构造函数constructor

颜色变化:用math函数随机设置颜色,食物生成后赋值颜色参数

蛇吃后根据食物颜色变化
历史回放:用数组存储移动方向和食物的位置颜色,回放时根据移动方向读取

1.index.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>Document</title>
    
    <audio autoplay="autoplay" controls="controls"loop="loop" preload="auto" src="爆炸贪吃蛇2.mp3">
    <style type="text/css">
        audio{
                display: none;
            }
   </style>
</audio>
    <style>
        body{
            background: -webkit-linear-gradient(rgba(0, 255, 200, 0.925), skyblue);
            color: #fff;
        }
        table{
            border-collapse: collapse;
            background: transparent;
            border: 1px solid #fff;
        }
        td{
            width: 24px;
            height: 24px;
            color: rgb(0, 251, 255);
            text-align: center;
            background: transparent;
            border: 1px solid #fff;
        }
        /* td标签下的color就是食物颜色 */
/* 绿rgb(85, 255, 0); */
/* 红rgb(255, 145, 0); */
/* 蓝rgb(0, 251, 255) */
    </style>
        <link rel="stylesheet" href="index.css">
    
</head>
<body>

</script>
<a href="#" onclick="flushPage()">贪吃蛇</a>
<a href="#" onclick="historyPlayD()">历史回放</a>
<script type="text/javascript">
    const buttons=document.querySelectorAll('a');
    buttons.forEach(btn =>{
        btn.addEventListener('click',function(e){
            let x=e.clientX-e.target.offsetLeft;
            let y=e.clientY-e.target.offsetTop;
            let ripples=document.createElement('span');
            ripples.style.left=x+'px';
            ripples.style.top=y+'px';
            this.appendChild(ripples);
            setTimeout(() =>{
                ripples.remove()
            },1000);
        })
    })

</script>
    <h3 id="f">帧编号:0</h3>
    <h3 id="score">分数:0</h3>
    <div id="app"></div>
    <script src="js/Game.js"></script>
    <script src="js/Snake.js"></script>
    <script src="js/food.js"></script>
    <link rel="stylesheet" href="css/index.css"> 
    <script>
        var game = new Game("0");
		function historyPlayD(){
			game = new Game("1");
		}
		function flushPage(){
			window.location.reload();
		}
    </script>
    
</body>
</html>

2.index.css

@import url('https://fonts.googleapis.com/css?family=Poppins:200,300,400,500,600,700,800,900&display');
*{
    margin:0;
    padding:0;
    font-family:'Poppins',sans-serif;
}
body{
    display:flex;
    justify-content:center;
    align-items:center;
    min-height:100vh;
    flex-direction:column;
    background:#040d15;
}
a{
    position:relative;
    display:inline-block;
    padding:12px 36px;
    margin:10px  0;
    color: #fff;
    text-decoration:none;
    text-transform:uppercase;
    font-size:18px;
    Letter-spacing:2px;
    border-radius:40px;
    overflow:hidden;
    background:linear-gradient(90deg,#0162c8,#55e7fc);
}
a:nth-child(2)
    {
        background:linear-gradient(90deg,#755bea,#ff72c0);  
    }
span{
    position:absolute;
    background:#fff;
    transform:translate(-50%,-50%); 
    pointer-events:none;
    border-radius:50%;
    animation:animate 1s linear infinite;
}
.xcolor1{
	color:rgb(85, 255, 0);
}
.xcolor2{
	color:rgb(255, 145, 0);
}
.xcolor3{
	color:rgb(0, 251, 255);
}
@keyframes animate
{
    0%
    {
        width:0px;
        height:0px;
        opacity:0.5;
    }
    100%
    {
        width:500px;
        height:500px;
        opacity:0.5;

    }


}

3.game.js

var tou = '-webkit-radial-gradient(center center , pink, red)';
var shen = '-webkit-radial-gradient(center center , orange, red)';
var snakeHistory = new Array();
var willDirectionHistory = new Array();
function Game(flag){
    //行数
    this.row = 20;
    //列数
    this.col = 20;
    //分数
    this.score = 0;
   
	if(flag == "0"){
		//初始化节点
		this.init();
		//实例化蛇类
		this.snake = new Snake();
		//初始化食物
		this.food = new Food(this,flag,0);
		//执行定时器任务
		this.start();
		//键盘的事件监听
		this.bindEvent();
	}else{
		countFood = 1;
		countMove = 0;
		tou = '-webkit-radial-gradient(center center , pink, red)';
		shen = '-webkit-radial-gradient(center center , orange, red)';
		document.getElementById("app").innerHTML = "";
		//初始化节点
		this.init();
		//实例化蛇类
		this.snake = new Snake();
		//初始化食物
		this.food = new Food(this,flag,0);
		//执行定时器任务
		this.historyPlay();

	}
}

Game.prototype.init = function(){
    this.dom = document.createElement("table");
    var tr,td;
    //遍历行和列上树
    for(var i = 0; i < this.row; i++){
        //遍历行,创建节点上树
        tr = document.createElement("tr");
        for(var j = 0; j < this.col; j++){
            //遍历列,创建节点上树
            td = document.createElement("td");    
       
            tr.appendChild(td);
        }
        //追加节点上树
        this.dom.appendChild(tr);
    }
    
    //表格上树
    document.getElementById("app").appendChild(this.dom);
};
Game.prototype.clear = function() {
    //遍历表格,擦除画布
    for(var i = 0; i < this.row; i++){
        for(var j = 0; j < this.col; j++){
            this.dom.getElementsByTagName("tr")[i].getElementsByTagName("td")[j].style.background = "transparent";
            this.dom.getElementsByTagName("tr")[i].getElementsByTagName("td")[j].innerHTML = "";
        }
    }
};
//设置颜色的方法
Game.prototype.setColor = function(row, col, color){
    //让表格的第几行,第几列设置什么颜色
    this.dom.getElementsByTagName("tr")[row].getElementsByTagName("td")[col].style.background = color; 
};
//渲染食物
Game.prototype.setHtml = function(row, col, html){
    this.dom.getElementsByTagName("tr")[row].getElementsByTagName("td")[col].innerHTML = html;
};
//设置键盘的事件监听
Game.prototype.bindEvent = function(){
    var self = this;
    //键盘事件
    //document.onkeydown来对用户敲击键盘事件进行监听
    document.onkeydown = function(event){
        switch(event.keyCode){
            //按下左键
            case 37:
                //先进行判断,如果当前的方式是向右移动,此时不能按左键
                if(self.snake.direction == "R")
                    break;
                self.snake.changeDirection("L");
                break;
            //按下上键
            case 38:
                //先进行判断,如果当前的方式是向下移动,此时我们不能按上键
                if(self.snake.direction == "D")
                    break;
                self.snake.changeDirection("U");
                break;
            //按下右键
            case 39:
                //先进行判断,如果当前的方式是向左移动,此时不能按右键
                if(self.snake.direction == "L")
                    break;
                self.snake.changeDirection("R");
                break;
            //按下下键
            case 40:
                //先进行判断,如果当前的方式是向上移动,此时不能按下键
                if(self.snake.direction == "U")
                    break;
                self.snake.changeDirection("D");
                break;

        }
    }
};
var starTimer = null;
Game.prototype.start = function(){
    //帧编号
    this.f = 0;
    //setInterval(code,millisec[,"lang"])
    //code:要调用的函数或要执行的代码串。
    //millisec:周期性执行或调用 code 之间的时间间隔,以毫秒计

    starTimer = setInterval(function() {
        //定时器里面的核心就是游戏的渲染本质,清屏-更新-渲染
        game.f++;score
        document.getElementById("f").innerHTML = "帧编号:"+game.f;
        //渲染分数
        document.getElementById("score").innerHTML = "分数:"+game.score;
        //清楚屏幕
        game.clear();
        //蛇的更新速度,当蛇变长速度要加快
        var during = game.snake.body.length < 30 ? 30 - game.snake.body.length : 1;
        //蛇的更新
        game.f % during == 0 && game.snake.update("0");
        //蛇的渲染
        game.snake.render();
		//渲染食物
		game.food.render();
    }, 20)
};
var historyPlayTimer = null;
Game.prototype.historyPlay = function(){
    //帧编号
    this.f = 0;
    //setInterval(code,millisec[,"lang"])
    //code:要调用的函数或要执行的代码串。
    //millisec:周期性执行或调用 code 之间的时间间隔,以毫秒计
    historyPlayTimer = setInterval(function() {
		//定时器里面的核心就是游戏的渲染本质,清屏-更新-渲染
        game.f++;score
        document.getElementById("f").innerHTML = "帧编号:"+game.f;
        //渲染分数
        document.getElementById("score").innerHTML = "分数:"+game.score;
        //清楚屏幕
        game.clear();
        //蛇的更新速度,当蛇变长速度要加快
        var during = game.snake.body.length < 30 ? 30 - game.snake.body.length : 1;
        //蛇的更新
        game.f % during == 0 && game.snake.update("1");
        //蛇的渲染
        game.snake.render();
		//渲染食物
		game.food.render();
    }, 20)
};

4.snake.js

function Snake(){
    //蛇的初始化身体
    this.body = [
        {"row":3, "col":5},
        {"row":3, "col":4},
        {"row":3, "col":3},
        {"row":3, "col":2}
    ];
    //信号量,设置的运行方向
    this.direction = "R";
    //即将改变的方向,目的是为了防止出现原地调头的情况
    this.willDirection = "R";
}
//蛇的更新
var countMove = 0;
var countFood = 1;
Snake.prototype.update = function(flag) {
    //让当前的direction接受以下willDirection
	if(flag == "0"){
		this.direction = this.willDirection;
		willDirectionHistory[willDirectionHistory.length] = this.direction;
	}else{
		this.direction = willDirectionHistory[countMove];
		countMove++;
	}
    switch(this.direction){
        case "R":
            //向右移动
            this.body.unshift({"row":this.body[0].row, "col":this.body[0].col + 1});
            //unshift是将新的放在数组第一个
            break;  
        case "D":
            //向下移动
            this.body.unshift({"row":this.body[0].row + 1, "col":this.body[0].col});
            break;
        case "L":
            //向左移动
            this.body.unshift({"row":this.body[0].row, "col":this.body[0].col - 1});
            break;
        case "U":
            //向上移动
            this.body.unshift({"row":this.body[0].row - 1, "col":this.body[0].col});
            break;
    }
    //死亡的判断,超出了表格边缘的部分  
    if(this.body[0].col > game.col-1 || this.body[0].row > game.row - 1 || this.body[0].col < 0 || this.body[0].row < 0){ 
		console.log(willDirectionHistory);
		console.log(snakeHistory);
        alert("游戏结束!您当前的得分为:"+game.score+"分。");
        this.body.shift();//shift是把数组第一个干掉
		if(flag == "0"){
			clearInterval(starTimer);//通过setInterval返回值timer来控制停止
		}else{
			clearInterval(historyPlayTimer);//通过setInterval返回值timer来控制停止
		}
    }
    //自己撞到了自己的时候,也会判定死亡
    for(var i = 1; i <this.body.length; i++){
        //如何能够判断死亡,也就是当前的蛇的头部与身体的某一部分的col和row完全重合的时候
        if (this.body[0].col == this.body[i].col && this.body[0].row == this.body[i].row){
            alert("游戏结束!您当前的得分为:"+game.score+"分。");
           if(flag == "0"){
           	clearInterval(starTimer);//通过setInterval返回值timer来控制停止
           }else{
           	clearInterval(historyPlayTimer);//通过setInterval返回值timer来控制停止
           }
        }
    }
    //蛇吃食物
    //判断如果当前的蛇的头部没有和食物进行重合,就代表此时没有吃到食物,此时就进行尾部删除,如果重合了就代表吃到了,此时我们不进行尾部删除。
    if(this.body[0].row == game.food.row &&  this.body[0].col == game.food.col){
		//此时的情况只有头部增加了,尾部没有删除
		var className = document.getElementsByTagName("tr")[game.food.row].getElementsByTagName("td")[game.food.col].getAttribute("class"); 
		if(className=="xcolor1"){
			tou = '-webkit-radial-gradient(center center , #55FF00, #B8FF00)';
			shen = '-webkit-radial-gradient(center center , #55FF00, #00E100)';
		}else if(className=="xcolor2"){
			tou = '-webkit-radial-gradient(center center , pink, red)';
			shen = '-webkit-radial-gradient(center center , orange, red)';
		}else{
			tou = '-webkit-radial-gradient(center center , #9EFBFF, #76F9FF)';
			shen = '-webkit-radial-gradient(center center , #00BCBF, #00FBFF)';
		}
        //创建新的食物
        game.food = new Food(game,flag,countFood);
        //加分数
        game.score++;
        //让帧编号归0,因为蛇会蹿一下
        game.f = 0;
		countFood++;
    }else{        
        this.body.pop();//移除数组最后一个元素
    }
    
};
//蛇的方向改变,防止的是在一次渲染之前会出现调头的情况
Snake.prototype.changeDirection = function(d){
    this.willDirection = d;
}
//蛇类snake调用game类的setColor方法,因为蛇要在table表格中设置颜色,table表格在game类中
Snake.prototype.render = function(){
    // 蛇头的渲染
    game.setColor(this.body[0].row, this.body[0].col, tou);
    //蛇身的渲染
    for(var i = 1; i < this.body.length; i++){
        game.setColor(this.body[i].row, this.body[i].col, shen);
    }
}

5.food.js

var colorlast = "";
function Food(gameSnake,flag,countFood){
    var self = this;
    //食物的位置
    //下面的do-while循环语句作用是先创建一个row和col,然后判断这个row和col是否在蛇的身上
    do{
		if(flag=="0"){
			this.row = parseInt(Math.random() * gameSnake.row);
			this.col = parseInt(Math.random() * gameSnake.col);
			colorlast = this.getColor();
		}else{
			var n = snakeHistory[countFood].split("|");
			this.row = n[0];
			this.col = n[1];
			colorlast = n[2]
		}
    }while((function(){
        //遍历蛇的row和col,然后和Food新随机出来的row和col来进行判断,是否重合
        for(var i = 0; i < gameSnake.snake.body.length; i++){
            if(gameSnake.snake.body[i].row == self.row || gameSnake.snake.body[i].col == self.col){
                return true;
            }
        }
        return false;
    })());
	if(flag=="0"){
		snakeHistory[snakeHistory.length] = this.row+"|"+this.col+"|"+colorlast;
	}
    console.log(this.row, this.col);
}


Food.prototype.render = function() {
	game.setHtml(this.row, this.col, "♥");
	document.getElementsByTagName("tr")[this.row].getElementsByTagName("td")[this.col].className = colorlast;
}

Food.prototype.getColor = function() {
	var x= parseInt(3*Math.random())+1;
	switch(x)
	{
		case 1: 
			colorlast= "xcolor1";
			break;
		case 2: 
			colorlast="xcolor2";
			break;
		case 3: 
			colorlast="xcolor3";
			break;
	}
	return colorlast;
}

总结

除了原本贪吃蛇的玩法没有创新,贪吃蛇颜色变化没有渐变效果,(希望大家一起完善,抄袭是不会有进步的,评论区发表代码或自己博客链接),这个背景音乐是b站up主给的想要的评论区来要(特别带感),代码不懂的地方就直接问就是。

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大黑山下一只猴^O^

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值