基于Java的连连看游戏设计与实现

简介: 基于Java的连连看游戏设计与实现

       连连看游戏顾名思义就是找出具有关联关系的事物并进行相应处理,经历了从桌面游戏、在线游戏再到社交游戏三个过程,形式多种多样。游戏的核心要求是将两个能用三根以内的直线连接的关联事物(相同事物)消除(或进行其他处理)。一般的连连看小游戏规则和操作都简单明了易上手,但也有很多新版本的连连看游戏加入了各类道具和游戏模式来丰富游戏内容,很好地增强了游戏的娱乐性,也带给玩家长期的挑战性和新鲜感。

1.现状

       提到游戏开发,更多地会提到C和C++,这两个语言在游戏开发中有稳定和高效的表现。作为主流开发语言的Java是否也能在游戏开发方面展现良好的一面呢?从理论上讲,Java语言当然能被用来开发游戏,随着Java技术不断的更新发展,Jogl、Java3D等技术的出现,Java程序在显卡愈发强大的硬件加速基础上运行效果显著提升,开发出了许多基于Java的2D、3D游戏和大型的在线游戏。但Java作为开发语言开发游戏仍然被诟病,可以开发游戏不等同于适合开发游戏,因此,要讨论Java开发游戏的优势和意义。

2.Java日益趋向成熟

       Java语言从面世至今已过20载,它的发展也越来越符合人们对它的要求和需要。企业级应用的实践在某些方面可以看出该语言在大型软件开发方面颇为适用。当Java语言运行效率不再成为诟病时,相信Java语言跨平台、安全性、高抽象的优势在开发的大型游戏软件方面会有很好的表现。

3.Java在网络方面的良好表现

       Java在解决网络方面的问题上有不错的优势,体现在两个方面。一方面,基于applet和Java Web Start的游戏很容易更新,不需要用户手动下载新版本。另一方面,Java在网络交互的API相对友好。

4.游戏速度

       基于Java的游戏执行效率方面一直颇受争议,在大型游戏应用方面的实例不多,更适用于中小型游戏开发。Java开发的游戏执行速度被很多程序员争议的地方在于Java程序运行时要启动Java虚拟机,具体表现如何,取决于开发项目的大小和软、硬件各方面的表现。与常用来开发游戏的C++语言相比,Java开发出的游戏速度更慢,但Java开发的成本也相对较低。

三、连连看开发环境

名称

版本

语种

操作系统

Windows 

Windows 11

简体中文

开发环境

Java

JDK Version1.8.0  

简体中文

开发工具

Eclipse

Eclipse-jee-mars

简体中文

四、技术介绍

Java swing

Java.awt是Java内置的包,属于Java基本库(JFC)一部分,要使用到该包中的类,必须显式声明import java.awt.*。Component是所有组件的父类,Component类或之类创建的对象叫组件。Button、Scrollbar、Canvas、List、Checkbox、TextField、TextArea、Label是包java.awt中的类,并且是包中Component的子类。容器也是组件,可以嵌套。上图中,Applet不是java.awt中的类。

五、连连看所用画图方法

1.简介

由Sun公司与Adobe系统公司合作推出的Java 2D API,提供了一个功能强大而且非常灵活的二维图形框架。Java 2D API扩展了java.awt包中定义的Graphics类和Image类,提供了高性能的二维图形、图像和文字,同时又维持了对现有AWT应用的兼容。

2.描述

Java 2D API是JFC(Java Fundation Classes)的一员,加强了传统AWT的描绘功能。在 JDK1.2中已经支援 Java 2D 的使用。透过Java 2D API ,程序员可以轻松地描绘出任意的几何图形、运用不同的填色效果、对图形做旋转( rotate)、缩放( scale)、扭曲( shear)等。

Applet程序中所采用的AWT的绘图机制主要涉及三个方法:paint()方法、update()方法和repaint()方法,update()方法和paint()方法都有一个Graphics类参数。Graphics是画图的关键,它可以支持两种绘图:一种是基本的绘图,如:画线、矩形、圆等;另一种是画图像,主要用于动画制作。处理图形则包括两个步骤:创建Graphics对象和使用Graphics对象绘制线条和形状、呈现文本或显示与操作图像。

3.方法

Graphics中提供的图形绘制方法有:

paint( ):进行绘图的具体操作,必须有程序员重写,系统自动调用;

update( ):用于更新图形,先清除背景、前景,再调用paint();

注意:

因为update()方法和paint()方法所传递的参数是Graphics类的对象,所以通常用于在动画程序中通过重载进行绘画。

repaint( ):用于重绘图形,在组件外形发生变化,即大小改变或位置移动repaint( )方法立即被系统自动调用,而实际上repaint()方法是自动调用update()方法。

六、连连看游戏设计

1.功能模块

 (1)消除模块:

   完成连连看游戏消除动作的模块。

(2)重新开始模块:

   完成连连看游戏中重新开始新一局游戏功能的模块。

(3)刷新模块:

   完成连连看游戏中,将游戏中未消除的图片个数记录并重新随机放置等数量图片的功能模块。

(4)选择难度模块:

   完成游戏中切换游戏难度并开始新一局游戏的模块。

(5)计时模块:

   完成游戏中倒计时并显示提示信息的模块。

(6)状态显示模块:

   完成游戏中对游戏得分、闯关数记录的模块。

2.玩家面向游戏体验

        通过用例图能更清晰的分析用户需求,连连看游戏的用例图。用户在连连看游戏中,能开始游戏、重新开始游戏、重排列游戏、结束游戏、退出游戏、消除图片、查看得分和闯关数等。

七、连连看游戏功能详细介绍

第一,图片的选择:选中的图片应该区别于其他图片。

第二,图片的排列:在开始新一局的游戏时,图片的排列要清晰,满足随机的同时,要保证成对出现。

第三,记分功能:在游戏界面上要有明确标识,直观的显示当前游戏状态,包括游戏得分情况和闯关关数情况。

第四,沙漏计时功能:沙漏状计时能将时间地流逝直观形象的表达,增加游戏难度的同时很好地刺激了游戏玩家的胜负欲。

第五,游戏的开始和重新开始:在游戏开始前,应对玩家有所提示。重新开始游戏要中断当前游戏,开始一局新的游戏,同样要有所提示。

第六,游戏关闭和结束:游戏关闭和结束是两个概念,游戏关闭相当于关闭程序,游戏结束是指游戏时间到,此处应该对玩家有所提示,游戏结束后游戏依旧在运行,可以开始新一局游戏,也可以结束游戏。

第七,游戏难度:游戏难度不要仅仅局限于一个难度,提升难度有利于激发玩家运行游戏,提高游戏对玩家的吸引力和游戏本身的可玩性。游戏难度应可选。游戏默认开始时游戏难度应该较低。现设置游戏难度为4个等级,每个等级显示总体个数相同但不同种类的图片,难度越高,种类越多,整体界面看起来越复杂。

第八,游戏闯关: 每一个难度下可以有很多关卡,每一个关游戏过关后可以直接进入下一关,最好能实现每一关较上一关略有难度。

第九,游戏提示功能(刷新功能):在游戏过程中可以使用一次刷新功能,重新排列当前游戏未消除的图片个数的图片。

第十,游戏必要的提示:包括前面叙述的关卡数提示、得分提示、游戏开始和结束提示、倒计时提示。

第十一,游戏要最大限度地实现了易维护性和易操作性,游戏运行稳定、安全可靠。

八、连连看游戏图片难度介绍

游戏图片

根据游戏难度的不同将选择显示不同种类的图片,现将按照难度区分,依次展示简单模式、中等模式、困难模式和变态模式下游戏界面将出现的游戏图片素材。除简单模式,每一难度模式将新增几种图片并包含上一难度模式下的图片种类。

简单模式下将出现四种简单的游戏图片,是四种颜色不同的球体

中等模式下将包括八种图片

困难模式将包括十二种图片

变态模式将包括十五种图片

九、详细设计

详细设计是在概要设计的基础上进行细化的过程。详细设计的主要任务是设计每个模块的实现算法即实现游戏界面、组件的事件处理等。在详细设计阶段,除了要注意具体模块的功能分解,还要绘制活动图、状态图、顺序图等动态模型,丰富静态结构图。

1.界面设计

游戏主界面以窗口形式给出,用户操作的游戏功能部分由各个组件构成,通过弹出的提示框开始游戏活动,用户通过点击窗口上的各个组件完成操作。

在游戏运行开始时,将弹出一个提示框以提示用户即将开始游戏

代码如下:

if (remainTimes == 0) {
  JOptionPane.showMessageDialog(null, "游戏开始?");

单击确定按钮进入游戏主界面,默认进入简单模式下的游戏主界面

       游戏主界面展示了本次连连看小游戏的主体布局界面。在界面的左侧是游戏主要操作和显示被操作图片的区域,界面的右侧展示了功能模块中几个功能组件。界面的右侧由上至下展示了“重新开始”按钮、“刷新”按钮、难度模式选择列表(默认是简单模式)、两个标签文本、一个沙漏状的倒计时动画(包括一个隐形的标签文本,当时间还剩五秒的时候,会显示倒计时信息)。

难度中等

变态难度

2.倒计时沙漏的设计

代码如下:

super.paintComponent(g);              
    g.setColor(Color.green);
    for (int i = 0; i < 56; i++) {
      g.drawLine(x1 + i / 2 + 2, y1 + i, x2 - i / 2 - 2, y1 + i);
    }
    if (remainTimes < 55) {
      for (int i = 0; i < remainTimes; i++) {
        g.drawLine(x1+i/2+2, y2-i-1,x2-i/2-2, y2-i-1);
      }
      g.drawLine((x1 + x2) / 2, (y1 + y2) / 2, (x1 + x2) / 2, y2 - 2);
      g.drawLine((x1+x2)/2+1, (y1+y2)/2+1, (x1+x2)/2+1, y2-2);
      g.setColor(getBackground());
      for (int i = 0; i < remainTimes; i++) {
        g.drawLine(x1 + i / 2 + 2, y1 + i, x2 - i / 2 - 2, y1 + i);
      }
    }
    if (remainTimes >= 50 && remainTimes <= 55)
      overJLabel.setText((55-remainTimes) + "second");    
    if (remainTimes == 56) 
      overJLabel.setText("OVER");
    remainTimes++;
    repaint();

倒计时结束时:

3.游戏执行的实现

因为每一次选择难度模式后、每通过一关游戏后都将开始一局新游戏,故一次游戏最终状态有两种,游戏失败和游戏结束。

      一次游戏过程中,“点击图片”、“点击‘重新开始’按钮”、“点击‘刷新’按钮”操作上可不分时间先后顺序。每一次游戏结束后可以开始新一局游戏直至用户退出游戏。

4.点击图片消除的实现

选中一个图片应该区别于其他未被选中的图片。具体实现方式:当点击第一图片时,图片会变成激活状态,颜色变暗(用到了JButton的SetEnable方法)。当点击第二张图片时如果符合游戏规则,则两张图片消除,否则第一张图片变回默认状态,第二张图片变为激活状态

此功能实现代码:BlockButton[j][i].setEnabled(false);

5.记分、记关数的实现

在游戏过程中,需要对游戏当前状态进行记录,一次得分记录

例如:

6.刷新功能的实现

       游戏中每一关只有一次使用刷新功能的机会,此功能键触发的响应事件,会调用chongzai()方法,按照当前状态未消除的图片个数重新在二维按钮排列的其他位置排放其他图片。

例如:

刷新前的

刷新后的

刷新代码如下:

if (e.getSource() == reLoad) {
    chongzai();
  reLoad.setEnabled(false);
chongzai()中部分代码如下:
for (int i = 0; i < jishushengyu / 2; i++) {
    kind = random.nextInt(Kinds) + 1;
    do {        
      randomx1 = random.nextInt(8);//0-8随机数
      randomy1 = random.nextInt(8);
      } while (map[randomy1][randomx1] > 0);
    map[randomy1][randomx1] = kind;
    do {
      randomx = random.nextInt(8);
      randomy = random.nextInt(8);
      } while (map[randomy][randomx] > 0);
    map[randomy][randomx] = kind;
  }

7.游戏的连接算法

       连连看游戏中对于用户来说最简单的呈现,就是单击两个连在一起的相同的图像时,这两个图像就会消去,但是在Java代码中是如何实现的呢?连连看游戏的联通算法一般包括回溯法和堆栈实现的迷宫算法。

       根据游戏规则能知道,即使不是紧邻的两个相同的图像块消去时也不能超过两次转弯。在本次开发的连连看中两个图像块可以消去的情况大致可以分为以下几种情况。

两点直线法

三点直角折线法

四点方形折线法

四点梯形折线法

下面对上面四种类型进行细致划分讲解:

两点直线法的连接类型包括:上下,左右

三点直角折线法包括:上左,上右,下左,下右,左上,右上,左下,右下

四点方形折线法包括:上左下,上右下,下左上,下右上,左上右,右上左,左下右,右下左

四点梯形折线法包括:上左上,下左下,上右上,下左下,左上左,右上右,左下左,右下右

连接标准:

判断在一列之内两图片之间是否全部是空白或直接相邻

判断在一行之内两图片之间是否全部是空白或直接相邻

是否可以一条直线相连

是否可以两条直线相连

是否可以三条直线相连

是否可以三条直线相连,似U形

是否可以三条直线相连,似之字形N

是否处于游戏区域的四条边的同一边上

代码如下:

// 判断在一列之内两图片之间是否全部是空白或直接相邻
// 直接相连,因而不包含空白
      if (Math.abs(posY1 - posY2) == 0) {
        return true;
      }
      int a = posY1 < posY2 ? posY1 : posY2;
      int b = posY1 < posY2 ? posY2 : posY1;
      for (int j = a + 1; j < b; j++) {
        if (map[posX1][j] != 0) {
          return false;
        }
      }
// 是否可以一直线相连
if (posX1 != posX2 && posY1 != posY2) {
        return false;
      }
      if (posX1 == posX2) {
        if (containsAllOrNoneZeroInColumn(posX1, posY1, posX2, posY2)) {
          return true;
        }
      }
      if (posY1 == posY2) {
        if (containsAllOrNoneZeroInRow(posX1, posY1, posX2, posY2)) {
          return true;
        }
      }
// 是否可以三直线相连,似之字形N
      if (isOnZigzagWith1Row2Cols(posX1, posY1, posX2, posY2)) {
        return true;
      }
      if (isOnZigzagWith2Rows1Col(posX1, posY1, posX2, posY2)) {
        return true;
      }
// 是否处于游戏区域的4条边的同一边上
      if ((posY1 == posY2 && posY2 == 0)
|| (posY1 == posY2 && posY2 == 8 - 1)
          || (posX1 == posX2 && posX2 == 0)
          || (posX1 == posX2 && posX2 == 8 - 1)) {
        return true;
      }   
// 是否可以三直线相连,似之字形, 两行一列 Z
      int moreX = posX1 < posX2 ? posX2 : posX1;
      int lessX = posX1 < posX2 ? posX1 : posX2;
      for (int i = lessX + 1; i < moreX; i++) {
        if (containsAllOrNoneZeroInColumn(i, posY1, i, posY2)
            && containsAllOrNoneZeroInRow(i, posY1, posX1, posY1)
            && containsAllOrNoneZeroInRow(i, posY2, posX2, posY2)
            && map[i][posY1] == 0 && map[i][posY2] == 0) {
          return true;
        }
      }

十、代码展示

package com.ctb.swing;
import java.awt.Button;
import java.awt.Choice;//下拉列表
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class LianLianKan extends JFrame {
  private static final long serialVersionUID = 1L;
  public LianLianKan() {
    LianLianKanJPanel llk = new LianLianKanJPanel();
    add(llk);
  }
  class LianLianKanJPanel extends JPanel implements ActionListener, ItemListener {
    private static final long serialVersionUID = 1L;// 序列化时为了保持版本的兼容性,即在版本升级时反序列化仍保持对象的唯一性。
    private int[][] map = new int[8][8];// 8*8的正方形
    private int kind, randomx, randomy, randomx1, randomy1; // 种类,随机x
    private int coordinatex, coordinatey, coordinatex1, coordinatey1; // 坐标X
    private Point lineStart = new Point(0, 0);// 判断两点之间的距离
    private int clicktimes;
    private int jishushengyu;// 计数剩余
    private int Kinds = 4;
    private int score;
    private int guanshu;// 关数
    loudou ld = new loudou();// 漏斗
    JButton BlockButton[][] = new JButton[8][8];//
    Choice difficultChoice = new Choice();// 下拉列表
    JButton newgameButton = new JButton("重新开始");
    JButton reLoad = new JButton("刷新");
    ImageIcon ii = new ImageIcon("src/im/bk.jpg");
    ImageIcon aIcon = new ImageIcon("src/im/1.jpg");
    ImageIcon bIcon = new ImageIcon("src/im/2.gif");
    ImageIcon cIcon = new ImageIcon("src/im/3.gif");
    ImageIcon dIcon = new ImageIcon("src/im/4.gif");
    ImageIcon eIcon = new ImageIcon("src/im/5.gif");
    ImageIcon fIcon = new ImageIcon("src/im/6.gif");
    ImageIcon gIcon = new ImageIcon("src/im/7.gif");
    ImageIcon hIcon = new ImageIcon("src/im/8.gif");
    ImageIcon iIcon = new ImageIcon("src/im/9.gif");
    ImageIcon jIcon = new ImageIcon("src/im/10.gif");
    ImageIcon kIcon = new ImageIcon("src/im/11.gif");
    ImageIcon lIcon = new ImageIcon("src/im/12.gif");
    ImageIcon mIcon = new ImageIcon("src/im/13.gif");
    ImageIcon nIcon = new ImageIcon("src/im/14.gif");
    ImageIcon oIcon = new ImageIcon("src/im/15.gif");
    public LianLianKanJPanel() {
      this.setLayout(null);
      newMap();
      for (int i = 0; i < 8; i++) {
        for (int j = 0; j < 8; j++) {
          BlockButton[i][j] = new JButton();
          add(BlockButton[i][j]);
          BlockButton[i][j].addActionListener(this);// 监听器
          // setBounds设置该view的左上角的坐标 进而影响子视图的问题
          // frame: 该view在父view坐标系统中的位置和大小。(参照点是,父亲的坐标系统)
          // bounds:该view在本地坐标系统中的位置和大小。(参照点是,本地坐标系统,就相当于ViewB自己的坐标系统,以0,0点为起点)
          // center:该view的中心点在父view坐标系统中的位置和大小。(参照点是,父亲的坐标系统)
          BlockButton[i][j].setBounds(30 + j * 40, 30 + i * 40, 31, 34);
          // BlockButton[i][j].setBorderPainted(false);
          // BlockButton[i][j].setVisible(true);
        }
      }
      difficultChoice.add("简单");
      difficultChoice.add("中等");
      difficultChoice.add("困难");
      difficultChoice.add("变态");
      newgameButton.setBounds(map[0].length * 40 + 80, 40, 100, 20);
      newgameButton.setBackground(Color.white);
      newgameButton.setBorderPainted(false); // 去边框
      reLoad.setBounds(map[0].length * 40 + 100, 80, 60, 20);
      reLoad.setBackground(Color.white);
      reLoad.setBorderPainted(false);
      difficultChoice.setBounds(map[0].length * 40 + 100, 120, 60, 20);
      difficultChoice.addItemListener(this);
      newgameButton.addActionListener(this);
      reLoad.addActionListener(this);
      this.add(newgameButton);
      this.add(reLoad);
      this.add(difficultChoice);
      // /-------------------------漏斗
      ld.setBounds(map[0].length * 40 + 100, 200, 70, 150);// 漏斗
      ld.setBackground(Color.black);
      this.add(ld);
    }
    class loudou extends JPanel implements Runnable {// 实现runnable接口
      private static final long serialVersionUID = 1L;
      private int dijiguan;
      int remainTimes = 0; // 时间
      int x1 = 0;
      int y1 = 30;
      int x2 = 60;
      int y2 = 150;
      Thread nThread1;// 线程
      JLabel overJLabel = new JLabel();
      JDialog dialog = new JDialog();// 对话框
      public loudou() {
        nThread1 = new Thread(this);
        nThread1.start();
        // :FlowLayout、BorderLayout、GridLayout、CardLayout、GridBagLayout
        this.setLayout(null);// 默认为流式布局
        this.add(overJLabel);
        overJLabel.setBounds(0, 0, 200, 50);
        // 使对话框在最前面显示
        overJLabel.setForeground(Color.white);
      }
      public void setdijiguan(int x) {
        this.dijiguan = x;
      }
      public void paintComponent(Graphics g) // 画画函数
      {
        super.paintComponent(g);
        g.setColor(Color.white);
        for (int i = 0; i < 56; i++) {
          // drawLine画一条线
          g.drawLine(x1 + i / 2 + 2, y1 + i, x2 - i / 2 - 2, y1 + i);
        }
        if (remainTimes < 55) {
          for (int i = 0; i < remainTimes; i++) {
            g.drawLine(x1 + i / 2 + 2, y2 - i - 1, x2 - i / 2 - 2, y2 - i - 1);
          }
          g.drawLine((x1 + x2) / 2, (y1 + y2) / 2, (x1 + x2) / 2, y2 - 2);
          g.drawLine((x1 + x2) / 2 + 1, (y1 + y2) / 2 + 1, (x1 + x2) / 2 + 1, y2 - 2);// 两条竖线
          g.setColor(getBackground());
          for (int i = 0; i < remainTimes; i++) {
            g.drawLine(x1 + i / 2 + 2, y1 + i, x2 - i / 2 - 2, y1 + i);// 覆盖上边的倒三角
          }
        }
        if (remainTimes >= 50 && remainTimes <= 55)
          overJLabel.setText(55 - remainTimes + "s");
        if (remainTimes == 56)
          overJLabel.setText("OVER");
      }
      public void setTimes(int x) {
        this.remainTimes = x;
      }
      public int getTimes() {
        return remainTimes;
      }
      private int sid;
      public void run() {
        while (dijiguan < 20) {
          if (remainTimes == 0) {
            JOptionPane.showMessageDialog(null, "游戏开始?");
          }
          if (remainTimes == 56) {
            JOptionPane.showMessageDialog(null, "时间到!游戏结束!");
          }
          remainTimes++;
          repaint();// 重新----重绘组件,调用paint方法
          try {
            if (dijiguan < 6)
              Thread.sleep(1500 - dijiguan * 100);
            if (dijiguan >= 6 && dijiguan <= 8)
              Thread.sleep(1000 - (dijiguan - 5) * 50);
            if (dijiguan > 8)
              Thread.sleep(850 - (dijiguan - 8) * 20);
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
      }
    }
    public void paintComponent(Graphics g) {// Graphics 制图法
      super.paintComponent(g);
      // 是父类JPanel里的方法,会把整个面板用背景色重画一遍,起到清屏的作用
      g.drawImage(ii.getImage(), 0, 0, this);
      // 绘制两个文本字符串
      g.setColor(Color.white);
      g.drawString("得分: " + score, 430, 165);
      g.drawString("第 " + (guanshu + 1) + " 关", 430, 190);
      for (int i = 0; i < 8; i++) {
        for (int j = 0; j < 8; j++) {
          switch (map[i][j]) {
          case 0:
            BlockButton[i][j].setVisible(false);
            break;
          case 1:
            BlockButton[i][j].setIcon(aIcon);
            break;
          case 2:
            BlockButton[i][j].setIcon(bIcon);
            break;
          case 3:
            BlockButton[i][j].setIcon(cIcon);
            break;
          case 4:
            BlockButton[i][j].setIcon(dIcon);
            break;
          case 5:
            BlockButton[i][j].setIcon(eIcon);
            break;
          case 6:
            BlockButton[i][j].setIcon(fIcon);
            break;
          case 7:
            BlockButton[i][j].setIcon(gIcon);
            break;
          case 8:
            BlockButton[i][j].setIcon(hIcon);
            break;
          case 9:
            BlockButton[i][j].setIcon(iIcon);
            break;
          case 10:
            BlockButton[i][j].setIcon(jIcon);
            break;
          case 11:
            BlockButton[i][j].setIcon(kIcon);
            break;
          case 12:
            BlockButton[i][j].setIcon(lIcon);
            break;
          case 13:
            BlockButton[i][j].setIcon(mIcon);
            break;
          case 14:
            BlockButton[i][j].setIcon(nIcon);
            break;
          case 15:
            BlockButton[i][j].setIcon(oIcon);
            break;
          default:
            break;
          }
        }
      }
    }
    // 重载
    public void chongzai() {
      jishushengyu = 0;
      for (int i = 0; i < 8; i++) {
        for (int j = 0; j < 8; j++) {
          if (map[i][j] > 0) {
            jishushengyu++;
          }
        }
      }
      int[][] map1 = new int[8][8];
      this.map = map1;
      Random random = new Random();
      for (int i = 0; i < jishushengyu / 2; i++) {
        kind = random.nextInt(Kinds) + 1;// 0~3+1 === 1~4
        do {
          randomx1 = random.nextInt(8);// 0-8随机数
          randomy1 = random.nextInt(8);
        } while (map[randomy1][randomx1] > 0);
        map[randomy1][randomx1] = kind;
        do {
          randomx = random.nextInt(8);
          randomy = random.nextInt(8);
        } while (map[randomy][randomx] > 0);
        map[randomy][randomx] = kind;
      }
      repaint();
      for (int i = 0; i < 8; i++) {
        for (int j = 0; j < 8; j++) {
          BlockButton[i][j].setVisible(true);
        }
      }
    }
    public void newGame() {
      // JOptionPane.showMessageDialog(null,"你按了开始按钮");
      for (int i = 0; i < 8; i++) {
        for (int j = 0; j < 8; j++) {
          // 设置为false,该控件永远不会活动,不管设置为什么属性,都无效;
          // 设置为true,表明激活该控件,控件处于活动状态,处于活动状态,就能响应事件了,比如触摸、点击、按键事件等;
          BlockButton[i][j].setEnabled(true);// 启用--相当于开关
          BlockButton[i][j].setVisible(true);//显示--false--隐藏
          Button button = new Button();
          button.setDropTarget(getDropTarget());
        }
      }
      int[][] map = new int[8][8];
      this.map = map;
      newMap();
      ld.setTimes(0);
      score = 0;
      guanshu = 0;
      ld.setdijiguan(guanshu);
    }
    public void guoguan() {
      int jishushengyu2 = 0;
      for (int i = 0; i < 8; i++) {
        for (int j = 0; j < 8; j++) {
          if (map[i][j] > 0) {
            jishushengyu2++;
          }
        }
      }
      if (jishushengyu2 == 0) {
        for (int i = 0; i < 8; i++) {
          for (int j = 0; j < 8; j++) {
            BlockButton[i][j].setEnabled(true);// 启用
            BlockButton[i][j].setVisible(true);// 显示
          }
        }
        int[][] map = new int[8][8];
        this.map = map;
        newMap();
        ld.setTimes(0);
        guanshu++;
        ld.setdijiguan(guanshu);
        reLoad.setEnabled(true);
      }
    }
    public void newMap() {
      ArrayList<Integer> numbers = new ArrayList<Integer>();// 链表
      for (int i = 0; i < Kinds; i++) {
        numbers.add(i + 1);// 加到列表尾部
        numbers.add(i + 1);
      } // 每一次重新布局的时候,能保证一定有前几种难度中的图片类型
      Random random = new Random();
      int temp = 0;
      for (int i = 0; i < 32 - Kinds; i++) {
        temp = random.nextInt(Kinds) + 1;// 0~kinds-1之间的随机数在加1
        numbers.add(temp);
        numbers.add(temp);
      }
      Collections.shuffle(numbers);// 随机打乱原来的顺序
      map = new int[8][8];
      temp = 0;
      for (int i = 0; i < 8; i++) {
        for (int j = 0; j < 8; j++) {
          // JOptionPane.showMessageDialog(null, numbers.get(temp));
          map[i][j] = numbers.get(temp++).intValue();// get方法返回第i个元素,intvalue 返回int类型
        }
      }
    }
    public void itemStateChanged(ItemEvent e) {
      // TODO 自动生成的方法存根
      if (e.getSource() == difficultChoice) {
        String selected = difficultChoice.getSelectedItem();
        if (selected == "简单") {
          Kinds = 4;
          newGame();
          repaint();
        } else if (selected == "中等") {
          Kinds = 8;
          newGame();
          repaint();
        } else if (selected == "困难") {
          Kinds = 12;
          newGame();
          repaint();
        } else if (selected == "变态") {
          Kinds = 15;
          newGame();
          repaint();
        }
      }
    }
    public void actionPerformed(ActionEvent e) {// 侦听器
      // TODO 自动生成的方法存根
      if (ld.getTimes() > 56) {
        for (int i = 0; i < 8; i++) {
          for (int j = 0; j < 8; j++) {
            BlockButton[j][i].setEnabled(false);
          }
        }
      }
      if (e.getSource() == reLoad) {
        chongzai();
        reLoad.setEnabled(false);
      }
      if (e.getSource() == newgameButton) {
        newGame();
        reLoad.setEnabled(true);
      }
      for (int i = 0; i < 8; i++) {
        for (int j = 0; j < 8; j++) {
          if (e.getSource() == BlockButton[j][i]) {
            clicktimes++; // 点击的时间
            lineStart.move(i, j);
            if (clicktimes % 2 == 1) {
              coordinatex1 = i;
              coordinatey1 = j;
              BlockButton[coordinatey1][coordinatex1].setEnabled(false);
              BlockButton[coordinatey][coordinatex].setEnabled(true);
              // BlockButton[j][i].setEnabled(false);
            }
            if (clicktimes % 2 == 0) {
              coordinatex = i;
              coordinatey = j;
              BlockButton[coordinatey][coordinatex].setEnabled(false);
              BlockButton[coordinatey1][coordinatex1].setEnabled(true);
            }
          }
        }
      }
      this.requestFocus();
      clearBlock();
      /*
       * for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) {
       * BlockButton[j][i].setEnabled(true); }
       * 
       * }
       */
      repaint();
    }
    // --------------------------------------------------------------------------
    // 判断在一列之内两图片之间是否全部是空白或直接相邻
    private boolean containsAllOrNoneZeroInColumn(int posX1, int posY1, int posX2, int posY2) {
      // 直接相连,因而不包含空白
      if (Math.abs(posY1 - posY2) == 0) {
        return true;
      }
      int a = posY1 < posY2 ? posY1 : posY2;
      int b = posY1 < posY2 ? posY2 : posY1;// y值:a小 b大
      for (int j = a + 1; j < b; j++) {
        if (map[posX1][j] != 0) {
          return false;
        }
      }
      return true;
    }
    // 判断在一行之内两图片之间是否全部是空白或直接相邻
    private boolean containsAllOrNoneZeroInRow(int posX1, int posY1, int posX2, int posY2) {
      // 直接相连,因而不包含空白
      if (Math.abs(posX1 - posX2) == 0) {
        return true;
      }
      int a = posX1 < posX2 ? posX1 : posX2;
      int b = posX1 < posX2 ? posX2 : posX1;
      for (int i = a + 1; i < b; i++) {
        if (map[i][posY1] != 0) {
          return false;
        }
      }
      return true;
    }
    // 是否可以一直线相连
    private boolean isLinkByOneLine(int posX1, int posY1, int posX2, int posY2) {
      if (posX1 != posX2 && posY1 != posY2) {
        return false;
      }
      if (posX1 == posX2) {
        if (containsAllOrNoneZeroInColumn(posX1, posY1, posX2, posY2)) {
          return true;
        }
      }
      if (posY1 == posY2) {
        if (containsAllOrNoneZeroInRow(posX1, posY1, posX2, posY2)) {
          return true;
        }
      }
      return false;
    }
    // 是否可以两直线相连
    private boolean isLinkByTwoLines(int posX1, int posY1, int posX2, int posY2) {
      if (posX1 != posX2 && posY1 != posY2) {
        // x1,y1 to x2,y1 to x2,y2
        if (containsAllOrNoneZeroInRow(posX1, posY1, posX2, posY1) && map[posX2][posY1] == 0
            && containsAllOrNoneZeroInColumn(posX2, posY1, posX2, posY2)) {
          return true;
        }
        // x1,y1 to x1,y2 to x2,y2
        if (containsAllOrNoneZeroInColumn(posX1, posY1, posX1, posY2) && map[posX1][posY2] == 0
            && containsAllOrNoneZeroInRow(posX1, posY2, posX2, posY2)) {
          return true;
        }
      }
      return false;
    }
    // 是否可以三直线相连
    private boolean isLinkByThreeLines(int posX1, int posY1, int posX2, int posY2) {
      if (isOnSameEdge(posX1, posY1, posX2, posY2)) {
        return true;
      }
      if (isOnThreeLinesLikeArc(posX1, posY1, posX2, posY2)) {
        return true;
      }
      if (isOnThreeLinesLikeZigzag(posX1, posY1, posX2, posY2)) {
        return true;
      }
      return false;
    }
    // 是否可以三直线相连,似U形
    private boolean isOnThreeLinesLikeArc(int posX1, int posY1, int posX2, int posY2) {
      if (isOnUpArc(posX1, posY1, posX2, posY2)) {
        return true;
      }
      if (isOnDownArc(posX1, posY1, posX2, posY2)) {
        return true;
      }
      if (isOnLeftArc(posX1, posY1, posX2, posY2)) {
        return true;
      }
      if (isOnRightArc(posX1, posY1, posX2, posY2)) {
        return true;
      }
      return false;
    }
    // ∪
    private boolean isOnUpArc(int posX1, int posY1, int posX2, int posY2) {
      // Y --> 0
      int lessY = posY1 < posY2 ? posY1 : posY2; // 找小y
      for (int j = lessY - 1; j >= 0; j--) {
        if (containsAllOrNoneZeroInRow(posX1, j, posX2, j)
            && containsAllOrNoneZeroInColumn(posX1, posY1, posX1, j)
            && containsAllOrNoneZeroInColumn(posX2, posY2, posX2, j) && map[posX1][j] == 0
            && map[posX2][j] == 0) {
          return true;
        }
      }
      if (isOnSameEdge(posX1, 0, posX2, 0) && containsAllOrNoneZeroInColumn(posX1, posY1, posX1, 0)
          && containsAllOrNoneZeroInColumn(posX2, posY2, posX2, 0)
          && (map[posX1][0] == 0 && map[posX2][0] == 0
              || map[posX1][0] == 0 && map[posX2][0] == map[posX2][posY2]
              || map[posX1][0] == map[posX1][posY1] && map[posX2][0] == 0)) {
        return true;
      }
      return false;
    }
    // ∩
    private boolean isOnDownArc(int posX1, int posY1, int posX2, int posY2) {
      int moreY = posY1 < posY2 ? posY2 : posY1;
      for (int j = moreY + 1; j <= 8 - 1; j++) {
        if (containsAllOrNoneZeroInRow(posX1, j, posX2, j)
            && containsAllOrNoneZeroInColumn(posX1, posY1, posX1, j)
            && containsAllOrNoneZeroInColumn(posX2, posY2, posX2, j) && map[posX1][j] == 0
            && map[posX2][j] == 0) {
          return true;
        }
      }
      if (isOnSameEdge(posX1, 8 - 1, posX2, 8 - 1) && containsAllOrNoneZeroInColumn(posX1, posY1, posX1, 8 - 1)
          && containsAllOrNoneZeroInColumn(posX2, posY2, posX2, 8 - 1)
          && (map[posX1][8 - 1] == 0 && map[posX2][8 - 1] == 0
              || map[posX1][8 - 1] == map[posX1][posY1] && map[posX2][8 - 1] == 0
              || map[posX1][8 - 1] == 0 && map[posX2][8 - 1] == map[posX2][posY2])) {
        return true;
      }
      return false;
    }
    // ﹚
    private boolean isOnLeftArc(int posX1, int posY1, int posX2, int posY2) {
      int lessX = posX1 < posX2 ? posX1 : posX2;
      for (int i = lessX - 1; i >= 0; i--) {
        if (containsAllOrNoneZeroInColumn(i, posY1, i, posY2)
            && containsAllOrNoneZeroInRow(i, posY1, posX1, posY1)
            && containsAllOrNoneZeroInRow(i, posY2, posX2, posY2) && map[i][posY1] == 0
            && map[i][posY2] == 0) {
          return true;
        }
      }
      if (isOnSameEdge(0, posY1, 0, posY2) && containsAllOrNoneZeroInRow(0, posY1, posX1, posY1)
          && containsAllOrNoneZeroInRow(0, posY2, posX2, posY2)
          && (map[0][posY1] == 0 && map[0][posY2] == 0
              || map[0][posY1] == map[posX1][posY1] && map[0][posY2] == 0
              || map[0][posY1] == 0 && map[0][posY2] == map[posX2][posY2])) {
        return true;
      }
      return false;
    }
    // (
    private boolean isOnRightArc(int posX1, int posY1, int posX2, int posY2) {
      int moreX = posX1 < posX2 ? posX2 : posX1;
      for (int i = moreX + 1; i <= 8 - 1; i++) {
        if (containsAllOrNoneZeroInColumn(i, posY1, i, posY2)
            && containsAllOrNoneZeroInRow(i, posY1, posX1, posY1)
            && containsAllOrNoneZeroInRow(i, posY2, posX2, posY2) && map[i][posY1] == 0
            && map[i][posY2] == 0) {
          return true;
        }
      }
      if (isOnSameEdge(8 - 1, posY1, 8 - 1, posY2) && containsAllOrNoneZeroInRow(posX1, posY1, 8 - 1, posY1)
          && containsAllOrNoneZeroInRow(posX2, posY2, 8 - 1, posY2)
          && (map[8 - 1][posY1] == 0 && map[8 - 1][posY2] == 0
              || map[8 - 1][posY1] == map[posX1][posY1] && map[8 - 1][posY2] == 0
              || map[8 - 1][posY1] == 0 && map[8 - 1][posY2] == map[posX2][posY2])) {
        return true;
      }
      return false;
    }
    // 是否可以三直线相连,似之字形N
    private boolean isOnThreeLinesLikeZigzag(int posX1, int posY1, int posX2, int posY2) {
      if (isOnZigzagWith1Row2Cols(posX1, posY1, posX2, posY2)) {
        return true;
      }
      if (isOnZigzagWith2Rows1Col(posX1, posY1, posX2, posY2)) {
        return true;
      }
      return false;
    }
    // 是否可以三直线相连,似之字形, 两行一列 Z
    private boolean isOnZigzagWith2Rows1Col(int posX1, int posY1, int posX2, int posY2) {
      int moreX = posX1 < posX2 ? posX2 : posX1;
      int lessX = posX1 < posX2 ? posX1 : posX2;
      for (int i = lessX + 1; i < moreX; i++) {
        if (containsAllOrNoneZeroInColumn(i, posY1, i, posY2)
            && containsAllOrNoneZeroInRow(i, posY1, posX1, posY1)
            && containsAllOrNoneZeroInRow(i, posY2, posX2, posY2) && map[i][posY1] == 0
            && map[i][posY2] == 0) {
          return true;
        }
      }
      return false;
    }
    // 是否可以三直线相连,似之字形, 一行两列
    private boolean isOnZigzagWith1Row2Cols(int posX1, int posY1, int posX2, int posY2) {
      int moreY = posY1 < posY2 ? posY2 : posY1;
      int lessY = posY1 < posY2 ? posY1 : posY2;
      for (int j = lessY + 1; j < moreY; j++) {
        if (containsAllOrNoneZeroInRow(posX1, j, posX2, j)
            && containsAllOrNoneZeroInColumn(posX1, posY1, posX1, j)
            && containsAllOrNoneZeroInColumn(posX2, posY2, posX2, j) && map[posX1][j] == 0
            && map[posX2][j] == 0) {
          return true;
        }
      }
      return false;
    }
    // 是否处于游戏区域的4条边的同一边上
    private boolean isOnSameEdge(int posX1, int posY1, int posX2, int posY2) {
      if ((posY1 == posY2 && posY2 == 0) || (posY1 == posY2 && posY2 == 8 - 1) || (posX1 == posX2 && posX2 == 0)
          || (posX1 == posX2 && posX2 == 8 - 1)) {
        return true;
      }
      return false;
    }
    // --------------------------------------------------------------------------
    public boolean ifcanTouch(int posX1, int posY1, int posX2, int posY2) {
      if (isLinkByOneLine(posX1, posY1, posX2, posY2)) {
        return true;
      }
      // 是否可以两直线相连
      if (isLinkByTwoLines(posX1, posY1, posX2, posY2)) {
        return true;
      }
      // 是否可以三直线相连
      if (isLinkByThreeLines(posX1, posY1, posX2, posY2)) {
        return true;
      }
      return false;
    }
    public void clearBlock() {
      if (clicktimes >= 2) {
        if (map[coordinatey1][coordinatex1] == map[coordinatey][coordinatex]
            && !((coordinatex1 == coordinatex) && (coordinatey1 == coordinatey))) {
          if (ifcanTouch(coordinatey1, coordinatex1, coordinatey, coordinatex)) {
            if (map[coordinatey1][coordinatex1] > 0)
              score = score + 10;
            map[coordinatey1][coordinatex1] = 0;
            map[coordinatey][coordinatex] = 0;
            guoguan();
          }
        }
      }
    }
  }
  public static void main(String[] args) {
    // Swing 由 MVC 结构组成的---模型、视图、控制器
    String lookAndFeel = "javax.swing.plaf.metal.MetalLookAndFeel";// swing 外观和感觉
    try {
      UIManager.setLookAndFeel(lookAndFeel);
    } catch (ClassNotFoundException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (InstantiationException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (IllegalAccessException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    } catch (UnsupportedLookAndFeelException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
    LianLianKan frame = new LianLianKan();
    frame.setTitle("连连看");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setBounds(100, 100, 560, 430);
    frame.setLocation(600, 570);// 设置位置
    // frame.setSize(600, 500);
    frame.setSize(600, 480);// 定型--设置大小
    frame.setVisible(true);// 设置可见
  }
}

十一、总结

1.掌握基本理论知识,结合理论完成实践,提高综合分析问题,解决问题的能力。

2.开发小型应用系统,掌握运用Java语言编写调试应用程序,训练独立开发应用系统,进行数据处理的综合能力。

3.掌握面向对象程序设计的方法。

4.熟练掌握Java语言的基本语法,灵活运用各种数据类型,对面向对象的程序设计语言的学习有更好的理解。

5.进一步掌握在集成环境下如何调试程序和修改程序。

若有什么不理解,可在评论区中阐述哦😊😊😊

相关文章
|
6月前
|
Java Windows
基于java Swing编写扫雷游戏设计实现(已调试)
基于java Swing编写扫雷游戏设计实现(已调试)
84 1
|
小程序 Java 开发工具
0037Java程序设计-基于Java的连连看游戏设计与实现毕业设计论文
0037Java程序设计-基于Java的连连看游戏设计与实现毕业设计论文
132 0
|
27天前
|
Java 开发者
Java多线程编程中的常见误区与最佳实践####
本文深入剖析了Java多线程编程中开发者常遇到的几个典型误区,如对`start()`与`run()`方法的混淆使用、忽视线程安全问题、错误处理未同步的共享变量等,并针对这些问题提出了具体的解决方案和最佳实践。通过实例代码对比,直观展示了正确与错误的实现方式,旨在帮助读者构建更加健壮、高效的多线程应用程序。 ####
|
3天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
28 6
|
18天前
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####
|
16天前
|
存储 监控 小程序
Java中的线程池优化实践####
本文深入探讨了Java中线程池的工作原理,分析了常见的线程池类型及其适用场景,并通过实际案例展示了如何根据应用需求进行线程池的优化配置。文章首先介绍了线程池的基本概念和核心参数,随后详细阐述了几种常见的线程池实现(如FixedThreadPool、CachedThreadPool、ScheduledThreadPool等)的特点及使用场景。接着,通过一个电商系统订单处理的实际案例,分析了线程池参数设置不当导致的性能问题,并提出了相应的优化策略。最终,总结了线程池优化的最佳实践,旨在帮助开发者更好地利用Java线程池提升应用性能和稳定性。 ####
|
18天前
|
缓存 Java 开发者
Java多线程编程的陷阱与最佳实践####
本文深入探讨了Java多线程编程中常见的陷阱,如竞态条件、死锁和内存一致性错误,并提供了实用的避免策略。通过分析典型错误案例,本文旨在帮助开发者更好地理解和掌握多线程环境下的编程技巧,从而提升并发程序的稳定性和性能。 ####
|
12天前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####
|
12天前
|
Java 调度
Java中的多线程编程与并发控制
本文深入探讨了Java编程语言中多线程编程的基础知识和并发控制机制。文章首先介绍了多线程的基本概念,包括线程的定义、生命周期以及在Java中创建和管理线程的方法。接着,详细讲解了Java提供的同步机制,如synchronized关键字、wait()和notify()方法等,以及如何通过这些机制实现线程间的协调与通信。最后,本文还讨论了一些常见的并发问题,例如死锁、竞态条件等,并提供了相应的解决策略。
34 3
|
13天前
|
监控 Java 开发者
深入理解Java中的线程池实现原理及其性能优化####
本文旨在揭示Java中线程池的核心工作机制,通过剖析其背后的设计思想与实现细节,为读者提供一份详尽的线程池性能优化指南。不同于传统的技术教程,本文将采用一种互动式探索的方式,带领大家从理论到实践,逐步揭开线程池高效管理线程资源的奥秘。无论你是Java并发编程的初学者,还是寻求性能调优技巧的资深开发者,都能在本文中找到有价值的内容。 ####