开源共享,希望大家在评论区共享想法以及添加游戏功能的代码(比如会逃跑的食物围剿才能吃)现在网上还没有发现其他共享的。
实现效果
点击按钮会有波纹特效,在css中有定义,蛇吃食物会变成食物相似的颜色,点击贪吃蛇会重新开始,点击历史回放会开始回放。
目录
开源共享,希望大家在评论区共享想法以及添加游戏功能的代码(比如会逃跑的食物围剿才能吃)现在网上还没有发现其他共享的。
基本思路:创建一个table表格作为蛇运行的地图,创建body作为蛇的数组,通过定时器不断的先清屏,然后更新蛇这个的数组,然后渲染蛇在table表格中来实现贪吃蛇。
细节:在制作过程中进行了类的拆分,分别为game类,snake类,food类3种。
通过中介类game类去调用snake和food类的方法。
Game类的方法:init,clear,setcolor,sethtml,bindevent,start,historyplay
Snake类的方法:update,changedirection,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主给的想要的评论区来要(特别带感),代码不懂的地方就直接问就是。