原生 Javascript 开发超速贪吃蛇小游戏

2020-04-25 12:29:26 +08:00
 Liulang007

前言

本课程是通过 JavaScript 结合 WebAPI DOM 实现的一版网页游戏---贪吃蛇的开发全过程,采用面向以象的思想设计开发。通过这个小游戏的开发, 不仅可以掌握 JS 的语法的应用,还可以学会 DOM 的使用, 更重要的是可以学习程序开发的业务逻辑,和项目开发过程,以及一些常用的游戏算法。

代码和课程来源(不是我做的): https://www.bilibili.com/video/BV1aE411K7Ga?from=search&seid=11376138008125697092

预览

请访问 https://www.meitubk.com/my/demo2/

代码

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8"/>
	<title>贪吃蛇桌面版小游戏</title>
	<link rel="icon" href="https://www.meitubk.com/my/avator.jpg" type="image/x-icon"/>
	<style type="text/css">
		body{margin: 0; padding: 0;}
		div#main{margin: 40px;}
		input.btn{width: 100px;height: 40px;}
		span.gtitle{font-size: 25px;font-weight: bold;}
		span#gnum, span#glen, span#gspeed{color:red}
	</style>
</head>
<body>
	<div id="main">
		<h1>贪吃蛇桌面版 -- 需 <span id="glen">1</span> 节 -- 第 <span id="gnum">1</span> 关 -- 速度间隔 <span id="gspeed">1</span></h1>
		<input class="btn" type="button" value="开始游戏" id="begin"/>
		<input class="btn" type="button" value="暂停游戏" id="pause"/>
		<input class="btn" type="button" value="我的博客" id="myblog"/>
		<input class="btn" type="button" value="视频教程" id="video"/>
		<script type="text/javascript">
			var main = document.getElementById("main");
			//是否开启画布格子
			var showcanvas = false;
			//地图对象构造方法
			function Map(atom, xnum, ynum){
				//原子、像素点
				this.atom = atom;
				//地图宽度
				this.xnum = xnum;
				//地图高度
				this.ynum = ynum;
				this.canvas = null;
				//创建画布函数
				this.create = function(){
					this.canvas = document.createElement("div");
					this.canvas.style.cssText = "position:absolute;top:170px;border:1px solid darkred;background:#FAFAFA;"
					this.canvas.style.width = this.atom * this.xnum + 'px';
					this.canvas.style.height = this.atom * this.ynum + 'px';
					main.appendChild(this.canvas);
					if(showcanvas){
						for(var y=0; y<ynum; y++){
							for(var x=0; x<xnum; x++){
								var item = document.createElement("div");
								item.style.cssText = "border:1px solid yellow;"
								item.style.width = this.atom + 'px';
								item.style.height = this.atom + 'px';
								item.style.backgroundColor = "green";
								this.canvas.appendChild(item);
								item.style.position = "absolute";
								item.style.left = x * this.atom + 'px';
								item.style.top = y * this.atom + 'px';
							}
						}
					}
				}
			}
			//创建食物函数
			function Food(map){
				this.width = map.atom;
				this.height = map.atom;
				this.bgColor = "rgb("+Math.floor(Math.random()*200)+","+Math.floor(Math.random()*200)+","+Math.floor(Math.random()*200)+")";
				//食物放在第几格
				this.x = Math.floor(Math.random() * map.xnum);
				this.y = Math.floor(Math.random() * map.ynum);
				//设置食物的样式
				this.flag = document.createElement("div");
				this.flag.style.width = this.width + 'px';
				this.flag.style.height =  this.height + 'px';
				this.flag.style.backgroundColor = this.bgColor;
				this.flag.style.position = "absolute";
				this.flag.style.left = this.x * this.width + 'px';
				this.flag.style.top = this.y * this.height + 'px';
				map.canvas.appendChild(this.flag);
			}
			//创建蛇对象
			function Snake(map){
				//设置宽、高
				this.width = map.atom;
				this.height = map.atom;
				//设置起始方向
				this.direction = 'right';
				this.body = [
					{x: 2, y: 0}, //头
					{x: 1, y: 0}, //身
					{x: 0, y: 0}  //尾
				]
				//显示蛇
				this.display = function(){
					for(var i=0;i < this.body.length; i++){
						//当吃到食物时,要把 x 设置为 null
						if(this.body[i].x != null){ 
							var item = document.createElement('div');
							//将节点保存到一个状态变量中,以便以后删除使用
							this.body[i].flag = item;
							//设置蛇的样式
							item.style.width = this.width + 'px';
							item.style.height = this.height + 'px';
							item.style.backgroundColor = "rgb("+Math.floor(Math.random()*200)+","+Math.floor(Math.random()*200)+","+Math.floor(Math.random()*200)+")";
							item.style.position = "absolute";
							item.style.left = this.body[i].x * this.width + 'px';
							item.style.top = this.body[i].y * this.height + 'px';
							map.canvas.appendChild(item);
						}
					}
				}
				//让蛇运动
				this.run = function(){
					for(var i=this.body.length-1; i>0 ; i--){
						this.body[i].x = this.body[i-1].x;
						this.body[i].y = this.body[i-1].y;
					}
					//判断方向
					switch(this.direction){
						case "left": this.body[0].x -= 1; break;
						case "right": this.body[0].x += 1; break;
						case "up": this.body[0].y -=1; break;
						case "down": this.body[0].y += 1; break;
					}
					//判断蛇头吃到食物
					if(this.body[0].x == food.x && this.body[0].y == food.y){
						//蛇加一节,根据最后节点
						this.body.push({x: null, y:null, flag: null});
						//判断下一级别
						if(this.body.length >= level.slength){
							level.setlevel();
						}
						map.canvas.removeChild(food.flag);
						food = new Food(map);
					}
					//判断是否出界
					if(this.body[0].x < 0 || this.body[0].x > map.xnum - 1 || this.body[0].y < 0 || this.body[0].y > map.ynum - 1){
						clearInterval(timer);
						alert("真笨呀,活活的撞墙死掉了!");
						//重新开始游戏
						restart(map, this, level);
						return false;
					}
					//判断是否吃到自己
					for(var i=4; i<this.body.length;i++){
						if(this.body[0].x == this.body[i].x && this.body[0].y == this.body[i].y){
							alert("哎,把自己给咬死了!");
							//重新开始游戏
							restart(map, this, level);
							return false;
						}
					}
					for(var i=0; i < this.body.length; i++){
						if(this.body[i].flag != null){
							map.canvas.removeChild(this.body[i].flag);
						}
					}
					this.display();
				}
			}
			//重新开始游戏
			function restart(map, snake, level){
				for(var i=0; i<snake.body.length;i++){
					map.canvas.removeChild(snake.body[i].flag);
				}
				snake.body = [
					{x: 2, y: 0}, //头
					{x: 1, y: 0}, //身
					{x: 0, y: 0}  //尾
				]
				snake.direction = 'right';
				snake.display();
				map.canvas.removeChild(food.flag);
				food = new Food(map);
				level.num = 1;
				level.speed = 100;
				level.slength = 6;
				level = new Level();
				level.display();
			}
			//设置级别对象
			function Level(){
				this.num = 1;
				this.speed = 100;
				this.slength = 6; //通过目标长度
				this.setlevel =  function(){
					this.num++;
					if(this.speed <= 20){
						this.speed = 20;
					}else{
						this.speed -= 20;
					}
					this.slength += 3;
					this.display();
					start();
				}
				this.display = function(){
					document.getElementById("gnum").innerHTML = this.num;
					document.getElementById("gspeed").innerHTML = this.speed;
					document.getElementById("glen").innerHTML = this.slength;
				}
			}
			var level = new Level();
			level.display();
			var map = new Map(20, 40, 20);
			map.create()
			var food = new Food(map);
			var snake = new Snake(map);
			snake.display();
			//键盘监听
			window.onkeydown = function(e){
				var event = e || window.event;
				//上下左右
				switch(event.keyCode){
					case 38:
						if(snake.direction != "down"){
							snake.direction = "up";
						}
						break;
					case 40:
						if(snake.direction != "up"){
							snake.direction = "down";
						}
						break;
					case 37:
						if(snake.direction != "right"){
							snake.direction = "left";
						}
						break;
					case 39:
						if(snake.direction != "left"){
							snake.direction = "right";
						}
						break;
				}
			}
			function start(){
				clearInterval(timer);
				timer = setInterval(function(){
					snake.run();
				}, level.speed);
			}
			var timer;
			document.getElementById('begin').onclick=function(){
				start();
			}
			document.getElementById('pause').onclick=function(){
				clearInterval(timer);
			}
			document.getElementById('myblog').onclick=function(){
				location.href = "https://www.meitubk.com/";
			}
			document.getElementById('video').onclick=function(){
				location.href = "https://www.bilibili.com/video/BV1aE411K7Ga";
			}
		</script>
	</div>
</body>
</html>
2302 次点击
所在节点    分享创造
7 条回复
zj1926
2020-04-25 12:34:48 +08:00
以前面试的时候我也写过一个 https://github.com/im6/vanilla-snake
Liulang007
2020-04-25 12:53:29 +08:00
@zj1926 不错啊
ksedz
2020-04-25 13:29:03 +08:00
提个 bug:往左走的时候同时按下和右可能导致直接向右走或者把自己咬死
Liulang007
2020-04-25 16:39:51 +08:00
@ksedz 是不是按太快了,我这边不会这样
Liulang007
2020-04-25 17:32:25 +08:00
@ksedz 确实哈哈变得太长后会这样,之前都没发现= =
jinmaoi
2020-04-26 10:14:49 +08:00
我也写过一个 C#版本的 https://www.seeull.com/archives/173.html
qiutianaimeili
2020-04-26 11:34:18 +08:00
不错,楼主还有自己的博客,厉害了!

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/665933

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX