Unity的C#编程教程_61_委托和事件 Delegates and Events 详解及应用练习

文章目录

    • C# Delegates
    • C# Events
    • Example Working with C# Delegates and Events
    • Challenge: Teleport Events
    • Practical Event Driven Programming
    • C# Actions
    • C# Return Type Delegates and Func
    • C# Lambda Expressions
    • Practicing C# Delegates with and without Return Types and Parameters
      • 1. Practice Delegate of Type Void With Parameters
      • 2. Practice Delegate of Type Void With No Parameters using Lambda
      • 3. Practice Delegate with Return Type without Parameters
      • 4. Practice Delegate with Return Type and Parameters
    • Simple Callback System

C# Delegates

  • 什么是 delegates 委托
    • 可以看作是一个变量,包含了一个或者多个方法 method
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Main : MonoBehaviour
{

    public delegate void ChangeColor(Color newColor);
    // delegate 可以理解为一种变量,用于保存 method
    // 后面的 void 是对应 method 的类型,这里的 method 需要传入一个颜色参数

    public ChangeColor onColorChange;
    // 这里我们设定一个变量,这个变量的类型就是 ChangeColor

    public delegate void MethodCompleted();
    // 这个方法没有输入参数
    public MethodCompleted methodCompleted;

    // Start is called before the first frame update
    void Start()
    {
        onColorChange = AlterColor;
        // 形式匹配后,我们就可以给 delegate 的变量赋值了
        // 形式不匹配会报错,比如 onColorChange = Task; 就不行
        onColorChange(Color.black);
        // 调用的方式和 method 相同

        methodCompleted = Task;
        // 同样,这里的形式也是匹配的,所以可以赋值
        methodCompleted();
        // 调用
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    public void AlterColor(Color newColor) // 这里的形式要和前面统一,需要有同样类型的传入参数
    {
        Debug.Log("Change color to: " + newColor.ToString());
    }

    public void Task()
    {
        Debug.Log("Task completed!");
    }
}

这种用法看起来不实用啊,直接调用 method 不好吗?

其实我们要用的是多重赋值,即 multicast:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Main : MonoBehaviour
{

    public delegate void ChangeColor(Color newColor);
    // delegate 可以理解为一种变量,用于保存 method
    // 后面的 void 是对应 method 的类型,这里的 method 需要传入一个颜色参数

    public ChangeColor onColorChange;
    // 这里我们设定一个变量,这个变量的类型就是 ChangeColor

    public delegate void MethodCompleted();
    // 这个方法没有输入参数
    public MethodCompleted methodCompleted;

    // Start is called before the first frame update
    void Start()
    {
        onColorChange = AlterColor;
        // 形式匹配后,我们就可以给 delegate 的变量赋值了
        // 形式不匹配会报错,比如 onColorChange = Task; 就不行
        onColorChange(Color.black);
        // 调用的方式和 method 相同

        methodCompleted += Task1;
        methodCompleted += Task2;
        methodCompleted += Task3;
        methodCompleted += Task1;
        methodCompleted -= Task2; // 移除

        if (methodCompleted != null)
        {
            methodCompleted();
        }   
        // 调用 invoke
        // 调用之前要记住先赋值,否则是 null 的话会报错
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    public void AlterColor(Color newColor) // 这里的形式要和前面统一,需要有同样类型的传入参数
    {
        Debug.Log("Change color to: " + newColor.ToString());
    }

    public void Task1()
    {
        Debug.Log("Task1 completed!");
    }

    public void Task2()
    {
        Debug.Log("Task2 completed!");
    }

    public void Task3()
    {
        Debug.Log("Task3 completed!");
    }
}

这样我们就做到了 method 的堆叠!

想象一下,比如在做图像数据增强处理,首先我们要把图像随机裁剪,然后随机转换颜色,最后要统一图像大小。

这样我们需要设计 3 个 method,以后调用的时候需要用一个大的 method (比如 imgProcession)包含这 3 个小的 method,然后进行统一调用。

这里 delegate 的方法就是做到了 method 的打包,而且小 method 的顺序调换也是非常便利的。

C# Events

  • 什么是 event 事件

    • 是一种特殊的 delegate 委托
    • 有 broadcast system 系统,允许其他的 class 和 object
  • 任务说明:

    • 创建 3 个 cube
    • 创建 UI 按钮
    • 点击按钮,cube 变成红色

创建 Main 脚本,挂载到 Main Camera 下面:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Main : MonoBehaviour
{
    public delegate void ClickAction(); // 创建一个 delegate 类
    public static event ClickAction click; // 创建一个 event 变量

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
    
    public void ChangeColor()
    {
        if (click != null)
        {
            click();
        }
    }
    
    
}

在 Button 下面的 On Click() 下面点击 + 号。

把 Main Camera 拖拽到 Object 中,在 Function 中选择 Main.ChangeColor 方法,即表示点击了 Button 以后,运行 Main Camera 游戏对象下 Main 脚本中的 ChangeColor 方法。

创建一个脚本 Cube 挂载到 3 个 Cube 下面:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Cube : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        Main.click += ChangeColor;
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    public void ChangeColor()
    {
        this.gameObject.GetComponent().material.color = Color.red;
    }

}

Cube 下面的脚本是独立的,不依赖于 Main,3 个之间也不相互依赖。Cube 的脚本会监听 Main 中的 broadcast 情况,完成对应的动作。

传统的做法是,Main 控制一切,所以需要载入 3 个 cube,然后控制颜色的改变,如果不是 3 个而是更多,这样的做法显然会有问题,比如 for 循环需要很长时间等。

这里我们让所有的游戏对象直接对按下按钮这个动作进行监听,只要监听到了,即自己执行对应动作(同样 cube 也不需要接入 Main Camera,两者是隔离的)。

这里注意:event 是一种特殊的 delegate,所以用 delegate 可以达到同样效果,但是 event 的好处在于,调用 method 仅允许通过 delegate 进行,而不能直接调用。

另外,堆叠到 event 的 method 在完成使命后记得退出,以避免报错:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Cube : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        Main.click += ChangeColor;
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    public void ChangeColor()
    {
        this.gameObject.GetComponent().material.color = Color.red;
    }

    private void OnDisable()
    {
        Main.click -= ChangeColor; // 这里设置退出,以避免程序报错问题
    }
}

Example Working with C# Delegates and Events

  • 来看一个 delegate 和 event 的案例

在 Main 脚本中,我们有一个 event 叫做 click,这个变量是基于 ClickAction 这个 delegate 所创建:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Main : MonoBehaviour
{
    public delegate void ClickAction(); // 创建一个 delegate 类
    public static event ClickAction click; // 创建一个 event 变量

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
    
    public void OnClick() // 点击按钮以后,会自动执行 Main 脚本下的这个函数
    {
        if (click != null) // 如果不为空
        {
            click(); // 则运行该事件
        }
    }
    
    
}

Cube 下面的脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Cube : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        Main.click += ChangeColor;
        // 为事件添加元素
        // 具体的元素内容其实与 Main 脚本无关,比如这里的 ChangeColor 方法是在本脚本中定义
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    public void ChangeColor()
    {
        this.gameObject.GetComponent().material.color = Color.red;
    }

    private void OnDisable()
    {
        Main.click -= ChangeColor; // 这里设置退出,以避免程序报错问题
    }
}

现在创建一个 Sphere,需要实现的是,点击按钮后,该球自由下落

首先需要为 Sphere 添加一个 Rigidbody,然后把 Use Gravity 的勾去除,添加同名脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Sphere : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        Main.click += Fall;
        // 为事件添加元素,该元素由本脚本中定义
        // 这里我们不用接入 Main 脚本挂载的游戏对象去获取按钮的点击情况
        // 只需要为 Main 脚本中的事件添加对应的元素即可!
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    public void Fall() // 该方法用于激活重力选项,这个方法的形式需要和 event 定义的形式相同,即这里无需输入参数
    {
        this.gameObject.GetComponent().useGravity = true;
    }
}

这里我们可以发现,球和方块都对应按钮进行各自的设定动作,但是却互不关联,也互不干扰。

Challenge: Teleport Events

  • 任务说明:
    • 使用 delegate 和 event
    • 按下空格键,为一个 cube 设定一个指定的位置,同时为一个 sphere 改变颜色

建立 Main 脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Main : MonoBehaviour
{
    public delegate void ClickAction(); // 创建一个 delegate 类
    public static event ClickAction clickSpace;
    // 创建一个 event 变量
    // 设定为 static 后,不需要进行实例化

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space)) // 按下空格键
        {
            clickSpace(); // 执行事件
        }
    }
    
}

创建 cube 及其脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Cube : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        Main.clickSpace += ChangePosition;
        // 为事件添加元素
        // 具体的元素内容其实与 Main 脚本无关,比如这里的 ChangeColor 方法是在本脚本中定义
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    public void ChangePosition()
    {
        this.transform.position = new Vector3(2, 3, 1);
    }

    private void OnDisable()
    {
        Main.clickSpace -= ChangePosition; // 这里设置退出,以避免程序报错问题
    }
}

创建 sphere 及其脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Sphere : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        Main.clickSpace += ChangeColor;
        // 为事件添加元素,该元素由本脚本中定义
        // 这里我们不用接入 Main 脚本挂载的游戏对象去获取按钮的点击情况
        // 只需要为 Main 脚本中的事件添加对应的元素即可!
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    public void ChangeColor()
    {
        this.gameObject.GetComponent().material.color = Color.blue;
    }

    private void OnDisable()
    {
        Main.clickSpace -= ChangeColor; // 这里设置退出,以避免程序报错问题
    }
}

Practical Event Driven Programming

  • 实践由 event 驱动的编程

  • 游戏说明:

    • 有 Player 对象
    • 有 UI 组件
    • 当按下空格键的时候,假设 Player 死亡
    • UI 组件显示死亡的总次数

创建一个 cube 命名为 Player,挂载同名脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            Death(); // 按下空格键代表死亡,调用对应的方法
        }
    }

    public void Death() // 死亡后执行
    {

        GameObject.FindObjectOfType().ResetPlayer();
        // 接入 GameManager 并执行其下面的 ResetPlayer 方法
        GameObject.FindObjectOfType().UpdateText();
        // 接入 UIManager 并执行其下面的 UpdateText 方法

    }
}

创建 UI,Text,命名为 DeathCountText,在 Canvas 上挂载 UIManager 脚本,并把 DeathCountText 游戏对象拖拽赋值到 UIManager 脚本下的 Death Count Text 格子中:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; // 需要引入 UI 的库

public class UIManager : MonoBehaviour
{
    public int deathCount;
    public Text deathCountText;

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    public void UpdateText() // 更新文本信息
    {
        deathCount++; // 死亡次数增加
        deathCountText.text = "Death count: " + deathCount; //显示
    }

}

创建空的游戏对象,命名为 GameManager,挂载同名脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameManager : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    public void ResetPlayer()
    {
        Debug.Log("Rest Player!");
    }

}

这种编程方法的问题在于,Player 脚本需要接入 GameManager 和 UIManager 的脚本,这对于模块化编程和代码复用来说不是好事。

所以更好的选择应该是 Player 应该是独立的,那就要用到 delegate 和 event:

首先修改 Player 的脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    public delegate void OnDeath(); // 设定委托
    public static event OnDeath onDeath; // 建立事件

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            Death(); // 按下空格键代表死亡,调用对应的方法
        }
    }

    public void Death() // 死亡后执行
    {
        /*
        GameObject.FindObjectOfType().ResetPlayer();
        // 接入 GameManager 并执行其下面的 ResetPlayer 方法
        GameObject.FindObjectOfType().UpdateText();
        // 接入 UIManager 并执行其下面的 UpdateText 方法
        */

        if (onDeath != null) // 如果这个事件中被添加了元素
        {
            onDeath(); // 执行该事件
        }
    }
}

然后是 GameManager:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameManager : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    public void ResetPlayer()
    {
        Debug.Log("Rest Player!");
    }

    public void OnEnable() // 游戏对象启用并处于激活状态的时候调用该函数
    {
        Player.onDeath += ResetPlayer;
        // 为事件添加元素
        // 注意这里必须使用 +=,使用 = 会报错
        // 在游戏启动的时候,为 Player 的死亡事件添加了一个 ResetPlayer 的元素
    }
}

然后是 UIManager:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; // 需要引入 UI 的库

public class UIManager : MonoBehaviour
{
    public int deathCount;
    public Text deathCountText;

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    public void UpdateText() // 更新文本信息
    {
        deathCount++; // 死亡次数增加
        deathCountText.text = "Death count: " + deathCount; //显示
    }

    public void OnEnable() // UI激活的时候
    {
        Player.onDeath += UpdateText;
        // 为事件添加元素
        // 这里表示游戏开始的时候,为 Player 死亡这个事件增加一个 UpdateText 的操作
    }
}

C# Actions

  • 什么是 action
    • 与 delegate 和 event 很类似,相当于是两者的结合

建立 Player 脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    public delegate void OnDamageReceived(int currentHealth);
    public static event OnDamageReceived onDamage;

    public int Health { get; set; } // 设定一个血量的 property

    // Start is called before the first frame update
    void Start()
    {
        Health = 10; // 初始化为 10 格血
    }

    // Update is called once per frame
    void Update()
    {

    }

    void Damage() // 收到伤害
    {
        Health--; // 扣血
        if (onDamage != null)
        {
            onDamage(Health);
        }
    }

}

建立 UIManager 脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; // 需要引入 UI 的库

public class UIManager : MonoBehaviour
{
    public void UpdateHealth(int health)
    {
        Debug.Log("Current Health: " + health);
    }

    public void OnEnable()
    {
        Player.onDamage += UpdateHealth;
    }
}

以上是用 event 的方法,我们可以改用 action:

修改 Player 脚本,首先要添加 System 库:

using System; // 这个库让我们可以使用 action
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player : MonoBehaviour
{
    /*
    public delegate void OnDamageReceived(int currentHealth);
    public static event OnDamageReceived onDamage;
    */
    public static Action onDamage;
    // 改成 action 后仅需 1 行代码
    // 如果这里没有传入参数,则 <> 中空着就行

    public int Health { get; set; } // 设定一个血量的 property

    // Start is called before the first frame update
    void Start()
    {
        Health = 10; // 初始化为 10 格血
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space)) // 假设按下空格键代表受到伤害
        {
            Damage();
        }
    }

    void Damage() // 收到伤害
    {
        Health--; // 扣血
        if (onDamage != null)
        {
            onDamage(Health);
        }
    }

}

使用 Action 和 event 是完全一样的效果,代码更简洁明了!

C# Return Type Delegates and Func

  • 什么是 functional delegate

    • 带有返回值的 delegate
  • 任务说明:

    • 输入一个字符串
    • 返回该字符串的长度

传统的方法是,创建 Main 脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Main : MonoBehaviour
{

    // Start is called before the first frame update
    void Start()
    {
        string s = "Good";
        int l = GetLenth(s);
        Debug.Log("Lenth: " + l);
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    int GetLenth(string s)
    {
        return s.Length;
    }
}

改用 delegate:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Main : MonoBehaviour
{
    public delegate int WordLenth(string text);
    // 输入和输出的类型要和下面的 GetLenth 方法匹配
    WordLenth wl;
    // 实例化一个 delegate

    // Start is called before the first frame update
    void Start()
    {
        wl = GetLenth; // 初始化赋值

        Debug.Log("Lenth: " + wl("GOOOD")); // 使用起来和直接调用方法一样
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    int GetLenth(string s)
    {
        return s.Length;
    }
}

要使用 functional delegate,需要添加 system 库:

using System; // 使用 Func<> 需要这个库
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Main : MonoBehaviour
{

    public Func WordLenth;
    // 这里表示传入的是 string 类型,输出的是 int 类型
    // 不再需要设定 delegate,也不需要进行实例化



    // Start is called before the first frame update
    void Start()
    {
        WordLenth = GetLenth; // 初始化赋值

        Debug.Log("Lenth: " + WordLenth("GOOOD")); // 使用起来和直接调用方法一样
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    int GetLenth(string s)
    {
        return s.Length;
    }
}

C# Lambda Expressions

  • 什么是 Lambda 表达式 Lambda Expression
    • 在一行里面写一个 method

看之前的案例:

using System; // 使用 Func<> 需要这个库
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Main : MonoBehaviour
{

    public Func WordLenth;
    // 这里表示传入的是 string 类型,输出的是 int 类型
    // 不再需要设定 delegate,也不需要进行实例化

    // Start is called before the first frame update
    void Start()
    {
        WordLenth = GetLenth; // 初始化赋值

        Debug.Log("Lenth: " + WordLenth("GOOOD")); // 使用起来和直接调用方法一样
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    int GetLenth(string s)
    {
        return s.Length;
    }
}

Practicing C# Delegates with and without Return Types and Parameters

1. Practice Delegate of Type Void With Parameters

  • 练习使用 delegate 和 lambda 表达式
    • 创建一个 delegate 用于加法计算,不需要返回值
    • 配合使用 lambda 表达式

不需要返回值,那么我们可以使用 void 类型,对应的就是 Action(反之,如果需要返回值,那我们使用 Func):

using System; // 使用 Func<> 需要这个库
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Main : MonoBehaviour
{
    public Action GetSum;
    // 不需要返回值,则使用 Action
    // 括号中的是我们输入元素的类型

    // Start is called before the first frame update
    void Start()
    {
        GetSum = DoSum; // 赋值
        GetSum(5, 6); // 调用
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    void DoSum(int a, int b) // 计算加法的函数
    {
        var result = a + b;
        Debug.Log("The answer is: " + result);
    }
}

如果该用 lambda 表达式:

using System; // 使用 Func<> 需要这个库
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Main : MonoBehaviour
{
    public Action GetSum;
    // 不需要返回值,则使用 Action
    // 括号中的是我们输入元素的类型

    // Start is called before the first frame update
    void Start()
    {
        GetSum = (a, b) => Debug.Log("The answer is: " + (a + b)); // 赋值
        GetSum(5, 6); // 调用
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
    /*
    void DoSum(int a, int b) // 计算加法的函数
    {
        var result = a + b;
        Debug.Log("The answer is: " + result);
    }
    */
}

在 lambda 表达式中我们也可以设置多行代码:

using System; // 使用 Action<> 需要这个库
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Main : MonoBehaviour
{
    public Action GetSum;
    // 不需要返回值,则使用 Action
    // 括号中的是我们输入元素的类型

    // Start is called before the first frame update
    void Start()
    {
        GetSum = (a, b) =>
        {

            var result = a + b;
            if (result > 10)
            {
                Debug.Log("The result is greater than 10.");
            }

            Debug.Log("The answer is: " + result);
        };

        GetSum(5, 6); // 调用
        
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

2. Practice Delegate of Type Void With No Parameters using Lambda

  • 任务说明:
    • 创建一个 delegate,没有输入参数
    • 显示游戏对象的名称

传统实现方法:

using System; // 使用 Action 需要这个库
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Main : MonoBehaviour
{
    public Action onGetName;
    // 这里不需要传入参数,所以没有 <>

    // Start is called before the first frame update
    void Start()
    {
        onGetName = GetName;
        onGetName();
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    void GetName()
    {
        Debug.Log("The name is: " + gameObject.name);
    }
}

使用 lambda 表达式:

using System; // 使用 Action 需要这个库
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Main : MonoBehaviour
{
    public Action onGetName;
    // 这里不需要传入参数,所以没有 <>

    // Start is called before the first frame update
    void Start()
    {
        onGetName = () => Debug.Log("The name is: " + gameObject.name);
        // 这里没有传入参数,所以括号内是空的

        onGetName();
    }

    // Update is called once per frame
    void Update()
    {
        
    }
    /*
    void GetName()
    {
        Debug.Log("The name is: " + gameObject.name);
    }
    */
}

在 lambda 表达式中使用更复杂的代码,只需要用大括号即可。

3. Practice Delegate with Return Type without Parameters

  • 练习使用带有返回值的 delegate

  • 任务说明:

    • 创建一个 delegate 返回游戏对象名字的长度
using System; // 使用 Func 需要这个库
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Main : MonoBehaviour
{
    public Func onGetNameLenth;
    // 这里返回的参数是 int

    // Start is called before the first frame update
    void Start()
    {
        onGetNameLenth = () => gameObject.name.Length; //赋值
        Debug.Log("Name lenth: " + onGetNameLenth()); //调用
    }

    // Update is called once per frame
    void Update()
    {
        
    }

}

4. Practice Delegate with Return Type and Parameters

  • 任务说明:
    • 输入 2 个参数,求和,返回结果
    • 使用 delegate
using System; // 使用 Func 需要这个库
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Main : MonoBehaviour
{
    public Func onGetSum;
    // 前面两个是输入类型,最后一个是输出类型,都是 int

    // Start is called before the first frame update
    void Start()
    {
        onGetSum = (a, b) => a + b;

        Debug.Log("The answer is: " + onGetSum(5, 6));
    }

    // Update is called once per frame
    void Update()
    {
        
    }

}

Simple Callback System

  • 任务说明:
    • 设计一个简单的反馈系统
    • 在游戏运行 5 秒后,给一个反馈提示
using System; // 使用 Action 和 Func 需要这个库
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Main : MonoBehaviour
{
    public Action onCallback;

    // Start is called before the first frame update
    void Start()
    {
        onCallback = () => Debug.Log("Passed 5 seconds");

        StartCoroutine(CallbackTime(onCallback)); // 启动协程序

        // 这里可以继续简化成 StartCoroutine(CallbackTime(() => Debug.Log("Passed 5 seconds")));
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    public IEnumerator CallbackTime(Action OnComplete = null) // 设定一个协程,这里的 null 表示可以后续没有动作
    {
        yield return new WaitForSeconds(5); // 等待 5 秒

        if (OnComplete != null) // 若果有需要在等待后执行的程序
        {
            OnComplete(); // 执行传入的 Action
        }
    }
}

这样设计的好处在于,我不需要在协程中定义具体的执行代码,协程仅仅用于计时。

你可能感兴趣的:(unity,c#,unity,unity3d,游戏开发)