首先在场景中创建Canvas,加几个Button
为了排版方便,用了GridLayout
每个Button的结构是这样的,你要为Button里面加四个Image,我这样弄是为了省事,少写脚本
这四个Image的名字也不能改,因为我后续是按照Image的名字进行检索的,也是为了省事……
在Fade上记得加个Button组件
简单说下这四个图片的作用:首先这是个翻牌游戏,游戏规则就是当玩家翻到两张一样的牌时计数板加一,计数到6个时游戏完成并加一分。这里面的Icon是放牌面的,就是让玩家知道这两张牌是一样的还是不一样的,起提示作用。Fade是牌背,玩家刚开始看到的就是12张牌背,点击牌背,牌背隐藏,出现牌面。True和False也起提示作用,当两张牌一样时,显示绿色的True,说明玩家选对了,不一样就显示红色的False。
然后你需要调整一下这四张Image的状态。
完事了把它复制12张,当然你也可以复制很多,保证是偶数就行了。
新建脚本CardGame,挂到Canvas或随便一个地方
新建三个Text,一个用于显示倒计时,一个用于显示计数,一个用于显示得分
新建一个Button,用于开始游戏
在网上找几张卡面,拖到Unity里(如果是3D项目,注意转换下格式),为了省事,我没新建文件夹,大家做游戏一定要注意项目的整洁哦
然后开始写CardGame脚本了
脚本的思路都在注释了,也没啥好说的,写好后在外面把找的图片赋给Sprite数组,按这一套下来应该就可以运行了,点击开始游戏即可开始翻牌
(之前的脚本忘记把引用加上,其实用vs自动纠错都可以解决的,现在的脚本已经不会报错了。)
(还有像我这种一个脚本写完翻牌游戏的,以后做项目或者做游戏尽量不要这样,这样写起来是省事,但是不利于项目维护。就这个游戏来说,起码应该写三个脚本,一个卡牌Model类,一个游戏控制类,一个UI显示类。)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class CardGame : MonoBehaviour
{
/// <summary>
/// 最大时间,超过该时间游戏失败
/// </summary>
public float maxTime=15;
/// <summary>
/// 用于计时
/// </summary>
private float m_time;
/// <summary>
/// 当前翻对了多少组牌
/// </summary>
private int Count;
/// <summary>
/// 需要翻对多少组牌才能胜利,简单来说就是所有牌数量除2
/// </summary>
private int maxCount;
/// <summary>
/// 得分
/// </summary>
private int Score;
/// <summary>
/// 储存牌面图片,注意你用了多少组牌,就得赋多少个牌面,比如教程里用了12个牌,就需要赋值6个Sprite
/// </summary>
public List<Sprite> sprites = new List<Sprite>();
/// <summary>
/// 储存所有牌的列表,
/// </summary>
private List<Transform> cardList = new List<Transform>();
/// <summary>
/// 用于初始化牌列表
/// </summary>
public Transform cardTrans;
/// <summary>
/// 编号列表,用于给每张卡进行编号
/// </summary>
private List<int> indexList = new List<int>();
/// <summary>
/// 是否开始游戏
/// </summary>
private bool isStart;
/// <summary>
/// 用于等待,主要是给玩家一点看牌的时间
/// </summary>
private bool isWaiting;
/// <summary>
/// 储存翻到的第一张牌
/// </summary>
private Transform FirstCard;
/// <summary>
/// 储存翻到的第二张牌
/// </summary>
private Transform NextCard;
/// <summary>
/// 开始游戏的按钮
/// </summary>
public Button startBtn;
/// <summary>
/// 倒计时的Text
/// </summary>
public Text timeText;
//[SerializeField]
//private Text timeText;
//你也可以尝试这种写法,不过我个人觉得这种写法多此一举
/// <summary>
/// 计数Text
/// </summary>
public Text countText;
/// <summary>
/// 计分Text
/// </summary>
public Text scoreText;
protected void Awake()
{
//初始化cardList
for (int i = 0; i < cardTrans.childCount; i++)
{
//遍历cardTrans,把他的每个子物体都加到list里
cardList.Add(cardTrans.GetChild(i));
int id = i;
//给卡背添加点击的监听,每张卡都绑定了一个Id
cardTrans.GetChild(i).Find("Fade").GetComponent<Button>().onClick.AddListener(() =>
{
CardClick(id);
});
}
for (int i = 0; i < cardList.Count / 2; i++)
{
for (int j = 0; j < 2; j++)
{
indexList.Add(i);//初始化编号列表,这里的结果是[0,0,1,1,2,2,3,3,4,4,5,5]
}
}
maxCount = cardList.Count / 2;
startBtn.onClick.AddListener(() =>
{
CardStart();
});
}
protected void Update()
{
//UI显示,我这里图省事写在了Update里,以后尽量别写Update里,可以用事件,或者把值封装下
timeText.text = m_time.ToString("f2") + "秒";
countText.text = "计数:" + Count;
scoreText.text = "得分:"+Score;
if (!isStart) //游戏没有进行中,不计时
return;
m_time -= Time.deltaTime;
if (m_time <= 0)//时间用完了还没结束游戏,则游戏失败
CardResult(false);
}
/// <summary>
/// 开始游戏
/// </summary>
private void CardStart()
{
if (isStart)//如果游戏在进行中,则点击无效
return;
m_time = maxTime;
Count = 0;//计数归0
indexList = RandomList(indexList);//把编号随机排序,相当于洗牌
for (int i = 0; i < cardList.Count; i++)
{
cardList[i].name = indexList[i] + "";//我这里直接用卡名作为判断依据,每两张卡如果卡名一样则为一组
cardList[i].Find("Icon").GetComponent<Image>().sprite = sprites[indexList[i]];
cardList[i].Find("Fade").gameObject.SetActive(true);
cardList[i].Find("True").gameObject.SetActive(false);
cardList[i].Find("False").gameObject.SetActive(false);
}
isStart = true;
}
/// <summary>
/// List随机排序
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="list"></param>
/// <returns></returns>
public List<T> RandomList<T>(List<T> list)
{
var random = new System.Random();
var newList = new List<T>();
foreach (var item in list)
{
newList.Insert(random.Next(newList.Count), item);
}
return newList;
}
/// <summary>
/// 点击卡背
/// </summary>
/// <param name="id"></param>
private void CardClick(int id)
{
if (!isStart) //游戏没有进行中,不能点
return;
if (isWaiting)
return;
Transform t = cardList[id];//找到这张卡
t.Find("Fade").gameObject.SetActive(false);//隐藏卡背
if (FirstCard == null) //翻第一张
FirstCard = t;
else if (NextCard == null)//翻第二张
{
NextCard = t;
bool isSame;
if (FirstCard.name == NextCard.name)//卡名相同,所以这两张卡是一样的,计数加1
{
FirstCard.Find("True").gameObject.SetActive(true);//用于提示玩家,你翻对了
NextCard.Find("True").gameObject.SetActive(true);
isSame = true;
Count++;
}
else//我这里为了易懂,写的比较啰嗦
{
FirstCard.Find("False").gameObject.SetActive(true);//用于提示玩家,你翻错了
NextCard.Find("False").gameObject.SetActive(true);
isSame = false;
}
if (Count >= maxCount)//如果都翻对了,游戏胜利
{
CardResult(true);
}
else
{
StartCoroutine(WaitFlip(isSame));//还没翻完,开启一个等待的协程
}
}
}
/// <summary>
/// 等待牌翻过来
/// </summary>
/// <param name="a">这两张牌是否一致</param>
/// <returns></returns>
IEnumerator WaitFlip(bool a)
{
isWaiting = true;
yield return new WaitForSeconds(0.35f);//等待0.35秒
//把用于提示的Image隐藏掉,这样看起来很啰嗦,其实应该给牌单独写个类,大家可以自己优化下
FirstCard.Find("True").gameObject.SetActive(false);
FirstCard.Find("False").gameObject.SetActive(false);
NextCard.Find("True").gameObject.SetActive(false);
NextCard.Find("False").gameObject.SetActive(false);
if (!a)//没翻对,这两卡翻回去
{
FirstCard.Find("Fade").gameObject.SetActive(true);
NextCard.Find("Fade").gameObject.SetActive(true);
}
//小重置一下,开启下一轮判断
FirstCard = null;
NextCard = null;
isWaiting = false;
}
/// <summary>
/// 游戏结果
/// </summary>
/// <param name="isSuccess">是否成果</param>
private void CardResult(bool isSuccess)
{
StopAllCoroutines();//停止所有运行中的协程
if (isSuccess)//游戏胜利,加一分
{
Score++;
}
else//游戏失败,减一分
{
Score--;
}
//重置变量
FirstCard = null;
NextCard = null;
isStart = false;
m_time = 0;
Count = 0;
}
}
效果预览
源文件链接
链接:https://pan.baidu.com/s/1KKIojoOxntCfVGBOJpQ9gg
提取码:jr7r