js实现简单的贪吃蛇游戏
主要就是运用面向对象的思想来解决贪吃蛇的游戏。思路也不是很难,创建三个对象,食物,蛇还有游戏(用于游戏的初始化控制等)。采用改进的工厂模式创建对象,再利用原型创建公共方法。
1.食物有坐标(x,y),宽,高还有颜色五个属性。有初始化的方法(随机出现在地图上)。用自调用函数封装,然后把构造函数赋给windows,使得可以全局调用创建对象
//自调用函数food
(function () {
var elements = [];//用于保存食物元素
//食物对象,包含宽,高,颜色,横纵坐标属性
function Food(x, y, width, height, color) {
this.x = x || 0;
this.y = y || 0;
this.width = width || 20;
this.heigth = height || 20;
this.color = color || "green";
}
//为食物对象添加初始化方法(作用,随机出现在地图上)
Food.prototype.init = function (map) {
//remove the food first
remove();
//create div
var div = document.createElement("div");
//add div to map
map.appendChild(div);
//assign value to div
div.style.width = this.width + "px";//do not forget px!!!
div.style.height = this.heigth + "px";
div.style.backgroundColor = this.color;
//position is random
div.style.position = "absolute";
this.x = parseInt(Math.random() * (map.offsetWidth / this.width)) * this.width;
this.y = parseInt(Math.random() * (map.offsetHeight / this.heigth)) * this.width;
div.style.left = this.x + "px";
div.style.top = this.y + "px";
//add div to the elements
elements.push(div);
}
//private function to delete food
function remove() {
for (var i = 0; i < elements.length; i++) {
var ele = elements[i];
ele.parentNode.removeChild(ele);
elements.splice(i, 1);
}
}
window.Food = Food;
}());
2.蛇。蛇有宽,高,坐标还有颜色五个属性,因为蛇的长度会变,所以可以将蛇又拆分成多个对象,根据实际长改变其对象个数。宽高都是一样的,就坐标和颜色不同。用一个数组来储存这些对象,以便后面的修改。蛇有初始化的方法,创建标签元素显示在地图上,同时还有移动的方法,处理蛇的每次移动。
//snake
(function () {
//create an array to store snake
var elements = [];
//constructor for snake
function Snake(width, height, direction) {
//the width and height of every part of snake,all the same
this.width = width || 20;
this.height = height || 20;
//the body of snake
this.body = [
{x: 3, y: 2, color: "red"},//head
{x: 2, y: 2, color: "orange"},//body
{x: 1, y: 2, color: "orange"}//body
];
//direction
this.direction = direction || "right";
}
//create init function
Snake.prototype.init = function (map) {
//delete the snake before
remove();
// traverse the array to create div
for (let i = 0; i < this.body.length; i++) {
let obj = this.body[i];
//create div
let div = document.createElement("div");
//append div to map
map.appendChild(div);
//set style for div
div.style.position = "absolute";
div.style.width = this.width + "px";
div.style.height = this.height + "px";
div.style.left = obj.x * this.width + "px";
div.style.top = obj.y * this.height + "px";
div.style.backgroundColor = obj.color;
//add div to array to delete more convenient later
elements.push(div);
}
}
//move the snake
Snake.prototype.move = function (food, map) {
//change the position of body
var i = this.body.length - 1;
for (; i > 0; i--) {//make sure the later followed the head!
this.body[i].x = this.body[i - 1].x;
this.body[i].y = this.body[i - 1].y;
}
//change the position of head
switch (this.direction) {
case "right":
this.body[0].x++;
break;
case "left":
this.body[0].x--;
break;
case "top":
this.body[0].y--;
break;
case "bottom":
this.body[0].y++;
break;
}
//judge if the food was ate
//position of head of snake
var headX = this.body[0].x*this.width;
var headY = this.body[0].y*this.height;
//position of food
var foodX = food.x;
var foodY = food.y;
//judge
// console.log(this.body);
if (headX==foodX&&headY==foodY){
//the length of snake +1
var last = this.body[this.body.length-1];//刚开始加上的方块和原先的最后一块方块是重叠,但在下一次移动的时候加上的方块
//位置不变,因为会移到前面一块方块地方,而原先的尾巴就移走了,此时就没有重叠了,soga
this.body.push({
x:last.x,
y:last.y,
color:last.color
});
//delete food and generate new food
food.init(map);
}
}
//delete snake function
function remove() {
//by array
for (var i = elements.length - 1; i >= 0; i--) {
// delete
elements[i].parentNode.removeChild(elements[i]);
//delete from array,too
elements.splice(i, 1);
}
}
window.Snake = Snake;
}());
3.游戏对象,主要有食物还有蛇对象的创建,还有游戏的初始化,让蛇跑起来。同时包含键盘事件,用于控制蛇的移动,还有鼠标事件用于控制游戏的难度,暂停继续等。
//game
(function () {
var that = null;
var speed = 200;
//create constructor of game
function Game(map) {
this.food = new Food();
this.snake = new Snake();
this.map = map;
this.timeId = null;
that = this;//use for setInterval
}
Game.prototype.init = function () {
//init food
this.food.init(this.map);
//init snake
this.snake.init(this.map);
//play the game
this.run(this.food, this.map);
//change direction
this.bindKey();
this.bindMouse();
// setInterval(function () {
// this.snake.move()
// })this is window ,so cannot use it
// setInterval(function () {
// that.snake.move(that.food,that.map);
// that.snake.init(that.map);
// },150);//do not use setInterval here
}
//snake running
Game.prototype.run = function (food, map) {
if (this.timeId){
clearInterval(this.timeId);
}
this.timeId = setInterval(function () {
var snake = this.snake;
//move the snake
snake.move(food, map);
//init snake
snake.init(map);
//the max x and y
var maxX = map.offsetWidth / snake.width;
var maxY = map.offsetHeight / snake.height;
//the position of snake
var headX = snake.body[0].x;
var headY = snake.body[0].y;
//judge
if (headX < 0 || headX >= maxX) {
clearInterval(this.timeId);
alert("game over");
}
if (headY < 0 || headY >= maxY) {
clearInterval(this.timeId);
alert("game over");
}
for (let i = 1;i <snake.body.length;i++){
if (headX==snake.body[i].x&&headY==snake.body[i].y){
clearInterval(this.timeId);
alert("game over");
}
}
}.bind(that), speed)//use bind to bind Game object
}
//get the key to change the direction
Game.prototype.bindKey = function(){
//get the key user press and change the direction
document.addEventListener("keydown",function (e) {
//this is document ,so use bind function
// switch (e.keyCode) {
// case 37:this.snake.direction = "left";break;
// case 38:this.snake.direction = "top";break;
// case 39:this.snake.direction = "right";break;
// case 40:this.snake.direction = "bottom";break;
// }
//when left or right,cannot press left or right,only top or bottom,yeah
if (this.snake.direction=="left"||this.snake.direction=="right") {
switch (e.keyCode) {
case 38:this.snake.direction = "top";break;
case 40:this.snake.direction = "bottom";break;
}
}else{
switch (e.keyCode) {
case 37:this.snake.direction = "left";break;
case 39:this.snake.direction = "right";break;
}
}
}.bind(that),false)
}
//bind mouse click event
Game.prototype.bindMouse = function(){
var btn = document.getElementById("btn").getElementsByTagName("button");
for (let i = 0;i < btn.length-2;i++){
btn[i].onclick = function () {
speed=250-50*i;
if (i==4){
speed = 20;
}
this.run(this.food, this.map);
}.bind(that);
}
//pause
btn[5].onclick = function () {
clearInterval(this.timeId);
}.bind(that);
//continue
btn[6].onclick = function () {
this.run(this.food, this.map);
}.bind(that);
}
window.Game = Game;
}());
将以上三个放在三个js文件中,如下是html内容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>贪吃蛇</title>
<style>
.map {
width: 800px;
height: 580px;
background-color: #CCCCCC;
position: relative;
}
button{
width: 80px;
height: 30px;
background-color: #0000ff;
color: #ff0000;
position: absolute;
font-size: 14px;
}
/*choose the first child of the father of button*/
button:nth-child(1){
top: 50px;
left: 860px;
}
button:nth-child(2){
top: 100px;
left: 860px;
}
button:nth-child(3){
top: 150px;
left: 860px;
}
button:nth-child(4){
top: 200px;
left: 860px;
}
button:nth-child(5){
top: 250px;
left: 860px;
}
button:nth-child(6){
top: 300px;
left: 860px;
}
button:nth-child(7){
top: 350px;
left: 860px;
}
</style>
</head>
<body>
<!--画出地图,设置样式-->
<div class="map"></div>
<div id="btn">
<button id="easy">easy</button>
<button id="normal">normal</button>
<button id="hard">hard</button>
<button id="veryHard">very hard</button>
<button id="crazy">crazy</button>
<button id="pause">pause</button>
<button id="continue">continue</button>
</div>
<script src="food.js"></script>
<script src="snake.js"></script>
<script src="game.js"></script>
<script>
//test the game
var map = document.querySelector(".map");
var game = new Game(map);
game.init();
// //外部测试代码
// var fd = new Food();
// fd.init(map);
// var sk = new Snake();
// sk.init(map);//generate snake
// sk.move(fd,map);//move snake
// sk.init(map);//delete former snake and generate new snake
</script>
</body>
</html>
至此,一个简单的贪吃蛇游戏就完成了,后面的重来,计分等功能可以自己添加。效果如下: