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

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

引言:

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

在这里插入图片描述

代码实现

创建窗口

首先创建一个游戏窗体类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类,窗口建出来了
在这里插入图片描述

创建菜单及菜单选项

创建菜单

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
在这里插入图片描述
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);
	}
}

在这里插入图片描述

初始化图片

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

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,别为我为什么?那我走?
图片列表如下
在这里插入图片描述
从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);
	}
}

在这里插入图片描述
到此时我们的布局和卡牌都完成了。

创建主线程

主线程,用来重绘页面,重绘全部交给主线程,主线程调用 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);
}

在这里插入图片描述

消除判断

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

同一行

判断AB两点是同一行,并且他们直接的图片是没有障碍物的(被消除过了)。所以代码只需要取出AB两点之间的元素,判断他们的类型是否等于0,是0则有障碍,如果全部都不是0,则AB两点执行消除。
为了更好看在AB消除的时候,取他们卡牌的中心点,绘制一条虚线,并且半秒之后消除(这个代码很简单就不说了)。
在这里插入图片描述

//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;
}

在这里插入图片描述

同一列

跟上面同一行的很相似,判断Y方向AB两点之间的元素有没有被消除,如果是消除的,证明路径是通的。
在这里插入图片描述

//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;
}

在这里插入图片描述

上下左右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));
	}
}

转一个弯

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

在这里插入图片描述
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;
}

在这里插入图片描述

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

两个拐角检测可分解为一个拐角检测和水平检测或垂直检测。即:
两个拐角检测 = 一个拐角检测 && (水平检测 || 垂直检测)在这里插入图片描述
如图,水平、垂直分别穿过 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 工程师,从CSDN官方那边搞来了一套 《Java 工程师学习成长知识图谱》,尺寸 870mm x 560mm,展开后有一张办公桌大小,也可以折叠成一本书的尺寸,原件129元现价 29 元先到先得,有兴趣的小伙伴可以了解一下
在这里插入图片描述

★ 更多精彩

♥ Java飞机大战 ♥

♥ Java植物大战僵尸 ♥

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

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

♥ Java学生宿舍管理系统 ♥

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

♥ Java学生成绩管理系统 ♥

编程界明世隐 CSDN认证博客专家 Java方面专家 JS大神 最强辅助小明
从事软件开发多年,精通Java、JavaScript,博主也是从零开始一步步把学习成长、深知学习和积累的重要性,喜欢跟广大ADC一起打野升级,欢迎您关注,期待与您一起学习、成长、起飞!
博主维护《编程界明世隐》公众号,里面有各种学习资料、面试技巧以及实例!相信也可以帮助你在下路杀穿对手!
代码范例列表 第1章 示例描述:本章演示如何开始使用JDK进行程序的开发。 HelloWorldApp.java 第一个用Java开发的应用程序。 firstApplet.java 第一个用Java开发的Applet小程序。 firstApplet.htm 用来装载Applet的网页文件 第2章 示例描述:本章介绍开发Java的基础语法知识。 accumulationByDoWhile.java 用do~while语句的累加程序 accumulationByFor.java 用for语句的累加程序 accumulationByWhile.java 用while语句的累加程序 boolExample.java 演示boolean变量的程序 charExample.java 演示char变量的程序 compare.java 演示前缀、后缀自加之间区别的程序 constCharExample.java 演示转义字符 converseNumber.java 逆向输出数字 daffodilNumber.java 求水仙数 division.java 演示整除结果 errorCompoundVariable.java 错误使用局部变量示例 factorial.java 求阶乘 Fibonacci.java 求Fiblnacci数列 GcdAndGcm.java 求最大公约数和最小公倍数 errorInit.java 演示变量初始化错误的程序 integerExample.java 演示各种整型变量的使用 isPrime.java 判断素数 leapYearByIf.java 用if语句判断闰年 leapYearByLogical.java 用逻辑表达式判断闰年 lowToUpper.java字母转换成大字母 lozengeStar.java 输出一个由*组成的菱形 multiplyTable.java 求乘法口诀表 narrowingConversion_1.java 缩减转换引发错误示例1 narrowingConversion_2.java 缩减转换引发错误示例2 notMultipleOfThree.java 把100-200之间不能被3整除的数输出 outputByDoWhile.java 用while循环随机输出数据 outputByWhile.java 用do~while循环随机输出数据 outputMax.java 求两个数中的最大数 overflowExample.java 演示溢出 precedence.java 演示自加运算符的优先级 primeNumber.java 输出100-200之间的所有素数 ranking.java 评定成绩等级 rankingBySwitch.java 用switch语句评定成绩等级 realExample.java 演示浮点数的有效位数 remainder.java 取余运算示例 showBreak.java 利用标号语句跳转出所有循环嵌套 showCount.java 循环计数示例 showDoubleLoop.java 演示双重循环 showDoubleLoopSe.java 改进的双重循环 showOrder_1.java 演示操作数求值顺序示例1 showOrder_2.java 演示操作数求值顺序示例2 sign.java 用条件运算实现符号函数示例 signByIF.java 用if语句实现符号函数示例 triangleStar.java 输出一个由*组成的直角三角形 upperToLowCase.java转换成小 variableScopeExample.java 变量使用范围示例 第3章 示例描述:本章学习对象和类。 accessMember.java 访问成员变量示例 constructNoPara.java 无参数的构造方法 constructWithPara.java 带参数的构造方法 declareDefault.java 缺省访问权限的使用 declarePrivate.java 私有访问权限的使用 dec
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付 69.90元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值