C#精要 - 异步篇
什么是异步?异步是一种任务执行的机制或者说方式,它的目的在于解决I/O等待会阻塞线程这个问题(最常见的就是GUI线程阻塞造成画面卡顿),它的实现依托于硬件底层的IRP(I/O Request Packet),它的本质其实是回调。
我可以使用比如 ReadAsync + Task.ContinueWith 的组合,来实现一个异步实践;
而更简单的方式是通过微软后续推出 .net4.5 的 async-await 这套关键词来实践。
异步函数 async-await异步函数,实际通过 核心类TaskAwaiter + 状态机 实现。
核心类 TaskAwaiter这个类比较简单,每个异步Task都有。我把它看作黑盒不细究,只看对外接口:
IsCompleted 属性:表示Task是否完成
GetResult() 方法:结束异步任务完成的等待
UnsafeOnCompleted(Action) 方法:设置延续任务
使用方法await必须在有async标记的方法内使用。如果async方法内部没有await,那它就和同步方法一样执行。
如果执行中遇到了await,就把需要awai ...
C#精要 - 类内成员篇
类成员初始化顺序一般初始化顺序:
子类静态字段内联
子类静态构造
子类实例字段内联
父类静态字段内联
父类静态构造
父类实例字段内联
父类实例构造
子类实例构造
原则就是:先内联后构造;先静态后实例;先子类后父类(除了实例构造器)。
方法abstract抽象方法、virtual方法、隐式实现接口方法,它们本质上、在IL代码层中,都是virtual方法。
IL提供两种方式去调用方法:
call:可用于调用实例方法、虚方法和静态方法。// 我个人测下来感觉只有静态方法是用call…
callvirt:可用于调用实例方法、虚方法。过程会比call复杂一些,事前会check null,执行时也会去查虚函数表。
非虚方式调用 callcall 调用的,是编译时确定的类型,也就是申明类型。
如果变量申明的类型没有对应的方法,就检查基类型来查找匹配方法。
虚方法调用 callvirtcallvirt 调用的,是运行时确定的类型,也就是变量指向对象的实际类型(new的类型)。
上面说的是结果,但如果深究过程,在IL代码层的话,多态方法全部都是 callvirt 最初父类的同一个方法 ...
工具篇:定点数运算数学库
初识什么是定点数?约定计算机中小数点的位置,且这个位置固定不变,小数点前、后的数字,分别用二进制表示,然后组合起来就可以把这个数字在计算机中存储起来,这种表示方式叫做「定点」表示法,用这种方法表示的数字叫做「定点数」。
为什么不用浮点数?具体怎么使用?定点数参与逻辑运算,转换回浮点数后不再用浮点数参与任何逻辑运算。
定点数型Int从浮点数转换到定点数放大1024倍,从而使得浮点数小数误差变小。
用long存储防止高位越界。
使用移位运算实现,比直接乘更优。
int型转换可以不损失精度;float型转换需要损失一些精度:(long)Math.Round(f * 1024)。
乘除运算出现问题乘法运算相当于多乘了一次倍数,所以直接除以1024(右移10位)即可。1024(原数1) * 2048(原数2) = 2^21 (原数是2^11而不是期待的2)
除法运算相当于多除了一次倍数,所以直接乘以1024(左移10位)即可。2048(原数2) / 1024(原数1)= 2 (原数是2^-9而不是期待的2)
大小比较简单重写operator。
定点数转换回浮点数缩小1024倍之后强转float ...
C#精要 - 线程篇
什么是线程、进程?线程是对物理CPU进行虚拟化,也是操作系统能调度的最小单位。
进程实际是应用程序的实例要使用的资源的集合,每个进程都被赋予了一个虚拟地址空间来避免被其它进程访问。
一个线程确定由某一进程拥有,一般不能跨进程。
线程结构线程有空间(内存耗用)和时间(上下文调度)上的开销。
内存耗用① 线程内核对象 (thread kernel object)
主要是有对线程的描述属性。x64使用约1240字节。
值得注意的是线程上下文(thread context):线程上下文是是线程上一次执行完毕后,CPU寄存器的状态(内存块)。
② 线程环境块 (thread environment block,TEB)
有GDI(图形设备接口)和OpenGL用的一些数据,以及异常处理链首:线程每进入一个try块,都会在链首(head)中插入一个节点(node),退出try块时删除该节点。x64中4KB。
③ 用户模式栈 (user- mode stack)
堆栈概念中的栈说的就是这个了,默认分配1MB内存(其实windows是保留1MB容量,等用了才调拨给你)。
④ 内核模式栈 (kernel- ...
C#精要 - GC篇
什么是GC?为什么需要GC?GC,即Garbage Collection,意为垃圾回收。
.Net不同于原生C++这种需要程序员手动管理内存的机制,存在自动释放内存的一套机制,这就叫GC。
GC可以让程序员不必关心资源的管理,也就是一个对象的生命流程中的4:
调用IL指令newobj,为代表资源的类型分配内存(C#中用new操作符完成)。
初始化内存,设置资源的初始状态并使资源可用。类型的实例构造器负责设置初始状态。
访问类型的成员来使用资源(有必要可以重复)。
摧毁资源的状态以进行清理。
释放内存。垃圾回收器独自负责这一步。
当然可以自己重写Finalize方法或手动调用GC.Collect。
简单列举GC的优势(对比手动管理)1.让程序员可以不必关心资源的管理。
2.避免手动管理时,顺序搞错,先销毁后调用导致空引用抛错。
3.避免手动管理时,由于标记引用未清空导致的内存泄露。
讲讲GC算法三个方向上讲:从总体流程上来说,它有标记 -> 压缩两个阶段;从确定是否销毁的方式上来说,它是从根遍历的;从销毁处理方式上来说,它是分代的。它解决了引用计数法循环引用的问题。
...
王者荣耀复刻项目 技能编辑器篇
从 技能配置可视化+纯业务 的角度,将原来的技能系统换个角度刨析一遍。
大体分类按照可配置的文件来分类,可分为以下五大类:
碰撞关系配置
buff配置
skill配置
单位配置
bullet配置
下面对这几类的核心配置数据进行列举。
碰撞关系配置
目标队伍
目标选择规则
目标单位
目标查找范围
buff配置
buff类型
buff附着目标
buff位置确定方式
碰撞关系配置
skill配置
图片、音效资源路径
技能动画名
技能目标确定方式、范围等
技能指示器类型
单位配置UnitInfoCfg,比较特殊,为了方便导表,逻辑碰撞体这块只提供一个Enum,在实际加载时使用它的派生类,根据Enum读取出不同的真实逻辑碰撞数据。
单位基础属性(基础血量、防御等)
单位资源位置
单位逻辑碰撞体类型(受击点高度 + 碰撞体大小、形状)
实现协作即想要结点编辑器、又想要表格式的批量填写。将他们协作起来需要多写一些代码,且他们的数据都必须来自同一套数据源——我采用Json数据源。
Luban由Luban提供2套类型:Editor专用的类型,用于和Json数据源 ...
工具篇:Odin 可视化编辑器
可视化方案什么是Unity的可视化方案很多,直接举例一些。
通用型github开源:
Node_Editor_Framework
xNode
特化型行为树
NodeCanvas
Behavior Designer
状态机
FlowCanvas
Bolt
PlayerMaker
行为树编辑器有什么作用
剧情(对话)编辑
AI编辑
技能编辑
碰撞关系编辑(纯数据,技能和单位是否可以碰撞)
装备合成路径编辑
其他
优劣
批量配置并不如Excel方便。
非常适合配合树状结构的数据。
思考实现Excel和可视化编辑器之间的相互转化,会方便很多。
下面只进行odin的使用,不再对行为树、状态机一类的可视化插件进行关注。
原生编辑器精灵、List、字典show.cs
123456789public class Show : MonoBehaviour{ public Sprite m_OriginSprite; public List<int> m_OriginList = new List<int>(); public Dicti ...
王者荣耀复刻项目 背包系统篇
设计基本实现数据库中只存储物品id、物品数量;客户端本地二进制式存储cfg,来显示物品详情;服务端本地二进制式存储cfg,来确定物品效果。
整体流程如下:
客户端登录时,获取到背包信息(物品id、物品数量)。
客户端打开背包时,根据内存中的背包信息 + 本地cfg,来展示物品。
客户端使用物品时,发送请求到服务端并让服务端在数据库验证数量。
如果数量不足,则失败;
如果数量足够,则根据服务端本地cfg来计算出账号收益,更新数据库后返回结果给客户端。
客户端获得返回结果,刷新内存数据。
优化
虚拟列表
对象池
异步加载
基础实现Scroller ViewViewport,遮罩,决定能看到的内容。(视野)
Content,真实内容。(履带)
设置格子锚点到左上角。
数据类型主要分为3块。
BagMgr,用来获取道具信息。
BagItem,背包格子类。
BagPanel,背包页面逻辑。
显式获取要计算出 起始显示的格子索引值、结束显示的格子索引值:
=========================================== ...
Enum转换List
EnumToList123456789101112131415161718192021222324252627282930313233343536373839public static List<T> EnumToList<T>(Type enumType) where T : ComboBoxSourceItem, new() { var result = new List<T>(); if (enumType.BaseType == typeof(System.Enum)) { foreach (var enumValue in System.Enum.GetValues(enumType)) { // 値の説明を取得する FieldInfo fi = enumType.GetField(System.E ...
工具篇:本地配置
非常感谢 luban 的作者,提供了如此好用的工具,并热心的教了我很多关于实际工作流的思路,对于我来说,这些都是很宝贵的。
初识luban简介就目前我的使用来说,luban可以用于excel导出配置数据、可以用于生成proto协议(并使用Bright.Net的序列化框架)、可以用于编辑器交互、可以用于编辑器数据源与运行时真实配置数据交互。是一个各方面都非常成熟的配置文件、通信协议的解决方案。
开源地址 : github
案例开源地址:github
luban高效地处理游戏开发中常见的excel、json、xml之类的数据,检查数据错误,生成c#等各种语言的代码,导出成bytes或json等多种格式。
强大的数据解析和转换能力 {excel(csv,xls,xlsx), json, bson, xml, yaml, lua, unity ScriptableObject} => {binary, json, bson, xml, lua, yaml, erlang}
增强的excel格式,可以简洁地配置出像简单列表、子结构、结构列表,以及任意复杂的深层次的嵌套结构。
完备的类型 ...