Unity中使用委托 代理 实现敌人自动检测目标并攻击

编程入门 行业动态 更新时间:2024-10-19 05:21:19

Unity中使用委托 代理 实现敌人<a href=https://www.elefans.com/category/jswz/34/1755731.html style=自动检测目标并攻击"/>

Unity中使用委托 代理 实现敌人自动检测目标并攻击

假如有一个类控制着游戏中某个关卡的敌人。所有敌人都有一个特点:只要敌人发现玩家了,它就会追赶玩家。最重要的是其他敌人会被通知到玩家的位置,并且也开始追赶玩家。

所以实现这个类应该向下面这样写。

using UnityEngine;using System.Collections;public class ReactiveEnemy : MonoBehaviour{//a variable to store this game object's Transformprivate Transform myTransform;//a variable to store the player's character Transformprivate Transform playerTransform;/*a static boolean variable to tell if the player has collided withany enemy trigger*/public static bool hasCollided = false;//Initialization codevoid Awake(){//get the game object the script is attached tomyTransform = this.GetComponent<Transform>();//get the player's TransformplayerTransform = GameObject.FindWithTag("Player").GetComponent<Transform>();}//runs every game cyclevoid Update(){//checks if the player has collided with any triggerif(hasCollided){//follow the playerFollow();}}//this method makes the enemy follow the playerprivate void Follow(){//look at the playermyTransform.LookAt(playerTransform);/*only follow the player if this enemy is4.5 units away from the player*/if(Vector3.Distance(myTransform.position,playerTransform.position)>=4.5f){//move the enemymyTransform.Translate(Vector3.forward * Time.deltaTime* 0.5f);}}}

下面解释下这段代码是如何工作的。在Awake()函数中找到player并且获得transform。在Update函数中监测是否看到玩家。如果看到玩家,就执行Follow函数。

需要在每个敌人物体上添加一个触发器。触发器可以让敌人在某范围内检测到玩家。下面的触发脚本只是在碰到玩家后将上面ReactiveEnemy 脚本中的静态变量 hasColliderd设为真。

using UnityEngine;  using System.Collections;  public class EnemyTrigger : MonoBehaviour  {  //if something collided with the trigger  void OnTriggerEnter(Collider col)  {  //if the player collided with the trigger  if(col.gameObject.tag=="Player")  {  //set hasCollided static variable to true  ReactiveEnemy.hasCollided = true;  }  }  }

脚本上的注释已经很清楚了,不做过多解释。这个脚本能正常运行但对于每个挂着这个脚本的敌人来说,他们需要在每次更新(Update)中监测是否有敌人看到了玩家(上面脚本中的第十行)。更多的敌人意味着更多的相同的情况,再次检查只是重复之前已经完成的操作。

还有一个问题:假如我们想为敌人增加另外一个动作,例如这次不是追赶了,要飞怎么办?当然,我们可以创建另一个类甚至可以还用之前的那个类传递一个布尔值来确定敌人是一个追赶者还是一个飞翔者,但这样又需要在每个游戏循环中添加一个if语句来判断。

在这种类似的情况下,我们可以使用一个委托来封装这些有相同特征的方法,并且只需判断一次玩家是否被敌人监测到,接着去执行根据引用而来的各种预期的动作。下面是用委托来编码同一个ReactiveEnemy类。这个类分为两部分:一部分设置委托监测玩家是否被发现(AllEnemiesClass),另一部分根据玩家做出实际处理动作(EnemyActions)。

下面是AllEnemies 类。

using UnityEngine;using System.Collections;public class AllEnemies : MonoBehaviour{//a variable to store the player's character Transformprivate Transform playerTransform;/* a static boolean variable  to tell if the player has collided withany enemy trigger*/public static bool hasCollided = false;//an array of game objects, to store every single enemy in the sceneprivate GameObject[] allEnemies;/*sets a delegate called 'AllEnemyActions', that returns void and takesa Transform as a parameter*/private delegate void AllEnemyActions(Transform pTransform);/*now that the 'AllEnemyActions' delegate signature is set, weinstantiate a delegate, naming it as 'aaaDelegate'*/private AllEnemyActions aaaDelegate;void Awake(){//get the player's character TransformplayerTransform = GameObject.FindWithTag("Player").GetComponent<Transform>();//get all enemies that are in this scene, and have the 'Enemy' tagallEnemies = GameObject.FindGameObjectsWithTag("Enemy");/* here, the delegate is instantiated. It takes a method as* a parameter, meaning that the FollowPlayer method from* the first enemy of the array 'allEnemies' is now being wrapped* by the aaaDelegate. So if we call the delegate right now,* by writing:* aaaDelegate(playerTransform);* it would be the same as writing:* allEnemies[0].GetComponent<EnemyActions>().FollowPlayer();*/aaaDelegate = new AllEnemyActions(allEnemies[0].GetComponent<EnemyActions>().FollowPlayer);/* now, we add other methods that have the same signature, so* the delegate can reference them, when it's called.*/aaaDelegate += allEnemies[1].GetComponent<EnemyActions>().FollowPlayer;aaaDelegate += allEnemies[2].GetComponent<EnemyActions>().FollowPlayer;aaaDelegate += allEnemies[3].GetComponent<EnemyActions>().FollowPlayer;aaaDelegate += allEnemies[4].GetComponent<EnemyActions>().FollowPlayer;aaaDelegate += allEnemies[5].GetComponent<EnemyActions>().FollowPlayer;/*NOTE: It is possible to use a loop to add these methods as* references to the delegate. To remove a reference to a method,you will need to use the '-=' operator. */}void Update(){//checks if the player has collided with any triggerif(hasCollided){//call the delegateaaaDelegate(playerTransform);/*The above line is the same as calling every FollowPlayer()* method from every GameObject has the 'Enemy' tag. */}}}

开始,我们定义委托有哪些特性(第十七行)。接着,当委托被定义后,我们在Awake()中第40行对它初始化。初始化需要添加一个函数引用,所以我们从allEnemies 数组中选一个加上。然后我们将其他的方法添加上(设置委托,43-47行)。

  Update函数中,我们要做的只是调用aaaDelegate的委托,并将playerTransform作为参数传递进去。完成了!所有被添加进委托的函数都会被调用。变量hasCollided 只会被判断一次,玩家的transform 也只需获取一次,不再是场景中的每个敌人都在Awake()函数中获取一次。

  那FollowPlayer()方法来自哪里呢?还记着我们将ReactiveEnemy 分成两部分吗?敌人FollowPlayer方法在另一个叫做EnemyActions脚本中。

using UnityEngine;using System.Collections;public class EnemyActions : MonoBehaviour{//a variable to store this game object's Transformprivate Transform myTransform;void Awake(){//get the game object the script is attached tomyTransform = this.GetComponent<Transform>();}//this method makes the enemy follow the playerpublic void FollowPlayer(Transform playerTransform){//look at the playermyTransform.LookAt(playerTransform);/*only follow the player if this enemy is 4.5 units away from the* the player*/if(Vector3.Distance(myTransform.position,playerTransform.position)>=4.5f){//move the enemymyTransform.Translate(Vector3.forward * Time.deltaTime* 0.5f);}}}

如果需要添加敌人的另外一种动作,只需做一件事就是添加一个新的方法即可,像这样:

//...//this method makes the enemy flypublic void Fly(Transform pTransform){//if the player is at 4.5f units awayif(Vector3.Distance(myTransform.position,pTransform.position)<=4.5f){//make it flymyTransform.Translate(Vector3.up * Time.deltaTime);}else{//make it landif(myTransform.position.y >= 0.525528f){myTransform.Translate(-Vector3.up * Time.deltaTime);}}}//...

之后,我们只需将这个方法添加到AllEnemies脚本的委托中

/*Instead of (line 47): aaaDelegate += allEnemies[5].GetComponent<enemyactions>().Follow;*/  //Write:  aaaDelegate += allEnemies[5].GetComponent<enemyactions>().Fly;

而且我们可以更改任何事情,因为委托只是对一个或多个方法的引用-这跟它被称为委托是一样的。

 

对于触发器的代码和上面展示的是相同的。委托为代码带来了极大的灵活性,尤其是需要重构时。它允许在游戏规则上多做思考,并且在不需要了解具体是如何实施的情况下执行。考虑将委托作为运行时可执行和不可执行的接口。可以通过-= 运算符移除这些方法,也可以通过添加方法合并委托。




更多推荐

Unity中使用委托 代理 实现敌人自动检测目标并攻击

本文发布于:2024-03-14 18:37:15,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1737092.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:自动检测   敌人   目标   Unity

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!