介绍内容
前些时间,阿尔法狗对战柯洁围棋大赛很热门,那只是人工智能中的一个方向,展示了机器能代替人做某些事情。
而围棋是很讲究智力的游戏,所以实现起来也是很难的,Google花了很多钱这在方面。
那段时间我也用JS写了一个小游戏——五子棋,五子棋相对来讲简单很多。我那时候在公众号上展示给大家,好像大家兴趣不大,可能是因为五子棋游戏太过简单,又或者是对我推的东西不感兴趣。那个公众号已经被封杀了,现在我又重新开了一个公众号。下面有二维码,如果有兴趣可以推一下。
现在我要写一下教程,你们可以点击这里体验一番。
这个实例完全是用JS实现的,有UI部分也有AI部分,我们需要有一定的canvas和JS的知识就足够了。
-
棋盘的实现
1.canvas绘画直线。
2.设置画笔的颜色。 -
棋子的实现
1.canvas画圆。
2.填充渐变色。
页面结构
页面上有一个正方形的棋盘,我们用画布canvas
来实现棋盘。
<canvas id="chess" width="450px" height="450px"></canvas>
棋盘有一定的阴影效果,使棋盘更美观些。我们通过定义CSS样式来实现。
canvas {
display: block;
margin: 50px auto;
box-shadow: -2px -2px 2px #EFEFEF, 5px 5px 5px #B9B9B9;
}
这个时候的效果如下,有一定的阴影效果:
阴影效果画棋盘的网格
这里用到JS来控制canvas画棋盘,我们知道五子棋的棋盘是由些纵线和横线组成的,棋盘的样子如下:
网格的实现分别有15条纵线和横线,每个格子为30px
的正方形,棋盘边缘有15px
的补白。
在棋盘中实现画线
在JS中用画笔画一条线:
var chess = document.getElementById("chess") ;//获取canvas
var context = chess.getContext("2d");
context.strokeStyle = "#aaa" ;//画笔的颜色
context.moveTo(15,15);
context.lineTo(435,15);
context.stroke();
效果:
一条横线的画法开始画棋盘的网线
我们回顾了一下画线,那接下来我们用循环方式画15条纵线和15条横线:
var chess = document.getElementById("chess") ;//获取canvas
var context = chess.getContext("2d");
context.strokeStyle = "#aaa" ;//画笔的颜色
for (var i=0; i<15; i++) {//通过循环画网格
context.moveTo(15,15+i*30);
context.lineTo(435,15+i*30);
context.stroke();
context.moveTo(15+i*30,15);
context.lineTo(15+i*30,435);
context.stroke();
}
效果:
完成网格棋盘背景
可能你已经发现了,白色的棋盘背景视觉非常不好,那么接下来我们就来为棋盘添加背景。我们选择一张木色的图片,如果你想为棋盘添加你特有的水印,可以通过制图软件添加。
H5添加图片的方法是通过画图的方式,画上去就会覆盖掉之前画的网格,所以我们通过对画网格的代码进行封装成一个函数,画完背景后再调用画网格的函数来达到不被覆盖的效果。总的代码如下:
var img = new Image();
img.src = "img/2.png" ;
img.onload = function (){
context.drawImage(img,0,0,450,450);
drawLine();
}
function drawLine () {//把画线封装成函数
for (var i=0; i<15; i++) {//通过循环画网格
context.moveTo(15,15+i*30);
context.lineTo(435,15+i*30);
context.stroke();
context.moveTo(15+i*30,15);
context.lineTo(15+i*30,435);
context.stroke();
}
}
最终的效果:
棋盘效果画棋子
我们是在背景上画棋子的,所以画棋子的代码应该放在onload方法里面。
棋子的画法
context.beginPath() ;
context.arc(200,200,100,0,2*Math.PI);
context.closePath() ;
context.fill();
解释一下特别的代码context.arc(200,200,100,0,2*Math.PI);
四个参数分别是圆心横坐标、圆心纵坐标、半径、开始弧度、结束弧度。
context.fill();
给圆填充颜色。
效果:
这个效果还不像棋子,棋子中间要有些发亮才行的,我们给棋子中间加一个亮度的渐变:
我们直接看onload方法里的代码,再解释其中重要的代码:
img.onload = function (){
context.drawImage(img,0,0,450,450);
drawLine();
//画棋子
context.beginPath() ;
context.arc(200,200,100,0,2*Math.PI);
context.closePath() ;
var gradient = context.createRadialGradient(200, 200, 50, 200, 200, 20);
gradient.addColorStop(0, "#0a0a0a");
gradient.addColorStop(1, "#636766");
context.fillStyle = gradient ;
context.fill();
}
这些代码的操作非常简单,首先画一个圆,然后填充渐变色,var gradient = context.createRadialGradient(200, 200, 50, 200, 200, 20);
这是定义一个有渐变的颜色变量,前三个参数是圆心在(200,200)处,半径为50的一个圆,同理,后三个参数是圆心在(200,200)处,半径为20的一个圆。
gradient.addColorStop(0, "#0a0a0a");
gradient.addColorStop(1, "#636766");
context.fillStyle = gradient ;
这三行代码分别设置上面的第一个圆的颜色,第二个圆的颜色,和把渐变色填充给棋子。最终的填充效果是在圆心为(200,200)内径为20,外径为50的一个圆环上产生渐变。
棋子最后的效果:
落子样式
那我们通过上面的学习就会画一个棋子啦,接下来我们改变棋子的半径大小和颜色就能得到我们想要的棋子了。
代码放在onload里面会显得很杂乱,这是我们不想看到的,所以我们必须封装成函数再使用。
封装成以下的函数:
var oneStep = function (i, j, me){//i,j分别是在棋盘中的定位,me代表白棋还是黑棋
context.beginPath() ;
context.arc(15+i*30, 15+j*30, 13, 0, 2*Math.PI);//圆心会变的,半径改为13
context.closePath() ;
var gradient = context.createRadialGradient(15+i*30+2, 15+j*30-2, 15, 15+i*30, 15+j*30, 0);
if(me){
gradient.addColorStop(0, "#0a0a0a");
gradient.addColorStop(1, "#636766");
}else{
gradient.addColorStop(0, "#D1D1D1");
gradient.addColorStop(1, "#F9F9F9");
}
context.fillStyle = gradient ;
context.fill();
}
主要改变了三部分,改变圆心和半径,根据接收到的参数确定圆心,判断是黑子还是白子。
然后通过在onload方法里调用函数来落子:
oneStep(0,0,true) ;
oneStep(1,1,false) ;
效果:
改变棋子大小实现鼠标落子
实现用鼠标点击棋盘就落下一颗棋子,我们用在画布上绑定单击事件来实现,代码如下:
var me = true ;
chess.onclick = function (e){
var x = e.offsetX ;
var y = e.offsetY ;
var i = Math.floor(x/30) ;
var j = Math.floor(y/30) ;
oneStep(i,j,me);
me = !me ;
}
通过e.offsetX
和e.offsetY
两个属性得到坐标,后转化成i和j,再调用oneStep()
方法,定义一个变量me来决定是黑子还是白子,每点击一次就改变一次me的值。
效果:
vzsf
这时候还有一个问题,已经下了黑子的点,重新点击还会被白子覆盖掉。那怎么解决呢?
首先我们定义一个二维数组,存放所有的落子点,如果有落子,就给其记录下来。落子的时候再判断是否已经落子,如果已经落子了就不允许重新落子。思路就是这样。
二维数组代码:
var chessBoard = [] ;
for (var i=0; i<15; i++) {
chessBoard[i] = [] ;
for (var j=0; j<15; j++) {
chessBoard[i][j] = 0;
}
}
二维数组的初始值都是0,然后在单击事件的方法里添加一个判断:
chess.onclick = function (e){
var x = e.offsetX ;
var y = e.offsetY ;
var i = Math.floor(x/30) ;
var j = Math.floor(y/30) ;
if(chessBoard[i][j] == 0){
oneStep(i,j,me);
if(me){
chessBoard[i][j] = 1 ;
}else{
chessBoard[i][j] = 2 ;
}
me = !me ;
}
}
落子位置等于0才可以落子,落完子后给相应的点附非0值,黑子就附1,白子附2。
效果:
总结
这是UI篇,你继续翻翻我的主页,肯定能找到AI篇的。
-
棋盘的实现
通过循环画直线 -
棋子的实现
画出你想要的棋子,渐变填充颜色,封装成一个函数供调用。 -
落子的实现
用数组存放每一个落子点,满足条件就落下对应的子。
UI篇到此就告一段落了,这里用到的知识并不多,相应的方法想了解更多可以到W3上看。AI篇将会在后期推出,到时候就可以实现人机交互了。跟自己打的代码比试五子棋不再只是一种想法,只要你动手,一定能实现。另外,想跟我的代码比试一下可以点击这里。
网友评论