在Project下创建好需要的四个文件夹,其中Resources文件夹是用来存放A-K和大小王正面纸牌资源,Textture文件夹用来存放带有Restart、END、Level 1、Level 2、Level3字样的纸牌反面图片,无字样纸牌反面图片和背景图片资源,注意的是将这些图片导入时,需要将它们的Texture Type属性改为Sprite 2D and UI。Script文件夹下包含两个c#脚本文件,一个是游戏的主入口脚本文件,另一个是控制纸牌翻转脚本文件。效果如下图所示:
图3-1项目创建和资源导入
创建Canvas画布,添加三个Panel节点(Panel节点相当于一张image图片),分别为关卡选择界面Panel_start、纸牌翻转界面Panel_card、游戏结束界面Panel_over。在Panel_start节点下添加三个Button按钮,分别对应游戏关卡一二三,然后将三张纸牌关卡背景图分别导入Button节点的image属性。效果如下图所示:
图3-2创建游戏UI
图3-3关卡按钮效果
:
public class GameMain : MonoBehaviour {
public Button btn_level1;//声明Button节点
public Button btn_level2;
public Button btn_level3;
public Transform panelStart;//声明Panel节点
public Transform panelCard;
public Transform panelOver;
void Start () { //分别对三个Button进行事件监听
btn_level1.onClick.AddListener (() => {
panelStart.gameObject.SetActive(false);
panelCard.gameObject.SetActive(true);
LoadLevelCard(2, 3);
});
btn_level2.onClick.AddListener (() => {
panelStart.gameObject.SetActive(false);
panelCard.gameObject.SetActive(true);
LoadLevelCard(2, 4);
});
btn_level3.onClick.AddListener (() => {
panelStart.gameObject.SetActive(false);
panelCard.gameObject.SetActive(true);
LoadLevelCard(2, 5);
});
}
在Panel_card节点下新建一个Panel节点并添加Grid Layout Group组件,并合理调整好padding、cell size、spacing等属性值,在Panel节点下添加image节点,将纸牌图片导入image,并调整好尺寸大小。
图3-4Grid Layout Group组件
通过在GameMain脚本文件中添加代码实现纸牌预制体效果,即每个关卡优美地展示出多少行多少列的纸牌,主要代码及注释和效果图如下:
viod Start(){ //向纸牌加载函数传参,第一个参数代表行数,第二个代表列数
LoadLevelCard(2, 3);
LoadLevelCard(2, 4);
LoadLevelCard(2, 5);
}
void LoadLevelCard(int row, int col)
{
Sprite[] sps = Resources.LoadAll<Sprite>(""); //加载所有卡牌图片
int totalCount = row * col / 2; //计算需要加载卡牌的数量
//计算随机加载卡牌的索引
List<Sprite> spsList = new List<Sprite>();
for (int i = 0; i < sps.Length; ++i) {
spsList.Add(sps[i]);
}
List<Sprite> needShowCardList = new List<Sprite>();
while (totalCount > 0) {
int radom = Random.Range(0, spsList.Count);
needShowCardList.Add (spsList [radom]);
needShowCardList.Add (spsList [radom]);
spsList.RemoveAt (radom);
totalCount--;
}
//显示纸牌到UI上
Transform contentRoot = panelCard.Find ("Panel");
GameObject itemTemplate = contentRoot.GetChild (0).gameObject;
//高等级通关后在玩低等级要销毁对象,且解除关联
for (int i = 1; i < contentRoot.childCount; ++i) {
GameObject itemTemp = contentRoot.GetChild (i).gameObject;
Sprite ss = itemTemp.transform.Find ("Image_front").GetComponent<Image> ().sprite;
Debug.Log (i + "," + ss.name);
itemTemp.transform.SetParent (null);
Destroy (itemTemp);
}
int normal_index = 0;
while(needShowCardList.Count > 0){
int index = Random.Range(0, needShowCardList.Count);
GameObject itemObject = null;
if (normal_index < contentRoot.childCount) {
itemObject = contentRoot.GetChild (normal_index).gameObject;
} else {
itemObject = GameObject.Instantiate<GameObject> (itemTemplate);
itemObject.transform.SetParent (contentRoot, false);
}
itemObject.transform.Find("Image_front").GetComponent<Image> ().sprite = needShowCardList [index];
CardFlipAnimation cardAnimal = itemObject.GetComponent<CardFlipAnimation> ();
cardAnimal.SetDefaultState ();
needShowCardList.RemoveAt (index);
++normal_index;
}
GridLayoutGroup glg = contentRoot.GetComponent<GridLayoutGroup> ();
float panelWidth = col * glg.cellSize.x + glg.padding.left + glg.padding.right + (col - 1) * glg.spacing.x;
float panelHeight = row * glg.cellSize.y + glg.padding.top + glg.padding.bottom + (row - 1) * glg.spacing.y;
contentRoot.GetComponent<RectTransform> ().sizeDelta = new Vector2 (panelWidth, panelHeight);
}
图3-5 Level 1
图3-6 Level 2
图3-7 Level 3
在纸牌翻转界面Panel_card下添加Panel节点,然后在Panel节点下添加新节点并命名为Card_item,其中包括纸牌正反面即Image_front和Image_back,在关掉Image_front和Image_back的Raycast Target参数(控制一张图片是否响应鼠标点击事件)后,将控制纸牌翻转脚本文件CardFilpAnimation导入Card_item中,然后在CardFilpAnimation中添加实现纸牌翻转功能的代码。
主要代码及注释如下:
IEnumerator FlipCardToFront() //翻转纸牌背面从0度到90度
{
cardFront.gameObject.SetActive (false);
cardBack.gameObject.SetActive (true);
cardBack.rotation = Quaternion.identity;
while (cardBack.rotation.eulerAngles.y < 90) {
cardBack.rotation *= Quaternion.Euler (0, Time.deltaTime*90*(1f / flip_duration), 0);
if (cardBack.rotation.eulerAngles.y > 90) {
cardBack.rotation = Quaternion.Euler (0, 90, 0);
}
yield return new WaitForFixedUpdate ();
}
//接着翻转正面从90度到0度
cardFront.gameObject.SetActive (true);
cardBack.gameObject.SetActive (false);
cardFront.rotation = Quaternion.Euler (0, 90, 0);
while (cardFront.rotation.eulerAngles.y > 0) {
cardFront.rotation *= Quaternion.Euler (0, -Time.deltaTime*90*(1f / flip_duration), 0);
if (cardFront.rotation.eulerAngles.y > 90) {
cardFront.rotation = Quaternion.Euler (0, 0, 0);
}
yield return new WaitForFixedUpdate ();
}
isFront = true;//标记纸牌在正面
Camera.main.gameObject.GetComponent<GameMain> ().checkGameOver ();
}
IEnumerator FlipCardToBack()//翻转纸牌正面从0度到90度
{
cardFront.gameObject.SetActive (true);
cardBack.gameObject.SetActive (false);
cardFront.rotation = Quaternion.identity;
while (cardFront.rotation.eulerAngles.y < 90) {
cardFront.rotation *= Quaternion.Euler (0, Time.deltaTime*90*(1f / flip_duration), 0);
if (cardFront.rotation.eulerAngles.y > 90) {
cardFront.rotation = Quaternion.Euler (0, 90, 0);
}
yield return new WaitForFixedUpdate ();
}
//接着翻转背面从90度到0度
cardFront.gameObject.SetActive (false);
cardBack.gameObject.SetActive (true);
cardBack.rotation = Quaternion.Euler (0, 90, 0);
while (cardBack.rotation.eulerAngles.y > 0) {
cardBack.rotation *= Quaternion.Euler (0, -Time.deltaTime*90*(1f / flip_duration), 0);
if (cardBack.rotation.eulerAngles.y > 90) {
cardBack.rotation = Quaternion.Euler (0, 0, 0);
}
yield return new WaitForFixedUpdate ();
}
isFront = false;//标记纸牌在反面
}
然后在GameMain脚本文件中改变卡牌初始化的方法,则会随机翻转出不同纸牌,实现代码如下:
itemObject.transform.Find("Image_front").GetComponent<Image> ().sprite = needShowCardList [index];
a先判断出当前翻转纸牌的数量;
b如果小于两张继续翻转,如果等于两张,接着判断两张纸牌的数字或字母是否相等,若相等进行消除,若不等两张纸牌返回背面;
c直到所有纸牌被消除完,游戏胜利。
对于a、b过程,在GameMain脚本文件中添加纸牌消除与否的代码如下:
//通过全局搜索找到所有纸牌
CardFlipAnimation[] allCards = GameObject.FindObjectsOfType<CardFlipAnimation> ();
if (allCards != null && allCards.Length > 0) {
List<CardFlipAnimation> cardInFront = new List<CardFlipAnimation> ();//用来存放正面纸牌的数量和内容
for (int i = 0; i < allCards.Length; ++i) {
CardFlipAnimation cardTemp = allCards [i];
if (cardTemp.isFront && !cardTemp.isOver) {
cardInFront.Add (cardTemp);
}
if (cardInFront.Count >= 2) {
//当正面纸牌数量大于等于2,获取纸牌的名字并判断是否相同
string cardImageName1 = cardInFront[0].GetCardImageName ();
string cardImageName2 = cardInFront[1].GetCardImageName ();
if (cardImageName1 == cardImageName2) {
cardInFront [0].MatchSuccess ();
cardInFront [1].MatchSuccess ();
}
else {
cardInFront [0].MatchFailed ();
cardInFront [1].MatchFailed ();
}
并在控制纸牌翻转CardFilpAnimation脚本文件中调用和实现上述方法:
Camera.main.gameObject.GetComponent<GameMain> ().checkGameOver ();
public string GetCardImageName()
{
return cardFront.GetComponent<Image> ().sprite.name;
}
public void MatchSuccess()//匹配成功,将两张纸牌的正反面均关闭掉
{
isOver = true;
cardFront.gameObject.SetActive (false);
cardBack.gameObject.SetActive (false);
}
public void MatchFailed(){ //匹配失败,将两张纸牌翻转回背面
StartCoroutine (FlipCardToBack ());
}
对于c过程,判断所有纸牌是否消除完毕,代码如下:
public void ToGameOverPage()//如果所有纸牌消除完,仅保留panelOver节点
{
panelStart.gameObject.SetActive(false);
panelCard.gameObject.SetActive(false);
panelOver.gameObject.SetActive(true);
}
allCards = GameObject.FindObjectsOfType<CardFlipAnimation> ();
bool AllOver = true;
for(int j = 0; j < allCards.Length;++j){
if (!allCards [j].isOver) {
AllOver = false;
}
}
if (AllOver) {
ToGameOverPage ();
}
break;
在Panel_over节点下添加end和restart节点,并分别将带END和Restart字样的纸牌背面图片导入,然后编写脚本文件实现跳转界面的功能,效果图和主要代码如下:
图3-8 游戏胜利后的跳转界面
btn_over.onClick.AddListener (() => {//END选择按钮,离开游戏
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
Application.Quit();
#endif
});
btn_restart.onClick.AddListener (() => {
//restart按钮,重新开始游戏,仅打开panelStart节点
panelStart.gameObject.SetActive(true);
panelCard.gameObject.SetActive(false);
panelOver.gameObject.SetActive(false);
});
public void SetDefaultState()
{
isFront = false;
isOver = false;
if (cardFront != null) {
cardFront.gameObject.SetActive (false);
cardFront.rotation = Quaternion.identity;
}
if (cardBack != null) {
cardBack.gameObject.SetActive (true);
cardBack.rotation = Quaternion.identity;
}
}
开始每一关卡游戏的时候,可以让所有的纸牌显示一段时间,让玩家瞬时记忆一会,以增加游戏效率和可操作性。
增加翻拍次数改进功能可防止玩家胡乱点击,已达到点击次数最少而获胜的效果。
纸牌翻转、纸牌消除和游戏胜利时刻都可以增添音效,以增加游戏的趣味性。
纸牌消除游戏压缩包:(Unity软件打开)
链接:https://pan.baidu.com/s/1FEzlxGv2dwJBtHZ-gjC_9A
提取码:angu