-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.html
1757 lines (1261 loc) · 214 KB
/
index.html
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge" >
<link rel="dns-prefetch" href="http://yoursite.com">
<title>Noodle's blog</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta property="og:type" content="website">
<meta property="og:title" content="Noodle's blog">
<meta property="og:url" content="http://yoursite.com/index.html">
<meta property="og:site_name" content="Noodle's blog">
<meta property="og:locale">
<meta property="article:author" content="Dong Lu">
<meta name="twitter:card" content="summary">
<link rel="alternative" href="/atom.xml" title="Noodle's blog" type="application/atom+xml">
<link rel="icon" href="https://avatars0.githubusercontent.com/u/364050?s=400&u=ac28bf8d753a9138650feabe16f57fea46d9a5cc&v=4">
<link rel="stylesheet" type="text/css" href="/./main.0cf68a.css">
<style type="text/css">
#container.show {
background: linear-gradient(200deg,#a0cfe4,#e8c37e);
}
</style>
<meta name="generator" content="Hexo 7.1.1"></head>
<body>
<div id="container" q-class="show:isCtnShow">
<canvas id="anm-canvas" class="anm-canvas"></canvas>
<div class="left-col" q-class="show:isShow">
<div class="overlay" style="background: #4d4d4d"></div>
<div class="intrude-less">
<header id="header" class="inner">
<a href="/" class="profilepic">
<img src="https://avatars0.githubusercontent.com/u/364050?s=400&u=ac28bf8d753a9138650feabe16f57fea46d9a5cc&v=4" class="js-avatar">
</a>
<hgroup>
<h1 class="header-author"><a href="/"></a></h1>
</hgroup>
<nav class="header-menu">
<ul>
<li><a href="/">主页</a></li>
</ul>
</nav>
<nav class="header-smart-menu">
<a q-on="click: openSlider(e, 'innerArchive')" href="javascript:void(0)">所有文章</a>
<a q-on="click: openSlider(e, 'aboutme')" href="javascript:void(0)">关于我</a>
</nav>
<nav class="header-nav">
<div class="social">
</div>
</nav>
</header>
</div>
</div>
<div class="mid-col" q-class="show:isShow,hide:isShow|isFalse">
<nav id="mobile-nav">
<div class="overlay js-overlay" style="background: #4d4d4d"></div>
<div class="btnctn js-mobile-btnctn">
<div class="slider-trigger list" q-on="click: openSlider(e)"><i class="icon icon-sort"></i></div>
</div>
<div class="intrude-less">
<header id="header" class="inner">
<div class="profilepic">
<img src="https://avatars0.githubusercontent.com/u/364050?s=400&u=ac28bf8d753a9138650feabe16f57fea46d9a5cc&v=4" class="js-avatar">
</div>
<hgroup>
<h1 class="header-author js-header-author"></h1>
</hgroup>
<nav class="header-nav">
<div class="social">
</div>
</nav>
<nav class="header-menu js-header-menu">
<ul style="width: 50%">
<li style="width: 100%"><a href="/">主页</a></li>
</ul>
</nav>
</header>
</div>
<div class="mobile-mask" style="display:none" q-show="isShow"></div>
</nav>
<div id="wrapper" class="body-wrap">
<div class="menu-l">
<div class="canvas-wrap">
<canvas data-colors="#eaeaea" data-sectionHeight="100" data-contentId="js-content" id="myCanvas1" class="anm-canvas"></canvas>
</div>
<div id="js-content" class="content-ll">
<article id="post-llvm ADT之栈上容器" class="article article-type-post article-index" itemscope itemprop="blogPost">
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2024/05/20/llvm%20ADT%E4%B9%8B%E6%A0%88%E4%B8%8A%E5%AE%B9%E5%99%A8/">llvm ADT之栈上容器</a>
</h1>
<a href="/2024/05/20/llvm%20ADT%E4%B9%8B%E6%A0%88%E4%B8%8A%E5%AE%B9%E5%99%A8/" class="archive-article-date">
<time datetime="2024-05-20T01:32:10.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2024-05-20</time>
</a>
</header>
<div class="article-entry" itemprop="articleBody">
<p>TL;DR<br><a href="https://github.com/noodle1983/llvm-ADT">https://github.com/noodle1983/llvm-ADT</a></p>
<h1 id="1-简要模型"><a href="#1-简要模型" class="headerlink" title="1. 简要模型"></a>1. 简要模型</h1><p>栈上容器,其实也不太对,他其实是指预分配内存的一类容器,只是他通常作为临时变量在函数内使用,编译期就确定了大小,构造时不使用堆内存(new/malloc),直接在栈上构建,随函数退出析构。大概的实现:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">class XXX{</span><br><span class="line"> //1. 存储</span><br><span class="line"> char storage[N];</span><br><span class="line"> </span><br><span class="line"> //2. 管理字段</span><br><span class="line"> ...</span><br><span class="line"> </span><br><span class="line"> //3. 算法</span><br><span class="line">};</span><br><span class="line"></span><br></pre></td></tr></table></figure>
<p>这样就比较好理解下面这句的内存分配了。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">void hello_world(){</span><br><span class="line"> llvm::SmallString<10> str("hello");</span><br><span class="line"> ...</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>这种情况也比较好理解:作为一个字段放在另外一个对象里去new一个对象,这段内存就不在栈上了。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">class ABC{</span><br><span class="line"> llvm::SmallString<16> name;</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line">ABC* abc = new ABC() //<- 这段内存</span><br></pre></td></tr></table></figure>
<p>如果大小超过了预分配内存,会如何?如果超过了,会退化成std相应容器。</p>
<h1 id="2-使用场景"><a href="#2-使用场景" class="headerlink" title="2. 使用场景"></a>2. 使用场景</h1><ul>
<li>临时对象,用完就丢的场合,没必要做内存泄漏方面的校验和检查,减少内存分配器压力,对用着内存校验的堆分配器会有明显的改进。</li>
</ul>
<p>std平替容器</p>
<table>
<thead>
<tr>
<th>std</th>
<th>栈容器</th>
<th>补充说明</th>
</tr>
</thead>
<tbody><tr>
<td>std::vector</td>
<td>llvm::SmallVector</td>
<td></td>
</tr>
<tr>
<td>std::string</td>
<td>llvm::SmallString</td>
<td></td>
</tr>
<tr>
<td>std::set</td>
<td>llvm::SmallDenseSet</td>
<td>对象个数限制2的n次幂</td>
</tr>
<tr>
<td>std::map</td>
<td>llvm::SmallDenseMap</td>
<td>对象个数限制2的n次幂</td>
</tr>
</tbody></table>
<p>2的n次幂的限制源于key的hash取Bucket index的Mask,改成mod应该能把他去掉,因为如果元素hash mask后是散列的,那么取余也是散列的。改了之后理论上会慢一点(但大多数时候不需要计较的那种)。我用的地方只有几处,没改。</p>
<h1 id="3-源码"><a href="#3-源码" class="headerlink" title="3. 源码"></a>3. 源码</h1><p>源码在llvm的<a href="https://llvm.org/doxygen/dir_32453792af2ba70c54e3ccae3a790d1b.html">ADT目录</a>下, 和llvm其他代码有耦合。</p>
<p>我把他做了剥离,参见<a href="https://github.com/noodle1983/llvm-ADT">https://github.com/noodle1983/llvm-ADT</a>,Linux/Windows测试和使用没什么问题。<br>除了栈上容器,还有其他的一些bit容器,稀疏容器等好东西,我没用的上。</p>
<p>用法参考源码里面的unittest,可以打开VS工程下个断点看看,Linux我直接集成进游戏工程了,没去折腾unittest。</p>
<h1 id="4-背后的故事"><a href="#4-背后的故事" class="headerlink" title="4. 背后的故事"></a>4. 背后的故事</h1><p>吹牛B时间。</p>
<p>我对它的认识还是刚毕业在爱立信的时候,做的第一个项目是基于Sun Solaris的,这个操作系统各方面的文档超全。在工作之余,一边读文档,一边拿项目做实验。印象很深的一个,是统计各个函数的总耗时,看看哪个函数比较耗,看看有什么改进的。结果出乎我的意料,最耗的函数,竟是std::string构造函数的内存分配,而且和第二名是数量级的差别。具体数据因历史久远就不可考了(当时的内存分配器比较弱,线程级的分配器因为有bug没开,估计加剧了这种差异),只是这个项目是一个电信的计费网关,没有密集的字符串处理,最多就是写日志和话单,所以当时印象深刻,却又束手无策,就惦记上了。</p>
<p>在爱立信的第二个项目,一个http网关,我神奇地发现,项目自己实现了一个MyString对象,预分配的64字节的空间,超出后退化为std::string。使用过程中发现,栈上这么一个MyString对象,64字节就在栈上,大部分时候,字符串都很短小,因此不会像堆申请任何内存,随着函数的退出,栈的弹出,自然就析构释放了,十分的精巧。</p>
<p>之后几年线程级内存分配器很快成熟,这块的消耗好像也不算什么了。仅在广州西山居的服务器后台见到,有种似曾相识的感觉。</p>
<p>辗转到游雁游戏,后端用着一个纯c++的游戏服务器,没带任何的内存检查设施,遂开发环境挂上Address Sanitizer。不出意料,卡。100毫秒的掉帧监控发现内存分配和释放都卡,尤其是释放有一个校验内存合法性的循环十分卡。本着有困难要上,没有困难创造困难也要上的精神,想先把大量的临时字符串内存用预分配的方法去掉。再本着别人有,就不用自己做的精神在Google/Github上搜。确实有一个SmallString,再朔源,发现是llvm的ADT(advanced data type)的单个文件剥离版。那段时间刚好在centos6上编译了最新的llvm,一看源码,我就乐了,不单止string有SmallString,vector/set/map都有相应的实现,和之前在爱立信看到的思路一样,超了就退化到std库,比较灵活的一点是预分配元素的个数可以通过模板参数提供。当然,还有其他的侵入式容器,bit相关容器等。</p>
<p>好东西,只是这个库依赖于llvm的其他组件,遂做移植,裁剪,linux/windows下可用。</p>
</div>
<div class="article-info article-info-index">
<div class="article-tag tagcloud">
<i class="icon-price-tags icon"></i>
<ul class="article-tag-list">
<li class="article-tag-list-item">
<a href="javascript:void(0)" class="js-tag article-tag-list-link color4">c++</a>
</li>
</ul>
</div>
<p class="article-more-link">
<a class="article-more-a" href="/2024/05/20/llvm%20ADT%E4%B9%8B%E6%A0%88%E4%B8%8A%E5%AE%B9%E5%99%A8/">展开全文 >></a>
</p>
<div class="clearfix"></div>
</div>
</div>
</article>
<aside class="wrap-side-operation">
<div class="mod-side-operation">
<div class="jump-container" id="js-jump-container" style="display:none;">
<a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
<i class="icon-font icon-back"></i>
</a>
<div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
<i class="icon-font icon-plane jump-plane"></i>
</div>
</div>
</div>
</aside>
<article id="post-NavmeshMarker_README" class="article article-type-post article-index" itemscope itemprop="blogPost">
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2024/04/14/NavmeshMarker_README/">NavimeshMarker_ExportWithoutBake_Readme</a>
</h1>
<a href="/2024/04/14/NavmeshMarker_README/" class="archive-article-date">
<time datetime="2024-04-14T11:32:10.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2024-04-14</time>
</a>
</header>
<div class="article-entry" itemprop="articleBody">
<h1 id="README-of-my-Unity-NavimeshMarker-plugin"><a href="#README-of-my-Unity-NavimeshMarker-plugin" class="headerlink" title="README of my Unity NavimeshMarker plugin"></a>README of my <a href="https://assetstore.unity.com/packages/tools/utilities/navimeshmarker-163744">Unity NavimeshMarker plugin</a></h1><p><span id="1"></span></p>
<h1 id="1-How-it-works"><a href="#1-How-it-works" class="headerlink" title="1. How it works"></a><a href="#1">1. How it works</a></h1><p>It is useful to bake meshes into a NavMesh file in Unity. As the project turns large, the meshes can be too complex to match a navigation area. And the longer I work with it, the stronger feelings I have would be if </p>
<ul>
<li>I can edit the navmesh freely within Unity. I have to select/un-select the existing mesh made by the artist, which is easy to make mistakes. If I want to mark certain areas, my jobs would probably depend on the artist. </li>
<li>I have the open format runtime data so that I don’t have to re-bake it for my server. The re-baking workflow makes the server data different from the client’s, which brings in bugs.</li>
</ul>
<p>So I make NavimeshMarker. NavimeshMarker is a Unity Editor tool that aims to</p>
<ul>
<li>be a navmesh editor within Unity. The user can easily generate and edit meshes sticking to the ground. With the meshes, it is easy to mark a certain area as a specific Navigation Area, which can have different navigation costs. Then a smarter navigation path will be generated at the runtime and leads to a better AI interaction with different terrains.</li>
<li>provide a bake-free runtime navmesh data with <a href="https://github.com/recastnavigation/recastnavigation">Open Detour format</a>.</li>
</ul>
<p>After the meshes are generated, you can follow the old workflow to bake it to the navmesh data in the Unity Navigation Panel. And the navmesh data, instead of the meshes, is utilized at runtime eventually. And the meshes won’t be saved with the scene. Instead, they are saved as files in a directory besides the scene file. And it needn’t be packed into the game, which will save the memory at runtime.</p>
<p>Or you can also export the navmesh without the baking workflow. The meshes intersection and the union calculation will be performed(The source code of this part is in secret.) during the exportation and the edge vertices are kept. Then the data will be saved in JSON format and transferred to the <a href="https://github.com/recastnavigation/recastnavigation">Detour format</a>. An example is provided to show how to use these data in Unity. And it can be used anywhere as it is written in C++. The exporting and the runtime source are available via <a href="https://github.com/noodle1983/recastnavigation">this link</a>(I only add new files and nothing changes are made to the original source, so it is easy to rebase.). In this workflow, you must pack the runtime data into the release package.</p>
<p>As an editor tool, an editor window is made to manage the meshes’ data. The features are as followed.</p>
<ul>
<li>Editor Only Data Storage</li>
<li>Edit A Markermesh<ul>
<li>Freely Creation.</li>
<li>Extension.</li>
<li>Edge Vertices Adjustment.</li>
<li>Connecting two edge points to form a new triangle.</li>
<li>Triangle Deletion.</li>
<li>Regular Poly Creation.</li>
</ul>
</li>
<li>Handle Multiple Markermeshes<ul>
<li>Merging</li>
<li>Splitting</li>
</ul>
</li>
<li>Detour Format Exportation.</li>
<li>Runtime Usage Example.</li>
</ul>
<p>The details come as follows.</p>
<p><span id="2"></span></p>
<h1 id="2-Editor-Only-Data-Storage"><a href="#2-Editor-Only-Data-Storage" class="headerlink" title="2. Editor Only Data Storage"></a><a href="#2">2. Editor Only Data Storage</a></h1><p>As described above, the meshes data is saved in the same directory as the Unity scene file(*.unity), with the same name as the scene name and the fixed postfix <strong>.markermesh</strong>. </p>
<figure>
<img src="https://cdn.jsdelivr.net/gh/noodle1983/private@7bc10bd387fc0b6cee9da28391e56049b6ad2192/NavimeshMaker/Screenshots/SavedDir.png" alt="The Saved Directory Example"/>
<br/><font size=2 face="黑体">Fig.1. The Saved Directory Example</font>
</figure>
<p>And the mesh files are named sequentially and stored in JSON format. For short, these mesh data are named <strong>markermesh</strong> below.</p>
<figure>
<img src="https://cdn.jsdelivr.net/gh/noodle1983/private@7bc10bd387fc0b6cee9da28391e56049b6ad2192/NavimeshMaker/Screenshots/SavedFiles.png" alt="The Saved Markermesh Data Example"/>
<br/><font size=2 face="黑体">Fig.2. The Saved Markermesh Data Example</font>
</figure>
<p>When a scene is loaded, nothing is changed to the scene in the hierarchy until you open the NavimeshMarker Editor Window via <strong>Window -> NavimeshMarkerWindow</strong>. Then, a gameobject named <strong>EditorOnly-WontSaved-MarkerMeshManager</strong> is added to the scene. The markermesh is loaded as sub-game objects with a MeshRenderer component attached to each of them, so the markermesh can be shown in the scene. The gameobject <strong>EditorOnly-WontSaved-MarkerMeshManager</strong> and the sub-objects are for editor only and won’t be saved in the scene. It means none of them will be included in the packed App. And it also means next time the scene is loaded in the editor, you have to open the NavimeshMarker Editor Window to show the markermesh in the Scene Window.</p>
<figure>
<img src="https://cdn.jsdelivr.net/gh/noodle1983/private@7bc10bd387fc0b6cee9da28391e56049b6ad2192/NavimeshMaker/Screenshots/window.png" alt="The Navimesh Marker Window"/>
<br/><font size=2 face="黑体">Fig.3. The Navimesh Marker Window</font>
</figure>
<p>In the NavimeshMarker Editor Window, there are 2 buttons named <strong>Reload From Files</strong> and <strong>Save To Files</strong>. Automatically, the data is loaded when the window is shown and is saved when you complete the editing jobs or the scene is saved. Still, these jobs can be done manually via the 2 buttons. With another button named <strong>Import From Mesh Object…</strong>, a normal mesh can be imported from the asset or the scene. </p>
<figure>
<img src="https://cdn.jsdelivr.net/gh/noodle1983/private@7bc10bd387fc0b6cee9da28391e56049b6ad2192/NavimeshMaker/Screenshots/FilesButton.png" alt="The Serialization Buttons"/>
<br/><font size=2 face="黑体">Fig.4. The Serialization Buttons</font>
</figure>
<p>The <strong>.markermesh</strong> files are in JSON format. And it is best to manage them with your revision control system(svn/git/…). Other than that, you don’t need to care about the storage too much, it has no impact on the scene data. </p>
<p><span id="3"></span></p>
<h1 id="3-Edit-A-Markermesh"><a href="#3-Edit-A-Markermesh" class="headerlink" title="3. Edit A Markermesh"></a><a href="#3">3. Edit A Markermesh</a></h1><p><span id="3.1"></span></p>
<h2 id="3-1-Freely-Creation"><a href="#3-1-Freely-Creation" class="headerlink" title="3.1. Freely Creation"></a><a href="#3.1">3.1. Freely Creation</a></h2><figure>
<img src="https://cdn.jsdelivr.net/gh/noodle1983/private@7bc10bd387fc0b6cee9da28391e56049b6ad2192/NavimeshMaker/Screenshots/CreateNewMarkermesh.png" alt="The Markermesh Creation Button"/>
<br/><font size=2 face="黑体">Fig.5. The Markermesh Creation Button</font>
</figure>
<p>By clicking <strong>the Markermesh Creation button</strong>, as shown above, you will start to mark a new area. The steps are as follows.</p>
<ul>
<li>Along the edge of the area, click on the scene view, a point is generated in the scene. </li>
<li>Click the next point on one side of the area’s edge, a new line is got.</li>
<li>Click the other side of the area’s edge, a new triangle is got.</li>
<li>Repeat the upper step.</li>
<li>Click the <strong>stop drawing button</strong> or type <strong>Esc</strong> key to stop and save.</li>
</ul>
<figure>
<img src="https://cdn.jsdelivr.net/gh/noodle1983/private@7bc10bd387fc0b6cee9da28391e56049b6ad2192/NavimeshMaker/Screenshots/StopDrawingButton.png" alt="The Stop Drawing Button"/>
<br/><font size=2 face="黑体">Fig.6. The Stop Drawing Button</font>
</figure>
<p>And some tips come as follows.</p>
<ul>
<li>The mesh needn’t be completed at one time. It can be extendable later as in <a href="#3.3">Chapter 3.3</a>.</li>
<li>The mesh needn’t be accurate this time. The edge vertices can be adjusted as in <a href="#3.4">Chapter 3.4</a>.</li>
<li>The mesh’s vertices do stick to the terrain. But still, the mesh can be partly unshown as the terrain is not plain and covers the mesh. </li>
<li>Since nothing is filtered in the ray test, so the clicked point can be on a tree. It can be fixed by adjusting the edge point in the following chapter.</li>
</ul>
<p>Here comes the video demonstration.</p>
<p>  <br><a href="http://www.youtube.com/watch?v=q2NvcNwFvEA" title="Creating A Mesh"><img src="http://img.youtube.com/vi/q2NvcNwFvEA/0.jpg" alt="To Create A Mesh"></a><br><br/>  <font size=2 face="黑体">Video 1. To Create A Mesh</font></p>
<p><span id="3.2"></span></p>
<h2 id="3-2-Regular-Poly-Creation"><a href="#3-2-Regular-Poly-Creation" class="headerlink" title="3.2. Regular Poly Creation"></a><a href="#3.2">3.2. Regular Poly Creation</a></h2><figure>
<img src="https://cdn.jsdelivr.net/gh/noodle1983/private@7bc10bd387fc0b6cee9da28391e56049b6ad2192/NavimeshMaker/Screenshots/RegPolyCreation.png" alt="The Regular Poly Creation"/>
<br/><font size=2 face="黑体">Fig.7. The Regular Poly Creation</font>
</figure>
<p>By clicking <strong>the Regular Poly Creation button</strong> as shown above, you will start to create a new regular poly marked as Unwalkable area.</p>
<figure>
<img src="https://cdn.jsdelivr.net/gh/noodle1983/private@7bc10bd387fc0b6cee9da28391e56049b6ad2192/NavimeshMaker/Screenshots/RegPolyCreationParam.png" alt="The Extension Button"/>
<br/><font size=2 face="黑体">Fig.8. The Regular Poly Creation Parameters</font>
</figure>
<p>Once you clicked the button, the parameters are shown. You can adjust the edges, the radius, and the rotation on the Y-axis.</p>
<p>Here comes the video demonstration.</p>
<p>  <br><a href="http://www.youtube.com/watch?v=xQd73ReBfTo" title="Creating A Regular Poly"><img src="http://img.youtube.com/vi/xQd73ReBfTo/0.jpg" alt="To Create A Regular Poly"></a><br><br/>  <font size=2 face="黑体">Video 2. To Create A Regular Poly</font></p>
<p><span id="3.3"></span></p>
<h2 id="3-3-Extension"><a href="#3-3-Extension" class="headerlink" title="3.3. Extension"></a><a href="#3.3">3.3. Extension</a></h2><figure>
<img src="https://cdn.jsdelivr.net/gh/noodle1983/private@7bc10bd387fc0b6cee9da28391e56049b6ad2192/NavimeshMaker/Screenshots/ExtendMesh.png" alt="The Extension Button"/>
<br/><font size=2 face="黑体">Fig.9. The Extension Button</font>
</figure>
<p>By clicking <strong>the Extension button</strong> as shown above, or type <strong>key c</strong>, you will start to extend an existing mesh. </p>
<ul>
<li>After the click, the nearest line to the mouse pointer will be highlighted. </li>
<li>Please select one with the left mouse button. </li>
<li>Then extend it to a triangle by moving the point. </li>
<li>And click the left button to confirm it.</li>
<li>Then move the mouse pointer as in <a href="#3.1">Chapter 3.1</a>.</li>
</ul>
<p>Here comes the video demonstration.</p>
<p>  <br><a href="http://www.youtube.com/watch?v=XzVKreyZNhs" title="Continue Drawing"><img src="http://img.youtube.com/vi/XzVKreyZNhs/0.jpg" alt="To Select An Edge Line To Continue"></a><br><br/>  <font size=2 face="黑体">Video 3. To Select An Edge Line To Continue</font></p>
<p><span id="3.4"></span></p>
<h2 id="3-4-Edge-Vertices-Adjustment"><a href="#3-4-Edge-Vertices-Adjustment" class="headerlink" title="3.4. Edge Vertices Adjustment"></a><a href="#3.4">3.4. Edge Vertices Adjustment</a></h2><figure>
<img src="https://cdn.jsdelivr.net/gh/noodle1983/private@7bc10bd387fc0b6cee9da28391e56049b6ad2192/NavimeshMaker/Screenshots/AdjustPoint.png" alt="The Edge Vertices Adjustment Button"/>
<br/><font size=2 face="黑体">Fig.10. The Edge Vertices Adjustment Button</font>
</figure>
<p>By clicking <strong>the Edge Vertices Adjustment button</strong> as shown above or type <strong>key v</strong>, you will start to adjust the edge vertices slightly. </p>
<ul>
<li>After the click, all the edge vertices are highlighted. </li>
<li>To move a vertice, please press the left mouse button pick one, then drag it and release it.</li>
<li>To add more edge vertices, please hold key CTRL and click on the edge line.</li>
</ul>
<p>Here comes the video demonstration.</p>
<p>  <br><a href="http://www.youtube.com/watch?v=lhzK9_jymi0" title="Adjusting Edge Vertices"><img src="http://img.youtube.com/vi/lhzK9_jymi0/0.jpg" alt="To Adjust Edge Vertices Picture"></a><br><br/>  <font size=2 face="黑体">Video 4. To Adjust Edge Vertices’ Position</font></p>
<p><span id="3.5"></span></p>
<h2 id="3-5-Connect-two-edge-points-to-form-a-new-triangle"><a href="#3-5-Connect-two-edge-points-to-form-a-new-triangle" class="headerlink" title="3.5. Connect two edge points to form a new triangle."></a><a href="#3.5">3.5. Connect two edge points to form a new triangle.</a></h2><figure>
<img src="https://cdn.jsdelivr.net/gh/noodle1983/private@7bc10bd387fc0b6cee9da28391e56049b6ad2192/NavimeshMaker/Screenshots/ConnectPoint.png" alt="The Connecting Edge Vertices Button"/>
<br/><font size=2 face="黑体">Fig.11. The Connecting Edge Vertices Button</font>
</figure>
<p>By clicking the <strong>Connecting Edge Vertices</strong> button as shown above, or type <strong>key z</strong>, you will start to connect two edge points of the highlighted mesh data. </p>
<ul>
<li>After the click, all the edge vertices are highlighted. </li>
<li>To make a new line, please select one vertice and then the other. The line is only kept in the memory(can’t be saved) until it forms a triangle with the existing lines.</li>
</ul>
<p>  <br><a href="http://www.youtube.com/watch?v=WtDO3sQOdjw" title="Connect Mesh Pieces"><img src="http://img.youtube.com/vi/WtDO3sQOdjw/0.jpg" alt="To Connect Mesh Pieces Picture Picture"></a><br><br/>  <font size=2 face="黑体">Video 5. To Connect Edge Vertices</font></p>
<p><span id="3.6"></span></p>
<h2 id="3-6-Triangle-Deletion"><a href="#3-6-Triangle-Deletion" class="headerlink" title="3.6. Triangle Deletion"></a><a href="#3.6">3.6. Triangle Deletion</a></h2><figure>
<img src="https://cdn.jsdelivr.net/gh/noodle1983/private@7bc10bd387fc0b6cee9da28391e56049b6ad2192/NavimeshMaker/Screenshots/DeleteTriangle.png" alt="The Triangle Deletion Button"/>
<br/><font size=2 face="黑体">Fig.12. The Triangle Deletion Button</font>
</figure>
<p>By clicking <strong>the Triangle Deletion button</strong> as shown above, or type <strong>key x</strong>, you will start to delete an existing triangle. </p>
<ul>
<li>As the mouse moves, the pointed triangle is highlighted. </li>
<li>Please type key d to delete it.</li>
</ul>
<p>  <br><a href="http://www.youtube.com/watch?v=vuIC3ZbkHqY" title="Delete Triangles"><img src="http://img.youtube.com/vi/vuIC3ZbkHqY/0.jpg" alt="To Delete Triangles Picture"></a><br><br/>  <font size=2 face="黑体">Video 6. To Delete Triangles</font></p>
<p><span id="4"></span></p>
<h1 id="4-Handle-Multiple-Markermeshes"><a href="#4-Handle-Multiple-Markermeshes" class="headerlink" title="4. Handle Multiple Markermeshes"></a><a href="#4">4. Handle Multiple Markermeshes</a></h1><p><span id="4.1"></span></p>
<h2 id="4-1-Merging"><a href="#4-1-Merging" class="headerlink" title="4.1. Merging"></a><a href="#4.1">4.1. Merging</a></h2><figure>
<img src="https://cdn.jsdelivr.net/gh/noodle1983/private@7bc10bd387fc0b6cee9da28391e56049b6ad2192/NavimeshMaker/Screenshots/MergeSplitMesh.png" alt="The Merging and Splitting Buttons"/>
<br/><font size=2 face="黑体">Fig.13. The Merging and Splitting Buttons</font>
</figure>
<p>By clicking the merge button, the markermesh will be merged to its upper/lower.<br>If the 2 markermeshes are not next to each other, please move them with the move button described in <a href="#4.2">Chapter 4.2</a>.</p>
<p><span id="4.2"></span></p>
<h2 id="4-2-Splitting"><a href="#4-2-Splitting" class="headerlink" title="4.2. Splitting"></a><a href="#4.2">4.2. Splitting</a></h2><figure>
<img src="https://cdn.jsdelivr.net/gh/noodle1983/private@7bc10bd387fc0b6cee9da28391e56049b6ad2192/NavimeshMaker/Screenshots/MoveMesh.png" alt="The Movement Buttons"/>
<br/><font size=2 face="黑体">Fig.14. The Movement Buttons</font>
</figure>
<p>By clicking the split mesh data button, the markermesh will be split into several not connecting pieces.<br>If all the vertices of the mesh triangles are connected, nothing will happen.</p>
<p><span id="4.3"></span></p>
<h2 id="4-3-video-demonstration"><a href="#4-3-video-demonstration" class="headerlink" title="4.3. video demonstration"></a><a href="#4.3">4.3. video demonstration</a></h2><p>  <br><a href="http://www.youtube.com/watch?v=bYXqzL_9Ymg" title="Merging and Split Mesh(es)"><img src="http://img.youtube.com/vi/bYXqzL_9Ymg/0.jpg" alt="To Merge and Split Meshes"></a><br><br/>  <font size=2 face="黑体">Video 7. To Merge and Split Meshes</font></p>
<p><span id="5"></span></p>
<h1 id="5-Detour-Format-Exportation"><a href="#5-Detour-Format-Exportation" class="headerlink" title="5. Detour Format Exportation."></a><a href="#5">5. Detour Format Exportation.</a></h1><figure>
<img src="https://cdn.jsdelivr.net/gh/noodle1983/private@7bc10bd387fc0b6cee9da28391e56049b6ad2192/NavimeshMaker/Screenshots/ExportBtn.png" alt="The Movement Buttons"/>
<br/><font size=2 face="黑体">Fig.15. The Movement Buttons</font>
</figure>
By clicking the Export Detour Mesh button, the markermesh gets clipped by its uppers(difference set) and merged.
<figure>
<img src="https://cdn.jsdelivr.net/gh/noodle1983/private@7bc10bd387fc0b6cee9da28391e56049b6ad2192/NavimeshMaker/Screenshots/ExportDetourMesh.png" alt="The Movement Buttons"/>
<br/><font size=2 face="黑体">Fig.16. The Movement Buttons</font>
</figure>
After the exportation is done, the final mesh is generated. You can click on its area to highlight it in the scene view. The file is in JSON format.
<p>Here comes the video demonstration.</p>
<p>  <br><a href="http://www.youtube.com/watch?v=x5vYUpBxwgQ" title="Export Detour Mesh"><img src="http://img.youtube.com/vi/x5vYUpBxwgQ/0.jpg" alt="To Export Detour Mesh"></a><br><br/>  <font size=2 face="黑体">Video 8. To Export Detour Mesh</font></p>
<p><span id="6"></span></p>
<h1 id="6-Runtime-Usage-Example"><a href="#6-Runtime-Usage-Example" class="headerlink" title="6. Runtime Usage Example"></a><a href="#6">6. Runtime Usage Example</a></h1><p>After the exportation is completed, the detour runtime file is generated from the final mesh too.<br>The examples will show how to use the detour format data.</p>
<p><span id="6.1"></span></p>
<h1 id="6-1-Runtime-File"><a href="#6-1-Runtime-File" class="headerlink" title="6.1. Runtime File"></a><a href="#6.1">6.1. Runtime File</a></h1><figure>
<img src="https://cdn.jsdelivr.net/gh/noodle1983/private@7bc10bd387fc0b6cee9da28391e56049b6ad2192/NavimeshMaker/Screenshots/RuntimeFile.png" alt="The Movement Buttons"/>
<br/><font size=2 face="黑体">Fig.17. The Movement Buttons</font>
</figure>
The runtime file is in Detour format. You need to pack it to the release version if you want to use this runtime data instead of the baked one.
<p><span id="6.2"></span></p>
<h1 id="6-2-Runtime-Query-Params"><a href="#6-2-Runtime-Query-Params" class="headerlink" title="6.2. Runtime Query Params"></a><a href="#6.2">6.2. Runtime Query Params</a></h1><figure>
<img src="https://cdn.jsdelivr.net/gh/noodle1983/private@7bc10bd387fc0b6cee9da28391e56049b6ad2192/NavimeshMaker/Screenshots/RuntimeParams.png" alt="The Movement Buttons"/>
<br/><font size=2 face="黑体">Fig.18. The Movement Buttons</font>
</figure>
<p>You can adjust the parameters as above during the test. Please refer to the <a href="https://github.com/recastnavigation/recastnavigation">Recast Doc</a> for the usages of the parameters.</p>
<p><span id="6.3"></span></p>
<h1 id="6-3-Runtime-Query-Example"><a href="#6-3-Runtime-Query-Example" class="headerlink" title="6.3. Runtime Query Example"></a><a href="#6.3">6.3. Runtime Query Example</a></h1><figure>
<img src="https://cdn.jsdelivr.net/gh/noodle1983/private@7bc10bd387fc0b6cee9da28391e56049b6ad2192/NavimeshMaker/Screenshots/RuntimeExample.png" alt="The Movement Buttons"/>
<br/><font size=2 face="黑体">Fig.19. The Movement Buttons</font>
</figure>
<p>There are 3 examples, which are <em>find path</em>, <em>find direct path</em>, and <em>raycast</em>. The <em>find path</em> will show the path from the start point to the endpoint. It may contain some mid-points which look not so straight. So the <em>find direct path</em> does more calculation basing on the <em>find path</em> to make it look more straight. Please refer to the video for how to set the start point and the endpoint.</p>
<p>  <br><a href="http://www.youtube.com/watch?v=2ScerXO8aI4" title="Runtime Query Example"><img src="http://img.youtube.com/vi/2ScerXO8aI4/0.jpg" alt="Runtime Query Example"></a><br><br/>  <font size=2 face="黑体">Video 9. Runtime Query Example</font></p>
<p><span id="7"></span></p>
<h1 id="7-Others"><a href="#7-Others" class="headerlink" title="7. Others"></a><a href="#7">7. Others</a></h1><ul>
<li>The navigation cost of each area needs to be set at runtime.<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">//For the bake workflow</span><br><span class="line"> NavMesh.SetAreaCost((int)NavMeshType.Road, 1);</span><br><span class="line"> NavMesh.SetAreaCost((int)NavMeshType.Walkable, 10);</span><br></pre></td></tr></table></figure></li>
</ul>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">auto navmesh = NativeNavmeshWrapper.NewInstance(detourBinPath);</span><br><span class="line">...</span><br><span class="line"></span><br><span class="line">//For the bake-free workflow </span><br><span class="line"> navmesh.SetAreaCost((int)NavMeshType.Road, 1);</span><br><span class="line"> navmesh.SetAreaCost((int)NavMeshType.Walkable, 10);</span><br></pre></td></tr></table></figure>
<p><span id="8"></span></p>
<h1 id="8-Support-Contact-When-you-think-of-it-is-better-if…-Consultation"><a href="#8-Support-Contact-When-you-think-of-it-is-better-if…-Consultation" class="headerlink" title="8. Support & Contact & When you think of it is better if… & Consultation"></a><a href="#8">8. Support & Contact & When you think of it is better if… & Consultation</a></h1><ul>
<li>email: <a href="mailto:noodle1983@126.com">noodle1983@126.com</a></li>
</ul>
</div>
<div class="article-info article-info-index">
<div class="article-tag tagcloud">
<i class="icon-price-tags icon"></i>
<ul class="article-tag-list">
<li class="article-tag-list-item">
<a href="javascript:void(0)" class="js-tag article-tag-list-link color1">Unity Plugin-nd</a>
</li>
</ul>
</div>
<p class="article-more-link">
<a class="article-more-a" href="/2024/04/14/NavmeshMarker_README/">展开全文 >></a>
</p>
<div class="clearfix"></div>
</div>
</div>
</article>
<aside class="wrap-side-operation">
<div class="mod-side-operation">
<div class="jump-container" id="js-jump-container" style="display:none;">
<a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
<i class="icon-font icon-back"></i>
</a>
<div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
<i class="icon-font icon-plane jump-plane"></i>
</div>
</div>
</div>
</aside>
<article id="post-走进Unity WebGL技术方案" class="article article-type-post article-index" itemscope itemprop="blogPost">
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2024/04/09/%E8%B5%B0%E8%BF%9BUnity%20WebGL%E6%8A%80%E6%9C%AF%E6%96%B9%E6%A1%88/">走进Unity WebGL技术方案</a>
</h1>
<a href="/2024/04/09/%E8%B5%B0%E8%BF%9BUnity%20WebGL%E6%8A%80%E6%9C%AF%E6%96%B9%E6%A1%88/" class="archive-article-date">
<time datetime="2024-04-09T11:32:10.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2024-04-09</time>
</a>
</header>
<div class="article-entry" itemprop="articleBody">
<h1 id="1-概述"><a href="#1-概述" class="headerlink" title="1. 概述"></a>1. 概述</h1><p>小游戏刚出来时,因为跑不起太炫的效果,常被人认为low。但是如果我们知道每个用户的成本,就会发现小游戏可以以更低成本把游戏推向我们的用户,低得让人没法拒绝。随着手机硬件性能的提升和Unity/微信这等行业巨头做的努力,小游戏优化后运行3D的MMO也没什么问题。</p>
<p>另一方面,很多的互联网产品都面临着流量变现的压力,比如有道云笔记免费用户突然只能支持两个终端,虽然影响不大,但我能感受到其中的无奈。</p>
<p>我的想法是,何不接入小游戏试试?正是看到这里大有可为,借着项目接入支付宝小游戏的机会,我对Unity的WebGL技术方案的接入方式做了研究,基本能跑进游戏,对APP怎么接入Unity WebGL也有了基本了解。这里做个总结,希望优秀的APP能活得更好,优秀的游戏渗透得更快。</p>
<p>此文均为我一家之言,个人理解难免有误,欢迎斧正。</p>
<h1 id="2-Unity-WebGL技术方案"><a href="#2-Unity-WebGL技术方案" class="headerlink" title="2. Unity WebGL技术方案"></a>2. Unity WebGL技术方案</h1><h2 id="2-1-Unity小游戏的两种技术方案"><a href="#2-1-Unity小游戏的两种技术方案" class="headerlink" title="2.1. Unity小游戏的两种技术方案"></a>2.1. Unity小游戏的两种技术方案</h2><ul>
<li>Native Instant Game</li>
<li>WebGL</li>
</ul>
<p>具体介绍请参考Unity官方引擎底层架构技术主管赵亮在2023年中Unity开放日上的分享:<a href="https://new.qq.com/rain/a/20230612A0AUZE00">Unity小游戏开发简介</a>(<-没找到官方的链接,这个是腾讯网的)。分享讲到了Unity小游戏的现状,两个方案的优缺点,优化方法和未来的技术发展方向,高屋建瓴,值得一读。</p>
<p>本文仅涉及WebGL方案,文章里面提到的未来的WebGPU也是基于Web标准规范的,可以认为是WebGL方案的延续,其中的变化仅限于Canvas的Context和相关接口。</p>
<h2 id="2-2-WebGL方案的限制"><a href="#2-2-WebGL方案的限制" class="headerlink" title="2.2. WebGL方案的限制"></a>2.2. WebGL方案的限制</h2><p>WebGL方案的限制,或者说是浏览器的js执行环境的限制,首先,他是单线程的(先不讨论Web worker,单线程模式在可预见的未来都是要支持的),意味着Unity的执行不能独占执行的上下文,执行完得释放,也不能自发地触发执行。他就像一个黑盒子,任何的操作都只能由js触发,包括帧循环,定时器,事件处理。如果我们暂停js的处理,Unity没有任何代码在执行。</p>
<p>由于安全的原因,js执行环境没有能力访问本地的文件系统,只能从远端下载,只是如果浏览器发现有Cache且有效,会直接返回。这是很大的限制,游戏内目光所及,均是从文件加载(有纯shader实现的动画,比如<a href="https://noodle1983.github.io/mouton-webgl/index.html">这个</a>,shader的初始化会卡1分钟,这样做不了游戏)。</p>
<p>所以可以想象,Unity在移植整套方案到WebGL的时候做了大量的工作,同时也暴露了很多实现到JS层,只要编译输出Debug版本,就能得到一份可读的JS中间层代码,给APP接入小游戏带来便利。以此为基础,我们可以分析App小游戏的接入,需要哪些组件,也可以自行分析出错的原因是什么。</p>
<h2 id="2-3-Unity-WebGL方案的运行结构"><a href="#2-3-Unity-WebGL方案的运行结构" class="headerlink" title="2.3. Unity WebGL方案的运行结构"></a>2.3. Unity WebGL方案的运行结构</h2><pre class="mermaid">block-beta
columns 1
U["Unity Engine and upper"]
block:Mandatory
Canvas["webgl Context Canvas"]
VirtualFileSystem
http["HTTP Event System"]
WebAudio
end</pre>
<p>上图是我个人理解,VirtualFileSystem提供的各种资源(assets),HTTP的事件系统提供玩家操作输入,经过Unity引擎和上层逻辑的处理,输出渲染结果到webgl的Canvas和播放音频。</p>
<h2 id="2-4-方案必要的组件"><a href="#2-4-方案必要的组件" class="headerlink" title="2.4. 方案必要的组件"></a>2.4. 方案必要的组件</h2><ul>
<li>webgl Context Canvas,渲染输出</li>
<li>WebAssembly,加载c/c++库,包括Unity引擎,il2cpp后的c#逻辑,游戏项目的代码库</li>
<li>VirtualFileSystem,主要是MEMFS</li>
<li>HTTP fetch,从远端下载资源</li>
<li>HTTP DOM API,查找元素,事件注册,加载scripts</li>
</ul>
<h2 id="2-5-方案可选组件"><a href="#2-5-方案可选组件" class="headerlink" title="2.5. 方案可选组件"></a>2.5. 方案可选组件</h2><ul>
<li>WebSocket,连服务器,单机不用不影响</li>
<li>IndexedDb,默认缺失相关组件会报错,可以通过初始配置关闭,关闭后相关文件(主要是PlayerPrefs)没有持久化(但不影响HTTP Cache,即通过上面HTTP fetch拿到的资源)。</li>
<li>WebAudio,默认缺失相关组件会报错,可以在Unity编辑器里关闭。</li>
</ul>
<h1 id="3-深入JS源代码"><a href="#3-深入JS源代码" class="headerlink" title="3. 深入JS源代码"></a>3. 深入JS源代码</h1><p>下面会基于一个空工程的debug版js代码,回答下面几个问题</p>
<ul>
<li>各个部分如何初始化</li>
<li>怎么启动帧循环</li>
<li>事件处理如何触发</li>
</ul>
<p>示例代码由Unity2022.3.xx导出,部分输出文件名随输出目录改变,示例代码的输出目录为webgl。</p>
<h2 id="3-1-Unity-Webgl平台输出文件"><a href="#3-1-Unity-Webgl平台输出文件" class="headerlink" title="3.1. Unity Webgl平台输出文件"></a>3.1. Unity Webgl平台输出文件</h2><p>部分文件名随输出目录改变,本文以webgl为例。</p>
<table>
<thead>
<tr>
<th>文件</th>
<th>用途</th>
</tr>
</thead>
<tbody><tr>
<td>index.html</td>
<td>包含Canvas定义,渲染输出;脚本网页入口(相当于main函数入口)</td>
</tr>
<tr>
<td>TemplateData/</td>
<td>网页模板资源,游戏主要是Canvas,网页上的资源没有也不影响,不重要</td>
</tr>
<tr>
<td>Build/webgl.loader.js</td>
<td>加载framework.js, webgl.data,</td>
</tr>
<tr>
<td>Build/webgl.framework.js</td>
<td>加载/桥接wasm,有一js内存文件系统,事件转发,音频</td>
</tr>
<tr>
<td>Build/webgl.data</td>
<td>打包Bundle外的数据文件,应该都在这里了</td>
</tr>
<tr>
<td>Build/webgl.wasm</td>
<td>引擎+C#逻辑, 优化参考<a href="https://github.com/wechat-miniprogram/minigame-unity-webgl-transform">微信小游戏和相关文档</a>,太专业了,无出其右,对着做就行</td>
</tr>
</tbody></table>
<h4 id="webgl-data"><a href="#webgl-data" class="headerlink" title="webgl.data"></a>webgl.data</h4><p>空的工程包含这些文件,就是Unity打包出来的全局的数据。之前折腾过Android的热更,知道这是对应asset/bin/Data下的子集,webgl版本没这方面的需求,没太关注这些文件。</p>
<ul>
<li>data.unity3d</li>
<li>RuntimeInitializeOnLoads.json</li>
<li>ScriptingAssemblies.json</li>
<li>boot.config</li>
<li>Il2CppData/Metadata/global-metadata.dat</li>
<li>Resources/unity_default_resources</li>
</ul>
<h2 id="3-2-启动流程"><a href="#3-2-启动流程" class="headerlink" title="3.2. 启动流程"></a>3.2. 启动流程</h2><p>下面会按启动流程,过一遍JS的源代码。帧循环的启动,操作响应事件的注册都在这里面,本章节开始的几个问题,就不另外再说了。<br>小游戏页面的加载从index.html开始,以拿到unityInstance结束,之后unity的帧循环开启。</p>
<h3 id="3-2-1-index-html"><a href="#3-2-1-index-html" class="headerlink" title="3.2.1. index.html"></a>3.2.1. index.html</h3><p>index.html很简单,分为两部分:</p>
<ul>
<li>html元素,定义html页面元素,套模板(TemplateData/)。</li>
<li>另一部分是js脚本,完成系统的初始化。</li>
</ul>
<p>把啰嗦的部分去掉,html部分必须的只有canvas,游戏渲染输出必备,至少支持webgl Context(其他两种是2d和webgl2,2d不行,webgl2更新一些,后续还有webgpu,技术的更新还是很快的)。</p>
<p>js部分也可以分成两部分,代码不多,这里直接贴代码:</p>
<ul>
<li><p>配置</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">var buildUrl = "Build";</span><br><span class="line">var loaderUrl = buildUrl + "/webgl.loader.js";</span><br><span class="line">var config = {</span><br><span class="line"> dataUrl: buildUrl + "/webgl.data",</span><br><span class="line"> frameworkUrl: buildUrl + "/webgl.framework.js",</span><br><span class="line"> codeUrl: buildUrl + "/webgl.wasm",</span><br><span class="line"> streamingAssetsUrl: "StreamingAssets",</span><br><span class="line"> companyName: "DefaultCompany",</span><br><span class="line"> productName: "DefaultProduct",</span><br><span class="line"> productVersion: "0.1",</span><br><span class="line"> showBanner: unityShowBanner,</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<p>默认的配置看上去只是些很常规的配置,不能做太多的东西。其实这里面大有乾坤,配置的属性和值会被一项一项地复制到unityInstance的底层framework,能够开关功能甚至覆盖实现逻辑,留意相关的注释,比如</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">// To lower canvas resolution on mobile devices to gain some</span><br><span class="line">// performance, uncomment the following line:</span><br><span class="line">// config.devicePixelRatio = 1;</span><br></pre></td></tr></table></figure>
</li>
<li><p>加载启动脚本, 构造出unityInstance就ok了。这里的unityInstance是js和Unity c#逻辑层交互的关键,一般一个页面就一个,全局存一下。如果有多个(同个页面同时操控多个游戏实例,实在想不出应用的场景),可以拿key在全局变量存一下。Unity的加载脚本都是createElement的方式加载到全局作用域,import的方式得自己改改。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">var script = document.createElement("script");</span><br><span class="line">script.src = loaderUrl;</span><br><span class="line">script.onload = () => {</span><br><span class="line"> createUnityInstance(canvas, config, (progress) => {</span><br><span class="line"> progressBarFull.style.width = 100 * progress + "%";</span><br><span class="line"> }).then((unityInstance) => {</span><br><span class="line"> loadingBar.style.display = "none";</span><br><span class="line"> fullscreenButton.onclick = () => {</span><br><span class="line"> unityInstance.SetFullscreen(1);</span><br><span class="line"> };</span><br><span class="line"> }).catch((message) => {</span><br><span class="line"> alert(message);</span><br><span class="line"> });</span><br><span class="line"> };</span><br><span class="line">document.body.appendChild(script);</span><br></pre></td></tr></table></figure></li>
</ul>
<h3 id="3-2-2-加载webgl-loader-js"><a href="#3-2-2-加载webgl-loader-js" class="headerlink" title="3.2.2. 加载webgl.loader.js"></a>3.2.2. 加载webgl.loader.js</h3><p>loader.js全局定义了一个大方法:createUnityInstance,其他的林林总总都在这里面,index.html传入canvas和config,异步拿到unityInstance。文件前面定义大量函数和逻辑,入口在最后面。</p>
<p>前面的函数和逻辑有几个重要的变量:</p>
<ul>
<li><p>Module, 后面的framework会以此变量去做初始化,初始化好后,这个变量就是framework。</p>
</li>
<li><p>config的转移,此前的Module定义的方法,可以在这里直接override,此后Unity也预留了一些判断,可以改变一些行为。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">for (var parameter in config)</span><br><span class="line"> Module[parameter] = config[parameter];</span><br></pre></td></tr></table></figure></li>
<li><p>unityInstance本身,常用的主要是给C#发消息的SendMessage,Unity的iOS/Android等各个平台都有,用过的看名字就知道。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">var unityInstance = {</span><br><span class="line"> Module: Module,</span><br><span class="line"> SetFullscreen: ...</span><br><span class="line"> SendMessage: ...</span><br><span class="line"> Quit: ...</span><br><span class="line"> GetMemoryInfo: ...</span><br><span class="line">};</span><br></pre></td></tr></table></figure></li>
<li><p>Module.SystemInfo,系统信息。</p>
</li>
</ul>
<p>最后loadBuild开始加载framework和data,进度从0开始,成功后以1结束</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">return new Promise(function (resolve, reject) {</span><br><span class="line"> if (!Module.SystemInfo.hasWebGL) {</span><br><span class="line"> reject("Your browser does not support WebGL.");</span><br><span class="line"> } else if (!Module.SystemInfo.hasWasm) {</span><br><span class="line"> reject("Your browser does not support WebAssembly.");</span><br><span class="line"> } else {</span><br><span class="line"> Module.startupErrorHandler = reject;</span><br><span class="line"> onProgress(0);</span><br><span class="line"> Module.postRun.push(function () {</span><br><span class="line"> onProgress(1);</span><br><span class="line"> delete Module.startupErrorHandler;</span><br><span class="line"> resolve(unityInstance);</span><br><span class="line"> });</span><br><span class="line"> loadBuild();</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<p>按图索骥,最后的loadBuild定义如下:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">function loadBuild() {</span><br><span class="line"> downloadFramework().then(function (unityFramework) {</span><br><span class="line"> unityFramework(Module);</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> var dataPromise = downloadBinary("dataUrl");</span><br><span class="line"> Module.preRun.push(function () {</span><br><span class="line"> Module.addRunDependency("dataUrl");</span><br><span class="line"> dataPromise.then(function (data) {</span><br><span class="line"> var view = new DataView(data.buffer, data.byteOffset, data.byteLength);</span><br><span class="line"> ...</span><br><span class="line"> Module.removeRunDependency("dataUrl");</span><br><span class="line"> });</span><br><span class="line"> });</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>上半部分异步加载webgl.framework.js,拿到初始化函数,传入Module初始化。</p>
<p>后半部分preRun加载webgl.data,解析,构造内存文件。为什么要放preRun里面呢,因为前面的framework下载和初始化是异步的,内存文件系统是还没初始化好。</p>
<h3 id="3-2-3-加载webgl-framework-js"><a href="#3-2-3-加载webgl-framework-js" class="headerlink" title="3.2.3. 加载webgl.framework.js"></a>3.2.3. 加载webgl.framework.js</h3><p>webgl.framework.js代码量很大,除了各种辅助类的接口外,大体有下面几大块:</p>
<h4 id="音频"><a href="#音频" class="headerlink" title="音频"></a>音频</h4><p><a href="https://www.w3.org/TR/webaudio/">web的音频定义</a>很复杂,在我调试的这段时间(2023年),还碰到Safari内存泄漏的问题,虽然后续版本有修复,但总体来说各个移动平台的web音频缓存对游戏来说,不太适应,表现为占用内存偏大,缓存释放不及时。</p>
<p>游戏中的音频虽然播放量大,杂,既有长的背景音,也有短的各种音效,但是播放的需求很简单,就是循环播放背景音+单次播放音效实例,复杂一点再调调各自音量,再复杂一点限制同一实例的个数。所以接入各个app原生的audio接口不复杂(我曾经想找一个audio context的polyfill工程,接上小游戏的audio接口,后来发现太复杂,不如直接接),接入后在Unity的原生音频就没用了,可以在设置里把Unity的音频给关了,再把这块代码裁剪掉。</p>
<h4 id="文件系统"><a href="#文件系统" class="headerlink" title="文件系统"></a>文件系统</h4><p>文件系统应该是<a href="https://emscripten.org/docs/api_reference/Filesystem-API.html#">emcripten的实现</a>,非常轻巧,值得一读,原来内存文件系统可以这么做。</p>
<ul>
<li>MEMFS</li>
<li>IDBFS</li>
<li>FS</li>
<li>SOCKFS</li>
<li>DNS</li>
<li>Linux C file api</li>
</ul>
<p>其中IDBFS会被用于存放持久化文件,没有用到IDBFS的太多特性。以DB存文件不算完美,纯属没其他办法的办法。Unity在初始化时预留了个扩展(如下),可以在配置里定义一个unityFileSystemInit方法,依葫芦画瓢把文件存其他地方(取代IDBFS);或者就定义一个空方法,把PlayerPrefs数据存服务器。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">// Initialize the IndexedDB based file system. Module['unityFileSystemInit'] allows</span><br><span class="line">// developers to override this with their own function, when they want to do cloud storage</span><br><span class="line">// instead.</span><br><span class="line">var unityFileSystemInit = Module['unityFileSystemInit'] || function () {</span><br><span class="line"> FS.mkdir('/idbfs');</span><br><span class="line"> FS.mount(IDBFS, {}, '/idbfs');</span><br><span class="line"> ...</span><br><span class="line">};</span><br><span class="line">unityFileSystemInit();</span><br></pre></td></tr></table></figure>
<h4 id="GL"><a href="#GL" class="headerlink" title="GL"></a>GL</h4><p>封装Canvas webgl Context的操作。</p>
<h4 id="webgl-wasm初始化"><a href="#webgl-wasm初始化" class="headerlink" title="webgl.wasm初始化"></a>webgl.wasm初始化</h4><p>下面的代码太多,只能上图把关键的函数列出来:</p>
<pre class="mermaid">flowchart TD
createWasm --> instantiateAsync
instantiateAsync -->D1{!FileURL and 支持流式实例化?}
D1--> |Y| F1[fetch] --> WebAssembly.instantiateStreaming --> receiveInstantiationResult
D1--> |N| instantiateArrayBuffer --> getBinaryPromise --> D2{FileURL?}
D2--> |Y| readAsync --> getBinary
D2--> |N| F2[fetch] --> getBinary --> WebAssembly.instantiate --> receiveInstantiationResult
receiveInstantiationResult --> receiveInstance --> Init[Module.asm=...\n wasmMemory=...\n Module.asm.__wasm_call_ctors on init]</pre>
<p>初始化后得到asm,再用createExportWrapper将asm的符号导出到Module变量,之后看到的_xxx函数基本都是wasm里的xxx,如_main即wasm里的main函数,作为这些后,静待游戏启动。</p>
<h4 id="Browser及帧循环启动"><a href="#Browser及帧循环启动" class="headerlink" title="Browser及帧循环启动"></a>Browser及帧循环启动</h4><p>帧循环的相关数据结构为Browser.mainLoop,其启动比Unity内部帧循环早,帧号比Unity C#的帧号大。</p>
<pre class="mermaid">flowchart TD
Module.preInit --> D1{config.noInitialRun?} --> |N| run --> preRun --> doRun --> initRuntime --> preMain --> callMain --> wasm.main --> _emscripten_set_main_loop --> setMainLoop --> init[init Browser.mainLoop.runner] --> D2{fps>0}
D2 --> |Y| timeout[_emscripten_set_main_loop_timing EM_TIMING_SETTIMEOUT] --> Browser.mainLoop.scheduler
D2 --> |N| raf[_emscripten_set_main_loop_timing EM_TIMING_RAF] --> Browser.mainLoop.scheduler</pre>
<p>C#里的帧循环函数在main函数执行时传入,存在Browser.mainLoop.runner里。Browser.mainLoop.scheduler即启动下一帧的更新,如果定帧,则用浏览器的timeout函数触发下一帧更新;否则,则用浏览器的requestAnimationFrame触发下一帧更新。无论怎样,下一帧更新浏览器都会调用Browser.mainLoop.runner,在Browser.mainLoop.runner里,会调用wasm传入的main_loop方法,然后再Browser.mainLoop.scheduler,如此反复,帧循环就跑起来了。</p>
<h3 id="JSEvents事件处理"><a href="#JSEvents事件处理" class="headerlink" title="JSEvents事件处理"></a>JSEvents事件处理</h3><pre class="mermaid">flowchart TD
callMain --> wasm.main --> wasm.InputInit --> _emscripten_set_xxx_callback_on_thread --> registerXXXEventCallback --> JSEvents.registerOrRemoveHandler --> event.target.addEventListener</pre>
<p>事件处理也是在main初始化时,将相关处理函数注册到js层,通过JSEvents统一向浏览器注册事件,各层均有回调,事件发生时,顺着回调函数,调回C#。调回c#时,会构造buffer作为事件参数传入。</p>
<h1 id="4-APP小游戏的接入"><a href="#4-APP小游戏的接入" class="headerlink" title="4. APP小游戏的接入"></a>4. APP小游戏的接入</h1><p>小游戏的接入,先接入一遍<a href="https://github.com/wechat-miniprogram/minigame-unity-webgl-transform">微信小游戏</a>,虽然微信重写了一遍JS部分代码,但是仍能站在巨人肩膀上,少走很多弯路。</p>
<p>参考Unity必选和可选组件,也感谢开源社区,Github上有大量的替代组件,可行性的问题解决后,后其他的问题应该都是工作量的问题。</p>
<ul>
<li>webgl Context Canvas,需要</li>
<li>WebAssembly,需要。</li>
<li>VirtualFileSystem,有内存就行。</li>
<li>HTTP fetch,需要。</li>
<li>WebSocket,连接服务器需要,如果仅是跑进游戏,是不需要的。</li>
<li>IndexedDb,用本地文件系统接入,效率更高。</li>
<li>WebAudio,用app自己的音频系统接入,效率更高。</li>
</ul>
<h1 id="5-最后"><a href="#5-最后" class="headerlink" title="5. 最后"></a>5. 最后</h1><p>全部写完,其实也只写了个大概,希望能把脉络给展现出来,方便读者深入进去。</p>
</div>
<div class="article-info article-info-index">
<div class="article-tag tagcloud">
<i class="icon-price-tags icon"></i>
<ul class="article-tag-list">
<li class="article-tag-list-item">
<a href="javascript:void(0)" class="js-tag article-tag-list-link color1">Unity WebGL 小游戏</a>
</li>
</ul>
</div>
<p class="article-more-link">
<a class="article-more-a" href="/2024/04/09/%E8%B5%B0%E8%BF%9BUnity%20WebGL%E6%8A%80%E6%9C%AF%E6%96%B9%E6%A1%88/">展开全文 >></a>
</p>
<div class="clearfix"></div>
</div>
</div>
</article>
<aside class="wrap-side-operation">
<div class="mod-side-operation">
<div class="jump-container" id="js-jump-container" style="display:none;">
<a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
<i class="icon-font icon-back"></i>
</a>
<div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
<i class="icon-font icon-plane jump-plane"></i>
</div>
</div>
</div>
</aside>
<article id="post-putty-nd7.9" class="article article-type-post article-index" itemscope itemprop="blogPost">
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2021/03/22/putty-nd7.9/">putty-nd7.9 release</a>
</h1>
<a href="/2021/03/22/putty-nd7.9/" class="archive-article-date">
<time datetime="2021-03-22T01:58:53.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2021-03-22</time>
</a>
</header>
<div class="article-entry" itemprop="articleBody">
<h3 id="1-Bugs"><a href="#1-Bugs" class="headerlink" title="1. Bugs"></a>1. Bugs</h3><ul>
<li>crash when session is unreachable.</li>
</ul>
<h3 id="2-Other-changes"><a href="#2-Other-changes" class="headerlink" title="2. Other changes"></a>2. Other changes</h3><h3 id="3-Ongoing"><a href="#3-Ongoing" class="headerlink" title="3. Ongoing"></a>3. Ongoing</h3><h3 id="4-Downloads"><a href="#4-Downloads" class="headerlink" title="4. Downloads"></a>4. Downloads</h3><p><a href="https://sourceforge.net/projects/putty-nd/files/old/putty_nd7.9.zip/download">Sourceforge</a><br><a href="https://cdn.jsdelivr.net/gh/noodle1983/private@0891f467c12e169adca5d2e580d379dba524d307/putty-nd/7.9/putty_nd7.9.zip">Github</a></p>
</div>
<div class="article-info article-info-index">
<div class="article-tag tagcloud">
<i class="icon-price-tags icon"></i>
<ul class="article-tag-list">
<li class="article-tag-list-item">
<a href="javascript:void(0)" class="js-tag article-tag-list-link color4">putty-nd</a>
</li>
</ul>
</div>
<p class="article-more-link">
<a class="article-more-a" href="/2021/03/22/putty-nd7.9/">展开全文 >></a>
</p>
<div class="clearfix"></div>
</div>
</div>
</article>
<aside class="wrap-side-operation">
<div class="mod-side-operation">
<div class="jump-container" id="js-jump-container" style="display:none;">
<a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
<i class="icon-font icon-back"></i>
</a>
<div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
<i class="icon-font icon-plane jump-plane"></i>
</div>
</div>
</div>
</aside>
<article id="post-putty-nd7.8" class="article article-type-post article-index" itemscope itemprop="blogPost">
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2021/03/19/putty-nd7.8/">putty-nd7.8 release</a>
</h1>
<a href="/2021/03/19/putty-nd7.8/" class="archive-article-date">
<time datetime="2021-03-19T07:58:53.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2021-03-19</time>
</a>
</header>
<div class="article-entry" itemprop="articleBody">
<h3 id="1-Bugs"><a href="#1-Bugs" class="headerlink" title="1. Bugs"></a>1. Bugs</h3><ul>
<li>crash when opening session group.</li>
</ul>
<h3 id="2-Other-changes"><a href="#2-Other-changes" class="headerlink" title="2. Other changes"></a>2. Other changes</h3><ul>
<li>integrate with <a href="https://github.com/unxed/putty4far2l">putty4far2l</a>. Please turn on the options in [Global Setting->Global Far2l Support] as it is disabled by default.</li>
<li>pipe to local EXE with pseudo-terminal for Pipe protocol(named adb originally).<br> Known issue: <ul>
<li>copied wrapped line and pasted as multi-lines. Because the cmd.exe and the powershell.exe sends ‘\r\n’ in the wrapped line.</li>
<li>the powershell may switch to a second line during inputting.</li>
</ul>
</li>
<li>In file explorer, “putty-nd -c/p” will open a pipe session to cmd.exe/powershell.exe with current directory set. Related session: Preset/cmd, Preset/powershell.<br><img src="https://cdn.jsdelivr.net/gh/noodle1983/private@861e61a7547293c3d75bccb859f65701247a746f/putty-nd/7.8/explorer-sc-cmd.gif" alt="cmd in explorer"></li>
<li>In file explorer, “putty-nd -sc/sp” will open a new administrator console to cmd.exe/powershell.exe with current directory set. Related session: Preset/sudo_cmd, Preset/sudo_powershell.<br><img src="https://cdn.jsdelivr.net/gh/noodle1983/private@861e61a7547293c3d75bccb859f65701247a746f/putty-nd/7.8/explorer-sc-sudo-cmd.gif" alt="sudo cmd in explorer"></li>
</ul>
<h3 id="3-Ongoing"><a href="#3-Ongoing" class="headerlink" title="3. Ongoing"></a>3. Ongoing</h3><h3 id="4-Downloads"><a href="#4-Downloads" class="headerlink" title="4. Downloads"></a>4. Downloads</h3><p><a href="https://sourceforge.net/projects/putty-nd/files/old/putty_nd7.8.zip/download">Sourceforge</a><br><a href="https://cdn.jsdelivr.net/gh/noodle1983/private@04a4d88c74ea584ad1c20e13447cb6a697151a54/putty-nd/7.8/putty_nd7.8.zip">Github</a></p>
</div>
<div class="article-info article-info-index">
<div class="article-tag tagcloud">
<i class="icon-price-tags icon"></i>
<ul class="article-tag-list">
<li class="article-tag-list-item">
<a href="javascript:void(0)" class="js-tag article-tag-list-link color4">putty-nd</a>
</li>
</ul>
</div>
<p class="article-more-link">
<a class="article-more-a" href="/2021/03/19/putty-nd7.8/">展开全文 >></a>
</p>
<div class="clearfix"></div>
</div>
</div>
</article>
<aside class="wrap-side-operation">
<div class="mod-side-operation">
<div class="jump-container" id="js-jump-container" style="display:none;">
<a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
<i class="icon-font icon-back"></i>
</a>
<div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
<i class="icon-font icon-plane jump-plane"></i>
</div>
</div>
</div>
</aside>
<article id="post-putty-nd7.7" class="article article-type-post article-index" itemscope itemprop="blogPost">
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2021/02/12/putty-nd7.7/">putty-nd7.7 release</a>
</h1>
<a href="/2021/02/12/putty-nd7.7/" class="archive-article-date">
<time datetime="2021-02-12T01:58:53.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2021-02-12</time>
</a>
</header>
<div class="article-entry" itemprop="articleBody">
<h3 id="1-Bugs"><a href="#1-Bugs" class="headerlink" title="1. Bugs"></a>1. Bugs</h3><h3 id="2-Other-changes"><a href="#2-Other-changes" class="headerlink" title="2. Other changes"></a>2. Other changes</h3><pre><code>1. support my extended rzsz-nd().
- sz/rz dir.
- support large file(>4GB)
- check existing file's CRC and then resume/skip/retransmit if previous transfer is broke/completed/invalid.
- options to update client/server working direction
</code></pre>
<h3 id="3-Ongoing"><a href="#3-Ongoing" class="headerlink" title="3. Ongoing"></a>3. Ongoing</h3><h3 id="4-Downloads"><a href="#4-Downloads" class="headerlink" title="4. Downloads"></a>4. Downloads</h3><p><a href="https://sourceforge.net/projects/putty-nd/files/old/putty_nd7.7.zip/download">Sourceforge</a><br><a href="https://noodle1983.github.io/putty-nd/putty_nd7.7.zip">Github</a></p>
</div>
<div class="article-info article-info-index">