Unity3D 小案例 像素贪吃蛇 第三期 蛇的碰撞(完结)
像素贪吃蛇
碰撞蛇身
当蛇头碰撞到蛇身时,游戏应该判定为失败。
找到蛇身预制体,添加 Body 标签和碰撞体,碰撞体的大小为 0.5,跟蛇头和蛇身的碰撞体范围一样,避免因碰撞范围过大而产生错误的碰撞效果。
修改一下 OnTriggerEnter2D 方法,判断碰撞到 Body 标签的物体,判定游戏失败,蛇停止移动。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Snake : MonoBehaviour
{void OnTriggerEnter2D(Collider2D other){if (other.CompareTag("Food")){// ...}else if (other.CompareTag("Body")){// 取消定时器CancelInvoke(nameof(Move));}}
}
如果此时运行游戏,会发现蛇一开始就不动了。因为之前初始生成身体时,都在同一个位置生成,一开始就会碰撞蛇身。
修改一下之前的逻辑,让初始生成的蛇身偏移位置。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Snake : MonoBehaviour
{Vector2 initPos; // 初始位置void Start(){initPos = transform.position;// 初始生成身体for (int i = 0; i < initBodyCount; i++){GenerateBody(true);}// ...}void GenerateBody(bool isInit = false){GameObject obj = Instantiate(body);// 初始生成if (isInit){obj.transform.position = initPos + Vector2.left;initPos = obj.transform.position;}else{// 已有身体if (bodyList.Count > 0){// 获取最后一个身体,在它的位置生成GameObject lastBody = bodyList[bodyList.Count - 1];obj.transform.position = lastBody.transform.position;}// 没有身体else{// 以蛇头的位置生成身体obj.transform.position = transform.position;}}// ...}
}
现在运行游戏,蛇身的初始位置就不会跟蛇头重叠了。
另外,蛇头的层级也要调整一下,当蛇头与蛇身重叠时,让蛇头能够显示在蛇身之上。
运行效果:
添加墙壁
创建四个正方形,调整缩放和位置,调整颜色,分别布置在上下左右四个方向。
因为蛇的移动范围,在 X 轴是 [-8, 8]
,在 Y 轴是 [-4, 4]
,所以左右的墙壁分别放置在 X 轴的 -9 和 9 的位置,上下的墙壁分别放置在 Y 轴的 5 和 -5 的位置。
然后给墙壁也添加标签 Wall,并且要添加碰撞体,碰撞体的大小保持默认的 1 即可。
在代码中也添加碰撞墙壁的逻辑。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Snake : MonoBehaviour
{void OnTriggerEnter2D(Collider2D other){if (other.CompareTag("Food")){// ...}else if (other.CompareTag("Body") || other.CompareTag("Wall")){// 取消定时器CancelInvoke(nameof(Move));}}
}
运行效果:
游戏失败
显示失败界面
简单搭建一个游戏失败界面。
创建一个 UIGameOver.cs
脚本,引用命名空间 UnityEngine.UI
,定义 UI 组件变量,拖拽引用。
using UnityEngine;
using UnityEngine.UI;public class UIGameOver : MonoBehaviour
{public Button btnRestart;public CanvasGroup canvasGroup;void Start(){}
}
创建一个 GameManager.cs
脚本,引用命名空间 System
,创建单例。
定义一个 Action
事件 showGameOver
,带一个布尔值参数,并对外提供一个接口,用于触发事件。
using System;
using UnityEngine;public class GameManager : MonoBehaviour
{public static GameManager instance;public event Action<bool> showGameOver;void Awake(){if (instance == null){instance = this;}else{Destroy(gameObject);}}public void TriggerGameOver(bool isShow){showGameOver.Invoke(isShow);}
}
界面脚本添加事件监听,根据是否显示界面,修改 canvasGroup
的参数。
using UnityEngine;
using UnityEngine.UI;public class UIGameOver : MonoBehaviour
{public Button btnRestart;public CanvasGroup canvasGroup;void Start(){GameManager.instance.showGameOver += (isShow) =>{canvasGroup.alpha = isShow ? 1 : 0;canvasGroup.blocksRaycasts = isShow;};}
}
当蛇碰撞时,触发事件显示游戏失败界面。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Snake : MonoBehaviour
{void OnTriggerEnter2D(Collider2D other){if (other.CompareTag("Food")){// ...}else if (other.CompareTag("Body") || other.CompareTag("Wall")){// 取消定时器CancelInvoke(nameof(Move));// 显示游戏失败界面GameManager.instance.TriggerGameOver(true);}}
}
运行效果:
重开游戏
在 UIGameOver.cs
脚本上添加按钮事件,隐藏失败界面,重新开始游戏。
using UnityEngine;
using UnityEngine.UI;public class UIGameOver : MonoBehaviour
{public Button btnRestart;public CanvasGroup canvasGroup;void Start(){// ...btnRestart.onClick.AddListener(()=>{GameManager.instance.TriggerGameOver(false);GameManager.instance.RestartGame();});}
}
在 GameManager.cs
脚本上添加重开游戏的事件和触发接口。
using System;
using UnityEngine;public class GameManager : MonoBehaviour
{//...public event Action restartGame;// ...public void RestartGame(){restartGame.Invoke();}
}
在 FoodManager.cs
脚本上添加重置网格接口,同时定义一个变量记录当前生成的食物,在重置网格时销毁当前食物。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class FoodManager : MonoBehaviour
{// ...public GameObject currentFood;public void GenerateFood(){// ...currentFood = obj;}public void ResetGrid(){gridList.Clear();for (int i = 0; i < rowMax; i++){for (int j = 0; j < colMax; j++){gridList.Add(new Vector3(borderLeft + j, borderTop - i, 0));}}Destroy(currentFood);}
}
最后,在 Snake.cs
脚本上添加事件监听,重置网格、蛇头、蛇身、食物,最后再次开启定时器,让蛇头重新开始移动。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Snake : MonoBehaviour
{// ...void Start(){// ...// 重开游戏,重置游戏物体GameManager.instance.restartGame += ()=>{// 重置网格FoodManager.instance.ResetGrid();// 销毁蛇身for (int i = 0; i < bodyList.Count; i++){Destroy(bodyList[i]);}bodyList.Clear();// 重置蛇头位置和方向transform.position = Vector3.zero;FoodManager.instance.MarkGridList(transform.position, true);direction = Vector2.right;lastDirection = Vector2.right;firstDirection = Vector2.right;initPos = transform.position;// 初始生成身体for (int i = 0; i < initBodyCount; i++){GenerateBody(true);}// 初始生成食物FoodManager.instance.GenerateFood();// 重新开始移动InvokeRepeating(nameof(Move), time, time);};}
}
运行效果: