Unity3D消消乐制作感想及部分代码:
1:首先记录下该项目用到的部分知识——》dwteen插件,队列(Queue)的使用,对象池的使用,二维数组的使用。
消消乐的核心是算法,(寻找相邻相同物体算法,下落算法)
寻找相邻相同算法,自我理解,该算法是泛洪算法(四邻域)利用递归将所有相邻相同物体存到一个list中,泛洪算法经常用于改变图片的颜色,但是不能用递归(用递归会造成内存溢出,后期会写出来,改变图片上的相邻相同颜色的值)
/// <summary>
/// 执行完成后,找到了所有和点击对象相邻,且具有相同sprite的对象
/// </summary>
public void FillSameItemsList(Item current)
{
//如果已存在,跳过
if (sameItemsList.Contains(current))
return;
//添加到列表
sameItemsList.Add(current);
//上下左右的Item
Item[] tempItemList = new Item[]{
_GetUpItem(current),_GetDownItem(current),
_GetLeftItem(current),_GetRightItem(current)};
for (int i = 0; i < tempItemList.Length; i++)
{
//如果Item不合法,跳过
if (tempItemList[i] == null)
continue;
if (current.indexTag == tempItemList[i].indexTag)
{
FillSameItemsList(tempItemList[i]);
}
}
}
/// <summary>
/// 获取上方Item
/// </summary>
/// <returns>The up item.</returns>
/// <param name="current">Current.</param>
private Item _GetUpItem(Item current)
{
int row = current.itemRow + 1;
int column = current.itemColumn;
if (!_CheckRCLegal(row, column))
return null;
return allItems[row, column];
}
/// <summary>
/// 获取下方Item
/// </summary>
/// <returns>The down item.</returns>
/// <param name="current">Current.</param>
private Item _GetDownItem(Item current)
{
int row = current.itemRow - 1;
int column = current.itemColumn;
if (!_CheckRCLegal(row, column))
return null;
return allItems[row, column];
}
/// <summary>
/// 获取左方Item
/// </summary>
/// <returns>The left item.</returns>
/// <param name="current">Current.</param>
private Item _GetLeftItem(Item current)
{
int row = current.itemRow;
int column = current.itemColumn - 1;
if (!_CheckRCLegal(row, column))
return null;
return allItems[row, column];
}
/// <summary>
/// 获取右方Item
/// </summary>
/// <returns>The right item.</returns>
/// <param name="current">Current.</param>
private Item _GetRightItem(Item current)
{
int row = current.itemRow;
int column = current.itemColumn + 1;
if (!_CheckRCLegal(row, column))
return null;
return allItems[row, column];
}
/// <summary>
/// 检测行列是否合法
/// </summary>
/// <returns><c>true</c>, if RC legal was checked, <c>false</c> otherwise.</returns>
/// <param name="itemRow">Item row.</param>
/// <param name="itemColumn">Item column.</param>
public bool _CheckRCLegal(int itemRow, int itemColumn)
{
if (itemRow >= 0 && itemRow < rowNum && itemColumn >= 0 && itemColumn < columnNum)
return true;
return false;
}
下落算法,逐列,逐个下落,利用队列存储需要下落的物体(使用队列的优点,先进先出—方便按照顺序逐个下落)
/// <summary>
/// 执行点击对象后的效果
/// </summary>
public void FillBoomList(Item current)
{
if (sameItemsList.Count < 3)
return;
boomList.AddRange(sameItemsList);
List<Item> tempBoomList = new List<Item>();
tempBoomList.AddRange(boomList);
StartCoroutine(ManipulateBoomList(tempBoomList, current));
}
IEnumerator ManipulateBoomList(List<Item> tempBoomList, Item current)
{
//将所有的物体都拿出来,进行效果展示
click_thenParent.transform.position = current.transform.position;
for (int i = 0; i < tempBoomList.Count; i++)
{
tempBoomList[i].transform.parent = click_thenParent.transform;
}
click_thenParent.transform.DOScale(1.1f, 0.3f).onComplete = delegate
{
click_thenParent.transform.DOScale(0f, 0.1f).onComplete = delegate
{
current.transform.parent = ItemParent;
current.transform.localScale = Vector3.one;
};
};
yield return new WaitForSeconds(0.4f);
//后期修改换成for循环
foreach (var item in tempBoomList)
{
//将被消除的Item在全局列表中移除
allItems[item.itemRow, item.itemColumn] = null;
//回收Item:可以先不回收,这样可以进行一系列的动画操作
SinglePool.instance.SetGameObject(item.gameObject);
item.transform.localScale = Vector3.one;
}
click_thenParent.transform.localScale = Vector3.one;
//开启下落
yield return StartCoroutine(ItemsDrop());
}
/// <summary>
/// 下落
/// </summary>
/// <returns>The drop.</returns>
public IEnumerator ItemsDrop()
{
//逐列检测
for (int i = 0; i < columnNum; i++)
{
//计数器
int count = 0;
//下落队列
Queue<Item> dropQueue = new Queue<Item>();
//逐行检测
for (int j = 0; j < rowNum; j++)
{
if (allItems[j, i] != null)
{
//计数
count++;
//放入队列
dropQueue.Enqueue(allItems[j, i]);
}
}
//当一列检查完毕后可以生成物体在上面
for (int m = 0; m < 7 - count; m++)
{
GameObject currentItem = SinglePool.instance.GetGameObject("Item", ItemParent);
currentItem.transform.localScale = Vector3.one;
Item currBtnItem = currentItem.GetComponent<Item>();
currBtnItem.itemRow = m;
currBtnItem.itemColumn = i;
int index = Random.Range(0, 2);
currBtnItem.indexTag = index;
currBtnItem.imgBg.sprite = bgSps[index];
currBtnItem.transform.position = InitPos[currBtnItem.itemRow, currBtnItem.itemColumn].position;
dropQueue.Enqueue(currBtnItem);
}
//下落
for (int k = 0; k < count; k++)
{
//获取要下落的Item
Item current = dropQueue.Dequeue();
//修改全局数组(原位置情况)
allItems[current.itemRow, current.itemColumn] = null;
//修改Item的行数
int tempRow = current.itemRow;
current.itemRow = k;
//修改全局数组(填充新位置)
allItems[current.itemRow, current.itemColumn] = current;
//下落
current._CurrentItemDrop(trueInitPos[current.itemRow, current.itemColumn].position);
}
for (int l = 0; dropQueue.Count>0; l++)
{
// print("Count: "+dropQueue.Count+" l:"+l);
//获取要下落的Item
Item current = dropQueue.Dequeue();
//修改Item的行数
current.itemRow = 6-l;
//修改全局数组(填充新位置)
allItems[current.itemRow, current.itemColumn] = current;
//下落
// print("row:"+current.itemRow+"column:"+current.itemColumn);
current._CurrentItemDrop(trueInitPos[current.itemRow, current.itemColumn].position);
}
}
yield break;
// print("创建新的对象");
// StartCoroutine(CreateNewItem());
}
下落之前需要在每列上方生成需要的物体,这里使用的是对象池管理对象,减少了生成销毁对象的步骤(这也算是优化了游戏)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using DG.Tweening;
public class SinglePool : MonoBehaviour
{
//单例
public static SinglePool instance;
//对象池
private Dictionary<string, List<GameObject>> pool;
void Awake()
{
instance = this;
pool = new Dictionary<string, List<GameObject>>();
}
/// <summary>
/// 对象池回收物体
/// </summary>
/// <param name="current">被回收的不需要的对象.</param>
public void SetGameObject(GameObject current)
{
//设置成非激活状态
current.SetActive(false);
//清空父对象
// current.transform.parent = null;
//是否有该类型的对象池
if (pool.ContainsKey(current.name))
{
//添加到对象池
pool[current.name].Add(current);
}
else
{
pool[current.name] = new List<GameObject>() { current };
}
}
/// <summary>
/// 对象池获取物体
/// </summary>
/// <returns>The game object.</returns>
/// <param name="objName">物体名字.</param>
/// <param name="parent">父对象名字.</param>
public GameObject GetGameObject(string objName, Transform parent = null)
{
GameObject current;
//包含此对象池,且有对象
string objName2 = objName + "(Clone)";
if (pool.ContainsKey(objName2) && pool[objName2].Count > 0)
{
//获取对象
// print("获取对象次中的对象");
current = pool[objName2][0];
current.transform.localScale = new Vector3(1, 1, 1);
pool[objName2].Remove(pool[objName2][0]);
}
else
{
//加载预设体
GameObject prefab = Resources.Load<GameObject>("Prefabs/" + objName);
//生成
current = Instantiate(prefab) as GameObject;
}
//设置激活状态
current.SetActive(true);
//设置父物体
current.transform.parent = parent;
//返回
return current;
}
}
至于加分过关等代码都是简单的添加就可以了。最主要的是上面的一些代码。
对我来说,容易忘记的是下落算法的实现(这里利用的是逐个下落的方式,这也算是个很好的方法)
原工程版本2018.2.7,需要的小伙伴可以到我的资源里下载。
链接: https://pan.baidu.com/s/1EbkNjfU5V2QzntmKk8d1fA 提取码: vd3s 复制这段内容后打开百度网盘手机App,操作更方便哦
实现效果:
这里有一个qq群:群号:319506028,名字:Unity、AR、VR俱乐部,里面有很多技术大神,可以在群里问问题(关于unity的任何问题,不限VR AR,群名不重要),欢迎你的加入。