1.智能巡逻兵
- 提交要求:
- 游戏设计要求:
- 创建一个地图和若干巡逻兵(使用动画);
- 每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址。即每次确定下一个目标位置,用自己当前位置为原点计算;
- 巡逻兵碰撞到障碍物,则会自动选下一个点为目标;
- 巡逻兵在设定范围内感知到玩家,会自动追击玩家;
- 失去玩家目标后,继续巡逻;
- 计分:玩家每次甩掉一个巡逻兵计一分,与巡逻兵碰撞游戏结束;
- 程序设计要求:
- 必须使用订阅与发布模式传消息
- subject:OnLostGoal
- Publisher: ?
- Subscriber: ?
- 工厂模式生产巡逻兵
- 必须使用订阅与发布模式传消息
- 友善提示1:生成 3~5个边的凸多边型
- 随机生成矩形
- 在矩形每个边上随机找点,可得到 3 - 4 的凸多边型
- 5 ?
- 友善提示2:参考以前博客,给出自己新玩法
2.场景构建
地面Plane
- 新建对象(地面)
- 新建对象(Cube)围墙
- 构建用于区分9个区域的障碍物,栅栏预制是资源商店(Assert Store)下载的,然后用4个栅栏组成。
- 地面搭建完成
3.预制动画
-
巡逻兵
巡逻兵在游戏开始时进入run状态,当游戏按下暂停时,动作会切换idle状态。这两个状态切换通过控制bool类型参数pause实现。当巡逻兵碰撞到玩家时,通过控制触发器类型参数shoot进入shoot状态,然后退出。
-
玩家
玩家在游戏开始时进入idle状态,当键盘输入进行移动时,控制bool类型参数run进入run状态,没有移动会切换idle状态。游戏暂停时也会进入idle状态,当游戏失败即玩家被巡逻兵捕获时,控制触发器类型参数death进入death状态。
4.实现
巡逻兵数据类
public class PatrolData : MonoBehaviour
{
public GameObject player; // 巡逻兵追击的玩家
public bool isHitObstacle; // 是否碰撞到障碍物
public bool isFollowing; // 是否正在追击
public int patrolRegion; // 巡逻兵所在区域
public int playerRegion; // 玩家所在区域
public bool isPlayerFound; // 是否感知到玩家
}
巡逻兵工厂类
public class PatrolFactory : MonoBehaviour
{
public GameObject patrol;
private List<PatrolData> used = new List<PatrolData>(); // 正在使用的巡逻兵
public List<GameObject> GetPatrols()
{
List<GameObject> patrols = new List<GameObject>();
float[] pos_x = { -4.5f, 1.5f, 7.5f };
float[] pos_z = { 7.5f, 1.5f, -4.5f };
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
patrol = Instantiate(Resources.Load<GameObject>("Prefabs/Patrol"));
patrol.transform.position = new Vector3(pos_x[j], 0, pos_z[i]);
patrol.GetComponent<PatrolData>().patrolRegion = i * 3 + j + 1;
patrol.GetComponent<PatrolData>().playerRegion = 4;
patrol.GetComponent<PatrolData>().isPlayerFound = false;
patrol.GetComponent<PatrolData>().isFollowing = false;
patrol.GetComponent<PatrolData>().isHitObstacle = false;
patrol.GetComponent<Animator>().SetBool("pause", true);
used.Add(patrol.GetComponent<PatrolData>());
patrols.Add(patrol);
}
}
return patrols;
}
public void PausePatrol()
{
//切换所有侦查兵的动画
for (int i = 0; i < used.Count; i++)
{
used[i].gameObject.GetComponent<Animator>().SetBool("pause", true);
}
}
public void StartPatrol()
{
//切换所有侦查兵的动画
for (int i = 0; i < used.Count; i++)
{
used[i].gameObject.GetComponent<Animator>().SetBool("pause", false);
}
}
}
巡逻兵动作类
- 巡逻兵巡逻动作
public class PatrolAction : SSAction
{
private float pos_x, pos_z; // 移动前的初始x和z方向坐标
private float moveSpeed = 1.2f; // 移动速度
private bool isChooseNewDir = true; // 是否选择新方向
private PatrolData data; // 巡逻兵的数据
private PatrolAction() { }
public static PatrolAction GetSSAction(Vector3 location)
{
PatrolAction action = CreateInstance<PatrolAction>();
action.pos_x = location.x;
action.pos_z = location.z;
return action;
}
public override void Start()
{
data = this.gameobject.GetComponent<PatrolData>();
}
public override void Update()
{
if (SSDirector.GetInstance().currentScenceController.getGameState().Equals(GameState.RUNNING))
{
// 巡逻兵巡逻
Patrol();
// 切换追击模式
if (!data.isFollowing && data.isPlayerFound && data.patrolRegion == data.playerRegion && !data.isHitObstacle)
{
this.enable = false;
this.destroy = true;
this.callback.SSActionEvent(this);
this.gameobject.GetComponent<PatrolData>().isFollowing = true;
Singleton<GameEventManager>.Instance.FollowPlayer(this.gameobject);
}
}
}
void Patrol()
{
// 碰撞后寻找新位置
if (isChooseNewDir)
{
pos_x = this.transform.position.x + Random.Range(-5f, 5f);
pos_z = this.transform.position.z + Random.Range(-5f, 5f);
this.transform.LookAt(new Vector3(pos_x, 0, pos_z));
this.gameobject.GetComponent<PatrolData>().isHitObstacle = false;
isChooseNewDir = false;
}
// 计算所在位置与目标位置的距离,用于判断是否碰撞
float distance = Vector3.Distance(transform.position, new Vector3(pos_x, 0, pos_z));
if (this.gameobject.GetComponent<PatrolData>().isHitObstacle)
{
// 碰撞,则向后转,寻找新位置
this.transform.Rotate(Vector3.up, 180);
GameObject temp = new GameObject();
temp.transform.position = this.transform.position;
temp.transform.rotation = this.transform.rotation;
temp.transform.Translate(0, 0, Random.Range(0.5f, 3f));
pos_x = temp.transform.position.x;
pos_z = temp.transform.position.z;
this.transform.LookAt(new Vector3(pos_x, 0, pos_z));
this.gameobject.GetComponent<PatrolData>().isHitObstacle = false;
Destroy(temp);
}
else if (distance <= 0.1)
{
isChooseNewDir = true;
}
else
{
// 向前移动巡逻兵
this.transform.Translate(0, 0, Time.deltaTime);
}
}
}
- 巡逻兵追击动作
public class PatrolFollowAction : SSAction
{
private float speed = 1.5f; // 跟随玩家的速度
private GameObject player; // 玩家
private PatrolData data; // 巡逻兵数据
private PatrolFollowAction() { }
public static PatrolFollowAction GetSSAction(GameObject player)
{
PatrolFollowAction action = CreateInstance<PatrolFollowAction>();
action.player = player;
return action;
}
public override void Start()
{
data = this.gameobject.GetComponent<PatrolData>();
}
public override void Update()
{
if (SSDirector.GetInstance().currentScenceController.getGameState().Equals(GameState.RUNNING))
{
// 巡逻兵追击玩家
transform.position = Vector3.MoveTowards(this.transform.position, player.transform.position, speed * Time.deltaTime);
this.transform.LookAt(player.transform.position);
// 切换巡逻模式
if (data.isFollowing && (!(data.isPlayerFound && data.patrolRegion == data.playerRegion) || data.isHitObstacle))
{
this.destroy = true;
this.enable = false;
this.callback.SSActionEvent(this);
this.gameobject.GetComponent<PatrolData>().isFollowing = false;
Singleton<GameEventManager>.Instance.PlayerEscape(this.gameobject);
}
}
}
}
巡逻兵碰撞检测
- 给巡逻兵添加组件Capsule Collider,用于检测碰撞,注意选项Is Trigger不要勾选。
public class PatrolCollide : MonoBehaviour
{
void OnCollisionEnter(Collision collision) {
if (collision.gameObject.tag == "Player") {
// 当巡逻兵碰到玩家
this.GetComponent<Animator>().SetTrigger("shoot");
Singleton<GameEventManager>.Instance.OnPlayerCatched();
} else {
// 当巡逻兵碰到其他障碍物
this.GetComponent<PatrolData>().isCollided = true;
}
}
}
订阅与发布模式
public class GameEventManager : MonoBehaviour
{
// 玩家逃脱事件
public delegate void EscapeEvent(GameObject patrol);
public static event EscapeEvent OnGoalLost;
// 巡逻兵追击事件
public delegate void FollowEvent(GameObject patrol);
public static event FollowEvent OnFollowing;
// 游戏失败事件
public delegate void GameOverEvent();
public static event GameOverEvent GameOver;
// 游戏胜利事件
public delegate void WinEvent();
public static event WinEvent Win;
// 玩家逃脱
public void PlayerEscape(GameObject patrol)
{
if (OnGoalLost != null)
{
OnGoalLost(patrol);
}
}
// 巡逻兵追击
public void FollowPlayer(GameObject patrol)
{
if (OnFollowing != null)
{
OnFollowing(patrol);
}
}
// 玩家被捕
public void OnPlayerCatched()
{
if (GameOver != null)
{
GameOver();
}
}
// 时间结束
public void TimeIsUP()
{
if (Win != null)
{
Win();
}
}
}
游戏场景控制器
public class FirstSceneController : MonoBehaviour, SceneController, IUserAction
{
public UserGUI userGUI;
private GameState gameState = GameState.START; // 游戏状态
public PatrolActionManager patrolActionManager; // 巡逻兵动作管理器
public PatrolFactory patrolFactory; // 巡逻兵工厂
private List<GameObject> patrols; // 场景中巡逻兵列表
public ScoreRecorder scoreRecorder; // 计分器
public GameObject player; // 游戏玩家
public int playerRegion; // 玩家所在区域
// 场景初始化
void Start() {
SSDirector director = SSDirector.GetInstance();
director.CurrentSceneController = this;
director.SetFPS(30);
director.leaveSeconds = 60;
scoreRecorder = Singleton<ScoreRecorder>.Instance;
patrolFactory = Singleton<PatrolFactory>.Instance;
userGUI = gameObject.AddComponent<UserGUI>() as UserGUI;
playerRegion = 5;
patrolActionManager = gameObject.AddComponent<PatrolActionManager>();
LoadResources();
// 巡逻兵开始巡逻
for (int i = 0; i < patrols.Count; i++) {
patrolActionManager.Patrol(patrols[i]);
}
}
// 加载游戏资源
public void LoadResources() {
Instantiate(Resources.Load<GameObject>("Prefabs/Plane"));
player = Instantiate(Resources.Load("Prefabs/Player"), new Vector3(-1.5f, 0, -1.5f), Quaternion.identity) as GameObject;
patrols = patrolFactory.GetPatrols();
// 镜头跟随
Camera.main.GetComponent<CameraFollowAction>().player = player;
}
private void Update() {
// 通知巡逻兵更新玩家所在区域信息
for (int i = 0; i < patrols.Count; i++) {
patrols[i].GetComponent<PatrolData>().playerRegion = playerRegion;
}
}
void OnEnable() {
GameEventManager.OnGoalLost += OnGoalLost;
GameEventManager.OnFollowing += OnFollowing;
GameEventManager.GameOver += GameOver;
GameEventManager.Win += Win;
}
void OnDisable() {
GameEventManager.OnGoalLost -= OnGoalLost;
GameEventManager.OnFollowing -= OnFollowing;
GameEventManager.GameOver -= GameOver;
GameEventManager.Win -= Win;
}
public void MovePlayer(float translationX, float translationZ) {
if (translationX != 0 || translationZ != 0) {
player.GetComponent<Animator>().SetBool("run", true);
} else {
player.GetComponent<Animator>().SetBool("run", false);
}
translationX *= Time.deltaTime;
translationZ *= Time.deltaTime;
float playerPosX = player.transform.position.x;
float playerPosY = player.transform.position.y;
float playerPosZ = player.transform.position.z;
player.transform.LookAt(new Vector3(playerPosX + translationX, playerPosY, playerPosZ + translationZ));
player.transform.Translate(0, 0, Mathf.Abs(translationZ) + Mathf.Abs(translationX));
}
// 失去目标, 巡逻兵切换巡逻模式,玩家得分
public void OnGoalLost(GameObject patrol) {
patrolActionManager.Patrol(patrol);
scoreRecorder.Record();
}
// 玩家进入范围,巡逻兵开始追击模式
public void OnFollowing(GameObject patrol) {
patrolActionManager.Follow(player, patrol);
}
// 失败
public void GameOver() {
gameState = GameState.LOSE;
StopAllCoroutines();
patrolFactory.PausePatrol();
player.GetComponent<Animator>().SetTrigger("death");
patrolActionManager.DestroyAllActions();
}
// 胜利
public void Win() {
gameState = GameState.WIN;
StopAllCoroutines();
patrolFactory.PausePatrol();
}
// 获取分数
public int GetScore() {
return scoreRecorder.score;
}
// 重新开始
public void Restart() {
SceneManager.LoadScene("Scenes/new");
}
// 暂停游戏
public void Pause() {
gameState = GameState.PAUSE;
patrolFactory.PausePatrol();
player.GetComponent<Animator>().SetBool("pause", true);
StopAllCoroutines();
}
// 开始游戏
public void Begin() {
gameState = GameState.RUNNING;
patrolFactory.StartPatrol();
player.GetComponent<Animator>().SetBool("pause", false);
StartCoroutine(SSDirector.GetInstance().CountDown());
}
// 获取游戏状态
public GameState getGameState() {
return gameState;
}
}