C# in Unity 协程&反射

C# in Unity 协程&反射

梚辰 2022.5.3
未经授权禁止转发

1. Coroutines 协程

Coroutines 协程作用

对于任务密集的流程(如A*寻路)将一个任务划分成多个部分,在多个逻辑帧中完成,从而使游戏更流畅。

同时协程可以通过回调来在某任务结束后调用相应函数。

Coroutines 协程使用
  1. 协程不是多线程,是一种异步多任务处理

    *常常容易将协程和多线程混淆,协程是暂停执行此部分的后续代码,等下一帧或满足某些条件时继续执行此部分代码,如果不用yield return则会阻滞当前线程;而多线程是异步同时执行,各线程间不会影响

  2. 使用IEnumerator接口,可以认为是返回值为IEnumerator的函数,或者任意YieldInstruction类型

  3. 配合yield的特殊语句使用(如yield return ...)

  4. StartCoroutine()开启协程,StopCoroutine()StopAllCoroutine()停止协程

    *StartCoroutine()调用时传入方法或方法名,而StopCoroutine()只传入方法名

    StartCoroutine(MyCoroutine(args));
    StartCoroutine("MyCoroutine",args);       StopCoroutine("MyCoroutine"); 
    //MyCoroutine返回值要为IEnumerator
    
  5. 常见yield return 用法

    yield return null;//下一帧以后执行后续代码
    yield return 0;//任意数字,同上
    yield break;//直接终止,类似于return
    yield return StartCoroutine("MyCoroutine");//等待MyCoroutine运行完成时继续执行后面的代码
    yield return new WaitForSeconds(time);//等待固定时间,受DeletaTime影响
    yield return new WaitForSecondsRealtime(time);//等待固定时间,不受DeletaTime影响
    yield return new WaitForEndOfFrame(); //等待直到该帧结束,在Unity渲染每一个摄像机和GUI之后,在屏幕上显示该帧之前。
    yield return new WaitUntil(() => time>1);//等到某判断条件为真时执行后续代码
    yield return new WaitWhile(() => time>1);//等到某判断条件为假时执行后续代码
    

    同时,我们可以自己定义一个YieldInstruction字段,针对不同的条件赋不同的值

    YieldInstruction time;
     
    if(a)
     time = null;
    else if(b)
     time = new WaitForSeconds(1.0f);
    else
     time = new WaitForSeconds(2.0f);
     
    yield return time;
    
  • 协程在Unity中的应用实例

来自Unity官方《C#中级编程》

using UnityEngine;
using System.Collections;

public class CoroutinesExample : MonoBehaviour
{
    public float smoothing = 1f;
    public Transform target;


    void Start ()
    {
        StartCoroutine(MyCoroutine(target));//调用协程
    }

    IEnumerator MyCoroutine (Transform target)
    {
        while(Vector3.Distance(transform.position, target.position) > 0.05f)
        {
            transform.position = Vector3.Lerp(transform.position, target.position, smoothing * Time.deltaTime);

            yield return null;
        }

        print("Reached the target.");

        yield return new WaitForSeconds(3f);

        print("MyCoroutine is now finished.");
    }
}
  • 与属性结合使用,可以发挥协程的强大作用,通过减少轮询来减少消耗性能
using UnityEngine;
using System.Collections;

public class PropertiesAndCoroutines : MonoBehaviour
{
    public float smoothing = 7f;
    //Target 通过鼠标点击事件赋值,此处省略
    public Vector3 Target
    {
        get { return target; }
        set
        {
            target = value;
            
            //停止未结束的Movement,开启新的Movement
            
            StopCoroutine("Movement");
            StartCoroutine("Movement", target);
        }
    }

    private Vector3 target;


    IEnumerator Movement (Vector3 target)
    {
        while(Vector3.Distance(transform.position, target) > 0.05f)
        {
            transform.position = Vector3.Lerp(transform.position, target, smoothing * Time.deltaTime);

            yield return null;
        }
    }
}

2. C#反射

所学视频:【Unity】【主程面试】一节课搞懂c#反射内部原理_哔哩哔哩_bilibili

从C#内存布局说起
  1. 类:是一种类型描述,描述这个类型有哪些数据组成,同时描述一些成员函数
  2. 类的实例:new()一个类的实例,是一个具体的内存对象,指向一块内存,这块内存是所有数据成员的集合
    • 类的成员函数属于代码指令,所有类的实例共用一份代码指令
  3. this实例:成员函数里面使用this,则指的是当前对象实例,通过this来操作当前对象实例的这块内存
  4. 编好一个类型后,编译器知道每个数据相对于对象实例内存块的偏移,也知道每个类的成员函数在代码段的偏移
C#反射及其作用

以Unity引擎为例:Unity是怎么加载脚本,读取脚本里的组件类型,并给物体添加该类型组件?

运用C#反射

由于每一个类都有独立的描述,所以我们新加一个类,就会多一种方式来描述,所有没有办法使用统一的方式来处理不同类或类的实例,所以需要用一种方式来描述所有类。

新的描述方式:

  • 类的实例是一个内存块,内存块的大小就是这个类所有数据成员的大小 ----> 类实例的内存块大小
  • 类有哪些数据成员,可以把数据成员通过数组等方式保存:
    {名字,类型,偏移量}
  • 类有哪些成员函数,通过数组等方式保存:
    {成员函数名字,类型,代码段位置}

通过这种方式,任意的类都可以转化成一种描述,从而解决上面的问题。

每个类,编译器知道其数据偏移,函数代码段位置等信息,可以在运行时为其申请一块内存空间,并通过描述信息来创建实例,并为这个类创建一个type。

通过这种方法,编译器可以为任何类创建实例,然后将内存块传递给构造函数,构造函数帮助初始化数据。

使用方法

Type t = System.Type.GetType(name);
gameObject.AddComponent(t);
  • Type里面存放了类的描述信息,根据这种描述信息并结合实例就可以把数据信息取出来
    • 针对数据信息,运用SetValue() /GetValue()对数据进行访问或者修改
    • 针对成员函数信息,运用getMethod(函数名) /methodInfo.Invoke(实例,参数列表)来获取函数信息或调用函数,函数返回值为Object
C#反射具体使用
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Reflection;

public class Test
{
    public int data1;
    public float data2;
    //...
    public int test(int param1, float param2, string param3)
    {
        //...
        return 1;
    }
}

public class ReflectTutorial:MonoBehaviour
{
    void Start()
    {
        //获取 类型描述Type 的实例
        Type t = System.Type.GetType("Test");
        //若非unity中则需要注明名称空间("名称空间名.类名")
        //根据此 类型描述Type 的实例,构建出一个对象
        var instance = Activator.CreateInstance(t);
        //利用我们存放的数据成员信息给它们赋值
        
        // 根据 instance + (偏移 + 内容) 来访问或修改数据
        FieldInfo[] fieldsinfo = t.GetFields();//获取所有数据
        
        FieldInfo data1Info = t.GetField("data1");//获取单个数据
        
        data1Info.SetValue(instance, 新的属性值);//设置属性值
        Debug.Log((instance as Test).data1);
        
        //调用成员函数
        //获取函数信息
        MethodInfo m = t.GetMethod("test");
        
        System.Object[] funcParams = {1, 1.0f, "111"};
        int? result = m.Invoke(instance, funcParams) as int?;
        //Invoke返回值可能为空
        //注:as必须与引用类型或者可以为null的类型一块使用
        Debug.Log(result);
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梚辰

感谢支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值