目录
一、效果图
二、 代码实现
1、布局规划
2、卡片生成
3、卡片属性
4、操作面板
5、游戏窗口
一、效果图
这里使用数字卡片来代替图片,纯Java代码实现, 使用Swing来绘图。
二、 代码实现
这里主要用来练手,尤其是对于新手来说,可以更深刻的理解多线程、异步操作、对象引用、简单算法、数据结构等。
1、布局规划
真个布局分为以上6个区域,其中较为复杂的为1和6。
1区域当前采用塔型结构进行堆叠演示(堆叠规则可以任意调整),技术点在于如果点击的卡片的上方有其它卡片,则该卡片不能移动。
技术点:
1、图中每个卡片最多可被四个卡片同时覆盖,其中四个角、边缘、中间区域的卡片覆盖个数都有差别,设计算法校验时需要考虑全面。
2、各个层级的图片之间没有使用遮照进行处理,主要个人喜好。
6区域看起来就是消除三个相同数字的卡片。
技术分析:
1、1~5区域的卡片要能移动到6,最大个数为6
2、对卡片进行排序,由小到大
3、卡片消除处理。
2~4区域的卡片相对简单,只需要注意点击的是最后一张图片即可。
2、卡片生成
每次运行游戏,都会随机生成所有卡片,是否通关完全靠运气,没有对死局进行处理。
通过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<Integer> cardsList = new ArrayList<>();
/**卡片name存储*/
public static List<String> centerCardData = new ArrayList<>();
/**中间卡片*/
public static List<CardNode> centerCards = new ArrayList<>();
/**左边卡片*/
public static List<CardNode> leftCards1 = new ArrayList<>();
public static List<CardNode> leftCards2 = new ArrayList<>();
/**右边卡片*/
public static List<CardNode> rightCards1 = new ArrayList<>();
public static List<CardNode> 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<CardNode> 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();
}
}
3、卡片属性
主要定义卡片的存放位置、展示位置、区域类型等
/**
* @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;
}
}
4、操作面板
关于操作面板中内容操作的代码实现:
/**
* @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<CardNode> 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<CardNode> 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) {
}
}
5、游戏窗口
/**
* @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();
}
});
}
}
更多推荐
“羊了个羊”Java版本实现
发布评论