V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
爱意满满的作品展示区。
Liulang007
V2EX  ›  分享创造

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

  •  
  •   Liulang007 · 2020-04-25 12:29:26 +08:00 · 2163 次点击
    这是一个创建于 1456 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前言

    本课程是通过 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>
    
    7 条回复    2020-04-26 11:34:18 +08:00
    zj1926
        1
    zj1926  
       2020-04-25 12:34:48 +08:00 via iPhone
    以前面试的时候我也写过一个 https://github.com/im6/vanilla-snake
    Liulang007
        2
    Liulang007  
    OP
       2020-04-25 12:53:29 +08:00
    @zj1926 不错啊
    ksedz
        3
    ksedz  
       2020-04-25 13:29:03 +08:00
    提个 bug:往左走的时候同时按下和右可能导致直接向右走或者把自己咬死
    Liulang007
        4
    Liulang007  
    OP
       2020-04-25 16:39:51 +08:00
    @ksedz 是不是按太快了,我这边不会这样
    Liulang007
        5
    Liulang007  
    OP
       2020-04-25 17:32:25 +08:00
    @ksedz 确实哈哈变得太长后会这样,之前都没发现= =
    jinmaoi
        6
    jinmaoi  
       2020-04-26 10:14:49 +08:00
    我也写过一个 C#版本的 https://www.seeull.com/archives/173.html
    qiutianaimeili
        7
    qiutianaimeili  
       2020-04-26 11:34:18 +08:00
    不错,楼主还有自己的博客,厉害了!
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2790 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 10:04 · PVG 18:04 · LAX 03:04 · JFK 06:04
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.