老Java程序员花2天写了个连连看,我竟连不过我女儿,我了个去!

老Java程序员花2天时间做了个连连看

引言:

最近做小游戏有点上瘾,这一不小心翻到了个连连看,二话不说找到素材,就撸了起来。
期间在我加入背景音乐的时候,被女儿听到了,她多次到我身边说:
“爸爸,我很会玩这个游戏,你给我玩,不然就捣乱”。
然后就在我旁边转来转去,动我鼠标摸我键盘,竟然不让我写代码。
讲实话我写代码正投入的时候,如果换做我老婆,我肯定一把推开。
没办法是我女儿,只好继续当好我慈祥老父亲的角色,我跟她说做完给你玩,如果连的过爸爸,就给你玩多几盘,但是现在不能,等我做完我们来比赛,她这才答应不捣乱。
打断我写代码的思路,我要抓狂的,不知各位亲在你全身心投入写代码的时候,女票、媳妇捣乱你会怎么做呢?欢迎就此话题留言,让我好生学习一波你们的精彩操作

老Java程序员花2天写了个连连看,我竟连不过我女儿,我了个去!_第1张图片

代码实现

创建窗口

首先创建一个游戏窗体类GameFrame,继承至JFrame,用来显示在屏幕上(window的对象),每个游戏都有一个窗口,设置好窗口标题、尺寸、布局等就可以。

/*
 * 游戏窗体类
 */
public class GameFrame extends JFrame {
     
	
	public GameFrame() {
     
		setTitle("连连看");//设置标题
		setSize(786, 510);//设定尺寸
		setLayout(new BorderLayout());
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//点击关闭按钮是关闭程序
        setLocationRelativeTo(null);   //设置居中
    	setResizable(false); //不允许修改界面大小
	}
}

创建面板容器GamePanel继承至JPanel

package main;

import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
/*
 * 画布类
 */
public class GamePanel extends JPanel{
     
	GamePanel gamePanel=this;
	private JFrame mainFrame=null;
	//构造里面初始化相关参数
	public GamePanel(JFrame frame){
     
		this.setLayout(null);
		mainFrame = frame;
		
		mainFrame.setVisible(true);
	}
	
	@Override
	public void paint(Graphics g) {
     
		
	}
}

再创建一个Main类,来启动这个窗口,用来启动。

package main;
public class Main {
     
	//主类
	public static void main(String[] args) {
     
		GameFrame frame = new GameFrame();
		GamePanel panel = new GamePanel(frame);
		frame.add(panel);
		frame.setVisible(true);//设定显示
	}
}

右键执行这个Main类,窗口建出来了
老Java程序员花2天写了个连连看,我竟连不过我女儿,我了个去!_第2张图片

创建菜单及菜单选项

创建菜单

private void  initMenu(){
     
	// 创建菜单及菜单选项
	jmb = new JMenuBar();
	JMenu jm1 = new JMenu("游戏");
	jm1.setFont(new Font("微软雅黑", Font.BOLD, 15));// 设置菜单显示的字体
	JMenu jm2 = new JMenu("帮助");
	jm2.setFont(new Font("微软雅黑", Font.BOLD, 15));// 设置菜单显示的字体
	
	JMenuItem jmi1 = new JMenuItem("开始新游戏");
	JMenuItem jmi2 = new JMenuItem("退出");
	jmi1.setFont(new Font("微软雅黑", Font.BOLD, 15));
	jmi2.setFont(new Font("微软雅黑", Font.BOLD, 15));
	
	JMenuItem jmi3 = new JMenuItem("操作说明");
	jmi3.setFont(new Font("微软雅黑", Font.BOLD, 15));
	JMenuItem jmi4 = new JMenuItem("胜利条件");
	jmi4.setFont(new Font("微软雅黑", Font.BOLD, 15));
	
	jm1.add(jmi1);
	jm1.add(jmi2);
	
	jm2.add(jmi3);
	jm2.add(jmi4);
	
	jmb.add(jm1);
	jmb.add(jm2);
	mainFrame.setJMenuBar(jmb);// 菜单Bar放到JFrame上
	jmi1.addActionListener(this);
	jmi1.setActionCommand("Restart");
	jmi2.addActionListener(this);
	jmi2.setActionCommand("Exit");
	
	jmi3.addActionListener(this);
	jmi3.setActionCommand("help");
	jmi4.addActionListener(this);
	jmi4.setActionCommand("win");
}

实现ActionListener并重写方法actionPerformed
老Java程序员花2天写了个连连看,我竟连不过我女儿,我了个去!_第3张图片
actionPerformed方法的实现

//各种事件的触发方法
@Override
public void actionPerformed(ActionEvent e) {
     
	String command = e.getActionCommand();
	UIManager.put("OptionPane.buttonFont", new FontUIResource(new Font("宋体", Font.ITALIC, 18)));
	UIManager.put("OptionPane.messageFont", new FontUIResource(new Font("宋体", Font.ITALIC, 18)));
	if ("Exit".equals(command)) {
     
		Object[] options = {
      "确定", "取消" };
		int response = JOptionPane.showOptionDialog(this, "您确认要退出吗", "",
				JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE, null,
				options, options[0]);
		if (response == 0) {
     
			System.exit(0);
		} 
	}else if("Restart".equals(command)){
     
		if(startFlag){
     
			Object[] options = {
      "确定", "取消" };
			int response = JOptionPane.showOptionDialog(this, "游戏中,您确认要重新开始吗", "",
					JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE, null,
					options, options[0]);
			if (response == 0) {
     
				//需要先结束游戏
				realGameEnd(1);
				restart();
			} 
		}else{
     
			restart();
		}
	}else if("help".equals(command)){
     
		JOptionPane.showMessageDialog(null, "鼠标点击相同的且没有阻挡的消除!",
				"提示!", JOptionPane.INFORMATION_MESSAGE);
	}else if("win".equals(command)){
     
		JOptionPane.showMessageDialog(null, "全部连对完成即获得胜利!时间超过200秒则失败!",
				"提示!", JOptionPane.INFORMATION_MESSAGE);
	}
}

老Java程序员花2天写了个连连看,我竟连不过我女儿,我了个去!_第4张图片

初始化图片

将所有要用到的图片初始化,方便待会使用

public class ImageValue {
     
	//小卡片
    public static List<BufferedImage> itemImageList = new ArrayList<BufferedImage>();
    //被点击后小卡片
    public static List<BufferedImage> itemOverImageList = new ArrayList<BufferedImage>();
    //胜利图片
    public static BufferedImage winImage = null;
    //失败图片
    public static BufferedImage loseImage = null;
    //准备图片
    public static BufferedImage readyImage = null;
	//图片路径
    public static String ImagePath = "/images/";
    //将图片初始化
    public static void init(){
     
    	
    	String path = "";
        //玛丽奥图片初始化
        for(int i=1;i<=36;i++){
     
            try {
     
            	path = ImagePath + i+".gif";
            	itemImageList.add(ImageIO.read(ImageValue.class.getResource(path)));
            	path = ImagePath + i+"_over.gif";
            	itemOverImageList.add(ImageIO.read(ImageValue.class.getResource(path)));
            } catch (IOException e) {
     
                e.printStackTrace();
            }
        }
        
        try {
     
        	path = ImagePath +"win.png";
			winImage = ImageIO.read(ImageValue.class.getResource(path));
			
			path = ImagePath +"lost.png";
			loseImage = ImageIO.read(ImageValue.class.getResource(path));
			
			path = ImagePath +"ready.png";
			readyImage = ImageIO.read(ImageValue.class.getResource(path));
		} catch (IOException e) {
     
			e.printStackTrace();
		}
    }
}

根据难度系数选取图片

本例共有36张图片,并设置为8行、18列,总共144个卡片位置,难度系数有三种,分别是12、24、36
难度系数的数字越大意味中难度越大
比如难度系数12,则系统从36组图片中选出12组,组成144个卡片,意味着重复的卡片较多,而如果是难度系数36,则重复的卡片数量较少,所以难度比较大。
作为老程序员,我果断选择了12,别为我为什么?那我走?
图片列表如下
老Java程序员花2天写了个连连看,我竟连不过我女儿,我了个去!_第5张图片
从36张图片中获取12张图片,怎么做?
从1-36中随机取一个数字并存到一个集合中,如果集合中存在则跳过继续随机,直到集合中的元素达到12个,则跳出。

//初始化下标值
private void initIndexs() {
     
	Random random = new Random();
	int n ;
	while(true){
     //
		n = random.nextInt(36)+1;//随机从36张图片下标中选取
		if(!indexs.contains(n)){
     //重复的则过滤
			indexs.add(n);
		}
		if(indexs.size()==difficulty){
     //根据difficulty 来确定要多少对
			break;
		}
	}
}

执行并打印indexs(每次都不一样,这是我截图的其中一次)
在这里插入图片描述

凑齐144张图片

多次循环上述indexs 集合添加数据,其实刚好循环12次

//凑齐144张图片
private void initImage144() {
     
	while(true){
     
		for(int i=0;i<indexs.size();i++){
     
			cardIndexs.add(indexs.get(i));
		}
		if(cardIndexs.size()==144){
     
			break;
		}
	}
}

打乱顺序

因为144 是根据indexs重复循环得来的,所以顺序很固定
在这里插入图片描述
采用 Collections的shuffle方法

//随机排序
private void sortImage() {
     
    Collections.shuffle(cardIndexs);
}

再打印看看
在这里插入图片描述

创建卡片类

卡片类,属性有坐标x、y,宽高、图片、选择后图片、类型、对应行、对应列等;方法主要有绘制、判断鼠标点击是否在范围内等。

package main;

import java.awt.Graphics;
import java.awt.image.BufferedImage;
import common.ImageValue;

public class Card {
     

	private int x = 0;//x坐标
	private int y = 0;//y坐标
	private int width = 0;//宽
	private int height = 0;//高
	private int i = 0;//行
	private int j = 0;//列
	private int index = 0;//对应图片下标值
	private int type = 0;//0 原始图片  1 over图片 2 空图片
	private BufferedImage image = null;//图片对象
	private BufferedImage overImage = null;//点击后图片对象
	private GamePanel panel=null;
	
	public Card(int x,int y,int index,int i,int j,GamePanel panel){
     
		this.x=x;
		this.y=y;
		this.panel=panel;
		this.index=index;
		this.i=i;
		this.j=j;
		
		this.image = ImageValue.itemImageList.get(index);
		this.overImage = ImageValue.itemOverImageList.get(index);
		
		this.width=this.image.getWidth();
		this.height=this.image.getHeight();
	}
	
	//绘制
	public void draw(Graphics g) {
     
		if(type==0){
     
			g.drawImage(image, x, y, width,height, null);
		}else if(type==1){
     
			g.drawImage(overImage, x, y, width,height, null);
		}else if(type==2){
     //不绘制
		}
	}
	
	//判断鼠标是否卡片范围内
	boolean isPoint(int x,int y){
     
		//大于左上角,小于右下角的坐标则肯定在范围内
		if(x>this.x && y >this.y
			&& x<this.x+this.width && y <this.y+this.height){
     
			return  true;
		}
		return false;
	}
	
	public int getType() {
     
		return type;
	}
	public void setType(int type) {
     
		this.type = type;
	}
	
	public int getI() {
     
		return i;
	}
	public void setI(int i) {
     
		this.i = i;
	}
	public int getJ() {
     
		return j;
	}

	public void setJ(int j) {
     
		this.j = j;
	}
	public int getIndex() {
     
		return index;
	}
	public void setIndex(int index) {
     
		this.index = index;
	}
	public int getX() {
     
		return x;
	}

	public void setX(int x) {
     
		this.x = x;
	}

	public int getY() {
     
		return y;
	}

	public void setY(int y) {
     
		this.y = y;
	}

	public int getWidth() {
     
		return width;
	}

	public void setWidth(int width) {
     
		this.width = width;
	}

	public int getHeight() {
     
		return height;
	}

	public void setHeight(int height) {
     
		this.height = height;
	}
}

初始化卡片

根据8行18列分别计算出对应的x、y位置,代码如下:

//初始化卡片
private void initCards() {
     
	Card card;
	int x = 0;
	int y = 0;
	int index = 0 ;
	int temp=0;
	for (int i = 0; i <ROWS; i++) {
     
		y = 35 + 49*i ;
		for (int j = 0; j < COLS; j++) {
     
			x = 40 + 39*j ;
			temp = Integer.valueOf(String.valueOf(cardIndexs.get(index)));
			card = new Card(x, y, temp-1, i, j, this);
			cards.add(card);
			index++;
		}
	}
}

在paint方法中绘制,并加入边框

@Override
public void paint(Graphics g) {
     
	super.paint(g);
	gameHeight = this.getHeight();
	gameWidth = this.getWidth();
	//绘制边框
	BasicStroke bs_2=new BasicStroke(3,BasicStroke.CAP_ROUND,BasicStroke.JOIN_MITER);
	Graphics2D g_2d=(Graphics2D)g;
	g_2d.setColor(new Color(0,191,255));
	g_2d.setStroke(bs_2);
	g_2d.drawRect(38, 32, 705, 396);
	
	Card card;
	for (int i = 0; i < cards.size(); i++) {
     
		card = (Card)cards.get(i);
		card.draw(g);
	}
}

老Java程序员花2天写了个连连看,我竟连不过我女儿,我了个去!_第6张图片
到此时我们的布局和卡牌都完成了。

创建主线程

主线程,用来重绘页面,重绘全部交给主线程,主线程调用 repaint方法就行,要产生动画就要靠这个repaint。

//刷新线程,用来重新绘制页面
private class RefreshThread implements Runnable {
     
	@Override
	public void run() {
     
		while (startFlag) {
     
			repaint();
			try {
     
				Thread.sleep(200);
			} catch (InterruptedException e) {
     
				e.printStackTrace();
			}
		}
	}
}

在GamePanel的构造里面启动这个主线程
在这里插入图片描述
有了这个主线程刷新,待会我们更新画面置,不需要另外的代码去调用repaint方法了(这是我的做法,仅供参考)。

创建鼠标点击事件

当鼠标点击后,判断是否在某个卡片的范围内,如果在:
1.如果当前卡片已经是消除过的,则跳过。
2.改变card的对应的图片为点击后的图片。
3.curCard 对象无值,则将当前的card赋值给curCard 。
4.如果curCard有值,则需要进行路径比较,创建好路径检查的方法checkPath,待会要在这个方法里面写具体的判断和消除逻辑。

//鼠标事件的创建
private void createMouseListener() {
     
	MouseAdapter mouseAdapter = new MouseAdapter() {
     
		
		@Override
		public void mouseClicked(MouseEvent e) {
     
			if(!startFlag) return ;
			
			int x = e.getX();
			int y = e.getY();
			Card card;
			for (int i = 0; i < cards.size(); i++) {
     
				card = (Card)cards.get(i);
				if(card.isPoint(x, y)){
     
					if(card.getType()!=0){
     
						continue;
					}
					
					MusicPlayer.chooseMisic();
					card.setType(1);
					if(curCard==null){
     
						curCard = card ;
					}else {
     
						checkPath(card);
					}
					break;
				}
			}
		}
	};
	addMouseMotionListener(mouseAdapter);
	addMouseListener(mouseAdapter);
}

老Java程序员花2天写了个连连看,我竟连不过我女儿,我了个去!_第7张图片

消除判断

1.同一行
2.同一列
3.上下左右4个边界
4.转一个弯
5.转二个弯

同一行

判断AB两点是同一行,并且他们直接的图片是没有障碍物的(被消除过了)。所以代码只需要取出AB两点之间的元素,判断他们的类型是否等于0,是0则有障碍,如果全部都不是0,则AB两点执行消除。
为了更好看在AB消除的时候,取他们卡牌的中心点,绘制一条虚线,并且半秒之后消除(这个代码很简单就不说了)。
老Java程序员花2天写了个连连看,我竟连不过我女儿,我了个去!_第8张图片

//X方向不转弯判断
private boolean turnZeroX(Card card,Card curCard) {
     
	if(card.getI()==curCard.getI()){
     //同一行
		Card itemCard;
		for (int i = 0; i < cards.size(); i++) {
     
			itemCard = (Card)cards.get(i);
			if(itemCard.getI()==card.getI()){
     //确保同一行
				if( (itemCard.getJ()>card.getJ() && itemCard.getJ()<curCard.getJ())
						|| (itemCard.getJ()>curCard.getJ() && itemCard.getJ()<card.getJ()) ){
     //在两个卡片之间的卡片
					if(itemCard.getType()==0){
     //表示有卡片存在,则不能消除
						return false;
					}
				}
			}
		}
		//设定消除时的虚线
		int x1 = curCard.getX()+curCard.getWidth()/2;
		int y1 = curCard.getY()+curCard.getHeight()/2;
		int x2 = card.getX()+card.getWidth()/2;
		int y2 = card.getY()+card.getHeight()/2;
		dasheds.add(new Dashed(x1, y1, x2, y2,this));
		return true;
	}
	return false;
}

老Java程序员花2天写了个连连看,我竟连不过我女儿,我了个去!_第9张图片

同一列

跟上面同一行的很相似,判断Y方向AB两点之间的元素有没有被消除,如果是消除的,证明路径是通的。
老Java程序员花2天写了个连连看,我竟连不过我女儿,我了个去!_第10张图片

//Y方向不转弯判断
private boolean turnZeroY(Card card,Card curCard) {
     
	if(card.getJ()==curCard.getJ()){
     //同一列,并且类型一样
		Card itemCard;
		for (int i = 0; i < cards.size(); i++) {
     
			itemCard = (Card)cards.get(i);
			if(itemCard.getJ()==card.getJ()){
     //确保同一列
				if( (itemCard.getI()>card.getI() && itemCard.getI()<curCard.getI())
						|| (itemCard.getI()>curCard.getI() && itemCard.getI()<card.getI()) ){
     //在两个卡片之间的卡片
					if(itemCard.getType()==0){
     //表示有卡片存在,则不能消除
						return false;
					}
				}
			}
		}
		int x1 = curCard.getX()+curCard.getWidth()/2;
		int y1 = curCard.getY()+curCard.getHeight()/2;
		int x2 = card.getX()+card.getWidth()/2;
		int y2 = card.getY()+card.getHeight()/2;
		dasheds.add(new Dashed(x1, y1, x2, y2,this));
		return true;
	}
	return false;
}

老Java程序员花2天写了个连连看,我竟连不过我女儿,我了个去!_第11张图片

上下左右4个边界

如果是在上边界(即最上面的一行),那么只要图片相同是可以直接消除的,不管中间是否有障碍物;其他同理。

//4条边界的处理
private boolean border(Card card, Card curCard) {
     
	if(card.getI()==curCard.getI()){
     
		if(card.getI()==0 || card.getI() == ROWS-1){
     //上边界 或者 下边界 
			createBorderDashed(card,curCard);
			return true;
		}
	}
	if(card.getJ()==curCard.getJ()){
     
		if(card.getJ()==0 || card.getJ() == COLS-1){
     //左边界 或者 右边界 
			createBorderDashed(card,curCard);
			return true;
		}
	}
	return false;
}
//边界的连接线处理
void createBorderDashed(Card card, Card curCard){
     
	int x1 = curCard.getX()+curCard.getWidth()/2;
	int y1 = curCard.getY()+curCard.getHeight()/2;
	int x2 = 0;
	int y2 = 0;
	if(card.getI()==0){
     //上边界
		x2 = curCard.getX()+curCard.getWidth()/2;
		y2 = curCard.getY()-curCard.getHeight()/2;
		dasheds.add(new Dashed(x1, y1, x2, y2,this));
		
		x1 = card.getX()+card.getWidth()/2;
		y1 = card.getY()-card.getHeight()/2;
		dasheds.add(new Dashed(x2, y2, x1, y1,this));
		
		x2 = card.getX()+card.getWidth()/2;
		y2 = card.getY()+card.getHeight()/2;
		dasheds.add(new Dashed(x1, y1, x2, y2,this));
		
	}else if(card.getI()== ROWS-1){
     //下边界
		x2 = curCard.getX()+curCard.getWidth()/2;
		y2 = curCard.getY()+curCard.getHeight()*3/2;
		dasheds.add(new Dashed(x1, y1, x2, y2,this));
		
		x1 = card.getX()+card.getWidth()/2;
		y1 = card.getY()+card.getHeight()*3/2;
		dasheds.add(new Dashed(x2, y2, x1, y1,this));
		
		x2 = card.getX()+card.getWidth()/2;
		y2 = card.getY()+card.getHeight()/2;
		dasheds.add(new Dashed(x1, y1, x2, y2,this));
		
	}else if(card.getJ()==0 ){
     //左边界
		x2 = curCard.getX()-curCard.getWidth()/2;
		y2 = curCard.getY()+curCard.getHeight()/2;
		dasheds.add(new Dashed(x1, y1, x2, y2,this));
		
		x1 = card.getX()-card.getWidth()/2;
		y1 = card.getY()+card.getHeight()/2;
		dasheds.add(new Dashed(x2, y2, x1, y1,this));
		
		x2 = card.getX()+card.getWidth()/2;
		y2 = card.getY()+card.getHeight()/2;
		dasheds.add(new Dashed(x1, y1, x2, y2,this));
	}else if(card.getJ()==COLS-1){
     //右边界 
		
		x2 = curCard.getX()+curCard.getWidth()*3/2;
		y2 = curCard.getY()+curCard.getHeight()/2;
		dasheds.add(new Dashed(x1, y1, x2, y2,this));
		
		x1 = card.getX()+card.getWidth()*3/2;
		y1 = card.getY()+card.getHeight()/2;
		dasheds.add(new Dashed(x2, y2, x1, y1,this));
		
		x2 = card.getX()+card.getWidth()/2;
		y2 = card.getY()+card.getHeight()/2;
		dasheds.add(new Dashed(x1, y1, x2, y2,this));
	}
}

转一个弯

分解为水平检测和垂直检测,当两个都满足时则相连:
一个拐角检测 = 水平检测 && 垂直检测

老Java程序员花2天写了个连连看,我竟连不过我女儿,我了个去!_第12张图片
A 点至 B 点能否连接可转化为满足任意一点:
1.A点至C点的垂直检测,以及C点至B点的水平检测;
2.A点至D点的水平检测,以及D点至B点的垂直检测。

//转弯一次判断
private boolean turnOnce(Card card,Card curCard) {
     
	//card先横向检查到临时点,再从临时点纵向检查到curCard
	return turnOnce1(card,curCard)||turnOnce2(card,curCard);
}
//根据i、j获取到临时点
private Card getTempCard(int i, int j) {
     
	Card itemCard;
	for (int k = 0; k < cards.size(); k++) {
     
		itemCard = (Card)cards.get(k);
		if(itemCard.getI()==i && itemCard.getJ()==j){
     //确保同一行
			return itemCard;
		}
	}
	return null;
}
//一次转弯判断方式1
private boolean turnOnce1(Card card,Card curCard) {
     
	Card tempCard =  getTempCard(card.getI(),curCard.getJ());
	if(tempCard==null || tempCard.getType()!=2){
     //如果找不到临时点,或者临时点的类型不为2 则直接返回false
		return false;
	}
	
	Card itemCard;
	for (int i = 0; i < cards.size(); i++) {
     
		itemCard = (Card)cards.get(i);
		if(itemCard.getI()==tempCard.getI()){
     //确保同一行
			if( (itemCard.getJ()>card.getJ() && itemCard.getJ()<tempCard.getJ())
					|| (itemCard.getJ()>tempCard.getJ() && itemCard.getJ()<card.getJ()) ){
     //在两个卡片之间的卡片
				if(itemCard.getType()==0){
     //表示有卡片存在,则不能消除
					return false;
				}
			}
		}
	}
	
	for (int i = 0; i < cards.size(); i++) {
     
		itemCard = (Card)cards.get(i);
		if(itemCard.getJ()==tempCard.getJ()){
     //确保同一列
			if( (itemCard.getI()>tempCard.getI() && itemCard.getI()<curCard.getI())
					|| (itemCard.getI()>curCard.getI() && itemCard.getI()<tempCard.getI()) ){
     //在两个卡片之间的卡片
				if(itemCard.getType()==0){
     //表示有卡片存在,则不能消除
					return false;
				}
			}
		}
	}
	
	int x1 = curCard.getX()+curCard.getWidth()/2;
	int y1 = curCard.getY()+curCard.getHeight()/2;
	int x2 = tempCard.getX()+tempCard.getWidth()/2;
	int y2 = tempCard.getY()+tempCard.getHeight()/2;
	dasheds.add(new Dashed(x1, y1, x2, y2,this));
	
	x1 = tempCard.getX()+tempCard.getWidth()/2;
	y1 = tempCard.getY()+tempCard.getHeight()/2;
	x2 = card.getX()+card.getWidth()/2;
	y2 = card.getY()+card.getHeight()/2;
	dasheds.add(new Dashed(x1, y1, x2, y2,this));
	
	return true;
}

//一次转弯判断方式2
private boolean turnOnce2(Card card,Card curCard) {
     
	Card tempCard =  getTempCard(curCard.getI(),card.getJ());
	if(tempCard==null || tempCard.getType()!=2){
     //如果找不到临时点,或者临时点的类型不为2 则直接返回false
		return false;
	}
	Card itemCard;
	for (int i = 0; i < cards.size(); i++) {
     
		itemCard = (Card)cards.get(i);
		if(itemCard.getI()==tempCard.getI()){
     //确保同一行
			if( (itemCard.getJ()>curCard.getJ() && itemCard.getJ()<tempCard.getJ())
					|| (itemCard.getJ()>tempCard.getJ() && itemCard.getJ()<curCard.getJ()) ){
     //在两个卡片之间的卡片
				if(itemCard.getType()==0){
     //表示有卡片存在,则不能消除
					return false;
				}
			}
		}
	}
	
	for (int i = 0; i < cards.size(); i++) {
     
		itemCard = (Card)cards.get(i);
		if(itemCard.getJ()==tempCard.getJ()){
     //确保同一列
			if( (itemCard.getI()>tempCard.getI() && itemCard.getI()<card.getI())
					|| (itemCard.getI()>card.getI() && itemCard.getI()<tempCard.getI()) ){
     //在两个卡片之间的卡片
				if(itemCard.getType()==0){
     //表示有卡片存在,则不能消除
					return false;
				}
			}
		}
	}
	
	int x1 = curCard.getX()+curCard.getWidth()/2;
	int y1 = curCard.getY()+curCard.getHeight()/2;
	int x2 = tempCard.getX()+tempCard.getWidth()/2;
	int y2 = tempCard.getY()+tempCard.getHeight()/2;
	dasheds.add(new Dashed(x1, y1, x2, y2,this));
	
	x1 = tempCard.getX()+tempCard.getWidth()/2;
	y1 = tempCard.getY()+tempCard.getHeight()/2;
	x2 = card.getX()+card.getWidth()/2;
	y2 = card.getY()+card.getHeight()/2;
	dasheds.add(new Dashed(x1, y1, x2, y2,this));
	
	return true;
}

老Java程序员花2天写了个连连看,我竟连不过我女儿,我了个去!_第13张图片

转二个弯(描述网上抄的,代码自己写的

两个拐角检测可分解为一个拐角检测和水平检测或垂直检测。即:
两个拐角检测 = 一个拐角检测 && (水平检测 || 垂直检测)老Java程序员花2天写了个连连看,我竟连不过我女儿,我了个去!_第14张图片
如图,水平、垂直分别穿过 A B 共有四条直线,扫描直线上所有不包含 A B 的点,看是否存在一点 C ,满足以下任意一项:
1.A 点至 C 点通过水平或垂直检测,C 点至 B 点可通过一个拐角连接。(图中用 C 表示)
2.A 点至 C 点可通过一个拐角连接,C 点至 B 点通过水平或垂直连接。(图中用 C 下划线表示)

//转弯2次
private boolean turnTwo(Card card,Card curCard) {
     
	//两个拐角检测 = 一个拐角检测 && (水平检测 || 垂直检测)
	/*
	 * 1.card 做一个转弯检查到临时点,临时点可以再  水平检测 || 垂直检测 到curCard
	 * 2.curCard 做一个转弯检查到临时点,临时点可以再  水平检测 || 垂直检测 到card
	 */
	
	Card itemCard;//作为临时点
	for (int i = 0; i < cards.size(); i++) {
     
		itemCard = (Card)cards.get(i);
		if(itemCard.getType()!=2){
     //临时点的类型不为2 则直接跳过
			continue;
		}
		if(itemCard.getI()!=card.getI() && itemCard.getI()!=curCard.getI()
				&& itemCard.getJ()!=card.getJ() && itemCard.getJ()!=curCard.getJ()){
     //确保与其中一个卡片有横向或纵向关系
			continue;
		}
		if( (itemCard.getI()==card.getI() && itemCard.getJ()==card.getJ()) ||
				 ( itemCard.getI()==curCard.getI() && itemCard.getJ()==curCard.getJ())){
     //确保不是这两个点的其中一个
			continue;
		}
		//1.card 做一个转弯检查到临时点,临时点可以再  水平检测 || 垂直检测 到curCard
		if(turnOnce(card, itemCard) && (turnZeroX(itemCard, curCard) || turnZeroY(itemCard, curCard)  ) ){
     
			return true;
		}
		dasheds.clear();
		//2.curCard 做一个转弯检查到临时点,临时点可以再  水平检测 || 垂直检测 到card
		if(turnOnce(curCard, itemCard) && (turnZeroX(itemCard, card) || turnZeroY(itemCard, card)  ) ){
     
			return true;
		}
		dasheds.clear();
	}
	return false;
}

把代码综合起来

//检查路径
protected void checkPath(Card card) {
     
	
	if(curCard.getI()==card.getI() && curCard.getJ()==card.getJ()){
     //如果是点击自己直接返回
		return ;
	}
	if(card.getIndex()!=curCard.getIndex()){
     //确保类型一样
		curCard.setType(0);
		curCard = card;
		return ;
	}
	boolean isPathed=false;//是否在路径上
	isPathed =     turnZeroX(card,curCard) 
				|| turnZeroY(card,curCard) 
				|| border(card,curCard)
				|| turnOnce(card,curCard)
				|| turnTwo(card,curCard);
	
	if(isPathed){
     //消除
		MusicPlayer.disappearMisic();
		card.setType(2);
		curCard.setType(2);
		curCard=null;
		
		curCount ++ ;
		
		if(curCount==winCount){
     
			//胜利
			MusicPlayer.winMisic();
			
			gameWin();
		}
	}else{
     
		curCard.setType(0);
		curCard = card ;
	}
}

老Java程序员花2天写了个连连看,我竟连不过我女儿,我了个去!_第15张图片
加入积分、计时、还有音效、临时图片等就完成了
于是我就尝试了几把,发现我竟然反应如此慢,好几次都没完成,相反我女儿倒是完成的很好,这是为啥?我竟然载到我自己做的游戏当中,这就很尴尬了。回头想个办法作弊!

看到这里的大佬,动动发财的小手 点赞 + 回复 + 收藏,能【 关注 】一波就更好了。
需要源码的 留言 或者 私信,我发给你!

为了帮助更多小白从零进阶 Java 工程师,从CSDN官方那边搞来了一套 《Java 工程师学习成长知识图谱》,尺寸 870mm x 560mm,展开后有一张办公桌大小,也可以折叠成一本书的尺寸,原件129元现价 29 元先到先得,有兴趣的小伙伴可以了解一下
老Java程序员花2天写了个连连看,我竟连不过我女儿,我了个去!_第16张图片

★ 更多精彩

♥ Java飞机大战 ♥

♥ Java植物大战僵尸 ♥

♥ Java坦克大战,回忆童年!♥

♥ Java扫雷小游戏,以前上学经常玩 ♥

♥ Java学生宿舍管理系统 ♥

♥ Java实验室预约管理系统 ♥

♥ Java学生成绩管理系统 ♥

你可能感兴趣的:(java项目实战,java,游戏,连连看,swing)