1. 程式人生 > >面向對象案例——貪吃蛇遊戲

面向對象案例——貪吃蛇遊戲

函數聲明 上線 auto ech 對象 render 暴露 ive food

最近項目上線,近一個星期沒更博了,今天來寫一個經典的遊戲案例——貪吃蛇。在這個簡單的案例裏可以體會javaScript 面向對象開發相關模式,學習使用面向對象的方式分析問題。

1.功能實現

1.1 搭建頁面:放一個容器盛放遊戲場景 div#map,設置樣式

<div class="map" id="map"></div>
 1 <style>
 2         #map{
 3             background-color: #000;
 4             width: 1500px;
 5             height: 700px
; 6 position: relative; 7 left: 0; 8 top: 0; 9 } 10 </style>

1.2 分析對象:食物對象、蛇對象、遊戲對象

1.3 創建食物對象Food

⑴ 屬性:位置(x,y)、大小(width、height)、顏色(color)

1 // 創建Food的構造函數,並設置屬性
2 function Food(width,height,bgColor) {
3     // 食物的寬度和高度(像素)
4     this.width=width||10;
5
this.height=height||10; 6 // 食物的顏色 7 this.bgColor=bgColor||"white"; 8 }

⑵ 方法:render() 隨機創建一個食物對象,並輸出到map上

 1 // 通過原型設置render方法,實現隨機產生食物對象,並渲染到map上
 2 Food.prototype.render=function (map) {
 3     remove(map);  
 4     // 隨機食物的位置,map.寬度/food.寬度,總共有多少分food的寬度,隨機一下。然後再乘以food的寬度
 5     this.x=Math.floor(Math.random()*(map.offsetWidth/this.width))*this.width;
 6
this.y=Math.floor(Math.random()*(map.offsetHeight/this.height))*this.height; 7 // 動態創建食物對應的div 8 var newDiv=document.createElement("div"); 9 newDiv.style.position="absolute"; 10 newDiv.style.left=this.x+"px"; 11 newDiv.style.top=this.y+"px"; 12 newDiv.style.backgroundColor=this.bgColor; 13 newDiv.style.width=this.width+"px"; 14 newDiv.style.height=this.height+"px"; 15 map.appendChild(newDiv); 16 li.push(newDiv); 17 }

1.4 創建蛇對象Snake

⑴ 屬性:大小(width、height)、顏色(color)、方向(direction)、身體數組對象(body)

 1 // Snake構造函數
 2 function Snake(width,height,bgColor,direction) {
 3     // 設置每一個蛇節的寬度
 4     this.width=width||10;
 5     this.height=height||10;
 6     this.bgColor=bgColor||"white";
 7     // 蛇的運動方向
 8     this.direction=direction||"right";
 9     // 蛇的每一部分, 第一部分是蛇頭
10     this.body=[
11         {x:3,y:1},
12         {x:2,y:1},
13         {x:1,y:1}
14     ];
15 }

⑵ 方法:render() 把蛇渲染到map上

 1 // render方法,原理與渲染食物相同
 2 Snake.prototype.render=function (map) {
 3     remove(map);
 4     for (var i = 0; i < this.body.length; i++) {
 5         var newDiv=document.createElement("div");
 6         newDiv.style.position="absolute";
 7         newDiv.style.left=this.body[i].x*this.width+"px";
 8         newDiv.style.top=this.body[i].y*this.height+"px";
 9         newDiv.style.width=this.width+"px";
10         newDiv.style.height=this.height+"px";
11         newDiv.style.backgroundColor=this.bgColor;
12         map.appendChild(newDiv);
13         list.push(newDiv);
14     }
15 }

1.5 創建遊戲對象Game(用來管理遊戲中的所有對象和開始遊戲)

⑴ 屬性:food、snake、map

1 // Game構造函數
2 function Game(map) { 
3     this.map=map;
4     this.snake=new Snake();
5     this.food=new Food();
6     that=this;
7 }

⑵ 方法:start() 開始遊戲(繪制所有遊戲對象)

1 //  開始遊戲,渲染食物對象和蛇對象
2 Game.prototype.startGame=function () { 
3     this.food.render(this.map);
4     this.snake.render(this.map);
5     autoMove();
6     keyBind();
7 }
// 在自調用函數中暴露Game對象
window.Game=Game;

2.遊戲邏輯

2.1 蛇的move方法

⑴ 在蛇對象(snake.js)中,在Snake的原型上新增move方法

⑵ 讓蛇移動起來,把蛇身體的每一部分往前移動一下

⑶ 蛇頭部分根據不同的方向決定 往哪裏移動

 1 Snake.prototype.move=function (food,map) {
 2     //  讓蛇身體的每一部分往前移動一下
 3    for (var i = this.body.length-1; i >0; i-- ){
 4        this.body[i].x=this.body[i-1].x;
 5        this.body[i].y=this.body[i-1].y; 
 6    }
 7     //  根據移動的方向,決定蛇頭如何處理
 8     switch (this.direction) {
 9         case "left":
10             this.body[0].x--;
11             break;
12         case "right":
13             this.body[0].x++;
14             break;
15         case "up":
16             this.body[0].y--;
17             break;
18         case "down":
19             this.body[0].y++;
20             break;
21         default:
22             break;
23     }
24 }
//在game中測試
this.snake.move(this.food, this.map);
this.snake.render(this.map);

2.2 讓蛇自己動起來

⑴ 在game.js中 添加autoMove的私有方法,開啟定時器調用蛇的move和render方法,讓蛇動起來(私有方法即不能被外部訪問的方法,使用自調用函數包裹)

 1 function autoMove() { 
 2     var timeId=setInterval(function () {
 3         this.snake.move(this.food,this.map);
 4         this.snake.render(this.map);
 5         // 判斷蛇是否撞墻
 6         var snakeHeadX=this.snake.body[0].x*this.snake.width;
 7         var snakeHeadY=this.snake.body[0].y*this.snake.width;
 8         if(snakeHeadX<0 || snakeHeadY<0 || snakeHeadX>=this.map.offsetWidth || snakeHeadY>=this.map.offsetHeight){
 9             clearInterval(timeId);
10             alert("Game over!");
11         }
12     }.bind(that),50);
13 }

⑵ 在snake中添加刪除蛇的私有方法,在render中調用

1 function remove(map) {
2     for (var i = 0; i < list.length; i++) {
3         map.removeChild(list[i]);
4     }
5     list.length=0;
6 }

⑶ 在game中通過鍵盤控制蛇的移動方向

 1 function keyBind() { 
 2     window.onkeydown=function (e) { 
 3     e=e||window.event;
 4     e.keyCode= e.keyCode|| e.charCode|| e.which;            
 5     // console.log(e.keyCode);
 6     switch (e.keyCode) {
 7         case 37:
 8             if(this.snake.direction!="right"){
 9                 this.snake.direction="left";
10             }
11             break;
12         case 38:
13             if (this.snake.direction != "down") {
14                 this.snake.direction = "up";
15             }
16             break; 
17         case 39:
18             if (this.snake.direction != "left") {
19                 this.snake.direction = "right";
20             }
21             break; 
22         case 40:
23             if (this.snake.direction != "up") {
24                 this.snake.direction = "down";
25             }
26             break;
27         default:
28             break;
29         }
30     }.bind(that);
31 }

⑷ 在start方法中調用keyBind()

2.3 判斷蛇是否吃到食物

在Snake的move方法中添加判斷

 1 // 在移動的過程中判斷蛇是否吃到食物
 2 var snakeHeadX=this.body[0].x*this.width;
 3 var snakeHeadY=this.body[0].y*this.height;
 4 var snakeTile=this.body[this.body.length-1];
 5 // 如果蛇頭和食物的位置重合代表吃到食物    
 6 if(snakeHeadX==food.x && snakeHeadY==food.y){
 7 // 吃到食物,往蛇節的最後加一節
 8     this.body.push({
 9 // 食物的坐標是像素,蛇的坐標是幾個寬度,進行轉換            
10         x:snakeTile.x,
11         y:snakeTile.y
12     });
13 // 把現在的食物對象刪除,並重新隨機渲染一個食物對象
14     food.render(map);        
15 }

★ ★ 自調用函數的參數

1 (function (window, undefined) {
2   var document = window.document;
3 }(window, undefined))

⑴ 傳入window對象:代碼壓縮的時候,可以把function (window) 壓縮成 function (w)

⑵ 傳入undefined:把undefined作為函數的參數(當前案例沒有使用) ,防止undefined 被重新賦值,因為在有的老版本的瀏覽器中 undefined可以被重新賦值

★ ★ 關於自調用函數的問題

如果存在多個自調用函數要用分號分割,否則語法錯誤

 1 // 下面代碼會報錯
 2 (function () {
 3 }())
 4 
 5 (function () {
 6 }())
 7 // 所以代碼規範中會建議在自調用函數之前加上分號
 8 // 下面代碼沒有問題
 9 ;(function () {
10 }())
11 
12 ;(function () {
13 }())

當自調用函數前面有函數聲明時,會把自調用函數作為參數

1 // 所以建議自調用函數前,加上;
2 var a = function () {
3   alert(‘11‘);
4 }
5 
6 (function () {
7   alert(‘22‘);
8 }())

面向對象案例——貪吃蛇遊戲