Unity3D_(游戏)卡牌04_游戏界面

 

 

 

启动屏界面、主菜单界面、选关界面、游戏界面

卡牌01_启动屏界面  传送门

卡牌02_主菜单界面  传送门

卡牌03_选关界面   传送门

卡牌04_游戏界面     传送门

 选关界面效果

 

   (源代码在文章最下面,本篇有点长 (´・_・`) )

实现过程

选关界面

  添加Canvas画布自适应屏幕分辨率、设置背景图片

  添加显示关卡文本

  两个文本Text,LevelLabel文本显示固定关卡文字,leveltxt文本作为LevelLabel文本的子对象,动态显示关卡数

  创建三个按钮控件(主菜单、声音、游戏说明)

  

  使用Panel控件创建游戏区域

 

卡牌预设体

  GameArea游戏区域下创建一个空物体对象,改名为Card,在Card下新建一个Image对象,作为卡牌背景

  每一个卡牌具有三种状态:未翻转状态、翻转开进行匹配状态、翻转开匹配完成状态

  卡牌未翻转状态bg(Image):

  翻转开进行匹配状态icon(Image)

  翻转开匹配完成状态mask(Image)

  将卡牌设置为预设体,绑定Card.cs脚本(绑定完Apply一下)

制作棋盘

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//用来管理游戏运行状态资源、整体性

public class GameManager : MonoBehaviour {
    public GameObject carPreb;
    private LevelInfo levelInfo;    //本关卡数据信息
    int levelId;

    private void Awake()
    {
        levelInfo = DataMgr.Instance().levelInfo;
        levelId = DataMgr.Instance().levelId;
    }


    // Use this for initialization
    void Start () {
        InitBoard();
    }
    
    // Update is called once per frame
    void Update () {
        
    }

    //初始化棋盘
    void InitBoard()
    {
        //求游戏区域的宽高
        GameObject gameAreaObj = GameObject.Find("GameArea");
        RectTransform rectTrans = gameAreaObj.GetComponent<RectTransform>();
        float gameWidth = rectTrans.rect.width;
        float gameHeigh = rectTrans.rect.height;
        //获取关卡信息
        int row = levelInfo.row;
        int col = levelInfo.col;
        //根据关卡信息的行列信息,初始化位置
        float spacingW = gameWidth / col / 10;
        float spacingH = gameHeigh / row / 10;
        float cellW = (gameWidth - spacingW * (col + 1)) / col;
        float cellH = (gameHeigh - spacingH * (row + 1)) / row;
        float cellSize = Mathf.Min(cellW,cellH);        //最终求出正方形卡牌尺寸

        //求出水平和垂直方向实际空格间隙
        float spacingX = (gameWidth - cellSize * col) / (col + 1);
        float spacingY = (gameHeigh - cellSize * row) / (row + 1);

        int count = row * col;

        //创建所有卡牌
        for(int i=0;i<count;i++)
        {
            int row2 = i / col;
            int col2 = i % col;
            GameObject cardObj = Instantiate(carPreb,rectTrans);    //通过预制体创建卡牌对象
            cardObj.name = "Card" + i.ToString();
            RectTransform cardTrans = cardObj.GetComponent<RectTransform>();
            cardTrans.anchoredPosition = new Vector2(spacingX + (spacingX + cellSize) * col2, -spacingY - (spacingY + cellSize) * row2);
            cardTrans.sizeDelta = new Vector2(cellSize, cellSize);
        }

    }
}
GameManager.cs

  创建GameManager(GameObject),挂载游戏管理器脚本

  游戏管理器中添加卡牌预设体

public GameObject carPreb;

  游戏管理器获得数据管理器中的关卡数据和关卡Id

    private void Awake()
    {
        levelInfo = DataMgr.Instance().levelInfo;
        levelId = DataMgr.Instance().levelId;
    }

  初始化棋盘

void InitBoard()
    {
        //求游戏区域的宽高
        GameObject gameAreaObj = GameObject.Find("GameArea");
        RectTransform rectTrans = gameAreaObj.GetComponent<RectTransform>();
        float gameWidth = rectTrans.rect.width;
        float gameHeigh = rectTrans.rect.height;
        //获取关卡信息
        int row = levelInfo.row;
        int col = levelInfo.col;
        //根据关卡信息的行列信息,初始化位置
        float spacingW = gameWidth / col / 10;
        float spacingH = gameHeigh / row / 10;
        float cellW = (gameWidth - spacingW * (col + 1)) / col;
        float cellH = (gameHeigh - spacingH * (row + 1)) / row;
        float cellSize = Mathf.Min(cellW,cellH);        //最终求出正方形卡牌尺寸

        //求出水平和垂直方向实际空格间隙
        float spacingX = (gameWidth - cellSize * col) / (col + 1);
        float spacingY = (gameHeigh - cellSize * row) / (row + 1);

        int count = row * col;

        //创建所有卡牌
        for(int i=0;i<count;i++)
        {
            int row2 = i / col;
            int col2 = i % col;
            GameObject cardObj = Instantiate(carPreb,rectTrans);    //通过预制体创建卡牌对象
            cardObj.name = "Card" + i.ToString();
            RectTransform cardTrans = cardObj.GetComponent<RectTransform>();
            cardTrans.anchoredPosition = new Vector2(spacingX + (spacingX + cellSize) * col2, -spacingY - (spacingY + cellSize) * row2);
            cardTrans.sizeDelta = new Vector2(cellSize, cellSize);
        }

    }

卡牌数据关联

  Card.cs中初始化卡牌三种状态,并在UI上关联卡牌的状态

public class Card : MonoBehaviour {

    public GameObject bgObj;
    public Image iconImg;
    public GameObject maskObj;

    // Use this for initialization
    void Start () {
        
    }
    
    // Update is called once per frame
    void Update () {
        
    }
}

  根据关卡定义中图片素材个数,初始化一个卡牌游戏索引下标的列表

        List<int> src_ids = new List<int>(levelInfo.sprites.Length);
        for(int i=0;i<src_ids.Count;i++)
        {
            src_ids.Add(i);
        }

  从src_ids的下标集合中,随机关卡中定义的count数出来

        int[] rand_ids = new int[levelInfo.count];  //卡牌随机的素材下标集合
        for(int i=0;i<levelInfo.count;i++)
        {
            int idx = Random.Range(0,src_ids.Count);
            rand_ids[i] = src_ids[idx];
            src_ids.Remove(idx);        //删除随机过的素材下标,下次循环从剩余的集合中随机
        }

  初始化卡牌id数组,我们卡牌id,可以用数组下标来表示,并且要保证卡牌成对出现

 List<int> cardIds = new List<int>(row * col);
        for(int i = 0; i < row * col / 2; i++)
        {
            if(i<levelInfo.count)
            {
                cardIds.Add(rand_ids[i]);
                cardIds.Add(rand_ids[i]);
            }
            else
            {
                int idx = Random.Range(0, rand_ids.Length);
                cardIds.Add(idx);
                cardIds.Add(idx);
            }
        }

  对所有卡牌下标集合进行随机打乱

  for(int i=0;i<row*col;i++)
        {
            int idx = Random.Range(0,cardIds.Count);
            int temp = cardIds[i];        //随机一张牌,跟i对调,进而打乱顺序
            cardIds[i] = cardIds[idx];
            cardIds[idx] = temp;
        }

  Card.cs脚本中初始化卡牌

    public void Init(int id,Sprite spr,GameManager manager)
    {
        this.id = id;
        gameMgr = manager;
        //根据卡牌id,设置对应的图片素材
        iconImg.sprite = spr;
    }

卡牌状态的实现

  点击游戏卡牌三种状态,游戏开始停留5s时间,卡牌全部翻转过去,点击卡牌,卡牌翻转过来

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class Card : MonoBehaviour,IPointerClickHandler {

    public GameObject bgObj;
    public Image iconImg;
    public GameObject maskObj;

    public enum STATE
    {
        Init,       //进入游戏时,展示几秒钟
        Closed,     //卡牌扣下状态
        Opened,     //点击一张卡牌时,翻开状态
        OK,        //匹配成功状态
        Fail        //匹配失败状态,短暂展示1秒钟,自动切换到扣下状态
    }

    int id;
    GameManager gameMgr;
    STATE m_state;
    float _timer;   

    // Use this for initialization
    void Start () {
        SwithState(STATE.Init);

    }
    
    // Update is called once per frame
    void Update () {
        _timer += Time.deltaTime;

        switch (m_state)
        {
            case STATE.Init:
                {
                    if (_timer > 5)
                    {
                        SwithState(STATE.Closed);
                    }
                }
                break;
            case STATE.Closed:
                break;
            case STATE.Opened:
                break;
            case STATE.OK:
                break;
            case STATE.Fail:
                {
                    if (_timer > 1)
                    {
                        SwithState(STATE.Closed);
                    }
                }
                break;
            default:
                break;
        }
    }

    public void Init(int id,Sprite spr,GameManager manager)
    {
        this.id = id;
        gameMgr = manager;
        //根据卡牌id,设置对应的图片素材
        iconImg.sprite = spr;
    }


    //卡牌点击事件,系统自动调用
    public void OnPointerClick(PointerEventData eventData)
    {
        //卡牌切换到opened状态
        if (m_state == STATE.Closed)
        {
            SwithState(STATE.Opened);
        }
    }

    //卡牌状态切换
    void SwithState(STATE state)
    {
        _timer = 0;
        m_state = state;
        switch (state)
        {
            case STATE.Init:
                iconImg.gameObject.SetActive(true);
                maskObj.SetActive(false);
                break;
            case STATE.Closed:
                iconImg.gameObject.SetActive(false);
                break;
            case STATE.Opened:
                iconImg.gameObject.SetActive(true);
                break;
            case STATE.OK:
                maskObj.SetActive(true);
                break;
            case STATE.Fail:
                break;
            default:
                break;
        }
    }
}
Card.cs

  枚举卡牌状态

    public enum STATE
    {
        Init,       //进入游戏时,展示几秒钟
        Closed,     //卡牌扣下状态
        Opened,     //点击一张卡牌时,翻开状态
        OK,        //匹配成功状态
        Fail        //匹配失败状态,短暂展示1秒钟,自动切换到扣下状态
    }

  管理卡牌状态切换

void SwithState(STATE state)
    {
        _timer = 0;
        m_state = state;
        switch (state)
        {
            case STATE.Init:
                iconImg.gameObject.SetActive(true);
                maskObj.SetActive(false);
                break;
            case STATE.Closed:
                iconImg.gameObject.SetActive(false);
                break;
            case STATE.Opened:
                iconImg.gameObject.SetActive(true);
                break;
            case STATE.OK:
                maskObj.SetActive(true);
                break;
            case STATE.Fail:
                break;
            default:
                break;
        }
    }

  卡牌点击事件,系统自动调用

public void OnPointerClick(PointerEventData eventData)
    {
        //卡牌切换到opened状态
        if (m_state == STATE.Closed)
        {
            SwithState(STATE.Opened);
        }
    }
    void Start () {
        SwithState(STATE.Init);

    }
    
    // Update is called once per frame
    void Update () {
        _timer += Time.deltaTime;

        switch (m_state)
        {
            case STATE.Init:
                {
                    if (_timer > 5)
                    {
                        SwithState(STATE.Closed);
                    }
                }
                break;
            case STATE.Closed:
                break;
            case STATE.Opened:
                break;
            case STATE.OK:
                break;
            case STATE.Fail:
                {
                    if (_timer > 1)
                    {
                        SwithState(STATE.Closed);
                    }
                }
                break;
            default:
                break;
        }
    }

核心算法实现

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//用来管理游戏运行状态资源、整体性

public class GameManager : MonoBehaviour {
    public GameObject carPreb;
    private LevelInfo levelInfo;    //本关卡数据信息
    int levelId;
    int open_card_num;          //翻开卡牌的数量
    int match_ok_num;           //匹配成功的卡牌数量

    Card selectCard1;           //保存翻开的第一张牌
    Card selectCard2;           //保存翻开的第二张牌

    private void Awake()
    {
        levelInfo = DataMgr.Instance().levelInfo;
        levelId = DataMgr.Instance().levelId;
    }


    // Use this for initialization
    void Start () {
        InitBoard();
    }
    
    // Update is called once per frame
    void Update () {
        
    }

    //初始化棋盘
    void InitBoard()
    {
        open_card_num = 0;
        match_ok_num = 0;

        //求游戏区域的宽高
        GameObject gameAreaObj = GameObject.Find("GameArea");
        RectTransform rectTrans = gameAreaObj.GetComponent<RectTransform>();
        float gameWidth = rectTrans.rect.width;
        float gameHeigh = rectTrans.rect.height;
        //获取关卡信息
        int row = levelInfo.row;
        int col = levelInfo.col;
        //根据关卡信息的行列信息,初始化位置
        float spacingW = gameWidth / col / 10;
        float spacingH = gameHeigh / row / 10;
        float cellW = (gameWidth - spacingW * (col + 1)) / col;
        float cellH = (gameHeigh - spacingH * (row + 1)) / row;
        float cellSize = Mathf.Min(cellW,cellH);        //最终求出正方形卡牌尺寸

        //求出水平和垂直方向实际空格间隙
        float spacingX = (gameWidth - cellSize * col) / (col + 1);
        float spacingY = (gameHeigh - cellSize * row) / (row + 1);


        //根据关卡定义中图片素材个数,初始化一个索引下标的列表
         List<int> src_ids = new List<int>(levelInfo.sprites.Length);
        for(int i=0;i< levelInfo.sprites.Length; i++)
        {
            src_ids.Add(i);
        }

        //从上面的下标集合中,随机关卡中定义的count数出来
        int[] rand_ids = new int[levelInfo.count];  //卡牌随机的素材下标集合
        for(int i=0;i<levelInfo.count;i++)
        {
            int idx = Random.Range(0,src_ids.Count);
            rand_ids[i] = src_ids[idx];
            src_ids.RemoveAt(idx);        //删除随机过的素材下标,下次循环从剩余的集合中随机
        }

        //初始化卡牌id数组,我们卡牌id,可以用数组下标来表示,并且要保证卡牌成对出现
        List<int> cardIds = new List<int>(row * col);
        for(int i = 0; i < row * col / 2; i++)
        {
            if(i<levelInfo.count)
            {
                cardIds.Add(rand_ids[i]);
                cardIds.Add(rand_ids[i]);
            }
            else
            {
                int idx = Random.Range(0, rand_ids.Length);
                cardIds.Add(idx);
                cardIds.Add(idx);
            }
        }

        //对所有卡牌下标集合进行随机打乱
        for(int i=0;i<row*col;i++)
        {
            int idx = Random.Range(0,cardIds.Count);
            int temp = cardIds[i];        //随机一张牌,跟i对调,进而打乱顺序
            cardIds[i] = cardIds[idx];
            cardIds[idx] = temp;
        }



         int count = row * col;

        //创建所有卡牌
        for(int i=0;i<count;i++)
        {
            int row2 = i / col;
            int col2 = i % col;
            GameObject cardObj = Instantiate(carPreb,rectTrans);    //通过预制体创建卡牌对象
            cardObj.name = "Card" + i.ToString();
            RectTransform cardTrans = cardObj.GetComponent<RectTransform>();
            cardTrans.anchoredPosition = new Vector2(spacingX + (spacingX + cellSize) * col2, -spacingY - (spacingY + cellSize) * row2);
            cardTrans.sizeDelta = new Vector2(cellSize, cellSize);
            Card card = cardObj.GetComponent<Card>();
            card.Init(cardIds[i],levelInfo.sprites[cardIds[i]],this);

        }

    }

    //棋盘两个核心AI检测
    public void CheckCard(Card selectCard)
    {
        switch (open_card_num)
        {
            case 0:     //一张牌没翻开时
                {
                    open_card_num = 1;
                    selectCard1 = selectCard;
                }
                break;
            case 1:     //已经翻开一张牌,此时的selectCard,是第二张牌
                {
                    if(selectCard1 == selectCard)   //实际上不会调用
                    {
                        return  ;   
                    }
                    selectCard2 = selectCard;
                    if(selectCard1.id == selectCard2.id)     //匹配成功
                    {
                        selectCard1.MatchOK();
                        selectCard2.MatchOK();
                        match_ok_num++;
                        if (match_ok_num >= levelInfo.row * levelInfo.col / 2)
                        {
                            //全部匹配成功,比赛胜利结束,进行下一关
                     
                        }
                    }
                    else                                  //匹配失败
                    {
                        selectCard1.MatchFail();
                        selectCard2.MatchFail();
                    }

                    open_card_num = 0;
                    selectCard1 = null;
                    selectCard2 = null;
                }
                break;
            default:
                break;
        }
    }

}
GameManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class Card : MonoBehaviour,IPointerClickHandler {

    public GameObject bgObj;
    public Image iconImg;
    public GameObject maskObj;

    public enum STATE
    {
        Init,       //进入游戏时,展示几秒钟
        Closed,     //卡牌扣下状态
        Opened,     //点击一张卡牌时,翻开状态
        OK,        //匹配成功状态
        Fail        //匹配失败状态,短暂展示1秒钟,自动切换到扣下状态
    }

    public int id;
    GameManager gameMgr;
    STATE m_state;
    float _timer;   

    // Use this for initialization
    void Start () {
        SwithState(STATE.Init);

    }
    
    // Update is called once per frame
    void Update () {
        _timer += Time.deltaTime;

        switch (m_state)
        {
            case STATE.Init:
                {
                    if (_timer > 5)
                    {
                        SwithState(STATE.Closed);
                    }
                }
                break;
            case STATE.Closed:
                break;
            case STATE.Opened:
                break;
            case STATE.OK:
                break;
            case STATE.Fail:
                {
                    if (_timer > 1)
                    {
                        SwithState(STATE.Closed);
                    }
                }
                break;
            default:
                break;
        }
    }

    public void Init(int id,Sprite spr,GameManager manager)
    {
        this.id = id;
        gameMgr = manager;
        //根据卡牌id,设置对应的图片素材
        iconImg.sprite = spr;
    }


    //卡牌点击事件,系统自动调用
    public void OnPointerClick(PointerEventData eventData)
    {
        //卡牌切换到opened状态
        if (m_state == STATE.Closed)
        {
            SwithState(STATE.Opened);
            gameMgr.CheckCard(this);
        }
    }

    //卡牌状态切换
    void SwithState(STATE state)
    {
        _timer = 0;
        m_state = state;
        switch (state)
        {
            case STATE.Init:
                iconImg.gameObject.SetActive(true);
                maskObj.SetActive(false);
                break;
            case STATE.Closed:
                iconImg.gameObject.SetActive(false);
                break;
            case STATE.Opened:
                iconImg.gameObject.SetActive(true);
                break;
            case STATE.OK:
                maskObj.SetActive(true);
                break;
            case STATE.Fail:
                break;
            default:
                break;
        }
    }

    public void MatchOK()
    {
        SwithState(STATE.OK);
    }

    public void MatchFail()
    {
        SwithState(STATE.Fail);
    }
}
Card.cs

  棋盘两个核心AI检测

  切换卡牌的状态

   public void MatchOK()
    {
        SwithState(STATE.OK);
    }

    public void MatchFail()
    {
        SwithState(STATE.Fail);
    }
 public void CheckCard(Card selectCard)
    {
        switch (open_card_num)
        {
            case 0:     //一张牌没翻开时
                {
                    open_card_num = 1;
                    selectCard1 = selectCard;
                }
                break;
            case 1:     //已经翻开一张牌,此时的selectCard,是第二张牌
                {
                    if(selectCard1 == selectCard)   //实际上不会调用
                    {
                        return  ;   
                    }
                    selectCard2 = selectCard;
                    if(selectCard1.id == selectCard2.id)     //匹配成功
                    {
                        selectCard1.MatchOK();
                        selectCard2.MatchOK();
                        match_ok_num++;
                        if (match_ok_num >= levelInfo.row * levelInfo.col / 2)
                        {
                            //全部匹配成功,比赛胜利结束,进行下一关
                     
                        }
                    }
                    else                                  //匹配失败
                    {
                        selectCard1.MatchFail();
                        selectCard2.MatchFail();
                    }

                    open_card_num = 0;
                    selectCard1 = null;
                    selectCard2 = null;
                }
                break;
            default:
                break;
        }
    }

  防止两次同时点击一张卡牌

                    if(selectCard1 == selectCard)   //实际上不会调用
                    {
                        return  ;   
                    }

  不会调用原因在于

  卡牌点击事件,系统自动调用

    public void OnPointerClick(PointerEventData eventData)
    {
        //卡牌切换到opened状态
        if (m_state == STATE.Closed)
        {
            SwithState(STATE.Opened);
            gameMgr.CheckCard(this);
        }
    }

  卡牌必须要是Close状态才能翻转回来

        if (m_state == STATE.Closed)
        {
            SwithState(STATE.Opened);
            gameMgr.CheckCard(this);
        }

比赛结束

   成功

    - 提示信息:恭喜过关

    - 【进入下一关】按钮

    - 返回主菜单

  

  失败

    - 提示信息:再接再厉

    - 【重玩本关】按钮

    - 返回主菜单

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class PanelResult : MonoBehaviour {

    public GameObject winTxtObj;
    public GameObject loseTxtObj;
    public Button nextBtn;
    public Button retryBtn;
    public Button mainmenuBtn;

    GameManager gameMgr;

    private void Awake()
    {
        nextBtn.onClick.AddListener(()=> { onNextLevelBtn(); });
        retryBtn.onClick.AddListener(() => { OnRetryBtn(); });
        mainmenuBtn.onClick.AddListener(() => { OnMainMenuBtn(); });

        gameMgr = GameObject.Find("GameManager").GetComponent<GameManager>();

    }

    // Use this for initialization
    void Start () {
        
    }
    
    // Update is called once per frame
    void Update () {
        
    }

    //进行下一关
    void onNextLevelBtn()
    {
        gameMgr.NextLevel();
    }

    //再来一次
    void OnRetryBtn()
    {
        gameMgr.RetryLevel();
    }

    void OnMainMenuBtn()
    {
        SceneManager.LoadScene("MainMenu");
    }

    //设置比赛结果相关信息显示
    public void MatchResult(bool win)
    {
        winTxtObj.SetActive(win);
        loseTxtObj.SetActive(!win);
        nextBtn.gameObject.SetActive(win);
        retryBtn.gameObject.SetActive(!win);
    }
}
PanelResult.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//用来管理游戏运行状态资源、整体性

public class GameManager : MonoBehaviour {
    public GameObject carPreb;
    public PanelResult resultPanel;

    public enum STATE
    {
        Normal,
        Pause,
        About,
        Result
    }

    STATE m_state;

    private LevelInfo levelInfo;    //本关卡数据信息
    int levelId;
    int open_card_num;          //翻开卡牌的数量
    int match_ok_num;           //匹配成功的卡牌数量

    Card selectCard1;           //保存翻开的第一张牌
    Card selectCard2;           //保存翻开的第二张牌
    List<Card> list_cards = new List<Card>();      //保存所有卡牌引用

    private void Awake()
    {
        levelInfo = DataMgr.Instance().levelInfo;
        levelId = DataMgr.Instance().levelId;
    }


    // Use this for initialization
    void Start () {

        InitBoard();
    }
    
    // Update is called once per frame
    void Update () {
        
    }

    //初始化棋盘
    void InitBoard()
    {
        open_card_num = 0;
        match_ok_num = 0;
        SwitchState(STATE.Normal);
        ClearAllCard();

        //求游戏区域的宽高
        GameObject gameAreaObj = GameObject.Find("GameArea");
        RectTransform rectTrans = gameAreaObj.GetComponent<RectTransform>();
        float gameWidth = rectTrans.rect.width;
        float gameHeigh = rectTrans.rect.height;
        //获取关卡信息
        int row = levelInfo.row;
        int col = levelInfo.col;
        //根据关卡信息的行列信息,初始化位置
        float spacingW = gameWidth / col / 10;
        float spacingH = gameHeigh / row / 10;
        float cellW = (gameWidth - spacingW * (col + 1)) / col;
        float cellH = (gameHeigh - spacingH * (row + 1)) / row;
        float cellSize = Mathf.Min(cellW,cellH);        //最终求出正方形卡牌尺寸

        //求出水平和垂直方向实际空格间隙
        float spacingX = (gameWidth - cellSize * col) / (col + 1);
        float spacingY = (gameHeigh - cellSize * row) / (row + 1);


        //根据关卡定义中图片素材个数,初始化一个索引下标的列表
         List<int> src_ids = new List<int>(levelInfo.sprites.Length);
        for(int i=0;i< levelInfo.sprites.Length; i++)
        {
            src_ids.Add(i);
        }

        //从上面的下标集合中,随机关卡中定义的count数出来
        int[] rand_ids = new int[levelInfo.count];  //卡牌随机的素材下标集合
        for(int i=0;i<levelInfo.count;i++)
        {
            int idx = Random.Range(0,src_ids.Count);
            rand_ids[i] = src_ids[idx];
            src_ids.RemoveAt(idx);        //删除随机过的素材下标,下次循环从剩余的集合中随机
        }

        //初始化卡牌id数组,我们卡牌id,可以用数组下标来表示,并且要保证卡牌成对出现
        List<int> cardIds = new List<int>(row * col);
        for(int i = 0; i < row * col / 2; i++)
        {
            if(i<levelInfo.count)
            {
                cardIds.Add(rand_ids[i]);
                cardIds.Add(rand_ids[i]);
            }
            else
            {
                int idx = Random.Range(0, rand_ids.Length);
                cardIds.Add(idx);
                cardIds.Add(idx);
            }
        }

        //对所有卡牌下标集合进行随机打乱
        for(int i=0;i<row*col;i++)
        {
            int idx = Random.Range(0,cardIds.Count);
            int temp = cardIds[i];        //随机一张牌,跟i对调,进而打乱顺序
            cardIds[i] = cardIds[idx];
            cardIds[idx] = temp;
        }



         int count = row * col;

        //创建所有卡牌
        for(int i=0;i<count;i++)
        {
            int row2 = i / col;
            int col2 = i % col;
            GameObject cardObj = Instantiate(carPreb,rectTrans);    //通过预制体创建卡牌对象
            cardObj.name = "Card" + i.ToString();
            RectTransform cardTrans = cardObj.GetComponent<RectTransform>();
            cardTrans.anchoredPosition = new Vector2(spacingX + (spacingX + cellSize) * col2, -spacingY - (spacingY + cellSize) * row2);
            cardTrans.sizeDelta = new Vector2(cellSize, cellSize);
            Card card = cardObj.GetComponent<Card>();
            card.Init(cardIds[i],levelInfo.sprites[cardIds[i]],this);
            list_cards.Add(card);
        }

    }

    //棋盘两个核心AI检测
    public void CheckCard(Card selectCard)
    {
        switch (open_card_num)
        {
            case 0:     //一张牌没翻开时
                {
                    open_card_num = 1;
                    selectCard1 = selectCard;
                }
                break;
            case 1:     //已经翻开一张牌,此时的selectCard,是第二张牌
                {
                    if(selectCard1 == selectCard)   //实际上不会调用
                    {
                        return  ;   
                    }
                     selectCard2 = selectCard;
                    if(selectCard1.id == selectCard2.id)     //匹配成功
                    {
                        selectCard1.MatchOK();
                        selectCard2.MatchOK();
                        match_ok_num++;
                        if (match_ok_num >= levelInfo.row * levelInfo.col / 2)
                        {
                            //全部匹配成功,比赛胜利结束,进行下一关
                            SwitchState(STATE.Result);
                            resultPanel.MatchResult(true);
                        }
                    }
                    else                                  //匹配失败
                    {
                        selectCard1.MatchFail();
                        selectCard2.MatchFail();
                       // SwitchState(STATE.Result);        解开有惊喜
                       //resultPanel.MatchResult(false);    解开有惊喜
                    }

                    open_card_num = 0;
                    selectCard1 = null;
                    selectCard2 = null;
                }
                break;
            default:
                break;
        }
    }

    public void NextLevel()
    {
        int levelCount = DataMgr.Instance().levelData.levels.Length;
        if (levelId < levelCount)
        {
            levelId++;
            DataMgr.Instance().levelId = levelId;
            levelInfo = DataMgr.Instance().levelData.levels[levelId - 1];
        }
        InitBoard();
    }

    public void RetryLevel()
    {
        InitBoard();
    }


    //销毁所有卡牌,以便重新初始化
    void ClearAllCard()
    {
        if(list_cards.Count == 0)
        {
            return;
        }
        foreach(Card item in list_cards)
        {
            Destroy(item.gameObject);
        }
    }

    void SwitchState(STATE state)
    {
        m_state = state;
        switch (state)
        {
            case STATE.Normal:
                resultPanel.gameObject.SetActive(false);
                break;
            case STATE.Pause:
                break;
            case STATE.About:
                break;
            case STATE.Result:
                resultPanel.gameObject.SetActive(true);
                break;
            default:
                break;
        }
    }

}
GameManager.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class Card : MonoBehaviour,IPointerClickHandler {

    public GameObject bgObj;
    public Image iconImg;
    public GameObject maskObj;

    public enum STATE
    {
        Init,       //进入游戏时,展示几秒钟
        Closed,     //卡牌扣下状态
        Opened,     //点击一张卡牌时,翻开状态
        OK,        //匹配成功状态
        Fail        //匹配失败状态,短暂展示1秒钟,自动切换到扣下状态
    }

    public int id;
    GameManager gameMgr;
    STATE m_state;
    float _timer;   

    // Use this for initialization
    void Start () {
        SwithState(STATE.Init);

    }
    
    // Update is called once per frame
    void Update () {
        _timer += Time.deltaTime;

        switch (m_state)
        {
            case STATE.Init:
                {
                    if (_timer > 5)
                    {
                        SwithState(STATE.Closed);
                    }
                }
                break;
            case STATE.Closed:
                break;
            case STATE.Opened:
                break;
            case STATE.OK:
                break;
            case STATE.Fail:
                {
                    if (_timer > 1)
                    {
                        SwithState(STATE.Closed);
                    }
                }
                break;
            default:
                break;
        }
    }

    public void Init(int id,Sprite spr,GameManager manager)
    {
        this.id = id;
        gameMgr = manager;
        //根据卡牌id,设置对应的图片素材
        iconImg.sprite = spr;
    }


    //卡牌点击事件,系统自动调用
    public void OnPointerClick(PointerEventData eventData)
    {
        //卡牌切换到opened状态
        if (m_state == STATE.Closed)
        {
            SwithState(STATE.Opened);
            gameMgr.CheckCard(this);
        }
    }

    //卡牌状态切换
    void SwithState(STATE state)
    {
        _timer = 0;
        m_state = state;
        switch (state)
        {
            case STATE.Init:
                iconImg.gameObject.SetActive(true);
                maskObj.SetActive(false);
                break;
            case STATE.Closed:
                iconImg.gameObject.SetActive(false);
                break;
            case STATE.Opened:
                iconImg.gameObject.SetActive(true);
                break;
            case STATE.OK:
                maskObj.SetActive(true);
                break;
            case STATE.Fail:
                break;
            default:
                break;
        }
    }

    public void MatchOK()
    {
        SwithState(STATE.OK);
    }

    public void MatchFail()
    {
        SwithState(STATE.Fail);
    }
}
Card.cs

  创建一个Panel,显示游戏结束时画面场景

  界面中添加三个按钮

  创建PanelResult脚本,挂载到ResultPanel控件上

  脚本中添加五个引用,关联Unity中控件(引用效率高)

public class PanelResult : MonoBehaviour {

    public GameObject winTxtObj;
    public GameObject loseTxtObj;
    public Button nextBtnObj;
    public Button retryBtn;
    public Button mainmenuBtn;


    // Use this for initialization
    void Start () {
        
    }
    
    // Update is called once per frame
    void Update () {
        
    }
}

  添加按钮点击事件

    private void Awake()
    {
        nextBtn.onClick.AddListener(()=> { onNextLevelBtn(); });
        retryBtn.onClick.AddListener(() => { OnRetryBtn(); });
        mainmenuBtn.onClick.AddListener(() => { OnMainMenuBtn(); });

        gameMgr = GameObject.Find("GameManager").GetComponent<GameManager>();

    }

 //进行下一关
    void onNextLevelBtn()
    {
        gameMgr.NextLevel();
    }

    //再来一次
    void OnRetryBtn()
    {
        gameMgr.RetryLevel();
    }

    void OnMainMenuBtn()
    {
        SceneManager.LoadScene("MainMenu");
    }

    //设置比赛结果相关信息显示
    public void MatchResult(bool win)
    {
        winTxtObj.SetActive(win);
        loseTxtObj.SetActive(!win);
        nextBtn.gameObject.SetActive(win);
        retryBtn.gameObject.SetActive(!win);
    }

比赛倒计时

  (设置游戏时间为8s为了方便检测游戏结束后重玩和进入主菜单界面效果)  

  添加四张Image图片放置数字,不断对图片进行刷新实现游戏倒计时

   (时钟与分钟之间的":"用文本来表示  GRB:E2F43F)

  GameManager.cs中添加四张图片、十个数字的引用、时间倒计时常量、时间计时器

    public Image minule1Img;
    public Image minule2Img;
    public Image second1Img;
    public Image second4Img;
    public Sprite[] num_sprites;   //  数字sprite的引用


    public readonly int MATCH_TIMA = 180;   //时间倒计时

   用来更新比赛倒计时显示

 void UpdateTimeDisplay(int timeRemain)
    {
        System.TimeSpan tspan = new System.TimeSpan(0,0,timeRemain);
        string timeStr = string.Format("{0:00}:{1:00}",tspan.Minutes,tspan.Seconds);
        minule1Img.sprite = num_sprites[int.Parse(timeStr.Substring(0, 1))];    //把第一位字符转换成整数
        minule2Img.sprite = num_sprites[int.Parse(timeStr.Substring(1, 1))];    //把第二位字符转换成整数
        second1Img.sprite = num_sprites[int.Parse(timeStr.Substring(3, 1))];    //把第四位字符转换成整数   跳过的第三位是":"符号
         second2Img.sprite = num_sprites[int.Parse(timeStr.Substring(4, 1))];    //把第五位字符转换成整数
    }

比赛其他界面交互

   动态改变关卡分数

    void Start () {

        InitBoard();
        GameObject.Find("levelTxt").GetComponent<Text>().text = levelId.ToString();
    }

  主菜单按钮上添加脚本

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class PanelPause : MonoBehaviour {

    public void Awake()
    {
        GameObject.Find("ContinueBtn").GetComponent<Button>().onClick.AddListener(()=> { OnContinueBtn(); });
        GameObject.Find("MainMenuBtn").GetComponent<Button>().onClick.AddListener(() => { OnBackMainMenu(); });
    }

    void OnContinueBtn()
    {
        gameObject.SetActive(false);
    }
    
    void OnBackMainMenu()
    {
        SceneManager.LoadScene("MainMenu");
    }
}
PanelPause.cs

  注册按钮控件

    public void Awake()
    {
        GameObject.Find("ContinueBtn").GetComponent<Button>().onClick.AddListener(()=> { OnContinueBtn(); });
        GameObject.Find("MainMenuBtn").GetComponent<Button>().onClick.AddListener(() => { OnBackMainMenu(); });
    }

  按钮点击事件

    void OnContinueBtn()
    {
        gameObject.SetActive(false);
    }
    
    void OnBackMainMenu()
    {
        SceneManager.LoadScene("MainMenu");
    }

  提示信息界面

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PanelAbout : MonoBehaviour {

    // Use this for initialization
    void Start () {
        
    }
    
    // Update is called once per frame
    void Update () {
        if (Input.anyKeyDown)
        {
            gameObject.SetActive(false);
        }
    }
}
PanelAbout.cs

  点击界面,提示面板不可见

    void Update () {
        if (Input.anyKeyDown)
        {
            gameObject.SetActive(false);
        }
    }

记录保存于关卡解锁

  设置比赛结果相关信息显示

 public void MatchResult(bool win)
    {
        winTxtObj.SetActive(win);
        loseTxtObj.SetActive(!win);
        nextBtn.gameObject.SetActive(win);
        retryBtn.gameObject.SetActive(!win);

        if (win)
        {
            string level_key = System.Enum.GetName(typeof(THEME_ID), DataMgr.Instance().themeId)+"_level";    //主题名+下划线作为值键
            PlayerPrefs.SetInt(level_key,gameMgr.levelId+1);  //保存当前解锁的关卡编号,实际是当前关卡的下一关

        }
    }

  LevelManager.cs关卡初始化

    public int unlock_levelID=1;

    public void Awake()
    {
        levelPanelCellPreb = Resources.Load<GameObject>("Prefabs/SelectLevel/LevelPanelCell");
        levelItemCellPreb = Resources.Load("Prefabs/SelectLevel/LevelItemCell") as GameObject;
        levelDescTxt = GameObject.Find("LevelDesc/text").GetComponent<Text>();

        GameObject.Find("BackMainMenu").GetComponent<Button>().onClick.AddListener(()=> { OnBackMainMenuBtn(); });

        DataMgr.Instance().InitLevelData(DataMgr.Instance().themeId);
        totalCount = DataMgr.Instance().levelData.levels.Length;    //根据关卡数据表的个数,动态设置单元数量


        string level_key = System.Enum.GetName(typeof(THEME_ID), DataMgr.Instance().themeId) + "_level";
        if(PlayerPrefs.HasKey(level_key))
        {
            unlock_levelID = PlayerPrefs.GetInt(level_key);
        }
        else
        {
            unlock_levelID = 1;         //如果是第一次玩,还没有记录过,给默认解锁第一关
        }
        
    }

Android发布设置

  Android横向设置

  第一第二个选择按钮

    设置Android从上到下和从下到上竖屏

  取消勾选

  PC版没那么多要求~

游戏源代码

程序结构

data文件夹

  

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DataMgr {
    private static DataMgr ins = null;

    public THEME_ID themeId;

    //某一主题所有关卡数据
    public LevelData levelData;
    public int levelId;             //进入游戏前,保存当前选择的关卡id
    public LevelInfo levelInfo;     //当前关卡信息

    private DataMgr()
    {
        //将构造函数设置为私有,这样避免外部创建类的实例,只能通过Instance()来获取数据管理类的实例
    }

    public static DataMgr Instance()
    {
        if(ins == null)
        { 
            ins = new DataMgr();
        }
        return ins;
    }

    public LevelData InitLevelData(THEME_ID id)
    {
        levelData = null;
        switch (id)
        {
            case THEME_ID.Logo:
                levelData = Resources.Load<LevelData>("Prefabs/data/LevelDataLogo");
                break;
            case THEME_ID.Student:
                levelData = Resources.Load<LevelData>("Prefabs/data/LevelDataStudent");
                break;
            default:
                levelData = Resources.Load<LevelData>("Prefabs/data/LevelDataLogo");
                break;
        }
        return levelData; 
    }
}
DataMgr.class (数据管理类)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

//某一关数据结构
[System.Serializable]
public class LevelInfo
{
    public int id;      //关卡id
    public int row;
    public int col;
    public int count;   //本关从指定集合中随机几个素材
    public string desc;     //关卡简要描述
    public Sprite[] sprites;
}

public class LevelData : MonoBehaviour {

    public LevelInfo[] levels;
}
LevelData.cs (某一主题关卡数据结构)

gameplay文件夹

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class Card : MonoBehaviour,IPointerClickHandler {

    public GameObject bgObj;
    public Image iconImg;
    public GameObject maskObj;

    public enum STATE
    {
        Init,       //进入游戏时,展示几秒钟
        Closed,     //卡牌扣下状态
        Opened,     //点击一张卡牌时,翻开状态
        OK,        //匹配成功状态
        Fail        //匹配失败状态,短暂展示1秒钟,自动切换到扣下状态
    }

    public int id;
    GameManager gameMgr;
    STATE m_state;
    float _timer;   

    // Use this for initialization
    void Start () {
        SwithState(STATE.Init);

    }
    
    // Update is called once per frame
    void Update () {

        if(gameMgr.m_state != GameManager.STATE.Normal)
        {
            return;
        }

        _timer += Time.deltaTime;

        switch (m_state)
        {
            case STATE.Init:
                {
                    if (_timer > 5)
                    {
                        SwithState(STATE.Closed);
                    }
                }
                break;
            case STATE.Closed:
                break;
            case STATE.Opened:
                break;
            case STATE.OK:
                break;
            case STATE.Fail:
                {
                    if (_timer > 1)
                    {
                        SwithState(STATE.Closed);
                    }
                }
                break;
            default:
                break;
        }
    }

    public void Init(int id,Sprite spr,GameManager manager)
    {
        this.id = id;
        gameMgr = manager;
        //根据卡牌id,设置对应的图片素材
        iconImg.sprite = spr;
    }


    //卡牌点击事件,系统自动调用
    public void OnPointerClick(PointerEventData eventData)
    {
        //卡牌切换到opened状态
        if (m_state == STATE.Closed)
        {
            SwithState(STATE.Opened);
            gameMgr.CheckCard(this);
        }
    }

    //卡牌状态切换
    void SwithState(STATE state)
    {
        _timer = 0;
        m_state = state;
        switch (state)
        {
            case STATE.Init:
                iconImg.gameObject.SetActive(true);
                maskObj.SetActive(false);
                break;
            case STATE.Closed:
                iconImg.gameObject.SetActive(false);
                break;
            case STATE.Opened:
                iconImg.gameObject.SetActive(true);
                break;
            case STATE.OK:
                maskObj.SetActive(true);
                break;
            case STATE.Fail:
                break;
            default:
                break;
        }
    }

    public void MatchOK()
    {
        SwithState(STATE.OK);
    }

    public void MatchFail()
    {
        SwithState(STATE.Fail);
    }
}
Card.cs (管理单张游戏卡牌)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

//用来管理游戏运行状态资源、整体性

public class GameManager : MonoBehaviour {
    public GameObject carPreb;
    public PanelResult resultPanel;

    public GameObject pausePanel;
    public GameObject aboutPanel;

    public Image minule1Img;
    public Image minule2Img;
    public Image second1Img;
    public Image second2Img;
    public Sprite[] num_sprites;   //  数字sprite的引用


    public readonly int MATCH_TIMA = 75;   //时间倒计时

    public enum STATE
    {
        Normal,
        Pause,
        About,
        Result
    }

    public STATE m_state;

    private LevelInfo levelInfo;    //本关卡数据信息

    [HideInInspector]
    public int levelId;
    int open_card_num;          //翻开卡牌的数量
    int match_ok_num;           //匹配成功的卡牌数量

    Card selectCard1;           //保存翻开的第一张牌
    Card selectCard2;           //保存翻开的第二张牌
    List<Card> list_cards = new List<Card>();      //保存所有卡牌引用
    float _timer;


    private void Awake()
    {
        levelInfo = DataMgr.Instance().levelInfo;
        levelId = DataMgr.Instance().levelId;

        GameObject.Find("PanseBtn").GetComponent<Button>().onClick.AddListener(() => { OnPauseBtn(); });
        GameObject.Find("AudioBtn").GetComponent<Button>().onClick.AddListener(() => { OnAudioBtn(); });
        GameObject.Find("AboutBtn").GetComponent<Button>().onClick.AddListener(() => { OnAboutBtn(); });
    }


    // Use this for initialization
    void Start() {

        InitBoard();
        GameObject.Find("levelTxt").GetComponent<Text>().text = levelId.ToString();
    }

    // Update is called once per frame
    void Update() {

        switch (m_state)
        {
            case STATE.Normal:
                {
                    _timer += Time.deltaTime;
                    int timeRemain = (int)(MATCH_TIMA - _timer);

                    if (timeRemain < 0)     //比赛失败
                    {
                        SwitchState(STATE.Result);
                        resultPanel.MatchResult(false);
                    }
                    else
                    {
                        UpdateTimeDisplay(timeRemain);      //更新比赛倒计时
                    }
                }
                break;
            case STATE.Pause:
                break;
            case STATE.About:
                break;
            case STATE.Result:
                break;
            default:
                break;
        }

    }

    //初始化棋盘
    void InitBoard()
    {
        open_card_num = 0;
        match_ok_num = 0;
        SwitchState(STATE.Normal);
        ClearAllCard();

        //求游戏区域的宽高
        GameObject gameAreaObj = GameObject.Find("GameArea");
        RectTransform rectTrans = gameAreaObj.GetComponent<RectTransform>();
        float gameWidth = rectTrans.rect.width;
        float gameHeigh = rectTrans.rect.height;
        //获取关卡信息
        int row = levelInfo.row;
        int col = levelInfo.col;
        //根据关卡信息的行列信息,初始化位置
        float spacingW = gameWidth / col / 10;
        float spacingH = gameHeigh / row / 10;
        float cellW = (gameWidth - spacingW * (col + 1)) / col;
        float cellH = (gameHeigh - spacingH * (row + 1)) / row;
        float cellSize = Mathf.Min(cellW, cellH);        //最终求出正方形卡牌尺寸

        //求出水平和垂直方向实际空格间隙
        float spacingX = (gameWidth - cellSize * col) / (col + 1);
        float spacingY = (gameHeigh - cellSize * row) / (row + 1);


        //根据关卡定义中图片素材个数,初始化一个索引下标的列表
        List<int> src_ids = new List<int>(levelInfo.sprites.Length);
        for (int i = 0; i < levelInfo.sprites.Length; i++)
        {
            src_ids.Add(i);
        }

        //从上面的下标集合中,随机关卡中定义的count数出来
        int[] rand_ids = new int[levelInfo.count];  //卡牌随机的素材下标集合
        for (int i = 0; i < levelInfo.count; i++)
        {
            int idx = Random.Range(0, src_ids.Count);
            rand_ids[i] = src_ids[idx];
            src_ids.RemoveAt(idx);        //删除随机过的素材下标,下次循环从剩余的集合中随机
        }

        //初始化卡牌id数组,我们卡牌id,可以用数组下标来表示,并且要保证卡牌成对出现
        List<int> cardIds = new List<int>(row * col);
        for (int i = 0; i < row * col / 2; i++)
        {
            if (i < levelInfo.count)
            {
                cardIds.Add(rand_ids[i]);
                cardIds.Add(rand_ids[i]);
            }
            else
            {
                int idx = Random.Range(0, rand_ids.Length);
                cardIds.Add(idx);
                cardIds.Add(idx);
            }
        }

        //对所有卡牌下标集合进行随机打乱
        for (int i = 0; i < row * col; i++)
        {
            int idx = Random.Range(0, cardIds.Count);
            int temp = cardIds[i];        //随机一张牌,跟i对调,进而打乱顺序
            cardIds[i] = cardIds[idx];
            cardIds[idx] = temp;
        }



        int count = row * col;

        //创建所有卡牌
        for (int i = 0; i < count; i++)
        {
            int row2 = i / col;
            int col2 = i % col;
            GameObject cardObj = Instantiate(carPreb, rectTrans);    //通过预制体创建卡牌对象
            cardObj.name = "Card" + i.ToString();
            RectTransform cardTrans = cardObj.GetComponent<RectTransform>();
            cardTrans.anchoredPosition = new Vector2(spacingX + (spacingX + cellSize) * col2, -spacingY - (spacingY + cellSize) * row2);
            cardTrans.sizeDelta = new Vector2(cellSize, cellSize);
            Card card = cardObj.GetComponent<Card>();
            card.Init(cardIds[i], levelInfo.sprites[cardIds[i]], this);
            list_cards.Add(card);
        }

    }

    //棋盘两个核心AI检测
    public void CheckCard(Card selectCard)
    {
        switch (open_card_num)
        {
            case 0:     //一张牌没翻开时
                {
                    open_card_num = 1;
                    selectCard1 = selectCard;
                }
                break;
            case 1:     //已经翻开一张牌,此时的selectCard,是第二张牌
                {
                    if (selectCard1 == selectCard)   //实际上不会调用
                    {
                        return;
                    }
                    selectCard2 = selectCard;
                    if (selectCard1.id == selectCard2.id)     //匹配成功
                    {
                        selectCard1.MatchOK();
                        selectCard2.MatchOK();
                        match_ok_num++;
                        if (match_ok_num >= levelInfo.row * levelInfo.col / 2)
                        {
                            //全部匹配成功,比赛胜利结束,进行下一关
                            SwitchState(STATE.Result);
                            resultPanel.MatchResult(true);
                        }
                    }
                    else                                  //匹配失败
                    {
                        selectCard1.MatchFail();
                        selectCard2.MatchFail();
                        // SwitchState(STATE.Result);        解开有惊喜
                        //resultPanel.MatchResult(false);    解开有惊喜
                    }

                    open_card_num = 0;
                    selectCard1 = null;
                    selectCard2 = null;
                }
                break;
            default:
                break;
        }
    }

    public void NextLevel()
    {
        int levelCount = DataMgr.Instance().levelData.levels.Length;
        if (levelId < levelCount)
        {
            levelId++;
            DataMgr.Instance().levelId = levelId;
            levelInfo = DataMgr.Instance().levelData.levels[levelId - 1];
        }
        InitBoard();
    }

    public void RetryLevel()
    {
        InitBoard();
    }


    //销毁所有卡牌,以便重新初始化
    void ClearAllCard()
    {
        if (list_cards.Count == 0)
        {
            return;
        }
        foreach (Card item in list_cards)
        {
            Destroy(item.gameObject);
        }
    }

    void SwitchState(STATE state)
    {
        m_state = state;
        _timer = 0;
        switch (state)
        {
            case STATE.Normal:
                resultPanel.gameObject.SetActive(false);
                pausePanel.SetActive(false);
                aboutPanel.SetActive(false);
                break;
            case STATE.Pause:
                pausePanel.SetActive(true);
                break;
            case STATE.About:
                aboutPanel.SetActive(true);
                break;
            case STATE.Result:
                resultPanel.gameObject.SetActive(true);
                break;
            default:
                break;
        }
    }

    //用来更新比赛倒计时显示
    void UpdateTimeDisplay(int timeRemain)
    {
        System.TimeSpan tspan = new System.TimeSpan(0, 0, timeRemain);
        string timeStr = string.Format("{0:00}:{1:00}", tspan.Minutes, tspan.Seconds);
        minule1Img.sprite = num_sprites[int.Parse(timeStr.Substring(0, 1))];    //把第一位字符转换成整数
        minule2Img.sprite = num_sprites[int.Parse(timeStr.Substring(1, 1))];    //把第二位字符转换成整数
        second1Img.sprite = num_sprites[int.Parse(timeStr.Substring(3, 1))];    //把第四位字符转换成整数   跳过的第三位是":"符号
        second2Img.sprite = num_sprites[int.Parse(timeStr.Substring(4, 1))];    //把第五位字符转换成整数
    }

    //暂停按钮,弹出暂停界面panel
    void OnPauseBtn()
    {
        SwitchState(STATE.Pause);
    }

    void OnAudioBtn()
    {
        //声音开关
    }

    void OnAboutBtn()
    {
        SwitchState(STATE.About);
    }
}
GameManager.cs (游戏管理器)

Panel文件夹

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PanelAbout : MonoBehaviour {

    // Use this for initialization
    void Start () {
        
    }
    
    // Update is called once per frame
    void Update () {
        if (Input.anyKeyDown)
        {
            gameObject.SetActive(false);
        }
    }
}
PanelAbout.cs (管理游戏状态下信息提示)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class PanelPause : MonoBehaviour {

    public void Awake()
    {
        GameObject.Find("ContinueBtn").GetComponent<Button>().onClick.AddListener(()=> { OnContinueBtn(); });
        GameObject.Find("MainMenuBtn").GetComponent<Button>().onClick.AddListener(() => { OnBackMainMenu(); });
    }

    void OnContinueBtn()
    {
        gameObject.SetActive(false);
    }
    
    void OnBackMainMenu()
    {
        SceneManager.LoadScene("MainMenu");
    }
}
PanelPause.cs (管理游戏状态下主菜单)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class PanelResult : MonoBehaviour {

    public GameObject winTxtObj;
    public GameObject loseTxtObj;
    public Button nextBtn;
    public Button retryBtn;
    public Button mainmenuBtn;

    GameManager gameMgr;

    private void Awake()
    {

        nextBtn.onClick.AddListener(()=> { onNextLevelBtn(); });
        retryBtn.onClick.AddListener(() => { OnRetryBtn(); });
        mainmenuBtn.onClick.AddListener(() => { OnMainMenuBtn(); });

        gameMgr = GameObject.Find("GameManager").GetComponent<GameManager>();

    }

    // Use this for initialization
    void Start () {
        
    }
    
    // Update is called once per frame
    void Update () {
        
    }

    //进行下一关
    void onNextLevelBtn()
    {
        gameMgr.NextLevel();
    }

    //再来一次
    void OnRetryBtn()
    {
        gameMgr.RetryLevel();
    }

    void OnMainMenuBtn()
    {
        SceneManager.LoadScene("MainMenu");
    }

    //设置比赛结果相关信息显示
    public void MatchResult(bool win)
    {
        winTxtObj.SetActive(win);
        loseTxtObj.SetActive(!win);
        nextBtn.gameObject.SetActive(win);
        retryBtn.gameObject.SetActive(!win);

        if (win)
        {
            string level_key = System.Enum.GetName(typeof(THEME_ID), DataMgr.Instance().themeId)+"_level";    //主题名+下划线作为值键
            PlayerPrefs.SetInt(level_key,gameMgr.levelId+1);  //保存当前解锁的关卡编号,实际是当前关卡的下一关

        }
    }
}
PanelResult.cs (设置比赛结果相关信息显示)

scene文件夹

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;


public enum THEME_ID
{
    Logo,
    Student
}

public class Scene_MainMenu : MonoBehaviour {

    // Use this for initialization
    void Start () {
        GameObject.Find("LogoBtn").GetComponent<Button>().onClick.AddListener(()=>{ OnClickThemeBtn(THEME_ID.Logo); });
        GameObject.Find("StudentBtn").GetComponent<Button>().onClick.AddListener(() => { OnClickThemeBtn(THEME_ID.Student); });
        GameObject.Find("CloseBtn").GetComponent<Button>().onClick.AddListener(() => { OnCloseApp(); });
    }
    
    // Update is called once per frame
    void Update () {
       
    }

    void OnClickThemeBtn(THEME_ID theme)
    {
        SceneManager.LoadScene("SelectLevel");
        DataMgr.Instance().themeId = theme;
    }

    //退出程序
    void OnCloseApp()
    {
        Application.Quit();
    }
 
}
Scene_MainMenu.cs (管理游戏主菜单场景)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class Scene_Start : MonoBehaviour {

    float _timer;
    GameObject anyKeyObj;

    // Use this for initialization
    void Start () {
        _timer = 0;
        anyKeyObj = GameObject.Find("anykeyTxt");
    }
    
    // Update is called once per frame
    void Update () {

        _timer += Time.deltaTime;

        if (_timer % 0.5f > 0.25f)
        {
            anyKeyObj.SetActive(true);
        }
        else
        {
            anyKeyObj.SetActive(false);
        }


        if (_timer>5||Input.anyKeyDown)
        {
            GoToMainMenu();
        }
    }

        void GoToMainMenu()
        {
            SceneManager.LoadScene("MainMenu");
        }
}
Scene_Start.cs (管理游戏开始时场景)

SelectLevel文件夹

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;


//选关界面,每一个关卡按钮单元
public class LevelItemCell : MonoBehaviour ,IPointerEnterHandler,IPointerExitHandler,
        IPointerDownHandler,IPointerUpHandler,IPointerClickHandler{
    public RectTransform numTrans;
    public GameObject LockObj;
    public GameObject pressObj;

    //[HideInInspector]

    public int id;      //每个单元对应的关卡id(从1开始)
    public LevelManager lvlMgr;

    LevelInfo levelInfo;
    bool isLock;        

    //点击按钮,跳转到游戏界面
    public void OnPointerClick(PointerEventData eventData)
    {
        if (isLock)
        {
            return;
        }
        DataMgr.Instance().levelId = id;
        DataMgr.Instance().levelInfo = levelInfo;
        SceneManager.LoadScene("Gameplay");
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        pressObj.SetActive(true);
    }

    //当鼠标进入本单元矩形区域,显示当前关卡描述
    public void OnPointerEnter(PointerEventData eventData)
    {
        lvlMgr.SetLevelDesc(levelInfo.desc);

    }

    public void OnPointerExit(PointerEventData eventData)
    {
        lvlMgr.SetLevelDesc("关卡信息");
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        pressObj.SetActive(false);
    }

    private void Awake()
    {
        LockObj.SetActive(false);
        pressObj.SetActive(false);
    }

    // Use this for initialization
    void Start () {

        //根据解锁关卡记录,设置关卡是否锁定
        if (lvlMgr.unlock_levelID < id)
        {
            LockObj.SetActive(true);
            isLock = true;
        }

        float scale = 2;
        //初始化关卡数字显示
        if (id < 10)
        {
            //完全用代码动态创建一个Image对象,并作为num的子节点
            GameObject obj = new GameObject("num1",typeof(Image));
            RectTransform rtf = obj.GetComponent<RectTransform>();
            rtf.SetParent(numTrans);
            //设置数字
            Image img = obj.GetComponent<Image>();
            img.sprite = lvlMgr.spr_nums[id];
            img.SetNativeSize();    //图片原始尺寸
            rtf.localScale = new Vector3(2,2,1);
            rtf.localPosition = Vector3.zero;

        }
        else if (id<100)
        {
            //十位数
            GameObject obj = new GameObject("num1", typeof(Image));
            RectTransform rtf = obj.GetComponent<RectTransform>();
            rtf.SetParent(numTrans);
            //设置数字
            Image img = obj.GetComponent<Image>();
            img.sprite = lvlMgr.spr_nums[id/10];
            img.SetNativeSize();    //图片原始尺寸
            rtf.localScale = new Vector3(2, 2, 1);
            rtf.localPosition = new Vector3(-scale * rtf.rect.width/2-1,0,0);

            //个位数
            GameObject obj2 = new GameObject("num2", typeof(Image));
            RectTransform rtf2 = obj2.GetComponent<RectTransform>();
            rtf2.SetParent(numTrans);
            //设置数字
            Image img2 = obj2.GetComponent<Image>();
            img2.sprite = lvlMgr.spr_nums[id % 10];
            img2.SetNativeSize();    //图片原始尺寸
            rtf2.localScale = new Vector3(2, 2, 1);
            rtf2.localPosition = new Vector3(scale * rtf2.rect.width / 2 + 1, 0, 0);
        }
        levelInfo = DataMgr.Instance().levelData.levels[id - 1];
    }
    
    // Update is called once per frame
    void Update () {
        
    }
}
LevelItemCell.cs (选关界面,每一个关卡按钮单元)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;
using UnityEngine.SceneManagement;

//用来管理所有关卡按钮
//根据我们关卡数据定义,判断一共多少个关卡按钮。LevelManager来创建LevelPanelCell表格单元,LevelPanelCell表格单元是LevelItemCell
public class LevelManager : MonoBehaviour
{

    int totalCount = 0; //临时测试,一共18关
    readonly int ITEM_COUNT_PAGE = 15;  //每一页显示15个关卡
    readonly float PANEL_SPACE = 10f;
    readonly float MOVE_TIME = 0.5f;

    public RectTransform contenTrans;
    public Sprite[] spr_nums;

  

    float panelCellOffset;
    int pageCount;

    GameObject levelPanelCellPreb;
    GameObject levelItemCellPreb;
    Text levelDescTxt;

    float pointerDownTimeBak;
    float pointerDownX;

    int pageIdx;
    [HideInInspector]
    public int unlock_levelID=1;

    public void Awake()
    {
        levelPanelCellPreb = Resources.Load<GameObject>("Prefabs/SelectLevel/LevelPanelCell");
        levelItemCellPreb = Resources.Load("Prefabs/SelectLevel/LevelItemCell") as GameObject;
        levelDescTxt = GameObject.Find("LevelDesc/text").GetComponent<Text>();

        GameObject.Find("BackMainMenu").GetComponent<Button>().onClick.AddListener(()=> { OnBackMainMenuBtn(); });

        DataMgr.Instance().InitLevelData(DataMgr.Instance().themeId);
        totalCount = DataMgr.Instance().levelData.levels.Length;    //根据关卡数据表的个数,动态设置单元数量


        string level_key = System.Enum.GetName(typeof(THEME_ID), DataMgr.Instance().themeId) + "_level";
        if(PlayerPrefs.HasKey(level_key))
        {
            unlock_levelID = PlayerPrefs.GetInt(level_key);
        }
        else
        {
            unlock_levelID = 1;         //如果是第一次玩,还没有记录过,给默认解锁第一关
        }
        
    }

    // Use this for initialization
    void Start()
    {
        //计算panel的偏移
        panelCellOffset = contenTrans.rect.width + PANEL_SPACE;
        //计算出一共需要多少页
        pageCount = Mathf.CeilToInt((float)totalCount / ITEM_COUNT_PAGE);
        //当前显示的第几页表格panel,从0开始
        pageIdx = 0;

        for (int i = 0; i < pageCount; i++)
        {
            CreateLevelPanclCell(i);
        }
    }

    // Update is called once per frame
    void Update()
    {
        //按下时记录鼠标当前位置
        if (Input.GetMouseButtonDown(0))
        {
            pointerDownTimeBak = Time.time;
            pointerDownX = Input.mousePosition.x;
        }

        if (Input.GetMouseButtonUp(0))
        {
            //模拟滑动事件,假定很短时间的滑动
            if (Time.time - pointerDownTimeBak < 0.5f)
            {
                float offsetX = Input.mousePosition.x - pointerDownX;
                //向左滑动鼠标,并超过一定距离
                if (offsetX < -200 && pageIdx < pageCount - 1)
                {
                    //向左滑动鼠标,并超过一段距离
                    pageIdx++;
                    //执行所有表格面板向左平滑滑动
                    //0.5秒对自身左边叠加量,向左移动一段距离
                    contenTrans.DOBlendableLocalMoveBy(new Vector3(-panelCellOffset, 0, 0), MOVE_TIME);

                }
                else if (offsetX > 200 && pageIdx > 0)
                {
                    //向右滑动鼠标,并超过一段距离
                    pageIdx--;
                    //执行所有表格面板向右平滑滑动
                    //0.5秒对自身左边叠加量,向右移动一段距离
                    contenTrans.DOBlendableLocalMoveBy(new Vector3(panelCellOffset, 0, 0), MOVE_TIME);
                }
            }
        }
    }

    //创建表格panel,从0开始编号
    void CreateLevelPanclCell(int pageInx)
    {
        GameObject panelObj = Instantiate(levelPanelCellPreb, contenTrans);
        RectTransform panelTrans = panelObj.GetComponent<RectTransform>();
        panelTrans.anchoredPosition = new Vector2(panelCellOffset * pageInx, 0);

        //确定本业 中具有的关卡元素个数
        int startId = ITEM_COUNT_PAGE * pageInx;
        //本业需要显示按钮的个数
         int count = totalCount - startId;

        if (count > ITEM_COUNT_PAGE)
        {
            count = ITEM_COUNT_PAGE;
        }

        //创建本页中所有关卡按钮
        for (int i = 0; i < count; i++)
        {
            //创建每一个关卡的按钮单元
            GameObject itemObj = Instantiate(levelItemCellPreb, panelTrans);

            //这一段cell相关调用,实在Start()之前执行的
            LevelItemCell cell = itemObj.GetComponent<LevelItemCell>();
            cell.id = startId + i + 1;      //设置每个单元的关卡编号
            cell.lvlMgr = this;
            itemObj.name = cell.id.ToString();

        }

    }

    //设置关卡描述
    public void SetLevelDesc(string content)
    {
        levelDescTxt.text = content;
    }

    void OnBackMainMenuBtn()
    {
        SceneManager.LoadScene("MainMenu");
    }
}
LevelManager.cs (用来管理所有关卡按钮)
(如需转载学习,请标明出处)
原文地址:https://www.cnblogs.com/1138720556Gary/p/9521264.html