用html+css+js中国象棋小游戏开发项目
最近刚学习完JS的相关课程,跟着老师做了两个小游戏项目,就已经抑制不住内心的小激动,想着要迫不及待的着手准备做一个网页小游戏——中国象棋
由于从小就比较喜欢中国象棋,对它的游戏规则还是比较了解的,但是,目前来说对于怎么用JS来实现,还是没有太多的思路:
由于这是小白第一个自己做的小项目,因此,从今天开始,我将每天记录自己的开发日志:
第一天 2016年9月23日:
首先,在网上找了棋盘,和棋子的一些图片素材,接着用html+css+js实现游戏的初始化界面,先附一张初始化界面的总的效果图:
目前的初始化界面只是实现了棋子的总体布局,和单击每个棋子后会出现红色边框的简单效果,我将重点对这两个方面进行介绍。
第一个方面:棋子的总体布局
1、棋盘我采用的是一个大的div,通过给div设置背景图片来实现,由于刚学的JS所以棋子部分的页面结构,是通过JS动态添加的,因此我的HTML文档相当简单;
2、棋子的布局,通过效果图可以看出,我将棋子活动的范围划分成9*10的格子单元,每个单元都有行标R(范围从0~8)和列标C(范围从0`9),具体划分图如下:
3、给棋盘和后续要添加的棋子设置css样式,样式代码如下:
#chess_box{
width:360px;
height:400px;
padding:20px;
margin:0 auto;
position:relative;
background:url("../images/chess.jpg") no-repeat;
cursor:pointer;
}
#chess_box>div{
width:33px;
height:33px;
position:absolute;
background:url("../images/chess.png") no-repeat;
border:1px solid #D3BF9A;
border-radius:50%;
}
#chess_box>div:hover{box-shadow:1px 2px 5px rgba(116,95,59,0.7);}
#chess_border{
width:36px;
height:36px;
background:url("../images/chess_boder.png") no-repeat;
position:absolute;
}
这样一来,棋盘的样式效果和棋子的样式效果就有了,接下来就用JS初始化界面,给页面添加棋子元素,并且给每个棋子绑定单击事件;整个JS采用的是面向对象的方法来做的:
第二方面:用JS实现页面初始化
1、创建棋子对象j集合,为每个棋子对象设置坐标属性
2、遍历棋子对象集合中的每个棋子对象,根据其坐标在页面相应位置创建div元素,并设置它的lef属性为下标乘以每个单元格宽度加上边距
3、遍历过程中为每个div元素绑定单击事件
JS实现的代码如下
var game={
RN:10,//行数
CN:9,//列数
CSIZE:40,//每个单元格大小
OFFSET:20,//单元格区域距离最外层边界的距离,
URL:"images/chess.png",//保存所有棋子的图片地址
chessPieces:{//保存所有棋子的初始位置坐标
redC1:{r:0,c:0,x:-144,y:0},
redC2:{r:0,c:8,x:-144,y:0},
redM1:{r:0,c:1,x:-108,y:0},
redM2:{r:0,c:7,x:-108,y:0},
redX1:{r:0,c:2,x:-72,y:0},
redX2:{r:0,c:6,x:-72,y:0},
redS1:{r:0,c:3,x:-36,y:0},
redS2:{r:0,c:5,x:-36,y:0},
redB2:{r:0,c:4,x:0,y:0},
redP1:{r:2,c:1,x:-180,y:0},
redP2:{r:2,c:7,x:-180,y:0},
redZ1:{r:3,c:0,x:-216,y:0},
redZ2:{r:3,c:2,x:-216,y:0},
redZ3:{r:3,c:4,x:-216,y:0},
redZ4:{r:3,c:6,x:-216,y:0},
redZ5:{r:3,c:8,x:-216,y:0},
blueC1:{r:9,c:0,x:-144,y:-36},
blueC2:{r:9,c:8,x:-144,y:-36},
blueM1:{r:9,c:1,x:-108,y:-36},
blueM2:{r:9,c:7,x:-108,y:-36},
blueX1:{r:9,c:2,x:-72,y:-36},
blueX2:{r:9,c:6,x:-72,y:-36},
blueS1:{r:9,c:3,x:-36,y:-36},
blueS2:{r:9,c:5,x:-36,y:-36},
blueB2:{r:9,c:4,x:0,y:-36},
blueP1:{r:7,c:1,x:-180,y:-36},
blueP2:{r:7,c:7,x:-180,y:-36},
blueZ1:{r:6,c:0,x:-216,y:-36},
blueZ2:{r:6,c:2,x:-216,y:-36},
blueZ3:{r:6,c:4,x:-216,y:-36},
blueZ4:{r:6,c:6,x:-216,y:-36},
blueZ5:{r:6,c:8,x:-216,y:-36},
},
start:function(){
//初始化棋盘
//创建文档片段
var frag=document.createDocumentFragment();
var chess=document.getElementById("chess_box");
//遍历chessPieces中的每一个对象,获取当前对象的r,c,x,y属性分别保存在变量r,c,x,y中
for(var key in this.chessPieces){
var r=this.chessPieces[key].r;
var c=this.chessPieces[key].c;
var x=this.chessPieces[key].x;
var y=this.chessPieces[key].y;
//生成div元素,设置其背景url为URL,设置其left属性为r*CSIZE+OFFSET;设置其top属性为c*CSIZE+OFFSET
var div=document.createElement("div");
div.style.background="url('"+this.URL+"') no-repeat "+x+"px "+y+"px";
div.style.left=c*this.CSIZE+this.OFFSET+"px";
div.style.top=r*this.CSIZE+this.OFFSET+"px";
div.className=key;
frag.appendChild(div);
div.οnclick=function(){
if(document.querySelector("span")){
chess.removeChild(document.querySelector("span"));
}
var span=document.createElement("span");
span.style.left=this.style.left;
span.style.top=this.style.top;
span.id="chess_border";
chess.appendChild(span);
}
}
chess.appendChild(frag);
},
};
game.start();
项目第二天:2016年9月24日
今天主要实现了给棋子绑定了移动的方法,并设置了其移动的规则,首先还是先附上今天完成的效果图
为实现以上效果,关键有两个算法逻辑,一个是利用单击事件触发棋子的移动方法;另一个便是棋子移动的规则:
1、绑定单击事件,这里的单击事件我在昨天的基础上进行了优化,不是给每个棋子绑定单击事件,而是给棋子的父级元素(也就是整个棋盘div)绑定事件监听,利用事件冒泡来触发每个棋子的单击事件;主要用到的知识点为:
element.addEventListener("click",function(e){具体移动棋子的语句块});
2、事件触发时,棋子移动的方法逻辑如下:
1、鼠标点击在棋子上面的时候,首先判断是否有其他的棋子被激活
//如果有其他棋子被激活(页面中存在span元素)再执行以下程序
//如果已激活的元素与目标元素不是同一颜色棋子
//找到已经激活的元素,保存其下标位置
//保存目标元素的下标位置
//将已激活的元素的下标和目标元素的下标作为判断棋子是否移动方法的参数传入
//如果棋子能够移动到目标位置
//移除目标位置的元素,调用更新棋子位置的方法将当前位置的元素替换(吃子的逻辑)
//如果不能,则将span的位置修改到目标位置
//如果已激活的元素与目标元素是同一颜色棋子
//将span的位置修改到目标位置
//如果没有其棋子被激活,执行更新棋子位置的方法
//将span的位置修改到目标位置
2、鼠标点击在空白处的时候;
//获取鼠标的位置,将鼠标的位置转换为棋盘中对应单元格的下标位置
//如果存在已激活的棋子
//获得已激活的棋子的下标位置
//调用判断棋子能否移动的方法
如果能够移动
调用更新棋子位置的方法将当前位置的元素替换(移动的逻辑)
3、判断棋子能否移动的方法的逻辑,就不在赘述,详见以下代码中的canMove()方法
var game={
RN:10,//行数
CN:9,//列数
CSIZE:40,//每个单元格大小
OFFSET:20,//单元格区域距离最外层边界的距离,
URL:"images/chess.png",//保存所有棋子的图片地址
data:null,//实时保存棋盘中棋子的对象
chessPieces:[//保存所有棋子的初始位置坐标
{name:'redC1',r:0,c:0,x:-144,y:0},
{name:'redC2',r:0,c:8,x:-144,y:0},
{name:'redM1',r:0,c:1,x:-108,y:0},
{name:'redM2',r:0,c:7,x:-108,y:0},
{name:'redX1',r:0,c:2,x:-72,y:0},
{name:'redX2',r:0,c:6,x:-72,y:0},
{name:'redS1',r:0,c:3,x:-36,y:0},
{name:'redS2',r:0,c:5,x:-36,y:0},
{name:'redB0',r:0,c:4,x:0,y:0},
{name:'redP1',r:2,c:1,x:-180,y:0},
{name:'redP2',r:2,c:7,x:-180,y:0},
{name:'redZ1',r:3,c:0,x:-216,y:0},
{name:'redZ2',r:3,c:2,x:-216,y:0},
{name:'redZ3',r:3,c:4,x:-216,y:0},
{name:'redZ4',r:3,c:6,x:-216,y:0},
{name:'redZ5',r:3,c:8,x:-216,y:0},
{name:'blueC1',r:9,c:0,x:-144,y:-36},
{name:'blueC2',r:9,c:8,x:-144,y:-36},
{name:'blueM1',r:9,c:1,x:-108,y:-36},
{name:'blueM2',r:9,c:7,x:-108,y:-36},
{name:'blueX1',r:9,c:2,x:-72,y:-36},
{name:'blueX2',r:9,c:6,x:-72,y:-36},
{name:'blueS1',r:9,c:3,x:-36,y:-36},
{name:'blueS2',r:9,c:5,x:-36,y:-36},
{name:'blueB0',r:9,c:4,x:0,y:-36},
{name:'blueP1',r:7,c:1,x:-180,y:-36},
{name:'blueP2',r:7,c:7,x:-180,y:-36},
{name:'blueZ1',r:6,c:0,x:-216,y:-36},
{name:'blueZ2',r:6,c:2,x:-216,y:-36},
{name:'blueZ3',r:6,c:4,x:-216,y:-36},
{name:'blueZ4',r:6,c:6,x:-216,y:-36},
{name:'blueZ5',r:6,c:8,x:-216,y:-36},
],
start:function(){
//初始化棋盘
//创建文档片段
var frag=document.createDocumentFragment();
var chess=document.getElementById("chess_box");
//给date添加RN个空元素
this.data=new Array(this.RN);
//给date每行添加CN个空元素;
for(var r=0;r=0&&r<=9&&c>=0&&c<=8){
//蓝卒的规则
if(div.className.indexOf("blueZ")!=-1){
return (divr>=5&&divr-r==1&&divc==c)||(divr<5&&((divr-r==1&&divc==c)||(divr==r&&Math.abs(divc-c)==1)))
}
//红兵的规则
if(div.className.indexOf("redZ")!=-1){
return (divr<5&&r-divr==1&&divc==c)||(divr>=5&&((r-divr==1&&divc==c)||(divr==r&&Math.abs(divc-c)==1)))
}
//炮和车的规则
if(div.className.indexOf("P")!=-1||div.className.indexOf("C")!=-1){
var data=this.data[r][c];
if(divc==c){
divr>r&&(r=[divr,divr=r][0]);
for(var i=0;divr<=r;this.data[divr++][c]&&i++);
}else if(divr==r){
divc>c&&(c=[divc,divc=c][0]);
for(var i=0;divc<=c;this.data[r][divc++]&&i++);
}
if(div.className.indexOf("P")!=-1){
return (!data&&i==1)||(data&&i==3);
}else{
return (!data&&i==1)||(data&&i==2);
}
}
//马的规则
if(div.className.indexOf("M")!=-1){
if(
(Math.abs(divr-r)==1&&Math.abs(divc-c)==2&&(!this.data[divr][(divc+c)/2]))
||(Math.abs(divr-r)==2&&Math.abs(divc-c)==1&&(!this.data[(divr+r)/2][divc]))
){return true;}
}
//相的规则
if(div.className.indexOf("X")!=-1){
var bool=(Math.abs(divr-r)==2&&Math.abs(divc-c)==2&&(!this.data[(divr+r)/2][(divc+c)/2]));
if(div.className.indexOf("redX")!=-1){
return r>=0&&r<5&&bool;
}else{
return r>=5&&r<=9&&bool;
}
}
//士的规则
if(div.className.indexOf("S")!=-1&&c>2&&c<6){
var bool=(Math.abs(divr-r)==1&&Math.abs(divc-c)==1);
if(div.className.indexOf("redS")!=-1){
return r>=0&&r<3&&bool;
}else{
return r>=7&&r<=9&&bool;
}
}
//将的规则
if(div.className.indexOf("B")!=-1&&c>2&&c<6){
var bool=((Math.abs(divr-r)==1&&divc==c)||(Math.abs(divc-c)==1&&divr==r));
if(div.className.indexOf("redB")!=-1){
return r>=0&&r<3&&bool;
}else{
return r>=7&&r<=9&&bool;
}
}
}
},
//更新棋子位置的
paintChees:function(div,r,c){
div.style.left=c*this.CSIZE+this.OFFSET+"px";
div.style.top=r*this.CSIZE+this.OFFSET+"px";
div.className=div.className.slice(0,-2)+r+c;
return div;
},
//更新边框位置
paintBorder:function(span,div){
span.style.left=div.style.left;
span.style.top=div.style.top;
span.className="index-"+div.className.slice(-2);
}
};
game.start();