一、框架視圖
二、關鍵代碼
Card
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;
public class Card : MonoBehaviour
{
public Button btnCard;
public Image imgCard;
public Sprite[] clickSprites;
public Sprite[] coveredSprites;
public int id;
public RectTransform rtf;
public List<Card> aboveCardList = new List<Card>();//覆蓋當前卡牌的其他卡牌
public List<Card> coverCardList = new List<Card>();//當前卡牌覆蓋的其他卡牌
// Start is called before the first frame update
void Start()
{
btnCard.onClick.AddListener(CardClickEvent);
}
// Update is called once per frame
void Update()
{
}
/// <summary>
/// 設置卡牌紋理圖案與ID
/// </summary>
public void SetCardSprite()
{
id = Random.Range(1,15);
imgCard.sprite = clickSprites[id - 1];
SpriteState ss= btnCard.spriteState;
ss.disabledSprite = coveredSprites[id - 1];
btnCard.spriteState=ss;
}
/// <summary>
/// 設置當前卡牌與其他卡牌的覆蓋關系
/// </summary>
/// <param name="targetCard">目標卡牌</param>
public void SetCoverCardState(Card targetCard)
{
//本身卡牌屏幕的坐標點
Vector2 cardPos= GUIUtility.GUIToScreenPoint(rtf.anchoredPosition);
//目標卡牌屏幕的坐標點
Vector2 targetCardPos= GUIUtility.GUIToScreenPoint(targetCard.rtf.anchoredPosition);
if (Mathf.Abs(cardPos.x-targetCardPos.x)<Deck.Instance.cardWidth
&&Mathf.Abs(cardPos.y-targetCardPos.y)<Deck.Instance.cardHeight)
{
//覆蓋上了目標卡牌
targetCard.AddAboveCard(this);
targetCard.CloseButtonClickState();
coverCardList.Add(targetCard);
}
}
/// <summary>
/// 關閉按鈕交互狀態
/// </summary>
public void CloseButtonClickState()
{
btnCard.interactable = false;
}
/// <summary>
/// 卡牌點擊事件
/// </summary>
public void CardClickEvent()
{
Deck.Instance.PlayClickSound();
transform.SetSiblingIndex(500);
//需要移除所有被我們覆蓋的卡牌持有的我們自身卡牌引用(我們當前自身)
for (int i = 0; i < coverCardList.Count; i++)
{
coverCardList[i].RemoveAboveCard(this);
coverCardList[i].JudgeCanClickState();
}
int posID = -1;
Transform targetTrans= Deck.Instance.GetPickDeckTargetTrans(id,out posID);
transform.DOMove(targetTrans.position,0.5f).OnComplete
(
() =>
{
if (targetTrans.childCount>0)
{
transform.SetParent(Deck.Instance.GetEmptyPickDeckTargetTrans(posID));
}
else
{
transform.SetParent(targetTrans);
}
transform.localPosition = Vector3.zero;
Deck.Instance.JudgeClearCard();
}
);
}
/// <summary>
/// 目標卡牌覆蓋了當前卡牌(我們自身去調用目標卡牌的方法)
/// </summary>
public void AddAboveCard(Card targetCard)
{
aboveCardList.Add(targetCard);
}
/// <summary>
/// 移除當前卡牌被目標卡牌覆蓋的引用(我們自身被其他覆蓋到我們的卡牌(目標卡牌)去調用)
/// </summary>
/// <param name="aboveCard"></param>
public void RemoveAboveCard(Card aboveCard)
{
aboveCardList.Remove(aboveCard);
}
/// <summary>
/// 判斷當前是否可以點擊
/// </summary>
public void JudgeCanClickState()
{
btnCard.interactable = aboveCardList.Count <= 0;
}
}
Deck
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using UnityEngine.SceneManagement;
public class Deck : MonoBehaviour
{
public static Deck Instance { get; set; }
public int cardWidth = 213;
public int cardHeight = 213;
public GameObject cardGo;
private int row = 7;
private int column = 7;
private int layer = 8;
private List<Card> cards = new List<Card>();
public RectTransform deckTrans;//卡牌需要生成到的位置的transform引用
public Transform[] pickDeckPosTrans;//撿牌堆的格子位置
public RectTransform centerDeckTrans;//中間牌堆的基礎位置(原點)
public RectTransform leftColumnDeckTrans;
public RectTransform rightColumnDeckTrans;
public RectTransform leftDownDeckTrans;
public RectTransform rightDownDeckTrans;
public int[] pickDeckCardIDs;//存放當前選中卡牌堆里的卡牌ID(跟當前位置一一對應)
private int totalCardNum = 168;
private int createCardNum = 0;
public GameObject gameOverPanelGo;
public AudioSource audioSource;
public AudioClip clickSound;
private int[,,] centerDeck = new int[,,]//層 行 列
{
//后5層
{
{0,0,0,0},
{0,2,2,2},
{0,2,2,2},
{0,2,2,2},
{0,2,2,2},
{0,2,2,2},
{0,0,0,0}
},
{
{0,0,0,0},
{0,2,2,2},
{0,2,2,2},
{0,2,2,2},
{0,2,2,2},
{0,2,2,2},
{0,0,0,0}
},
{
{0,0,0,0},
{0,2,2,2},
{0,2,2,2},
{0,2,2,2},
{0,2,2,2},
{0,2,2,2},
{0,0,0,0}
},
{
{0,0,0,0},
{0,2,2,2},
{0,2,2,2},
{0,2,2,2},
{0,2,2,2},
{0,2,2,2},
{0,0,0,0}
},
{
{0,0,0,0},
{0,2,2,2},
{0,2,2,2},
{0,2,2,2},
{0,2,2,2},
{0,2,2,2},
{0,0,0,0}
},
//上兩層
{
{0,0,0,0},
{0,1,1,1},
{0,0,1,1},
{0,0,1,1},
{0,1,2,2},
{0,2,2,1},
{0,0,0,0}
},
{
{0,0,0,0},
{0,1,1,3},
{0,0,2,2},
{3,3,3,3},
{0,0,0,3},
{0,0,3,3},
{0,0,0,0}
},
//最上層
{
{0,3,1,2},
{0,0,0,0},
{3,2,2,2},
{3,3,3,3},
{0,0,2,2},
{0,0,0,0},
{0,0,1,3}
}
};
private void Awake()
{
Instance = this;
}
// Start is called before the first frame update
void Start()
{
pickDeckCardIDs = new int[7] { -1,-1,-1,-1,-1,-1,-1};
//生成中間的一疊
//層
for (int k = 0; k < layer; k++)
{
//行
for (int j = 0; j < row; j++)
{
//隨機當前列是否偏移
bool ifMoveX = Convert.ToBoolean(UnityEngine.Random.Range(0, 2));
int dirX = 0;
if (ifMoveX)
{
//偏移方向是左還是右
dirX = UnityEngine.Random.Range(0, 2) == 0 ? 1 : -1;
}
//隨機當前行是否偏移
bool ifMoveY = Convert.ToBoolean(UnityEngine.Random.Range(0, 2));
int dirY = 0;
if (ifMoveY)
{
//偏移方向是上還是下
dirY = UnityEngine.Random.Range(0, 2) == 0 ? 1 : -1;
}
//準備臨時數組存貯前半部分隨機到的卡牌狀態,以對稱生成后半部分狀態
CREATESTATE[] halfState = new CREATESTATE[column / 2];
//列
for (int i = 0; i < column; i++)
{
GameObject go = null;
CREATESTATE cs;
if (i<=column/2)
{
//前半部分直接從三維數組取1 2 3 4 5 6 7
// 2 1 0 0 1 2
cs = (CREATESTATE)centerDeck[k, j, i];
if (i!=column/2)
{
halfState[column / 2 - i - 1] = cs;
}
}
else
{
cs = halfState[i - column / 2 - 1];
}
switch (cs)
{
case CREATESTATE.NONE:
break;
case CREATESTATE.CREATE:
go = CreateCardGo(i, j, dirX, dirY);
break;
case CREATESTATE.RANDOM:
if (UnityEngine.Random.Range(0,2)==0?true:false)
{
//隨機生成
go= CreateCardGo(i,j,dirX,dirY);
}
break;
case CREATESTATE.ONLYCREATE:
go = CreateCardGo(i, j, 0, 0);
break;
default:
break;
}
if (go)
{
Card card = go.GetComponent<Card>();
card.SetCardSprite();
SetCoverState(card);
cards.Add(card);
createCardNum++;
go.name = "I:" + i.ToString() + " J:" + j.ToString() + " K:" + k.ToString();
}
}
}
}
//生成其他四疊
int createNum = (totalCardNum - createCardNum) / 4;
int leftNum = totalCardNum - createCardNum - createNum * 4;
//左豎
for (int i = createNum + leftNum; i>0 ; i--)
{
CreateCard(leftColumnDeckTrans,0,-i);
}
//右豎
for (int i = 0; i < createNum; i++)
{
CreateCard(rightColumnDeckTrans,0,-i);
}
//左下
for (int i = 0; i < createNum; i++)
{
CreateCard(leftDownDeckTrans,i,0);
}
//右下
for (int i = createNum; i >0 ; i--)
{
CreateCard(rightDownDeckTrans, i, 0);
}
}
/// <summary>
/// 生成旁邊四疊卡牌
/// </summary>
/// <param name="zeroTrans"></param>
/// <param name="indexX"></param>
/// <param name="indexY"></param>
private void CreateCard(RectTransform zeroTrans,int indexX,int indexY)
{
GameObject go= Instantiate(cardGo,deckTrans);
RectTransform rft = go.GetComponent<RectTransform>();
rft.anchoredPosition = zeroTrans.anchoredPosition +
new Vector2(cardWidth*indexX*0.15f,cardHeight*indexY*0.15f);
Card card = go.GetComponent<Card>();
card.SetCardSprite();
SetCoverState(card);
cards.Add(card);
}
/// <summary>
/// 產生卡牌游戲物體
/// </summary>
private GameObject CreateCardGo(int column,int row,int dirX,int dirY)
{
GameObject go = Instantiate(cardGo, deckTrans);
go.GetComponent<RectTransform>().anchoredPosition
= centerDeckTrans.anchoredPosition +
new Vector2(cardWidth * (column + 0.5f * dirX), -cardHeight * (row + 0.5f * dirY));
return go;
}
/// <summary>
/// 設置當前新生成的卡牌與其他卡牌的覆蓋關系
/// </summary>
/// <param name="card"></param>
private void SetCoverState(Card card)
{
for (int i = 0; i < cards.Count; i++)
{
card.SetCoverCardState(cards[i]);
}
}
/// <summary>
/// 獲取當前撿牌堆的目標位置
/// </summary>
/// <param name="currentID">當前新選中的牌</param>
/// <param name="posID">格子位置ID索引</param>
/// <returns></returns>
public Transform GetPickDeckTargetTrans(int currentID, out int posID)
{
posID = -1;
for (int i = 0; i < pickDeckCardIDs.Length; i++)
{
//當前撿牌堆中的卡牌有沒有與新選中的卡牌ID相等
if (pickDeckCardIDs[i]==currentID&&i+1<=pickDeckCardIDs.Length)
{
posID = i + 1;
return pickDeckPosTrans[i + 1];
}
}
//當前撿牌堆沒有與新選卡牌相同的ID
Transform sf= GetEmptyPickDeckTargetTrans();
if (sf)
{
return sf;
}
return null;
}
/// <summary>
/// 獲取選中卡牌堆的空位置
/// </summary>
/// <param name="posID">當前選中卡牌堆的格子位置索引ID</param>
/// <returns></returns>
public Transform GetEmptyPickDeckTargetTrans(int posID=-1)
{
for (int i = 0; i < pickDeckCardIDs.Length; i++)
{
if (pickDeckCardIDs[i]==-1)
{
//pickDeckCardIDs[i] = posID;
//有兩個或兩個以下相同ID的卡牌,需要調整位置
if (posID!=-1)
{
pickDeckPosTrans[i].SetSiblingIndex(posID);
}
return pickDeckPosTrans[i];
}
}
//Debug.Log("游戲結束");
return null;
}
/// <summary>
/// 排序卡牌位置和ID
/// </summary>
public void SortCardAndCardID()
{
SortCard();
SortID();
}
/// <summary>
/// 卡牌排序
/// </summary>
public void SortCard()
{
Transform[] tempTrans = new Transform[pickDeckPosTrans.Length];
for (int i = 0; i < pickDeckPosTrans.Length; i++)
{
int siblingIndex= pickDeckPosTrans[i].GetSiblingIndex();
tempTrans[siblingIndex] = pickDeckPosTrans[i];
}
for (int i = 0; i < pickDeckPosTrans.Length; i++)
{
pickDeckPosTrans[i] = tempTrans[i];
}
}
/// <summary>
/// ID排序
/// </summary>
public void SortID()
{
for (int i = 0; i < pickDeckCardIDs.Length; i++)
{
if (pickDeckPosTrans[i].childCount > 0)
{
pickDeckCardIDs[i] = pickDeckPosTrans[i].GetChild(0).GetComponent<Card>().id;
}
else
{
pickDeckCardIDs[i] = -1;
}
}
}
/// <summary>
/// 卡牌消除判定方法
/// </summary>
public void JudgeClearCard()
{
SortCardAndCardID();
//清除判定
ClearCards();
Invoke("SortGridPos",0.1f);
}
/// <summary>
/// 消除
/// </summary>
public void ClearCards()
{
int sameCount = 0;
int judgeID = -1;
int startIndex = -1;
for (int i = 0; i <pickDeckCardIDs.Length; i++)
{
//空格子
if (pickDeckCardIDs[i]==-1)
{
break;
}
//當前三消判定開始的第一個元素
if (sameCount==0)
{
sameCount++;
judgeID = pickDeckCardIDs[i];
startIndex = i;
}
else
{
if (judgeID == pickDeckCardIDs[i])
{
sameCount++;
}
else
{
sameCount = 1;
judgeID = pickDeckCardIDs[i];
startIndex = i;
}
}
if (sameCount>=3)
{
for (int j = startIndex; j < startIndex+3; j++)
{
pickDeckCardIDs[j] = -1;
Destroy(pickDeckPosTrans[j].GetChild(0).gameObject);
}
PlayClickSound();
break;
}
if (i>= pickDeckCardIDs.Length-1)
{
gameOverPanelGo.SetActive(true);
}
}
}
/// <summary>
/// 排序消除后的格子位置(消除后補齊)
/// </summary>
private void SortGridPos()
{
for (int i = 0; i < pickDeckPosTrans.Length; i++)
{
if (pickDeckPosTrans[i].childCount<=0)
{
//空格子
pickDeckPosTrans[i].SetSiblingIndex(6);
}
}
SortCardAndCardID();
}
public void ReturnToMainScene()
{
SceneManager.LoadScene(1);
}
public void Replay()
{
SceneManager.LoadScene(2);
}
public void PlayClickSound()
{
audioSource.PlayOneShot(clickSound);
}
}
/// <summary>
/// 卡牌的生成狀態
/// </summary>
public enum CREATESTATE
{
NONE,//該位置不生成卡牌
CREATE,//生成并且位置可能偏移
RANDOM,//可能生成也可能偏移
ONLYCREATE//生成一定不偏移
}
SheepMove
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class SheepMove : MonoBehaviour
{
public RectTransform rt;
public Transform[] movePointsTrans;
private Vector3[] movePoints;
public bool loadScene;
private AsyncOperation ao;
// Start is called before the first frame update
void Start()
{
movePoints = new Vector3[movePointsTrans.Length];
for (int i = 0; i < movePoints.Length; i++)
{
movePoints[i] = movePointsTrans[i].localPosition;
}
if (loadScene)
{
ao = SceneManager.LoadSceneAsync(1);
ao.allowSceneActivation = false;
}
transform.DOLocalPath(movePoints, 3).SetEase(Ease.Linear).OnComplete
(
() =>
{
if (loadScene)
{
ao.allowSceneActivation = true;
}
}
);
}
// Update is called once per frame
void Update()
{
if (rt.anchoredPosition.y >= 0)
{
transform.eulerAngles = new Vector3(0, 180, 0);
}
else
{
transform.eulerAngles = Vector3.zero;
}
}
}
Sheeps
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;
using UnityEngine.SceneManagement;
public class Sheeps : MonoBehaviour
{
// Start is called before the first frame update
public Transform targetTrans;
private AsyncOperation ao;
public bool ifLoadGameScene;
void Start()
{
ao = SceneManager.LoadSceneAsync(2);
ao.allowSceneActivation = false;
transform.DOLocalMove(targetTrans.localPosition, 2).SetEase(Ease.Linear).OnComplete
(
OnCompeleteEvent
);
}
private void OnCompeleteEvent()
{
if (ifLoadGameScene)
{
ao.allowSceneActivation = true;
}
else
{
Destroy(gameObject);
}
}
}