使用canvas制作五子棋游戏
要制作JS五子棋的话我们可以一开始来理清一下思路,这样对我们后来的编程是有好处的
1、棋盘使用canvas制作。canvas用来做这种不用太过复杂的图形的时候是很有用处的,下图是我制作的一个五子棋棋盘

2、确定你是想做PC端的还是做移动端的
3、点击的棋子是放在棋盘上,还是另建一个canvas画布
对于这个问题,我选择了新建一个canvas画布,如果不新建的话,如果想有撤回按钮(我这边没放上去),不太好操作,因为使用API删除会不干净,所以我使用的是存下棋子数组,每一次下棋子,存入数组后,清空棋盘,重新绘制,那每次还要重新绘制一个棋盘,所以我把棋盘作为了一个固定的canvas。还有一点,棋盘的绘制,如果棋盘和canvas画布一样大小的话,下在最旁边的棋子是会有一部分看不见的,所以要么这个canvas大一部分,要么另起一个只放置棋子的画布。
HTML:
<!DOCTYPE html>
<html lang="en"> <head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />
<meta name="viewport" content="target-densitydpi=320,width=640,user-scalable=no">
<link rel="stylesheet" href="./CSS/reset.css">
<link rel="stylesheet" href="./CSS/index.css">
<script type="text/javascript">
window.onresize = function () {
resetPage();
}
function resetPage() {
var deviceWidth = document.documentElement.clientWidth || document.body.clientWidth;
var deviceHeight = document.documentElement.clientHeight || document.body.clientHeight;
var scale = deviceWidth / 480;
deviceHeight = deviceHeight / scale;
document.body.style.zoom = scale;
document.body.style.height = deviceHeight + 'px';
} </script>
<title>Document</title>
</head> <body>
<canvas id="canvas_0" width="560px" height="560px"></canvas>
<canvas id="canvas_1" width="600px" height="600px"></canvas>
<div id="shadowBox"></div>
<div id="back1">后退</div>
<div id="back2">后退</div>
<script type="text/javascript" src="./JS/index.js"></script>
</body> </html>
canvas_0是棋盘canvas
canvas_1是棋子canvas
shadowBox是外面的边框(可以优化到canvas_0里面去)
后退按钮做了,不过没有放上去,在JS中功能有做出来。
CSS
#canvas_0 {
box-sizing: border-box;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border: 1px solid black;
background-color: #FFD75B;
z-index:;
}
#canvas_1 {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index:;
}
#shadowBox{
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border: 20px solid #FFD75B;
}
#back1{
position: absolute;
top: 10%;
left: 50%;
transform: translateX(-50%);
width: 50px;
height: 50px;
line-height: 50px;
border-radius: 50%;
font-size: 20px;
background-color: red;
color: white;
}
#back2{
position: absolute;
bottom: 10%;
left: 50%;
transform: translateX(-50%);
width: 50px;
height: 50px;
line-height: 50px;
border-radius: 50%;
font-size: 20px;
background-color: red;
color: white;
}
这里面没有什么需要注意的,因为本身canvas里面的东西和CSS就没多大关系
JavaScript代码有很多,我分开讲
JS => 起始部分
var Board = document.getElementById('canvas_0');
var BoardToGraw = document.getElementById('canvas_1');
var shadowBox = document.getElementById('shadowBox');
var back = document.getElementById('back1');
var back = document.getElementById('back2');
var windowWidth = document.documentElement.clientWidth || document.body.clientWidth;//拿到当前浏览器可视宽度
var windowHeight = document.documentElement.clientHeight || document.body.clientHeight;//拿到当前浏览器可视高度
Board.style.width = windowWidth * 0.9 + "px";//棋盘宽度为当前浏览器可视宽度的90%
Board.style.height = windowWidth * 0.9 + "px";//棋盘高度为当前浏览器可视宽度的90%
BoardToGraw.style.width = windowWidth * 0.9 + 40 + "px";//棋子canvas宽度为当前浏览器可视宽度的90% + 左右各20px
BoardToGraw.style.height = windowWidth * 0.9 + 40 + "px";//棋子canvas高度为当前浏览器可视宽度的90% + 上下各20px
shadowBox.style.width = windowWidth * 0.9 + "px";//边框宽度为当前浏览器可视宽度的90%
shadowBox.style.height = windowWidth * 0.9 + "px";//边框高度为当前浏览器可视宽度的90%
JS => 棋盘部分
var piecesList = [];//棋子数组
// 15×15
if (Board.getContext) {
var ctx0 = Board.getContext('2d'); /*获取上下文,或者说绘制工具箱ctx*/
/*1、构造函数*/
var LineChart = function (ctx0) {
/*获取绘图工具*/
this.ctx0 = ctx0 || document.getElementById('canvas_0').getContext('2d');
/*画布的大小*/
this.canvasWidth = 560;//这里是算过了,560/14 = 20 , 每个格子20
this.canvasHeight = 560;
}
/*2、行为方法 */
LineChart.prototype.init = function () {
this.drawGrid();
// 画四个点
let LineTotal = 13;
let EmptyWidth = this.canvasWidth / (LineTotal + 1);
this.DrawCircle(EmptyWidth * 3, EmptyWidth * 3, 5, "#000");
this.DrawCircle(this.canvasWidth - EmptyWidth * 3, EmptyWidth * 3, 5, "#000");
this.DrawCircle(EmptyWidth * 3, this.canvasHeight - EmptyWidth * 3, 5, "#000");
this.DrawCircle(this.canvasWidth - EmptyWidth * 3, this.canvasHeight - EmptyWidth * 3, 5, "#000");
}
/*绘制网格*/
LineChart.prototype.drawGrid = function () {
/*x方向的线*/
let LineTotal = 13;
let EmptyWidth = this.canvasWidth / (LineTotal + 1);
//画X轴方向
for (let i = 0; i < LineTotal; i++) {
this.ctx0.beginPath();
this.ctx0.moveTo(0, EmptyWidth * (i + 1));
this.ctx0.lineTo(this.canvasWidth, EmptyWidth * (i + 1));
this.ctx0.strokeStyle = '#000';
this.ctx0.closePath();
this.ctx0.stroke();
}
/*y方向的线*/
for (let i = 0; i < LineTotal; i++) {
this.ctx0.beginPath();
this.ctx0.moveTo(EmptyWidth * (i + 1), 0);
this.ctx0.lineTo(EmptyWidth * (i + 1), this.canvasHeight);
this.ctx0.strokeStyle = '#000';
this.ctx0.closePath();
this.ctx0.stroke();
}
}
LineChart.prototype.DrawCircle = function (start, end, r, color) {
this.ctx0.beginPath();
this.ctx0.moveTo(start - r, end);
this.ctx0.arc(start, end, r, 2 * Math.PI, 0, true);
this.ctx0.strokeStyle = color;
this.ctx0.closePath();
this.ctx0.fillStyle = color;
this.ctx0.fill();
}
var lineChart = new LineChart();
lineChart.init();
} else {
console.log("error!!!");
}
由于棋盘是14 × 14的,所以一共要画13条宽,13条高,其中棋盘宽高的1/14就是一个格子的宽度,我这边设置成了20
if (BoardToGraw.getContext) {
var ctx0 = BoardToGraw.getContext('2d'); /*获取上下文,或者说绘制工具箱ctx*/
/*1、构造函数*/
var LineChart2 = function (ctx0) {
/*获取绘图工具*/
this.ctx0 = ctx0 || document.getElementById('canvas_1').getContext('2d');
/*画布的大小*/
this.canvasWidth = 600;
this.canvasHeight = 600;
}
//绘制整个棋子数组的函数
LineChart2.prototype.DrawCircleList = function (circleList) {
let r = 18;
for(circle of circleList){
this.ctx0.beginPath();
this.ctx0.moveTo(circle.X * 40 + 20 - r, circle.Y);
this.ctx0.arc(circle.X * 40 + 20, circle.Y * 40 + 20, r, 2 * Math.PI, 0, true);
this.ctx0.strokeStyle = circle.color;
this.ctx0.closePath();
this.ctx0.fillStyle = circle.color;
this.ctx0.fill();
}
}
var LineChart2 = new LineChart2();
} else {
console.log("error!!!");
}
棋子部分的canvas代码就很简单了,因为只需要获取棋子数组,然后画出来就OK,所以只有一个绘制函数
JS => 触摸事件部分,以左上角小黑点为例,写了很详细的注释
BoardToGraw.addEventListener('click', (event) => {
//每click一次,清除一次canvas,数组中添加当前棋子,重绘
let x = event.offsetX;
let y = event.offsetY;
//棋子排位位置
let rankingX, rankingY;
//最终画出的位置
let endX, endY;
var data = {};//用于储存单个棋子信息
var color = (piecesList.length % 2) == 1 ? "white" : "black";//从黑开始交替下子
rankingX = Math.round((x - 20) / 40);//比如如果是左上角的小黑点位置的棋子,rankingX = 3
rankingY = Math.round((y - 20) / 40);//比如如果是左上角的小黑点位置的棋子,rankingY = 3
//rankingX,rankingY可能为-0
rankingX == -0 ? rankingX = 0 : rankingX = rankingX;//如果是-0,转化为0
rankingY == -0 ? rankingY = 0 : rankingY = rankingY;//如果是-0,转化为0
endX = rankingX * 40 + 20;//真正应该画在的地方,endX = 140
endY = rankingY * 40 + 20;//真正应该画在的地方,endY = 140
//封装进piecesList的棋子排位对象
data.X = rankingX;
data.Y = rankingY;
data.color = color;
//data = {X:3,Y:3,color:"black"}
for (pieces of piecesList) {//如果此次下的棋子位置与之前的棋子数组中的发生重复,那么退出这个函数
if (pieces.X == data.X && pieces.Y == data.Y) {
return;
}
}
clearCanvas();//清除整个棋子canvas
piecesList.push(data);//下棋成功,push进棋子数组
LineChart2.DrawCircleList(piecesList);//将push后的棋子数组传入绘制函数
checkFiveLink(piecesList);//重绘后,判断是否有胜利者。
})

JS => 判断是否有胜利者
这里每次下棋只会判断当前棋子color的所有者会不会胜利,因为只有最后一个棋子,才有可能胜利,之前的棋子都是已经判断过的。
由于水平,垂直,倾斜的都可能成功连成五子,所以其实一共有8个方向 => 左、右、上、下、左下、左上、右下、右上。
而有可能我会下在两个棋子的中间,所以每次应该同时判断两个方向 => 左右、上下、左上及右下、左下及右上。
//用于判断是否有五子相连的函数
var checkFiveLink = function (piecesList) {
let nowPrices = piecesList[piecesList.length - 1];
let bolleanLr = lrCheck(piecesList, nowPrices);
let bolleanTb = tbCheck(piecesList, nowPrices);
let bolleanRtLb = rtLbCheck(piecesList, nowPrices);
let bolleanLtRb = ltRbCheck(piecesList, nowPrices);
if (bolleanLr || bolleanTb || bolleanRtLb || bolleanLtRb) {
alert(`${nowPrices.color} : success !!!`);
clearCanvas();
}
}
//水平
var lrCheck = function (piecesList, nowPrices) {
var count = 1;
var count2 = 1;
//右
for (let i = 0; i < 4; i++) {
let obj = {};
obj.X = nowPrices.X + i + 1;
obj.Y = nowPrices.Y;
obj.color = nowPrices.color;
let staticNum = count;
for (piece of piecesList) {
if (piece.X == obj.X && piece.Y == obj.Y && piece.color == obj.color) {
count++;
}
}
if (staticNum == count) {
break;
}
}
for (let i = 0; i < 4; i++) {
let obj = {};
obj.X = nowPrices.X - i - 1;
obj.Y = nowPrices.Y;
obj.color = nowPrices.color;
let staticNum = count2;
for (piece of piecesList) {
if (piece.X == obj.X && piece.Y == obj.Y && piece.color == obj.color) {
count2++;
}
}
if (staticNum == count2) {
break;
}
}
let Link = count + count2 - 1;
if (Link >= 5) {
return true;
}
else {
return false;
}
}
//竖直
var tbCheck = function (piecesList, nowPrices) {
var count = 1;
var count2 = 1;
//上
for (let i = 0; i < 4; i++) {
let obj = {};
obj.X = nowPrices.X;
obj.Y = nowPrices.Y + i + 1;
obj.color = nowPrices.color;
let staticNum = count;
for (piece of piecesList) {
if (piece.X == obj.X && piece.Y == obj.Y && piece.color == obj.color) {
count++;
}
}
if (staticNum == count) {
break;
}
}
for (let i = 0; i < 4; i++) {
let obj = {};
obj.X = nowPrices.X;
obj.Y = nowPrices.Y - i - 1;
obj.color = nowPrices.color;
let staticNum = count2;
for (piece of piecesList) {
if (piece.X == obj.X && piece.Y == obj.Y && piece.color == obj.color) {
count2++;
}
}
if (staticNum == count2) {
break;
}
}
let Link = count + count2 - 1;
if (Link >= 5) {
return true;
}
else {
return false;
}
}
//右上至左下
var rtLbCheck = function (piecesList, nowPrices) {
var count = 1;
var count2 = 1;
//右上
for (let i = 0; i < 4; i++) {
let obj = {};
obj.X = nowPrices.X + i + 1;
obj.Y = nowPrices.Y + i + 1;
obj.color = nowPrices.color;
let staticNum = count;
for (piece of piecesList) {
if (piece.X == obj.X && piece.Y == obj.Y && piece.color == obj.color) {
count++;
}
}
if (staticNum == count) {
break;
}
}
for (let i = 0; i < 4; i++) {
let obj = {};
obj.X = nowPrices.X - i - 1;
obj.Y = nowPrices.Y - i - 1;
obj.color = nowPrices.color;
let staticNum = count2;
for (piece of piecesList) {
if (piece.X == obj.X && piece.Y == obj.Y && piece.color == obj.color) {
count2++;
}
}
if (staticNum == count2) {
break;
}
}
let Link = count + count2 - 1;
if (Link >= 5) {
return true;
}
else {
return false;
}
}
//左上至右下
var ltRbCheck = function (piecesList, nowPrices) {
var count = 1;
var count2 = 1;
//左上
for (let i = 0; i < 4; i++) {
let obj = {};
obj.X = nowPrices.X - i - 1;
obj.Y = nowPrices.Y + i + 1;
obj.color = nowPrices.color;
let staticNum = count;
for (piece of piecesList) {
if (piece.X == obj.X && piece.Y == obj.Y && piece.color == obj.color) {
count++;
}
}
if (staticNum == count) {
break;
}
}
for (let i = 0; i < 4; i++) {
let obj = {};
obj.X = nowPrices.X + i + 1;
obj.Y = nowPrices.Y - i - 1;
obj.color = nowPrices.color;
let staticNum = count2;
for (piece of piecesList) {
if (piece.X == obj.X && piece.Y == obj.Y && piece.color == obj.color) {
count2++;
}
}
if (staticNum == count2) {
break;
}
}
let Link = count + count2 - 1;
if (Link >= 5) {
return true;
}
else {
return false;
}
}
JS = > 其他部分
back1.addEventListener('click',(event)=>{
backStep();
})
back2.addEventListener('click',(event)=>{
backStep();
})
//清除canvas画布
function clearCanvas() {
var c = document.getElementById("canvas_1");
c.height = c.height;
}
//后退一步
function backStep() {
var c = document.getElementById("canvas_1");
c.height = c.height;
piecesList.pop();
LineChart2.DrawCircleList(piecesList);
}
这个小网页我放在了我的服务器上,大家可以通过链接http://www.jobsofferings.cn/五子棋/index.html进行访问,谢谢大家
使用canvas制作五子棋游戏的更多相关文章
- 原生JS+Canvas实现五子棋游戏
一.功能模块 先看下现在做完的效果: 线上体验:https://wj704.github.io/five_game.html 主要功能模块为: 1.人机对战功能 2.悔棋功能 3.撤销悔棋功能 二.代 ...
- 用Canvas制作小游戏——贪吃蛇
今天呢,主要和小伙伴们分享一下一个贪吃蛇游戏从构思到实现的过程~因为我不是很喜欢直接PO代码,所以只copy代码的童鞋们请出门左转不谢. 按理说canvas与其应用是老生常谈了,可我在准备阶段却搜索不 ...
- 怎样用HTML5 Canvas制作一个简单的游戏
原文连接: How To Make A Simple HTML5 Canvas Game 自从我制作了一些HTML5游戏(例如Crypt Run)后,我收到了很多建议,要求我写一篇关于怎样利用HTML ...
- Vue+WebSocket+ES6+Canvas 制作「你画我猜」小游戏
Vue+WebSocket+ES6+Canvas 制作「你画我猜」小游戏 转载 来源:jrainlau 链接:https://segmentfault.com/a/1190000005804860 项 ...
- [译]怎样用HTML5 Canvas制作一个简单的游戏
这是我翻译自LostDecadeGames主页的一篇文章,原文地址:How To Make A Simple HTML5 Canvas Game. 下面是正文: 自从我制作了一些HTML5游戏(例如C ...
- 自定义View实现五子棋游戏
成功的路上一点也不拥挤,因为坚持的人太少了. ---简书上看到的一句话 未来请假三天顺带加上十一回家结婚,不得不说真是太坑了,去年婚假还有10天,今年一下子缩水到了3天,只能赶着十一办事了. 最近还在 ...
- Android实训案例(八)——单机五子棋游戏,自定义棋盘,线条,棋子,游戏逻辑,游戏状态存储,再来一局
Android实训案例(八)--单机五子棋游戏,自定义棋盘,线条,棋子,游戏逻辑,游戏状态存储,再来一局 阿法狗让围棋突然就被热议了,鸿洋大神也顺势出了篇五子棋单机游戏的视频,我看到了就像膜拜膜拜,就 ...
- 使用CocosSharp制作一个游戏 - CocosSharp中文教程
注:本教程翻译自官方<Walkthrough - Building a game with CocosSharp>,官方教程有很多地方说的不够详细,或者代码不全,导致无法继续,本人在看了G ...
- 用Canvas制作简单的画图工具
今天用Canvas制作了一个画图工具,非常简单,功能也不是很多,主要有背景网格,画线,画圆,画矩形和画圆角矩形,也用到了canvas的一些基本知识,在这里一一列举. 1.线段的绘制: 如何绘制真正的1 ...
随机推荐
- Flutter中Expanded组件用法
Flutter中Expanded组件用法 Expanded组件可以使Row.Column.Flex等子组件在其主轴方向上展开并填充可用空间(例如,Row在水平方向,Column在垂直方向).如果多个子 ...
- Redis入门--1.安装Redis
redis是什么? 是完全开源免费的,用c语言编写的,是一个单线程,高性能的(key/value)内存数据库,基于内存运行并支持持久化的nosql数据库 redis能干嘛? 主要是用来做缓存,但不仅仅 ...
- MySQL保存 emoji 表情(微信昵称表情)
问题分析 在微信开发过程中,总是会遇到带有emoji表情昵称的微信用户无法自动登录的问题. 后台代码抛出类似下面的异常信息. java.sql.SQLException: Incorrect stri ...
- Java Calendar类(java.util包)
Date 类最主要的作用就是获得当前时间,同时这个类里面也具有设置时间以及一些其他的功能,但是由于本身设计的问题,这些方法却遭到众多批评,不建议使用,更推荐使用 Calendar 类进行时间和日期的处 ...
- 搭建自己的Online Judge
前言 很多人对于做题有点厌烦,但是,如果让你出题给别人做那么可能会很有意思.可是,出题只能出在一些别人的OJ上,甚至只能在自己的Word文档里出.今天我教大家一个厉害点的,叫做搭建自己的Online ...
- APICloud开发者进阶之路 | UIPickerView 模块示例demo
本文出自APICloud官方论坛 rongCloud2 3.2.8 版本更新后添加了发送小视频接口,发送文件接口. rongCloud2 概述 融云是国内首家专业的即时通讯云服务提供商,专注为互联 ...
- 点分治 (等级排) codeforces 321C
Now Fox Ciel becomes a commander of Tree Land. Tree Land, like its name said, has n cities connected ...
- 有关lightning页面中页面加载时间计数器颜色分别表示的时间范围的问题
之所以要转载这篇文章的原因是,有一次调查lightning中右上角有个页面加载时间控件颜色分别代表多少时间范围的问题,搜索查阅了很多资料和文件,最终在一篇有关Lighthouse前端性能优化测试工具的 ...
- pycharm 连接mysql失败
1.下载与之对应的驱动 2.更改数据库的时区(问题大多数出现在这里) .查看时区 show variables like '%time_zone%'; .设置时区 set global time_zo ...
- es7中数组如何判断元素是否存在
const arr = [1,2,3,4,5,6] console.log(arr.includes(4)) //true