分析
把貪吃蛇這個游戲當做一個物件,他又由一下部分組成:
1.食物; Food (寬,高,顏色,坐標xy)
2.小蛇; Snake (寬,高,顏色,坐標xy,小蛇移動)
3.游戲規則;Game(地圖map) (游戲結束和勝利,用戶鍵盤控制,解決連續鍵盤事件)
我們將這三部分也當做三個物件,逐個分析,對它們添加自己的屬性和方法,因為地圖比較簡單只需要設定樣式即可,就不做分析,分析順序按照難度從易到難來分析;(形參皆為游戲物件后期傳進去的屬性)
宣告:本文有借助CSDN博主id:那個方的思路(吐槽下:原貼上缺少地圖物件,還有幾行莫名代碼(可能是缺失物件的));然后處理了幾個bug,添加了幾個功能,如下:
- 撞擊自己game over
- 食物不會重繪在小蛇的身體上
- 解決連續鍵盤事件game.flag
- 添加游戲勝利條件
- 添加游戲開始(重新開始)
- 添加暫停繼續
代碼如下:(有具體注釋)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>貪吃蛇</title>
<style type="text/css">
* {
margin: 0;
}
.map {
margin: 40px auto 20px;
position: relative;
height: 400px;
width: 800px;
background: rgb(206, 176, 176);
}
.control {
margin: 0 auto;
text-align: center;
}
button {
margin: 0 40px 0 40px;
width: 60px;
height: 30px;
}
</style>
</head>
<body>
<div class="map"></div>
<div class="control">
<button class="start">開始</button>
<button class="stop">暫停</button>
</div>
<script>
// 食物
;(function () {
// 用來存盤渲染到頁面上的食物物件
var elements = []
// 創建食物建構式
// 有寬高顏色屬性 食物的坐標
function Food(width, height, color) {
this.width = width || 20
this.height = height || 20
this.color = color || 'skyblue'
// 食物的坐標
this.x = 0
this.y = 0
}
Food.prototype.init = function (map, snake) {
this.removeFood()
// 創建食物div模型
var div = document.createElement('div')
// 食物div的寬高顏色
div.style.width = this.width + 'px'
div.style.height = this.height + 'px'
div.style.backgroundColor = this.color
// 食物div產生的位置是隨機的
this.x = parseInt(Math.random() * (map.offsetWidth / this.width))
this.y = parseInt(Math.random() * (map.offsetHeight / this.height))
// 如果食物產生在小蛇身體上,則重新產生
while (true) {
var i
for (i = 0; i < snake.body.length; i++) {
if (this.x == snake.body[i].x && this.y == snake.body[i].y) {
this.x = parseInt(Math.random() * (map.offsetWidth / this.width))
this.y = parseInt(Math.random() * (map.offsetHeight / this.height))
break
}
}
if (i == snake.body.length) break
}
// 食物div盒子的位置
// 定位
div.style.position = 'absolute'
div.style.left = this.x * this.width + 'px'
div.style.top = this.y * this.height + 'px'
// 將食物div放進map中
map.appendChild(div)
// 將食物存盤,方便后面洗掉
elements.push(div)
}
// 當吃到食物時,清除食物坐標
Food.prototype.removeFood = function () {
for (var i = 0; i < elements.length; i++) {
elements && elements[i].remove()
elements.splice(0, 1)
}
}
window.Food = Food
})()
// 小蛇
;(function () {
// 存盤小蛇物件的陣列
var elements = []
// 小蛇物件
function Snake(width, height, direction) {
// 小蛇身體每部分的寬高
this.width = width || 20
this.height = height || 20
// 小蛇頭部朝向
this.direction = direction || 'right'
// 小蛇上一次朝向 用來比較本次與上次的方向是否有沖突
this.beforeDirection = this.direction
// 小蛇坐標和顏色
this.body = [
{ x: 3, y: 2, color: 'red' },
{ x: 2, y: 2, color: 'gray' },
{ x: 1, y: 2, color: 'gray' }
]
}
// 小蛇初始化
Snake.prototype.init = function (map) {
// 清除前一次小蛇元素物件
this.removeSnake()
// 重新創建小蛇坐標
for (var i = 0; i < this.body.length; i++) {
var div = document.createElement('div')
map.appendChild(div)
// 小蛇身體每部分的寬高
div.style.width = this.width + 'px'
div.style.height = this.height + 'px'
// 小蛇身體每部分的坐標 顏色
div.style.position = 'absolute'
div.style.left = this.body[i].x * this.width + 'px'
div.style.top = this.body[i].y * this.height + 'px'
div.style.backgroundColor = this.body[i].color
div.style.borderRadius = '50%'
// 將小蛇物件存盤到elements中
elements.push(div)
}
}
// 創建清除小蛇的函式
Snake.prototype.removeSnake = function () {
for (var i = 0; i < elements.length; i++) {
elements[i].remove()
}
elements.splice(0, elements.length)
}
// 小蛇移動函式,小蛇移動就是除頭部以外的部分向前移動一個身位
Snake.prototype.move = function (map, food) {
// 小蛇移動就是除頭部以外的部分向前移動一個身位,即前一個狀態的最后一個身位丟棄
var index = this.body.length - 1
for (var i = index; i > 0; i--) {
this.body[i].x = this.body[i - 1].x
this.body[i].y = this.body[i - 1].y
}
// 小蛇的頭部移動依靠當前direction來判斷坐標
switch (this.direction) {
case 'right':
this.body[0].x++
break
case 'left':
this.body[0].x--
break
case 'up':
this.body[0].y--
break
case 'down':
this.body[0].y++
break
}
// console.log(this.body[0].x, this.body[0].y)
// 判斷是否吃到了食物
if (this.body[0].x == food.x && this.body[0].y == food.y) {
// 重新生成食物
food.init(map, this)
// 得到小蛇目前狀態的身體最后一點的屬性
var last = this.body[this.body.length - 1]
this.body.push({ x: last.x, y: last.y, color: last.color })
}
}
// 將Snake建構式傳給window作為其屬性
window.Snake = Snake
})()
// 地圖map html中類名為map的即為地圖
// 游戲規則Game
;(function () {
// 創造游戲建構式
function Game() {
this.food = new Food()
this.snake = new Snake()
this.map = document.querySelector('.map')
// 用于判斷連續按鍵觸發方向改變
this.flag = false
}
// 游戲規則 不能碰壁 不能撞擊自身 獲得勝利
Game.prototype.death = function () {
this.timer = setInterval(
function () {
this.snake.move(this.map, this.food)
// 將移動后的坐標渲染在頁面上
this.snake.init(this.map)
// 重新渲染后方向按鍵才生效
this.flag = true
// 根據小蛇頭部坐標來判斷小蛇是否撞墻死亡
var maxX = this.map.offsetWidth / this.snake.width
var maxY = this.map.offsetHeight / this.snake.height
// 小蛇頭部坐標
var headX = this.snake.body[0].x
var headY = this.snake.body[0].y
if (headX < 0 || headX >= maxX) {
this.snake.removeSnake()
clearInterval(this.timer)
alert('游戲結束')
}
if (headY < 0 || headY >= maxY) {
this.snake.removeSnake()
clearInterval(this.timer)
alert('游戲結束')
}
// 判斷小蛇是否撞到自己的身體
for (var i = 1; i < this.snake.body.length; i++) {
if (this.snake.body[0].x == this.snake.body[i].x && this.snake.body[0].y == this.snake.body[i].y) {
this.snake.removeSnake()
clearInterval(this.timer)
alert('游戲結束')
}
}
// 游戲勝利
if (maxX * maxY == this.snake.body.length) alert('恭喜你,吃完了所有食物')
}.bind(this),
100
)
}
// 注冊鍵盤按下事件
Game.prototype.changeDirection = function () {
document.addEventListener(
'keydown',
function (e) {
if (this.flag) {
switch (e.key) {
case 'ArrowRight':
// 如果小蛇上一個狀態頭部朝向是向左,則按右鍵不更改方向;否則,改變頭部朝向向右
this.snake.direction = this.snake.beforeDirection == 'left' ? 'left' : 'right'
break
case 'ArrowLeft':
// 如果小蛇上一個狀態頭部朝向是向右,則按左鍵不更改方向;否則,改變頭部朝向向左
this.snake.direction = this.snake.beforeDirection == 'right' ? 'right' : 'left'
break
case 'ArrowUp':
// 如果小蛇上一個狀態頭部朝向是向下,則按上鍵不更改方向;否則,改變頭部朝向向上
this.snake.direction = this.snake.beforeDirection == 'down' ? 'down' : 'up'
break
case 'ArrowDown':
// 如果小蛇上一個狀態頭部朝向是向上,則按下鍵不更改方向;否則,改變頭部朝向向下
this.snake.direction = this.snake.beforeDirection == 'up' ? 'up' : 'down'
break
}
// 同步方向
this.snake.beforeDirection = this.snake.direction
this.flag = false
}
// this指標指向game物件
}.bind(this)
)
}
// 將Game建構式傳給window作為其屬性
window.Game = Game
})()
;(function () {
// 獲取開始按鈕 也是重新開始按鈕
var gameStart = document.querySelector('.start')
// 獲取暫停按鈕
var gameStop = document.querySelector('.stop')
var game = null
// 開始按鈕 也是重新開始按鈕
gameStart.addEventListener('click', function () {
// 如果game物件存在則清空資料
if (game) {
// 清空食物
game.food.removeFood()
// 終止定時器
clearInterval(game.timer)
// 移除小蛇元素
game.snake.removeSnake()
// game物件置空
game = null
}
game = new Game()
game.food.init(game.map, game.snake)
game.death()
game.changeDirection()
})
// 暫停/繼續按鈕
gameStop.addEventListener('click', function () {
if (game && this.innerHTML.trim() == '繼續') {
clearInterval(game.timer)
game.death()
this.innerHTML = '暫停'
} else if (game && this.innerHTML.trim() == '暫停') {
clearInterval(game.timer)
this.innerHTML = '繼續'
}
})
})()
</script>
</body>
</html>
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/237984.html
標籤:其他
