Unity制作卡牌游戏
我的第一个unity项目是卡牌游戏,是看着慕课网以及用同学分享的项目资源跟着老师做的。慕课网课程的链接,希望老师允许我把课程的链接放在这里,宁静方致远 分享的项目,内含素材,希望他们可以同意把他们的链接放在这里。游戏功能是判断两张翻转的卡牌是否相同,如果相同就消掉两张卡牌,不相同就把卡牌翻转到背面,游戏共有三个关卡,分别有23、24,2*5张卡牌。下面是游戏的界面1.panel_start界面..
我的第一个unity项目是卡牌游戏,是看着慕课网以及用同学分享的项目资源跟着老师做的。慕课网课程的链接,希望老师允许我把课程的链接放在这里,宁静方致远 分享的项目,内含素材,希望他们可以同意把他们的链接放在这里。
游戏功能是判断两张翻转的卡牌是否相同,如果相同就消掉两张卡牌,不相同就把卡牌翻转到背面,游戏共有三个关卡,分别有23、24,2*5张卡牌。下面是游戏的界面
1.panel_start界面
2.panel_card界面
3.panel_over界面
项目的构建过程
1.先建一个unity的3D项目,然后在File——>Build Setting中建一个叫game的Scenes,后期build生成apk的时候用。
2.建目录,在Project window下的Assets(资源)下建各种文件夹用于组织各种资源。我建了四个目录,第一个是Resources,用于保存项目需要的静态资源(图片);Scenes目录,用于用于放置unity场景文件,方便大包时引用;Script目录用于保存文件脚本(C#文件);Testure用于也是用于保存游戏需要的图片。然后把需要的资源导入相应的目录,把Txeture Type(纹理类型)设置为Sprite(2D and UI)
3.在game场景下建一个画布Canvas,在Canvas下建一个个panel,设置其layer为UI,在把Testure下的游戏背景图拖进Source Image,点击set native size,这样一个有图片的panel就弄好了。因为都是同一个背景,所以可以通过Ctrl+d复制panel两次,分别取名Panelstart(游戏开始的主界面),PanelCard(卡牌界面),PanelOver(结束界面)。
4.在PanelStart 下面建一个Button,设置layer为UI,在把Testure下的level1拖进Source Image,点击set native size,在自己调整合适的Button大小。Ctrl复制两次,分别取名ButtonLevel1(关卡一的按钮)、ButtonLevel2(关卡二的按钮)ButtonLevel3(关卡三的按钮),然后在更改ButtonLevel2,ButtonLevel3的Source Image为Level2,Level3图片。通过同样的步骤建立如下的机构,Image_Back是卡牌的背面,Image_front是卡牌的正面,Button_to_start是一个关卡通过后从新回到PanelStart的按钮,Button_to_end关闭游戏。
4.Script下建立两个脚本,分别是叫gameMain,CardFlipAnimtionCtrl。
gameMain的代码:
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class gameMain : MonoBehaviour//只有继承于monobehavior 类的游戏对象才可以直接在unity 中运行
{
public Button btnLevel1;
public Button btnLevel2;
public Button btnLevel3;
public Button Button_to_start;
public Button Button_to_end;
public Transform panelStart;
public Transform panelCard;
public Transform panelOver;
// Start is called before the first frame update
void Start()// 从MonoBehaviour继承的方法,start在MonoBehaviour的生命周期中只会被执行一次
{
//跳转到关卡一
btnLevel1.onClick.AddListener(() =>
{
panelStart.gameObject.SetActive(false);//设置panelStart为不可用,不显示panelStart
panelCard.gameObject.SetActive(true);//设置panelCard为可用
LoadLevelCard(3,2);
});
btnLevel2.onClick.AddListener(() =>
{
panelStart.gameObject.SetActive(false);
panelCard.gameObject.SetActive(true);
LoadLevelCard(4,2);
});
btnLevel3.onClick.AddListener(() =>
{
panelStart.gameObject.SetActive(false);
panelCard.gameObject.SetActive(true);
LoadLevelCard(5,2);
});
//跳转到游戏开始的页面,关闭游戏卡牌界面和游戏结束见面
Button_to_start.onClick.AddListener(() => {
panelStart.gameObject.SetActive(true);
panelCard.gameObject.SetActive(false);
panelOver.gameObject.SetActive(false);
});
//关闭游戏
Button_to_end.onClick.AddListener(() => {
Application.Quit();
});
}
//跳转到游戏开始界面,即观其选择的那个界面
private void ToGameStartPage()
{
panelStart.gameObject.SetActive(true);
panelCard.gameObject.SetActive(false);
panelOver.gameObject.SetActive(false);
}
//为levelCrad界面加载卡牌
void LoadLevelCard(int width,int height)//width:卡牌的一行的数量;heigth:卡牌一列的数量
{
//1.加载卡牌图片
Sprite[] sps = Resources.LoadAll<Sprite>("");//加载根目录下的资源,加载Assets下Resources目录下的资源,这个目录名字必须是Resources,卡牌的格式都被设置为Sprite(2D and UI),所以泛型的参数是Sprite
/*
* for(int i=0;i<sps.Length;i++)
{
Debug.LogError(sps[i].name);//在Unity控制台打印数据的名称
}
*/
//2.计算需要加载卡牌的数量,因为每一种牌有两张,所以实际上只需要一半不同的卡牌
int totalCount = width * height / 2;
//3.计算随机加载卡牌的索引
List<Sprite> spsList = new List<Sprite>();
//把数组里的数据存到列表里,列表可以调用RemoveAt()删除已经加载的卡牌索引,这样就不会加载同样的图片
for (int i = 0; i < sps.Length; i++)
{
spsList.Add(sps[i]);
}
List<Sprite> needShowCardList = new List<Sprite>();
while(totalCount>0)
{
int randomIndex = UnityEngine.Random.Range(0, spsList.Count);
needShowCardList.Add(spsList[randomIndex]);//需要加载两个
needShowCardList.Add(spsList[randomIndex]);
spsList.RemoveAt(randomIndex);//删除已经加载的图片
totalCount--;
}
//高等级通关后在玩低等级要销毁对象,且解除关联
Transform contentRoot = panelCard.Find("Panel");//获取panelCard下的Panel节点的对象
for (int i = 1; i < contentRoot.childCount; ++i)
{
GameObject itemTemp = contentRoot.GetChild(i).gameObject;
Sprite ss = itemTemp.transform.Find("Image_front").GetComponent<Image>().sprite;//获取Image_front的对象
Debug.Log(i + "," + ss.name);//打印Image_front对象的名字
itemTemp.transform.SetParent(null);//把itemTemp的父节点设置为null
Destroy(itemTemp);//删除itemTemp
}
//4.显示卡牌到UI上
int maxCount = Mathf.Max(contentRoot.childCount, needShowCardList.Count);//比较panel这个节点的子节点的数量和需要展示的卡牌数量的大小
GameObject itemPrefab = contentRoot.GetChild(0).gameObject;//获取容器的第一个节点
for(int i=0;i<maxCount; i++)
{
int index = Random.Range(0, needShowCardList.Count);
GameObject itemObject = null;
if(i<contentRoot.childCount)
{
itemObject = contentRoot.GetChild(i).gameObject;//用容器原有的子节点对象给空的对象引用赋值
}
else
{
itemObject = GameObject.Instantiate<GameObject>(itemPrefab);//克隆对象
itemObject.transform.SetParent(contentRoot, false);//将itemObject的父节点设置为contentRoot,也就是把itemObject添加到panel下
}
itemObject.transform.Find("Image_front").GetComponent<Image>().sprite = needShowCardList[index];//给图片资源对象赋给sprite属性
needShowCardList.RemoveAt(index);//从需要展示的列表里删除已经赋给sprite的图片资源
CardFlipAnimtionCtrl cardAniCtrl = itemObject.GetComponent<CardFlipAnimtionCtrl>();
cardAniCtrl.SetDefaultState();//设置卡牌默认初始状态
}
GridLayoutGroup glg = contentRoot.GetComponent<GridLayoutGroup>();//获取contentRoot(panel的对象)的网格布局组的组件
/*自动计算panel节点的宽高
*宽=元素的个数*元素的宽+元素距离左边的距离+元素距离右边的距离
+(元素个数-1 )*元素的间距x
**/
float panelWidth = width * glg.cellSize.x + glg.padding.left +
glg.padding.right + (width - 1) * glg.spacing.x;
float panelHeight = height * glg.cellSize.y + glg.padding.top +
glg.padding.bottom + (height - 1) * glg.spacing.y;
contentRoot.GetComponent<RectTransform>().sizeDelta =
new Vector2(panelWidth, panelHeight);//给contenRoot的大小赋值
}
//判断玩家是否通关
public void CheckIsGameOver()
{
CardFlipAnimtionCtrl[] allCards= GameObject.FindObjectsOfType<CardFlipAnimtionCtrl>();//首相获取所有对象,找的方法是通过全局检查,找的类型是卡牌动画脚本。
if(allCards!=null&&allCards.Length>0)
{
List<CardFlipAnimtionCtrl> cardInFront = new List<CardFlipAnimtionCtrl>(); //这个list是用来存储遍历过程中翻到正面的卡牌,快捷键:选中一个变量,按f12,快速跳到这变量所在个的位置
for(int i=0;i<allCards.Length;i++)
{
CardFlipAnimtionCtrl cardTem = allCards[i];
if(cardTem.isInFront && !cardTem.isOver)
{
cardInFront.Add(cardTem);
}
if(cardInFront.Count>=2)//当翻转的两张牌了两张牌以上
{
string cardImageName1 = cardInFront[0].getCradImageName();
string cardImageName2 = cardInFront[1].getCradImageName();
if(cardImageName1==cardImageName2)
{
cardInFront[0].MatchSuccess();//需要做的事情为,把这两张牌标记为匹配结束状态
cardInFront[1].MatchSuccess();
}
else
{
cardInFront[0].MatchFail();//把卡牌翻转到反面
cardInFront[1].MatchFail();
}
bool isAllOver =true;
for (int j=0;j<allCards.Length;j++)
{
isAllOver &= allCards[j].isOver;//当数组中的CardFlipAnimtionCtrl的isOver为ture时isAllover才为ture
}
if(isAllOver)
{
ToGameOverPage();
}
break;
}
}
}
}
private void ToGameOverPage()
{
panelStart.gameObject.SetActive(false);
panelCard.gameObject.SetActive(false);
panelOver.gameObject.SetActive(true);
}
//ctrl+—号快速返回原来的位置
}
在脚本里声明的的button变量只有和button按钮关联才会有作用,关联的方法:第一种方法点击Main Camera,在将对应的button按钮拖到对应的变量上面
后者点击变量旁边的小圆圈,在Select Button选择对应的Button
为panel添加Grid Layout Group组件:点击add Component,在弹出的搜索框中搜索Grid Layout Group
Grid Layout Group是为了组织卡牌元素。
CardFlipAnimationCtrl(卡牌翻转动画控制)代码
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
//封装卡牌的属性和操作
public class CardFlipAnimtionCtrl : MonoBehaviour,IPointerClickHandler
{
Transform cardFront;//卡牌的正面
Transform cardBack;//卡牌的反面
float flipDuaration=0.2f;
public bool isInFront = false;//卡牌是否正面朝上
public bool isOver=false;//正面朝上的卡牌是否匹配成功
public void OnPointerClick(PointerEventData eventData)//当鼠标点击触发的事件
{
if (isOver) return;//如果已经配对成功就不需要任何操作了
if (!isInFront)
{
StartCoroutine(FlipCardToFront());//将翻转到正面
}
else
{
StartCoroutine(FlipCardToBack());
}
}
//把卡牌翻转到正面
IEnumerator FlipCardToFront()
{
//1.翻转反面到90度
cardFront.gameObject.SetActive(false);
cardBack.gameObject.SetActive(true);
cardFront.rotation = Quaternion.identity;//卡牌初始角度,Quaternion.identity相当于Quaternion.Euler(0,0,0)
while (cardBack.rotation.eulerAngles.y < 90)//欧拉角小于90°的时候,一直执行
{
cardBack.rotation *= Quaternion.Euler(0, Time.deltaTime*90f*(1f/flipDuaration), 0);//0.2秒内翻转90°
if(cardBack.rotation.eulerAngles.y>90)//当欧拉角大90°时设置为90°
{
cardBack.rotation = Quaternion.Euler(0, 90, 0);
break;
}
yield return new WaitForFixedUpdate();
}
//翻转卡牌到正面
cardFront.gameObject.SetActive(true);
cardBack.gameObject.SetActive(false);
cardFront.rotation = Quaternion.Euler(0, 90, 0);//设置卡牌初始状态的欧拉角为90度
while(cardFront.rotation.eulerAngles.y>0)
{
cardFront.rotation *= Quaternion.Euler(0, -Time.deltaTime * 90f * (1f / flipDuaration),0);
if(cardFront.rotation.eulerAngles.y>90)
{
cardFront.rotation = Quaternion.Euler(0, 0, 0);
break;
}
yield return new WaitForFixedUpdate();
}
isInFront = true;
Camera.main.GetComponent<gameMain>().CheckIsGameOver();//每次将牌翻转到正面都要调用gameMain类中的CheckIsGameOver()的方法判断是不是所有的开牌都匹配成功
//应为这个脚本是放在主相机,通过主相机的对象可以获取这个脚本的对象,然后获取其方法。
}
//把卡牌翻转到反面
IEnumerator FlipCardToBack()
{
//翻转正面到90°,翻转反面到0°
cardFront.gameObject.SetActive(true);
cardBack.gameObject.SetActive(false);
cardBack.rotation = Quaternion.identity;
while(cardFront.rotation.eulerAngles.y<90)
{
cardFront.rotation *= Quaternion.Euler(0, Time.deltaTime * 90f * (1f / flipDuaration), 0);
if(cardFront.rotation.eulerAngles.y>90)
{
cardFront.rotation = Quaternion.Euler(0, 90, 0);
break;
}
yield return new WaitForFixedUpdate();
}
cardFront.gameObject.SetActive(false);
cardBack.gameObject.SetActive(true);
while(cardBack.rotation.eulerAngles.y>0)
{
cardBack.rotation *= Quaternion.Euler(0, -Time.deltaTime * 90f * (1f / flipDuaration), 0);
if(cardBack.rotation.eulerAngles.y>90)
{
cardBack.rotation = Quaternion.Euler(0, 0, 0);
break;
}
yield return new WaitForFixedUpdate();
}
isInFront = false;
}
// Start is called before the first frame update
//初始化cardFront,cardBack
void Start()
{
cardFront = transform.Find("Image_front");
cardBack = transform.Find("Image_back");
}
// Update is called once per frame
void Update()
{
}
//得到卡牌的名字
internal string getCradImageName()
{
return cardFront.GetComponent<Image>().sprite.name;
}
//两只卡牌匹配成功
internal void MatchSuccess()
{
isOver = true;//卡牌匹配成功
cardFront.gameObject.SetActive(false);
cardBack.gameObject.SetActive(false);
}
//两张卡牌匹配失败
internal void MatchFail()
{
StartCoroutine(FlipCardToBack());//将卡牌翻转为反面朝上
}
//设置卡牌的默认状态即初始状态
internal void SetDefaultState()
{
isInFront = false;
isOver = false;
if (cardFront != null)
{
cardFront.gameObject.SetActive(false);
cardFront.rotation = Quaternion.identity;
}
if (cardBack != null)
{
cardBack.gameObject.SetActive(true);
cardBack.rotation = Quaternion.identity;
}
}
}
更多推荐
所有评论(0)