小游戏之莫交叉

       做这个小游戏的灵感来源于...那时候在西永上班的几个同事,一个设计健哥,一个后台wei哥,另一个产品wei哥,再加上我这个前端,组成的一个开发小组,有一段时间我想自己写一个网络上比较火的贪吃蛇游戏耍,我就把想法和健哥讨论了下,结果不得了,估计大家工作都做得快(PS人事的艳姐看到这里莫敲我们脑壳,公司的事情点都没耽误:)),就七嘴八舌的一起讨论,就说要不然我们几个一起来开发一款益智游戏,当然我的贪吃蛇提议就肯定没被采纳,因为都感觉简单了,虽然我考虑的贪吃蛇并不是大众所认为的那样,最终应该是健哥提议的莫交叉这个游戏,我们找了几个网上的范本玩了玩,感觉还可以,就打算从这个游戏入手,想打下我们几个人的小游戏江山,我只能说终究我们还是太年轻了,虽然我最终把游戏原型做出来了,健哥把设计也画出来了,但是还是因为要做工作,慢慢的就不了了之,N年后的今天,我突然想到我最近看得最强大脑这个节目,上面就有很多这种类似的“小游戏”,早就手痒想盗版做几个了,不知怎么就联想到了当初做的那个莫交叉游戏,在N个云盘、服务器上翻箱倒柜,终于还找到了当初做的源码,自己也再拾掇拾掇,打算还是写个博客,纪念一下N年前那个夏天的idea。

       游戏的玩法,找了下百度,书名叫做“不交叉”,具体的解释链接是https://baike.baidu.com/item/%E4%B8%8D%E4%BA%A4%E5%8F%89/1581989?fr=aladdin,简单的说就是理清N个点之间的连线,除了点的交叉,没有线的交叉,随着初始点越来越多,线就越来越难理清楚,玩到后面还是有点烧脑,下面我们先来看下一个简单的起始面:

现在我们初始是5个点,拖动每个点的时候,每个点相关联的线都会跟着移动,绿色的线是不交叉的,红色的线是因为有交叉,我们需要把所有的线都变成绿色就成功了,下面有个简单的动画演示效果

可能大家看了这个动画,就感觉这个游戏很简单,是的,确实很简单,我曾经也这样天真的认为,那是因为我当时还没想清楚如果去开发这个游戏,其实最开始我觉得这个游戏可能有点难,我首先需要考虑3个问题:1、每个点都需要去连接线段,但是如何去连,可以看出有的点之间是没有连线的;2、点的移动是实时的,那么线的移动也是实时的,如何去考虑性能问题;3、根据第2点的问题,如何去判定游戏最终是成功的。当时我想如果把这3个问题解决了,应该就能考虑出代码思路了,然而还是too young了,我突然想到,点是随机生成,我怎么样才能知道哪些点之间要连线呢?突然就卡在这里了,不过考虑考虑,就在想,出这个游戏应该是要有解的吧(PS在写到这里的时候,突然冒出了一个想法,如果可以选择无解,那游戏的难度又会上一个台阶),那我为什么不先把正确的结果连接出来,然后再去随机移动初始点,再让用户去移动解答,这样就直接把思路理清楚了,至于第2点性能问题,貌似只能用Canvas了,所以就选择了createjs框架。

    其实整体的游戏核心代码很简洁,总共才300多行,而且都是纯前端代码,那时候为了快速出效果,后面也没维护,所以就没怎么写注释,将就着看。

 首先我们先定义画布(html):

1 <div class="box">
2     <canvas id="gameView" width="600" height="600" style="background-color:rgba(0,0,0,0.6)"></canvas>    
3 </div>

 初始化系列参数(js):

 1 var stage = new createjs.Stage("gameView");
 2 createjs.Touch.enable(stage,true,false);
 3 var containerLine = new createjs.Container;
 4 var containerOther = new createjs.Container;
 5 stage.addChild(containerLine,containerOther);
 6 var arr = [];
 7 var lineArr = [];
 8 var indexArr = [];
 9 var jiaocha = false;
10 var rightArr = [];
11 var level = 5;

 重置方法:

 1 var restart = function(){
 2     containerLine.removeAllChildren()
 3     containerOther.removeAllChildren()
 4     arr = [];
 5     lineArr = [];
 6     indexArr = [];
 7     jiaocha = false;
 8     rightArr = [];
 9     start();
10 }

 初始化界面上的点

 1             var start = function(){
 2                 for (var i =0;i<level;i++) {
 3                     var circle = new createjs.Shape();
 4                     circle.graphics.beginFill("green").drawCircle(0, 0, 10);
 5                     let w = Math.random()*bgW;
 6                     let h = Math.random()*bgH;
 7                     let x = w;
 8                     let y = h;
 9                     circle.x = x;
10                     circle.y = y;
11                     circle.index = i;
12                     circle.addEvent(i);
13                     containerOther.addChild(circle);
14                     arr.push(circle)
15                     
16                     var point = {};
17                     point.x = x;point.y = y;point.index = i;
18                     rightArr.push(point);
19                     
20                     var text = new createjs.Text(i, "20px Arial", "#ff7700");
21                     text.x = x;text.y = y;
22                     text.textBaseline = "alphabetic";
23                      containerOther.addChild(text)
24                     stage.update();
25                 }
26                 drawLine1();
27                 for(var j=0;j<level;j++){
28                     randomLine();
29                 }
30                 drawLine2();
31             }
32             start();

  初始化方法里面的两个随机连线方法randomLine,drawLine1,drawLine2

 1             var randomLine = function(){
 2                 containerLine.removeAllChildren();
 3                 var rand = Math.floor(Math.random()*arr.length);
 4                 var x = Math.random()*bgW;
 5                 var y = Math.random()*bgH;
 6                 for(var i = 0;i<lineArr.length;i++){
 7                     var p3 = lineArr[i].p3;
 8                     var p4 = lineArr[i].p4;
 9                     if(rand == p3.index){
10                         lineArr[i].p3.x = x;
11                         lineArr[i].p3.y = y;
12                     }
13                     if(rand == p4.index){
14                         lineArr[i].p4.x = x;
15                         lineArr[i].p4.y = y;
16                     }
17                     
18                 }
19                 arr[rand].x = x;
20                 arr[rand].y = y;
21                 stage.update();
22             }
 1             var drawLine1 = function(){
 2                 //在每个点的时候都要去找一下另外的匹配不交叉点
 3                 for(var i=0;i<arr.length;i++){
 4                     var p1 = {},p2 = {};
 5                     p1.x = arr[i].x,p1.y = arr[i].y,p1.index = arr[i].index;
 6                     
 7                     var count = Math.floor(Math.random()*level)+3;
 8                     for(let j=0;j<arr.length;j++){
 9                         if(i!=j){
10                             p2.x = arr[j].x,p2.y = arr[j].y,p2.index = arr[j].index;
11                             var result = checkLine(p1,p2,lineArr);
12                             if(result == false){
13                                 //先检查点有多少个了,多了就不画了
14                                 let countP = checkPointCount(p1,p2);
15                                 if(countP<=count)
16                                 {
17                                     var line = new createjs.Shape();
18                                       line.graphics.setStrokeStyle(2).beginStroke("green");
19                                       line.graphics.moveTo(p1.x, p1.y);
20                                       line.graphics.lineTo(p2.x, p2.y);
21                                       line.graphics.endStroke();
22                                       containerLine.addChild(line);
23                                       stage.update();
24                                       var point = [];
25                                       let p3={},p4={};
26                                       p3.x = p1.x,p3.y = p1.y,p3.index = p1.index;
27                                       p4.x = p2.x,p4.y = p2.y,p4.index = p2.index;
28                                       point.p3 = p3;point.p4 = p4;
29                                       lineArr.push(point)
30                                       
31                                       if(indexArr.indexOf(p1.index)==-1){
32                                           indexArr.push(p1.index)
33                                       }
34                                       if(indexArr.indexOf(p2.index)==-1){
35                                           indexArr.push(p2.index)
36                                       }
37                                 }
38                             }
39                         }
40                     }
41                     
42                     var c = checkPointCount(p1,p2);
43                     console.log(i,c)
44                 }
45             }
 1             var drawLine2 = function(){
 2                 jiaocha = false;
 3                 for(var i = 0;i<lineArr.length;i++){
 4                     var p1 = lineArr[i].p3;var p2 = lineArr[i].p4;
 5                     var result = false;
 6                     for(var j=0;j<lineArr.length;j++){
 7                         if(i!=j){
 8                             var p3 = lineArr[j].p3;var p4 = lineArr[j].p4;
 9                             if(checkCross(p1,p2,p3,p4)==true){
10                                 if((p1.x == p3.x && p1.y == p3.y) || (p1.x == p4.x && p1.y == p4.y) || (p2.x == p3.x && p2.y == p3.y) || (p2.x == p4.x && p2.y == p4.y))
11                                 {
12                                     result = false;
13                                 }
14                                 else
15                                 {
16                                     result = true;
17                                     jiaocha = true;
18                                     break;
19                                 }
20                             }
21                         }
22                     }
23                     var line = new createjs.Shape();
24                     //要在这里判断每条线的交叉,然后再变颜色和做判断
25                       line.graphics.setStrokeStyle(2).beginStroke(result==true?"red":"green");
26                       line.graphics.moveTo(p1.x, p1.y);
27                       line.graphics.lineTo(p2.x, p2.y);
28                       line.graphics.endStroke();
29                       containerLine.addChild(line);
30                       stage.update();
31                 }
32                 if(jiaocha==false){
33                     document.getElementsByClassName("over")[0].style.display = "block";
34                 }
35             }

在drawLine1的时候还有一个检查点的方法checkPointCount

 1             var checkPointCount = function(p1,p2){
 2                 var count = 0;
 3                 for(var i = 0;i<lineArr.length;i++){
 4                     var p3 = lineArr[i].p3;
 5                     var p4 = lineArr[i].p4;
 6                     if(p1.index == p3.index || p1.index == p4.index || p2.index == p3.index || p2.index == p4.index){
 7                         count++;
 8                     }
 9                 }
10                 return count;
11             }

当上面的完成后,就成了我们初始化的界面,level是初始的点,我也用来当成通关数,接下来就是移动点的操作了,给每一个点增加一个移动事件,并在每一个点移动的时候去调用drawLine2方法重绘每一条关联的线

 1             createjs.Shape.prototype.addEvent = function(count){
 2                 var _this = this;
 3                 this.addEventListener("mouseover",function(){
 4                     alert();
 5                 })
 6                 this.addEventListener("pressmove",function(_this){
 7                     if(jiaocha==false){
 8                         return;
 9                     }
10                     var cir = _this.target;
11                     var nowx = _this.stageX,nowy = _this.stageY;
12                     cir.x = nowx;cir.y = nowy;
13                     stage.update();
14                     containerLine.removeAllChildren();
15                     //这里要改变移动了点的信息
16                     var index = _this.target.index;
17                     for(var i = 0;i<lineArr.length;i++){
18                         if(lineArr[i].p3.index == index){
19                             lineArr[i].p3.x = nowx;lineArr[i].p3.y = nowy;
20                         }
21                         if(lineArr[i].p4.index == index){
22                             lineArr[i].p4.x = nowx;lineArr[i].p4.y = nowy;
23                         }
24                     }
25                     drawLine2()
26                 })
27             }

在drawLine2方法里面有一个jiaocha变量,根据checkCross方法做一个判断所有lineArr数组里面的线是否有交汇,判断交汇方法如下:

 1             var checkCross = function(p1, p2, p3, p4) {
 2                 var v1 = {
 3                         x: p1.x - p3.x,
 4                         y: p1.y - p3.y
 5                     },
 6                     v2 = {
 7                         x: p2.x - p3.x,
 8                         y: p2.y - p3.y
 9                     },
10                     v3 = {
11                         x: p4.x - p3.x,
12                         y: p4.y - p3.y
13                     },
14                     v = crossMul(v1, v3) * crossMul(v2, v3)
15                 v1 = {
16                     x: p3.x - p1.x,
17                     y: p3.y - p1.y
18                 }
19                 v2 = {
20                     x: p4.x - p1.x,
21                     y: p4.y - p1.y
22                 }
23                 v3 = {
24                     x: p2.x - p1.x,
25                     y: p2.y - p1.y
26                 }
27                 var result = (v <= 0 && crossMul(v1, v3) * crossMul(v2, v3) <= 0) ? true : false;
28                 if(result){
29                     if((p1.x == p3.x && p1.y == p3.y) || (p1.x == p4.x && p1.y == p4.y) || (p2.x == p3.x && p2.y == p3.y) || (p2.x == p4.x && p2.y == p4.y))
30                     {
31                         result = false;
32                     }
33                     else
34                     {
35                         result = true;
36                     }
37                 }
38                 return result;
39             }

 1 var crossMul = function(v1, v2) { 2 return v1.x * v2.y - v1.y * v2.x; 3 } 

  到这里,整个游戏的思路就已经结束了,整体来说代码量不多,但是游戏是真的好玩,有兴趣的同学可以玩一下(在线地址:http://hudong.miaos.me/xxoo/ko_all.html),因为要涉及到很多点的操作,所以在PC上面操作体验会更好一些。另外目前的这种方法其实对于性能的问题并没有得到很好的解决,因为目前采用框架对于重绘是非常消耗资源,最后来一个30关的镇楼图吧,太烧脑了,目前我还没去研究最优解题的思路,后面哪天兴趣来了再去考虑。

最后我觉得还是要把健哥的设计贴出来,做个纪念,虽然我们最终没能一起完成这个项目,但是我还是很欣赏你的设计的,不仅限于这个莫交叉:)

原文地址:https://www.cnblogs.com/qinyulin/p/13496855.html