甜品消消乐游戏
(脚本源码在游戏UI设计最下方)
三消游戏,存在L型消除有一点小Bug,当甜品在饼干附近消除时会清除饼干
饼干作为游戏障碍物时不可移动的,多块饼干并排时会挡住甜品掉落
发现消除类游戏的算法还是比较复杂的
游戏小道具因算法绕的我头有点晕就没有实现
甜品掉落速度可以在GameManager游戏管理类上设置fill Time值(我这里是0.25)
emm,游戏开始界面有点low,未添加渲染动画
游戏项目已托管到Github上 传送门
甜品消消乐01_游戏基础界面 传送门
甜品消消乐02_游戏核心算法 传送门
甜品消消乐03_游戏UI设计 传送门
(文章最下边有游戏脚本)
实现过程
素材导入,基本预制体的制作
通过Gizmos影藏Scene场景中小摄像机
(点击摄像机进行关闭)
新建一个GameObject->New Sprite,甜甜圈拖动上Sprite上,将游戏分层开发(分为模型层和渲染层)
当不知道甜甜圈尺寸时,可以新建一个3D Object->Cube,Cube默认长宽比例为1m
将甜甜圈长宽缩放比例0.45
将甜甜圈做成预设体
制作格子背景
设置格子缩放比例为0.65
为设置格子背景在甜甜圈背面,将Gird背景的Order in Layout设置为-1
将Gird设置成预设体
(不需要用到Cube我就把它删除了)
游戏管理的创建,巧克力的生成
新建一个GameObject->New Sprite,制作游戏背景
(背景比摄像机范围稍微大一些,放曝光)
添加GameManager游戏管理类脚本,挂在到GameObject(GameManager)上
using System.Collections; using System.Collections.Generic; using UnityEngine; public class GameManager : MonoBehaviour { //单例实例化 private static GameManager _instance; public static GameManager Instance { get { return _instance; } set { _instance = value; } } //大网格的行列数 public int xColumn; public int yRow; public GameObject gridPrefab; private void Awake() { _instance = this; } // Use this for initialization void Start () { for(int x = 0; x < xColumn; x++) { for (int y=0;y<yRow;y++) { GameObject chocolate = Instantiate(gridPrefab,new Vector3(x,y,0),Quaternion.identity); chocolate.transform.SetParent(transform); } } } // Update is called once per frame void Update () { } }
巧克力块游戏背景
大网格的行列数
public int xColumn; public int yRow;
public GameObject gridPrefab; void Start () { for(int x = 0; x < xColumn; x++) { for (int y=0;y<yRow;y++) { GameObject chocolate = Instantiate(gridPrefab,new Vector3(x,y,0),Quaternion.identity); chocolate.transform.SetParent(transform); } } }
(为了使背景在巧克力块后面,设置background的Order in Layout 为 -2)
实际需要实例化巧克力的X位置 = GameManager位置的X坐标-大网格长度的一半+行列对应的X坐标
实际需要实例化巧克力的Y位置 = GameManager位置的Y坐标-大网格长度的一半+行列对应的Y坐标
调整巧克力块背景
public Vector3 CorrectPosition(int x ,int y) { //实际需要实例化巧克力的X位置 = GameManager位置的X坐标-大网格长度的一半+行列对应的X坐标 // 实际需要实例化巧克力的Y位置 = GameManager位置的Y坐标-大网格长度的一半+行列对应的Y坐标 return new Vector3(transform.position.x-xColumn/2f+x,transform.position.y+yRow/2f-y); }
。
using System.Collections; using System.Collections.Generic; using UnityEngine; public class GameManager : MonoBehaviour { //单例实例化 private static GameManager _instance; public static GameManager Instance { get { return _instance; } set { _instance = value; } } //大网格的行列数 public int xColumn; public int yRow; public GameObject gridPrefab; private void Awake() { _instance = this; } // Use this for initialization void Start () { for(int x = 0; x < xColumn; x++) { for (int y=0;y<yRow;y++) { GameObject chocolate = Instantiate(gridPrefab,CorrectPosition(x,y),Quaternion.identity); chocolate.transform.SetParent(transform); } } } // Update is called once per frame void Update () { } public Vector3 CorrectPosition(int x ,int y) { //实际需要实例化巧克力的X位置 = GameManager位置的X坐标-大网格长度的一半+行列对应的X坐标 // 实际需要实例化巧克力的Y位置 = GameManager位置的Y坐标-大网格长度的一半+行列对应的Y坐标 return new Vector3(transform.position.x-xColumn/2f+x,transform.position.y+yRow/2f-y); } }
甜品的类型枚举,结构体,字典的创建
甜品的种类
public enum SweetsType { EMPTY, NORMAL, BARRIER, ROE_CLEAR, COLUMN_CLEAR, RAINBOWCANDY, COUNT //标记类型 }
甜品预制体的字典,我们可以通过甜品的种类来得到对应的甜品游戏物体
public Dictionary<SweetsType, GameObject> sweetPrefabDict; [System.Serializable] public struct SweetPrefab { public SweetsType type; public GameObject prefab; } public SweetPrefab[] sweetPrefabs;
字典的实例化
sweetPrefabDict = new Dictionary<SweetsType, GameObject>(); for(int i=0;i<sweetPrefabs.Length;i++) { if (sweetPrefabDict.ContainsKey(sweetPrefabs[i].type)) { sweetPrefabDict.Add(sweetPrefabs[i].type,sweetPrefabs[i].prefab); } }
甜品的生成
甜品数组
private GameObject[,] sweets;
void Start () { //字典的实例化 sweetPrefabDict = new Dictionary<SweetsType, GameObject>(); for(int i=0;i<sweetPrefabs.Length;i++) { if (!sweetPrefabDict.ContainsKey(sweetPrefabs[i].type)) { sweetPrefabDict.Add(sweetPrefabs[i].type,sweetPrefabs[i].prefab); } } for(int x = 0; x < xColumn; x++) { for (int y=0;y<yRow;y++) { GameObject chocolate = Instantiate(gridPrefab,CorrectPosition(x,y),Quaternion.identity); chocolate.transform.SetParent(transform); } } sweets = new GameObject[xColumn, yRow]; for (int x = 0; x < xColumn; x++) { for (int y = 0; y < yRow; y++) { sweets[x,y] = Instantiate(sweetPrefabDict[SweetsType.NORMAL], CorrectPosition(x, y), Quaternion.identity); sweets[x,y].transform.SetParent(transform); } } }
using System.Collections; using System.Collections.Generic; using UnityEngine; public class GameSweet : MonoBehaviour { private int x; public int X { get { return x; } set { x = value; } } private int y; public int Y { get { return y; } set { y = value; } } private GameManager.SweetsType type; public GameManager.SweetsType Type { get { return type; } } public GameManager gameManager; public void Init(int _x,int _y,GameManager _gameManager,GameManager.SweetsType _type) { x = _x; y = _y; gameManager = _gameManager; type = _type; } }
using System.Collections; using System.Collections.Generic; using UnityEngine; public class GameManager : MonoBehaviour { //甜品的种类 public enum SweetsType { EMPTY, NORMAL, BARRIER, ROE_CLEAR, COLUMN_CLEAR, RAINBOWCANDY, COUNT //标记类型 } //甜品预制体的字典,我们可以通过甜品的种类来得到对应的甜品游戏物体 public Dictionary<SweetsType, GameObject> sweetPrefabDict; [System.Serializable] public struct SweetPrefab { public SweetsType type; public GameObject prefab; } public SweetPrefab[] sweetPrefabs; //单例实例化 private static GameManager _instance; public static GameManager Instance { get { return _instance; } set { _instance = value; } } //大网格的行列数 public int xColumn; public int yRow; public GameObject gridPrefab; //甜品数组 private GameSweet[,] sweets; private void Awake() { _instance = this; } // Use this for initialization void Start () { //字典的实例化 sweetPrefabDict = new Dictionary<SweetsType, GameObject>(); for(int i=0;i<sweetPrefabs.Length;i++) { if (!sweetPrefabDict.ContainsKey(sweetPrefabs[i].type)) { sweetPrefabDict.Add(sweetPrefabs[i].type,sweetPrefabs[i].prefab); } } for(int x = 0; x < xColumn; x++) { for (int y=0;y<yRow;y++) { GameObject chocolate = Instantiate(gridPrefab,CorrectPosition(x,y),Quaternion.identity); chocolate.transform.SetParent(transform); } } sweets = new GameSweet[xColumn, yRow]; for (int x = 0; x < xColumn; x++) { for (int y = 0; y < yRow; y++) { GameObject newSweet = Instantiate(sweetPrefabDict[SweetsType.NORMAL], CorrectPosition(x, y), Quaternion.identity); newSweet.transform.SetParent(transform); sweets[x,y] = newSweet.GetComponent<GameSweet>(); sweets[x, y].Init(x, y, this, SweetsType.NORMAL); } } } // Update is called once per frame void Update () { } public Vector3 CorrectPosition(int x ,int y) { //实际需要实例化巧克力的X位置 = GameManager位置的X坐标-大网格长度的一半+行列对应的X坐标 // 实际需要实例化巧克力的Y位置 = GameManager位置的Y坐标-大网格长度的一半+行列对应的Y坐标 return new Vector3(transform.position.x-xColumn/2f+x,transform.position.y+yRow/2f-y); } }
甜品移动
添加甜品移动脚本
using System.Collections; using System.Collections.Generic; using UnityEngine; public class MovedSweet : MonoBehaviour { private GameSweet sweet; private void Awake() { sweet = GetComponent<GameSweet>(); } public void Move(int newX,int newY) { sweet.X = newX; sweet.Y = newY; sweet.transform.localPosition = sweet.gameManager.CorrectPosition(newX, newY); } }
using System.Collections; using System.Collections.Generic; using UnityEngine; public class GameSweet : MonoBehaviour { private int x; private int y; public int X { get { return x; } set { if (CanMove()) { x = value; } } } public int Y { get { return y; } set { if (CanMove()) { y = value; } } } private GameManager.SweetsType type; public GameManager.SweetsType Type { get { return type; } } [HideInInspector] public GameManager gameManager; public MovedSweet MovedComponet { get { return movedComponet; } } private MovedSweet movedComponet; //判断甜品是否可以移动 public bool CanMove() { return movedComponet != null; } public void Init(int _x,int _y,GameManager _gameManager,GameManager.SweetsType _type) { x = _x; y = _y; gameManager = _gameManager; type = _type; } }
判断甜品是否可以移动
public bool CanMove() { return movedComponet != null; }
private int x; private int y; public int X { get { return x; } set { if (CanMove()) { x = value; } } } public int Y { get { return y; } set { if (CanMove()) { y = value; } } }
移动的测试
测试甜品移动到Vector.zero位置处
using System.Collections; using System.Collections.Generic; using UnityEngine; public class MovedSweet : MonoBehaviour { private GameSweet sweet; private void Awake() { sweet = GetComponent<GameSweet>(); } public void Move(int newX,int newY) { sweet.X = newX; sweet.Y = newY; sweet.transform.localPosition = sweet.gameManager.CorrectPosition(newX, newY); } }
using System.Collections; using System.Collections.Generic; using UnityEngine; public class GameSweet : MonoBehaviour { private int x; private int y; public int X { get { return x; } set { if (CanMove()) { x = value; } } } public int Y { get { return y; } set { if (CanMove()) { y = value; } } } private GameManager.SweetsType type; public GameManager.SweetsType Type { get { return type; } } [HideInInspector] public GameManager gameManager; public MovedSweet MovedComponet { get { return movedComponet; } } private MovedSweet movedComponet; //判断甜品是否可以移动 public bool CanMove() { return movedComponet != null; } private void Awake() { movedComponet = GetComponent<MovedSweet>(); } public void Init(int _x,int _y,GameManager _gameManager,GameManager.SweetsType _type) { x = _x; y = _y; gameManager = _gameManager; type = _type; } }
using System.Collections; using System.Collections.Generic; using UnityEngine; public class GameManager : MonoBehaviour { //甜品的种类 public enum SweetsType { EMPTY, NORMAL, BARRIER, ROE_CLEAR, COLUMN_CLEAR, RAINBOWCANDY, COUNT //标记类型 } //甜品预制体的字典,我们可以通过甜品的种类来得到对应的甜品游戏物体 public Dictionary<SweetsType, GameObject> sweetPrefabDict; [System.Serializable] public struct SweetPrefab { public SweetsType type; public GameObject prefab; } public SweetPrefab[] sweetPrefabs; //单例实例化 private static GameManager _instance; public static GameManager Instance { get { return _instance; } set { _instance = value; } } //大网格的行列数 public int xColumn; public int yRow; public GameObject gridPrefab; //甜品数组 private GameSweet[,] sweets; private void Awake() { _instance = this; } // Use this for initialization void Start () { //字典的实例化 sweetPrefabDict = new Dictionary<SweetsType, GameObject>(); for(int i=0;i<sweetPrefabs.Length;i++) { if (!sweetPrefabDict.ContainsKey(sweetPrefabs[i].type)) { sweetPrefabDict.Add(sweetPrefabs[i].type,sweetPrefabs[i].prefab); } } for(int x = 0; x < xColumn; x++) { for (int y=0;y<yRow;y++) { GameObject chocolate = Instantiate(gridPrefab,CorrectPosition(x,y),Quaternion.identity); chocolate.transform.SetParent(transform); } } sweets = new GameSweet[xColumn, yRow]; for (int x = 0; x < xColumn; x++) { for (int y = 0; y < yRow; y++) { // CorrectPosition(x, y) GameObject newSweet = Instantiate(sweetPrefabDict[SweetsType.NORMAL],Vector3.zero,Quaternion.identity); newSweet.transform.SetParent(transform); sweets[x,y] = newSweet.GetComponent<GameSweet>(); sweets[x, y].Init(x, y, this, SweetsType.NORMAL); // if (sweets[x, y].CanMove()) //{ // sweets[x, y].MovedComponet.Move(); // } } } } // Update is called once per frame void Update () { } public Vector3 CorrectPosition(int x ,int y) { //实际需要实例化巧克力的X位置 = GameManager位置的X坐标-大网格长度的一半+行列对应的X坐标 // 实际需要实例化巧克力的Y位置 = GameManager位置的Y坐标-大网格长度的一半+行列对应的Y坐标 return new Vector3(transform.position.x-xColumn/2f+x,transform.position.y+yRow/2f-y); } }
甜品颜色
添加脚本颜色脚本ColorSweet.cs
通过更改Sweet下的Sprite来更改甜品的种类
using System.Collections; using System.Collections.Generic; using UnityEngine; public class ColorSweet : MonoBehaviour { public enum ColorType { YELLOW, PUPLE, RED, BLUE, GREEN, PNGK, ANY, COUNT } [System.Serializable] public struct ColorSprite { public ColorType color; public Sprite sprite; } public ColorSprite[] colorSprites; private Dictionary<ColorType, Sprite> colorSpriteDict; }
public enum ColorType { YELLOW, PUPLE, RED, BLUE, GREEN, PNGK, ANY, COUNT }
每一种颜色对应一种甜品类型
using System.Collections; using System.Collections.Generic; using UnityEngine; public class GameManager : MonoBehaviour { //甜品的种类 public enum SweetsType { EMPTY, NORMAL, BARRIER, ROE_CLEAR, COLUMN_CLEAR, RAINBOWCANDY, COUNT //标记类型 } //甜品预制体的字典,我们可以通过甜品的种类来得到对应的甜品游戏物体 public Dictionary<SweetsType, GameObject> sweetPrefabDict; [System.Serializable] public struct SweetPrefab { public SweetsType type; public GameObject prefab; } public SweetPrefab[] sweetPrefabs; //单例实例化 private static GameManager _instance; public static GameManager Instance { get { return _instance; } set { _instance = value; } } //大网格的行列数 public int xColumn; public int yRow; public GameObject gridPrefab; //甜品数组 private GameSweet[,] sweets; private void Awake() { _instance = this; } // Use this for initialization void Start () { //字典的实例化 sweetPrefabDict = new Dictionary<SweetsType, GameObject>(); for(int i=0;i<sweetPrefabs.Length;i++) { if (!sweetPrefabDict.ContainsKey(sweetPrefabs[i].type)) { sweetPrefabDict.Add(sweetPrefabs[i].type,sweetPrefabs[i].prefab); } } for(int x = 0; x < xColumn; x++) { for (int y=0;y<yRow;y++) { GameObject chocolate = Instantiate(gridPrefab,CorrectPosition(x,y),Quaternion.identity); chocolate.transform.SetParent(transform); } } sweets = new GameSweet[xColumn, yRow]; for (int x = 0; x < xColumn; x++) { for (int y = 0; y < yRow; y++) { // CorrectPosition(x, y) GameObject newSweet = Instantiate(sweetPrefabDict[SweetsType.NORMAL],Vector3.zero,Quaternion.identity); newSweet.transform.SetParent(transform); sweets[x,y] = newSweet.GetComponent<GameSweet>(); sweets[x, y].Init(x, y, this, SweetsType.NORMAL); if (sweets[x, y].CanMove()) { sweets[x, y].MovedComponet.Move(x,y); } if (sweets[x, y].CanColor()) { sweets[x, y].ColorComponet.SetColor((ColorSweet.ColorType)(Random.Range(0, sweets[x, y].ColorComponet.NumColors))); } } } } // Update is called once per frame void Update () { } public Vector3 CorrectPosition(int x ,int y) { //实际需要实例化巧克力的X位置 = GameManager位置的X坐标-大网格长度的一半+行列对应的X坐标 // 实际需要实例化巧克力的Y位置 = GameManager位置的Y坐标-大网格长度的一半+行列对应的Y坐标 return new Vector3(transform.position.x-xColumn/2f+x,transform.position.y+yRow/2f-y); } }
当甜品可以移动时,甜品可以进行移动
if (sweets[x, y].CanMove()) { sweets[x, y].MovedComponet.Move(x,y); }
当甜品存在颜色使填充随机色
if (sweets[x, y].CanColor()) { sweets[x, y].ColorComponet.SetColor((ColorSweet.ColorType)(Random.Range(0, sweets[x, y].ColorComponet.NumColors))); }
sweets = new GameSweet[xColumn, yRow]; for (int x = 0; x < xColumn; x++) { for (int y = 0; y < yRow; y++) { // CorrectPosition(x, y) GameObject newSweet = Instantiate(sweetPrefabDict[SweetsType.NORMAL],Vector3.zero,Quaternion.identity); newSweet.transform.SetParent(transform); sweets[x,y] = newSweet.GetComponent<GameSweet>(); sweets[x, y].Init(x, y, this, SweetsType.NORMAL); //当甜品可以移动时,甜品可以进行移动 if (sweets[x, y].CanMove()) { sweets[x, y].MovedComponet.Move(x,y); } //当甜品存在颜色使填充随机色 if (sweets[x, y].CanColor()) { sweets[x, y].ColorComponet.SetColor((ColorSweet.ColorType)(Random.Range(0, sweets[x, y].ColorComponet.NumColors))); } } }
空甜品的生成
创建空游戏物体预制体
产生甜品的方法
public GameSweet CreateNewSweet(int x,int y,SweetsType type) { GameObject newSweet = Instantiate(sweetPrefabDict[type], CorrectPosition(x, y), Quaternion.identity); newSweet.transform.parent = transform; sweets[x, y] = newSweet.GetComponent<GameSweet>(); sweets[x, y].Init(x,y,this,type); return sweets[x, y]; }
void Start () { //字典的实例化 sweetPrefabDict = new Dictionary<SweetsType, GameObject>(); for(int i=0;i<sweetPrefabs.Length;i++) { if (!sweetPrefabDict.ContainsKey(sweetPrefabs[i].type)) { sweetPrefabDict.Add(sweetPrefabs[i].type,sweetPrefabs[i].prefab); } } for(int x = 0; x < xColumn; x++) { for (int y=0;y<yRow;y++) { GameObject chocolate = Instantiate(gridPrefab,CorrectPosition(x,y),Quaternion.identity); chocolate.transform.SetParent(transform); } } sweets = new GameSweet[xColumn, yRow]; for (int x = 0; x < xColumn; x++) { for (int y = 0; y < yRow; y++) { CreateNewSweet(x, y, SweetsType.EMPTY); } } }
填充核心算法
填充全部甜品的方法
public void AllFill() { while (Fill()) { } }
当甜品填充满时返回true
public bool Fill() { bool FilledNotFinshed = false; //用来判断本次是否完成 //行遍历 for(int y=yRow-2;y>=0;y--) { for(int x=0;x<xColumn;x++) { GameSweet sweet = sweets[x, y]; //得到当前元素位置 //如果无法移动,则无法往下填充 if (sweet.CanMove()) { GameSweet sweetBelow = sweets[x, y + 1]; if(sweetBelow.Type == SweetsType.EMPTY) { sweet.MovedComponet.Move(x,y+1); sweets[x, y + 1] = sweet; CreateNewSweet(x, y, SweetsType.EMPTY); FilledNotFinshed = true; } } } } //最上排的特殊情况 for (int x = 0; x < xColumn; x++) { GameSweet sweet = sweets[x, 0]; if(sweet.Type == SweetsType.EMPTY) { GameObject newSweet = Instantiate(sweetPrefabDict[SweetsType.NORMAL], CorrectPosition(x,-1), Quaternion.identity); newSweet.transform.parent = transform; sweets[x, 0] = newSweet.GetComponent<GameSweet>(); sweets[x, 0].Init(x, -1,this,SweetsType.NORMAL); sweets[x, 0].MovedComponet.Move(x, 0); sweets[x, 0].ColorComponet.SetColor((ColorSweet.ColorType)Random.Range(0,sweets[x,0].ColorComponet.NumColors)); FilledNotFinshed = true; } } return FilledNotFinshed; }
在Start()中调用 AllFill()方法
void Start() { //字典的实例化 sweetPrefabDict = new Dictionary<SweetsType, GameObject>(); for(int i=0;i<sweetPrefabs.Length;i++) { if (!sweetPrefabDict.ContainsKey(sweetPrefabs[i].type)) { sweetPrefabDict.Add(sweetPrefabs[i].type,sweetPrefabs[i].prefab); } } for(int x = 0; x < xColumn; x++) { for (int y=0;y<yRow;y++) { GameObject chocolate = Instantiate(gridPrefab,CorrectPosition(x,y),Quaternion.identity); chocolate.transform.SetParent(transform); } } sweets = new GameSweet[xColumn, yRow]; for (int x = 0; x < xColumn; x++) { for (int y = 0; y < yRow; y++) { CreateNewSweet(x, y, SweetsType.EMPTY); } } AllFill(); }
using System.Collections; using System.Collections.Generic; using UnityEngine; public class GameManager : MonoBehaviour { //甜品的种类 public enum SweetsType { EMPTY, NORMAL, BARRIER, ROE_CLEAR, COLUMN_CLEAR, RAINBOWCANDY, COUNT //标记类型 } //甜品预制体的字典,我们可以通过甜品的种类来得到对应的甜品游戏物体 public Dictionary<SweetsType, GameObject> sweetPrefabDict; [System.Serializable] public struct SweetPrefab { public SweetsType type; public GameObject prefab; } public SweetPrefab[] sweetPrefabs; //单例实例化 private static GameManager _instance; public static GameManager Instance { get { return _instance; } set { _instance = value; } } //大网格的行列数 public int xColumn; public int yRow; public GameObject gridPrefab; //甜品数组 private GameSweet[,] sweets; private void Awake() { _instance = this; } // Use this for initialization void Start() { //字典的实例化 sweetPrefabDict = new Dictionary<SweetsType, GameObject>(); for(int i=0;i<sweetPrefabs.Length;i++) { if (!sweetPrefabDict.ContainsKey(sweetPrefabs[i].type)) { sweetPrefabDict.Add(sweetPrefabs[i].type,sweetPrefabs[i].prefab); } } for(int x = 0; x < xColumn; x++) { for (int y=0;y<yRow;y++) { GameObject chocolate = Instantiate(gridPrefab,CorrectPosition(x,y),Quaternion.identity); chocolate.transform.SetParent(transform); } } sweets = new GameSweet[xColumn, yRow]; for (int x = 0; x < xColumn; x++) { for (int y = 0; y < yRow; y++) { CreateNewSweet(x, y, SweetsType.EMPTY); } } AllFill(); } // Update is called once per frame void Update () { } public Vector3 CorrectPosition(int x ,int y) { //实际需要实例化巧克力的X位置 = GameManager位置的X坐标-大网格长度的一半+行列对应的X坐标 // 实际需要实例化巧克力的Y位置 = GameManager位置的Y坐标-大网格长度的一半+行列对应的Y坐标 return new Vector3(transform.position.x-xColumn/2f+x,transform.position.y+yRow/2f-y); } //产生甜品的方法 public GameSweet CreateNewSweet(int x,int y,SweetsType type) { GameObject newSweet = Instantiate(sweetPrefabDict[type], CorrectPosition(x, y), Quaternion.identity); newSweet.transform.parent = transform; sweets[x, y] = newSweet.GetComponent<GameSweet>(); sweets[x, y].Init(x,y,this,type); return sweets[x, y]; } //填充甜品的方法 public void AllFill() { while (Fill()) { } } public bool Fill() { bool FilledNotFinshed = false; //用来判断本次是否完成 //行遍历 for(int y=yRow-2;y>=0;y--) { for(int x=0;x<xColumn;x++) { GameSweet sweet = sweets[x, y]; //得到当前元素位置 //如果无法移动,则无法往下填充 if (sweet.CanMove()) { GameSweet sweetBelow = sweets[x, y + 1]; if(sweetBelow.Type == SweetsType.EMPTY) { sweet.MovedComponet.Move(x,y+1); sweets[x, y + 1] = sweet; CreateNewSweet(x, y, SweetsType.EMPTY); FilledNotFinshed = true; } } } } //最上排的特殊情况 for (int x = 0; x < xColumn; x++) { GameSweet sweet = sweets[x, 0]; if(sweet.Type == SweetsType.EMPTY) { GameObject newSweet = Instantiate(sweetPrefabDict[SweetsType.NORMAL], CorrectPosition(x,-1), Quaternion.identity); newSweet.transform.parent = transform; sweets[x, 0] = newSweet.GetComponent<GameSweet>(); sweets[x, 0].Init(x, -1,this,SweetsType.NORMAL); sweets[x, 0].MovedComponet.Move(x, 0); sweets[x, 0].ColorComponet.SetColor((ColorSweet.ColorType)Random.Range(0,sweets[x,0].ColorComponet.NumColors)); FilledNotFinshed = true; } } return FilledNotFinshed; } }
为了使游戏填充时间肉眼速度看得见,添加移动填充协成
移动填充的协成
填充时间
public float fillTime;
填充甜品的方法
public IEnumerator AllFill() { while (Fill()) { yield return new WaitForSeconds(fillTime); } }
void Start() { //字典的实例化 sweetPrefabDict = new Dictionary<SweetsType, GameObject>(); for(int i=0;i<sweetPrefabs.Length;i++) { if (!sweetPrefabDict.ContainsKey(sweetPrefabs[i].type)) { sweetPrefabDict.Add(sweetPrefabs[i].type,sweetPrefabs[i].prefab); } } for(int x = 0; x < xColumn; x++) { for (int y=0;y<yRow;y++) { GameObject chocolate = Instantiate(gridPrefab,CorrectPosition(x,y),Quaternion.identity); chocolate.transform.SetParent(transform); } } sweets = new GameSweet[xColumn, yRow]; for (int x = 0; x < xColumn; x++) { for (int y = 0; y < yRow; y++) { CreateNewSweet(x, y, SweetsType.EMPTY); } } StartCoroutine(AllFill()); }
此时出场动画会出现卡顿现象,通过修改MovedSweet.cs脚本添加协成
使每一帧都会进行出场动画
using System.Collections; using System.Collections.Generic; using UnityEngine; public class MovedSweet : MonoBehaviour { private GameSweet sweet; private IEnumerator moveCoroutine; private void Awake() { sweet = GetComponent<GameSweet>(); } //开启或者结束一个协成 public void Move(int newX,int newY,float time) { if(moveCoroutine!=null) { StopCoroutine(moveCoroutine); } moveCoroutine = MoveCoroutine(newX,newY,time); StartCoroutine(moveCoroutine); } //负责移动的协成 private IEnumerator MoveCoroutine(int newX,int newY,float time) { sweet.X = newX; sweet.Y = newY; //每一帧移动一点点 Vector3 startPos = transform.position; Vector3 endPos = sweet.gameManager.CorrectPosition(newX,newY); for(float t=0;t<time;t+=Time.deltaTime) { sweet.transform.position = Vector3.Lerp(startPos,endPos,t/time); yield return 0; } sweet.transform.position = endPos; } }
开启或者结束一个协成
public void Move(int newX,int newY,float time) { if(moveCoroutine!=null) { StopCoroutine(moveCoroutine); } moveCoroutine = MoveCoroutine(newX,newY,time); StartCoroutine(moveCoroutine); }
负责移动的协成
private IEnumerator MoveCoroutine(int newX,int newY,float time) { sweet.X = newX; sweet.Y = newY; //每一帧移动一点点 Vector3 startPos = transform.position; Vector3 endPos = sweet.gameManager.CorrectPosition(newX,newY); for(float t=0;t<time;t+=Time.deltaTime) { sweet.transform.position = Vector3.Lerp(startPos,endPos,t/time); yield return 0; } sweet.transform.position = endPos; }
public bool Fill() { bool FilledNotFinshed = false; //用来判断本次是否完成 //行遍历 for(int y=yRow-2;y>=0;y--) { for(int x=0;x<xColumn;x++) { GameSweet sweet = sweets[x, y]; //得到当前元素位置 //如果无法移动,则无法往下填充 if (sweet.CanMove()) { GameSweet sweetBelow = sweets[x, y + 1]; if(sweetBelow.Type == SweetsType.EMPTY) { sweet.MovedComponet.Move(x,y+1,fillTime); sweets[x, y + 1] = sweet; CreateNewSweet(x, y, SweetsType.EMPTY); FilledNotFinshed = true; } } } } //最上排的特殊情况 for (int x = 0; x < xColumn; x++) { GameSweet sweet = sweets[x, 0]; if(sweet.Type == SweetsType.EMPTY) { GameObject newSweet = Instantiate(sweetPrefabDict[SweetsType.NORMAL], CorrectPosition(x,-1), Quaternion.identity); newSweet.transform.parent = transform; sweets[x, 0] = newSweet.GetComponent<GameSweet>(); sweets[x, 0].Init(x, -1,this,SweetsType.NORMAL); sweets[x, 0].MovedComponet.Move(x, 0,fillTime); sweets[x, 0].ColorComponet.SetColor((ColorSweet.ColorType)Random.Range(0,sweets[x,0].ColorComponet.NumColors)); FilledNotFinshed = true; } } return FilledNotFinshed; }
障碍物_饼干
制作饼干预制体
void Start() { //字典的实例化 sweetPrefabDict = new Dictionary<SweetsType, GameObject>(); for(int i=0;i<sweetPrefabs.Length;i++) { if (!sweetPrefabDict.ContainsKey(sweetPrefabs[i].type)) { sweetPrefabDict.Add(sweetPrefabs[i].type,sweetPrefabs[i].prefab); } } for(int x = 0; x < xColumn; x++) { for (int y=0;y<yRow;y++) { GameObject chocolate = Instantiate(gridPrefab,CorrectPosition(x,y),Quaternion.identity); chocolate.transform.SetParent(transform); } } sweets = new GameSweet[xColumn, yRow]; for (int x = 0; x < xColumn; x++) { for (int y = 0; y < yRow; y++) { CreateNewSweet(x, y, SweetsType.EMPTY); } } //在(4,4)这个坐标点生成障碍物 Destroy(sweets[4, 4].gameObject); CreateNewSweet(4,4,SweetsType.BARRIER); StartCoroutine(AllFill()); }
斜向填充算法
如果无法移动,其它甜品则无法往下填充
public bool Fill() { bool FilledNotFinshed = false; //用来判断本次是否完成 //行遍历 for(int y=yRow-2;y>=0;y--) { for(int x=0;x<xColumn;x++) { GameSweet sweet = sweets[x, y]; //得到当前元素位置 //如果无法移动,则无法往下填充 if (sweet.CanMove()) { GameSweet sweetBelow = sweets[x, y + 1]; if(sweetBelow.Type == SweetsType.EMPTY)//垂直填充 { Destroy(sweetBelow.gameObject); sweet.MovedComponet.Move(x,y+1,fillTime); sweets[x, y + 1] = sweet; CreateNewSweet(x, y, SweetsType.EMPTY); FilledNotFinshed = true; } else { //-1代表左,1代表右 for (int down = -1; down <= 1; down++) { if (down != 0) { int downX = x + down; //排除边界的时候 //左下方 if (downX >= 0 && downX < xColumn) { GameSweet downSweet = sweets[downX, y + 1]; if (downSweet.Type == SweetsType.EMPTY) { bool canfill = true; //用来判断垂直填充是否可以满足填充要求 for (int aboutY = y; aboutY >= 0; aboutY--) { GameSweet sweetAbove = sweets[downX, aboutY]; if (sweetAbove.CanMove()) { break; } else if (!sweetAbove.CanMove() && sweetAbove.Type != SweetsType.EMPTY) { canfill = false; break; } } if (!canfill) { Destroy(downSweet.gameObject); sweet.MovedComponet.Move(downX, y + 1, fillTime); sweets[downX, y + 1] = sweet; CreateNewSweet(x, y, SweetsType.EMPTY); FilledNotFinshed = true; break; } } } } } } } } } //最上排的特殊情况 for (int x = 0; x < xColumn; x++) { GameSweet sweet = sweets[x, 0]; if(sweet.Type == SweetsType.EMPTY) { GameObject newSweet = Instantiate(sweetPrefabDict[SweetsType.NORMAL], CorrectPosition(x,-1), Quaternion.identity); newSweet.transform.parent = transform; sweets[x, 0] = newSweet.GetComponent<GameSweet>(); sweets[x, 0].Init(x, -1,this,SweetsType.NORMAL); sweets[x, 0].MovedComponet.Move(x, 0,fillTime); sweets[x, 0].ColorComponet.SetColor((ColorSweet.ColorType)Random.Range(0,sweets[x,0].ColorComponet.NumColors)); FilledNotFinshed = true; } } return FilledNotFinshed; }
using System.Collections; using System.Collections.Generic; using UnityEngine; public class MovedSweet : MonoBehaviour { private GameSweet sweet; private IEnumerator moveCoroutine; private void Awake() { sweet = GetComponent<GameSweet>(); } //开启或者结束一个协成 public void Move(int newX,int newY,float time) { if(moveCoroutine!=null) { StopCoroutine(moveCoroutine); } moveCoroutine = MoveCoroutine(newX,newY,time); StartCoroutine(moveCoroutine); } //负责移动的协成 private IEnumerator MoveCoroutine(int newX,int newY,float time) { sweet.X = newX; sweet.Y = newY; //每一帧移动一点点 Vector3 startPos = transform.position; Vector3 endPos = sweet.gameManager.CorrectPosition(newX,newY); for(float t=0;t<time;t+=Time.deltaTime) { sweet.transform.position = Vector3.Lerp(startPos,endPos,t/time); yield return 0; } sweet.transform.position = endPos; } }
using System.Collections; using System.Collections.Generic; using UnityEngine; public class ColorSweet : MonoBehaviour { public enum ColorType { YELLOW, PUPLE, RED, BLUE, GREEN, PNGK, ANY, COUNT } [System.Serializable] public struct ColorSprite { public ColorType color; public Sprite sprite; } public ColorSprite[] ColorSprites; private Dictionary<ColorType, Sprite> colorSpriteDict; private SpriteRenderer sprite; public int NumColors { get{ return ColorSprites.Length; } } public ColorType Color { get { return color; } set { SetColor(value); } } private ColorType color; public void Awake() { sprite = transform.Find("Sweet").GetComponent<SpriteRenderer>(); colorSpriteDict = new Dictionary<ColorType, Sprite>(); for(int i = 0; i < ColorSprites.Length; i++) { if (!colorSpriteDict.ContainsKey(ColorSprites[i].color)) { colorSpriteDict.Add(ColorSprites[i].color,ColorSprites[i].sprite); } } } public void SetColor(ColorType newColor) { color = newColor; if (colorSpriteDict.ContainsKey(newColor)) { sprite.sprite = colorSpriteDict[newColor]; } } }
using System.Collections; using System.Collections.Generic; using UnityEngine; public class GameSweet : MonoBehaviour { private int x; private int y; public int X { get { return x; } set { if (CanMove()) { x = value; } } } public int Y { get { return y; } set { if (CanMove()) { y = value; } } } private GameManager.SweetsType type; public GameManager.SweetsType Type { get { return type; } } [HideInInspector] public GameManager gameManager; public MovedSweet MovedComponet { get { return movedComponet; } } private MovedSweet movedComponet; public ColorSweet ColorComponet { get { return coloredCompent; } } private ColorSweet coloredCompent; //判断甜品是否可以移动 public bool CanMove() { return movedComponet != null; } //判断是否可以着色 public bool CanColor() { return coloredCompent != null; } private void Awake() { movedComponet = GetComponent<MovedSweet>(); coloredCompent = GetComponent<ColorSweet>(); } public void Init(int _x,int _y,GameManager _gameManager,GameManager.SweetsType _type) { x = _x; y = _y; gameManager = _gameManager; type = _type; } }
using System.Collections; using System.Collections.Generic; using UnityEngine; public class GameManager : MonoBehaviour { //甜品的种类 public enum SweetsType { EMPTY, NORMAL, BARRIER, ROE_CLEAR, COLUMN_CLEAR, RAINBOWCANDY, COUNT //标记类型 } //甜品预制体的字典,我们可以通过甜品的种类来得到对应的甜品游戏物体 public Dictionary<SweetsType, GameObject> sweetPrefabDict; [System.Serializable] public struct SweetPrefab { public SweetsType type; public GameObject prefab; } public SweetPrefab[] sweetPrefabs; //单例实例化 private static GameManager _instance; public static GameManager Instance { get { return _instance; } set { _instance = value; } } //大网格的行列数 public int xColumn; public int yRow; //填充时间 public float fillTime; public GameObject gridPrefab; //甜品数组 private GameSweet[,] sweets; private void Awake() { _instance = this; } // Use this for initialization void Start() { //字典的实例化 sweetPrefabDict = new Dictionary<SweetsType, GameObject>(); for(int i=0;i<sweetPrefabs.Length;i++) { if (!sweetPrefabDict.ContainsKey(sweetPrefabs[i].type)) { sweetPrefabDict.Add(sweetPrefabs[i].type,sweetPrefabs[i].prefab); } } for(int x = 0; x < xColumn; x++) { for (int y=0;y<yRow;y++) { GameObject chocolate = Instantiate(gridPrefab,CorrectPosition(x,y),Quaternion.identity); chocolate.transform.SetParent(transform); } } sweets = new GameSweet[xColumn, yRow]; for (int x = 0; x < xColumn; x++) { for (int y = 0; y < yRow; y++) { CreateNewSweet(x, y, SweetsType.EMPTY); } } //在(4,4)这个坐标点生成障碍物 Destroy(sweets[4, 4].gameObject); CreateNewSweet(4,4,SweetsType.BARRIER); StartCoroutine(AllFill()); } // Update is called once per frame void Update () { } public Vector3 CorrectPosition(int x ,int y) { //实际需要实例化巧克力的X位置 = GameManager位置的X坐标-大网格长度的一半+行列对应的X坐标 // 实际需要实例化巧克力的Y位置 = GameManager位置的Y坐标-大网格长度的一半+行列对应的Y坐标 return new Vector3(transform.position.x-xColumn/2f+x,transform.position.y+yRow/2f-y); } //产生甜品的方法 public GameSweet CreateNewSweet(int x,int y,SweetsType type) { GameObject newSweet = Instantiate(sweetPrefabDict[type], CorrectPosition(x, y), Quaternion.identity); newSweet.transform.parent = transform; sweets[x, y] = newSweet.GetComponent<GameSweet>(); sweets[x, y].Init(x,y,this,type); return sweets[x, y]; } //填充甜品的方法 public IEnumerator AllFill() { while (Fill()) { yield return new WaitForSeconds(fillTime); } } public bool Fill() { bool FilledNotFinshed = false; //用来判断本次是否完成 //行遍历 for(int y=yRow-2;y>=0;y--) { for(int x=0;x<xColumn;x++) { GameSweet sweet = sweets[x, y]; //得到当前元素位置 //如果无法移动,则无法往下填充 if (sweet.CanMove()) { GameSweet sweetBelow = sweets[x, y + 1]; if(sweetBelow.Type == SweetsType.EMPTY)//垂直填充 { Destroy(sweetBelow.gameObject); sweet.MovedComponet.Move(x,y+1,fillTime); sweets[x, y + 1] = sweet; CreateNewSweet(x, y, SweetsType.EMPTY); FilledNotFinshed = true; } else { //-1代表左,1代表右 for (int down = -1; down <= 1; down++) { if (down != 0) { int downX = x + down; //排除边界的时候 //左下方 if (downX >= 0 && downX < xColumn) { GameSweet downSweet = sweets[downX, y + 1]; if (downSweet.Type == SweetsType.EMPTY) { bool canfill = true; //用来判断垂直填充是否可以满足填充要求 for (int aboutY = y; aboutY >= 0; aboutY--) { GameSweet sweetAbove = sweets[downX, aboutY]; if (sweetAbove.CanMove()) { break; } else if (!sweetAbove.CanMove() && sweetAbove.Type != SweetsType.EMPTY) { canfill = false; break; } } if (!canfill) { Destroy(downSweet.gameObject); sweet.MovedComponet.Move(downX, y + 1, fillTime); sweets[downX, y + 1] = sweet; CreateNewSweet(x, y, SweetsType.EMPTY); FilledNotFinshed = true; break; } } } } } } } } } //最上排的特殊情况 for (int x = 0; x < xColumn; x++) { GameSweet sweet = sweets[x, 0]; if(sweet.Type == SweetsType.EMPTY) { GameObject newSweet = Instantiate(sweetPrefabDict[SweetsType.NORMAL], CorrectPosition(x,-1), Quaternion.identity); newSweet.transform.parent = transform; sweets[x, 0] = newSweet.GetComponent<GameSweet>(); sweets[x, 0].Init(x, -1,this,SweetsType.NORMAL); sweets[x, 0].MovedComponet.Move(x, 0,fillTime); sweets[x, 0].ColorComponet.SetColor((ColorSweet.ColorType)Random.Range(0,sweets[x,0].ColorComponet.NumColors)); FilledNotFinshed = true; } } return FilledNotFinshed; } }