咖啡丶七

自律给我自由

入口

win+r 键入 regedit

官方文档

https://developer.microsoft.com/en-us/microsoft-store/register/

较详细的教程

https://www.cnblogs.com/kstsixeam/p/15251377.html

http://www.taodudu.cc/news/show-4273100.html?action=onClick

鼠标右键路径

右键背景

HKEY_CLASSES_ROOT\Directory\Background\shell\

右键文件夹

(无用)HKEY_CLASSES_ROOT\Directory\shellex\ContextMenuHandlers\

HKEY_CLASSES_ROOT\Directory\shell\

右键文件

(无用)HKEY_CLASSES_ROOT*\shellex\ContextMenuHandlers\

HKEY_CLASSES_ROOT*\shell\

右键背景用的比较多,着重讲解

在HKEY_CLASSES_ROOT\Directory\Background\shell\下创建一个项,就能出现在右键点击背景的菜单中

比如创建了一个Customize

在Customize中添加字符串SubCommands就能显示二级菜单了

在Customize下创建shell项,将想要显示的二级菜单创建在shell中

权限文件

右键——权限——高级——对应账户设置为完全控制

MUIVerb 右键时展示的文字
Icon 文字描述前方的图标
SubCommands 是否为多级菜单
Extended 按下shift时才显示
Position top顶部
%V 当前目录路径
%1 当前选中文件路径


热更新

学习测试,本打包流程以OfflinePlayMode(单机模式)做示范:

拉取项目工程

  1. 运行菜单 HybridCLR/Install… 安装HybridCLR,每次更新HybridCLR版本需要重新执行一次安装。

  2. 运行菜单 HybridCLR/Define Symbols/Enable HybridCLR 运行开启HybridCLR热更新。

  3. 运行菜单 HybridCLR/Generate/All 进行必要的生成操作。这一步不可遗漏!!!

  4. 运行菜单 HybridCLR/Build/BuildAssets And CopyTo AssemblyPath,生成热更新dll并copy到热更程序集中。

  5. 运行菜单TEngine/QuickBuild/一键打包window,

  6. 更改任意代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    using TEngine;

    namespace GameLogic
    {
    [Window(UILayer.UI)]
    public partial class UITest : UIWindow
    {
    protected override void OnCreate()
    {
    // m_textTitle.text = "UI测试";
    m_textTitle.text = "热更新";
    }
    }
    }
  7. (这步必不可少)运行菜单 HybridCLR/Build/BuildAssets And CopyTo AssemblyPath

  8. 运行菜单TEngine/QuickBuild/一键打包AssetBundle

  9. (可选)比较差异,运行菜单YooAsset/Extension/补丁包对比工具

  10. 替换热更新文件:(如果没有执行第九步,那么可以直接将所有文件上传,如果有重复的就替换该文件),将第九步中显示的差异资产选择出来并

    经测试,可以将旧的bundle和信息等文件删除

  11. 效果如下


微信小游戏热更

  1. 下载并安装插件

  2. 打包设置:

    • CDN地址需要填写在该面板下

    • 在Edit-ProjectSetting-TEngine-TEngineSettings的InnerResourceSource、ExtraResourceSource和FormalResourceSource中填写相同的地址

  3. 点击生成并转换,生成微信小游戏

  4. 部署资源

    • 老样子,先部署微信生成的资源,将./webgl/下的两个文件和一个文件夹上传服务器

      此步骤类似于设置游戏的StreamingAssets,只不过相对windows来说,咱们是吧StreamingAssets文件放置在了服务器上,而不是跟随着包体

    • 然后,创建文件夹./Default_0/WebGL/,将webgl里的文件再上传一次()

      此步骤类似于设置游戏的远程热更新地址,启动游戏时会判断./addressable/Default_0/WebGL/PackageManifest_DefaultPackage.version与./addressable/StreamingAssets/package/DefaultPackage/PackageManifest_DefaultPackage.version,两个的版本号。从而来进行更新

  5. 热更新,修改代码后

    • 运行菜单 HybridCLR/Build/BuildAssets And CopyTo AssemblyPath
    • 运行菜单TEngine/QuickBuild/一键打包AssetBundle
    • 将生成的补丁包放置在./addressable/Default_0/WebGL/下

直接列出测试数据:

加载ScriptableObject的集合ScriptableObject对象

monster为ScriptableObtjet类的子类

1
2
3
4
5
6
7
8
9
10
11
12
13
private void Start()
{
Debug.Log($"start time: {Time.time}");
LoadMonsterUtil().Forget(); // 0.26s
// LoadMonsters().Forget(); // 0.57s
// LoadJson().Forget(); // 0.1s
}

private async UniTaskVoid LoadMonsterUtil()
{
await Addressables.LoadAssetAsync<monsterUtil>("Assets/_Project/ScriptableObject/Excel/monster/_monsterUtil.asset");
Debug.Log($"加载MonsterUtil成功{Time.time}");
}

  • 加载时间:0.33s
  • 资产大小:共172kb

直接加载所有的ScriptableObject对象

1
2
3
4
5
6
7
8
9
10
11
12
13
private void Start()
{
Debug.Log($"start time: {Time.time}");
// LoadMonsterUtil().Forget(); // 0.26s
LoadMonsters().Forget(); // 0.57s
// LoadJson().Forget(); // 0.1s
}

private async UniTaskVoid LoadMonsters()
{
await Addressables.LoadAssetsAsync<monster>("monster", null);
Debug.Log($"加载Monster成功{Time.time}");
}

  • 加载时间:1.56s
  • 资产大小:共128kb

加载Json文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private void Start()
{
Debug.Log($"start time: {Time.time}");
// LoadMonsterUtil().Forget(); // 0.26s
// LoadMonsters().Forget(); // 0.57s
LoadJson().Forget(); // 0.1s
}
private async UniTaskVoid LoadJson()
{
var json = await Addressables.LoadAssetAsync<TextAsset>("Assets/TestData.json");
Debug.Log($"成功获取到json文件 {Time.time}");
var monsterUtil = JsonConvert.DeserializeObject<monsterUtil>(json.text);
Debug.Log($"反序列化成功 {Time.time}");
}

  • 加载时间:0.21s
  • 资产大小:33kb

加载普通类型的集合ScriptableObject对象

monster为普通的被Serializable修饰的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private void Start()
{
Debug.Log($"start time: {Time.time}");
// LoadMonsterUtil().Forget(); // 0.26s
// LoadMonsters().Forget(); // 0.57s
// LoadJson().Forget(); // 0.1s
LoadMonsterUtil2().Forget();
}

private async UniTaskVoid LoadMonsterUtil2()
{
await Addressables.LoadAssetAsync<monsterUtil>("Assets/_Project/ScriptableObject/Excel/monsterUtil.asset");
Debug.Log($"加载MonsterUtil2成功{Time.time}");
}

  • 加载时间:0.45s
  • 资产大小:63kb

结论:

可以看出,json不管是加载时间还是资产大小都遥遥领先

但json无法在unity中直接存储Sprite等资产

如果确实需要引用unity中的Sprite等资产,可以使用集合ScriptableObject对象

  • 如果注重时间,可以将子数据也写成ScriptableObject
  • 如果注重大小,可以将子数据写成普通的被Serializable修饰的类

启动速度

在出现白屏到unity加载完毕的时间:

unity版本 游戏内容(开发/体验版) iphone15pm 小米10
unity2021.3.20f1c1 空包 7.5s 3.5s
裸TEngine框架 7.4s 4.5s
unity2021.2.5f1c303 空包 6~8s 4.7s
守住这个城 7.6s 6.3s

以上数据都是开发版(体验版也是一样的),启动速度都至少需要7s

unity版本 游戏内容(正式版) inphone15pm 小米10
unity2021.2.5f1c303 守住这个城 2.5s

结论

在游戏成为正式版之后,启动速度将会极大加快

生产字体设置

对照组

Padding

可以看到值越小,字体越细

AtlasResolution

可以看到值越大,字体越细

字符集

github地址:Unity-TextMeshPro-Chinese-Characters-Set

3000

1
啊阿埃挨哎唉诶哀皑癌蔼矮艾碍爱隘鞍氨安俺按暗岸胺案肮昂盎凹敖熬翱袄傲奥懊澳芭捌扒叭吧笆八疤巴拔跋靶把耙坝霸罢爸白柏百摆佰败拜稗斑班搬扳般颁板版扮拌伴瓣半办绊邦帮梆榜膀绑棒磅蚌镑傍谤苞胞包褒剥薄雹保堡饱宝抱报暴豹鲍爆杯碑悲卑北辈背贝钡倍狈备惫焙被奔苯本笨崩绷甭泵蹦迸逼鼻比鄙笔彼碧蓖蔽毕毙毖币庇痹闭敝弊必辟壁臂避陛鞭边编贬扁便变卞辨辩辫遍标彪膘表鳖憋别瘪彬斌濒滨宾摈兵冰柄丙秉饼炳病并玻菠播拨钵波博勃搏铂箔伯帛舶脖膊渤泊驳捕卜哺补埠不布步簿部怖擦猜裁材才财睬踩采彩菜蔡餐参蚕残惭惨灿苍舱仓沧藏操糙槽曹草厕策侧册测层蹭插叉茬茶查碴搽察岔差诧拆柴豺搀掺蝉馋谗缠铲产阐颤昌猖场尝常长偿肠厂敞畅唱倡超抄钞朝嘲潮巢吵炒车扯撤掣彻澈郴臣辰尘晨忱沉陈趁衬撑称城橙成呈乘程惩澄诚承逞骋秤吃痴持匙池迟弛驰耻齿侈尺赤翅斥炽充冲虫崇宠抽酬畴踌稠愁筹仇绸瞅丑臭初出橱厨躇锄雏滁除楚础储矗搐触处揣川穿椽传船喘串疮窗幢床闯创吹炊捶锤垂春椿醇唇淳纯蠢戳绰疵茨磁雌辞慈瓷词此刺赐次聪葱囱匆从丛凑粗醋簇促蹿篡窜摧崔催脆瘁粹淬翠村存寸磋撮搓措挫错搭达答瘩打大呆歹傣戴带殆代贷袋待逮怠耽担丹单郸掸胆旦氮但惮淡诞弹蛋当挡党荡档刀捣蹈倒岛祷导到稻悼道盗德得的蹬灯登等瞪凳邓堤低滴迪敌笛狄涤翟嫡抵底地蒂第帝弟递缔颠掂滇碘点典靛垫电佃甸店惦奠淀殿碉叼雕凋刁掉吊钓调跌爹碟蝶迭谍叠丁盯叮钉顶鼎锭定订丢东冬董懂动栋侗恫冻洞兜抖斗陡豆逗痘都督毒犊独读堵睹赌杜镀肚度渡妒端短锻段断缎堆兑队对墩吨蹲敦顿囤钝盾遁掇哆多夺垛躲朵跺舵剁惰堕蛾峨鹅俄额讹娥恶厄扼遏鄂饿恩而儿耳尔饵洱二贰发罚筏伐乏阀法珐藩帆番翻樊矾钒繁凡烦反返范贩犯饭泛坊芳方肪房防妨仿访纺放菲非啡飞肥匪诽吠肺废沸费芬酚吩氛分纷坟焚汾粉奋份忿愤粪丰封枫蜂峰锋风疯烽逢冯缝讽奉凤佛否夫敷肤孵扶拂辐幅氟符伏俘服浮涪福袱弗甫抚辅俯釜斧脯腑府腐赴副覆赋复傅付阜父腹负富讣附妇缚咐噶嘎该改概钙盖溉干甘杆柑竿肝赶感秆敢赣冈刚钢缸肛纲岗港杠篙皋高膏羔糕搞镐稿告哥歌搁戈鸽胳疙割革葛格蛤阁隔铬个各给根跟耕更庚羹埂耿梗工攻功恭龚供躬公宫弓巩汞拱贡共钩勾沟苟狗垢构购够辜菇咕箍估沽孤姑鼓古蛊骨谷股故顾固雇刮瓜剐寡挂褂乖拐怪棺关官冠观管馆罐惯灌贯光广逛瑰规圭硅归龟闺轨鬼诡癸桂柜跪贵刽辊滚棍锅郭国果裹过哈骸孩海氦亥害骇酣憨邯韩含涵寒函喊罕翰撼捍旱憾悍焊汗汉夯杭航壕嚎豪毫郝好耗号浩呵喝荷菏核禾和何合盒貉阂河涸赫褐鹤贺嘿黑痕很狠恨哼亨横衡恒轰哄烘虹鸿洪宏弘红喉侯猴吼厚候后呼乎忽瑚壶葫胡蝴狐糊湖弧虎唬护互沪户花哗华猾滑画划化话槐徊怀淮坏欢环桓还缓换患唤痪豢焕涣宦幻荒慌黄磺蝗簧皇凰惶煌晃幌恍谎灰挥辉徽恢蛔回毁悔慧卉惠晦贿秽会烩汇讳诲绘荤昏婚魂浑混豁活伙火获或惑霍货祸击圾基机畸稽积箕肌饥迹激讥鸡姬绩缉吉极棘辑籍集及急疾汲即嫉级挤几脊己蓟技冀季伎祭剂悸济寄寂计记既忌际妓继纪嘉枷夹佳家加荚颊贾甲钾假稼价架驾嫁歼监坚尖笺间煎兼肩艰奸缄茧检柬碱碱拣捡简俭剪减荐槛鉴践贱见键箭件健舰剑饯渐溅涧建僵姜将浆江疆蒋桨奖讲匠酱降蕉椒礁焦胶交郊浇骄娇嚼搅铰矫侥脚狡角饺缴绞剿教酵轿较叫窖揭接皆秸街阶截劫节桔杰捷睫竭洁结解姐戒藉芥界借介疥诫届巾筋斤金今津襟紧锦仅谨进靳晋禁近烬浸尽劲荆兢茎睛晶鲸京惊精粳经井警景颈静境敬镜径痉靖竟竞净炯窘揪究纠玖韭久灸九酒厩救旧臼舅咎就疚鞠拘狙疽居驹菊局咀矩举沮聚拒据巨具距踞锯俱句惧炬剧捐鹃娟倦眷卷绢撅攫抉掘倔爵觉决诀绝均菌钧军君峻俊竣浚郡骏喀咖卡咯开揩楷凯慨刊堪勘坎砍看康慷糠扛抗亢炕考拷烤靠坷苛柯棵磕颗科壳咳可渴克刻客课肯啃垦恳坑吭空恐孔控抠口扣寇枯哭窟苦酷库裤夸垮挎跨胯块筷侩快宽款匡筐狂框矿眶旷况亏盔岿窥葵奎魁傀馈愧溃坤昆捆困括扩廓阔垃拉喇蜡腊辣啦莱来赖蓝婪栏拦篮阑兰澜谰揽览懒缆烂滥琅榔狼廊郎朗浪捞劳牢老佬姥酪烙涝勒乐雷镭蕾磊累儡垒擂肋类泪棱楞冷厘梨犁黎篱狸离漓理李里鲤礼莉荔吏栗丽厉励砾历利僳例俐痢立粒沥隶力璃哩俩联莲连镰廉怜涟帘敛脸链恋炼练粮凉梁粱良两辆量晾亮谅撩聊僚疗燎寥辽潦了撂镣廖料列裂烈劣猎琳林磷霖临邻鳞淋凛赁吝拎玲菱零龄铃伶羚凌灵陵岭领另令溜琉榴硫馏留刘瘤流柳六龙聋咙笼窿隆垄拢陇楼娄搂篓漏陋芦卢颅庐炉掳卤虏鲁麓碌露路赂鹿潞禄录陆戮驴吕铝侣旅履屡缕虑氯律率滤绿峦挛孪滦卵乱掠略抡轮伦仑沦纶论萝螺罗逻锣箩骡裸落洛骆络妈麻玛码蚂马骂嘛吗埋买麦卖迈脉瞒馒蛮满蔓曼慢漫谩芒茫盲氓忙莽猫茅锚毛矛铆卯茂冒帽貌贸么玫枚梅酶霉煤没眉媒镁每美昧寐妹媚门闷们萌蒙檬盟锰猛梦孟眯醚靡糜迷谜弥米秘觅泌蜜密幂棉眠绵冕免勉娩缅面苗描瞄藐秒渺庙妙蔑灭民抿皿敏悯闽明螟鸣铭名命谬摸摹蘑模膜磨摩魔抹末莫墨默沫漠寞陌谋牟某拇牡亩姆母墓暮幕募慕木目睦牧穆拿哪呐钠那娜纳氖乃奶耐奈南男难囊挠脑恼闹淖呢馁内嫩能妮霓倪泥尼拟你匿腻逆溺蔫拈年碾撵捻念娘酿鸟尿捏聂孽啮镊镍涅您柠狞凝宁拧泞牛扭钮纽脓浓农弄奴努怒女暖虐疟挪懦糯诺哦欧鸥殴藕呕偶沤啪趴爬帕怕琶拍排牌徘湃派攀潘盘磐盼畔判叛乓庞旁耪胖抛咆刨炮袍跑泡呸胚培裴赔陪配佩沛喷盆砰抨烹澎彭蓬棚硼篷膨朋鹏捧碰坯砒霹批披劈琵毗啤脾疲皮匹痞僻屁譬篇偏片骗飘漂瓢票撇瞥拼频贫品聘乒坪苹萍平凭瓶评屏坡泼颇婆破魄迫粕剖扑铺仆莆葡菩蒲埔朴圃普浦谱曝瀑期欺栖戚妻七凄漆柒沏其棋奇歧畦崎脐齐旗祈祁骑起岂乞企启契砌器气迄弃汽泣讫掐洽牵扦钎铅千迁签仟谦乾黔钱钳前潜遣浅谴堑嵌欠歉枪呛腔羌墙蔷强抢橇锹敲悄桥瞧乔侨巧鞘撬翘峭俏窍切茄且怯窃钦侵亲秦琴勤芹擒禽寝沁青轻氢倾卿清擎晴氰情顷请庆琼穷秋丘邱球求囚酋泅趋区蛆曲躯屈驱渠取娶龋趣去圈颧权醛泉全痊拳犬券劝缺炔瘸却鹊榷确雀裙群然燃冉染瓤壤攘嚷让饶扰绕惹热壬仁人忍韧任认刃妊纫扔仍日戎茸蓉荣融熔溶容绒冗揉柔肉茹蠕儒孺如辱乳汝入褥软阮蕊瑞锐闰润若弱撒洒萨腮鳃塞赛三叁伞散桑嗓丧搔骚扫嫂瑟色涩森僧莎砂杀刹沙纱傻啥煞筛晒珊苫杉山删煽衫闪陕擅赡膳善汕扇缮墒伤商赏晌上尚裳梢捎稍烧芍勺韶少哨邵绍奢赊蛇舌舍赦摄射慑涉社设砷申呻伸身深娠绅神沈审婶甚肾慎渗声生甥牲升绳省盛剩胜圣师失狮施湿诗尸虱十石拾时什食蚀实识史矢使屎驶始式示士世柿事拭誓逝势是嗜噬适仕侍释饰氏市恃室视试收手首守寿授售受瘦兽蔬枢梳殊抒输叔舒淑疏书赎孰熟薯暑曙署蜀黍鼠属术述树束戍竖墅庶数漱恕刷耍摔衰甩帅栓拴霜双爽谁水睡税吮瞬顺舜说硕朔烁斯撕嘶思私司丝死肆寺嗣四伺似饲巳松耸怂颂送宋讼诵搜艘擞嗽苏酥俗素速粟僳塑溯宿诉肃酸蒜算虽隋随绥髓碎岁穗遂隧祟孙损笋蓑梭唆缩琐索锁所塌他它她塔獭挞蹋踏胎苔抬台泰酞太态汰坍摊贪瘫滩坛檀痰潭谭谈坦毯袒碳探叹炭汤塘搪堂棠膛唐糖倘躺淌趟烫掏涛滔绦萄桃逃淘陶讨套特藤腾疼誊梯剔踢锑提题蹄啼体替嚏惕涕剃屉天添填田甜恬舔腆挑条迢眺跳贴铁帖厅听烃汀廷停亭庭艇通桐酮瞳同铜彤童桶捅筒统痛偷投头透凸秃突图徒途涂屠土吐兔湍团推颓腿蜕褪退吞屯臀拖托脱鸵陀驮驼椭妥拓唾挖哇蛙洼娃瓦袜歪外豌弯湾玩顽丸烷完碗挽晚皖惋宛婉万腕汪王亡枉网往旺望忘妄威巍微危韦违桅围唯惟为潍维苇萎委伟伪尾纬未蔚味畏胃喂魏位渭谓尉慰卫瘟温蚊文闻纹吻稳紊问嗡翁瓮挝蜗涡窝我斡卧握沃巫呜钨乌污诬屋无芜梧吾吴毋武五捂午舞伍侮坞戊雾晤物勿务悟误昔熙析西硒矽晰嘻吸锡牺稀息希悉膝夕惜熄烯溪汐犀檄袭席习媳喜铣洗系隙戏细瞎虾匣霞辖暇峡侠狭下厦夏吓掀锨先仙鲜纤咸贤衔舷闲涎弦嫌显险现献县腺馅羡宪陷限线相厢镶香箱襄湘乡翔祥详想响享项巷橡像向象萧硝霄削哮嚣销消宵淆晓小孝校肖啸笑效楔些歇蝎鞋协挟携邪斜胁谐写械卸蟹懈泄泻谢屑薪芯锌欣辛新忻心信衅星腥猩惺兴刑型形邢行醒幸杏性姓兄凶胸匈汹雄熊休修羞朽嗅锈秀袖绣墟戌需虚嘘须徐许蓄酗叙旭序畜恤絮婿绪续轩喧宣悬旋玄选癣眩绚靴薛学穴雪血勋熏循旬询寻驯巡殉汛训讯逊迅压押鸦鸭呀丫芽牙蚜崖衙涯雅哑亚讶焉咽阉烟淹盐严研蜒岩延言颜阎炎沿奄掩眼衍演艳堰燕厌砚雁唁彦焰宴谚验殃央鸯秧杨扬佯疡羊洋阳氧仰痒养样漾邀腰妖瑶摇尧遥窑谣姚咬舀药要耀椰噎耶爷野冶也页掖业叶曳腋夜液一壹医揖铱依伊衣颐夷遗移仪胰疑沂宜姨彝椅蚁倚已乙矣以艺抑易邑屹亿役臆逸肄疫亦裔意毅忆义益溢诣议谊译异翼翌绎茵荫因殷音阴姻吟银淫寅饮尹引隐印英樱婴鹰应缨莹萤营荧蝇迎赢盈影颖硬映哟拥佣臃痈庸雍踊蛹咏泳涌永恿勇用幽优悠忧尤由邮铀犹油游酉有友右佑釉诱又幼迂淤于盂榆虞愚舆余俞逾鱼愉渝渔隅予娱雨与屿禹宇语羽玉域芋郁吁遇喻峪御愈欲狱育誉浴寓裕预豫驭鸳渊冤元垣袁原援辕园员圆猿源缘远苑愿怨院曰约越跃钥岳粤月悦阅耘云郧匀陨允运蕴酝晕韵孕匝砸杂栽哉灾宰载再在咱攒暂赞赃脏葬遭糟凿藻枣早澡蚤躁噪造皂灶燥责择则泽贼怎增憎曾赠扎喳渣札轧铡闸眨栅榨咋乍炸诈摘斋宅窄债寨瞻毡詹粘沾盏斩辗崭展蘸栈占战站湛绽樟章彰漳张掌涨杖丈帐账仗胀瘴障招昭找沼赵照罩兆肇召遮折哲蛰辙者锗蔗这浙珍斟真甄砧臻贞针侦枕疹诊震振镇阵蒸挣睁征狰争怔整拯正政帧症郑证芝枝支吱蜘知肢脂汁之织职直植殖执值侄址指止趾只旨纸志挚掷至致置帜峙制智秩稚质炙痔滞治窒中盅忠钟衷终种肿重仲众舟周州洲诌粥轴肘帚咒皱宙昼骤珠株蛛朱猪诸诛逐竹烛煮拄瞩嘱主著柱助蛀贮铸筑住注祝驻抓爪拽专砖转撰赚篆桩庄装妆撞壮状椎锥追赘坠缀谆准捉拙卓桌琢茁酌啄着灼浊兹咨资姿滋淄孜紫仔籽滓子自渍字鬃棕踪宗综总纵邹走奏揍租足卒族祖诅阻组钻纂嘴醉最罪尊遵昨左佐柞做作坐座  `~!@#$%^&*()_+-=,./;'[]\<>?:"{}|·!¥……()——,。、;‘’【】、《》?:“”ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`~!@#$%^&*()_+-=,./;'[]\<>?:"{}|·!¥……()——,。、;‘’【】、《》?:“”‰≤≥ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789

7000

1
〔〕一丨丿丶乛一乙二十丁厂七卜八人入儿九匕几了乃刀力又乜三干亍于亏士土工才下寸丈大兀与万弋上小口山巾千乞川亿彳个么久勺丸夕凡及广亡门丫义之尸已巳弓己卫孑子孓也女飞刃习叉马乡幺丰王井开亓夫天元无韦云专丐扎廿艺木五支厅卅不仄太犬区历友歹尤匹厄车巨牙屯戈比互切瓦止少曰日中贝内水冈见手午牛毛气壬升夭长仁仃什片仆仉化仇币仂仍仅斤爪反兮刈介父爻从仑今凶分乏公仓月氏勿风欠丹匀乌勾殳凤卞六文亢方闩火为斗忆计订户讣认讥冗心尹尺夬引丑爿巴孔队办以允邓予劝双书毋幻玉刊末未示击邗戋打巧正扑卉扒邛功扔去甘世艾艽古节艿本术札可叵匝丙左厉丕石右布夯龙戊平灭轧东匜劢卡北占凸卢业旧帅归目旦且叮叶甲申号电田由卟叭只央史叱叽兄叼叩叫叻叨另叹冉皿凹囚四生失矢氕乍禾仨仕丘付仗代仙仟仡仫伋们仪白仔他仞斥卮瓜乎丛令用甩印氐乐尔句匆犰册卯犯外处冬鸟务刍包饥主市庀邝立冯邙玄闪兰半汀汁汇头汈汉忉宁穴宄它讦讧讨写让礼讪讫训必议讯记永司尻尼民弗弘阢出阡辽奶奴尕加召皮边孕发圣对弁台矛纠驭母幼丝匡耒邦玎玑式迂刑邢戎动圩圬圭扛寺吉扣扦圪考托圳老圾巩执扩圹扪扫圯圮地扬场耳芋芏共芊芍芨芄芒亚芝芎芑芗朽朴机权过亘臣吏再协西压厌厍戌在百有存而页匠夸夺夼灰达戍尥列死成夹夷轨邪尧划迈毕至此乩贞师尘尖劣光当吁早吐吓旯曳虫曲团同吕吊吃因吸吗吆屿屹岌帆岁回岂屺则刚网肉凼囝囡钆钇年朱缶氘氖牝先丢廷舌竹迁乔迄伟传乒乓休伍伎伏伛优臼伢伐仳延佤仲仵件任伤  伥价伦份伧华仰伉仿伙伪伫自伊血向囟似后行甪舟全会杀合兆企汆氽众爷伞创刖肌肋朵杂夙危旬旭旮旨负犴刎犷匈犸舛各名多凫争邬色饧冱壮冲妆冰庄庆亦刘齐交次衣产决亥邡充妄闭问闯羊并关米灯州汗污江汕汔汲汐汛汜池汝汤汊忖忏忙兴宇守宅字安讲讳讴军讵讶祁讷许讹论讼农讽设访诀聿寻那艮厾迅尽导异弛阱阮孙阵阳收阪阶阴防丞奸如妁妇妃好她妈戏羽观牟欢买纡红纣驮纤纥驯纨约级纩纪驰纫巡佘寿玕\弄玙麦玖玚玛形进戒吞远违韧运扶抚坛抟技坏抔抠坜扰扼拒找批扯址走抄汞坝贡攻赤圻折抓扳坂抡扮抢扺孝坎坍均坞抑抛投抃\坟坑抗坊抖护壳志块抉扭声把报拟抒却劫毐芙芫芜苇邯芸芾芰苈苊苣芽芷芮苋芼苌花芹芥苁芩芬苍芪芴芡芟苄芳严苎芦芯劳克芭苏苡杆杜杠材村杖杌杏杉巫杓极杧杞李杨杈求忑孛甫匣更束吾豆两邴酉丽医辰励邳否还矶奁豕尬歼来忒连欤轩轪轫迓邶忐芈步卤卣邺坚肖旰旱盯呈时吴呋助县里呓呆吱吠呔呕园呖呃旷围呀吨旸吡町足虬邮男困吵串呙呐呗员听吟吩呛吻吹呜吭吣吲吼邑吧囤别吮岍帏岐岖岈岗岘帐岑岚兕财囵囫钉针钊钋钌迕氙氚牡告我乱利秃秀私岙每佞兵邱估体何佐伾佑攸但伸佃佚作伯伶佣低你佝佟住位伴佗身皂伺佛伽囱近彻役彷返佘余希佥坐谷孚妥豸含邻坌岔肝肟肛肚肘肠邸龟甸奂免劬狂犹狈狄角删狃狁鸠条彤卵灸岛邹刨饨迎饩饪饫饬饭饮系言冻状亩况亨庑床庋库庇疔疖疗吝应冷这庐序辛肓弃冶忘闰闱闲闳间闵闶闷羌判兑灶灿灼炀弟沣汪沅沄沐沛沔汰沤沥沌沘沏沚沙汩汨汭汽沃沂沦汹汾泛沧沨沟没汴汶沆沩沪沈沉沁泐怃忮怀怄忧忡忤忾怅忻忪怆忭忱快忸完宋宏牢究穷灾良证诂诃启评补初社祀祃诅识诈诉罕诊诋诌词诎诏诐译诒君灵即层屁屃尿尾迟局改张忌际陆阿孜陇陈阽阻阼附坠陀陂陉妍妩妓妪妣妙妊妖妗姊妨妫妒妞姒妤努邵劭忍刭劲甬邰矣鸡纬纭驱纯纰纱纲纳纴纵驳纶纷纸纹纺纻驴纽纾奉玩玮环玡武青责现玫玠玢玥表玦甙盂忝规匦抹卦邽坩坷坯拓垅拢拔抨坪拣拤拈坫垆坦担坤押抻抽拐拃拖拊者拍顶坼拆拎拥抵坻拘势抱拄垃拉拦幸拌拧坨坭抿拂拙招坡披拨择拚抬拇坳拗耵其耶取茉苷苦苯昔苛苤若茂茏苹苫苴苜苗英苒苘茌苻苓茚苟茆茑苑苞范茓茔茕直苠茀茁茄苕茎苔茅枉林枝杯枢枥柜枇杪杳枘枧杵枚枨析板枞松枪枫构杭枋杰述枕杻杷杼丧或画卧事刺枣雨卖矸郁矻矾矽矿砀码厕奈刳奔奇奄奋态瓯欧殴垄殁郏妻轰顷转轭斩轮软到郅鸢非叔歧肯齿些卓虎虏肾贤尚盱旺具昊昙果味杲昃昆国哎咕昌呵咂畅呸昕明易咙昀昂旻昉炅咔畀虮迪典固忠咀呷呻黾咒咋咐呱呼呤咚鸣咆咛咏呢咄呶咖呦咝岵岢岸岩帖罗岿岬岫帜帙帕岭岣峁刿峂迥岷剀凯帔峄沓败账贩贬购贮囹图罔钍钎钏钐钓钒钔钕钗邾制知迭氛迮垂牦牧物乖刮秆和季委竺秉迤佳侍佶岳佬佴供使侑佰侉例侠臾侥版侄岱侦侣侗侃侧侏凭侨侩佻佾佩货侈侪佼依佯侬帛卑的迫阜侔质欣郈征徂往爬彼径所舍金刽郐刹命肴郄斧怂爸采籴觅受乳贪念贫忿瓮戗肼肤朊肺肢肽肱肫肿肭胀朋肷股肮肪肥服胁周剁昏迩郇鱼兔狉狙狎狐忽弥狗狍狞狒咎备炙枭饯饰饱饲饳饴冽变京享冼庞店夜庙府底庖疟疠疝疙疚疡剂卒郊兖庚废净妾盲放刻於劾育氓闸闹郑券卷单炜炬炖炒炝炊炕炎炉炔沫浅法泔泄沽沭河泷沾泸沮泪油泱泅泗泊泠泜泺泃沿泖泡注泣泫泮泞沱泻泌泳泥泯沸泓沼波泼泽泾治怔怯怙怵怖怦怛怏性怍怕怜怩怫怊怿怪怡学宝宗定宕宠宜审宙官空帘穸穹宛实宓诓诔试郎诖诗诘戾肩房诙戽诚郓衬衫衩祆祎祉视祈诛诜话诞诟诠诡询诣诤该详诧诨诩建肃隶录帚屉居届刷鸤\屈弧弥弦承孟陋戕陌孤孢陕亟降函陔限卺妹姑姐妲妯姓姗妮始帑弩孥驽姆虱迦迢驾叁参迨艰线绀绁绂练驵组绅细驶织驷驸驹终绉驺驻绊驼绋绌绍驿绎经骀贯甾砉耔契贰奏春帮珏珐珂珑玷玳珀顸珍玲珊珉珈玻毒型韨拭挂封持拮拷拱垭挝垣项垮挎垯挞城挟挠垤政赴赵赳贲垱挡拽垌哉垲挺括挢埏郝垍垧垢拴拾挑垛指垫挣挤垓垟拼垞挖按挥挦挪垠拯拶某甚荆茸革茜茬荐荙巷荚荑贳荛荜茈带草茧茼莒茵茴茱莛荞茯荏荇荃荟茶荀茗荠茭茨荒垩茳茫荡荣荤荥荦荧荨茛故荩胡荪荫茹荔南荬荭药柰标栈柑枯栉柯柄柘栊柩枰栋栌相查柙枵柚枳柞柏柝栀柃柢栎枸栅柳柱柿栏柈柠柁枷柽树勃剌郚剅要酊郦柬咸威歪甭研砖厘砗厚砑砘砒砌砂泵砚斫砭砜砍面耐耍奎耷牵鸥虺残殂殃殇殄殆轱轲轳轴轵轶轷轸栎轺轻鸦虿皆毖韭背战觇点虐临览竖尜省削尝哐昧眄眍盹是郢眇眊盼眨昽眈哇咭哄哑显冒映禺哂星昨咴曷昴咧昱昵咦哓昭哔畎畏毗趴呲胄胃贵畋畈界虹虾虼虻蚁思蚂盅咣虽品咽骂哕剐郧勋咻哗囿咱咿响哌哙哈哚咯哆咬咳咩咪咤哝哪哏哞哟峙炭峡峣罘帧罚峒峤峋峥峧帡贱贴贶贻骨幽钘钙钚钛钝钞钟钡钠钢钣钤钥钦钧钨钩钪钫钬钭钮钯御缸拜看矩矧毡氡氟氢牯怎郜牲选适秕秒香种秭秋科重复竽竿笈笃俦段俨俅便俩俪叟垡贷牮顺修俏俣俚保俜促俄俐侮俭俗俘信皇泉皈鬼侵禹侯追俑俟俊盾逅待徊徇徉衍律很须舢舣叙俞弇郗剑逃俎郤爰郛食瓴盆胚胧胨胩胪胆胛胂胜胙胍胗胝朐胞胖脉胫胎鸨葡勉狨狭狮独狯狰狡飐飑狩狱狠狲訇訄逄昝贸怨急饵饶蚀饷饸饹饺饻胤饼峦弯孪娈将奖哀亭亮庤度弈奕迹庭庥疬疣疥疭疮疯疫疢疤庠咨姿亲竑音彦飒帝施闺闻闼闽闾闿阀阁阂差养美羑姜迸叛送类籼迷籽娄前酋首逆兹总炳炻炼炟炽炯炸烀烁炮炷炫烂烃剃洼洁洱洪洹洒洧洌浃柒浇泚浈浉浊洞洇洄测洙洗活洑涎洎洫派浍洽洮染洵洚洺洛浏济洨浐洋洴洣洲浑浒浓津浔浕洳恸恃恒恹恢恍恫恺恻恬恤恰恂恪恼恽恨举觉宣宦宥宬室宫宪突穿窀窃客诫冠诬语扁扃袆衲衽袄衿袂祛祜祓祖神祝祚诮祗祢祠误诰诱诲诳鸩说昶诵郡垦退既屋昼咫屏屎弭费陡逊牁眉胥孩陛陟陧陨除险院娃姞姥娅姨娆姻姝娇姚姽姣姘姹娜怒架贺盈怼羿勇炱怠癸蚤柔矜垒绑绒结绔骁绕骄骅绗绘给绚彖绛络骆绝绞骇统骈耕耘耖耗耙艳挈恝泰秦珥珙顼珰珠珽珩珧珣珞琤班珲敖素匿蚕顽盏匪恚捞栽捕埔埂捂振载赶起盐捎捍埕捏埘埋捉捆捐埚埙损袁挹捌都哲逝耆耄捡挫捋埒换挽贽挚热恐捣垸壶捃捅盍埃挨耻耿耽聂莰茝荸莆恭莽莱莲莳莫莴莪莉莠莓荷莜莅荼莶莩荽获莸荻莘晋恶莎莞莹茛莺真莙鸪莼框梆桂桔栲栳郴桓栖桡桎桢桄档桐桤株梃栝桥桕桦桁栓桧桃桅栒格桩校核样栟桉根栩逑索逋彧哥速鬲豇逗栗贾酐酎酌配酏逦翅辱唇厝孬夏砝砹砸砺砰砧砷砟砼砥砾砣础破硁恧  原套剞逐砻烈殊殉顾轼轾轿辀辁辂较鸫顿趸毙致剕龀柴桌鸬虔虑监紧逍党眬唛逞晒晟眩眠晓眙唝哧哳哮唠鸭晃哺哽唔晔晌晁剔晏晖晕鸮趵趿畛蚌蚨蚜蚍蚋蚬畔蚝蚧蚣蚊蝌蚓哨唢哩圃哭圄哦唣唏恩盎唑鸯唤唁哼唧啊唉唆帱崂崃罡罢罟峭峨峪峰圆觊峻贼贿赂赃赅赆钰钱钲钳钴钵钷钹钺钻钼钽钾钿铀铁铂铃铄铅铆铈铉铊铋铌铍铎眚缺氩氤氦氧氨毪特牺造乘敌舐秣秫秤租秧积盉秩称秘透笄笕笔笑笊笫笏笋笆俸倩债俵倻借偌值倚俺倾倒俳俶倬倏倘俱倡候赁恁倭倪俾倜隼隽倞俯倍倦倓倌倥臬健臭射皋躬息郫倨倔衄颀徒徕徐殷舰舨舱般航舫瓞途拿釜耸爹舀爱豺豹奚鬯衾鸰颁颂翁胯胰胱胴胭脍脎脆脂胸胳脏脐胶脑胲胼朕脒胺脓鸱玺鱽鸲逛狴狸狷猁狳猃狺逖狼卿狻逢桀鸵留袅眢鸳皱饽饿馀馁凌凇凄栾挛恋桨浆衰勍衷高亳郭席准座症疳疴病疽疸疾痄斋疹痈疼疱疰痃痂疲痉脊效离衮紊唐凋颃瓷资恣凉站剖竞部旁旆旄旅旃畜阃阄阅阆羞羔恙瓶桊拳敉粉料粑益兼朔郸烤烘烜烦烧烛烟烨烩烙烊剡郯烬递涛浙涝浡浦涑浯酒涞涟涉娑消涅涠浞涓涢涡浥涔浩海浜涂浠浴浮涣浼涤流润涧涕浣浪浸涨烫涩涌涘浚悖悚悟悭悄悍悝悃悒悔悯悦悌悢悛害宽宸家宵宴宾窍窅窄容窈剜宰案请朗诸诹诺读扅诼冢扇诽袜袪袒袖袗袍袢被袯祯祧祥课冥诿谀谁谂调冤谄谅谆谇谈谊剥恳展剧屑屐屙弱陵陬勐奘疍牂蚩祟陲陴陶陷陪烝姬娠娱娌娉娟娲恕娥娩娴娣娘娓婀砮哿畚通能难逡预桑剟绠骊绡骋绢绣验绤绥绦骍继绨骎骏邕鸶赊彗耜焘舂琎球琏琐理琇麸琉琅捧掭堵揶措描埴域捺掎埼掩埯捷捯排焉掉掳掴埸堌捶赦赧推堆捭埠晳掀逵授捻埝堋教堍掏掐掬鸷掠掂掖培掊接堉掷掸控捩掮探悫埭埽据掘掺掇掼职聃基聆勘聊聍娶菁菝著菱萁菥菘堇勒黄萘萋勚菲菽菖萌萜萝菌萎萸萑菂菜棻菔菟萄萏菊萃菩菼菏萍菹菠菪菅菀萤营萦乾萧菰菡萨菇械梽彬梵梦婪梗梧梾梢梏梅觋检桴桷梓梳棁梯桫棂桶梭救啬郾匮曹敕副豉票鄄酝酞酗酚厢厣戚戛硎硅硭硒硕硖硗硐硚硇硌鸸瓠匏奢盔爽厩聋龚殒殓殍盛赉匾雩雪辄辅辆堑龁颅虚彪雀堂常眶眭唪眦啧匙晡晤晨眺眵睁眯眼眸悬野圊啪啦喏喵啉勖曼晦晞晗晚冕啄啭啡畦趼趺距趾啃跃啮跄略蚶蛄蛎蛆蚰蚺蛊圉蚱蚯蛉蛀蛇蛏蚴唬累鄂唱患啰唾唯啤啥啁啕唿啐唼唷啴啖啵啶啷唳啸啜帻崖崎崦崭逻帼崮崔帷崟崤崩崞崇崆崛赇赈婴赊圈铐铑铒铕铗铘铙铚铛铜铝铞铟铠铡铢铣铤铥铧铨铩铪铫铭铬铮铯铰铱铲铳铴铵银铷矫氪牾甜鸹秸梨犁稆秽移秾逶笺筇笨笸笼笪笛笙笮符笱笠笥第笳笤笾笞敏偾做鸺偃偕袋悠偿偶偈偎偲傀偷您偬售停偻偏躯皑兜皎假衅鸻徘徙倘得衔舸舻舳盘舴舶船鸼舷舵斜龛盒鸽瓻敛悉欲彩领翎脚脖脯豚脶脸脞脬脱脘脲朘匐鱾象够逸猜猪猎猫猗凰猖猡猊猞猄猝斛觖猕猛馗祭馃馄馅馆凑减鸾毫孰烹庶庹麻庵庼庾庳痔痍疵痊痒痕廊康庸鹿盗章竟翊商旌族旎旋望袤率阇阈阉阊阋阌阍阎阏阐着羚羝羟盖眷粝粘粗粕粒断剪兽敝焐焊烯焓焕烽焖烷烺焌清渍添渚鸿淇淋淅淞渎涯淹涿渠渐淑淖挲淌淏混淠涸渑淮淦淆渊淫淝渔淘淳液淬涪淤淡淙淀涫深渌涮涵婆梁渗淄情惬悻惜惭悱悼惝惧惕惘悸惟惆惚惊惇惦悴惮惋惨惯寇寅寄寂逭宿窒窑窕密谋谌谍谎谏扈皲谐谑裆袱袼裈裉祷祸祲谒谓谔谕谖谗谙谚谛谜谝逮逯敢尉屠艴弹隋堕郿随蛋隅隈粜隍隗隆隐婧婊婞婳婕娼婢婚婵婶婉胬袈颇颈翌恿欸绩绪绫骐续骑绮绯绰骒绲绳骓维绵绶绷绸绹绺绻综绽绾绿骖缀缁巢耠琫琵琴琶琪瑛琳琦琢琥琨靓琼斑琰琮琯琬琛琚辇替鼋揳揍款堪堞搽塔搭塃揸堰揠堙揩越趄趁趋超揽提堤揖博揾颉揭喜彭揣塄揿插揪搜煮堠耋揄援搀蛰蛩絷塆裁揞搁搓搂搅揎壹握摒揆搔揉掾葜聒斯期欺联葑葚葫靰靸散葳惹蒇葬蒈募葺葛蒉葸萼蓇萩董葆葩葡敬葱蒋葶蒂蒌葓蒎落萱葖韩戟朝葭辜葵棒楮棱棋椰植森棼焚椟椅椒棹棵棍椤棰椎棉椑鹀赍棚椋椁棬棕棺榔楗棣椐椭鹁惠惑逼覃粟棘酣酤酢酥酡酦鹂觌厨厦硬硝硪硷确硫雁厥殖裂雄殚殛颊雳雯辊辋  椠暂辌辍辎雅翘辈斐悲紫凿黹辉敞棠牚赏掌晴睐暑最晰量睑睇鼎睃喷戢喋嗒喃喳晶喇遇喊喱喹遏晷晾景喈畴践跖跋跌跗跞跚跑跎跏跛跆遗蛙蛱蛲蛭蛳蛐蛔蛛蜓蛞蜒蛤蛴蛟蛘蛑畯喁喝鹃喂喟斝喘啾嗖喤喉喻喑啼嗟喽嗞喧喀喔喙嵌嵘嵖幅崴遄詈帽嵎崽嵚嵬嵛翙嵯嵝嵫幄嵋赋赌赎赐赑赔黑〔丿〕铸铹铺铻铼铽链铿销锁锃锄锂锅锆锇锈锉锊锋锌锎锏锐锑锒锓锔锕甥掣掰短智矬氰毳毯氮毽氯犊犄犋鹄犍鹅颋剩嵇稍程稀黍桴税稂筐等筘筑策筚筛筜筒筅筏筵筌答筋筝傣傲傅傈舄牍牌傥堡集焦傍傧储遑皓皖粤奥傩遁街惩御徨循舾艇舒畲弑逾颌翕釉番释鹆禽舜貂腈腊腌腓腆腴脾腋腑腙腚腔腕腱腒鱿鲀鲁鲂鲃颍猢猹猩猥猬猾猴飓觞觚猸猱惫飧然馇馈馉馊馋亵装蛮脔就敦裒庾斌痣痨痦痘痞痢痤痪痫痧痛鄌赓竦童瓿竣啻颏鹇阑阒阔阕善翔羡普粪粞尊奠遒道遂孳曾焯焜焰焙焱鹈湛港渫滞湖湘渣渤湮湎湝湨湜渺湿温渴渭溃湍溅滑湃湫溲湟溆渝湲湾渡游溠溇湔滋湉渲溉渥湄滁愤慌惰愠惺愦愕惴愣愀愎惶愧愉愔慨喾割寒富寓窜窝窖窗窘寐谟扉遍雇扊裢裎裣裕裤裥裙祾祺祼谠禅禄幂谡谢谣谤谥谦谧塈遐犀属屡孱弼强粥巽疏隔骘隙隘媒媪絮嫂媛婷媚婿巯毵翚登皴婺骛缂缃缄缅彘缆缇缈缉缌缎缏缑缒缓缔缕骗编缗骙骚缘飨耢瑟瑚鹉瑁瑞瑰瑀瑜瑗瑄瑕遨骜瑙遘韫魂髡肆摄摸填搏塥塬鄢趔趑摅塌摁鼓摆赪携塮蜇搋搬摇搞搪塘搒搐搛搠摈彀毂搌搦摊搡聘蓁戡斟蒜蓍鄞勤靴靳靶鹊蓐蓝墓幕蓦鹋蒽蓓蓖蓊蒯蓟蓬蓑蒿蒺蓠蒟蒡蓄蒹蒴蒲蒗蓉蒙蓂蓥颐蒸献蓣楔椿楠禁楂楚楝楷榄想楫榀楞楸椴槐槌楯榆榇榈槎楼榉楦概楣楹椽裘赖剽甄酮酰酯酪酩酬蜃感碛碍碘碓碑硼碉碎碚碰碇碗碌碜鹌尴雷零雾雹辏辐辑辒输督频龃龄龅龆觜訾粲虞鉴睛睹睦瞄睚嗪睫韪嗷嗉睡睨睢雎睥睬嘟嗜嗑嗫嗬嗔鄙嗦嗝愚戥嗄暖盟煦歇暗暅暄暇照遢暌畸跬跨跶跷跸跣跹跳跺跪路跻跤跟遣蛸蜈蜎蜗蛾蜊蜍蜉蜂蜣蜕畹蛹嗣嗯嗅嗥嗲嗳嗡嗌嗍嗨嗤嗵嗓署置罨罪罩蜀幌嵊嵩嵴骰锖锗错锘锚锛锜锝锞锟锡锢锣锤锥锦锧锨锪锫锩锬锭键锯锰锱矮雉氲犏辞歃稞稚稗稔稠颓愁筹筠筢筮筻筲筼筱签简筷毁舅鼠牒煲催傻像躲鹎魁敫僇衙微徭愆艄觎毹愈遥貊貅貉颔腻腠腩腰腼腽腥腮腭腹腺腧鹏塍媵腾腿詹鲅鲆鲇鲈鲉鲊稣鲋鲌鲍鲏鲐肄猿颖鹐飔飕觥触解遛煞雏馌馍馏馐酱鹑禀亶廒瘃痱痹痼廓痴痿瘐瘁瘅痰瘆廉鄘麂裔靖新鄣歆韵意旒雍阖阗阘阙羧豢誊粳粮数煎猷塑慈煤煳煜煨煅煌煊煸煺滟溱溘滠满漭漠滢滇溥溧溽源滤滥裟溻溷溦滗滫溴滏滔溪滃溜滦漓滚溏滂溢溯滨溶滓溟滘溺滍粱滩滪愫慑慎慥慊誉鲎塞骞寞窥窦窠窣窟寝谨裱褂褚裸裼裨裾裰禊福谩谪谫谬群殿辟障媾嫫媳媲嫒嫉嫌嫁嫔媸叠缙缜缚缛辔缝骝缟缠缡缢缣缤骟剿耥璈静碧瑶璃瑭瑢獒赘熬觏慝嫠韬髦墈墙摽墟墁撂摞嘉摧撄赫截翥踅誓銎摭墉境摘墒摔撇榖撖摺綦聚蔫蔷靺靼鞅靽鞁靿蔌慕暮摹蔓蔑薨蔸蓰蔹蔡蔗蔟蔺戬蔽蕖蔻蓿蔼斡熙蔚鹕兢嘏蓼榛榧模槚槛榻榫槜榭槔榴槁榜槟榨榕槠榷榍歌遭僰酵酽酾酲酷酶酴酹酿酸厮碶\碡碟碴碱碣碳碲磋磁碹碥愿劂臧豨殡需霆霁辕辖辗蜚裴翡雌龇龈睿裳颗夥瞅瞍睽墅嘞嘈嗽嘌嘁嘎暧暝踌踉跽踊蜻蜞蜡蜥蜮蜾蝈蜴蝇蜘蜱蜩蜷蝉蜿螂蜢嘘嘡鹗嘣嘤嘚嘛嘀嗾嘧罴罱幔嶂幢赙罂赚骷骶鹘锲锴锶锷锸锹锻锽锾锵锿镀镁镂镃镄镅舞犒舔稳熏箐箦箧箍箸箨箕箬算箅箩箪箔管箜箢箫箓毓舆僖儆僳僚僭僬劁僦僮僧鼻魄魅魃魆睾艋鄱貌膜膊膈膀膑鲑鲔鲙鲚鲛鲜鲟疑獐獍飗觫雒孵夤馑馒銮裹敲豪膏塾遮麽廙腐瘩瘌瘗瘟瘦瘊瘥瘘瘙廖辣彰竭韶端旗旖膂阚鄯鲞精粼粹粽糁歉槊鹚弊熄熘熔煽熥潢潆潇漤漆漕漱漂滹漫漯漶潋潴漪漉漳滴漩漾演澉漏潍慢慷慵寨赛搴寡窬窨窭察蜜寤寥谭肇綮谮褡褙褐褓褛褊褪禚谯谰谱谲暨屣鹛隧嫣嫱嫩嫖嫦嫚嫘嫜嫡嫪鼐翟翠熊凳瞀鹜骠缥缦缧骡缨骢缩缪缫慧耦耧瑾璜璀璎璁璋璇璆奭撵髯髫撷撕撒撅撩趣趟撑撮撬赭播墦擒撸鋆墩撞撤撙增撺墀撰聩聪觐鞋鞑蕙鞒鞍蕈蕨蕤蕞蕺瞢蕉劐蕃蕲蕰蕊赜蔬蕴鼒槿横樯槽槭樗樘樱樊橡槲樟橄敷鹝豌飘醋醌醇醉醅靥魇餍磕磊磔磙磅碾磉殣慭\震霄霉霈辘龉龊觑瞌瞒题暴瞎瞑嘻嘭噎嘶噶嘲颙暹嘹影踔踝踢踏踟踬踩踮踣踯踪踺踞蝽蝶蝾蝴蝻蝠蝰蝎蝌蝮螋蝗蝓蝣蝼蝤蝙噗嘬颚嘿噍噢噙噜噌嘱噀噔颛幞幡嶓幢嶙嶝墨骺骼骸镊镆镇镈镉镋镌镍镎镏镐镑镒镓镔靠稽稷稻黎稿稼箱箴篑篁篌篓箭篇篆僵牖儇儋躺僻德徵艘磐虢鹞鹟膝膘膛滕鲠鲡鲢鲣鲥鲤鲦鲧鲩鲪鲫鲬橥獗獠觯鹠馓馔熟摩麾褒廛瘛瘼瘪瘢瘤瘠瘫齑鹡凛颜毅羯羰糊糇遴糌糍糈糅翦遵鹣憋熜熵熠潜澍澎澌潵潮潸潭潦鲨潲鋈潟澳潘潼澈澜潽潺澄潏懂憬憔懊憧憎寮窳额谳翩褥褴褫禤谴鹤谵憨熨慰劈履屦嬉勰戮蝥豫缬缭缮缯骣畿耩耨耪璞璟靛璠璘聱螯髻髭髹擀撼擂操熹甏擐擅擞磬鄹颞蕻鞘燕黇颟薤蕾薯薨薛薇檠擎薪薏蕹薮薄颠翰噩薜薅樾橱橛橇樵檎橹橦樽樨橙橘橼墼整橐融翮瓢醛醐醍醒醚醑觱磺磲赝飙殪霖霏霓霍霎錾辙辚臻冀餐遽氅瞟瞠瞰嚄嚆噤暾曈蹀蹅踶踹踵踽嘴踱蹄蹉蹁蹂螨蟒蟆螈螅螭螗螃螠螟噱器噪噬噫噻噼幪罹圜鹦赠默黔镖镗镘镚镛镜镝镞镠氇氆赞憩穑穆穄篝篚篥篮篡簉篦篪篷篙篱盥儒劓翱魉魈邀徼衡歙盦膨膪膳螣膦膙雕鲭鲮鲯鲰鲱鲲鲳鲴鲵鲷鲸鲺鲹鲻獴獭獬邂憝亸鹧磨廨赟癀瘭瘰廪瘿瘵瘴癃瘾瘸瘳斓麇麈凝辨辩嬴壅羲糙糗糖糕瞥甑燎燠燔燃燧  燏濑濒濉潞澧澡澴激澹澥澶濂澼憷懒憾懈黉褰寰窸窿褶禧壁避嬖犟隰嬗鹨翯颡缰缱缲缳缴璨璩璐璪戴螫擤壕擦觳罄擢藉薹鞡鞠藏薷薰藐藓藁檬檑檄檐檩檀懋醢翳繄礁礅磷磴鹩霜霞龋龌豳壑黻瞭瞧瞬瞳瞵瞩瞪嚏曙嚅蹑蹒蹋蹈蹊蹓蹐蟥螬螵疃螳螺蟋蟑蟀嚎嚓羁罽罾嶷赡黜黝髁髀镡镢镣镤镥镦镧镨镩镪镫罅穗黏魏簧簌篾簃篼簏簇簖簋繁鼢黛儡鹪鼾皤魍徽艚龠爵繇貘邈貔臌朦臊膻臁臆臃鲼鲽鲾鳀鳁鳂鳃鳄鳅鳆鳇鳈鳉鳊獯螽燮鹫襄糜縻膺癍癌麋辫赢糟糠馘燥懑濡濮濞濠濯懦豁蹇謇邃襕襁臀檗甓臂擘孺隳嬷翼蟊鹬鍪骤鏊鳌鬶鬈鬃瞽藕鞯鞨鞭鞫鞧鞣藜藠藤藩鹲檫檵覆醪蹙礞礓礌燹餮瞿瞻曛颢曜躇蹦鹭蹢蹜蟛蟪蟠蟮嚚嚣鹮黠黟髅髂镬镭镯镰镱馥簠簟簪簦鼫鼬鼩雠艟翻臑鳍鳎鳏鳐鳑鹱鹰癞癔癜癖糨冁蹩瀑瀍瀌鎏懵襟璧戳彝邋鬏攉攒鞲鞴藿蘧孽蘅警蘑藻麓攀醭醮醯礤酃霪霭黼曝嚯蹰蹶蹽蹼蹯蹴蹾蹲蹭蹿蹬蠖蠓蠋蟾蠊巅黢髋髌镲籀簸籁簿鳘齁魑艨鼗鳓鳔鳕鳗鳙鳚蟹颤靡癣麒鏖瓣蠃羸羹鳖爆瀚瀣瀛襦谶襞疆骥缵瓒鬓壤攘馨蘩蘖蘘醵醴霰颥酆耀矍曦躁躅蠕鼍嚼嚷巍巉黩黥镳镴黧籍纂鼯臜鳜鳝鳞鳟獾魔糯灌瀹瀵譬孀骧耰蠢瓘鼙醺礴礳霸露霹颦曩躏黯髓鼱鳡鳢癫麝赣夔爝灏禳鐾羼蠡耲耱懿韂蘸鹳蘼囊霾氍饕躔躐髑镵镶穰鳤瓤饔鬻鬟趱攫攥颧躜罐鼹鼷癯麟蠲矗蠹醾躞衢鑫灞襻纛鬣攮囔馕戆蠼爨齉  `~!@#$%^&*()_+-=,./;'[]\<>?:"{}|·!¥……()——,。、;‘’【】、《》?:“”ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789`~!@#$%^&*()_+-=,./;'[]\<>?:"{}|·!¥……()——,。、;‘’【】、《》?:“”ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789℃μ

热更实操

配置

先展示一下配置,具体如何配置查看上一篇文章

与上一篇文章设置的一样

前置准备

  • 切换成安卓平台
  • 删除./ServerData/文件夹和./Library/com.unity.addressables/aa/文件夹

经测试:

  • 直接使用Build Profiles的Build时,也会进行Build -> NewBuild -> Default Build Script生成资产文件

  • Addressable Asset Settings -> Build Remote Catalog是非常重要的设置,进行以下实验时需时刻注意本地缓存文件。

    • 所有的资产都在Default Local Group中:
      • 未勾选:不会向服务器发送请求,物体能正常加载。(可以在包体路径中找到本地资产)
      • 勾选+Local:不会向服务器发送请求,物体能正常加载(可以在包体路径中找到本地资产,且多了catalog_0.1.0.bincatalog_0.1.0.hash文件,这两个文件是有版本后缀的,不要与catalog.bincatalog.hash混淆了)
      • 勾选+Remote:向服务器发送请求,若请求失败,则不会加载物体(即使local中有资产);请求成功后,若没有更新则直接使用本地本地资源;若有更新则将更新缓存到本地。(可以在包体路径中找到本地资产,没有catalog文件)。
    • 所有的资产都在Remote Group中:
      • 未勾选:向服务器发送请求(报错:无法链接到目标地址),且无法加载Remote Group中的物体。(并未在包体中找到ServerData文件,且包体本地中也未找到任何资产),只有将打包生成的ServerData上传服务器才能显示图片。
      • 勾选+Local:向服务器发送请求(报错:无法链接到目标地址),且无法加载Remote Group中的物体。(同样没找到资产文件,但本地路径下多了两个catalog文件)
      • 勾选+Remote:向服务器发送请求,若有更新则将更新缓存到本地;

    总结:

    • 经测试Android与Window效果一致(需注意缓存文件)。
    • 直接放置在Remote Group中的资产不管如何设置都会向服务器发送请求。因为该组的资产只build在ServerData中,用户获取的包体里并没有该组的资产。用户只能从服务器上下载。
    • 另外有一个特殊情况:”勾选+Remote”时,用户打开游戏的时候会先向服务器确认是否有更新(如果请求失败不会显示图片),然后再显示图片(没有更新:直接显示本地的;有更新:将远程的资产下载到本地)。具体流程如下图

另外发现一个很奇怪的问题:

在删除Library编译文件,和C:\Users\coffeeofnosugar\AppData\LocalLow\Unity\DefaultCompany_XLuaProject本地缓存之后。如果你之前向服务器获取过这张图片,即使服务器上什么都没有,打开游戏时也能正常显示图片。应该是本地缓存了,但作者还为找到存在什么位置。

上方的流程图和下方的测试是没毛病的


Local Group热更新

具体流程就像上图,这里我们具体操作一下,Windows方便查看log和本地缓存,所以先还是使用Windows版

经上面的测试,我们只需测试,只有在设置成”勾选+Remote”的时候才会向服务器发送请求,设置如下

打包后开启游戏

  • 即使Remote组中没有物体,也会向服务器发送请求

  • 即使物体是在Local Group中,服务端上没有该物体,依然不会加载

  • 报错内容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15

    Unable to open archive file: D:/UnityProject/XLuaProject/Build/XLuaProject_Data/StreamingAssets/aa/StandaloneWindows64/defaultlocalgroup_assets_all_57c55f293171dd03a4c62f3e626f64b9.bundle
    RemoteProviderException : Invalid path in AssetBundleProvider: 'D:/UnityProject/XLuaProject/Build/XLuaProject_Data/StreamingAssets/aa\StandaloneWindows64\defaultlocalgroup_assets_all_57c55f293171dd03a4c62f3e626f64b9.bundle'.
    OperationException : GroupOperation failed because one of its dependencies failed
    RemoteProviderException : Invalid path in AssetBundleProvider: 'D:/UnityProject/XLuaProject/Build/XLuaProject_Data/StreamingAssets/aa\StandaloneWindows64\defaultlocalgroup_assets_all_57c55f293171dd03a4c62f3e626f64b9.bundle'.
    System.Exception: Dependency Exception ---> UnityEngine.ResourceManagement.Exceptions.OperationException: GroupOperation failed because one of its dependencies failed ---> UnityEngine.ResourceManagement.Exceptions.RemoteProviderException: RemoteProviderException : Invalid path in AssetBundleProvider: 'D:/UnityProject/XLuaProject/Build/XLuaProject_Data/StreamingAssets/aa\StandaloneWindows64\defaultlocalgroup_assets_all_57c55f293171dd03a4c62f3e626f64b9.bundle'.

    --- End of inner exception stack trace ---
    --- End of inner exception stack trace ---
    Failed to read data for the AssetBundle 'aa\StandaloneWindows64\defaultlocalgroup_assets_all_57c55f293171dd03a4c62f3e626f64b9.bundle'.
    OperationException : ChainOperation failed because dependent operation failed
    System.Exception: Dependency Exception ---> UnityEngine.ResourceManagement.Exceptions.OperationException: GroupOperation failed because one of its dependencies failed ---> UnityEngine.ResourceManagement.Exceptions.RemoteProviderException: RemoteProviderException : Invalid path in AssetBundleProvider: 'D:/UnityProject/XLuaProject/Build/XLuaProject_Data/StreamingAssets/aa\StandaloneWindows64\defaultlocalgroup_assets_all_57c55f293171dd03a4c62f3e626f64b9.bundle'.

    --- End of inner exception stack trace ---
    --- End of inner exception stack trace ---

提示找不到下面的路径,但奇怪的是这路径是确实是我们的本地路径,为什么没有资源呢./XLuaProject_Data/StreamingAssets/aa/StandaloneWindows64/defaultlocalgroup_assets_all_57c55f293171dd03a4c62f3e626f64b9.bundle

该路径下确实有Default Local Group,但并不是报错所要的内容。

上传资产

因为我们选择的是”勾选+Remote”,所以会在./ServerData/StandaloneWindows64/路径下生成两个catalog文件。将这个两个文件上传到服务器,后再次打开游戏——加载成功,并且没有报错。

这时很奇怪的一幕就会发现,当我们把服务器上的资产删掉后,再次打开游戏。服务端会接受到请求,但物体能正常加载,且并不是简单的缓存,而是直接读取的客户端的本地资产。

此时我们将锤子更换成弓箭

使用Update a Previous Build重新打包

更新资产

此时直接打开游戏,依然是锤子。我们需要将新的ServerData包体上传到服务器,再打开游戏,但最后图片加载失败,报错内容:

1
Unable to open archive file: D:/UnityProject/XLuaProject/Build/XLuaProject_Data/StreamingAssets/aa/StandaloneWindows64/defaultlocalgroup_assets_assets/game/art/sprites_0d30367a8dddecfef828636864c4bae5.bundle

正确做法:

  1. 将更改的资产添加到Remote Group中
  2. 更新包体,并上传到服务器

再次打开游戏,能看到图片变成弓箭了,并且在服务端log上能看到下载.bundle的log

此时我们能看到C:\Users\coffeeofnosugar\AppData\LocalLow\Unity\DefaultCompany_XLuaProject路径下多了一个文件夹,这个就是刚才从服务器上下载下来的缓存文件,测试以下两种情景。

  • 再次打开游戏,向服务器发送请求获取catalog,得知要使用弓箭图片,使用本地缓存资产。
  • 删掉缓存文件,打开游戏,向服务器发送请求获取catalog,得知要使用弓箭图片,重新下载。

得知,热更之后,如果有本地缓存,则限优先使用本地缓存,若没有没有本地缓存则向服务端下载。

再来测试以下四种情景,先把缓存文件下载下来:

  • 保留服务器的catalog文件、删除资产文件,保留缓存文件:启动游戏,能显示弓箭——使用的本地缓存。
  • 保留服务器的catalog文件、删除资产文件,并删除缓存文件:启动游戏,弓箭显示失败,能看到服务器上获取弓箭的资产404
  • 删除服务器的catalog资产文件,但保留缓存文件:启动游戏,显示弓箭——使用本地缓存。
  • 删除服务器的catalog资产文件,且删除缓存文件:启动游戏,弓箭和锤头都不显示了,且在退出游戏时会卡死无响应。

可以得知,一旦游戏热更之后,如果服务器无法访问,则游戏无法继续运行

小结

最关键的点是:最开始物体在Local Group中,在需要实现热更的时候将其移动到Remote Group中。

这种方式可以很大程度上的缓解服务器的压力,让玩家在下载游戏时并不是所有内容都从服务器上拉取,很适合小工作室。

经过新项目测试,发现Local Group中的物体在打开游戏时,如果服务器上没有catalog文件,图片是不会显示的。所以如果想要通过将资产放在local中,从而完全避免搭建服务器是不可能的。


Remote Group热更新

上一篇文章探讨了如果设置成”勾选+Retmoe”时Retmoe Group组中的热更新情况。

这里用流程图概括一下


总结

上面过程可能比较长,主要是为了测试效果,以及每一步的表现,这里简单的概括一下总流程

  1. 物体最开始可以直接放置在Local中,也可以放置在Remote中
    • Local:虽然也会访问服务器,但只是检查更新,并不会下载任何东西
    • Remote:该组的内容都是从服务器上拉取下来的
  2. 若需要热更新
    • Local:将其从local组中添加到remote组中,再执行Update a Previous Build,将新生成的文件上传到服务器上
    • Remote:修改好后直接执行Update a Previous Build,并将新生成的文件上传到服务器上
  3. 真机测试

本地Addressables使用

Group配置

加载资源

1
2
3
4
5
6
7
8
9
10
11
12
public class LoadSprite : MonoBehaviour
{
[SerializeField] private AssetReferenceSprite sprite;

private IEnumerator Start()
{
var spHandle = sprite.LoadAssetAsync<Sprite>();
yield return spHandle;
GetComponent<Image>().sprite = spHandle.Result;
Addressables.Release(spHandle); // 不规范写法,应写在OnDestroy中
}
}

开始游戏后成功屏幕中间的白方块变成单手剑

如果直接引用资源,在游戏启动的时候就会加载该资源(如果资源很多就会卡很久),且该资源会一直存在游戏生命周期内。

本地使用Addressables的优点:

  • 不会在场景开始的时候就加载该资源,而是会等到真正使用的时候加载
  • 可以手动释放该资源占用内容,减少内存压力。

详情测试观看How to use Addressables FASTER Loading FREE Memory SMALL Download

  • 4:05:基础Instantiate()方法加载预制体,启动游戏时间需20秒;且未创建物体时就已经占用内存了。
  • 11:25:Addressable方法加载预制体,启动游戏时间只需2秒;未创建物体时没有占用内存,只有当物体加载出来时才会占用内存。

所以,为了优化游戏,不管你是否需要热更新,最好使用Addressables加载资源,而不是常规方式

真实环境测试

上方的代码直接运行是没有问题的,因为此时unity是直接从Assets Databse(也就是资产路径中)获取资产

为了模拟真实的用户环境,需要如下设置

此时运行游戏会发现:剑只出现了一帧,然后又变成了白方块

原因:Addressables.Release(spHandle)的时候释放了weapon这个图片,所以Image在内存中也找不到该图片。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 正确的写法:
AsyncOperationHandle<Sprite> spHandle; // 声明为字段
private IEnumerator Start()
{
spHandle = sprite.LoadAssetAsync<Sprite>();
yield return spHandle;
GetComponent<Image>().sprite = spHandle.Result;
Debug.Log(spHandle.Result == null);
}
private void OnDestroy()
{
Addressables.Release(spHandle); // 手动释放,减少GC压力
}

远程Addressables使用

配置

添加新的路径配置

添加远程Group。不要删掉Local Group!!!!!!

设置为Pack Separately时,从上图可以看出来,虽然Remote组下有三个物体,但是依然只会生成两个文件。因为我们是添加了两个文件夹在Group中。

Addressable设置

此时如果在Use Existing Build模式下将无法启动unity,且会报错。因为并没有构建Addressables资产,先Build -> New Build -> Default build Script一份吧。

1
Player content must be built before entering play mode with packed data.  This can be done from the Addressables window in the Build->Build Player Content menu command.

将资产上传服务器

构建之后,直接启动unity也会报错,且图片不显示。因为我们并没有将物体放置到服务器上。看起来这是很理所当然的事情。但这里我需要强调的是:在物体进入到了Group之后,该物体就不访问本地,而只能访问远程的了。因为本地根本就不存在该资产

1
2
3
4
RemoteProviderException : Unable to load asset bundle from : https://www.coffeeofnosugar.top/addressable/c15f93f7cc4db145f53ace35f7416bfa.bundle
UnityWebRequest result : ProtocolError : HTTP/1.1 404 Not Found
ResponseCode : 404, Method : GET
url : https://www.coffeeofnosugar.top/addressable/c15f93f7cc4db145f53ace35f7416bfa.bundle

上传服务器

此时启动unity,就能看到单手剑了

注意:

从服务器获取到的资源会缓存到本地,这时我们再次启动游戏,游戏不会向服务器发送请求

可以使用Everything找到该缓存,删除后再测试一遍,缓存名称为.bundle文件的哈希值

如此也从侧面的证明了,该资产确实是从服务器获取到的

实现热更

将单手剑替换成锤子,在Explorer中直接替换文件

更新包体

此时,你会发现ServerData/StandaloneWindow64中多了一个文件,将其上传到服务器上

重新打开游戏,热更成功。


调试

Ctrl+7唤出性能调试面板

就拿前面的Addressables.Release(spHandle);举例


总结

  1. Addressables十分强大,不管是否需要使用热更,最好都使用Addressable管理资源
  2. 直接将所有资产都放到Remote Group下肯定是不理想的,这样一来所有资产都需要从网上拉取。(但有的手游每次大更新的时候都需要下载很长的一条内容,原因应该正是如此。)能否有更好的方法能减少服务器压力呢,请看下一篇文章。

原因:引用的是地址,而不是值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static void Main(string[] args)
{
Action[] actions = new Action[3];
for (int i = 0; i < 3; i++)
{
actions[i] = () => Console.WriteLine(i); // 该委托保存的是i的地址
}

foreach (var action in actions)
{
// 输出i的地址的值
action(); // 输出 3 3 3,而不是 0 1 2
}
}

交换两个数的值

元组解构

该方法也会创建一个临时变量(元组)

1
(a, b) = (b, a)

异或

a,b 不能是同一地址。由于异或的特性,如果两个是同一地址会导致输出为0

1
2
3
a = a ^ b;
b = a ^ b;
a = a ^ b;

拓展:异或

相同则为0,不同则为1,可以理解成“不进位的加法”

  • 0 ^ N = N N ^ N = 0
  • 满足交换律和结合律

加减

同样a,b不能是同一地址,不然在第二步的时候,自己减自己等于0

1
2
3
a = a + b;
b = a - b;
a = a - b;

时间复杂度

常数操作的次数


排序算法

选择排序

简单选出最小的数,将其放置在最前面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
static void SelectionSort(int[] arr)
{
if (arr == null || arr.Length < 2) return; // 直接返回不需要排序的数组
for (int i = 0; i < arr.Length; i++)
{
int minIndex = i;
for (int j = i + 1; j < arr.Length; j++) // 寻找最小值的序号
{
if (arr[j] < arr[minIndex])
minIndex = j;
}
Swap(arr, i, minIndex); // 找出最小值的序号后与最前面的数值交换位置
}
}

static void Swap(int[] arr, int a, int b)
{
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}


int[] nums = new[] { 5, 2, 7, 3, 9, 1, 8, 6 };
nums = QuickSort(nums);
Console.WriteLine(string.Join(", ", nums));

时间复杂度计算:

set1:

  • n-1次获取数值
  • n-1次比较
  • 1次交换

set2:

  • n-2次获取数值
  • n-2次比较
  • 1次交换

……

由等差数列公式可知:

  • 获取数值共:n(n+1)/2次
  • 比较共:n(n+1)/2次
  • 交换共:n次

三者相加最大的阶乘为2,所以时间复杂度为O(n^2)

拓展:等差数列公式

冒泡排序

相邻的两个数相比较,一步一步的将最大的数移动到最右边

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
static void BubbleSort(int[] arr)
{
if (arr == null || arr.Length < 2) return;
for (int k = 0; k < arr.Length; k++) // 共需要移动length的次数
{
for (int i = 0; i < arr.Length - 1; i++)
{
if (arr[i] > arr[i + 1]) // 将最大的值移动到最右边
{
Swap(arr, i, i + 1);
}
}
}
}
// 冒泡排序不会与自己交换位置,所以可以使用异或交换值
static void Swap(int[] arr, int a, int b)
{
arr[a] = arr[a] ^ arr[b];
arr[b] = arr[a] ^ arr[b];
arr[a] = arr[a] ^ arr[b];
}

插入排序

第一步:将[0]排序;第二步:将[0,1]排序;第三步:将 [0,2]排序;第四步:将[0,3]排序……

具体插入实现方法(set7):

当我们到达set7时,[0,6]一定是有序的。所以从右往左一个一个比较

  • 若arr[7]小于arr[6]两者交换位置。
1
2
3
4
5
6
7
8
9
10
11
static void InsertionSort(int[] arr)
{
if (arr == null || arr.Length < 2) return;
for (int i = 1; i < arr.Length; i++)
{
for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) // 当j>=0,且arr[j]大于其右边的数,两者交换位置
{
Swap(arr, j, j + 1);
}
}
}

归并排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
static void MergeSort(int[] arr, int left, int right)
{
if (left >= right) return;
int mid = left + ((right - left) >> 1);
MergeSort(arr, left, mid);
MergeSort(arr, mid + 1, right);
merge(arr, left, mid, right);
}

static void merge(int[] arr, int L, int M, int R)
{
int[] temp = new int[R - L + 1];
int i = 0, p1 = L, p2 = M + 1;

while (p1 <= M && p2 <= R)
temp[i++] = arr[p1] <= arr[p2] ? arr[p1++] : arr[p2++];
while (p1 <= M)
temp[i++] = arr[p1++];
while (p2 <= R)
temp[i++] = arr[p2++];
for (int j = 0; j < temp.Length; j++)
arr[L + j] = temp[j];
}

快速排序

在学习快速排序前先看一下荷兰国旗问题

现有一个数组arr和target,要求小于target的数放在数组左边,大于target的放在数组的右边,等于target的数放在中间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 荷兰国旗
static void Partition(int[] arr, int target)
{
int left = -1, right = arr.Length;
int i = 0;
while (i < right)
{
if (arr[i] < target)
Swap(arr, ++left, i++);
else if (arr[i] > target)
Swap(arr, i, --right);
else
i++;
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 快速排序
static void QuickSort(int[] arr, int L, int R)
{
if (L >= R) return;
int random = new Random().Next(R - L + 1); // 随机取出一个数将其作为基准,这样可以将事件复杂度降低为O(N*logN)
Swap(arr, L + random, R);
int[] p = Partition(arr, L, R);
QuickSort(arr, L, p[0] - 1); // < 区域
QuickSort(arr, p[1] + 1, R); // > 区域
}

/// <summary> 荷兰国旗问题 </summary>
/// <returns>返回等于区域的左右边界</returns>
static int[] Partition(int[] arr, int L, int R)
{
int less = L - 1; // < 区域右边界
int more = R; // > 区域左边界
while (L < more)
{
if (arr[L] < arr[R]) // 当指针上的数小于基准数时,将指针上的数与 < 区域的下一个数交换,指针右移
Swap(arr, ++less, L++);
else if (arr[L] > arr[R]) // 当指针上的数大于基准数时,将指针上的数与 > 区域的前一个数交换,指针不动
Swap(arr, --more, L);
else // 当指针上的数等于基准数时,指针右移
L++;
}
Swap(arr, R, more); // 将基准数与 > 区域的第一个数交换
return new[] { less + 1, more }; // 返回等于区域的左右边界
}

二分法

在有序数列中查找某个值

1
2
3
4
5
6
7
8
9
10
static void fun(int[] arr, int target)
{
int m = arr.Length / 2;
if (arr[m] == target)
Console.WriteLine(m);
else if (arr[m] > target)
fun(arr[..m], target);
else
fun(arr[(m + 1)..], target);
}

在有序数列中查找最大值

1
2
3
4
5
6
7
8
static int fun2(int[] arr, int start, int end)
{
if (start == end) return arr[start];
int mid = start + ((end - start) >> 1);
int leftMax = fun2(arr, start, mid);
int rightMax = fun2(arr, mid + 1, end);
return Math.Max(leftMax, rightMax);
}

递归方法的时间复杂度:

master公式的使用:

使用master公式的前提条件是在递归的时候平分数据,如fun2将数平方成了2份,也可以平分成3份、2/3份,只要是平分就行。
$$
T(N) = a * T (\frac{N}{b}) + O(N^d)
$$
a:一个方法中使用多少次递归

b:递归的时候,将数据平方成了多少份

d:除去递归函数外其他部分的时间

最终的结果为:
$$
T(N) = 2 * T (\frac{N}{2}) + O(N^0)
$$
当递归函数满足master公式时,可以通过a、b、d得到算法复杂度
$$
\begin{split}
&若log_b{a}<d,则O(N^d) \
&若log_b{a}>d,则O(N^{log_b{a}}) \
&若log_b{a}=d,则O(N^d*logN)
\end{split}
$$

所以最后的时间复杂度为O(N)

批处理

批处理的目的是为了减少Draw Call的调用次数

什么是Draw Call?

  1. CPU先准备好模型的网格、用到的贴图和Shader,然后将网格、贴图、Shader加载到显存里面。
  2. 然后是CPU设置渲染状态:每个网格用哪个Shader、贴图渲染。
  3. CPU通知GPU开始渲染(通知的这个命令就叫做Draw Call)。

UGUI实质上也是由网格组成的,所以也会产生Draw Call

总结

合批优化

  • 使用图集
  • 动静分离(动态部分和静态部分分别使用不同的Canvas)
  • Text如果可以用图片代替就用图片代替
  • 避免频繁删除/增加UI对象,UI层次结构变化会引起Canvas的更新(rebuild)
  • 避免UI元素数目过多和层次结构过于复杂影响Batch更新速度
  • 尽量不要使用Mask(其内部使用了模板缓冲,至少会造成增加2个Draw Call)

其他优化

  • 尽量不要使用Outline、TiledSprite(两者会产生许多顶点)
  • 不需要点击事件的取消勾选Raycast

参考

Unity3D UGUI系列之合批