5小时复刻《羊了个羊》,Java代码已开源,还有108套皮肤
抽了5个小时用Java实现了一个桌面版本,效果如下:羊了个羊开发现场实现步骤先画界面,给界面添加上逻辑。第一步:画界面——界面分区把界面分成叠卡区、翻牌区、验卡区三个部分,然后一个区域的话。第一步:画叠卡区——实现思路叠卡区又分成三步来实现:生成卡片:生成所需要卡片,不放到一个卡片集合中,注意顺序要打乱摆放卡片:把生成的卡片摆放对应区域、对应层次错落有致:
简介
羊了个羊游戏爆火,就是太难玩了,我玩了几十次,玩不过去,很纠结,作为技术人员的我,忍不了,就抽了5个小时用Java实现了一个桌面版本,效果如下:
测试现场
羊了个羊开发现场
实现思路+代码实现
实现步骤:先画界面,给界面添加上逻辑。
第一步:画界面——界面分区
把界面分成叠卡区、翻牌区、验卡区三个部分,然后一个区域的话。
第一步:画叠卡区——实现思路
叠卡区又分成三步来实现:
-
生成卡片:生成所需要卡片,不放到一个卡片集合中,注意顺序要打乱
-
摆放卡片:把生成的卡片摆放对应区域、对应层次
-
错落有致:让上下层的卡片有错落感
第一步:画叠卡区——生成卡片思路
-
取一张图片按照下面3部生成
-
取第二张图片重复上面过程
-
最后把所有图案都按照上述过程实现一遍,即可得到一个随机乱序的卡片集合
以上思路实现的参考代码如下:
int maxLevel = 10;//多少层
int maxWidth = 6;// 跨度个数
int maxHeight = 5;// 最大宽度
int maxFlop = 60;//翻牌数量;
Random random = new Random();
// 如果需要随机皮肤,修改为true即可
List<String> list = ReadResourceUtil.readSkin(true);
int typeSize = list.size();// 多少种类
System.out.println("种类:"+typeSize);
int groupNumber = (int) Math.ceil((maxLevel*maxWidth*maxHeight+maxFlop)/(3f*typeSize));// 求得每种种类的个数
System.out.println("每种组数:"+groupNumber);
int groupCount = groupNumber*3;
System.out.println("每种总数:"+groupCount);
System.out.println("共计数量:"+(typeSize*groupCount+maxFlop));
// 绘制卡槽
int initX = 100;
int initY = 50;
CardSlotCantainer cardSlotCantainer = new CardSlotCantainer(imageCantainer,initX+((maxWidth-7)*FruitObject.defaultWidht)/2,+initY+FruitObject.defaultHeight*(maxHeight+2));
// 随机生成卡片集合:注意打乱顺序
List<FruitObject> objects = new ArrayList<>();
for (String temp : list) {
try {
BufferedImage bufferedImage = ImageIO.read(ReadResourceUtil.getUri("/"+temp));
int count = groupCount+(maxFlop>0?random.nextInt(maxFlop):0);
for (int i = 0; i < count; i++) {
int size = objects.size()-1;
Fruits fruits = new Fruits(imageCantainer,bufferedImage,temp);
fruits.setPreferredSize(new Dimension(100, 100));
int index = 0;
if(size>10) {
index = random.nextInt(size);
}
objects.add(index, new FruitObject(cardSlotCantainer,fruits, 0, 0, 0));
maxFlop--;
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
System.out.println("实际数量:"+objects.size());
第一步:画叠卡区——摆放卡片思路
-
直接从上一步集合中取出卡片依此摆放到对应层的卡片位置
-
当一层摆放完成后,循环摆放下一层,以此类推
以上思路实现的参考代码如下:
// 给每个对象设置坐标
int index = 0;
for (int i = 0; i < maxLevel; i++) {
for (int x = 0; x < maxWidth; x++) {
for (int y = 0; y < maxHeight; y++) {
FruitObject fruitObject = objects.get(index++);
fruitObject.setX(x);
fruitObject.setY(y);
fruitObject.setLevel(i);
fruitObject.show(imageCantainer,initX-FruitObject.defaultWidht/4,initY-FruitObject.defaultHeight/4);
}
}
}
System.out.println("重叠数量:"+index);
第一步:画叠卡区——卡片错落感实现思路
-
给上层卡片的地点x、y值增加随机值,即可实现层与层之间的卡片错落感
以上思路实现的参考代码如下:
/**
* 添加到叠卡区
* @param imageCantainer
* @param initX
* @param initY
*/
public void show(ImageCantainer imageCantainer, int initX, int initY) {
this.imageCantainer = imageCantainer;
// 随机生成开始坐标偏移量,实现上下层错落有致的视觉感
boolean ranDomWidth = RANDOM.nextInt(10)%2==0;
boolean ranDomHeight = RANDOM.nextInt(10)%2==0;
int pointX = initX + x*defaultWidht+(ranDomWidth?defaultWidht/2:0);
int pointY = initY + y*defaultHeight+(ranDomHeight?defaultHeight/2:0);
// 设置卡片显示在背景面板中位置
fruits.setBounds(pointX,pointY,defaultWidht,defaultHeight);
// 记录卡片的空间信息
SpaceManager.rectangle(this);
imageCantainer.add(fruits,0);
addClick();
}
第一步:画翻牌区——实现思路
翻牌区实现的思路和叠卡区类似,少一步错落有致的步骤。因此大家参考上述的思路理解即可。
以上思路实现的参考代码如下:
/**
* 添加到翻牌区
* @param imageCantainer
* @param initX
* @param initY
* @param offset
* @param isLeft
*/
public void showFold(ImageCantainer imageCantainer, int initX, int initY, int offset,boolean isLeft) {
this.imageCantainer = imageCantainer;
// 随机生成开始坐标偏移量,实现上下层错落有致的视觉感
int pointX = initX + x*defaultWidht+offset;
int pointY = initY + y*defaultHeight-defaultHeight/4;
if(isLeft){
this.leftFold = true;
}else{
this.rightFold= true;
}
// 设置卡片显示在背景面板中位置
fruits.setBounds(pointX,pointY,defaultWidht,defaultHeight);
// 记录卡片的空间信息
SpaceManager.rectangle(this);
imageCantainer.add(fruits,0);
addClick();
}
第一步:画验卡区——实现思路
验卡区可以用两个圆角长方形直接重叠实现即可。
以上思路实现的参考代码如下:
@Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
BasicStroke basicStroke = new BasicStroke(borderSize);
g2d.setStroke(basicStroke);
// 绘制第1层底色
g2d.setColor(bgColor);
g2d.fillRoundRect(0, 0, (int) (getSize().width - borderSize), (int) (getSize().height - borderSize),arc,arc);
// 绘制第2层底色
g2d.setColor(borderColor);
g2d.fillRoundRect(borderSize, borderSize, (int) (getSize().width - 1-borderSize*3), (int) (getSize().height - 1-borderSize*3),arc,arc);
super.paintComponent(g);
}
第二步:实现界面逻辑控制——实现思路
每个去都有自己的界面控制逻辑,如下图,具体内容就可以参考代码了
以上思路实现的参考代码如下:
public void addSlot(FruitObject object){
if(isOver){
return;
}
slot.add(object);
// 验卡区的卡片删除点击事件
object.removeImageCantainer();
MouseListener[] mouseListeners = object.getFruits().getMouseListeners();
if(mouseListeners!=null){
for (MouseListener mouseListener : mouseListeners) {
object.getFruits().removeMouseListener(mouseListener);
}
}
// 排序验卡区中的图片
slot.sort(Comparator.comparing(FruitObject::getImageName));
// 3张图片的判断,如果有直接消除,思路是:分组后看每组数量是否超过3张如果超过则消除
Map<String, List<FruitObject>> map = slot.stream().collect(Collectors.groupingBy(FruitObject::getImageName));
Set<String> keys = map.keySet();
for (String key : keys) {
List<FruitObject> objects = map.get(key);
if(objects.size()==3){
if(audioClip!=null){
audioClip.play();
}
// 消除的元素直接从集合中删除
for (FruitObject fruitObject : objects) {
fruitObject.removeCardSlotCantainer();
}
slot.removeAll(objects);
}
}
// 新添加的卡片,显示到验卡区
redraw();
// 判断游戏是否结束
if(slot.size()==solt){
isOver = true;
failClip.play();
JOptionPane.showMessageDialog(this.getParent(), "Game Over:槽满了","Tip",JOptionPane.ERROR_MESSAGE);
}
}
更多推荐
所有评论(0)