Match 3 小项目学习
1 using UnityEngine; 2 using System.Collections; 3 using Holoville.HOTween; 4 5 /// <summary> 6 /// 游戏逻辑 7 /// </summary> 8 public class Main:MonoBehaviour { 9 10 /// <summary> 11 /// 点击框预设 12 /// </summary> 13 public GameObject _indicator; 14 /// <summary> 15 /// 二维方块数组 16 /// </summary> 17 public GameObject[,] _arrayOfShapes; 18 /// <summary> 19 /// 当前点击框 20 /// </summary> 21 private GameObject _currentIndicator; 22 /// <summary> 23 /// 第一个选择的物体 24 /// </summary> 25 private GameObject _FirstObject; 26 /// 第二个选择的物体 27 /// </summary> 28 private GameObject _SecondObject; 29 /// <summary> 30 /// 方块预设列表 31 /// </summary> 32 public GameObject[] _listOfGems; 33 /// <summary> 34 /// 空物体 35 /// </summary> 36 public GameObject _emptyGameobject; 37 /// <summary> 38 /// 星星特效 39 /// </summary> 40 public GameObject _particleEffect; 41 /// <summary> 42 /// 匹配时的特效 43 /// </summary> 44 public GameObject _particleEffectWhenMatch; 45 /// <summary> 46 /// 能否在对角线上转换 47 /// </summary> 48 public bool _canTransitDiagonally = false; 49 /// <summary> 50 /// 每次匹配时的增长分数 51 /// </summary> 52 public int _scoreIncrement; 53 /// <summary> 54 /// 总分 55 /// </summary> 56 private int _scoreTotal = 0; 57 /// <summary> 58 /// 当前特效 59 /// </summary> 60 private ArrayList _currentParticleEffets = new ArrayList(); 61 /// <summary> 62 /// 匹配时的声音 63 /// </summary> 64 public AudioClip MatchSound; 65 /// <summary> 66 /// 网格x轴上的格子数量 67 /// </summary> 68 public int _gridWidth; 69 /// <summary> 70 /// 网格y轴上的格子数量 71 /// </summary> 72 public int _gridHeight; 73 74 75 void Start() { 76 //为2维方块数组分配内存 77 _arrayOfShapes = new GameObject[_gridWidth,_gridHeight]; 78 79 80 //从左到右,从下到上创建格子 81 for(int i = 0;i <= _gridWidth - 1;i++) { 82 for(int j = 0;j <= _gridHeight - 1;j++) { 83 //var gameObject = GameObject.Instantiate(_listOfGems[Random.Range(0,_listOfGems.Length)] as GameObject,new Vector3(i,j,0),transform.rotation) as GameObject; 84 85 GameObject gameObject = null; 86 if(j <= 2) { 87 gameObject = GameObject.Instantiate(_listOfGems[0] as GameObject,new Vector3(i,j,0),transform.rotation) as GameObject; 88 } else if(j <= 5) { 89 gameObject = GameObject.Instantiate(_listOfGems[1] as GameObject,new Vector3(i,j,0),transform.rotation) as GameObject; 90 } else { 91 gameObject = GameObject.Instantiate(_listOfGems[2] as GameObject,new Vector3(i,j,0),transform.rotation) as GameObject; 92 } 93 _arrayOfShapes[i,j] = gameObject; 94 } 95 } 96 //Adding the star effect to the gems and call the DoShapeEffect continuously 97 //InvokeRepeating("DoShapeEffect",1f,0.21F); 98 99 } 100 101 void Update() { 102 //是否转换 103 bool shouldTransit = false; 104 //当用户点击按钮且没有动画 105 if(Input.GetButtonDown("Fire1") && HOTween.GetTweenInfos() == null) { 106 //销毁当前选择框 107 Destroy(_currentIndicator); 108 //生成从屏幕中心点到鼠标点击位置的射线 109 Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); 110 //发射射线 111 RaycastHit2D hit = Physics2D.Raycast(Camera.main.ScreenToWorldPoint(Input.mousePosition),Vector2.zero); 112 //如果射线碰到物体 113 if(hit.transform != null) { 114 //第一个选择的物体为空 115 if(_FirstObject == null) { 116 //将点击的物体设置为第一个选择物体 117 _FirstObject = hit.transform.gameObject; 118 //如果第一个选择的物体不为空 119 } else { 120 //将点击的物体设置为第二个选择物体 121 _SecondObject = hit.transform.gameObject; 122 //可以转换 123 shouldTransit = true; 124 } 125 126 //根据点击位置创建选择框 127 _currentIndicator = GameObject.Instantiate(_indicator,new Vector3(hit.transform.gameObject.transform.position.x,hit.transform.gameObject.transform.position.y,-1),transform.rotation) as GameObject; 128 //如果可以转换 129 if(shouldTransit) { 130 //获取两个物体之间的距离 131 var distance = _FirstObject.transform.position - _SecondObject.transform.position; 132 //如果x,y的距离 都小于等于1 133 if(Mathf.Abs(distance.x) <= 1 && Mathf.Abs(distance.y) <= 1) { 134 //如果不能在斜线上交换 135 if(!_canTransitDiagonally) { 136 // 137 if(distance.x != 0 && distance.y != 0) { 138 //销毁选择框 139 Destroy(_currentIndicator); 140 _FirstObject = null; 141 _SecondObject = null; 142 return; 143 } 144 } 145 //播放交换动画 146 DoSwapMotion(_FirstObject.transform,_SecondObject.transform); 147 //交换在数组中的位置 148 DoSwapTile(_FirstObject,_SecondObject,ref _arrayOfShapes); 149 //如果不能转换 150 } else { 151 _FirstObject = null; 152 _SecondObject = null; 153 154 } 155 //销毁选择框 156 Destroy(_currentIndicator); 157 158 } 159 160 } 161 162 } 163 164 165 //如果没有播放动画 166 if(HOTween.GetTweenInfos() == null) { 167 //查找匹配的格子 168 var Matches = FindMatch(_arrayOfShapes); 169 //如果找到匹配的格子 170 if(Matches.Count > 0) { 171 //更新分数 172 _scoreTotal += Matches.Count * _scoreIncrement; 173 174 //遍历匹配物体 175 foreach(GameObject go in Matches) { 176 //播放匹配声音 177 GetComponent<AudioSource>().PlayOneShot(MatchSound); 178 //创建匹配特效 179 var destroyingParticle = GameObject.Instantiate(_particleEffectWhenMatch as GameObject,new Vector3(go.transform.position.x,go.transform.position.y,-2),transform.rotation) as GameObject; 180 //1秒后销毁特效 181 Destroy(destroyingParticle,1f); 182 //用空物体替换匹配物体 183 _arrayOfShapes[(int)go.transform.position.x,(int)go.transform.position.y] = GameObject.Instantiate(_emptyGameobject,new Vector3((int)go.transform.position.x,(int)go.transform.position.y,-1),transform.rotation) as GameObject; 184 //0.1秒后删除匹配物体 185 Destroy(go,0.1f); 186 } 187 _FirstObject = null; 188 _SecondObject = null; 189 190 DoEmptyDown(ref _arrayOfShapes); 191 } 192 //如果没有找到格子,将格子的位置复位 193 else if(_FirstObject != null && _SecondObject != null) { 194 //播放交换动画 195 DoSwapMotion(_FirstObject.transform,_SecondObject.transform); 196 //交换格子位置 197 DoSwapTile(_FirstObject,_SecondObject,ref _arrayOfShapes); 198 _FirstObject = null; 199 _SecondObject = null; 200 } 201 } 202 203 //更新分数 204 (GetComponent(typeof(TextMesh)) as TextMesh).text = _scoreTotal.ToString(); 205 } 206 207 /// <summary> 208 /// 查找匹配 209 /// </summary> 210 /// <param name="cells"></param> 211 /// <returns></returns> 212 private ArrayList FindMatch(GameObject[,] cells) { 213 //存储匹配的方块数组 214 ArrayList stack = new ArrayList(); 215 //将列上至少3个颜色相同的方块放进数组中 216 //从左到右 217 for(var x = 0;x <= cells.GetUpperBound(0);x++) { 218 //从下到上 219 for(var y = 0;y <= cells.GetUpperBound(1);y++) { 220 //当前网格 221 var thiscell = cells[x,y]; 222 //如果是空物体,忽略 223 if(thiscell.name == "Empty(Clone)") { 224 continue; 225 } 226 //匹配数量 227 int matchCount = 0; 228 //y2 网格列上的最大索引 229 int y2 = cells.GetUpperBound(1); 230 //y1 网格列内y格子上方的格子索引 231 int y1; 232 //遍历列中其余的格子 233 for(y1 = y + 1;y1 <= y2;y1++) { 234 //如果是空物体或者名字不匹配,跳出循环 235 if(cells[x,y1].name == "Empty(Clone)" || thiscell.name != cells[x,y1].name) { 236 break; 237 } 238 239 //匹配数量加一 240 matchCount++; 241 } 242 243 //如果匹配数量大于等于2 244 if(matchCount >= 2) { 245 //限制y1的最小值 246 y1 = Mathf.Min(cells.GetUpperBound(1),y1 - 1); 247 //从 y遍历到 y1, 248 for(var y3 = y;y3 <= y1;y3++) { 249 //将不重复的格子添加到数组中 250 if(!stack.Contains(cells[x,y3])) { 251 stack.Add(cells[x,y3]); 252 } 253 } 254 } 255 } 256 } 257 258 259 //将行上至少3个颜色相同的方块放进数组中 260 //从下到上 261 for(var y = 0;y < cells.GetUpperBound(1) + 1;y++) { 262 //从左到右 263 for(var x = 0;x < cells.GetUpperBound(0) + 1;x++) { 264 //当前网格 265 var thiscell = cells[x,y]; 266 //如果是空物体,忽略 267 if(thiscell.name == "Empty(Clone)") { 268 continue; 269 } 270 //匹配数量 271 int matchCount = 0; 272 //x2 网格行上的最大索引 273 int x2 = cells.GetUpperBound(0); 274 //x1 网格行内x格子右方的格子索引 275 int x1; 276 //遍历行中其余的格子 277 for(x1 = x + 1;x1 <= x2;x1++) { 278 //如果是空物体或者名字不匹配,跳出循环 279 if(cells[x1,y].name == "Empty(Clone)" || thiscell.name != cells[x1,y].name) { 280 break; 281 } 282 //匹配数量加一 283 matchCount++; 284 } 285 286 //如果匹配数量大于等于2 287 if(matchCount >= 2) { 288 //限制x1的最小值 289 x1 = Mathf.Min(cells.GetUpperBound(0),x1 - 1); 290 //从x遍历到 x1, 291 for(var x3 = x;x3 <= x1;x3++) { 292 //将不重复的格子添加到数组中 293 if(!stack.Contains(cells[x3,y])) { 294 stack.Add(cells[x3,y]); 295 } 296 } 297 } 298 } 299 } 300 return stack; 301 } 302 303 /// <summary> 304 /// 交换动画 305 /// </summary> 306 void DoSwapMotion(Transform a,Transform b) { 307 Vector3 posA = a.localPosition; 308 Vector3 posB = b.localPosition; 309 TweenParms parms = new TweenParms().Prop("localPosition",posB).Ease(EaseType.EaseOutQuart); 310 HOTween.To(a,0.25f,parms).WaitForCompletion(); 311 parms = new TweenParms().Prop("localPosition",posA).Ease(EaseType.EaseOutQuart); 312 HOTween.To(b,0.25f,parms).WaitForCompletion(); 313 } 314 315 316 /// <summary> 317 /// 交换网格在数组中的位置 318 /// </summary> 319 void DoSwapTile(GameObject a,GameObject b,ref GameObject[,] cells) { 320 GameObject cell = cells[(int)a.transform.position.x,(int)a.transform.position.y]; 321 cells[(int)a.transform.position.x,(int)a.transform.position.y] = cells[(int)b.transform.position.x,(int)b.transform.position.y]; 322 cells[(int)b.transform.position.x,(int)b.transform.position.y] = cell; 323 } 324 325 /// <summary> 326 /// 移动网格 327 /// </summary> 328 private void DoEmptyDown(ref GameObject[,] cells) { 329 330 //从左到右 331 for(int x = 0;x <= cells.GetUpperBound(0);x++) { 332 //从下到上 333 for(int y = 0;y <= cells.GetUpperBound(1);y++) { 334 //当前网格 335 var thisCell = cells[x,y]; 336 //如果是空网格 337 if(thisCell.name == "Empty(Clone)") { 338 //从下往上遍历y轴网格 339 for(int y2 = y;y2 <= cells.GetUpperBound(1);y2++) { 340 //如果不是空网格,交换 341 if(cells[x,y2].name != "Empty(Clone)") { 342 cells[x,y] = cells[x,y2]; 343 cells[x,y2] = thisCell; 344 break; 345 } 346 } 347 } 348 } 349 } 350 351 //从左到右 352 for(int x = 0;x <= cells.GetUpperBound(0);x++) { 353 //从下到上 354 for(int y = 0;y <= cells.GetUpperBound(1);y++) { 355 //如果是空网格 356 if(cells[x,y].name == "Empty(Clone)") { 357 //删除空格子 358 Destroy(cells[x,y]); 359 //随机创建新格子 360 cells[x,y] = GameObject.Instantiate(_listOfGems[Random.Range(0,_listOfGems.Length)] as GameObject,new Vector3(x,cells.GetUpperBound(1) + 2,0),transform.rotation) as GameObject; 361 } 362 } 363 } 364 365 //从左到右 366 for(int x = 0;x <= cells.GetUpperBound(0);x++) { 367 //从下到上 368 for(int y = 0;y <= cells.GetUpperBound(1);y++) { 369 //将格子移动到指定位置 370 TweenParms parms = new TweenParms().Prop("position",new Vector3(x,y,-1)).Ease(EaseType.EaseOutQuart); 371 HOTween.To(cells[x,y].transform,.4f,parms); 372 } 373 } 374 } 375 376 /// <summary> 377 /// 创建闪烁特效 378 /// </summary> 379 void DoShapeEffect() { 380 foreach(GameObject row in _currentParticleEffets) { 381 Destroy(row); 382 } 383 for(int i = 0;i <= 2;i++) 384 _currentParticleEffets.Add(GameObject.Instantiate(_particleEffect,new Vector3(Random.Range(0,_arrayOfShapes.GetUpperBound(0) + 1),Random.Range(0,_arrayOfShapes.GetUpperBound(1) + 1),-1),new Quaternion(0,0,Random.Range(0,1000f),100)) as GameObject); 385 } 386 387 388 }
视频:https://pan.baidu.com/s/1qXYv2Mw
项目:https://pan.baidu.com/s/1i55JGVj