Patrol

article/2025/10/4 8:36:27

设计要求

  • 游戏设计要求
    • 创建一个地图和若干巡逻兵(使用动画)
    • 每个巡逻兵走一个3~5个边的凸多边型,位置数据是相对地址。即每次确定下一个目标位置,用自己当前位置为原点计算
    • 巡逻兵碰撞到障碍物,则会自动选下一个点为目标
    • 巡逻兵在设定范围内感知到玩家,会自动追击玩家
    • 失去玩家目标后,继续巡逻
    • 计分:玩家每次甩掉一个巡逻兵计一分,与巡逻兵碰撞游戏结束
  • 程序设计要求
    • 必须使用订阅与发布模式传消息
    • 工厂模式生产巡逻兵
  • 游戏规则
    • 使用方向键控制人物移动,60秒内
      不被抓到即胜利

预制

地图

在这里插入图片描述
九宫格地图,每个格子为一个巡逻兵的巡逻区域,每个区域挂在一个MyRegion脚本

	public int num;//编号FirstSceneController sceneController;//场记    private void OnTriggerEnter(Collider other) {sceneController = SSDirector.GetInstance().CurrentSceneController as FirstSceneController;if (other.gameObject.tag == "Player") {sceneController.playerRegion = num;}    }private void OnTriggerExit(Collider other) {if (other.gameObject.tag == "Patrol") {other.gameObject.GetComponent<Patrol>().isCollided = true;}}

此处用tag区分玩家和巡逻兵

Patrol

在这里插入图片描述
巡逻兵和玩家预制都从Asset Store获取,为巡逻兵的加上一个朝前的capsule collider,相当于他的视线范围,当玩家进入视线范围,collider发生碰撞,巡逻兵检测到玩家存在进而追捕

Animator Controller

巡逻兵的动画控制:
在这里插入图片描述
其中有参数bool类型的shoot和pause,触发器death,pause为true则停止(对应m_weapon_idle_A),否则run;当巡逻兵抓到玩家,shoot触发器控制巡逻兵射击

玩家的动画控制:
在这里插入图片描述
由bool类型的run,pause和触发器death控制

代码实现

巡逻兵

巡逻兵数据

public class Patrol : MonoBehaviour
{public int patrolRegion;//巡逻兵所在区域public bool isFollowing;//是否追捕玩家public GameObject player;//玩家 public bool isPlayerInRange;//玩家是否在侦擦范围    public bool isCollided;//是否碰撞         public int playerRegion;//玩家所在区域           
}

工厂模式创建巡逻兵

public class PatrolFactory : MonoBehaviour
{public GameObject patrol = null;private List<Patrol> used = new List<Patrol>();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<Patrol>().patrolRegion = i * 3 + j + 1;patrol.GetComponent<Patrol>().playerRegion = 4;patrol.GetComponent<Patrol>().isPlayerInRange = false;patrol.GetComponent<Patrol>().isFollowing = false;patrol.GetComponent<Patrol>().isCollided = false;patrol.GetComponent<Animator>().SetBool("pause", true);used.Add(patrol.GetComponent<Patrol>());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 : Action
{private float pos_x, pos_z;private bool turn = true;//是否转向private Patrol data;public static PatrolAction GetAction(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<Patrol>();}public override void Update() {if (SSDirector.GetInstance().CurrentSceneController.getGameState().Equals(GameState.RUNNING)) {Patrol();if (!data.isFollowing && data.isPlayerInRange && data.patrolRegion == data.playerRegion && !data.isCollided) {this.destroy = true;this.enable = false;this.callback.ActionEvent(this);this.gameObject.GetComponent<Patrol>().isFollowing = true;Singleton<GameEventManager>.Instance.FollowPlayer(this.gameObject);}}}//巡逻void Patrol() {if (turn) {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<Patrol>().isCollided = false;turn = false;}float distance = Vector3.Distance(transform.position, new Vector3(pos_x, 0, pos_z));if (this.gameObject.GetComponent<Patrol>().isCollided) {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<Patrol>().isCollided = false;Destroy(temp);} else if (distance <= 0.1) {turn = true;} else {this.transform.Translate(0, 0, Time.deltaTime);}}
}
public class PatrolFollowAction : Action
{private float speed = 1.5f; private GameObject player;private Patrol data; public static PatrolFollowAction GetAction(GameObject player) {PatrolFollowAction action = CreateInstance<PatrolFollowAction>();action.player = player;return action;}public override void Start() {data = this.gameObject.GetComponent<Patrol>();}public override void Update() {if (SSDirector.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<Patrol>().isFollowing = false;Singleton<GameEventManager>.Instance.PlayerEscape(this.gameObject);}}}
}

通过ActionManager控制动作开始结束

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){ }
}

巡逻兵捕获玩家

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<Patrol>().isCollided = true;}}
}

玩家

玩家移动

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;player.transform.LookAt(new Vector3(player.transform.position.x + translationX, player.transform.position.y, player.transform.position.z + translationZ));if (translationX == 0)player.transform.Translate(0, 0, Mathf.Abs(translationZ) * 2);else if (translationZ == 0)player.transform.Translate(0, 0, Mathf.Abs(translationX) * 2);elseplayer.transform.Translate(0, 0, Mathf.Abs(translationZ) + Mathf.Abs(translationX));}

区域

public class MyRegion : MonoBehaviour
{public int num;//编号FirstSceneController sceneController;//场记    //标记玩家进入private void OnTriggerEnter(Collider other) {sceneController = SSDirector.GetInstance().CurrentSceneController as FirstSceneController;if (other.gameObject.tag == "Player") {sceneController.playerRegion = num;}    }//巡逻兵在自己的区域private void OnTriggerExit(Collider other) {if (other.gameObject.tag == "Patrol") {other.gameObject.GetComponent<Patrol>().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();} }
}

订阅者

	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 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 class CameraFollowAction : MonoBehaviour
{public GameObject player;            //相机跟随的物体public float smothing = 10f;          //相机跟随的平滑速度Vector3 offset;                      //相机与物体相对偏移位置void Start() {offset = new Vector3(0, 5, -5);}void FixedUpdate() {// 设置摄像机目标位置Vector3 target = player.transform.position + offset;//摄像机自身位置到目标位置平滑过渡transform.position = Vector3.Lerp(transform.position, target, smothing * Time.deltaTime);}
}

GUI

public class UserGUI : MonoBehaviour
{private UserAction action;private SceneController controller;GUIStyle scoreStyle;GUIStyle buttonStyle;GUIStyle countDownStyle;GUIStyle finishStyle;void Start() {scoreStyle = new GUIStyle();scoreStyle.fontSize = 40;scoreStyle.normal.textColor = Color.white;buttonStyle = new GUIStyle("button");buttonStyle.fontSize = 15;buttonStyle.normal.textColor = Color.white;countDownStyle = new GUIStyle();countDownStyle.fontSize = 25;countDownStyle.normal.textColor = Color.white;finishStyle = new GUIStyle();finishStyle.fontSize = 40;finishStyle.normal.textColor = Color.white;}private void Update() {action = SSDirector.GetInstance().CurrentSceneController as UserAction;controller = SSDirector.GetInstance().CurrentSceneController as SceneController;if (controller.getGameState().Equals(GameState.RUNNING)) {// 获取键盘输入float translationX = Input.GetAxis("Horizontal");float translationZ = Input.GetAxis("Vertical");//移动玩家action.MovePlayer(translationX, translationZ);}}private void OnGUI() {controller = SSDirector.GetInstance().CurrentSceneController as SceneController;string buttonText = "";if (controller.getGameState().Equals(GameState.START) || controller.getGameState().Equals(GameState.PAUSE)) {buttonText = "Start";}if (controller.getGameState().Equals(GameState.LOSE)) {buttonText = "Restart";GUI.Label(new Rect(Screen.width / 2 - 110, Screen.height / 2 - 100, 200, 50), "Game Over!", finishStyle);}if (controller.getGameState().Equals(GameState.WIN)) {buttonText = "Restart";GUI.Label(new Rect(Screen.width / 2 - 80, Screen.height / 2 , 200, 50), "You Win!", finishStyle);}if (controller.getGameState().Equals(GameState.RUNNING)) {buttonText = "Pause";}GUI.Label(new Rect(Screen.width / 2 - 80 , Screen.height / 2 - 175, 100, 50),"Score: " + controller.GetScore().ToString(), scoreStyle);GUI.Label(new Rect(Screen.width / 2 - 50, Screen.height / 2 - 225, 100, 50),"Time: " + SSDirector.GetInstance().leftSeconds.ToString(), countDownStyle);if (GUI.Button(new Rect(Screen.width / 2 - 50, Screen.height / 2 + 100, 100, 50), buttonText, buttonStyle)) {// 按下按钮控制游戏状态if (buttonText == "Pause") {controller.Pause();} else if (buttonText == "Start") {controller.Begin();} else if (buttonText == "Restart") {controller.Restart();}}}
}

效果

在这里插入图片描述
视频链接

参考博客

Unity3d学习之路-简单巡逻兵


http://chatgpt.dhexx.cn/article/VGiuk0DT.shtml

相关文章

Pareto(帕雷托)理论—凸优化、最优化理论学习

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、Pareto&#xff08;帕雷托&#xff09;理论二、Pareto&#xff08;帕雷托&#xff09;解三、Pareto&#xff08;帕雷托&#xff09;改进四、Pareto Front五…

p-中位模型与pareto解集

p-中位模型&#xff08;p-median model&#xff09;是一种用于解决设施选址问题的数学模型。在设施选址问题中&#xff0c;需要选择若干个设施点来满足需求区域内的服务需求&#xff0c;使得服务覆盖范围内的总成本或距离最小。p-中位模型是其中一种常用的优化模型。 在p-中位…

Matlab论文插图绘制模板第46期—帕累托图(Pareto)

在之前的文章中&#xff0c;分享过Matlab双轴柱线图的绘制模板&#xff1a; 这一次&#xff0c;再来分享一种特殊的柱线图&#xff1a;帕累托图。 ‘帕累托图&#xff08;Pareto chart&#xff09;是将出现的质量问题和质量改进项目按照重要程度依次排列而采用的一种图表。以意…

帕累托最优(Pareto Optimality)

帕累托最优&#xff08;Pareto Optimality&#xff09;&#xff0c;也称为帕累托效率&#xff08;Pareto efficiency&#xff09;&#xff0c;是指资源分配的一种理想状态&#xff0c;假定固有的一群人和可分配的资源&#xff0c;从一种分配状态到另一种状态的变化中&#xff0…

Pareto(帕雷托)相关知识

原文地址&#xff1a;Pareto&#xff08;帕雷托&#xff09;相关知识 作者&#xff1a; XIAO_QingJun 1879年&#xff0c;经济学家意大利人维弗雷多帕雷托 (Villefredo Pareto) 提出&#xff1a;社会财富的80%是掌握在20%的人手中&#xff0c;而余下的80%的人只占有20%的财富。…

【python科研绘图】绘制帕累托图(Pareto)步骤解析,并封装后直接调用

python绘制帕累托图 1 帕累托图2 python实现2.1 数据准备2.2 特征因素数值排序2.3 计算累计频率百分比2.4 标记累计百分比80%特征因素的位置2.5 输出核心的特征因素信息2.6 绘制帕累托图 3 封装3.1 全部代码3.2 应用示例 手动反爬虫&#xff1a; 原博地址 https://blog.csdn.n…

多目标优化——帕累托最优Pareto

多目标优化——帕累托最优Pareto 0.前言 本文是本人在学习过程中为便于记忆利用博客进行总结&#xff0c;期待与各位大佬共同学习交流 什么是多目标优化 在了解帕累托最优之前&#xff0c;我们先来讨论一下&#xff0c;什么是多目标优化&#xff08;Multiobjective Optimiz…

【多目标进化优化】Pareto 定义及基于 Pareto 的多目标进化算法的算法流程

声明 本文内容来源于 《多目标进化优化》 郑金华 邹娟著&#xff0c;非常感谢两位老师的知识分享&#xff0c;如有侵权&#xff0c;本人立即删除&#xff0c;同时在此表示&#xff0c;本文内容仅学习使用&#xff0c;禁止侵权&#xff0c;谢谢&#xff01; 0 前言 多目标优化…

【数字孪生百科】可视化图表知识科普——Pareto图(Pareto Chart)

简介 Pareto图&#xff08;Pareto Chart&#xff09;又称帕累托图、排列图&#xff0c;是一种特殊类型的条形图。图中标绘的值是按照事件发生的频率排序而成&#xff0c;显示由于各种原因引起的缺陷数量或不一致的排列顺序。Pareto图是根据 Vilfredo Pareto 命名的&#xff0c;…

帕累托图 (Pareto Chart)

帕累托图用于识别最有可能考虑影响过程或结果的变量行为的因子类别。流程改进团队使用Pareto图表来确定其工作的优先级。 在帕累托图中&#xff0c;垂直条通常代表每个类别&#xff0c;条形图上方的累积折线图确定了最有可能成为研究重要目标的因素。 优点 使用和理解的简单…

多目标优化中的帕累托(Pareto)相关概念

本文整理自&#xff1a; 多目标优化之帕累托最优 - 知乎、多目标优化---帕累托&#xff08;Pareto&#xff09;_纯粹的博客-CSDN博客、百度百科 1、多目标优化 在现实生活中有很多的问题都是由互相冲突和影响的多个目标组成&#xff0c;这些目标不可能同时达到最优的状态&am…

Pareto(帕累托)

转自&#xff1a;https://blog.csdn.net/scutwjh/article/details/46129405 由于最近看到了一篇社交网络中的论文提高了Pareto相关知识&#xff0c;所以整理了下网上关于Pareto相关理论的讲解&#xff0c;供大家参考&#xff1a; 维弗雷多帕雷托 (Villefredo Pareto) 在1987年…

Pareto 问题详解

Pareto 问题详解 1 Pareto1.1 Pareto 问题1.1.2 Pareto Improvement1.1.3 Pareto Front1.1.4 Pareto Analysis 1.2 Pareto 解1.2.1 解A优于解B (解A强 Pareto 支配解B)1.2.2 解A无差别于解B(解A能 Pareto 支配解B)1.2.3 最优解与Pareto 最优解1) 最优解2) Pareto 最优解3) 理解…

Pareto最优解排序

•1879年&#xff0c;经济学家意大利人维弗雷多帕雷托(Villefredo Pareto) 提出&#xff1a;社会财富的80%是掌握在20%的人手中&#xff0c;而余下的80%的人只占有20%的财富。渐渐地&#xff0c;这种“关键的少数&#xff08;vital few&#xff09;和次要的多数&#xff08;tri…

多目标优化---帕累托(Pareto)

参考与这个链接的博客https://blog.csdn.net/paulfeng20171114/article/details/82454310 多目标优化—帕累托&#xff08;Pareto&#xff09; 1 多目标优化简介 2多目标优化数学语言描述 3 多目标优化的Pareto占优 1 多目标优化简介&#xff1a; 在现实生活中有很多的问题都…

Pareto最优解 Pareto分布

1.该定律是指绝大多数的问题或缺陷产生于相对有限的起因。就是常说的二八定律&#xff0c;即20%的原因造成80%的问题。 在帕累托分布中&#xff0c;如果X是一个随机变量&#xff0c; 则X的概率分布如下面的公式所示&#xff1a; 其中x是任何一个大于xmin的数&#xff0c;xmin是…

手机取证技术路线图

Andriod手机取证的难度&#xff0c;其实在于一个门槛。最高人民检察院司法鉴定中心是国家级司法鉴定中心&#xff0c;他们对手机取证的总结&#xff0c;还是挺全面和震撼的。感谢本图原创作者&#xff1a;Friez Wittenfeld。 图片较大、较长&#xff0c;请您受累把手机横起来看…

无人机取证——飞行日志分析取证

前言 随着科技发展日新月异&#xff0c;利用无人机犯罪的途径和案例越来越多&#xff0c;无人机除了拍照录像的用途外&#xff0c;亦可能被有心人士用于运送违法物品等不法行为&#xff0c;甚至&#xff0c;若是绑上自制炸弹&#xff0c;刻意飞去冲撞人或车或建筑物&#xff0…

【Android取证篇】华为手机OTG备份密码重置教程

【Android取证篇】华为手机OTG备份密码重置教程 ​ 提取华为设备遇到OTG备份出来的数据信息软件无法正常解析时&#xff0c;排除数据提取不完整、软件设备等问题&#xff0c;可考虑重置华为的备份密码&#xff0c;重新备份数据再分析—【suy】 文章目录 【Android取证篇】华为…

安卓手机微信数据恢复取证研究 EnMicroMsg.db FTS5IndexMicroMsg_encrypt.db

一、记录级恢复 对于安卓手机微信数据恢复取证&#xff0c;大部分用户删除微信数据时&#xff0c;只对部分消息进行针对性的删除&#xff0c;实际上仅仅删除了EnMicroMsg.db文件message数据表中的局部内容&#xff0c;由于没有覆盖新数据&#xff0c;在安卓微信5.2版本前可以通…