目录:
游戏概述
制作巡逻兵
制作巡逻路径
制作巡逻兵工厂
发布订阅者模式
制作玩家游戏概述 这次的游戏是基于智能巡逻兵的“困难”躲避游戏,玩家通过wads键控制方向,当玩家出现在巡逻兵侦察范围会自动追击,追到则失败。玩家需要摆脱巡逻兵,摆脱后巡逻兵依旧巡逻,分数+1。巡逻兵的巡逻路线采用随机生成矩形,然后再再边上随机点构成多边形,作为巡逻路线。 制作巡逻兵 在网上找的师兄的博客,然后对照他的步骤找的巡逻兵的资源 在Patrol上添加一个Capsule Collider,用于检测巡逻兵与障碍物,玩家的碰撞。在Bip001上添加一个Capsule Collider,用于感知玩家。自定义Collider的形状,使巡逻兵有一定的视线范围。 每个巡逻兵,需要发现玩家的标志变量,存储巡逻路径的变量,追踪玩家动作,巡逻动作。 巡逻兵平常进行巡逻动作,按照存储的巡逻路径进行巡逻。当发现玩家后,停止巡逻动作,执行追逐玩家动作,如果被甩掉,则继续执行巡逻动作。 巡逻兵数据
1 2 3 4 5 6 7 8 9 10 public class PatrolData : MonoBehaviour { public bool isPlayerInRange; public bool isFollowing; public bool isCollided; public int patrolRegion; public int playerRegion; public GameObject player; }
巡逻兵碰撞检测 1 2 3 4 5 6 7 8 9 10 11 12 13 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 ; } } }
巡逻兵动作管理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class PatrolActionManager : ActionManager , ActionCallback { public PatrolAction patrol; public PatrolFollowAction follow; public void Patrol (GameObject ptrl ) { this .patrol = PatrolAction.GetAction(ptrl.transform.position); this .RunAction(ptrl, patrol, this ); } public void Follow (GameObject player, GameObject patrol ) { this .follow = PatrolFollowAction.GetAction(player); this .RunAction(patrol, follow, this ); } public void DestroyAllActions ( ) { DestroyAll(); } public void ActionEvent (Action source, ActionEventType events = ActionEventType.Completed, int intParam = 0 , string strParam = null , object objectParam = null ) { } }
巡逻兵追击玩家 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class PatrolFollowAction : Action { private float speed = 1.5f ; private GameObject player; private PatrolData data; public static PatrolFollowAction GetAction (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 (Director.GetInstance().CurrentSceneController.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.isPlayerInRange && data.patrolRegion == data.playerRegion) || data.isCollided)) { this .destroy = true ; this .enable = false ; this .callback.ActionEvent(this ); this .gameObject.GetComponent<PatrolData>().isFollowing = false ; Singleton<GameEventManager>.Instance.PlayerEscape(this .gameObject); } } } }
巡逻兵检测范围 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class PlayerInRange : MonoBehaviour { void OnTriggerEnter (Collider collider ) { if (collider.gameObject.tag == "Player" ) { this .gameObject.transform.parent.GetComponent<PatrolData>().isPlayerInRange = true ; this .gameObject.transform.parent.GetComponent<PatrolData>().player = collider.gameObject; } } void OnTriggerExit (Collider collider ) { if (collider.gameObject.tag == "Player" ) { this .gameObject.transform.parent.GetComponent<PatrolData>().isPlayerInRange = false ; this .gameObject.transform.parent.GetComponent<PatrolData>().player = null ; } } }
制作巡逻路径 巡逻路径,采取生成随机点列表作为路径,让巡逻兵从一个点,运动到另一个点,作为巡逻动作。 首先生成一个随机的矩形左下角点和边长,然后算出其余三个顶点,接着在它的四条边每条边随机选取一个点,作为巡逻路径点,这样可以得到一个随机的凸四边形
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public float positionRange = 20.0f ;public float defaultSideLength = 5.0f ;public float yPosition = 1.0f ;public List<Vector3> GetRandomRect (int sides = 4 , float sideLength = 0 ) { List<Vector3> rect = new List<Vector3>(); if (sideLength == 0 ) { sideLength = defaultSideLength; } sideLength = Random.Range(10f , 20f ); Vector3 leftDown = new Vector3( Random.Range(-positionRange, positionRange), yPosition, Random.Range(-positionRange, positionRange)); Vector3 rightDown = leftDown + Vector3.right * sideLength; Vector3 rightUp = leftDown + Vector3.forward * sideLength; Vector3 leftUp = rightDown + Vector3.forward * sideLength; Vector3 temp = leftDown + Vector3.forward * sideLength * Random.Range(0f , 1f ); rect.Add(temp); temp = leftUp + Vector3.right * sideLength * Random.Range(0f , 1f ); rect.Add(temp); temp = rightUp + Vector3.forward * sideLength * Random.Range(0f , 1f ); rect.Add(temp); if (sides >= 4 ) { temp = rightDown + Vector3.right * (-sideLength) * Random.Range(0f , 0.5f ); rect.Add(temp); if (sides == 5 ) { temp = rightDown + Vector3.right * (-sideLength) * Random.Range(0f , 0.5f ); rect.Add(temp); } } return rect; }
制作巡逻兵工厂 只有巡逻兵的工厂,因为玩家只有一个。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public class PatrolFactory : MonoBehaviour { public GameObject patrol = null ; 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>().isPlayerInRange = false ; patrol.GetComponent<PatrolData>().isFollowing = false ; patrol.GetComponent<PatrolData>().isCollided = 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 ); } } }
发布订阅者模式 多个订阅者拥有自己的对同一类时间的各自事件的处理逻辑,然后在每个订阅者实例化的时候,向一个专门负责事件发布与接收的地方注册自己的事件处理函数(回调函数)。然后发布者在感知并产生事件之后,向同样的负责事件分发的地方传送自己的事件,然后该事件依次进入不同订阅者的逻辑中,实施其自己的工作。 对于这个游戏来说,只有巡逻兵那个对事件发生并感知事件,事件逻辑已经足够简单,所以发布者和订阅者都可以简化成:发布者使每个巡逻兵,订阅者是FirstSceneController。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class PatrolActionManager : ActionManager , ActionCallback { public PatrolAction patrol; public PatrolFollowAction follow; public void Patrol (GameObject ptrl ) { this .patrol = PatrolAction.GetAction(ptrl.transform.position); this .RunAction(ptrl, patrol, this ); } public void Follow (GameObject player, GameObject patrol ) { this .follow = PatrolFollowAction.GetAction(player); this .RunAction(patrol, follow, this ); } public void DestroyAllActions ( ) { DestroyAll(); } public void ActionEvent (Action source, ActionEventType events = ActionEventType.Completed, int intParam = 0 , string strParam = null , object objectParam = null ) { } }
制作玩家 给预制挂载Collider和刚体属性 玩家动作触发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class EnterRegion : MonoBehaviour { public int region; FirstSceneController sceneController; void OnTriggerEnter (Collider collider ) { sceneController = Director.GetInstance().CurrentSceneController as FirstSceneController; if (collider.gameObject.tag == "Player" ) { sceneController.playerRegion = region; } } private void OnTriggerExit (Collider collider ) { if (collider.gameObject.tag == "Patrol" ) { collider.gameObject.GetComponent<PatrolData>().isCollided = true ; } } }
游戏视频 游戏视频