Unity3D脚本语言的类型系统

C#的类型系统

C#是静态,安全,大多数时候显示的语言,而且所有类型都派生自Object类,同时Unity都是以MonoBehavior这个类作为基础的。

值类型与引用类型

引用类型

引用类型总是从托管堆分配,而C#要求所有对象都是用new关键词来创建。

简单介绍一下new操作符所做的事情:

  1. 计算所需内存空间,new操作符会计算其所有基类中定义的所有实例字段所需字节数。同时为了方便Mono运行时管理对象,还有一些额外信息需要托管堆为其分配空间,如类型对象指针和同步索引块。
  2. 在托管堆上分配其所要的内存空间,分配的所有字节设为0
  3. 初始化对象的类型对象指针和同步索引块
  4. 调用类型的实例构造器,同时编译器会自动调用当前类型的基类构造器
  5. 最后返回一个新建对象的引用,这就是说新建的变量是一个引用而不是对象本身

综上,引用类型可以概括为以下四点:

  1. 存储引用类型对象的内存空间从托管堆上分配
  2. 每一个对象都有额外的成员为Mono运行时提供操作该对象的信息
  3. 对象中的其他字段的字节总是0
  4. 并没有一个关键词能删除对象,所以当没有空间可用时会触发垃圾回收

值类型

如果所有类型都是引用类型,那么会消耗巨大的内存分配。因此,值类型实例都是分配在线程栈上的,并且不受垃圾回收(GC)影响

此外,值类型不能派生出其他类型,而且是隐式密封的。这就导致无须提供额外信息,也就不需要再托管堆上为其分配空间

并非所有值类型都分配在线程栈上,比如数组中的元素,引用类型中的值类型字段,迭代器中的局部变量。

引用类型总是分配在托管堆上,而值类型并不总是在线程栈上。

综上,对于值类型可以概括为:

  1. 不派生出其他类型,也不需要从其他类型派生
  2. 值类型不可变,指的是他没有提供会更改其字段的成员
  3. 值类型都派生自System.ValueTybe
  4. 值类型有已装箱和未装箱两种表示方式。装箱指的是将值类型转换为引用类型,很多情况需要获取和操作对值类型实例的引用,这便需要装箱机制

具体类型分类

引用类型:

​ 采用 Class,Interface,Delegate关键词来声明的自定义引用类型

​ C#内建的引用类型:Object, string(string类型是对字符串的一个引用),List类,Decoder

值类型:

​ 结构:数字型结构(System.Int32, System.Float),布尔型结构(System.Boolean),自定义的结构

​ 枚举: System.IO.FileAttribute等

当我们深入了解各个类型之间的关系时,所有结构都派生自抽象类System.ValueTybe,枚举的基类System.Enum也都派生自System.ValueTybe。事实上所有值类型都派生自这个类

Unity脚本语言中的引用类型

UnityEngine.Object是Unity3D中C#脚本语言最基本的类,所有的脚本派生自MonoBehavior类

Unity3d中脚本会按照规定流程来执行
请添加图片描述

概括一下:

  1. 调用所有的Awake方法,再调用所有的Start方法

  2. 游戏逻辑循环

    1)所有FixedUpdate方法

    2)物理模拟

    3)OnEnter,Exit,Stay触发函数

    4)OnEnter,Exit,Stay碰撞函数

  3. 刚体插值,主要作用于transform.position和transform.rotation

  4. 输入事件如OnMouseDown,OnMouseUp

  5. 所有Update方法

  6. 高级动画,混合并应用到变换

  7. 所有LateUpdate

  8. 渲染

  9. 对象销毁或退出场景

此外,加载阶段的事件执行条件

  • Awake 是在gameObject被设置为Active后立即触发的, 且仅触发一次, 无论是场景中、Instantiate、还是SetActive(true);
  • OnEnable 只有在gameObject为Active且脚本enable为true时才会被触发, 如果enable为true时gameObject处于关闭状态, 则在gameObject被打开时跟在Awake后被触发;
  • Start 是在第一次Update之前触发的, 只有在gameObject为Active且脚本enable为true时才会被触发, 且仅触发一次;

Unity脚本语言中的值类型

向量类型主要表示位置和方向,以及纹理坐标,网格切线等

既然说到了向量就复习一下点乘和叉乘吧

点乘积: 两向量点乘等于他们的模长乘以向量夹角cos,可以判断当前物体是否朝向另一个物体,只需要计算transform.forward和(target.transform.position- transform.position)的点乘,大于0则面对另一个物体

叉乘:两向量的叉乘得到的向量与这两个向量组成的平面垂直,得到的模长等于他们的模长与夹角sin相乘,满足右手法则

其他的比如Color,Ray,Touch(描述手指触摸屏幕的状态)

装箱和拆箱

我们有时候就是需要一个引用类型,比如我们使用ArrayList来容纳Vector3结构,ArrayList类中的Add方法参数就是一个Object类型。但向量不是一个引用类型,这时候Vector3实例就必须转换成在托管堆上分配的对象,且必须获得对象的引用。

介绍一下装箱的步骤:

  1. 在托管堆队中分配内存,此时就需要各个字段以及那两个额外成员所需的内存
  2. 将值类型字段复制到新分配的堆内存中
  3. 返回对象地址,也就是对象的引用

我们可以发现我们只是将值类型变量复制了一份到托管堆上,我们改变原始值不会改变箱内的值

当然我们读取ArrayList中的元素便会拆箱,我们需要告诉编译器需要拆箱成什么类型(因为他已经是Object类型了)。

拆箱具体过程就是获取ArrayList中索引为0的元素包含的引用,再将其指向的对象复制到值类型的实例去,拆箱时我们要明确指定为最初的值类型。

从这些步骤我们就能看出由于装箱拆箱、复制会影响程序的速度和内存,而且由于频繁的操作托管堆会增加GC次数,所以日常开发中我们并不常用这类会触发装箱机制的类型,而是采用List这样的泛型

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值