• 使用canvas制作五子棋游戏


    要制作JS五子棋的话我们可以一开始来理清一下思路,这样对我们后来的编程是有好处的

    1、棋盘使用canvas制作。canvas用来做这种不用太过复杂的图形的时候是很有用处的,下图是我制作的一个五子棋棋盘

    2、确定你是想做PC端的还是做移动端的

    3、点击的棋子是放在棋盘上,还是另建一个canvas画布

    对于这个问题,我选择了新建一个canvas画布,如果不新建的话,如果想有撤回按钮(我这边没放上去),不太好操作,因为使用API删除会不干净,所以我使用的是存下棋子数组,每一次下棋子,存入数组后,清空棋盘,重新绘制,那每次还要重新绘制一个棋盘,所以我把棋盘作为了一个固定的canvas。还有一点,棋盘的绘制,如果棋盘和canvas画布一样大小的话,下在最旁边的棋子是会有一部分看不见的,所以要么这个canvas大一部分,要么另起一个只放置棋子的画布。

    HTML:

     1 <!DOCTYPE html>
     2 <html lang="en">
     3 
     4 <head>
     5     <meta charset="UTF-8">
     6     <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />
     7     <meta name="viewport" content="target-densitydpi=320,width=640,user-scalable=no">
     8     <link rel="stylesheet" href="./CSS/reset.css">
     9     <link rel="stylesheet" href="./CSS/index.css">
    10     <script type="text/javascript">
    11         window.onresize = function () {
    12             resetPage();
    13         }
    14         function resetPage() {
    15             var deviceWidth = document.documentElement.clientWidth || document.body.clientWidth;
    16             var deviceHeight = document.documentElement.clientHeight || document.body.clientHeight;
    17             var scale = deviceWidth / 480;
    18             deviceHeight = deviceHeight / scale;
    19             document.body.style.zoom = scale;
    20             document.body.style.height = deviceHeight + 'px';
    21         }
    22 
    23     </script>
    24     <title>Document</title>
    25 </head>
    26 
    27 <body>
    28     <canvas id="canvas_0" width="560px" height="560px"></canvas>
    29     <canvas id="canvas_1" width="600px" height="600px"></canvas>
    30     <div id="shadowBox"></div>
    31     <div id="back1">后退</div>
    32     <div id="back2">后退</div>
    33     <script type="text/javascript" src="./JS/index.js"></script>
    34 </body>
    35 
    36 </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: 5;
    }
    
    #canvas_1 {
        position: fixed;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        z-index: 10;
    }
    #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

    LineChart.prototype.DrawCircle是我用来画五子棋的四个小黑点的,原理和画棋子差不多。
    JS => 棋子部分
    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进行访问,谢谢大家

  • 相关阅读:
    LINQ to DataSet
    LINQ to SQL
    $.ajax()方法解析
    【转】数据库获得当前时间getdate()
    几种单例模式解析
    WebView上实现Java与JavaScript交互
    Dapper(.NET下的ORM框架)的基本使用
    IPtables中SNAT和MASQUERADE的区别
    我的桌面版fedora10安装
    我的fedora10的virtual box网络设置
  • 原文地址:https://www.cnblogs.com/JobsOfferings/p/JonsOfferings_five.html
Copyright © 2020-2023  润新知