彩球连珠——游戏《五子连珠碰碰球》编程指南
编程爱好者
这里使用的开发工具是Borland C++Builder6.0,相关程序及源代码的下载地址:http://www.cpcw.com/06/game.rar
一、动手前的准备——了解游戏
游戏玩法
本游戏结合了《五子棋》和《连连看》的特点。在游戏中我们可以点选一个彩球,然后点击它能直接移动到的地方(哪些才算能直接移动到的地方呢?能通过没有彩球的空白路径到达的就算。),当有相同颜色的彩球在一条直线上达到五个的时候就可以消除了。
但需要注意的是,移动彩球找寻可到达的路径也必须遵循特定的规则,即移动的彩球只能走横竖的行列,而空的斜对角线是不可以直接走的。而每当彩球能成功移动到新位置后,又会在空位置上随即出现新的三个小球。当有相同颜色的彩球按照游戏规则消除后,玩家就能拿到相应的得分了。
程序主体
这款游戏的制作重点全都集中在了彩球的移动和消除上了,所以我们就要重点讲解这部分内容。在这之前,我们先来看看程序主体的大致结构:
void __fastcall TForm1::Image1MouseD
own(TObject *Sender,
TMouseButton Button,TShiftState Shift,int X,int Y)
{
……
m=JudgeMove(x,y); //用来判断彩球是否能按规则移动至新位置
if(m==1) //m为1表示判断路径通,进行移点,消点判断 及产生新彩球
{
MoveBall(x,y); //移动球
MousePosX=0; //完成移动球,记录上次选球情况为空
MousePosY=0;
ClearBall(x,y); //消除同色彩球
RandomBall();//产生随即彩球
}
……
}
在了解了程序的大致结构以后,我们只需要针对各个定义函数,完善它们的相应功能,就可以完成游戏的制作了。而游戏制作的重点主要就围绕生、消、移三大部分展开,下面我们就来依次看看。
二、生——产生彩球
思路讲解
首先我们需要考虑的是如何随机产生彩球。游戏中的平面由8×8的小格组成,我们可以采用数组Col[10][10]。Col[i][j]中,i、j为0或9表示边界点,其余的8×8个点对应游戏中的彩球。另用Fre[64]记录下最初的所有8×8的空点坐标。这样当需要产生随机彩球的时候,通过随机函数在Fre的队列中取出一个空点,并将空点后的点前移,填补原空点在队列中的位置,同时将记录空点数量的FreeNum自减一就可以了。

如果把空点队列看成是用细绳连接并垂直悬挂的一串小球,当随机取走其中一个球后,它上面的球会自动落下,填补取走小球后留下的空位置。不难理解,这样做的目的是为了能完全掌控空点队列的具体情况。
而当有点消除时,情况则相反,需要在队列末尾加上新空点的坐标,并且执行FreeNum++,这就如同在悬挂的一串小球上又添加新的小球。
通过上述的处理方法,我们就能很容易地随时获取剩余空点的情况了。
示例代码
具体函数RandomBall()代码请参考http://www.cpcw.com/06/game.rar,这里给出随机产生一个新彩球的示例代码供读者参考:
if(FreeNum<1)
{
ShowMessage("GAME OVER");//表示记录队列中已无空点,游戏结束
break;
}
tem=random(FreeNum);
y=Fre[tem]/8+1; //这里xy用来记录随机产生的彩球位置坐标
x=Fre[tem]%8+1;
FreeNum--; //产生新彩球后,记录空点的值减1
for(int j=tem;j Fre[j]=Fre[j+1]; //取出空点坐标值后,Fre[]队列后面的位置数补上空出位置 Col[y][x]=NextCol[i]; Image1->Canvas->Pen->Color = BackCol; Image1->Canvas->Brush->Color = BallCol[Col[y][x]] ; Image1->Canvas->Ellipse((x-1)*40+5, (y-1)*40+5, (x-1)*40+35, (y-1)*40+35); //随机在空位置产生彩球, ClearBall(x,y); //用来检验随机产生的彩球是否自动发生同色彩珠消除事件, //并更新下一组彩球(左侧显示的NEXT中的彩球)。 NextCol[i]= random(6)+1; Image2->Canvas->Pen->Color = BackCol; Image2->Canvas->Brush->Color = BallCol[NextCol[i]] ; Image2->Canvas->Ellipse((i)*30+5, 5, i*30+25, 25); 当有五个相同颜色的彩球在一条直线上的时候,就要将它们消除了。而消除的重点则在于随时搜集彩球的位置信息。比如要检查水平方向是否有连续5个以上彩球时,我们可以以彩球移动后的点为中心点,按一定的顺序进行查找,具体的方式大家可以参考下面的示例代码: xa=1; ya=0; //xa、ya表示向右时坐标增值 ty=ty+ya; //tx,ty初值是中心点坐标,加上坐标增量后表示右侧的第一个点。 tx=tx+xa; //当判断出右侧点和中心点颜色相同时,记录相同颜色彩球自变量s加1。 if(Col[y][x]==Col[ty][tx]) s++; //通过循环找下一个点,而如果发现颜色不同时,则由中心原点出发向反方向找点 xa=0-xa; //左边增量取负值,起到反方向取点作用 ya=0-ya; ttx=tx; //ttx,tty用于记录连续点一侧坐标,方便后面消点时计算出所有可消点的坐标。 tty=ty; tx=x; //将tx、ty值重新赋值为中心点坐标值 ty=y; t++; //记录取点反向次数,大于1时则表示已经取完中心点两侧的连续点 if(t>1) break; 而当出现连续相同点满足五个以上的情况时,开始消除对应点并给予相应分数,具体代码参见函数源代码ClearBall(x,y),下载地址:http://www.cpcw.com/06/game.rar。 “移”分为判断路径是否连通和移动两部分,分别对应函数JudgeMove(x,y)和MoveBall(x,y)。至于其中判断小球是否可移动至新位置的思路,则可用解决走迷宫的算法。 如图,若想将左侧标记的黑球移至“×”处,可以通过逐步移动判断:首先令黑球为1步,判断其四周位置,若无彩球,可移动。图中画2的位置为第2步,然后记录下这些第2步的位置,通过循环,逐个判断每个第2步位置的四周点的情况,再走出第3步。 当走至某位置,该处恰好为×点坐标时,说明彩球可移动至此位置。则JudgeMove(x,y);完成,并返回值1,表示可以移动至新位置。 而当走至第N步,判断所有第N步位置的四周都不可行走时(不存在空点),无法走第N+1步,则说明彩球无法按规则移动至新位置,返回值0表示不可达,然后再回溯寻找新的出路。 玩过此游戏的读者一定发现游戏中有时会出现走的路径不是最短路径的情况,那么我们在编程的时候如何来克服这样的情况呢? 首先通过上面的方法,将可走每一步的步数保存入对应的位置,当判断某步四周点时,若发现其周围某位置曾经走过,则比较这个位置留下的“脚印”的步数,若比当前要走的步数小,说明是走回头路,而比当前步数大,则说明原“脚印”的步数不是最短路径,这就需要改变位置并记录下当前需走的步数“脚印”。 当最终能走至新位置时,则可由新位置的步数倒推,判断新位置四周的步数是否比此位置少1。找到满足条件的点,记录下它的坐标,并通过循环,即能找出现在走的最短路径上的所有坐标位置,这样就能描绘出所走的最短路径了。 到此,编程的重点就讲解完了。回过头来再看,游戏本身结合了《五子棋》和《连连看》的特点,玩起来十分有趣,但同时也就为编程带来了不小的困难。里面的核心问题就是我们在前面重点讲解的“生”、“消”、“移”三个部分,而其中则以如何判断移动路径最为困难。从这里也可以看到,一个好的路径搜索方案对程序的性能影响巨大,希望读者在自己的实践中多多尝试不同的路径寻找方法,来亲身体会一下其中的不同。三、消——清除连通的彩球
思路讲解
示例代码
四、移——判断连通及移动路径
思路讲解

走最短路径
五、小结