• unity中使用代理(翻译)


    本文为个人翻译的,感谢原作者!

    原文链接:

    http://www.41post.com/3146/programming/using-c-delegates-in-unity3d-scripts

      当unity3d 3.0发布时,这个版本不仅修复了许多bug,添加了很多新特性,而且也提升了Mono这个开发工具的使用,包括支持了命名空间,LINQ,委托等。本文下面将讨论什么是委托以及在unity3d中使用委托进行开发的优势。

    通常C#中的委托是指对某一方法或者某一些有相同特性方法(返回值类型相同并且有相同的参数)的引用。更好的解释见于MSDN

     

    A delegate is a type that safely encapsulates a method, similar to a function pointer in C and C++. Unlike C function pointers, delegates are object-oriented, type safe, and secure. The type of a delegate is defined by the name of the delegate.

      如上所说,我们从一个例子中来看如何使用委托。假如有一个类控制着游戏中某个关卡的敌人。所有敌人都有一个特点:只要敌人发现玩家了,它就会追赶玩家。最重要的是其他敌人会被通知到玩家的位置,并且也开始追赶玩家。

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

     

     1 using UnityEngine;
     2 
     3 using System.Collections;
     4 
     5  
     6 
     7 public class ReactiveEnemy : MonoBehaviour
     8 
     9 {
    10 
    11 //a variable to store this game object's Transform
    12 
    13 private Transform myTransform;
    14 
    15 //a variable to store the player's character Transform
    16 
    17 private Transform playerTransform;
    18 
    19 /*a static boolean variable to tell if the player has collided with
    20 
    21 any enemy trigger*/
    22 
    23 public static bool hasCollided = false;
    24 
    25  
    26 
    27 //Initialization code
    28 
    29 void Awake()
    30 
    31 {
    32 
    33 //get the game object the script is attached to
    34 
    35 myTransform = this.GetComponent<Transform>();
    36 
    37 //get the player's Transform
    38 
    39 playerTransform = GameObject.FindWithTag("Player").GetComponent<Transform>();
    40 
    41 }
    42 
    43  
    44 
    45 //runs every game cycle
    46 
    47 void Update()
    48 
    49 {
    50 
    51 //checks if the player has collided with any trigger
    52 
    53 if(hasCollided)
    54 
    55 {
    56 
    57 //follow the player
    58 
    59 Follow();
    60 
    61 }
    62 
    63 }
    64 
    65  
    66 
    67 //this method makes the enemy follow the player
    68 
    69 private void Follow()
    70 
    71 {
    72 
    73 //look at the player
    74 
    75 myTransform.LookAt(playerTransform);
    76 
    77  
    78 
    79 /*only follow the player if this enemy is
    80 
    81 4.5 units away from the player*/
    82 
    83 if(Vector3.Distance(myTransform.position,playerTransform.position)>=4.5f)
    84 
    85 {
    86 
    87 //move the enemy
    88 
    89 myTransform.Translate(Vector3.forward * Time.deltaTime* 0.5f);
    90 
    91 }
    92 
    93 }
    94 
    95 }

     

    下面解释下这段代码是如何工作的。在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 类。

      1 using UnityEngine;
      2 
      3 using System.Collections;
      4 
      5  
      6 
      7 public class AllEnemies : MonoBehaviour
      8 
      9 {
     10 
     11 //a variable to store the player's character Transform
     12 
     13 private Transform playerTransform;
     14 
     15 /* a static boolean variable  to tell if the player has collided with
     16 
     17 any enemy trigger*/
     18 
     19 public static bool hasCollided = false;
     20 
     21  
     22 
     23 //an array of game objects, to store every single enemy in the scene
     24 
     25 private GameObject[] allEnemies;
     26 
     27  
     28 
     29 /*sets a delegate called 'AllEnemyActions', that returns void and takes
     30 
     31 a Transform as a parameter*/
     32 
     33 private delegate void AllEnemyActions(Transform pTransform);
     34 
     35  
     36 
     37 /*now that the 'AllEnemyActions' delegate signature is set, we
     38 
     39 instantiate a delegate, naming it as 'aaaDelegate'*/
     40 
     41 private AllEnemyActions aaaDelegate;
     42 
     43  
     44 
     45 void Awake()
     46 
     47 {
     48 
     49 //get the player's character Transform
     50 
     51 playerTransform = GameObject.FindWithTag("Player").GetComponent<Transform>();
     52 
     53  
     54 
     55 //get all enemies that are in this scene, and have the 'Enemy' tag
     56 
     57 allEnemies = GameObject.FindGameObjectsWithTag("Enemy");
     58 
     59  
     60 
     61 /* here, the delegate is instantiated. It takes a method as
     62 
     63 * a parameter, meaning that the FollowPlayer method from
     64 
     65 * the first enemy of the array 'allEnemies' is now being wrapped
     66 
     67 * by the aaaDelegate. So if we call the delegate right now,
     68 
     69 * by writing:
     70 
     71 * aaaDelegate(playerTransform);
     72 
     73 * it would be the same as writing:
     74 
     75 * allEnemies[0].GetComponent<EnemyActions>().FollowPlayer();
     76 
     77 */
     78 
     79 aaaDelegate = new AllEnemyActions(allEnemies[0].GetComponent<EnemyActions>().FollowPlayer);
     80 
     81 /* now, we add other methods that have the same signature, so
     82 
     83  * the delegate can reference them, when it's called.*/
     84 
     85 aaaDelegate += allEnemies[1].GetComponent<EnemyActions>().FollowPlayer;
     86 
     87 aaaDelegate += allEnemies[2].GetComponent<EnemyActions>().FollowPlayer;
     88 
     89 aaaDelegate += allEnemies[3].GetComponent<EnemyActions>().FollowPlayer;
     90 
     91 aaaDelegate += allEnemies[4].GetComponent<EnemyActions>().FollowPlayer;
     92 
     93 aaaDelegate += allEnemies[5].GetComponent<EnemyActions>().FollowPlayer;
     94 
     95  
     96 
     97 /*NOTE: It is possible to use a loop to add these methods as
     98 
     99 * references to the delegate. To remove a reference to a method,
    100 
    101 you will need to use the '-=' operator. */
    102 
    103  
    104 
    105 }
    106 
    107  
    108 
    109 void Update()
    110 
    111 {
    112 
    113 //checks if the player has collided with any trigger
    114 
    115 if(hasCollided)
    116 
    117 {
    118 
    119 //call the delegate
    120 
    121 aaaDelegate(playerTransform);
    122 
    123  
    124 
    125 /*The above line is the same as calling every FollowPlayer()
    126 
    127 * method from every GameObject has the 'Enemy' tag. */
    128 
    129 }
    130 
    131  
    132 
    133 }
    134 
    135 }

      开始,我们定义委托有哪些特性(第十七行)。接着,当委托被定义后,我们在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 Transform
    
    private Transform myTransform;
    
     
    
    void Awake()
    
    {
    
    //get the game object the script is attached to
    
    myTransform = this.GetComponent<Transform>();
    
    }
    
     
    
    //this method makes the enemy follow the player
    
    public void FollowPlayer(Transform playerTransform)
    
    {
    
    //look at the player
    
    myTransform.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 enemy
    
    myTransform.Translate(Vector3.forward * Time.deltaTime* 0.5f);
    
    }
    
    }
    
    }

     

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

     1 //...
     2 
     3  
     4 
     5 //this method makes the enemy fly
     6 
     7 public void Fly(Transform pTransform)
     8 
     9 {
    10 
    11 //if the player is at 4.5f units away
    12 
    13 if(Vector3.Distance(myTransform.position,pTransform.position)<=4.5f)
    14 
    15 {
    16 
    17 //make it fly
    18 
    19 myTransform.Translate(Vector3.up * Time.deltaTime);
    20 
    21 }
    22 
    23 else
    24 
    25 {
    26 
    27 //make it land
    28 
    29 if(myTransform.position.y >= 0.525528f)
    30 
    31 {
    32 
    33 myTransform.Translate(-Vector3.up * Time.deltaTime);
    34 
    35 }
    36 
    37 }
    38 
    39 }
    40 
    41  
    42 
    43 //...

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

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

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

     

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

  • 相关阅读:
    SpringMVC案例3----spring3.0项目拦截器、ajax、文件上传应用
    TCP/IP、UDP、 Http、Socket的差别
    HttpClient 图讲解明
    数据库设计--数据的垂直拆分
    未经处理的异常在 System.Data.dll 中发生。其它信息:在应使用条件的上下文(在 &#39;***&#39; 附近)中指定了非布尔类型的表达式。
    VMware 下扩展linux硬盘空间
    cocos2d_android 第一个游戏
    解决安卓程序安装没图标的问题
    Qt编译错误GL/gl.h: No such file or directory
    【编程题目】二元树的深度
  • 原文地址:https://www.cnblogs.com/leesymbol/p/4162702.html
Copyright © 2020-2023  润新知