很多人写游戏都是从连连看或者五子棋这类的简单小游戏入手的,最近我也尝试着写了一个连连看,想要再梳理一遍其中的思路。
连连看的规则
连连看要求在一定范围内找到两个特征一样并且能够通过空白的通道在两折(直角)以内相连的东西,连续点击两个东西之后消除。
通常我们会选择用图片来进行匹配,这样更直观有趣。
如何存储连连看的数据
使用二维数组进行存储,每一个数组元素对应一个位置上的图片种类。
例如我们分别用1,2,3,4代表四个不同的图片,用0代表没有图片;那么二维数组{{},{},{},{}}就相应地存储对应位置的数据。如果要对图片进行变更(连连看中的消除),那么只需要改变该位置对应的数组元素的值就行了(在这个例子中,消除就改为0)。
如何实现数组数据的初始化
1、一般来说连连看开始都会随机生成图片,此处的随机生成需要用到Random类里面的nextInt方法,能随机生成给定范围内的随机一个整数。
2、我们生成数组的时候要考虑一个问题:连连看相同的图片个数是偶数,这样才不会到最后形成落单的尴尬局面。
//随机给出数组值,初始化游戏数据
public int[][] DATA(int[][] data){
int[][] d=data;
Random random=new Random();
for(int i=0;i<8;i++){
d[0][i]=0;
d[7][i]=0;
}
for(int i=1;i<7;i++){
d[i][0]=0;
d[i][7]=0;
}
for(int i=0;i<18;i++){
int m=random.nextInt(Const.Const_TYPECOUNNT)+1;
int x1=random.nextInt(Const.COL)+1;
int y1=random.nextInt(Const.COL)+1;
while(d[x1][y1]!=0){
x1=random.nextInt(Const.COL)+1;
y1=random.nextInt(Const.COL)+1;
}
d[x1][y1]=m;
int x2=random.nextInt(Const.COL)+1;
int y2=random.nextInt(Const.COL)+1;
while(d[x2][y2]!=0){
x2=random.nextInt(Const.COL)+1;
y2=random.nextInt(Const.COL)+1;
}
d[x2][y2]=m;
}
return(d);
}
如何绘制图片
连连看的存储都是以int的类型存储的,可我们要呈现出来的效果是一张一张的图片……
先用Image导入所有图片,最好图片的命名能够用数字命名,这样就可以批量导入了。这里就不过多赘述了,看实例代码吧:
public void init(){
imgArr = new Image[Const.Const_TYPECOUNNT];
for(int i=0; i < imgArr.length; i++){
imgArr[i] = new ImageIcon("llkImage/"+i+".jpg").getImage();
}
}
关于图片大小的调整我用的是比较原始的方法:编辑图片本身像素大小;还可以通过drawImage来直接规定大小。
重绘棋子
没有重绘的窗体在我们对其进行拖动或者最小化等改变的时候会变成空白的,重绘就是将窗体的paint方法重写,这样对窗体的任何操作都会将paint方法重新写一遍。也就是在屏幕上重新画一遍。
我们的棋子都存在一个二维数组里面,那么我们就可以将棋子绘制的步骤放在重绘里面来实现,那么棋子就跟窗体一样,可以“一直存在”了,也可以根据数组的改变来改变。
代码示例:
public void paint(Graphics g) {
// 重绘paint方法
super.paint(g);
//
for (int i = 1; i < Const.COL+1; i++) {
for (int j = 1; j < Const.ROW+1; j++) {
if (data[i][j] == 0) {
g.setColor(new Color(69,175,198));
g.fillRect(Const.START_X + i * Const.SIZE, Const.START_Y + j * Const.SIZE, Const.SIZE, Const.SIZE);
}
else {
g.drawImage(imgArr[data[i][j]-1], Const.START_X + i * Const.SIZE, Const.START_Y + j * Const.SIZE, null);
}
}
}
连连看的实现算法
两个图片消除的方法有三种:
1.直线连接
2.一折连接
3.二折连接
其中二折连接可以找一个点既与其中一个图片一折连接又与另一个图片直线连接;一折连接又可以找一个点与其中一个图片直线连接又与另一个图片直线连接。
所以综上所述,只需要写出一个直线连接的判断方法,我们所有的规则方法就都可以实现了。
数组的元素=0时代表这里的图片为空,有路可走。
首先,两个数组元素直线相连的前提是这两个元素的横坐标或者纵坐标相等且路径上的数组元素全部为零。
代码示例:
//0折点的方法
public boolean zero(int[][] data,int X1, int Y1,int X2,int Y2){
int max;
int min;
//x值相等
if(X1==X2){
max=Y1 > Y2 ? Y1:Y2;
min=Y1 < Y2 ? Y1:Y2;
for(int i=min+1;i<max;i++){
if(data[X1][i]!=0){
return false;
}
}
return true;
}
//y值相等
else if(Y1==Y2){
max=X1 > X2 ? X1:X2;
min=X1 < X2 ? X1:X2;
for(int i=min+1;i<max;i++){
if(data[i][Y1]!=0){
return false;
}
}
return true;
}
else return false;
}
//1折点的方法
public boolean one(int[][] data,int X1, int Y1,int X2,int Y2){
if((zero(data,X1,Y1,X1,Y2)&&zero(data,X1,Y2,X2,Y2)&&data[X1][Y2]==0)||(data[X2][Y1]==0&&zero(data,X1,Y1,X2,Y1)&&zero(data,X2,Y1,X2,Y2))){
return true;
}
return false;
}
//2折点的方法
public boolean two(int[][] data,int X1, int Y1,int X2,int Y2){
//向下寻找一个第一折点
for(int i=X1+1;i<=Const.COL+1;i++){
if(zero(data,X1,Y1,i,Y1)&&one(data,i,Y1,X2,Y2)){
return true;
}
}
//向上寻找一个第一折点
for(int i=X1-1;i>=0;i--){
if(zero(data,X1,Y1,i,Y1)&&one(data,i,Y1,X2,Y2)){
return true;
}
}
//向左
for(int i=Y1-1;i>=0;i--){
if(zero(data,X1,Y1,X1,i)&&one(data,X1,i,X2,Y2)){
return true;
}
}
//向右
for(int i=Y1+1;i<=Const.ROW+1;i++){
if(zero(data,X1,Y1,X1,i)&&one(data,X1,i,X2,Y2)){
return true;
}
}
return false;
}
如何获取两次鼠标点击的位置
废话不多说,直接上代码:
//鼠标点击松开事件
public void mouseReleased(MouseEvent e) {
int x,y;
Rule rule = new Rule();
x = e.getX();
y = e.getY();
if(count%2==0){
X1 = (x - Const.START_X) / Const.SIZE;
Y1 = (y - Const.START_Y) / Const.SIZE;
count++;
}
else {
X2 = (x - Const.START_X) / Const.SIZE;
Y2 = (y - Const.START_Y) / Const.SIZE;
count++;
if (rule.judge(X1, Y1, X2, Y2, data)&&
(rule.zero(data,X1,Y1,X2,Y2)||rule.one(data, X1, Y1, X2, Y2)||rule.two(data, X1, Y1, X2, Y2))){
data[X1][Y1]=0;data[X2][Y2]=0;
}
}
ui.repaint();
}
成果展示
以上,我的游戏还是个很简单的模板,仍待完善,可以把界面做得更精美,还可以增加游戏难度的选择以及时间和步数的规定,最后还可以加上游戏的输赢判断,希望能够帮助到有需要的人。