目录
这里使用数字卡片来代替图片,纯Java代码实现, 使用Swing来绘图。

这里主要用来练手,尤其是对于新手来说,可以更深刻的理解多线程、异步操作、对象引用、简单算法、数据结构等。

真个布局分为以上6个区域,其中较为复杂的为1和6。
1区域当前采用塔型结构进行堆叠演示(堆叠规则可以任意调整),技术点在于如果点击的卡片的上方有其它卡片,则该卡片不能移动。
技术点:
1、图中每个卡片最多可被四个卡片同时覆盖,其中四个角、边缘、中间区域的卡片覆盖个数都有差别,设计算法校验时需要考虑全面。
2、各个层级的图片之间没有使用遮照进行处理,主要个人喜好。
6区域看起来就是消除三个相同数字的卡片。

技术分析:
1、1~5区域的卡片要能移动到6,最大个数为6
2、对卡片进行排序,由小到大
3、卡片消除处理。
2~4区域的卡片相对简单,只需要注意点击的是最后一张图片即可。
每次运行游戏,都会随机生成所有卡片,是否通关完全靠运气,没有对死局进行处理。
通过Swing来练手,对新手来说,了解Java面向对象的设计理念很有帮助。
卡片数字范围1~9,卡片个数93张,其中1区域55张,2、3各10张,4、5各9张。
代码实现:
- /**
- * @ClassName: NumCardsUtil
- * @Description 工具类
- * @author 月夜烛峰
- * @date 2022/9/27 20:18
- */
- public class NumCardsUtil {
-
- /**随机卡片数组*/
- public static int[] cardsCount = new int[9];
- /**所有卡片数字存储*/
- public static List
cardsList = new ArrayList<>(); - /**卡片name存储*/
- public static List
centerCardData = new ArrayList<>(); -
- /**中间卡片*/
- public static List
centerCards = new ArrayList<>(); - /**左边卡片*/
- public static List
leftCards1 = new ArrayList<>(); - public static List
leftCards2 = new ArrayList<>(); - /**右边卡片*/
- public static List
rightCards1 = new ArrayList<>(); - public static List
rightCards2 = new ArrayList<>(); -
- /**
- * 初始化卡片数字
- */
- public static void initCards() {
- clearCards();
-
- //初始化数字卡片
- Random r = new Random();
- int num;
- for (int i = 0; i < 93; i++) {
- num = r.nextInt(8) + 1;
- cardsCount[num]++;
- cardsList.add(num);
- }
-
- initCardList(centerCards, 55);
- initCardList(leftCards1, 9);
- initCardList(leftCards2, 10);
- initCardList(rightCards1, 9);
- initCardList(rightCards2, 10);
- }
-
- /**
- * 初始化卡片对象
- * @param list
- * @param cardCount
- */
- public static void initCardList(List
list, int cardCount) { - Random r = new Random();
- int index;
- CardNode numLabel;
- for (int i = 0; i < cardCount; i++) {
- index = r.nextInt(cardsList.size());
- numLabel = new CardNode(String.valueOf(cardsList.get(index)), JLabel.CENTER);
- list.add(numLabel);
- cardsList.remove(index);
- }
- }
-
- /**
- * 根据层级个数生成图片,固定为5,可拓展
- * @param level
- */
- public static void initcenterCardData(int level) {
-
- for (int n = 1; n < level; n++) {
- for (int i = 0, x = 0, y = -1; i < n * n; i++) {
- if (i % n == 0) {
- x = 0;
- y++;
- }
- centerCardData.add("center-" + n + "-" + x + "-" + y);
- x++;
- }
- }
- }
-
- /**
- * 清空数据
- */
- public static void clearCards() {
- centerCardData.clear();
- centerCards.clear();
- leftCards1.clear();
- leftCards2.clear();
- rightCards1.clear();
- rightCards2.clear();
- }
- }
主要定义卡片的存放位置、展示位置、区域类型等
- /**
- * @ClassName: CardNode
- * @Description 卡片属性
- * @author 月夜烛峰
- * @date 2022/9/27 20:13
- */
- public class CardNode extends JLabel {
-
- /**区域类型*/
- private int type;
- /**堆放级别*/
- private int level;
- /**横坐标索引*/
- private int xIndex;
- /**纵坐标索引*/
- private int yIndex;
-
- public CardNode(String text, int horizontalAlignment) {
- super(text, horizontalAlignment);
- }
-
- public int getType() {
- return type;
- }
-
- public void setType(int type) {
- this.type = type;
- }
-
- public int getLevel() {
- return level;
- }
-
- public void setLevel(int level) {
- this.level = level;
- }
-
- public int getxIndex() {
- return xIndex;
- }
-
- public void setxIndex(int xIndex) {
- this.xIndex = xIndex;
- }
-
- public int getyIndex() {
- return yIndex;
- }
-
- public void setyIndex(int yIndex) {
- this.yIndex = yIndex;
- }
- }
关于操作面板中内容操作的代码实现:
- /**
- * @ClassName: PlayGamePanel
- * @Description 游戏面板
- * @author 月夜烛峰
- * @date 2022/9/27 20:00
- */
- public class PlayGamePanel extends JPanel implements MouseListener {
-
- private int[] nodeCount = new int[9];
- private int[][] bottomPoints = new int[6][2];
- private List
bottomNodeList = new ArrayList<>(); -
- private JPanel bottomPanel = new JPanel();
-
- private JLabel tips = new JLabel("游戏进行中...", JLabel.CENTER);
-
- public PlayGamePanel() {
- this.setLayout(null);
- bottomPanel.setLayout(null);
- bottomPanel.setBounds(150, 480, 500, 80);
- bottomPanel.setBackground(Color.LIGHT_GRAY);
-
- tips.setFont(new Font("Dialog", Font.PLAIN, 20));
- tips.setBounds(150, 370, 500, 80);
- add(tips);
-
- initCardLabel();
- //初始化底部卡槽背景
- for (int i = 6; i > 0; i--) {
- JLabel emptyLabel = new JLabel();
- emptyLabel.setOpaque(true);
- emptyLabel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
- emptyLabel.setBackground(Color.GRAY);
- emptyLabel.setBounds(70 * i - 20, 15, 50, 50);
- bottomPanel.add(emptyLabel);
- }
- //初始化底部6个卡槽坐标
- for (int x = 0; x < 6; x++) {
- bottomPoints[x][0] = 200 + x * 70;
- bottomPoints[x][1] = 495;
- }
-
- add(bottomPanel);
- }
-
- public void initCardLabel() {
- NumCardsUtil.initCards();
- NumCardsUtil.initcenterCardData(5);
-
- int curIndex = 0;
- int curLineNum = 0;
- int nextLineNum = 0;
-
-
- for (int n = 1; n <= 5; n++) {
- for (int i = 0; i < n * n; i++) {
- if (i % n == 0) {
- nextLineNum++;
- curLineNum = 0;
- }
- CardNode node = createNumLabel("center-" + n + "-" + curLineNum + "-" + (nextLineNum - 1), NumCardsUtil.centerCards.get(curIndex++), 0, 395 - n * 25 + 50 * curLineNum, 125 - 25 * n + 50 * nextLineNum);
- node.setxIndex(curLineNum);
- node.setyIndex(nextLineNum - 1);
- node.setLevel(n);
-
- curLineNum++;
- }
- curLineNum = 0;
- nextLineNum = 0;
- }
-
- //底部卡片列表
- for (int i = 8; i > 0; i--) {
- //NumCardsUtil.leftCards
- createNumLabel("left1-" + i, NumCardsUtil.leftCards1.get(i), 1, 20 + 15 * i, 400);
- createNumLabel("right1-" + i, NumCardsUtil.rightCards1.get(i), 2, 730 - 15 * i, 400);
- }
-
- //中部卡片列表
- for (int i = 9; i > 0; i--) {
- //NumCardsUtil.leftCards
- createNumLabel("left2-" + i, NumCardsUtil.leftCards2.get(i), 3, 150, 180 + 15 * i);
- createNumLabel("right2-" + i, NumCardsUtil.rightCards2.get(i), 4, 600, 180 + 15 * i);
- }
- }
-
- /**
- * 数字卡片
- * @param numLabel
- * @param x
- * @param y
- */
- public CardNode createNumLabel(String name, CardNode numLabel, int type, int x, int y) {
- numLabel.setName(name);
- numLabel.setFont(new Font("Dialog", Font.PLAIN, 18));
- numLabel.setOpaque(true);
- numLabel.setType(type);
-
- numLabel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
- numLabel.setBackground(Color.GRAY);
- numLabel.setBounds(x, y, 50, 50);
- numLabel.setPreferredSize(new Dimension(50, 50));
- numLabel.setVerticalTextPosition(JLabel.BOTTOM);
- numLabel.setHorizontalTextPosition(JLabel.CENTER);
- numLabel.addMouseListener(this);
-
- add(numLabel);
- return numLabel;
- }
-
- /**
- * 刷新面板
- */
- public void startRun() {
- new Thread() {
- @Override
- public void run() {
- while (true) {
- try {
- Thread.sleep(100);
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- //刷新屏幕,自动调用paint()方法
- repaint();
- }
- }
- }.start();
- }
-
- /**
- * 是否存在父级
- * @param level
- * @param x
- * @param y
- * @return
- */
- public boolean hasParentLabel(int level, int x, int y) {
- String[] ids = {level + "-" + x + "-" + y, level + "-" + (x - 1) + "-" + y, level + "-" + x + "-" + (y - 1), level + "-" + (x - 1) + "-" + (y - 1)};
- for (String id : ids) {
- if (NumCardsUtil.centerCardData.contains("center-" + id)) {
- return true;
- }
- }
- return false;
- }
-
- public void dealUnCenterCardList(List
list, CardNode node) { - CardNode lastNode = list.get(list.size() - 1);
- if (lastNode != node) {
- return;
- }
- list.remove(node);
- orderList(node);
- }
-
- /**
- * 重排序
- * @param node
- */
- private void orderList(CardNode node) {
- int curValue = Integer.parseInt(node.getText());
- nodeCount[curValue]++;
- if (bottomNodeList.size() == 0) {
- bottomNodeList.add(node);
- node.setLocation(bottomPoints[0][0], bottomPoints[0][1]);
- return;
- }
- int curIndex = 0;
- for (CardNode n : bottomNodeList) {
- int tempValue = Integer.parseInt(n.getText());
- if (curValue >= tempValue) {
- curIndex++;
- } else {
- break;
- }
- }
- bottomNodeList.add(curIndex, node);
- for (int j = 0; j < bottomNodeList.size(); j++) {
- bottomNodeList.get(j).setLocation(bottomPoints[j][0], bottomPoints[j][1]);
- }
-
- if (nodeCount[curValue] >= 3) {
- nodeCount[curValue] = 0;
- for (int i = 0; i < 3; i++) {
- bottomNodeList.get(curIndex).setVisible(false);
- bottomNodeList.remove(curIndex);
- if (curIndex > 0) {
- curIndex--;
- }
- }
- }
- for (int j = 0; j < bottomNodeList.size(); j++) {
- bottomNodeList.get(j).setLocation(bottomPoints[j][0], bottomPoints[j][1]);
- }
- }
-
- @Override
- public void mouseClicked(MouseEvent e) {
-
- }
-
- @Override
- public void mousePressed(MouseEvent e) {
-
- }
-
- @Override
- public void mouseReleased(MouseEvent e) {
-
- Component com = e.getComponent();
- if (com instanceof CardNode) {
- CardNode node = (CardNode) com;
- switch (node.getType()) {
- case 0:
- boolean flag = hasParentLabel(node.getLevel() - 1, node.getxIndex(), node.getyIndex());
- if (!flag) {
- NumCardsUtil.centerCardData.remove(node.getName());
- orderList(node);
- }
- break;
- case 1:
- dealUnCenterCardList(NumCardsUtil.leftCards1, node);
- break;
- case 2:
- dealUnCenterCardList(NumCardsUtil.rightCards1, node);
- break;
- case 3:
- dealUnCenterCardList(NumCardsUtil.leftCards2, node);
- break;
- case 4:
- dealUnCenterCardList(NumCardsUtil.rightCards2, node);
- break;
- default:
- break;
- }
-
- if (bottomNodeList.size() >= 6) {
- tips.setText("游戏结束!");
- tips.setForeground(Color.RED);
-
- this.setEnabled(false);
- }
- }
- }
-
- @Override
- public void mouseEntered(MouseEvent e) {
-
- }
-
- @Override
- public void mouseExited(MouseEvent e) {
-
- }
- }
- /**
- * @ClassName: GameFrame
- * @Description 游戏窗口
- * @author 月夜烛峰
- * @date 2022/9/27 20:05
- */
- public class GameFrame extends JFrame{
-
- public GameFrame() {
- init();
- }
-
- public void init() {
- this.setTitle("月夜烛峰");
- //添加面板
- this.setSize(800, 600);
- this.setLocationRelativeTo(null);
- this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- this.setResizable(false);
- this.setVisible(true);
- PlayGamePanel play = new PlayGamePanel();
- add(play);
- play.startRun();
-
- }
- }
运行游戏:
- /**
- * @ClassName: StartGame
- * @Description 开始游戏
- * @author 月夜烛峰
- * @date 2022/9/27 19:04
- */
- public class StartGame {
- public static void main(String[] args) {
- // 显示应用 GUI
- SwingUtilities.invokeLater(new Runnable() {
- @Override
- public void run() {
- new GameFrame();
- }
- });
- }
- }