从零开始的 STL 实现记录
前言
从上个月的四号创建 TinySTL以来已经过了一个多月,中间还准备了一场面试放了暑假,鸽了很久。但总之这个亲手实现基本 SGI-STL 的项目终于要完成了!
项目链接:https://github.com/Qlipphoth/TinySTL
在实现 STL 的过程中,我对 C++ 的语法与模板的使用有了更深入的理解,和一个月前确实不可同日而语,只能说 STL 不愧是被称为任何 C++ 初学者都得手搓一两遍的项目。为了加深自己的理解与印象,博主会陆续将 STL 的实现过程与笔记都整理至博客中,方便自己回顾的同时也希望能帮助到后续想要或正在实现 STL 的读者们。
STL 中有六大组件:
Allocator (空间配置器)
Iterator (迭代器)
Container (容器)
Alogorithm (算法)
Functor (仿函数)
Adapter (适配器)
六大组件的相互套用,成就了 STL 强大的功能。值得注意的是,虽然六大组件看似独立,但在实际实现某一组件的过程中,往往会涉及到其他组件的相关内容,如迭代器中的一些操作就使用了算法组件中的一些 ...
UGUI 源码解析
前言
Unity UI(UGUI) 是一套基于 GameObject 的 UI 系统,提供了从事件检测到渲染再到用户交互的完整功能。基于 UGUI 系统,我们可以快速构建出一个完整的 UI 界面。虽然 UGUI 使用简单,但是作为一名有追求的游戏开发者,我们不能只停留在使用的层面,更应该深入了解其原理,这样才能更好的根据需求优化甚至扩展 UGUI。
本系列文章将深入 UGUI 底层,从源码角度解析 UGUI 的实现原理,帮助大家建立起对 UGUI 的更深刻理解。
参考资料
本系列文章使用的 UGUI 源码来自于 Unity 官方的开源版本代码:https://github.com/Unity-Technologies/uGUI
由于该开源版本的 UGUI 源码较为老旧,许多功能与新版本 Unity 中所展示的并不一致。因此建议大家使用早期的 Unity 版本进行实验与学习,如 Unity 2019.4.1f1。
Unity 官方提供了许多文档与教程:
UGUI: https://docs.unity.cn/Packages/com.unity.ugui@1.0/manua ...
TortoiseSVN 简单入门
前言
Subversion(SVN) 是一个开源的版本控制系統, TortoiseSVN 是 Subversion 版本控制系统的一个免费开源客户端, 适用于 Windows 操作系统。TortoiseSVN 提供了一个用户友好的界面, 使得用户可以方便地访问 Subversion 版本控制系统,本文将简要介绍 TortoiseSVN 的使用。
TortoiseSVN 的使用
安装
下载地址: https://tortoisesvn.net/downloads.html, 页面里有语言包补丁的下载链接。
这里要注意安装时一定要勾选 command line client tools,这样才能在命令行中使用 svn 命令,以及启动服务端。
创建 SVN 仓库
一般在使用 SVN 时都会有一个远程的服务器,通过服务器的 URL 就可以 Check Out 仓库,这里为了演示方便,我们在本地创建一个 SVN 仓库充当远程服务器:
这里我们将 RemoteRepo 作为远程仓库,LocalRepo 作为本地仓库。
在 RemoteRepo 目录下右键,选择 TortoiseSVN -> ...
Lua 源码解析(二):基本数据类型 —— 表(下)
前言
在 上一篇文章 中我们探讨了 Lua 中表的基本结构,以及表的创建、访问及插入元素等操作。但 Lua 中的表还有一个非常重要的特性:元表(metatable)。元表是 Lua 中表的一个重要特性,它可以让我们对表进行一些特殊的操作,比如重载表的操作符、修改表的行为等。我们甚至可以利用元表来实现面向对象编程。本文将继续探讨 Lua 中表的元表。
元表与元方法
每个值都可以有 元表(metatable)(注意,是所有)。元表 是定义了原始数据在某些事件下行为的一个普通Lua表。你可以通过设置其元表的某些特定属性来改变某个值的某些行为。举个例子,一个非数字值进行加法操作时,Lua会在这个值的元表中查找__add属性函数,找到了的情况下,Lua就会调用这个函数来执行加法操作:
1234567891011mt = {}mt.__add = function (a, b) return a.a + b.aenda = {a = 1}b = {a = 2}setmetatable(a, mt)setmetatable(b, mt ...
Lua 源码解析(二):基本数据类型 —— 表(上)
前言
使用表来统一表示 Lua 中的一切数据,是Lua区分于其他语言的一个特色。这个特色从最开始的Lua版本保持至今,很大的原因是为了在设计上保持简洁。Lua 表分为数组和散列表部分,其中数组部分不像其他语言那样,从0开始作为第一个索引,而是从1开始。散列表部分可以存储任何其他不能存放在数组部分的数据,唯一的要求就是键值不能为nil。尽管内部实现上区分了这两个部分,但是对使用者而言却是透明的。使用Lua表,可以模拟出其他各种数据结构——数组、链表、树等。
table 的结构
表的节点以及表本身的结构均定义在 lobject.h 中:
12345678910111213141516/*** Nodes for Hash tables: A pack of two TValue's (key-value pairs)** plus a 'next' field to link colliding entries. The distribution** of the key's fields ('key_tt' and ' ...
Lua 源码解析(二):基本数据类型 —— 字符串
前言
Lua 中共有两种字符串的变体类型,分别是短字符串和长字符串。在 Lua 中,字符串是一种不可变的数据类型,即一旦创建,就不能再被修改。因此,Lua 会维护一个字符串池,在创建字符串之前会先检查字符串是否已经存在,如果存在则直接返回,否则创建一个新的字符串。为此 Lua 会对字符串做很多额外的工作,如计算它们的哈希值,初始化字符串池等等。我们将在本文中详细讨论这些内容。
String 的定义
在 Lua 中的数据类型 中我们介绍了 Lua 中字符串的类型定义:
12345678/* Variant tags for strings */#define LUA_VSHRSTR makevariant(LUA_TSTRING, 0) /* short strings */#define LUA_VLNGSTR makevariant(LUA_TSTRING, 1) /* long strings */#define setsvalue(L,obj,x) \ { TValue *io = (obj); TString *x_ = (x); \ val_(io).g ...
Lua 源码解析(二):基本数据类型 —— Lua 中的数据类型
前言
本节开始,我们将分析 Lua 中各种数据类型的定义与实现。众所周知,Lua 是一种动态弱类型语言,在使用时我们不需要显式地声明变量的类型,而是由 Lua 运行时根据变量的值自动推断其类型。
所谓动态弱类型语言,是指在运行时才确定变量的类型,而且变量的类型可以随时改变。这种特性使得 Lua 在编程时更加灵活,但也增加了一些运行时的开销。为了达到动态类型效果,Lua 需要在内部维护一些数据结构来表示不同的数据类型。本节我们将从 Lua 中的类型定义开始,逐步分析 Lua 中的基本数据类型。
本文基于 Lua 5.4 版本源码进行分析。
Lua 中的类型定义
Lua 中一些比较重要的宏被放在了 lua.h 中,其中就包括了 Lua 中的数据类型定义。我们可以在 lua.h 中找到如下的定义:
12345678910111213141516/*** basic types*/#define LUA_TNONE (-1)#define LUA_TNIL 0#define LUA_TBOOLEAN 1#define LUA_TLIGHTUSERDATA 2#define LUA_T ...
UGUI 番外之合批
Refs:
Unity3D UGUI系列之合批
UGUI学习 - 合批规则
UGUI合批源码分析及优化
前言
无论是我们的日常开发还是面试,UI 的合批永远是一个绕不开的话题。本文将分析 UGUI 中的合批机制,力图帮助大家以及笔者自己彻底搞清楚这一重要的概念。
批处理
批处理是渲染中常用的一种优化手段,它的核心思想是将多个绘制操作合并为一个绘制操作,从而减少 CPU 与 GPU 之间的通信次数(即 Draw Call),提高渲染效率。
在介绍批处理之前我们先看看一个普通的3D模型是怎么渲染出来的(详见 图形学补完计划(二):Rendering Pipeline):
CPU 准备好这个模型的网格、用到的贴图和Shader,然后 GPU 将网格、贴图、Shader加载到显存里面。
CPU 设置渲染状态。所谓设置渲染状态,就是 CPU 设置渲染网格时需要使用的使用的Shader,贴图等信息。
CPU 设置完毕渲染状态后,向 GPU 发出渲染的指令,然后GPU才开始按照 2 中设置的 Shader 和贴图真正渲染这个模型,并把渲染后的结果层递到屏幕上。CPU 向 GPU 发出 ...
Unity 补完计划(四):UGUI-16:Dropdown
前言
本节我们将分析 UGUI 中的最后一个组件:Dropdown。Dropdown 意为下拉菜单,由多个我们之前分析过的 UI 组件共同组成,如 Button、Toggle、ScrollRect 等。Dropdown 是一个非常常用的 UI 组件,我们可以用它来实现下拉菜单、选项卡等功能。
Dropdown
Dropdown 的属性
在场景中新建一个 Dropdown 组件,可以看到其组成如下:
其中,Label 即代表显示在 Dropdown 主界面上的文本,Arrow 代表下拉箭头。Template 是一个比较特殊的对象,他是一个预制好的下拉列表模板,当点击 Dropdown 时,会根据此模板生成对应的下拉列表。
Template 本身是一个 ScrollRect,Item 即为下拉列表中的每个选项。
再来查看 Dropdown 的属性:
其中 Template 即为上面提到的下拉列表模板,Caption Text 与 Caption Image 分别代表 Dropdown 主界面上的文本和图片,而 Item Text 与 Item Image 则代表每个选项的文本与图片。 ...
Unity 补完计划(四):UGUI-15:ScrollBar & ScrollRect
前言
ScrollBar 和 ScrollRect 通常配合使用以实现滚动视图。ScrollBar 用于控制 ScrollRect 的滚动位置,而 ScrollRect 用于显示滚动内容。本文将介绍这两个组件的实现。
ScrollBar
ScrollBar 的作用实际上与 Slider 十分类似,代码的实现也大同小异。但就使用上的区别而言,Slider 主要用于数值的调整,而 ScrollBar 则大部分情况下用于滚动条。
ScrollBar 的属性
ScrollBar 的属性与 Slider 的属性基本一致,都有一个 OnValueChanged 事件,用于监听值的变化。不同之处在于 Slider 除了 HandleRect 之外还有一个 FillRect 用于填充的显示,而 ScrollBar 只有 HandleRect。
ScrollBar 的交互
ScrollBar 也实现了 OnXXXDrag 系列事件以及 OnPointerXXX 系列事件,用于处理拖拽以及点击的交互。
拖拽类事件
1234567891011121314151617181920212223242526 ...
Unity 补完计划(四):UGUI-14:InputField
前言
输入框 InputField 也是 UGUI 中较为常见的组件,经常被用于设置用户名、密码以及聊天框等。由于需要处理各种输入情况,因此 InputField 的代码量巨大,足足达到了 3000 行。这其中的大部分代码都用于处理输入时的细节,如对不同类型输入的限制、光标的显示、拖拽选中区域等。作为研究 UGUI 机制的系列文章,我们大可不必深入研究这些细节,只需要了解 InputField 对于不同的事件是如何处理的即可。
InputField 的属性
InputField 有非常多的可配置属性,对上述各属性的解释如下:
Text Component :文本组件,也就是显示输入所用到的 Text 组件
Text :文本内容
Character Limit :字符长度限制
Content Type :内容类型(数字、邮箱、字母、密码共十种类型选择)
Standard:标准,允许所有字符
Autocorrected:自动纠正,允许所有字符,但会自动纠正(针对某些平台)
Integer Number:整数,只允许输入数字
Decimal Number:小数,只允许输 ...
Unity 补完计划(四):UGUI-13:Toggle & ToggleGroup & Slider
前言
Toggle (开关)与 ToggleGroup (开关组)经常被用于 UI 界面的各种选项操作中,前者多用于开启或关闭某项功能,而后者则用于从多个选项中选择一项。而 Slider (滑动条)则多用于连续的数值调节中。
本文将分析这些组件的实现。
Toggle
事件的触发
与 Button 类似,Toggle 也支持在点击时触发事件,但开关具有两个状态,因此 Toggle 绑定的也是 UnityEvent<bool> 类型的事件。
123public class ToggleEvent : UnityEvent<bool> {}public ToggleEvent onValueChanged = new ToggleEvent();
在 Set() 函数中,会根据开关的状态以及配置来触发事件。此外,Set() 中还调用了 PlayEffect() 函数,用于设置 CheckMark 的显示。
1234567891011121314151617181920212223void Set(bool value, bool s ...
Unity 补完计划(四):UGUI-12:Selectable & Button
前言
经过前面数篇文章的分析,现在我们已经对 UGUI 的布局以及渲染过程有了一定的了解,也分析了 Image 以及 Text 等显示类组件的实现。但 UI 元素除了排版与显示外,最重要的功能便是交互了。我们在 EventSystem 中介绍了 UGUI 接受以及处理事件的流程。那么,具体的 UI 元素又该如何接收事件以及按照各自的逻辑处理呢?
这个故事还得从 Selectable 说起。
Selectable
Selectable 是 UGUI 中所有可交互元素的基类,实现了包括 IMoveHandler、IPointerXXXHandler、ISelectHandler 等接口,用于处理各种基本的交互事件。以此派生出的 UI 组件只需要再实现自己独特的 Handle 接口即可。
Selectable 的成员变量
为了维护可选中物体的状态以及交互逻辑,UGUI 定义了一些结构体以及枚举类,这些数据都会在 Selectable 的逻辑处理中用到。
1234567891011121314private Navigation m_Navigation = Navigation.defaul ...
Unity 补完计划(四):UGUI-11:Text
前言
Text 是 UGUI 中最常用的组件之一了,用于显示文本。无论是 UI 界面还是对话框,都会用到 Text。
可以看到,UGUI 渲染文字的方式与图片类似,一个基本的字符也是由四个顶点及分割出的两个三角形组成的。
与 Image 类似,Text 也继承自 MaskableGraphic,并且实现了 ILayoutElement 接口。虽然显示文字的功能非常复杂,但 Text 本身却并不涉及到底层的处理,而是把功能全都交给了一个名为 TextGenerator 的类,因此 Text 的代码量并不大。
Text 的属性
123[SerializeField] private FontData m_FontData = FontData.defaultFontData;[TextArea(3, 10)][SerializeField] protected string m_Text = String.Empty;
m_FontData 是一个 FontData 类型的变量,用于存储字体的属性,Text 中的大部分属性都是根据 m_FontData 来设置的。m_Text 是一个字符 ...
Unity 补完计划(四):UGUI-10:Image & RawImage
前言
将 MaskableGraphic 分析完之后,就可以来看 UGUI 中真正使用到的图像组件了。Image 和 RawImage 是 UGUI 中最常用的两个图像组件,它们分别用于显示 Sprite 和 Texture。本文将分析这两个组件的实现。
Sprite 与 Texture
在 UGUI 中,Sprite 和 Texture 是两个最常用的图像资源。Sprite 是 Unity 引擎中专门用于 2D 图像的资源,它包含了图像的像素数据、UV 坐标、边界信息等。Texture 是 Unity 引擎中用于存储图像像素数据的资源,它可以被 Sprite 使用,也可以被 RawImage 使用。
Unity 的官方手册中,这样描述 Sprite :
Sprite 是一种 2D 图形对象,用于 2D 游戏中的角色、道具、飞弹和其他游戏元素。图形是从位图图像 Texture2D 获取的。Sprite 类主要标识应该用于特定精灵的图像部分。然后,GameObject 上的 SpriteRenderer 组件可以使用该信息来实际显示图形。
Sprite 是 2D 图形对象。如果习 ...