JavaScript貪喫蛇
// 思路分析
// 三個對象 map snake Food
// map 寬800px和高500px 可以整除 snake每節的寬度
// food 寬10px 高10px 相當於每一節蛇的寬度 同樣有(x,y)
// snake 不是一個整體,分爲三節 頭 身 尾 每節都有自己的座標(x,y),但是三節又是連着的
// 功能介紹
/*
通過蛇頭喫到在界面上隨機出現的點,然後增加自身的長度
增加的原理:就是在body數組中增加一條數據,喫到一個點後,重新創建一條蛇
點(food):界面上的點每隔一定時間會隨機出現,因爲點是一個對象,我們把界面上所有隨機出現的點放在一個allFood數組中,當蛇喫到點後,會去這個數組中匹配喫到的那個點,然後移除該點(這樣做遊戲的性能會比較差,因爲當界面上點很多很多的時候,這個判斷就比較費時了,時間複雜度貌似O(n))
空格鍵控制暫停開始,以及蛇的速度,原理:修改定時器的時間,時間越小,蛇移動越快
*/
// 蛇的移動方式
/*
1、蛇的移動方向由蛇頭控制
2、後一個節點走到前一個節點的位置上,頭的位置是我們控制的
3、移動的距離是一個點的單位長度,而不是px,這個根據自己的點大小進行控制
4、移動的範圍是在地圖內,注意:蛇不是以px移動的,所以一上面的爲例,蛇的移動範圍在 0<=x<=800 0<=y<=500,但是這是距離,水平移動的次數 0~80 垂直移動的次數 0~50
*/
// 蛇的死亡情況
/*
1、當蛇頭碰到邊界的時候
2、當蛇頭碰到自身的時候
*/
不多說,直接上代碼,在代碼中有註釋,自己理解
界面上調用
<script>
var map = new Map();
var food = new Food();
var snake = new Snake();
var speed = 200;
var allFood = [];
allFood.push(food);
var timeId1 = setInterval(() => {
var food = new Food();
allFood.push(food);
}, 5000);
var timeId = setInterval("snake.move(timeId,timeId1,allFood)", speed);
var isPause = true;
document.onkeydown = function (e) {
switch (e.keyCode) {
case 37:
if (snake.direction != "right") {
snake.direction = "left";
}
break;
case 38:
if (snake.direction != "down") {
snake.direction = "up";
}
break;
case 39:
if (snake.direction != "left") {
snake.direction = "right";
}
break;
case 40:
if (snake.direction != "up") {
snake.direction = "down";
}
break;
case 32:
if (isPause) {
clearInterval(timeId);
isPause = false;
} else {
speed -= 50;
if (speed <= 50) speed = 50;
timeId = setInterval("snake.move(timeId,timeId1,allFood)", speed);
isPause = true;
}
break;
}
}
</script>
地圖map對象(其實是構造函數,但是用的是oop的思想)
一個全局變量count,用於計分
var count = 0;
// 定義地圖對象構造函數
function Map() {
// 設置地圖對象的寬高和背景色
this.width = 800;
this.height = 500;
this.bgColor = "#000";
// 創建map對象,並且顯示在界面上
this.showMap = function () {
var map = document.createElement("div");
map.setAttribute("id", "map");
map.style.width = this.width + "px";
map.style.height = this.height + "px";
map.style.position = "relative";
map.style.backgroundColor = this.bgColor;
map.style.margin = "50px auto";
document.body.appendChild(map);
var score = document.createElement("div");
score.setAttribute("id", "score");
score.style.width = this.width + "px";
score.style.height = "50px";
score.style.position = "absolute";
score.style.top = "-50px";
score.style.left = "0px";
score.style.backgroundColor = "#ccc";
score.innerText = "當前分數:" + count;
map.appendChild(score);
}
// 調用方法,創建對象
this.showMap();
}
點對象(Food)
// 定義食物對象構造函數
function Food() {
this.width = 10;
this.height = 10;
this.color = "#fff";
this.showFood = function () {
var food = document.createElement("div");
this.node = food;
food.style.width = this.width + "px";
food.style.height = this.height + "px";
food.style.backgroundColor = this.color;
food.style.borderRadius = "50%";
food.style.position = "absolute";
// 現在map對象的寬度是800 ,而Food對象的寬度是10 ,相當於 x的座標取值∈[0,79]
// 如果取到80.那麼 * this.width後就會Food的left就是800 就會超過map
this.x = parseInt(Math.random() * 79);
this.y = parseInt(Math.random() * 49);
// 隨機出現在地圖上
/*
如果上面直接取800或500內的隨機數,就會導致出現在不能被蛇喫到,
還有一點就是會超出邊界, 比如 794 + this.width = 804
同樣的,這個點就會導致snake無法喫到,因爲蛇的移動是以 10px 爲一個移動單位的
*/
food.style.left = this.x * this.width + "px";
food.style.top = this.y * this.height + "px";
var map = document.querySelector('#map');
map.appendChild(food);
}
this.showFood();
}
蛇對象(snake)
// 定義蛇對象構造函數
function Snake() {
this.width = 10;
this.height = 10;
this.direction = "right"; // 默認向右移動
// 定義初始的蛇,之後喫到food後就會往這個數組中添加喫到的food
// posX 設置的是蛇尾的位置,初始的時候蛇長 爲 3 個單位, 所以蛇尾的取值在 [0,77]之間
var posX = parseInt(Math.random() * 77);
// var posX = 77;
var posY = parseInt(Math.random() * 49);
this.body = [
{ x: posX + 2, y: posY, color: "darkblue" }, // 定義初始蛇頭的位置
{ x: posX + 1, y: posY, color: "darkcyan" }, // 定義初始蛇身的位置
{ x: posX, y: posY, color: "skyblue" } // 定義初始蛇尾的位置
];
var map = document.querySelector('#map');
// 顯示蛇
this.showSnake = function () {
for (var i = 0; i < this.body.length; i++) {
var s = document.createElement("div");
this.body[i].node = s;
s.style.width = this.width + "px";
s.style.height = this.height + "px";
s.style.backgroundColor = this.body[i].color;
s.style.borderRadius = "50%";
s.style.position = "absolute";
s.style.left = this.body[i].x * this.width + "px";
s.style.top = this.body[i].y * this.height + "px";
map.appendChild(s);
}
}
/*
移動規則:只控制蛇頭的方向,後續節點到前一個節點的位置上
*/
this.move = function (timeId, timeId1, allFood) {
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 "right":
// 蛇頭的x座標往右移動一個單位
this.body[0].x += 1;
break;
case "left":
this.body[0].x -= 1;
break;
case "up":
// 蛇頭往上,距離map的頂邊距離就越小,所以是減一個單位
this.body[0].y -= 1;
break;
case "down":
this.body[0].y += 1;
break;
}
for (var i = 0; i < allFood.length; i++) {
// 當蛇頭的座標和Food的座標一致的時候就表示喫到點了
if (this.body[0].x == allFood[i].x && this.body[0].y == allFood[i].y) {
// if (this.body[0].x == food.x && this.body[0].y == food.y) {
/*
爲什麼這麼賦值?
這一次的移動執行完之後,到下一次的移動時就會讓後一個點的座標等於前一個的座標
因此下一次的時候就可以給這個點賦座標了
*/
this.body.push({ x: null, y: null, color: "skyblue", node: null })
// 把喫掉的那個點從map上移除
map.removeChild(allFood[i].node);
allFood.splice(i, 1);
// 重新生成新的點
// food.showFood();
count++;
score.innerText = "當前分數:" + count;
}
}
// 死亡情況,1、撞邊界
if (this.body[0].x < 0 || this.body[0].x >= 80 || this.body[0].y < 0 || this.body[0].y >= 50) {
clearInterval(timeId);
clearInterval(timeId1);
alert("GAME OVER!");
// 移除死蛇
for (var i = 0; i < this.body.length; i++) {
/*
極限情況,當Food處於(790,0)的位置,蛇剛喫到就死了,導致還未在界面上創建出節點
所以這個情況下的node是不用移除的
*/
if (this.body[i].node != null) {
map.removeChild(this.body[i].node);
}
}
// 遊戲重置
var posX = parseInt(Math.random() * 77);
var posY = parseInt(Math.random() * 49);
this.body = [
{ x: posX + 2, y: posY, color: "darkblue" }, // 定義初始蛇頭的位置
{ x: posX + 1, y: posY, color: "darkcyan" }, // 定義初始蛇身的位置
{ x: posX, y: posY, color: "skyblue" } // 定義初始蛇尾的位置
];
this.direction = "right";
this.showSnake();
location.reload();
return false;
}
// 死亡情況,2、咬自己
for (var i = 4; i < this.body.length; i++) {
// 因爲只有從第5節開始,蛇纔可能咬到自己
if (this.body[0].x == this.body[i].x && this.body[0].y == this.body[i].y) {
clearInterval(timeId);
clearInterval(timeId1);
alert("GAME OVER,咬到自己了!")
// 移除死蛇
for (var i = 0; i < this.body.length; i++) {
/*
極限情況,當Food處於(790,0)的位置,蛇剛喫到就死了,導致還未在界面上創建出節點
所以這個情況下的node是不用移除的
*/
if (this.body[i].node != null) {
map.removeChild(this.body[i].node);
}
}
// 遊戲重置
var posX = parseInt(Math.random() * 77);
var posY = parseInt(Math.random() * 49);
this.body = [
{ x: posX + 2, y: posY, color: "darkblue" }, // 定義初始蛇頭的位置
{ x: posX + 1, y: posY, color: "darkcyan" }, // 定義初始蛇身的位置
{ x: posX, y: posY, color: "skyblue" } // 定義初始蛇尾的位置
];
this.direction = "right";
this.showSnake();
location.reload();
return false;
}
}
/*
移除原因:
讓後面的點往前移動一節,但是最後一節一直沒有刪除,導致蛇長增加
移除後調用showSnake()方法,根據現有的body長度進行生成蛇
*/
for (var i = 0; i < this.body.length; i++) {
// 因爲執行顯示的代碼在下面,表示現在蛇的node還沒有存值
if (this.body[i].node != null) {
map.removeChild(this.body[i].node);
}
}
this.showSnake();
}
}
總結:上面的還是有一些不足的地方,比如:當蛇死了,重新開始遊戲需要刷新瀏覽器,以及蛇的速度不能根據分數進行自動的調節,這部分還是有待完善的。
參考博客:https://blog.csdn.net/snow_small/article/details/79108630
demo下載:https://download.csdn.net/download/f_felix/11151401