一、读懂题目!
一开始看题目我就看了很久,面对下面这张图解我完全看不懂,不知道这个游戏到底怎么进行,后来在win10自带的游戏里找到了这个游戏才知道是要做什么。(做一个项目之前必须得完完全全理解这个项目具体的要求是什么,不能摸棱两可)
(点击solitaire进去后选择经典单人游戏即可感受游戏)
游戏说明:将一副扑克牌去掉大小王,只剩下四个花色的1-13,将这些牌放进四种卡堆中,1、deck cards(牌库)2、discard card(遗弃堆)3、suitpiles(正序堆)4、takepiles(倒序堆)四个正序堆每个牌堆对应一种花色,且该堆必须从小到大牌放,即黑桃1在最下面,黑桃2在黑桃1上面直到黑桃13。七个倒序堆牌堆由13在最下面,1在最上面,且任意两张牌之间花色为红黑交替,在倒序堆间移动卡片只能移动连续放置的卡片。卡库为正序牌堆和倒序牌堆外剩余的牌,点击卡库可以将一张牌放在遗弃堆的最上面,遗弃堆只显示最上面一张牌。
(以上的四个牌堆对应我项目中的deck、waste、foundation、tableau)
二、设计思路
1.有关牌堆的设计
(1)枚举类Suit和Colour我们需要纸牌的四个花色和十三个大小,所以初始化了两个枚举类Suit和Colour,分别存放花色和大小,以便卡牌的初始化。
(2)Card类我们需要卡牌类Card,然后初始化每一张卡牌,Card类中有卡片图像、数值、花色是否正面朝上的属性,以及各个属性对应的get和set方法。
(3)Pile由于牌堆后进先出的,所以我使用了 stack来模拟了堆栈进行操作。Pile类有两个属性,一个是Pile堆在面板上的位置,一个是stack存储该牌堆的所有card。实现的方法有,获取最顶部卡牌,往牌堆顶添加一张卡牌,push一组卡牌,pop一组卡牌,删除顶端卡牌,判断牌堆是否为空等等方法。
(4)Deck卡库继承自Pile。初始化时就把所有卡片先放入Deck中,添加paintComponent方法(画出deck堆,若无牌则显示空白框,有牌则显示有牌的背面)。
(5)waste丢弃堆继承自Pile。添加paintComponent方法(画出waste堆,若无牌则显示空白框,有牌则只显示最上面一张牌的正面,其他牌被覆盖)。(6)Foundation正序堆堆花色堆继承自Pile,是收集已经有序的牌的地方。添加paintComponent方法(画出deck堆,若没有牌则显示花色底牌,有牌则只显示最上面一张牌的正面,其他牌被覆盖)、moveFromWaste方法(从丢弃堆移动到正序堆)、moveTo方法(从正序堆移动到倒序堆)、accepts方法(该卡片是否能放到正序堆)、intToSuit方法(花色和花色相应的数字是否匹配)
(7)Tableau倒序堆桌面堆是玩牌的主要区域,需要根据游戏的规则堆牌堆进行拖动,达到排序的效果,最终让牌全部放到花色堆。桌面堆继承自Pile,并且添加paintComponent方法(画出tableau堆,一沓牌只有最上面的牌显示为正面,其他的显示为背面,并且堆叠放置)、moveFromWaste方法(从丢弃堆移动到倒序堆)、accepts方法(该卡片是否能放到倒序堆)、两个moveTo方法(分别是移动到正序堆和其他倒序堆)、getClickedCard方法(获取选中的是倒序堆的哪些牌)
2. 有关界面的设计
(1)定义GameMoveListener类继承自MouseInputAdapter,其中重写mousePressed、mouseReleased方法,鼠标下压如果选中的是卡库、丢弃堆、或倒序堆的牌如果能放到对应的正序堆则直接放上去。若不能,则根据鼠标释放时的位置判断是否允许该次移动,最后重画各个变化了的组件。
(2)定义GamePanel,在其上添加监听器、各个牌堆类并放置好位置。
三、具体细节
(大家先看一下项目的文件分布)
cards包用来装卡片的图像图片
(命名格式非常重要!!!!)
//back001为卡片背面图片,bottom01为空白卡片轮廓图片,放在空白遗弃堆位置。fpBase为四个花色的背景图片,放在空白正序堆位置。
实现思路:
Pile类为牌堆的基类
其他四个牌堆继承Pile类,
Color、suit类为枚举类,存放颜色、花色信息
GameMoveListener实现移动卡片的监听器
GamePanel为创建游戏面板,添加入所有组件
PhotoMoving为一个移动卡片拖拽的实现,尝试了加在项目里面结果没成功(于是项目现在的卡片移动过程看不到,只能怪看到移动成功后的卡片位置变换)
上代码!!!
Suit.java
package solitaire;
public enum Suit {
Spades, Hearts, Clubs, Diamonds }//enum枚举类型这
//段代码实际上调用了Enum(String name, int ordinal):new Enum<EnumTest>("MON",0);
Colour.java
package solitaire;
public enum Colour {
Red, Black, Neither }
Card.java
package solitaire;
import java.awt.Image;
import javax.swing.ImageIcon;
public class Card {
public static String cardBackFilename = "back001", cardOutlineFilename = "bottom01",
fpBaseFilename = "fpBase0";
public static String directory = "cards", extension = ".gif";
private Image im;//每一张卡片的正面图片
private int value;//卡片数值
private String suit;//花色
private boolean faceUp; private Colour colour;
public Card(int value, Suit suit) {
//通过switch选择花色
this.value = value;
switch(suit) {
case Clubs:
this.suit = "c";
colour = Colour.Black;
break;
case Diamonds:
this.suit = "d";
colour = Colour.Red;
break;
case Spades:
this.suit = "s";
colour = Colour.Black;
break;
case Hearts:
this.suit = "h";
colour = Colour.Red;
break;
}
faceUp = false;
try {
ImageIcon ii = new
ImageIcon(getClass().getResource("/"+directory + cardFile(suit, value)));
im = ii.getImage();
}catch(Exception e) {
System.err.println("Error: " + e.getMessage()+"lalala");
}
}
private String cardFile(Suit s, int val) {
//返回卡片对应图片的文件名
char ch;
if (val < 1 || val > 13)
throw new IllegalArgumentException("Bad Card Number");
if(s == Suit.Clubs) {
ch = 'c';
}else if(s == Suit.Hearts) {
ch = 'h'; }else if(s == Suit.Spades) {
ch = 's'; }else if(s == Suit.Diamonds) {
ch = 'd'; } else throw new IllegalArgumentException("Bad Card Suit"); if(val < 10) return "/0" + val + ch + extension; else return "/" + val + ch + extension; } public Image getCardImage() {
return im; }
public boolean isFaceUp() {
//是否为正面
return faceUp; }
public Colour getColour() {
return colour; }
@Override
public String toString() {
return value + " of " + suit ; }
public static Image getFoundationBase(int suit) {
//获取牌的花色信息,,空牌
ImageIcon ii = new ImageIcon( Card.class.getResource("/"+directory + "/" + fpBaseFilename + suit + extension));
Image image = ii.getImage();
return image; }
public static Image getCardOutline() {
//获取空白的卡牌图像
ImageIcon ii = new ImageIcon(
Card.class.getResource("/"+directory + "/" + cardOutlineFilename + extension));
Image image = ii.getImage();
return image; }
public static Image getCardBack() {
//返回盖着的牌的图像
ImageIcon ii = new ImageIcon(Card.class.getResource("/"+directory + "/" + cardBackFilename + extension));
Image image = ii.getImage();
return image; }
public int getValue() {
return value; }
public String getSuit() {
return suit; }
public void showFace() {
faceUp = true; } }
Pile.java
package solitaire;
import java.util.EmptyStackException;
import java.util.Stack;
import javax.swing.JPanel;
public abstract class Pile extends JPanel {
//牌堆基类
//牌堆类
protected int x, y;//牌堆放置的位置
protected Stack<Card> cards;//用堆栈来存储牌堆里的卡片
public Pile(int x, int y) {
super.setLocation(x, y);//设置面板的位置
cards = new Stack<>()