JavaScript简单贪吃蛇,基本面向对象
没有写博客的习惯,这篇算心血来潮,算篇近几天编写的小程序纪实.
以编写此程序的方式结束Javascript的本阶段的学习.编写的目的在于熟悉javascript的编程方式,包括代码风格,面向对象的运用等.
回到程序,说说Snake的移动的实现方法.其实很简单,向头部添加Unit,然后删除尾部.其他,参见注释.
程序包括一个html文件:snake.html和一个js文件:snake.js
snake.html:
$s(function(){
$s.SnakeContext.init();
});
snake.js:
/*
* JavaScript简单贪吃蛇.基本面向对象.
* 规则:
* 1.没有墙,左与右连接,上与下连接.
* 2.当蛇头碰撞到自身时死亡.
* 兼容性:
* 完全支持Firefox,Chrome
* 基本支持IE(除了调试部分)
*
* 作者:pcenshao
* 转载请注明来自:
* http://blog.csdn.net/pywepe
* http://pcenshao.taobao.com
*/
(function(){
$s = function(){
if(arguments.length == 1){
if(typeof arguments[0] == "string"){
return document.getElementById(arguments[0]);
}else if(typeof arguments[0] == "function"){
window.onload = arguments[0];
}
}
};
$s.UNIT_WIDTH = 10; // 单元的宽度
$s.UNIT_HEIGHT = 10;
$s.PANEL_WIDTH = 30; // 逻辑宽度
$s.PANEL_HEIGHT = 20; // 逻辑高度
$s.STEP = 250 ; // 每一步的时间
$s.HEAD_COLOR = "red"; // 蛇头颜色
$s.BODY_COLOR = "black"; // 蛇体颜色
/*
* 食物的颜色
*/
$s.COLORS = ["blue","green","#494e8f","#905d1d","#845538","#77ac98","#8552a1"];
/*
* 调试相关
* $s.DEBUG 调试信息显示开关
* $s.KEY_UP_DIR_ID 监视方向键的结点id,若不存在,则不显示
* $s.HEAD_LOCATION_ID 监视蛇头位置的结点id,若不存在,则不显示
*/
$s.DEBUG = false;
$s.KEY_UP_DIR_ID = "keyup";
$s.HEAD_LOCATION_ID = "headLocation";
$s.Dir = { // 代表方向,强制以$s.Dir.UP方法调用,避免参数错误
UP : {},
DOWN : {},
LEFT : {},
RIGHT : {},
NONE : {}
};
$s.State = { // 代表状态
STOP : {},
RUNNGIN : {},
PAUSE : {}
};
$s.Unit = function(){ // 一个单元格,用MVC的眼光看,Unit是模型,UnitView是视图
this.x = 0;
this.y = 0;
this.view = new $s.UnitView();
this.view.unit = this;
this.color = $s.BODY_COLOR;
};
$s.Unit.prototype.repaint = function(){
if(this.view != null){
this.view.repaint(); // 通知重绘
}
};
$s.Snake = function(){
this.units = [];
};
$s.Snake.prototype.init = function(dir,count){
var x = 5;
var y = 5;
for(var i = 0 ; i < count ; i ++){
var u = new $s.Unit();
u.x = x ;
u.y = y ++;
this.units.push(u);
if(i == (count - 1 )){
u.color = $s.HEAD_COLOR;
}
u.repaint();
}
};
$s.Snake.prototype.crash = function(x,y){ // 传入头部的位置,返回true表示碰撞自身
for(var i = this.units.length - 2 ; i >= 0 ; i --){ // 不包括头自身
var u = this.units[i];
if(u.x == x && u.y == y){
return true;
}
}
return false;
};
$s.Snake.prototype.go = function(){
// 判断前方是否有食物
// 是否撞墙
var _x = 0 , _y = 0;
var head = this.units[this.units.length - 1];
_x = head.x;
_y = head.y;
var dir = $s.SnakeContext.dir;
if(this.crash(_x,_y)){ // 判断是否碰撞到自身
$s.SnakeContext.stop();
$s.SnakeContext.ondead(); // 触发dead事件
return;
}
if(dir == $s.Dir.LEFT){
_x --;
}else if(dir == $s.Dir.RIGHT){
_x ++;
}else if(dir == $s.Dir.UP){
_y --;
}else if(dir == $s.Dir.DOWN){
_y ++;
}
// 实现左右连接,上下连接
if(_x >= $s.PANEL_WIDTH){
_x = 0;
}
if(_x < 0){
_x = $s.PANEL_WIDTH - 1;
}
if(_y >= $s.PANEL_HEIGHT){
_y = 0;
}
if(_y < 0){
_y = $s.PANEL_HEIGHT - 1;
}
var h = new $s.Unit(); // 新头
if($s.SnakeContext.hasFood(_x,_y)){ // 下一步碰到食物
this.eat(_x,_y);
head = this.units[this.units.length - 1]; // 因为eat方法可以改变头部,所以重新获取
_x = head.x;
_y = head.y;
if(dir == $s.Dir.LEFT){
_x --;
}else if(dir == $s.Dir.RIGHT){
_x ++;
}else if(dir == $s.Dir.UP){
_y --;
}else if(dir == $s.Dir.DOWN){
_y ++;
}
head.color = $s.HEAD_COLOR;
head.repaint();
var oldHead = this.units[this.units.length - 2];
oldHead.color = $s.BODY_COLOR;
oldHead.repaint();
return;
}
var tail = this.units.shift();
$s.NodePool.releaseNode(tail);
h.x = _x;
h.y = _y;
this.units.push(h);
for(var i = this.units.length - 1; i >= 0; i --){
var u = this.units[i];
if(i == (this.units.length - 1)){ // 头
u.color = $s.HEAD_COLOR;
}else{
u.color = $s.BODY_COLOR;
}
u.repaint();
}
};
$s.Snake.prototype.eat = function(x,y){
var food = $s.SnakeContext.food;
if(food != null){
food.alive = false;
this.units.push(food.unit);
$s.SnakeContext.oneat();
}else{
alert("error:no food on (" + x + "," + y + ")");
}
}
/*
* 随机数产生器,提供简便的方法
*/
$s.Random = {
randomNumber : function(lower,upper){ // 返回区间[lower,upper]的整数
var choices = upper - lower + 1;
return Math.floor(Math.random() * choices + lower); // value = Math.floor(Math.random() * 可能值的个数+ 第一个可能的值)
},
randomLocation : function(maxX,maxY){
var x = $s.Random.randomNumber(0,maxX);
var y = $s.Random.randomNumber(0,maxY);
return {x:x,y:y};
}
};
$s.Food = function(x,y){ // 代表食物,由一个Unit表示
this.unit = new $s.Unit();
this.unit.x = x;
this.unit.y = y;
var color = $s.COLORS[$s.Random.randomNumber(0,$s.COLORS.length - 1)];
this.unit.color = color;
this.alive = true;
this.unit.repaint();
};
$s.Food.prototype.locateOn = function(x,y){
return this.unit.x == x && this.unit.y == y;
};
/*
* HTML结点池,主要目的是提高效率
* 因为snake的移动是通过删除尾部结点并向头部添加结点实现的,
* 在这个过程中会有大量的结点创建操作,为了操作效率,所以对结点进行池化管理.
* 尾部的结点不删除,而是隐藏,需要结点时可以重用之
*/
$s.NodePool = {
nodes : []
};
$s.NodePool._findHideNode = function(){ // 查找隐藏的div结点
for(var i = 0 ; i < this.nodes.length ; i ++){
var n = this.nodes[i];
if(n.style.display == "none"){
return n;
}
}
return null;
};
$s.NodePool.createNode = function(){
var pooledNode = this._findHideNode();
if(pooledNode != null){
return pooledNode;
}else{
var newNode = document.createElement("div");
this.nodes.push(newNode);
return newNode;
}
};
$s.NodePool.releaseNode = function(node){
if(node != undefined && node != null){
if(node instanceof $s.Unit){
var view = node.view;
if(view != null){
var div = view.node;
div.style.display = "none";
}
}
}
}
$s.UnitView = function(){ // Unit的视图
this.unit = null;
this.node = null;
};
$s.UnitView.prototype.repaint = function(){
if(this.node == null){ // 初始化
var tag = $s.NodePool.createNode();
tag.style.width = $s.UNIT_WIDTH + "px";
tag.style.height = $s.UNIT_HEIGHT + "px";
tag.style.borderStyle = "dotted";
tag.style.borderWidth = "1px";
tag.style.borderColor = "white";
tag.style.margintLeft = "1px";
tag.style.marginRight = "1px";
tag.style.marginTop = "1px";
tag.style.marginBottom = "1px";
tag.style.backgroundColor = this.unit.color; // 颜色由模型Unit指定
tag.style.position = "absolute"; //容器的position指定为relative,孩子的position指定为absolute时,表示孩子相对容器绝对定位.
tag.style.display = "block"; // 重要,因为从NodePool取现的结点是隐藏状态的
var x = this.unit.x * $s.UNIT_WIDTH;
var y = this.unit.y * $s.UNIT_HEIGHT;
tag.style.top = y + "px";
tag.style.left = x + "px";
this.node = tag;
$s.SnakeContext.panelView.append(this);
}else{
var tag = this.node;
var x = this.unit.x * $s.UNIT_WIDTH;
var y = this.unit.y * $s.UNIT_HEIGHT;
tag.style.top = y + "px";
tag.style.left = x + "px";
tag.style.backgroundColor = this.unit.color;
}
};
$s.PanelView = function(){ // 整个游戏区域,包括按钮区
var panel = document.createElement("div");
panel.style.width = ($s.PANEL_WIDTH * $s.UNIT_WIDTH ) + "px";
panel.style.height = ($s.PANEL_HEIGHT * $s.UNIT_HEIGHT ) + "px";
panel.style.borderStyle = "dotted";
panel.style.borderColor = "red";
panel.style.borderWidth = "1px";
panel.style.marginLeft = "auto";
panel.style.marginRight = "auto";
panel.style.marginTop = "50px";
panel.style.position = "relative"; // 容器的position指定为relative,孩子的position指定为absolute时,表示孩子相对容器绝对定位.
panel.style.marginBottom = "auto";
this.node = panel;
document.body.appendChild(panel);
var len = document.createElement("div");
len.style.marginLeft = "auto";
len.style.marginRight = "auto";
len.style.marginBottom = "20px";
len.style.color = "gray";
len.style.fontSize = "12px";
len.innerHTML = "长度:";
document.body.appendChild(len);
$s.SnakeContext._len = len;
var startBn = document.createElement("button");
startBn.innerHTML = "开始";
startBn.style.marginLeft = "10px";
startBn.onclick = function(){
$s.SnakeContext.run();
};
$s.SnakeContext._startBn = startBn;
document.body.appendChild(startBn);
var pauseBn = document.createElement("button");
pauseBn.innerHTML = "暂停";
pauseBn.style.marginLeft = "10px";
pauseBn.onclick = function(){
$s.SnakeContext.pause();
};
$s.SnakeContext._pauseBn = pauseBn;
document.body.appendChild(pauseBn);
/*
var stopBn = document.createElement("button");
stopBn.innerHTML = "停止";
stopBn.style.marginLeft = "10px";
stopBn.onclick = function(){
$s.SnakeContext.stop();
};
$s.SnakeContext._stopBn = stopBn;
document.body.appendChild(stopBn);
*/
var restartBn = document.createElement("button");
restartBn.innerHTML = "重新开始";
restartBn.style.marginLeft = "10px";
restartBn.onclick = function(){
window.location.href = window.location.href;
};
$s.SnakeContext._restartBn = restartBn;
document.body.appendChild(restartBn);
var line = document.createElement("div");
line.style.height = "10px";
document.body.appendChild(line);
var span = document.createElement("span");
span.style.color = "gray";
span.style.fontSize = "12px";
span.innerHTML = "调试";
document.body.appendChild(span);
var debug = document.createElement("input");
debug.type = "checkbox";
debug.checked = $s.DEBUG;
debug.onchange = function(){
$s.SnakeContext.setDebug(debug.checked);
};
document.body.appendChild(debug);
};
$s.PanelView.prototype.append = function(unitView){
try{
this.node.appendChild(unitView.node);
}catch(e){
alert(e);
}
};
/*
* 全局环境类,代表应用
* 约定以_开头的成员为私有成员
* 启动程序的方法:
* window.onload = function(){
* $s.SnakeContext.init();
* }
*/
$s.SnakeContext = {
dir : $s.Dir.NONE,
state : $s.State.STOP,
goTimer : null,
run : function(){
if(this.state != $s.State.RUNNGIN){
this.state = $s.State.RUNNGIN;
this.goTimer = window.setInterval(function(){
$s.SnakeContext.updateFood();
$s.SnakeContext.snake.go();
},$s.STEP);
}
},
stop : function(){
this._setState($s.State.STOP);
},
pause : function(){
this._setState($s.State.PAUSE);
},
_setState : function(s){
if(this.state != s && this.goTimer != null){
window.clearInterval(this.goTimer);
this.goTimer = null;
this.state = s;
}
},
getFood : function(x,y){
for(var f in this.foods){
if(f.x == x && f.y == y){
return f;
}
}
return null;
},
init : function(){
this.panelView = new $s.PanelView();
this.snake = new $s.Snake();
this.dir = $s.Dir.DOWN;
this.snake.init($s.Dir.UP,3);
this._len.innerHTML = "长度:" + 3;
document.body.onkeyup = function(e){
var code = null;
if(window.event){ // fuck的IE
code = window.event.keyCode;
}else{
code = e.keyCode;
}
var str = "";
var oldDir = $s.SnakeContext.dir;
switch(code){
case 37: // left
if($s.SnakeContext.dir != $s.Dir.RIGHT){
$s.SnakeContext.dir = $s.Dir.LEFT;
}
str = "left";
break;
case 38 : // up
if($s.SnakeContext.dir != $s.Dir.DOWN){
$s.SnakeContext.dir = $s.Dir.UP;
}
str = "up";
break;
case 39: // right
if($s.SnakeContext.dir != $s.Dir.LEFT){
$s.SnakeContext.dir = $s.Dir.RIGHT;
}
str = "right";
break;
case 40: // down
if($s.SnakeContext.dir != $s.Dir.UP){
$s.SnakeContext.dir = $s.Dir.DOWN;
}
str = "down";
break;
}
if($s.SnakeContext.dir != oldDir){
if($s.DEBUG){
var v = $s($s.KEY_UP_DIR_ID);
if(v){
v.innerHTML = "方向键:" + str;
}
}
if($s.SnakeContext.goTimer != null){
window.clearInterval($s.SnakeContext.goTimer);
$s.SnakeContext.goTimer = null;
}
$s.SnakeContext.snake.go();
$s.SnakeContext.goTimer = window.setInterval(function(){
$s.SnakeContext.updateFood();
$s.SnakeContext.snake.go();
},$s.STEP);
}
};
var loc = $s.Random.randomLocation($s.PANEL_WIDTH - 1, $s.PANEL_HEIGHT - 1);
this.food = new $s.Food(loc.x,loc.y);
},
snake : null,
foods : [],
panelView : null,
food : null,
updateFood : function(){
if(this.food.alive){ // 当前Food还存活
return;
}
var loc = null;
do{
// 随机产生一个点,直到不Snake重叠
loc = $s.Random.randomLocation($s.PANEL_WIDTH - 1,$s.PANEL_HEIGHT - 1);
}while(this.overlap(loc));
this.food = new $s.Food(loc.x,loc.y);
},
overlap : function(loc){ // 检查是否与Snake重叠,当重叠时返回true
var x = loc.x;
var y = loc.y;
for(var i = 0 ; i < this.snake.units.length ; i ++ ){
var u = this.snake.units[i];
if(u.x == x && u.y == y){
return true;
}
}
return false;
},
hasFood : function(x,y){
if($s.DEBUG){
var xt = $s($s.HEAD_LOCATION_ID);
if(xt){
xt.innerHTML = "头部位置:(" + x + "," + y + ")";
}
}
return this.food.locateOn(x,y);
},
setDebug : function(enable){
if(enable != $s.DEBUG){
$s.DEBUG = enable;
if($s.DEBUG){ // 显示
var i = $s($s.KEY_UP_DIR_ID);
$s.SnakeContext._show(i);
i = $s($s.HEAD_LOCATION_ID);
$s.SnakeContext._show(i);
}else{ // 隐藏
var i = $s($s.KEY_UP_DIR_ID);
$s.SnakeContext._hide(i);
i = $s($s.HEAD_LOCATION_ID);
$s.SnakeContext._hide(i);
}
}
},
_show : function(tag){
if(tag){
tag.style.display = "block";
}
},
_hide : function(tag){
if(tag){
tag.style.display = "none";
}
},
ondead : function(){ // Snake死亡时回调
if(this._startBn){
this._startBn.disabled = true;
}
if(this._pauseBn){
this._pauseBn.disabled = true;
}
if(this._stopBn){
this._stopBn.disabled = true;
}
alert("挂了");
},
oneat : function(){ // Snake长度增加时回调
this._len.innerHTML = "长度:" + this.snake.units.length;
},
_startBn : null,
_pauseBn : null,
_stopBn : null,
_restartBn : null,
_len : null
};
})();

Outils d'IA chauds

Undresser.AI Undress
Application basée sur l'IA pour créer des photos de nu réalistes

AI Clothes Remover
Outil d'IA en ligne pour supprimer les vêtements des photos.

Undress AI Tool
Images de déshabillage gratuites

Clothoff.io
Dissolvant de vêtements AI

AI Hentai Generator
Générez AI Hentai gratuitement.

Article chaud

Outils chauds

Bloc-notes++7.3.1
Éditeur de code facile à utiliser et gratuit

SublimeText3 version chinoise
Version chinoise, très simple à utiliser

Envoyer Studio 13.0.1
Puissant environnement de développement intégré PHP

Dreamweaver CS6
Outils de développement Web visuel

SublimeText3 version Mac
Logiciel d'édition de code au niveau de Dieu (SublimeText3)

Comment utiliser WebSocket et JavaScript pour mettre en œuvre un système de reconnaissance vocale en ligne Introduction : Avec le développement continu de la technologie, la technologie de reconnaissance vocale est devenue une partie importante du domaine de l'intelligence artificielle. Le système de reconnaissance vocale en ligne basé sur WebSocket et JavaScript présente les caractéristiques d'une faible latence, d'un temps réel et d'une multiplateforme, et est devenu une solution largement utilisée. Cet article explique comment utiliser WebSocket et JavaScript pour implémenter un système de reconnaissance vocale en ligne.

WebSocket et JavaScript : technologies clés pour réaliser des systèmes de surveillance en temps réel Introduction : Avec le développement rapide de la technologie Internet, les systèmes de surveillance en temps réel ont été largement utilisés dans divers domaines. L'une des technologies clés pour réaliser une surveillance en temps réel est la combinaison de WebSocket et de JavaScript. Cet article présentera l'application de WebSocket et JavaScript dans les systèmes de surveillance en temps réel, donnera des exemples de code et expliquera leurs principes de mise en œuvre en détail. 1. Technologie WebSocket

Comment utiliser WebSocket et JavaScript pour mettre en œuvre un système de réservation en ligne. À l'ère numérique d'aujourd'hui, de plus en plus d'entreprises et de services doivent fournir des fonctions de réservation en ligne. Il est crucial de mettre en place un système de réservation en ligne efficace et en temps réel. Cet article explique comment utiliser WebSocket et JavaScript pour implémenter un système de réservation en ligne et fournit des exemples de code spécifiques. 1. Qu'est-ce que WebSocket ? WebSocket est une méthode full-duplex sur une seule connexion TCP.

Introduction à l'utilisation de JavaScript et de WebSocket pour mettre en œuvre un système de commande en ligne en temps réel : avec la popularité d'Internet et les progrès de la technologie, de plus en plus de restaurants ont commencé à proposer des services de commande en ligne. Afin de mettre en œuvre un système de commande en ligne en temps réel, nous pouvons utiliser les technologies JavaScript et WebSocket. WebSocket est un protocole de communication full-duplex basé sur le protocole TCP, qui peut réaliser une communication bidirectionnelle en temps réel entre le client et le serveur. Dans le système de commande en ligne en temps réel, lorsque l'utilisateur sélectionne des plats et passe une commande

JavaScript et WebSocket : Construire un système efficace de prévisions météorologiques en temps réel Introduction : Aujourd'hui, la précision des prévisions météorologiques revêt une grande importance pour la vie quotidienne et la prise de décision. À mesure que la technologie évolue, nous pouvons fournir des prévisions météorologiques plus précises et plus fiables en obtenant des données météorologiques en temps réel. Dans cet article, nous apprendrons comment utiliser la technologie JavaScript et WebSocket pour créer un système efficace de prévisions météorologiques en temps réel. Cet article démontrera le processus de mise en œuvre à travers des exemples de code spécifiques. Nous

Tutoriel JavaScript : Comment obtenir le code d'état HTTP, des exemples de code spécifiques sont requis Préface : Dans le développement Web, l'interaction des données avec le serveur est souvent impliquée. Lors de la communication avec le serveur, nous devons souvent obtenir le code d'état HTTP renvoyé pour déterminer si l'opération a réussi et effectuer le traitement correspondant en fonction de différents codes d'état. Cet article vous apprendra comment utiliser JavaScript pour obtenir des codes d'état HTTP et fournira quelques exemples de codes pratiques. Utilisation de XMLHttpRequest

Utilisation : En JavaScript, la méthode insertBefore() est utilisée pour insérer un nouveau nœud dans l'arborescence DOM. Cette méthode nécessite deux paramètres : le nouveau nœud à insérer et le nœud de référence (c'est-à-dire le nœud où le nouveau nœud sera inséré).

Introduction à la méthode d'obtention du code d'état HTTP en JavaScript : Dans le développement front-end, nous devons souvent gérer l'interaction avec l'interface back-end, et le code d'état HTTP en est une partie très importante. Comprendre et obtenir les codes d'état HTTP nous aide à mieux gérer les données renvoyées par l'interface. Cet article explique comment utiliser JavaScript pour obtenir des codes d'état HTTP et fournit des exemples de code spécifiques. 1. Qu'est-ce que le code d'état HTTP ? Le code d'état HTTP signifie que lorsque le navigateur lance une requête au serveur, le service
