启动屏界面、主菜单界面、选关界面、游戏界面
卡牌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 / ;
float spacingH = gameHeigh / row / ;
float cellW = (gameWidth - spacingW * (col + )) / col;
float cellH = (gameHeigh - spacingH * (row + )) / row;
float cellSize = Mathf.Min(cellW,cellH); //最终求出正方形卡牌尺寸 //求出水平和垂直方向实际空格间隙
float spacingX = (gameWidth - cellSize * col) / (col + );
float spacingY = (gameHeigh - cellSize * row) / (row + ); int count = row * col; //创建所有卡牌
for(int i=;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 / ;
float spacingH = gameHeigh / row / ;
float cellW = (gameWidth - spacingW * (col + )) / col;
float cellH = (gameHeigh - spacingH * (row + )) / row;
float cellSize = Mathf.Min(cellW,cellH); //最终求出正方形卡牌尺寸 //求出水平和垂直方向实际空格间隙
float spacingX = (gameWidth - cellSize * col) / (col + );
float spacingY = (gameHeigh - cellSize * row) / (row + ); int count = row * col; //创建所有卡牌
for(int i=;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=;i<src_ids.Count;i++)
{
src_ids.Add(i);
}
从src_ids的下标集合中,随机关卡中定义的count数出来
int[] rand_ids = new int[levelInfo.count]; //卡牌随机的素材下标集合
for(int i=;i<levelInfo.count;i++)
{
int idx = Random.Range(,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 = ; i < row * col / ; i++)
{
if(i<levelInfo.count)
{
cardIds.Add(rand_ids[i]);
cardIds.Add(rand_ids[i]);
}
else
{
int idx = Random.Range(, rand_ids.Length);
cardIds.Add(idx);
cardIds.Add(idx);
}
}
对所有卡牌下标集合进行随机打乱
for(int i=;i<row*col;i++)
{
int idx = Random.Range(,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 > )
{
SwithState(STATE.Closed);
}
}
break;
case STATE.Closed:
break;
case STATE.Opened:
break;
case STATE.OK:
break;
case STATE.Fail:
{
if (_timer > )
{
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 = ;
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 = ;
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 > )
{
SwithState(STATE.Closed);
}
}
break;
case STATE.Closed:
break;
case STATE.Opened:
break;
case STATE.OK:
break;
case STATE.Fail:
{
if (_timer > )
{
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 = ;
match_ok_num = ; //求游戏区域的宽高
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 / ;
float spacingH = gameHeigh / row / ;
float cellW = (gameWidth - spacingW * (col + )) / col;
float cellH = (gameHeigh - spacingH * (row + )) / row;
float cellSize = Mathf.Min(cellW,cellH); //最终求出正方形卡牌尺寸 //求出水平和垂直方向实际空格间隙
float spacingX = (gameWidth - cellSize * col) / (col + );
float spacingY = (gameHeigh - cellSize * row) / (row + ); //根据关卡定义中图片素材个数,初始化一个索引下标的列表
List<int> src_ids = new List<int>(levelInfo.sprites.Length);
for(int i=;i< levelInfo.sprites.Length; i++)
{
src_ids.Add(i);
} //从上面的下标集合中,随机关卡中定义的count数出来
int[] rand_ids = new int[levelInfo.count]; //卡牌随机的素材下标集合
for(int i=;i<levelInfo.count;i++)
{
int idx = Random.Range(,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 = ; i < row * col / ; i++)
{
if(i<levelInfo.count)
{
cardIds.Add(rand_ids[i]);
cardIds.Add(rand_ids[i]);
}
else
{
int idx = Random.Range(, rand_ids.Length);
cardIds.Add(idx);
cardIds.Add(idx);
}
} //对所有卡牌下标集合进行随机打乱
for(int i=;i<row*col;i++)
{
int idx = Random.Range(,cardIds.Count);
int temp = cardIds[i]; //随机一张牌,跟i对调,进而打乱顺序
cardIds[i] = cardIds[idx];
cardIds[idx] = temp;
} int count = row * col; //创建所有卡牌
for(int i=;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 : //一张牌没翻开时
{
open_card_num = ;
selectCard1 = selectCard;
}
break;
case : //已经翻开一张牌,此时的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 / )
{
//全部匹配成功,比赛胜利结束,进行下一关 }
}
else //匹配失败
{
selectCard1.MatchFail();
selectCard2.MatchFail();
} open_card_num = ;
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 > )
{
SwithState(STATE.Closed);
}
}
break;
case STATE.Closed:
break;
case STATE.Opened:
break;
case STATE.OK:
break;
case STATE.Fail:
{
if (_timer > )
{
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 = ;
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 : //一张牌没翻开时
{
open_card_num = ;
selectCard1 = selectCard;
}
break;
case : //已经翻开一张牌,此时的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 / )
{
//全部匹配成功,比赛胜利结束,进行下一关 }
}
else //匹配失败
{
selectCard1.MatchFail();
selectCard2.MatchFail();
} open_card_num = ;
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 = ;
match_ok_num = ;
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 / ;
float spacingH = gameHeigh / row / ;
float cellW = (gameWidth - spacingW * (col + )) / col;
float cellH = (gameHeigh - spacingH * (row + )) / row;
float cellSize = Mathf.Min(cellW,cellH); //最终求出正方形卡牌尺寸 //求出水平和垂直方向实际空格间隙
float spacingX = (gameWidth - cellSize * col) / (col + );
float spacingY = (gameHeigh - cellSize * row) / (row + ); //根据关卡定义中图片素材个数,初始化一个索引下标的列表
List<int> src_ids = new List<int>(levelInfo.sprites.Length);
for(int i=;i< levelInfo.sprites.Length; i++)
{
src_ids.Add(i);
} //从上面的下标集合中,随机关卡中定义的count数出来
int[] rand_ids = new int[levelInfo.count]; //卡牌随机的素材下标集合
for(int i=;i<levelInfo.count;i++)
{
int idx = Random.Range(,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 = ; i < row * col / ; i++)
{
if(i<levelInfo.count)
{
cardIds.Add(rand_ids[i]);
cardIds.Add(rand_ids[i]);
}
else
{
int idx = Random.Range(, rand_ids.Length);
cardIds.Add(idx);
cardIds.Add(idx);
}
} //对所有卡牌下标集合进行随机打乱
for(int i=;i<row*col;i++)
{
int idx = Random.Range(,cardIds.Count);
int temp = cardIds[i]; //随机一张牌,跟i对调,进而打乱顺序
cardIds[i] = cardIds[idx];
cardIds[idx] = temp;
} int count = row * col; //创建所有卡牌
for(int i=;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 : //一张牌没翻开时
{
open_card_num = ;
selectCard1 = selectCard;
}
break;
case : //已经翻开一张牌,此时的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 / )
{
//全部匹配成功,比赛胜利结束,进行下一关
SwitchState(STATE.Result);
resultPanel.MatchResult(true);
}
}
else //匹配失败
{
selectCard1.MatchFail();
selectCard2.MatchFail();
// SwitchState(STATE.Result); 解开有惊喜
//resultPanel.MatchResult(false); 解开有惊喜
} open_card_num = ;
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 - ];
}
InitBoard();
} public void RetryLevel()
{
InitBoard();
} //销毁所有卡牌,以便重新初始化
void ClearAllCard()
{
if(list_cards.Count == )
{
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 > )
{
SwithState(STATE.Closed);
}
}
break;
case STATE.Closed:
break;
case STATE.Opened:
break;
case STATE.OK:
break;
case STATE.Fail:
{
if (_timer > )
{
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 = ;
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 = ; //时间倒计时
用来更新比赛倒计时显示
void UpdateTimeDisplay(int timeRemain)
{
System.TimeSpan tspan = new System.TimeSpan(,,timeRemain);
string timeStr = string.Format("{0:00}:{1:00}",tspan.Minutes,tspan.Seconds);
minule1Img.sprite = num_sprites[int.Parse(timeStr.Substring(, ))]; //把第一位字符转换成整数
minule2Img.sprite = num_sprites[int.Parse(timeStr.Substring(, ))]; //把第二位字符转换成整数
second1Img.sprite = num_sprites[int.Parse(timeStr.Substring(, ))]; //把第四位字符转换成整数 跳过的第三位是":"符号
second2Img.sprite = num_sprites[int.Parse(timeStr.Substring(, ))]; //把第五位字符转换成整数
}
比赛其他界面交互
动态改变关卡分数
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+); //保存当前解锁的关卡编号,实际是当前关卡的下一关 }
}
LevelManager.cs关卡初始化
public int unlock_levelID=; 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 = ; //如果是第一次玩,还没有记录过,给默认解锁第一关
} }
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 > )
{
SwithState(STATE.Closed);
}
}
break;
case STATE.Closed:
break;
case STATE.Opened:
break;
case STATE.OK:
break;
case STATE.Fail:
{
if (_timer > )
{
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 = ;
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 = ; //时间倒计时 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 < ) //比赛失败
{
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 = ;
match_ok_num = ;
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 / ;
float spacingH = gameHeigh / row / ;
float cellW = (gameWidth - spacingW * (col + )) / col;
float cellH = (gameHeigh - spacingH * (row + )) / row;
float cellSize = Mathf.Min(cellW, cellH); //最终求出正方形卡牌尺寸 //求出水平和垂直方向实际空格间隙
float spacingX = (gameWidth - cellSize * col) / (col + );
float spacingY = (gameHeigh - cellSize * row) / (row + ); //根据关卡定义中图片素材个数,初始化一个索引下标的列表
List<int> src_ids = new List<int>(levelInfo.sprites.Length);
for (int i = ; i < levelInfo.sprites.Length; i++)
{
src_ids.Add(i);
} //从上面的下标集合中,随机关卡中定义的count数出来
int[] rand_ids = new int[levelInfo.count]; //卡牌随机的素材下标集合
for (int i = ; i < levelInfo.count; i++)
{
int idx = Random.Range(, 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 = ; i < row * col / ; i++)
{
if (i < levelInfo.count)
{
cardIds.Add(rand_ids[i]);
cardIds.Add(rand_ids[i]);
}
else
{
int idx = Random.Range(, rand_ids.Length);
cardIds.Add(idx);
cardIds.Add(idx);
}
} //对所有卡牌下标集合进行随机打乱
for (int i = ; i < row * col; i++)
{
int idx = Random.Range(, cardIds.Count);
int temp = cardIds[i]; //随机一张牌,跟i对调,进而打乱顺序
cardIds[i] = cardIds[idx];
cardIds[idx] = temp;
} int count = row * col; //创建所有卡牌
for (int i = ; 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 : //一张牌没翻开时
{
open_card_num = ;
selectCard1 = selectCard;
}
break;
case : //已经翻开一张牌,此时的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 / )
{
//全部匹配成功,比赛胜利结束,进行下一关
SwitchState(STATE.Result);
resultPanel.MatchResult(true);
}
}
else //匹配失败
{
selectCard1.MatchFail();
selectCard2.MatchFail();
// SwitchState(STATE.Result); 解开有惊喜
//resultPanel.MatchResult(false); 解开有惊喜
} open_card_num = ;
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 - ];
}
InitBoard();
} public void RetryLevel()
{
InitBoard();
} //销毁所有卡牌,以便重新初始化
void ClearAllCard()
{
if (list_cards.Count == )
{
return;
}
foreach (Card item in list_cards)
{
Destroy(item.gameObject);
}
} void SwitchState(STATE state)
{
m_state = state;
_timer = ;
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(, , timeRemain);
string timeStr = string.Format("{0:00}:{1:00}", tspan.Minutes, tspan.Seconds);
minule1Img.sprite = num_sprites[int.Parse(timeStr.Substring(, ))]; //把第一位字符转换成整数
minule2Img.sprite = num_sprites[int.Parse(timeStr.Substring(, ))]; //把第二位字符转换成整数
second1Img.sprite = num_sprites[int.Parse(timeStr.Substring(, ))]; //把第四位字符转换成整数 跳过的第三位是":"符号
second2Img.sprite = num_sprites[int.Parse(timeStr.Substring(, ))]; //把第五位字符转换成整数
} //暂停按钮,弹出暂停界面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+); //保存当前解锁的关卡编号,实际是当前关卡的下一关 }
}
}
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 = ;
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>||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 = ;
//初始化关卡数字显示
if (id < )
{
//完全用代码动态创建一个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(,,);
rtf.localPosition = Vector3.zero; }
else if (id<)
{
//十位数
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(, , );
rtf.localPosition = new Vector3(-scale * rtf.rect.width/-,,); //个位数
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 % ];
img2.SetNativeSize(); //图片原始尺寸
rtf2.localScale = new Vector3(, , );
rtf2.localPosition = new Vector3(scale * rtf2.rect.width / + , , );
}
levelInfo = DataMgr.Instance().levelData.levels[id - ];
} // 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 = ; //临时测试,一共18关
readonly int ITEM_COUNT_PAGE = ; //每一页显示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=; 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 = ; //如果是第一次玩,还没有记录过,给默认解锁第一关
} } // Use this for initialization
void Start()
{
//计算panel的偏移
panelCellOffset = contenTrans.rect.width + PANEL_SPACE;
//计算出一共需要多少页
pageCount = Mathf.CeilToInt((float)totalCount / ITEM_COUNT_PAGE);
//当前显示的第几页表格panel,从0开始
pageIdx = ; for (int i = ; i < pageCount; i++)
{
CreateLevelPanclCell(i);
}
} // Update is called once per frame
void Update()
{
//按下时记录鼠标当前位置
if (Input.GetMouseButtonDown())
{
pointerDownTimeBak = Time.time;
pointerDownX = Input.mousePosition.x;
} if (Input.GetMouseButtonUp())
{
//模拟滑动事件,假定很短时间的滑动
if (Time.time - pointerDownTimeBak < 0.5f)
{
float offsetX = Input.mousePosition.x - pointerDownX;
//向左滑动鼠标,并超过一定距离
if (offsetX < - && pageIdx < pageCount - )
{
//向左滑动鼠标,并超过一段距离
pageIdx++;
//执行所有表格面板向左平滑滑动
//0.5秒对自身左边叠加量,向左移动一段距离
contenTrans.DOBlendableLocalMoveBy(new Vector3(-panelCellOffset, , ), MOVE_TIME); }
else if (offsetX > && pageIdx > )
{
//向右滑动鼠标,并超过一段距离
pageIdx--;
//执行所有表格面板向右平滑滑动
//0.5秒对自身左边叠加量,向右移动一段距离
contenTrans.DOBlendableLocalMoveBy(new Vector3(panelCellOffset, , ), MOVE_TIME);
}
}
}
} //创建表格panel,从0开始编号
void CreateLevelPanclCell(int pageInx)
{
GameObject panelObj = Instantiate(levelPanelCellPreb, contenTrans);
RectTransform panelTrans = panelObj.GetComponent<RectTransform>();
panelTrans.anchoredPosition = new Vector2(panelCellOffset * pageInx, ); //确定本业 中具有的关卡元素个数
int startId = ITEM_COUNT_PAGE * pageInx;
//本业需要显示按钮的个数
int count = totalCount - startId; if (count > ITEM_COUNT_PAGE)
{
count = ITEM_COUNT_PAGE;
} //创建本页中所有关卡按钮
for (int i = ; i < count; i++)
{
//创建每一个关卡的按钮单元
GameObject itemObj = Instantiate(levelItemCellPreb, panelTrans); //这一段cell相关调用,实在Start()之前执行的
LevelItemCell cell = itemObj.GetComponent<LevelItemCell>();
cell.id = startId + i + ; //设置每个单元的关卡编号
cell.lvlMgr = this;
itemObj.name = cell.id.ToString(); } } //设置关卡描述
public void SetLevelDesc(string content)
{
levelDescTxt.text = content;
} void OnBackMainMenuBtn()
{
SceneManager.LoadScene("MainMenu");
}
}
LevelManager.cs (用来管理所有关卡按钮)