-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrss.xml
965 lines (867 loc) · 159 KB
/
rss.xml
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
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>funcman's blog</title><link href="https://funcman.me/" rel="alternate"></link><link href="https://funcman.me/rss.xml" rel="self"></link><id>https://funcman.me/</id><updated>2023-01-16T11:24:07+08:00</updated><entry><title>收集了一些前期的图像视频芯片的信息</title><link href="https://funcman.me/about_some_oldschool_gpus.html" rel="alternate"></link><published>2023-01-16T11:24:07+08:00</published><updated>2023-01-16T11:24:07+08:00</updated><author><name>funcman</name></author><id>tag:funcman.me,2023-01-16:/about_some_oldschool_gpus.html</id><summary type="html"><p>现代的计算机系统,无论是台式电脑、笔记本、手机、游戏机,基本上都有专门的图形芯片,用于图像的输出。今天,我们一般将这 …</p></summary><content type="html"><p>现代的计算机系统,无论是台式电脑、笔记本、手机、游戏机,基本上都有专门的图形芯片,用于图像的输出。今天,我们一般将这种图形芯片,称为GPU(Graphic Processing Unit),即图形处理单元。</p>
<p>除了一些作为控制器使用MCU系统,一般的通用型计算机系统,都包括了CPU。在PC上,GPU在显卡上存在。而手机等系统,GPU则作为SoC(System on a Chip)的一部分,在主芯片上存在。</p>
<p>在计算机的发展早期,是没有专门的芯片用于图像输出的,早期的计算机系统甚至没有图像输出。后来计算机越来越为个人所使用,图像输出变得重要起来。于是就有了专门的芯片。</p>
<p>本篇记录和罗列一些我收集到的前期图像视频芯片的信息,后面会做陆续更新。</p>
<p>早期图像视频芯片是没有统一的名称的,所以需要罗列一下:</p>
<table>
<thead>
<tr>
<th>缩写</th>
<th>全称</th>
<th>备注</th>
</tr>
</thead>
<tbody>
<tr>
<td>CRTC</td>
<td>Cathode Ray Tube (CRT) Controller</td>
<td></td>
</tr>
<tr>
<td>GDC</td>
<td>Graph Display Controller</td>
<td></td>
</tr>
<tr>
<td>GSP</td>
<td>Graphics System Processor</td>
<td>TMS34010</td>
</tr>
<tr>
<td>GDP</td>
<td>Graphics Display Processor</td>
<td></td>
</tr>
<tr>
<td>PPU</td>
<td>Picture Processing Unit</td>
<td>任天堂<a href="https://en.wikipedia.org/wiki/Nintendo_Entertainment_System" title="任天堂红白机">红白机</a>、<a href="https://en.wikipedia.org/wiki/Super_Nintendo_Entertainment_System" title="超级任天堂">超任</a></td>
</tr>
<tr>
<td>VDC</td>
<td>Video Display Controller</td>
<td></td>
</tr>
<tr>
<td>VDG</td>
<td>Video Display Generator</td>
<td><code>Generator</code>这个词和<code>Controller</code>差不多的意思</td>
</tr>
<tr>
<td>VDP</td>
<td>Video Display Processor</td>
<td>世嘉<a href="https://en.wikipedia.org/wiki/Master_System" title="世嘉Mark3">Mark3</a>、<a href="https://en.wikipedia.org/wiki/Sega_Genesis" title="世嘉MD">MD</a>,<a href="https://en.wikipedia.org/wiki/MSX" title="MSX电脑">MSX</a>电脑</td>
</tr>
<tr>
<td>VIA</td>
<td>Television Interface Adaptor</td>
<td>雅达利2600</td>
</tr>
<tr>
<td>VIC</td>
<td>Video Interface Chip</td>
<td>MOS公司</td>
</tr>
</tbody>
</table>
<p>具体的图像视频芯片做一下罗列:</p>
<table>
<thead>
<tr>
<th>型号</th>
<th>厂商</th>
<th>备注 MDMD</th>
</tr>
</thead>
<tbody>
<tr>
<td>6560</td>
<td>MOS</td>
<td>6502同期的视频接口芯片,就是VIC,以及6561</td>
</tr>
<tr>
<td>6567</td>
<td>MOS</td>
<td>和6566、8562、8564都属于VIC-II,<a href="https://en.wikipedia.org/wiki/Commodore_64" title="Commdore 64">C64</a>使用</td>
</tr>
<tr>
<td>CDP1861</td>
<td>美国无线电公司</td>
<td>七十年代中期产品,常与RCA1802微处理器搭配</td>
</tr>
<tr>
<td>EF9345</td>
<td>意法半导体</td>
<td></td>
</tr>
<tr>
<td>SN76489</td>
<td>德州仪器</td>
<td>世嘉Mark3中使用了雅马哈生产的SN76489</td>
</tr>
<tr>
<td>TMS34010</td>
<td>德州仪器</td>
<td>芯片上还有CPU,主要用于街机</td>
</tr>
<tr>
<td>TMS9918</td>
<td>德州仪器</td>
<td><a href="https://en.wikipedia.org/wiki/TI-99/4A" title="TI-99/4A">TI-99/4A</a>,<a href="https://en.wikipedia.org/wiki/MSX" title="MSX电脑">MSX</a>初代</td>
</tr>
<tr>
<td>MC6845</td>
<td>摩托罗拉</td>
<td>常与Z80和6502在<a href="https://en.wikipedia.org/wiki/Apple_II" title="苹果2">家用电脑</a>中搭配使用</td>
</tr>
<tr>
<td>MC6847</td>
<td>摩托罗拉</td>
<td></td>
</tr>
<tr>
<td>V9938</td>
<td>雅马哈</td>
<td>用于<a href="https://en.wikipedia.org/wiki/MSX" title="MSX电脑">MSX</a>2电脑</td>
</tr>
<tr>
<td>V9958</td>
<td>雅马哈</td>
<td>用于<a href="https://en.wikipedia.org/wiki/MSX" title="MSX电脑">MSX</a>2+电脑</td>
</tr>
<tr>
<td>µPD7220</td>
<td>NEC</td>
<td>NEC的<a href="https://en.wikipedia.org/wiki/PC-98" title="NEC PC-98">PC-98</a></td>
</tr>
</tbody>
</table>
<p>做这些信息的收集,主要是为了后面自制怀旧风格[家用电脑]做准备。</p></content><category term="gamedev"></category><category term="GPU"></category><category term="PPU"></category><category term="VDP"></category><category term="CRTC"></category><category term="芯片"></category></entry><entry><title>这可能是FC游戏《地球冒险》难以汉化的原因</title><link href="https://funcman.me/why_is_it_difficult_to_chineseize_fc_mother.html" rel="alternate"></link><published>2023-01-13T09:36:45+08:00</published><updated>2023-01-13T09:36:45+08:00</updated><author><name>funcman</name></author><id>tag:funcman.me,2023-01-13:/why_is_it_difficult_to_chineseize_fc_mother.html</id><summary type="html"><p>FC游戏《地球冒险》是红白机上的知名作品。作为一个RPG,它有大量的文字。这样的游戏理应出现汉化版,毕竟中文游戏圈已经做了 …</p></summary><content type="html"><p>FC游戏《地球冒险》是红白机上的知名作品。作为一个RPG,它有大量的文字。这样的游戏理应出现汉化版,毕竟中文游戏圈已经做了这么多年汉化了。但是事实上就是没有弄出来。我作为一个红白机开发技术上的萌新,尝试一下从技术上分析,这个游戏的汉化难度。</p>
<p><img alt="《地球冒险》标题" src="./images/fc-mother-title.png"> <img alt="《地球冒险》对话" src="./images/fc-mother-talk.png"> </p>
<p>《地球冒险》的一些基本信息:它是个日版游戏,标题名称为“Mother”。没有出过官方英文版,但是有爱好者改制的英化版。从Mapper的角度看,它是一个Mapper4游戏,使用了MMC3芯片。</p>
<p><a href="./mmc3_irqs.html">MMC3支持IRQ中断</a>,它可以对扫描线进行计数,可以在PPU绘制第N条扫描线时,触发IRQ中断。在IRQ中断执行代码中,我们可以更改PPU绘制时依赖的各种数据。这意味着,我们可以实现屏幕分区域的绘制。比如在第N条扫描线时,改变图样表(Pattern Table)对CHR-ROM的映射。然后在第M条扫描线时,把图样表改回来。这样就能跨段(Bank)绘制更多的CHR(在背景中)。</p>
<p>下面我们来看《地球冒险》中战斗画面的一帧:</p>
<p><img alt="战斗中" src="./images/mother-fighting-screenshot.png"></p>
<p>我们对画面绘制过程进行分解,来看IRQ如何改变图样表实现屏幕分区域的绘制的:</p>
<blockquote>
<p>图片太大,如果看不清,请在图片上右键“在新标签中打开图片”然后放大观看。</p>
</blockquote>
<p><img alt="PPU在绘制《地球冒险》战斗中的一帧发生的变化" src="./images/ppu-changes-of-mother.png"></p>
<p>用的是Mesen模拟器的PPU调试功能,它的好处是不仅能定位扫描线(scanline),还可以定位PPU时钟周期(cycle),这样我们就可以了解到一段IRQ中断代码在多少个PPU周期被执行完毕。</p>
<p>一共截取了10个片段,下面逐一解释:</p>
<blockquote>
<p>这里要注意的是Mesen的Nametable Viewer,并不是用来显示扫描线在屏幕会绘像素点的实时结果的。
它给出的就是可视化的PPU数据,大多数模拟器的调试功能都会这么做。</p>
</blockquote>
<ol>
<li>一开始的情况,这和上一帧结束情况是一样的。</li>
<li>当绘制扫描线1和运行到第340个PPU时钟循环时(下面用<code>S1C340</code>这样的形式表示),PPU的第一个图样表被切到CHR-ROM中敌人的相关段。</li>
<li>第3到第6个截取时刻(从S2C40到S2C130),我们能看到图样表1已经被切得只剩敌人的相关段了。之所以全部切掉,我猜是因为后面有尺寸更大的敌人,比如BOSS。而程序都是一套。</li>
<li>从第6个截取时刻,也就是S78C323一直往后,我们能看到图样表1在逐步切回文字界面的相关段。这样PPU又能绘制文字界面了。</li>
</ol>
<p>于是,就实现了屏幕分区域的绘制。</p>
<p>高级一点的,比如<a href="https://en.wikipedia.org/wiki/Kirakira_(video_game)">《闪闪亮亮星星之夜DX》</a>。这张动图里的天空和地面就是采用屏幕分区域绘制的视差卷动效果。</p>
<p><img alt="闪闪亮亮星星之夜DX" src="./images/kira-kira-star-night-dx.gif"></p>
<p>PPU在一般情况下,绘制一个像素,计为一个PPU时钟周期。在NTSC电视制式下,三次PPU时钟周期的时间,对应一次CPU时钟周期。</p>
<p>虽然红白机的屏幕像素是256x240点阵的,但是并不是每一个像素都计为一个PPU时钟周期。电视机电子束在进入下一条扫描线前(水平过扫描),在256个像素之外,还有84个额外的PPU时钟周期(可能不止84个,我也不确定,萌新了喂)。</p>
<p>详细资料请参阅:<a href="https://www.nesdev.org/wiki/PPU_rendering">NESDev Wiki - PPU渲染</a></p>
<blockquote>
<p><strong>CPU时钟周期又是什么呢?</strong>
简单来说,比如<code>LDA #$01</code>的执行需要2个CPU时钟周期。
这和这段汇编的对应机器码<code>0xA9 0x01</code>是2个字节有关。
详细资料请参阅:<a href="https://www.nesdev.org/wiki/Cycle_reference_chart">NESDev Wiki - 时钟周期参考小抄</a></p>
</blockquote>
<p>已经讲了这么多,那么《地球冒险》的汉化难度到底在哪里呢?</p>
<p>我们来看一个游戏前期户外场景下,打开菜单界面的一帧:</p>
<p><img alt="《地球冒险》菜单" src="./images/fc-mother-menu.png"></p>
<p>看那个<strong>PSI</strong>选项,它的字模所在的CHR-ROM段,与哪些日文字模不在一起。我们用Mesen模拟器的调试功能查看一下这个PSI绘制的前后过程:</p>
<p><img alt="《地球冒险》在绘制菜单项PSI时PPU前后变化" src="./images/ppu-changes-of-mother2.png"></p>
<p>我们看到,在S64C8时,英文字模已经切到图样表1里了。这时PSI已经可以正常被PPU绘制了。</p>
<p>而到了S64C56,图样表1中英文字模的段又被切回了场景的图样。</p>
<p>这特么是在一条扫描线完成的!也就是说《地球冒险》不是以扫描线划分屏幕去切换对CHR-ROM的映射的。</p>
<p>不管原始开发者这样做是不是很牛X,这给汉化《地球冒险》带来了困难。</p>
<p>相对于英文、日文假名来说,中文字模尺寸庞大,无法在很短的PPU周期中做到切换。例子中PSI后面只有24个像素,也就是原始开发者在24个PPU时钟周期(8个CPU时钟周期)里完成了场景图样的切回工作。这对于中文是不可能的任务。除非说是菜单项这种,与大段文字不共用相同的图样地址空间,用非编码的方式。一帧里要显示大段的中文,对于红白机十分有限的图样表来说,仅通过切换CHR-ROM的段映射,是无法完成的。</p>
<p>那么有没有办法完成这个任务?还是有的。我们查看那些中文RPG文字卡,就会发现它们实际上基本都带有CHR-RAM。</p>
<p><img alt="CHR-RAM" src="./images/chr-ram.png"></p>
<p>有了CHR-RAM,我们可以将中文字模放在PRG-ROM中,并且压缩起来(一个图样像素可以有4个值,所以字模可以叠加)。然后在NMI中断中,将下一帧需要的中文都腾挪到CHR-RAM上。最后PPU显示画面时,把图样表对CHR-RAM做映射切换即可。</p>
<p><img alt="《重装机兵》" src="./images/fc-metal-max-zh-title.png"></p>
<p>比如《重装机兵》的D商汉化版。它是<a href="https://www.nesdev.org/wiki/INES_Mapper_074">Mapper74</a>的,也算是MMC3的Mapper(仿制芯片),能发出IRQ。它还用了74LS138和74LS139芯片,将1KiB的CHR-ROM的段8和段9,重定向到2KiB的CHR-RAM上。</p>
<blockquote>
<p>是的,用74芯片也可以做出Mapper来。不知道能不能用通用逻辑芯片实现IRQ中断。</p>
</blockquote>
<p><img alt="《重装机兵》显示中文" src="./images/fc-metal-max-zh.png"></p>
<p>比如《重装机兵》里对话的这一帧画面,我们能看到:</p>
<p><img alt="《重装机兵》显示中文时PPU的前后变化" src="./images/ppu-changes-of-mother3.png"></p>
<p>在S145C0时,PPU的图样表映射的内容,已经能够满足显示中文的需要。并且,只根据需要用到的文字,组织了CHR-RAM里的字模。</p>
<blockquote>
<p>处于共用的目的,ROM中(可能是PRG-ROM,也可能是CHR-ROM)里的字模图样肯定不是按照对话文字这样排列。
这里点选了“Display as 8x16 sprites”,这样《重装机兵》里的字模才容易被看出来。</p>
</blockquote>
<p>当然,《重装机兵》没有像《地球冒险》一样,定位到PPU时钟循环去改变图样表映射。它的对话框直接完全覆盖了下半部分的游戏画面。</p>
<p><img alt="对话框覆盖了场景" src="./images/fc-metal-max-zh2.png"> <img alt="场景全貌" src="./images/fc-metal-max-zh3.png"></p>
<p>好了,说到这里,大致已经讲清了《地球冒险》的汉化难度。应该不是不可能,只是难度极高。游戏扩容时要加入CHR-RAM的支持,要选择合适的Mapper方案。模拟器也不一定能支持得好(看下图)。如果我们想把汉化后的游戏放进<a href="https://krikzz.com/our-products/cartridges/n8-pro-fami.html">N8烧录卡</a>,或者做一张MMC3实体卡带,达到运行无误也是有难度的。</p>
<p><img alt="Mesen并不能很好支持对带CHR-RAM的游戏的CHR的查看" src="./images/mesen-donot-support-show-chr-with-ram.png"></p>
<p>作为一个萌新,可能难免有所失误,希望得到指教。</p></content><category term="gamedev"></category><category term="红白机"></category><category term="FC"></category><category term="NES"></category><category term="MMC3"></category><category term="汉化"></category><category term="游戏"></category></entry><entry><title>MMC3的IRQ中断</title><link href="https://funcman.me/mmc3_irqs.html" rel="alternate"></link><published>2023-01-11T13:26:34+08:00</published><updated>2023-01-11T13:26:34+08:00</updated><author><name>funcman</name></author><id>tag:funcman.me,2023-01-11:/mmc3_irqs.html</id><summary type="html"><p><a href="http://bobrost.com/nes/files/mmc3irqs.txt">原文</a>作者:Allan Blomquist</p>
<p>iNES Mapper #4 (MMC3)提供了一种方便的方法,来对PPU进行帧间改变,以实现NES游戏中的<strong>分屏卷动</strong>等“特殊效果”。与其使用<code>Sprite-0 Hits</code>去择机写入PPU硬件,不如 …</p></summary><content type="html"><p><a href="http://bobrost.com/nes/files/mmc3irqs.txt">原文</a>作者:Allan Blomquist</p>
<p>iNES Mapper #4 (MMC3)提供了一种方便的方法,来对PPU进行帧间改变,以实现NES游戏中的<strong>分屏卷动</strong>等“特殊效果”。与其使用<code>Sprite-0 Hits</code>去择机写入PPU硬件,不如使用IRQ。这可以使CPU在一帧内就做完工作,而不是浪费时间去轮询<code>Sprite-0 Hits</code>标志。 MMC3有4个寄存器,是MMC3为IRQ触发而设的:</p>
<p><code>$C000</code> - <strong>IRQ计数器</strong> - Mapper用它来倒计时以触发IRQ。每一条扫描线,该值减一;当它到0时,IRQ就触发。</p>
<blockquote>
<p><strong>注意:</strong> 背景和精灵必须启用,在<code>$2001</code>(<code>D3</code>、<code>D4</code>),这样倒数计数才会进行。</p>
</blockquote>
<p><code>$C001</code> - <strong>IRQ计数锁存器</strong> - 写入你想要的值,也就是用来倒数的值;而不是直接往<code>IRQ计数寄存器</code>里写值。</p>
<p><code>$E000</code> - <strong>IRQ禁用/确认器</strong> - 往这里写任意值,都能禁用IRQ的触发,但也能将<code>IRQ计数器锁存器</code>中的值复制到实际的<code>IRQ计数器</code>。一旦确认了一次IRQ,就会保证发生中断。</p>
<p><code>$E001</code> - <strong>IRQ启动器</strong> - 往这里写入任意值,都能启用IRQ的触发。</p>
<p>因此,大多数的商业游戏都是这样使用这些寄存器:</p>
<p>当你想设置的IRQ时:(放在你的NMI/vblank程序中)</p>
<ol>
<li>写<code>$E000</code>以确认任何当前待定的中断</li>
<li>向<code>$C000</code>和<code>$C001</code>写入你要等待的扫描线数</li>
<li>再次写<code>$E000</code>以锁定倒数值</li>
<li>写<code>$E001</code>以启用<code>IRQ计数器</code></li>
</ol>
<p>一旦你的IRQ发生了:(在你的IRQ程序中)</p>
<ol>
<li>写<code>$E000</code>以确认IRQ并禁用<code>IRQ计数器</code></li>
</ol>
<p>你在游戏使用MMC3的IRQ之前,有几件事情你必须设置好。<strong>首先</strong>,你要设置好ROM结尾中断向量表里的IRQ向量,指向IRQ发生时你想被调用的代码。IRQ向量的位置在$FFFE,在NMI向量和RESET向量的右边。当一个IRQ中断触发时,CPU会跳到的16位地址$FFFE上,并且CPU标志被自动存下来,在IRQ程序返回时自动恢复。但是CPU的寄存器不会被保存,所以你必须确保在IRQ程序结束时,它们会被恢复原样(开始时应该入栈AXY寄存器,结束时出栈YXA,注意顺序)。</p>
<p><strong>第二</strong>件你必须做的事情,使用MMC3的IRQ,就要阻止其他的在NES上产生的IRQ中断。NES有一个“帧计数器”,默认会在每帧结束时产生IRQ中断,非常像NMI。如果不禁用这个,你的IRQ代码次数,会比你要的更多,从而搞砸你的游戏。禁用帧计数器的IRQ中断,只需要在你的程序开始时,把<code>$40</code>写入到<code>$4017</code>。</p>
<p><strong>最后</strong>,为了能够使用IRQ,你一定要告诉NES的CPU,你有实际确认它们,而不是忽略了它们,因为它在默认情况下(这也是为什么你不必担心在简单的游戏里,帧计数器的IRQ的发生 - CPU实际上忽略了它们)会启用的IRQ。你必须用“CLI”指令清除CPU中的中断禁用标志。在某些时刻,如果你想重新禁用它们,要用“SEI”。</p>
<p>下面是一个在nbasic中使用MMC3 IRQ中断的最小例子。 它使用一个IRQ中断来改变屏幕上一半的背景颜色,而不需要使用<code>Sprite-0-Hits</code>来确定你的位置。</p>
<blockquote>
<p><strong>注意:</strong> MMC3 IRQ的时机安排是大多数模拟器没有真正做到的事情(例如,一个倒数值X,可能会在扫描线X-1或X+1上,实际触发一个IRQ中断,这取决于你使用的模拟器)。如果你使用IRQ,请在各种模拟器多测试几遍你的游戏。</p>
</blockquote>
<div class="highlight"><pre><span></span><code><span class="c1">;//nesasm文件头</span>
<span class="nf">asm</span>
<span class="w"> </span><span class="na">.inesprg</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="c1">;//1个PRG段</span>
<span class="w"> </span><span class="na">.ineschr</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="c1">;//0个CHR段</span>
<span class="w"> </span><span class="na">.inesmir</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="c1">;//镜像类型0</span>
<span class="w"> </span><span class="na">.inesmap</span><span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="c1">;//内存使用mapper 4 (MMC3)</span>
<span class="w"> </span><span class="na">.org</span><span class="w"> </span><span class="no">$C000</span>
<span class="w"> </span><span class="na">.bank</span><span class="w"> </span><span class="mi">0</span>
<span class="nf">endasm</span>
<span class="nl">start:</span>
<span class="w"> </span><span class="nf">gosub</span><span class="w"> </span><span class="no">vwait</span>
<span class="w"> </span><span class="nf">set</span><span class="w"> </span><span class="no">$2000</span><span class="w"> </span><span class="no">$80</span><span class="w"> </span><span class="c1">;// 打开NMI</span>
<span class="w"> </span><span class="nf">set</span><span class="w"> </span><span class="no">$2001</span><span class="w"> </span><span class="no">$08</span><span class="w"> </span><span class="c1">;// 打开BG使MMC3能够计数</span>
<span class="w"> </span><span class="nf">set</span><span class="w"> </span><span class="no">$4017</span><span class="w"> </span><span class="no">$40</span><span class="w"> </span><span class="c1">;// 禁用帧计数器IRQ</span>
<span class="nf">asm</span>
<span class="w"> </span><span class="nf">cli</span><span class="w"> </span><span class="c1">;// 启用IRQ处理过程</span>
<span class="nf">endasm</span>
<span class="nl">gameloop:</span>
<span class="w"> </span><span class="nf">goto</span><span class="w"> </span><span class="no">gameloop</span>
<span class="c1">;// NMI程序</span>
<span class="nl">nmi:</span>
<span class="w"> </span><span class="nf">set</span><span class="w"> </span><span class="no">$2006</span><span class="w"> </span><span class="no">$3F</span>
<span class="w"> </span><span class="nf">set</span><span class="w"> </span><span class="no">$2006</span><span class="w"> </span><span class="no">$00</span><span class="w"> </span><span class="c1">;// 设置背景色为红色</span>
<span class="w"> </span><span class="nf">set</span><span class="w"> </span><span class="no">$2007</span><span class="w"> </span><span class="no">$06</span>
<span class="w"> </span><span class="nf">set</span><span class="w"> </span><span class="no">$E000</span><span class="w"> </span><span class="no">$01</span>
<span class="w"> </span><span class="nf">set</span><span class="w"> </span><span class="no">$C000</span><span class="w"> </span><span class="no">$78</span><span class="w"> </span><span class="c1">;// 设置MMC3让IRQ中断发生在</span>
<span class="w"> </span><span class="nf">set</span><span class="w"> </span><span class="no">$C001</span><span class="w"> </span><span class="no">$78</span><span class="w"> </span><span class="c1">;// 120条扫描线之后</span>
<span class="w"> </span><span class="nf">set</span><span class="w"> </span><span class="no">$E000</span><span class="w"> </span><span class="no">$01</span>
<span class="w"> </span><span class="nf">set</span><span class="w"> </span><span class="no">$E001</span><span class="w"> </span><span class="no">$01</span>
<span class="w"> </span><span class="nf">set</span><span class="w"> </span><span class="no">$2006</span><span class="w"> </span><span class="no">$00</span>
<span class="w"> </span><span class="nf">set</span><span class="w"> </span><span class="no">$2006</span><span class="w"> </span><span class="no">$00</span>
<span class="w"> </span><span class="nf">set</span><span class="w"> </span><span class="no">$2001</span><span class="w"> </span><span class="no">$08</span><span class="w"> </span><span class="c1">;// 打开BG使MMC3能够计数</span>
<span class="w"> </span><span class="nf">resume</span>
<span class="c1">;// IRQ程序</span>
<span class="nl">irq:</span>
<span class="nf">asm</span>
<span class="w"> </span><span class="nf">pha</span>
<span class="w"> </span><span class="nf">txa</span>
<span class="w"> </span><span class="nf">pha</span><span class="w"> </span><span class="c1">;// 保存寄存器A,X和Y。你必须这么做,因为</span>
<span class="w"> </span><span class="nf">tya</span><span class="w"> </span><span class="c1">;// IRQ中断了你的主线程代码。改变你的</span>
<span class="w"> </span><span class="nf">pha</span><span class="w"> </span><span class="c1">;// 寄存器值,对主线程来说,这是可是</span>
<span class="w"> </span><span class="c1">;// 不太好的...</span>
<span class="nf">endasm</span><span class="w"> </span>
<span class="w"> </span><span class="no">set</span><span class="w"> </span><span class="no">$2001</span><span class="w"> </span><span class="no">$00</span><span class="w"> </span><span class="c1">;// 强制关闭背景,迫使愚蠢的模拟器意识到我在改变背景色</span>
<span class="w"> </span><span class="nf">set</span><span class="w"> </span><span class="no">$2006</span><span class="w"> </span><span class="no">$3F</span>
<span class="w"> </span><span class="nf">set</span><span class="w"> </span><span class="no">$2006</span><span class="w"> </span><span class="no">$00</span><span class="w"> </span><span class="c1">;// 设置背景色为绿色</span>
<span class="w"> </span><span class="nf">set</span><span class="w"> </span><span class="no">$2007</span><span class="w"> </span><span class="no">$0A</span>
<span class="w"> </span><span class="nf">set</span><span class="w"> </span><span class="no">$E000</span><span class="w"> </span><span class="no">$01</span><span class="w"> </span><span class="c1">;// 确认IRQ并禁止进一步产生IRQ</span>
<span class="w"> </span><span class="nf">set</span><span class="w"> </span><span class="no">$2006</span><span class="w"> </span><span class="no">$00</span>
<span class="w"> </span><span class="nf">set</span><span class="w"> </span><span class="no">$2006</span><span class="w"> </span><span class="no">$00</span>
<span class="nf">asm</span>
<span class="w"> </span><span class="nf">pla</span>
<span class="w"> </span><span class="nf">tay</span><span class="w"> </span><span class="c1">;// 取回你的寄存器值</span>
<span class="w"> </span><span class="nf">pla</span>
<span class="w"> </span><span class="nf">tax</span>
<span class="w"> </span><span class="nf">pla</span>
<span class="nf">endasm</span>
<span class="w"> </span><span class="nf">resume</span>
<span class="c1">;//等待屏幕刷新</span>
<span class="nl">vwait:</span>
<span class="w"> </span><span class="nf">asm</span>
<span class="w"> </span><span class="nf">lda</span><span class="w"> </span><span class="no">$2002</span>
<span class="w"> </span><span class="nf">bpl</span><span class="w"> </span><span class="no">vwait</span><span class="w"> </span><span class="c1">;//等待回溯开始</span>
<span class="w"> </span><span class="nl">vwait_1:</span>
<span class="w"> </span><span class="nf">lda</span><span class="w"> </span><span class="no">$2002</span>
<span class="w"> </span><span class="nf">bmi</span><span class="w"> </span><span class="no">vwait_1</span><span class="w"> </span><span class="c1">;//等待回溯结束</span>
<span class="w"> </span><span class="nf">endasm</span>
<span class="w"> </span><span class="nf">return</span>
<span class="c1">;//文件脚部</span>
<span class="nf">asm</span>
<span class="w"> </span><span class="c1">;//跳转表指向NMI,Reset和IRQ开始位置</span>
<span class="w"> </span><span class="na">.bank</span><span class="w"> </span><span class="mi">1</span>
<span class="w"> </span><span class="na">.org</span><span class="w"> </span><span class="no">$fffa</span>
<span class="w"> </span><span class="na">.dw</span><span class="w"> </span><span class="no">nmi</span><span class="p">,</span><span class="w"> </span><span class="no">start</span><span class="p">,</span><span class="w"> </span><span class="no">irq</span>
<span class="nf">endasm</span>
</code></pre></div></content><category term="gamedev"></category><category term="红白机"></category><category term="FC"></category><category term="NES"></category><category term="IRQ"></category><category term="MMC3"></category><category term="芯片"></category></entry><entry><title>如何配置运行在Docker中的GitLab的外部地址</title><link href="https://funcman.me/how_to_configure_gitlab_external_url_in_docker.html" rel="alternate"></link><published>2022-06-09T15:56:42+08:00</published><updated>2022-06-09T15:56:42+08:00</updated><author><name>funcman</name></author><id>tag:funcman.me,2022-06-09:/how_to_configure_gitlab_external_url_in_docker.html</id><summary type="html"><p>家里弄了个NAS服务器,在NAS上安装了一个Docker,于是弄了很多内部服务放在Docker上跑。</p>
<p>但是在设置GitLab时遇到了点问题,经过一番折腾,得到这个很简单的处理方式。</p>
<p>先说问题。大 …</p></summary><content type="html"><p>家里弄了个NAS服务器,在NAS上安装了一个Docker,于是弄了很多内部服务放在Docker上跑。</p>
<p>但是在设置GitLab时遇到了点问题,经过一番折腾,得到这个很简单的处理方式。</p>
<p>先说问题。大多数服务都是基于Web的,会要求暴露出80端口。Docker上有很多服务,IP共用主机的没问题,但是端口必须特别指定,然后映射到容器里的80端口上。</p>
<p>比如这样:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># docker run --detach \</span>
<span class="w"> </span>-p<span class="w"> </span><span class="m">11443</span>:443<span class="w"> </span>-p<span class="w"> </span><span class="m">11480</span>:80<span class="w"> </span>-p<span class="w"> </span><span class="m">11422</span>:22<span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--name<span class="w"> </span>gitlab<span class="w"> </span><span class="se">\</span>
<span class="w"> </span>--restart<span class="w"> </span>always<span class="w"> </span><span class="se">\</span>
<span class="w"> </span>-v<span class="w"> </span>/gitlab/config:/etc/gitlab<span class="w"> </span><span class="se">\</span>
<span class="w"> </span>-v<span class="w"> </span>/gitlab/logs:/var/log/gitlab<span class="w"> </span><span class="se">\</span>
<span class="w"> </span>-v<span class="w"> </span>/gitlab/data:/var/opt/gitlab<span class="w"> </span><span class="se">\</span>
<span class="w"> </span>gitlab/gitlab-ce
</code></pre></div>
<p>但是如此设置,GitLab中一旦建立项目后,项目的“克隆”里并不能自动提供合适的地址,只会给出这样的东西:</p>
<div class="highlight"><pre><span></span><code>http://b483cf8abf26/some_projects/project.git
</code></pre></div>
<p>很恶心,不好用!</p>
<p>按照网上的一些文章,据说修改<code>/etc/gitlab/gitlab.rb</code>中的<code>external_url</code>那一行,就可以了。比如修改成:</p>
<div class="highlight"><pre><span></span><code><span class="n">external_url</span><span class="w"> </span><span class="s2">&quot;http://192.168.1.114:11480&quot;</span>
</code></pre></div>
<p>但是一旦应用修改:</p>
<div class="highlight"><pre><span></span><code>gitlab-ctl<span class="w"> </span>reconfigure
</code></pre></div>
<p>服务直接访问不了啦。</p>
<p>怎么回事呢?实际上,这个<code>external_url</code>设置一旦带有端口,服务原本的80端口是会被改成设置的端口的。那么按照创建容器时的设置的<code>-p 11480:80</code>,肯定是不通的。</p>
<p>改一下容器的端口映射,直接<code>-p 11480:11480</code>,OK了。另外注意一下,这个GitLab镜像内部还占用8080端口,所以最好不要用,否则还要配置<code>/etc/gitlab/gitlab.rb</code>文件:</p>
<div class="highlight"><pre><span></span><code><span class="n">unicorn</span><span class="o">[</span><span class="s1">&#39;port&#39;</span><span class="o">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">8800</span>
</code></pre></div>
<p>这个unicorn的端口配置我没有试验过,仅作为笔记摘录。</p>
<p>这样配置好<code>external_url</code>,也只是解决了第一步,项目<code>克隆</code>中的<code>使用HTTP克隆</code>里的地址,<code>使用SSH克隆</code>还不正常,端口不对。怎么改?继续配置<code>/etc/gitlab/gitlab.rb</code>文件:</p>
<div class="highlight"><pre><span></span><code><span class="n">gitlab_rails</span><span class="o">[</span><span class="s1">&#39;gitlab_shell_ssh_port&#39;</span><span class="o">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">11422</span>
</code></pre></div>
<p>好了,重置、重启GitLab:</p>
<div class="highlight"><pre><span></span><code>gitlab-ctl<span class="w"> </span>reconfigure
gitlab-ctl<span class="w"> </span>restart
</code></pre></div>
<p>这里重启应该是没啥必要性的,多做无过。完事后,克隆里的地址应该都是正常可用的了。</p>
<p>事毕,我们可以查看一下端口信息:</p>
<div class="highlight"><pre><span></span><code><span class="c1"># netstat -l</span>
Active<span class="w"> </span>Internet<span class="w"> </span>connections<span class="w"> </span><span class="o">(</span>only<span class="w"> </span>servers<span class="o">)</span>
Proto<span class="w"> </span>Recv-Q<span class="w"> </span>Send-Q<span class="w"> </span>Local<span class="w"> </span>Address<span class="w"> </span>Foreign<span class="w"> </span>Address<span class="w"> </span>State
tcp<span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="m">0</span>.0.0.0:22<span class="w"> </span><span class="m">0</span>.0.0.0:*<span class="w"> </span>LISTEN
tcp<span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="m">0</span><span class="w"> </span>localhost:9229<span class="w"> </span><span class="m">0</span>.0.0.0:*<span class="w"> </span>LISTEN
tcp<span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="m">0</span><span class="w"> </span>localhost:9236<span class="w"> </span><span class="m">0</span>.0.0.0:*<span class="w"> </span>LISTEN
tcp<span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="m">0</span>.0.0.0:8060<span class="w"> </span><span class="m">0</span>.0.0.0:*<span class="w"> </span>LISTEN
tcp<span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="m">0</span><span class="w"> </span>localhost:9168<span class="w"> </span><span class="m">0</span>.0.0.0:*<span class="w"> </span>LISTEN
tcp<span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="m">0</span><span class="w"> </span>localhost:9187<span class="w"> </span><span class="m">0</span>.0.0.0:*<span class="w"> </span>LISTEN
tcp<span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="m">0</span><span class="w"> </span>localhost:9090<span class="w"> </span><span class="m">0</span>.0.0.0:*<span class="w"> </span>LISTEN
tcp<span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="m">0</span><span class="w"> </span>localhost:9093<span class="w"> </span><span class="m">0</span>.0.0.0:*<span class="w"> </span>LISTEN
tcp<span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="m">0</span><span class="w"> </span>localhost:9121<span class="w"> </span><span class="m">0</span>.0.0.0:*<span class="w"> </span>LISTEN
tcp<span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="m">0</span><span class="w"> </span>localhost:8154<span class="w"> </span><span class="m">0</span>.0.0.0:*<span class="w"> </span>LISTEN
tcp<span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="m">0</span><span class="w"> </span>localhost:8155<span class="w"> </span><span class="m">0</span>.0.0.0:*<span class="w"> </span>LISTEN
tcp<span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="m">0</span><span class="w"> </span>localhost:8153<span class="w"> </span><span class="m">0</span>.0.0.0:*<span class="w"> </span>LISTEN
tcp<span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="m">0</span><span class="w"> </span>localhost:8150<span class="w"> </span><span class="m">0</span>.0.0.0:*<span class="w"> </span>LISTEN
tcp<span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="m">0</span><span class="w"> </span>localhost:8151<span class="w"> </span><span class="m">0</span>.0.0.0:*<span class="w"> </span>LISTEN
tcp<span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="m">0</span><span class="w"> </span>localhost:8092<span class="w"> </span><span class="m">0</span>.0.0.0:*<span class="w"> </span>LISTEN
tcp<span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="m">0</span><span class="w"> </span>localhost:8082<span class="w"> </span><span class="m">0</span>.0.0.0:*<span class="w"> </span>LISTEN
tcp<span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="m">0</span><span class="w"> </span>localhost:8080<span class="w"> </span><span class="m">0</span>.0.0.0:*<span class="w"> </span>LISTEN
tcp<span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="m">0</span>.0.0.0:11480<span class="w"> </span><span class="m">0</span>.0.0.0:*<span class="w"> </span>LISTEN
tcp<span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="m">0</span><span class="w"> </span>localhost:3000<span class="w"> </span><span class="m">0</span>.0.0.0:*<span class="w"> </span>LISTEN
tcp<span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="m">0</span><span class="w"> </span>:::22<span class="w"> </span>:::*<span class="w"> </span>LISTEN
tcp<span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="m">0</span><span class="w"> </span>:::9094<span class="w"> </span>:::*<span class="w"> </span>LISTEN
udp<span class="w"> </span><span class="m">0</span><span class="w"> </span><span class="m">0</span><span class="w"> </span>:::9094<span class="w"> </span>:::*
Active<span class="w"> </span>UNIX<span class="w"> </span>domain<span class="w"> </span>sockets<span class="w"> </span><span class="o">(</span>only<span class="w"> </span>servers<span class="o">)</span>
Proto<span class="w"> </span>RefCnt<span class="w"> </span>Flags<span class="w"> </span>Type<span class="w"> </span>State<span class="w"> </span>I-Node<span class="w"> </span>Path
unix<span class="w"> </span><span class="m">2</span><span class="w"> </span><span class="o">[</span><span class="w"> </span>ACC<span class="w"> </span><span class="o">]</span><span class="w"> </span>STREAM<span class="w"> </span>LISTENING<span class="w"> </span><span class="m">5584197</span><span class="w"> </span>/var/opt/gitlab/gitaly/gitaly.socket
unix<span class="w"> </span><span class="m">2</span><span class="w"> </span><span class="o">[</span><span class="w"> </span>ACC<span class="w"> </span><span class="o">]</span><span class="w"> </span>STREAM<span class="w"> </span>LISTENING<span class="w"> </span><span class="m">5588069</span><span class="w"> </span>/var/opt/gitlab/gitaly/run/gitaly-306/sock.d/ruby.1
unix<span class="w"> </span><span class="m">2</span><span class="w"> </span><span class="o">[</span><span class="w"> </span>ACC<span class="w"> </span><span class="o">]</span><span class="w"> </span>STREAM<span class="w"> </span>LISTENING<span class="w"> </span><span class="m">5584198</span><span class="w"> </span>/var/opt/gitlab/gitaly/run/gitaly-306/sock.d/intern
unix<span class="w"> </span><span class="m">2</span><span class="w"> </span><span class="o">[</span><span class="w"> </span>ACC<span class="w"> </span><span class="o">]</span><span class="w"> </span>STREAM<span class="w"> </span>LISTENING<span class="w"> </span><span class="m">5573442</span><span class="w"> </span>/var/opt/gitlab/gitaly/run/gitaly-306/sock.d/ruby.0
unix<span class="w"> </span><span class="m">2</span><span class="w"> </span><span class="o">[</span><span class="w"> </span>ACC<span class="w"> </span><span class="o">]</span><span class="w"> </span>STREAM<span class="w"> </span>LISTENING<span class="w"> </span><span class="m">5591350</span><span class="w"> </span>/var/opt/gitlab/postgresql/.s.PGSQL.5432
unix<span class="w"> </span><span class="m">2</span><span class="w"> </span><span class="o">[</span><span class="w"> </span>ACC<span class="w"> </span><span class="o">]</span><span class="w"> </span>STREAM<span class="w"> </span>LISTENING<span class="w"> </span><span class="m">5609652</span><span class="w"> </span>/var/opt/gitlab/gitlab-rails/sockets/gitlab.socket
unix<span class="w"> </span><span class="m">2</span><span class="w"> </span><span class="o">[</span><span class="w"> </span>ACC<span class="w"> </span><span class="o">]</span><span class="w"> </span>STREAM<span class="w"> </span>LISTENING<span class="w"> </span><span class="m">5591983</span><span class="w"> </span>/var/opt/gitlab/gitlab-workhorse/sockets/socket
unix<span class="w"> </span><span class="m">2</span><span class="w"> </span><span class="o">[</span><span class="w"> </span>ACC<span class="w"> </span><span class="o">]</span><span class="w"> </span>STREAM<span class="w"> </span>LISTENING<span class="w"> </span><span class="m">5589222</span><span class="w"> </span>/var/opt/gitlab/redis/redis.socket
</code></pre></div>
<p>因为<code>external_url</code>的配置,GitLab完全没有用到<code>80</code>端口,而是<code>11480</code>。</p>
<p>GitLab居然会根据外部地址的端口,指定内部使用的端口,这谁能想到啊。</p></content><category term="others"></category><category term="GitLab"></category><category term="Docker"></category><category term="编程环境"></category></entry><entry><title>Unity ShaderLab 个人学习笔记</title><link href="https://funcman.me/unity_shader_lab_note.html" rel="alternate"></link><published>2021-12-21T11:44:12+08:00</published><updated>2021-12-21T11:44:12+08:00</updated><author><name>funcman</name></author><id>tag:funcman.me,2021-12-21:/unity_shader_lab_note.html</id><summary type="html"><p>最近在回顾Unity的着色器知识。</p>
<p>这里主要记录个人在ShaderLab中遇到的一些重点内容。</p>
<p>这篇会做持续记录,直到完成此遍回顾。</p>
<h2>ShaderLab的基本结构</h2>
<p>ShaderLab是基于Cg(HLSL)的,但不等同于Cg。它在此基础上又添 …</p></summary><content type="html"><p>最近在回顾Unity的着色器知识。</p>
<p>这里主要记录个人在ShaderLab中遇到的一些重点内容。</p>
<p>这篇会做持续记录,直到完成此遍回顾。</p>
<h2>ShaderLab的基本结构</h2>
<p>ShaderLab是基于Cg(HLSL)的,但不等同于Cg。它在此基础上又添加了一套东西,是高于Shader的东西。</p>
<p>某种程度可以把ShaderLab代码等同于Unity的材质。它包含了整套东西,概念上像是HLSL基础之上的D3D Effects。</p>
<div class="highlight"><pre><span></span><code><span class="n">Shader</span><span class="w"> </span><span class="s">&quot;ShaderName&quot;</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">Properties</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="c1">// 属性</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="n">SubShader</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">Pass</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="c1">// 具体的Shader程序</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="n">Pass</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="c1">// 有时会用到多个Pass</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="n">SubShader</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="c1">// 因为硬件的差异,有的旧硬件不支持某些Shader</span>
<span class="w"> </span><span class="c1">// 可能需要提供多个子Shader供Unity选择</span>
<span class="w"> </span><span class="c1">// Unity会选择第一个能运行的子Shader</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="c1">// 当所有子Shader都不能运行时</span>
<span class="w"> </span><span class="c1">// 提供一个应急方案,双引号里是Shader名称</span>
<span class="w"> </span><span class="c1">// 或者关掉这个功能</span>
<span class="w"> </span><span class="c1">// 代码是:Fallback off</span>
<span class="w"> </span><span class="n">Fallback</span><span class="w"> </span><span class="s">&quot;Diffuse&quot;</span>
<span class="p">}</span>
</code></pre></div>
<p>注意这里Fallback有个隐藏影响,在Unity预计算阴影时,会在SubShader里找相关Pass,找不到就会用Fallback提供的。</p>
<h2>Properties 属性设置</h2>
<p>如果把Shader看成一套功能,或者广义的函数,那Properties就是Shader被提交给GPU执行时携带的参数。</p>
<div class="highlight"><pre><span></span><code><span class="p">...</span>
<span class="n">Properies</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="c1">// 形式:</span>
<span class="w"> </span><span class="c1">// Name(&quot;DisplayName&quot;, PropertyType) = DefaultValue</span>
<span class="w"> </span><span class="c1">// 例如:</span>
<span class="w"> </span><span class="n">_pos</span><span class="p">(</span><span class="s">&quot;Position&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">Vector</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">...</span>
</code></pre></div>
<p>这里<code>_pos</code>可以在后面的Shader代码里作为“变量名”使用。</p>
<p>PropertyType支持<code>Int</code>、<code>Float</code>、<code>Range(min,max)</code>、<code>Color</code>、<code>Vector</code>、<code>2D</code>、<code>Cube</code>、<code>3D</code>这些类型,其中后3种是纹理类型。</p>
<p>贴图类型可以像这样:<code>_tex("Texture", 2D) = "white"{}</code>,指定默认纹理。<code>"white"</code>是默认纹理名称。</p>
<p>代码种的属性会对应到Unity材质属性设置面板中。</p>
<h2>SubShader的一般结构</h2>
<div class="highlight"><pre><span></span><code><span class="p">...</span>
<span class="n">SubShader</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="c1">// 不一定要有</span>
<span class="w"> </span><span class="n">Tags</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="c1">// 标签块</span>
<span class="w"> </span><span class="c1">// 键值对形式 </span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="c1">// 不一定要有</span>
<span class="w"> </span><span class="n">RenderSetup</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="c1">// 状态设置块</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="n">Pass</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="c1">// Pass的名字,不一定要给</span>
<span class="w"> </span><span class="n">Name</span><span class="w"> </span><span class="s">&quot;Pass1&quot;</span>
<span class="w"> </span><span class="c1">// Pass的Tags</span>
<span class="w"> </span><span class="c1">// 注意和和SubRender的不一样</span>
<span class="w"> </span><span class="c1">// 也可以有Pass自己的RenderSetup</span>
<span class="w"> </span><span class="c1">// CGPROGAM/ENDCG 表示该块是Cg代码</span>
<span class="w"> </span><span class="c1">// GLSLPROGAM/ENDGLSL 表示该块是glsl代码</span>
<span class="w"> </span><span class="c1">// 但是不推荐使用glsl,因为Unity能够将Cg交叉编译给glsl,反之不能</span>
<span class="w"> </span><span class="n">CGPROGRAM</span>
<span class="w"> </span><span class="c1">// 指定具体的功能函数入口</span>
<span class="w"> </span><span class="cp">#pragma vertex vert</span>
<span class="w"> </span><span class="cp">#pragma fragment frag</span>
<span class="w"> </span><span class="n">float4</span><span class="w"> </span><span class="n">vert</span><span class="p">(</span><span class="n">float4</span><span class="w"> </span><span class="n">v</span><span class="w"> </span><span class="o">:</span><span class="w"> </span><span class="n">POSITION</span><span class="p">)</span><span class="w"> </span><span class="o">:</span><span class="w"> </span><span class="n">SV_POSITION</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">mul</span><span class="p">(</span><span class="n">UNITY_MATRIX_MVP</span><span class="p">,</span><span class="w"> </span><span class="n">v</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="n">fixed4</span><span class="w"> </span><span class="n">frag</span><span class="p">()</span><span class="w"> </span><span class="o">:</span><span class="w"> </span><span class="n">SV_Target</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">fixed4</span><span class="p">(</span><span class="mf">1.0</span><span class="p">,</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="mf">0.0</span><span class="p">,</span><span class="w"> </span><span class="mf">1.0</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="n">ENDCG</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">}</span>
<span class="p">...</span>
</code></pre></div>
<p>Tags和RenderSetup放到后面记录。</p>
<p>其实ShaderLab还支持固定渲染管线,但是毕竟现在几乎已经没有了非可编程管线的硬件,就不做记录了。</p>
<h2>RenderSetup 渲染状态设置</h2>
<p>常用的状态表:</p>
<table>
<thead>
<tr>
<th>状态名</th>
<th>设置项目</th>
<th>解释</th>
</tr>
</thead>
<tbody>
<tr>
<td>Cull</td>
<td>Back, Front, Off</td>
<td>剔除模式:背面、正面、关闭</td>
</tr>
<tr>
<td>ZTest</td>
<td>Less, Greater, LEqual, GEqual, Equal, NotEqual, Always</td>
<td>深度测试: &lt;、&gt;、&lt;=、&gt;=、==、!=、总是</td>
</tr>
<tr>
<td>ZWrit</td>
<td>On, Off</td>
<td>深度写入开关</td>
</tr>
<tr>
<td>Blend</td>
<td>原模式,目的模式</td>
<td>打开并设置混合模式</td>
</tr>
</tbody>
</table></content><category term="gamedev"></category><category term="Unity"></category><category term="ShaderLab"></category></entry><entry><title>Orthodox C++</title><link href="https://funcman.me/orthodox_cpp.html" rel="alternate"></link><published>2021-12-17T09:40:38+08:00</published><updated>2021-12-17T09:40:38+08:00</updated><author><name>funcman</name></author><id>tag:funcman.me,2021-12-17:/orthodox_cpp.html</id><summary type="html"><p>本文翻译自<a href="https://gist.github.com/bkaradzic/2e39896bc7d8c34e042b">https://gist.github.com/bkaradzic/2e39896bc7d8c34e042b</a>,原作者是<a href="https://twitter.com/bkaradzic">Бранимир Караџић</a>。</p>
<h2>什么是Orthodox C++?</h2>
<p>Orthodox C++(有时被称为<strong>C+</strong>)是C++的最小子集,它改进了C,但避免了所谓的现 …</p></summary><content type="html"><p>本文翻译自<a href="https://gist.github.com/bkaradzic/2e39896bc7d8c34e042b">https://gist.github.com/bkaradzic/2e39896bc7d8c34e042b</a>,原作者是<a href="https://twitter.com/bkaradzic">Бранимир Караџић</a>。</p>
<h2>什么是Orthodox C++?</h2>
<p>Orthodox C++(有时被称为<strong>C+</strong>)是C++的最小子集,它改进了C,但避免了所谓的现代C++中所有不必要的东西。它与<a href="https://stackoverflow.com/questions/3661237/what-is-modern-c">现代C++</a>的预设恰好相反。</p>
<h2>为什么否定现代C++?</h2>
<p>在1990年末,我们也是当时的现代C++潮人,我们会用最新的功能。我们告诉大家就应该用这些特性。随着时间的推移,我们搞清了使用一些语言特性其实是大可不必的,这很明显,我们用的特性已经被证明是不好的(像RTTI、异常和流),非必要的代码复杂性搞得适得其反。如果你认为这是无稽之谈,那就再多等几年,到时<a href="http://archive.md/2016.05.17-214038/https://www.linkedin.com/pulse/why-i-dont-spend-time-modern-c-anymore-henrique-bucher-phd">你也会讨厌现代C++</a>(LinkedIn的归档文章:《为什么我不再花时间研究现代C++了》)。</p>
<p><img alt="一副漫画" src="https://funcman.me/images/modern_cpp_caricature.png"></p>
<h2>为什么要用Orthodox C++?</h2>
<p>基于Orthodox C++限制所编写的代码,将更容易理解,更简单,并且可以用更老的编译器构建。用Orthodox C++子集编写的项目,将更容易被其他C++项目接受,因为Orthodox C++用到的子集,不太会与采用者的C++子集偏好相冲突。</p>
<h2>使用Orthodox C++编写的Hello World</h2>
<div class="highlight"><pre><span></span><code><span class="w"> </span><span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;stdio.h&gt;</span>
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span>
<span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">&quot;hello, world</span><span class="se">\n</span><span class="s">&quot;</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
</code></pre></div>
<h2>我该使用什么?</h2>
<ul>
<li>类C的C++是个好的开始,如果代码不需要更多的复杂性,就不要增加不必要的C++复杂性。一般情况下,代码对于熟知C语言的人来说,应该是可读的。</li>
<li>不要做<a href="http://archive.md/2014.04.28-125041/http://www.boost.org/doc/libs/1_55_0/libs/geometry/doc/html/geometry/design.html">这事</a>,Orthodox C++“设计原理”的结尾,应该紧跟“很简单,且可用。<strong>EOF</strong>”。</li>
<li>不要使用<a href="https://web.archive.org/web/20190116034706/http://www.lighterra.com/papers/exceptionsharmful/">异常</a>。</li>
</ul>
<blockquote>
<p>异常处理是唯一的需要复杂的运行时系统支撑的C++语言特性,也是唯一的即使你不用也要付出运行时消耗的C++特性——很多时候,它会在每个对象的构造、析构以及try块的进入/退出时,额外地加入隐藏代码,还总是很大地限制编译器能做的优化。然而,C++的异常又是编译期是不执行的,所以你甚至不知道是否忘了处理一些错误的情况!从风格上看,处理错误的异常,与C的错误返回码风格并不匹配,这导致了编程风格的真实分裂,因为C++代码必然会调用底层C库。</p>
</blockquote>
<ul>
<li>不要使用RTTI。</li>
<li>不要使用被C++运行时包装的C运行时,包括(<code>&lt;cstdio&gt;</code>、<code>&lt;cmath&gt;</code>等),要使用C运行时(<code>&lt;stdio.h&gt;</code>、<code>&lt;math.h&gt;</code>等)。</li>
<li>不要使用流(<code>&lt;iostream&gt;</code>、<code>&lt;stringstream&gt;</code>等),使用printf风格的函数代替。</li>
<li>不要使用任何由STL分配的内存,除非你不关心内存管理。详见<a href="https://www.youtube.com/watch?v=LIb3L4vKZ7U">CppCon 2015:《std::allocator归于分配,std::vector归于纠结》(Andrei Alexandrescu)</a>这个讲座,以了解更多信息。</li>
<li>不要过度使用元编程来进行学术自慰。要适度,只在必要时,在能降低代码复杂度时,使用它。</li>
<li>对当前的标准C++&lt;年份&gt;中引入的任何特性,要有警惕性,最好等到这些特性在标准的下一次迭代中得到了改进。例如,C++11中的“constexpr”待到C++14中再采用(<a href="http://archive.md/2018.02.01-171248/https://twitter.com/lefticus/status/958931109009440768">来自Jason Turner</a>,cppbestpractices.com的站长)。</li>
</ul>
<h2>使用现代C++&lt;年份&gt;的功能都还安全吗?</h2>
<p>由于编译器、操作系统发行版等,对C++标准采用的滞后。通常不能立即开始使用那些新的、有用的语言特性。一般性指导是:如果今年是C++<em>年份</em>+5,就可以安全地开始<strong>有选择性地</strong>使用C++_年份_的特性。例如,如果标准是C++11,而今年&gt;=2016,则基本是安全的。如果编译你的代码需要的标准是C++17,并且今年是2016年,那么显然你是在实践“简历驱动开发”方法论。如果你在做开源项目,那你就不是在造别人能用的东西。</p>
<p><strong>更新</strong> 截至2019年1月14日, Orthodox C++委员会已认可对C++14的使用。</p>
<h2>还有其他类似的想法?</h2>
<ul>
<li><a href="https://en.wikipedia.org/wiki/Embedded_C%2B%2B">Embedded C++</a></li>
<li><a href="http://archive.md/2016.08.07-162105/https://namandixit.github.io/blog/nominal-c++/">Nominal C++</a></li>
<li><a href="http://archive.md/2016.08.07-162220/http://flohofwoe.blogspot.nl/2013/06/sane-c.html">Sane C++</a></li>
<li><a href="http://archive.md/2017.03.19-055108/https://hacksoflife.blogspot.nl/2017/03/why-your-c-should-be-simple.html">为什么你的C++应该变简单</a></li>
<li><a href="https://web.archive.org/web/20190227061553/https://c0de517e.blogspot.com/2019/02/c-its-not-you-its-me.html">C++,非你。是我。</a></li>
<li><a href="https://www.youtube.com/watch?v=lTXHOOwfTAo">《保持C-mple》Alexander Radchenko于悉尼C++聚会</a></li>
<li><a href="https://web.archive.org/web/20200521234043/https://satish.net.in/20180302/">一种C++方言</a></li>
</ul>
<h2>代码示例</h2>
<ul>
<li>任何被C++编译器通过的C源码</li>
<li><a href="https://github.com/id-Software/DOOM-3-BFG">DOOM 3 BFG</a></li>
<li><a href="https://github.com/qtproject">Qt</a> (使用无RTTI、无异常构建的)</li>
<li><a href="https://github.com/ocornut/imgui">dear imgui</a></li>
<li><a href="https://github.com/bkaradzic/bgfx">bgfx</a></li>
<li><a href="https://github.com/ConfettiFX/The-Forge">TheForge</a></li>
<li><a href="https://github.com/floooh/oryol">Oryol</a></li>
<li><a href="https://github.com/networknext/sdk">Network Next SDK</a></li>
</ul></content><category term="programming"></category><category term="C++"></category><category term="编程风格"></category></entry><entry><title>改用Pelican重建这个博客</title><link href="https://funcman.me/replace_jekyll_with_pelican_for_my_blog.html" rel="alternate"></link><published>2021-09-19T03:53:55+08:00</published><updated>2021-09-19T03:53:55+08:00</updated><author><name>funcman</name></author><id>tag:funcman.me,2021-09-19:/replace_jekyll_with_pelican_for_my_blog.html</id><summary type="html"><p>这里简单发一篇博客,讲一下我在使用了Pelican来重建这个博客。至于回归博客的感言什么的,回头再说了。</p>
<p>之所以不再使用Jekyll,主要是不想在电 …</p></summary><content type="html"><p>这里简单发一篇博客,讲一下我在使用了Pelican来重建这个博客。至于回归博客的感言什么的,回头再说了。</p>
<p>之所以不再使用Jekyll,主要是不想在电脑上安装过多的开发环境了。而且Github Pages会是不是升级Jekyll环境,我还得跟着升级,太麻烦了。</p>
<p>Pelican的好处是非常简单,很容易生成静态页面,且我会用到Python。</p>
<p>Pelican的用法网上很多,这里主要记录一下特别的地方。</p>
<p>首先是图片等涉及静态路径的,需要在<strong>pelicanconf.py</strong>里设置<strong>STATIC_PATHS</strong>:</p>
<div class="highlight"><pre><span></span><code><span class="n">STATIC_PATHS</span> <span class="o">=</span> <span class="p">[</span>
<span class="s1">&#39;images&#39;</span><span class="p">,</span>
<span class="p">]</span>
</code></pre></div>
<p>并且Markdown中,引用图片需要这样:</p>
<div class="highlight"><pre><span></span><code>
</code></pre></div>
<p>然后是favicon,我是这么设置的,并且把<strong>favicon.ico</strong>放在<strong>/content/extra/</strong>目录下:</p>
<div class="highlight"><pre><span></span><code><span class="n">STATIC_PATHS</span> <span class="o">=</span> <span class="p">[</span>
<span class="s1">&#39;images&#39;</span><span class="p">,</span>
<span class="s1">&#39;extra&#39;</span><span class="p">,</span>
<span class="p">]</span>
<span class="n">EXTRA_PATH_METADATA</span> <span class="o">=</span> <span class="p">{</span>
<span class="s1">&#39;extra/robots.txt&#39;</span><span class="p">:</span> <span class="p">{</span><span class="s1">&#39;path&#39;</span><span class="p">:</span> <span class="s1">&#39;robots.txt&#39;</span><span class="p">},</span>
<span class="s1">&#39;extra/favicon.ico&#39;</span><span class="p">:</span> <span class="p">{</span><span class="s1">&#39;path&#39;</span><span class="p">:</span> <span class="s1">&#39;favicon.ico&#39;</span><span class="p">},</span>
<span class="s1">&#39;extra/CNAME&#39;</span><span class="p">:</span> <span class="p">{</span><span class="s1">&#39;path&#39;</span><span class="p">:</span> <span class="s1">&#39;CNAME&#39;</span><span class="p">},</span>
<span class="p">}</span>
</code></pre></div>
<p>显而易见,我的<strong>extra</strong>目录下还放了<strong>CNAME</strong>和<strong>rebots.txt</strong>,Pelican在生成时,会把他们都放到<strong>output</strong>目录的根部。</p>
<p>通过运行<code>pelican content</code>很容易就在<strong>output</strong>下生成了全部的静态网页,把这些页面通过git更新到Github Pages上即可完成发布。因为我之前用的是Jekyll,所以我删除了原有的全部文件。另外,这个博客的Pelican的工程需要单独开仓库存储。</p>
<p>另外,Pelican可以更换主题,可以通过网站<a href="http://www.pelicanthemes.com">Pelican Themes</a>找到你喜欢的主题。等我发完这篇,我就来弄主题。</p>
<p>大致就是这样。</p></content><category term="others"></category><category term="Pelican"></category></entry><entry><title>另一种在Windows10下使用Jekyll的方法</title><link href="https://funcman.me/another_way_to_use_jekyll_on_windows_10.html" rel="alternate"></link><published>2019-03-31T08:37:45+08:00</published><updated>2019-03-31T08:37:45+08:00</updated><author><name>funcman</name></author><id>tag:funcman.me,2019-03-31:/another_way_to_use_jekyll_on_windows_10.html</id><summary type="html"><p>最近准备继续记博客,需要安装Jekyll环境。</p>
<p>手头既有Mac也有PC,考虑到上班期间带的是PC,所以还是要在Windows下配置一个Jekyll环境。</p>
<p>按照Jekyll官网的方法,是去安装RubyInstaller。但是,RubyInstaller本身是基于MSYS2的,我的 …</p></summary><content type="html"><p>最近准备继续记博客,需要安装Jekyll环境。</p>
<p>手头既有Mac也有PC,考虑到上班期间带的是PC,所以还是要在Windows下配置一个Jekyll环境。</p>
<p>按照Jekyll官网的方法,是去安装RubyInstaller。但是,RubyInstaller本身是基于MSYS2的,我的PC已经装了MSYS2,不想再多搞一套,于是决定基于MSYS2安装Ruby和Jekyll。</p>
<p>MSYS2类似Cygwin(其实就是一个Cygwin分支),可以让用户在Windows下使用UNIX环境的套件。这里先介绍一下相关概念:</p>
<ul>
<li>
<p>Pacman是一套流行于Arch Linux下的软件包管理软件。</p>
</li>
<li>
<p>MinGW是一套在Windows使用的GNU工具链,MinGW-w64是新一代MinGW,同时支持32位和64位。</p>
</li>
<li>
<p>MSYS是为帮助MinGW在Windows下使用,随MinGW提供的一套基本的POSIX操作环境。它是基于较旧的Cygwin项目创建的一个分支。MSYS2和MSYS并不是同一个项目的不同版本,它们各为独立项目。</p>
</li>
</ul>
<p>MSYS2集成了Pacman和MinGW-w64。</p>
<p>我们在中国使用MSYS2,最好设置一下国内的镜像,网上的文章基本都是让用<a href="https://lug.ustc.edu.cn/wiki/mirrors/help/msys2">中科大的镜像</a>,但目前他们的东西有点问题,只好改用<a href="https://mirrors.tuna.tsinghua.edu.cn/help/msys2">清华的镜像</a>。可惜了,实际上中科大离我很近。</p>
<p>按照页面上的说明设置好镜像,并执行:</p>
<div class="highlight"><pre><span></span><code>pacman -Syu
</code></pre></div>
<p>在最后提示关闭MSYS2时,点窗口的X进行关闭,再去MSYS2安装目录里运行一下autorebase.bat。然后打开MSYS2,执行一下更新:</p>
<div class="highlight"><pre><span></span><code>pacman -Su
</code></pre></div>
<p>之后就安装一系列软件,我这里是:</p>
<div class="highlight"><pre><span></span><code><span class="n">pacman</span><span class="w"> </span><span class="o">-</span><span class="n">S</span><span class="w"> </span><span class="n">vim</span><span class="w"> </span><span class="n">git</span><span class="w"> </span><span class="n">base</span><span class="o">-</span><span class="n">devel</span><span class="w"> </span><span class="n">mingw</span><span class="o">-</span><span class="n">w64</span><span class="o">-</span><span class="n">x86_64</span><span class="o">-</span><span class="n">toolchain</span><span class="w"> </span><span class="n">mingw</span><span class="o">-</span><span class="n">w64</span><span class="o">-</span><span class="n">x86_64</span><span class="o">-</span><span class="n">ruby</span><span class="w"> </span><span class="n">mingw</span><span class="o">-</span><span class="n">w64</span><span class="o">-</span><span class="n">x86_64</span><span class="o">-</span><span class="n">ruby</span><span class="o">-</span><span class="n">native</span><span class="o">-</span><span class="n">package</span><span class="o">-</span><span class="n">installer</span>
</code></pre></div>
<p>之所以是使用mingw64/mingw-w64-x86_64-ruby,而不是msys/ruby,是因为我先用它试了一番,在Gem安装Jekyll时总是有个库编不过去。我没有深究下去,先这么着吧,以后有空再找找解决方法。</p>
<p>安装了这个版本的Ruby之后,需要将<a href="https://github.com/msys2/msys2/wiki/MSYS2-introduction#subsystems">shell type</a>设为mingw64进入MSYS2环境,也就是从“MSYS2 MinGW 64-bit”而不是“MSYS2 MSYS”进。前者会把/mingw64/bin加入到PATH里,而此版本Ruby安装在/mingw64/bin中。</p>
<p>然后,我用Gem安装Bundler和Jekyll:</p>
<div class="highlight"><pre><span></span><code>gem install bundler jekyll
</code></pre></div>
<p>我已经有个很久没写过的旧博客,需要升级一下,所以进入博客的目录,执行了一下:</p>
<div class="highlight"><pre><span></span><code>bundle update
</code></pre></div>
<p>据说升级Gem是一件很折腾人的事情,Ruby程序员往往一次升级单个Gem,一次性bundle update,容易出问题。我也是反复在bundle install和bundle update之间折腾,才搞定了这件事。具体可以查一下<a href="https://bing.com/search?q=bundle%20install%20bundle%20update%20%E5%8C%BA%E5%88%AB">bundle install和bundle update之间的区别</a>。</p>
<p>升级完成之后,就可以继续<a href="https://jekyllrb.com/docs/step-by-step/01-setup">用Jekyll写博客</a>了。</p>
<p>PS:</p>
<p>我为了复习怎么用Jekyll,粗略的看了一下<a href="https://jekyllrb.com/docs/step-by-step/01-setup">Jekyll Step by Step Tutorial</a>。它在第一步里讲使用jekyll serve来运行Jekyll,这和我们平常使用bundle exec jekyll serve的方式有点不一样。实际上在第十步里有解释<a href="https://jekyllrb.com/docs/step-by-step/10-deployment/#gemfile">怎么一回事</a>。我也不是很熟悉Ruby,使用Jekyll的很多人估计也是这样。反正Bundler大概是一个处理相同Gem的不同版本被同时引用的工具,我们记得用它就行了。</p>
<p>另外,这一篇虽然标题是Windows10,并不是说不适用于之前的Windows版本,我只是没有试过。</p></content><category term="others"></category><category term="Jekyll"></category><category term="Ruby"></category><category term="编程环境"></category></entry><entry><title>“你是如果管理内存的?”</title><link href="https://funcman.me/how_do_you_manage_memory.html" rel="alternate"></link><published>2016-07-22T04:46:07+08:00</published><updated>2016-07-22T04:46:07+08:00</updated><author><name>funcman</name></author><id>tag:funcman.me,2016-07-22:/how_do_you_manage_memory.html</id><summary type="html"><p>还没找到工作,面了几家,都没下文。我脸皮厚,写点总结发博客。</p>
<p>像标题这样的面试问题,这些天遇到不止一两次。第一次我略 …</p></summary><content type="html"><p>还没找到工作,面了几家,都没下文。我脸皮厚,写点总结发博客。</p>
<p>像标题这样的面试问题,这些天遇到不止一两次。第一次我略懵,毕竟好久没被面了,没什么准备,所以没做出全面的回答。不过,就算到现在,我也没能在面试时把这个问题回答得很到位。可能这个问题对我来说已经不是什么困扰,平时想得也不多,遇到了我也能解决。这里做一下笔记,这样再遇到这个问题,我可以表现得好一些。</p>
<p>首先破题,为什么需要考虑管理内存的事,因为C/C++主要提供的是一种很原始的手工释放内存的方式。人难免出错,如果申请了内存,最后忘记释放,就会造成内存泄漏。此外,对于那些在好几个模块之间共享的内存,怎样才能做到正确且合理地释放,也是个问题。如果其中某个模块做了解引用(dereference)操作,还要专门去通知别的模块,再根据情况的不同决定是否需要真的释放内存,那模块之间必然是紧耦合的。所以,并不是小心谨慎就能解决好内存管理问题,好的内存管理一定要借助合适的方法和工具。</p>
<p>我这里第一个要谈的工具,是内存池。内存池实际上是在一开始申请一大块内存,再将这大块的内存按照一定规则划分成几个尺寸的小块内存集合。同一尺寸的小块内存用一个叫FreeList的东西串起来,有几种尺寸就搞几条FreeList。当我们需要内存时,根据需要的尺寸,就近找到一条合适的FreeList(比如系统内有8 Bytes和16 Bytes等尺寸的FreeList,我们需要9 Bytes的内存,那么就挑16 Bytes的FreeList),从中拿一个小块即可。当这个内存不再使用时,被释放的过程,就是将它还回FreeList的过程。当然,内存池有其它的实现方式,但也是差不多的东西,大同小异。</p>
<p>内存池技术并没有减免程序员的内存释放工作,那它带来了什么好处呢。由于它要考虑的系统的分配器少一点,所以一般效率会高那么一点。同时保证了一定的内存对齐,这里的对齐并不是为了CPU更高效的寻址,而是一定程度上可以避免内存碎片。如果分配内存时没有对齐,最终内存中的连续空闲块会越来越小,分配大块内存时会因为找不到尺寸足够的连续空闲内存,而失败。内存对齐分配的内存池技术,很大程度能够缓解这个问题。</p>
<p>实际上,内存池技术在很多开源内存分配器就带了,甚至操作系统内核里就有。除非是在极端应用场景,否则没有必要自己自制内存池。我觉得如果真遇到这样的场景,恐怕就不是内存池技术能解决的了,更静态一些、固定一些的分配方案,会更好一点。</p>
<p>如果自制内存池,可以同时集成一个内存跟踪器。内存跟踪器可以帮助程序员找到内存泄漏。前面已经说到了,内存泄漏是由于没有及时释放内存所致。跟踪器Hook在分配器上,登记每一次内存的分配操作。同时Hook在释放操作上,每次释放时注销之前的登记。当进程或模块退出时,可以查看跟踪器中还有没有登记项目,如果有,说明有内存存在泄漏的情况。根据登记信息,我们可以知道哪里申请的内存最后没有被释放,进而我们可以找到放置释放代码的地方。内存跟踪器是和内存池互为独立的工具,并不依赖内存池存在。</p>
<p>对于很多应用场景来说,我们可以借助其数据的关联结构,来减轻手工内存释放的痛苦程度。这些系统中,数据往往成父子关联关系:比如GUI系统中,按钮是窗体的子对象(不是说子类);再如游戏场景树中,物件实体作为子对象挂在场景根节点上,物件实体又可以挂子物件。对于这样的结构,可以将子对象的释放操作交给父对象来完成。这样,只要树状结构中一个节点不再使用(即需要被移除并释放),那么这个节点连同其后代,就可以以一种遍历子树的方式,全部被释放掉。</p>
<p>很多时候,对象之间的关系,并没有呈现出一种像树状的规则的结构。这些对象被不同模块所持有,模块之间关系往往是平行的。那么如何在不制造耦合的情况下,减轻内存释放的难度?这就可以使用引用计数技术了:每次申请一块内存,将这块内存的计数器标为1;每次对这块内存做引用传递时,将这块内存的计数器加1;每次对这块内存做解引用时,将计数器减1;当计数器减到0时,就可以真正释放这块内存了。这样,是否需要释放,是在引用计数器内部实现的,和具体的引用对象的模块无关,是正交的,不存在增加模块间耦合的问题。</p>
<p>对于最简单的引用计数器实现来说,加一、减一操作,往往要我们手工进行。即使将加一、减一隐藏在构造、析构、赋值运算符中,依然需要我们手工写释放操作来进一步触发减一机制。要解决这一问题,我们可以引入智能指针技术:栈对象在出作用域时会自动触发析构,智能指针对象其实就是一种栈对象,它能自动触发减一操作。实际上,新的C++1x的std::shared_ptr,就是一种结合了引用计数的高级智能指针。</p>
<p>引用计数技术有个问题,就是一旦对象之间出现循环引用的关系时,就会出问题,无法正常的释放。因为对象的引用计数,由于循环引用的存在,无法降为0。这时需要手工将循环引用关系打断,才能正常的释放。而在真实的编码中,我们会极力避免循环引用的出现,所依赖的手段是好的程序结构。</p>
<p>对于C/C++的内存管理,一般不谈论GC垃圾回收。我不把引用计数归为GC,这里说的GC是指使用标记法之类的那种GC。它们普遍比引用计数低效一些,C/C++这种比较偏向执行效率的语言,人们不太将它与GC一起谈。虽然C/C++也是有GC库可用的,但是一旦使用GC库,GC库分配的内存,就必须由GC库管理,且缺乏语言的语法支持。至于细节,不同的GC库使用不同的算法,和那么支持GC的语言并没有太多本质上的区别,其特点可以参考其它语言。GC对比引用计数机制,有一个好处,就是不担心循环引用。</p>
<p>对于使用什么手段来解决内存管理问题,不能一概而论,没有方法是最优的。优化程序结构,可以在大的结构节点,使用手工申请、释放的方式控制内存;可以在系统逻辑主干部分,设计结构性强的对象关系,系统地进行对象的释放。不过度设计,减少不必要的机制,比如不滥用Lazy初始化,固化对象生命周期的次序,减少发生循环引用的可能。需要我们编写复杂代码的地方,应该是对性能有严格要求的地方。但即使是游戏编程,也不是所有地方都对性能有强烈的需求。对于那些真正有需求的地方,我们则采取一些特别的手段来管理内存,且要付出的代价不仅仅在编码实现上,还需要有足量的测试,一切根据实际出发。而多数地方,我们要采用现成的、性价比好的方法。</p></content><category term="programming"></category><category term="面试"></category><category term="内存管理"></category><category term="内存池"></category></entry><entry><title>✔ JUST DO IT</title><link href="https://funcman.me/just_do_it.html" rel="alternate"></link><published>2016-07-12T17:07:07+08:00</published><updated>2016-07-12T17:07:07+08:00</updated><author><name>funcman</name></author><id>tag:funcman.me,2016-07-12:/just_do_it.html</id><summary type="html"><p>最近辞职了,准备回游戏业,在找工作。</p>
<p>其实游戏业给我的,是一种失败感,尤其最近。比如斧子的惨状。再如手游审批这事,其实 …</p></summary><content type="html"><p>最近辞职了,准备回游戏业,在找工作。</p>
<p>其实游戏业给我的,是一种失败感,尤其最近。比如斧子的惨状。再如手游审批这事,其实我对这块不感冒,但也有殃及池鱼的感觉。总结起来,就是一个没劲。主机解禁没带来多少改变,现在又闹这一出,一代又一代的人深陷魔掌无法脱出。</p>
<p>特佩服也特感谢一些没心没肺的观点——自己做好了不怕外界环境恶劣。这话是对的,比如放在斧子游戏机这事上,不是大家不挺你,是有些事你自己作死。能这么想的人,都是内心坚毅的人。而且,作为弱者,不干了这碗鸡汤,还能怎样。</p>
<p>以前老大批评我想太多,我很认同。业内如何,轮不上我这样的去想,想了也没用。我是程序员,不专注在自己手艺上是不行的。你看老罗,虽然手机做得还行,至少做出来了,但是总给人一种只会耍嘴炮的感觉,间接也耽误了锤子的行情。所以呢,我还是只专注于技术,不然哪一天风云幻变,做出中国巫师、中国神海的人里就没有我了(笑)。</p>
<p>十岁想做游戏,二十岁在做游戏,三十岁再回来。觉得对的事,就去做吧。</p>
<p><img alt="JUSE FUCKING DO IT" src="https://funcman.me/images/just_fucking_do_it.png"></p></content><category term="gamedev"></category><category term="职业"></category><category term="事业"></category><category term="工作"></category></entry><entry><title>如何在OSX El Capitan下使用基于CH34x芯片的USB串口适配线</title><link href="https://funcman.me/how_to_use_ch34x_usb_to_serial_adapter_on_osx_el_capitan.html" rel="alternate"></link><published>2015-12-10T07:47:23+08:00</published><updated>2015-12-10T07:47:23+08:00</updated><author><name>funcman</name></author><id>tag:funcman.me,2015-12-10:/how_to_use_ch34x_usb_to_serial_adapter_on_osx_el_capitan.html</id><summary type="html"><p>淘宝上有很多廉价的USB转串口的适配线,这其中不少使用的是南京的沁恒公司的CH34x芯片。</p>
<p>我们可以在“关于本机”-“系统报告”-“硬件”-“USB”里查看适配线 …</p></summary><content type="html"><p>淘宝上有很多廉价的USB转串口的适配线,这其中不少使用的是南京的沁恒公司的CH34x芯片。</p>
<p>我们可以在“关于本机”-“系统报告”-“硬件”-“USB”里查看适配线的VID(厂商ID),来确定适配线是否基于CH34x芯片。CH34x的VID是0x1a86,我这根的PID(产品ID)是0x7523,你的PID可能不同,0x5523也是一个可用的PID。</p>
<p>确定芯片后,就可以去找驱动程序了。沁恒的网址是<a href="http://www.wch.cn">http://www.wch.cn</a>,我们可以从上面找到我们需要的驱动。<a href="http://www.wch.cn/download/CH341SER_MAC_ZIP.html">Mac的驱动也是有的</a>,不过太老了,未经签名,所以通过不了苹果在OSX 10.11上加入的SIP<sup id="fnref:1"><a class="footnote-ref" href="#fn:1">1</a></sup>保护技术。如果直接安装这个驱动,肯定是用不了的。必须屏蔽SIP对kext<sup id="fnref:2"><a class="footnote-ref" href="#fn:2">2</a></sup>的保护。</p>
<p>设置SIP,我们需要用到csrutil,它就是SIP的配置修改工具。使用csrutil,必须进入恢复模式。恢复模式的进入方法是:开机(重启)时,同时按CMD键和R键。进入恢复模式之后,在“实用工具”里找到“终端”,在终端里输入:</p>
<div class="highlight"><pre><span></span><code>csrutil enable --without kext
</code></pre></div>
<p>然后重启。这时再去安装从沁恒公司网站下载的那个Mac驱动。安装完成后,再次重启,进入系统之后就能在<strong>/dev</strong>下面找到<strong>tty.wchusbserial14130</strong>了。注意,因为我的PID是0x7523,所以tty.wchusbserial后面的数字是14130(即0x7523的十进制数)。你也可能看到别的类似的设备文件名。</p>
<p>这样操作之后,就能正常使用CH34x适配线了。但是也丧失了SIP对系统的某些保护,降低了系统的安全性。如果手头宽裕,还是建议购买贵一些的,持续支持Mac系统的适配线。购买之前,先搞清楚要买的线能不能很方便的在最新的Mac上使用。不要像我,买的时候没花时间直接下单,到货了折腾半天才用起来。</p>
<div class="footnote">
<hr>
<ol>
<li id="fn:1">
<p>System Integrity Protection&#160;<a class="footnote-backref" href="#fnref:1" title="Jump back to footnote 1 in the text">&#8617;</a></p>
</li>
<li id="fn:2">
<p>OSX的内核扩展,驱动一般是以这种形式存在的&#160;<a class="footnote-backref" href="#fnref:2" title="Jump back to footnote 2 in the text">&#8617;</a></p>
</li>
</ol>
</div></content><category term="programming"></category><category term="OSX El Capitan"></category><category term="CH34x"></category><category term="串口"></category></entry><entry><title>如何背单词</title><link href="https://funcman.me/how_to_remember_english_words.html" rel="alternate"></link><published>2015-11-25T06:44:07+08:00</published><updated>2015-11-25T06:44:07+08:00</updated><author><name>funcman</name></author><id>tag:funcman.me,2015-11-25:/how_to_remember_english_words.html</id><summary type="html"><p>我是个英语很烂的人,所有关于英语的能力,都来自于技术工作中对英文的不得不进行的接触。因为英语太烂,也失去了一 …</p></summary><content type="html"><p>我是个英语很烂的人,所有关于英语的能力,都来自于技术工作中对英文的不得不进行的接触。因为英语太烂,也失去了一些不错的机会。怎么学英语,我是不懂的。至于背单词,大多数英语好的人,应该都去尝试过。我也尝试过,但一直不得其法。</p>
<p>现在,为了完成学业,我又准备拿起单词书,再次尝试一下。在开始做这件事时,我想了一下,到底是什么阻碍了我之前在背单词上的持续努力?</p>
<p>我不是没有目的的去学习一个东西,相反,学好了英语对我来说有很多积极的意义。在我内心里,是有学习的冲动的。所以,之前的不顺利,应该是方法上出了问题。</p>
<p>背单词是一种记忆活动,在记忆领域,人们有一个普遍的共识,就是人脑记忆新事物遵循着“艾宾浩斯记忆曲线”。我之前也采用了遵循这种规律的记忆复习方法,相信在这类方法的应用上,没有什么问题。</p>
<p>在还没有想清楚这个问题的时候,我还在找背单词的工具。幸运的是我找到了一个叫<a href="https://itunes.apple.com/app/id888483369/">墨墨背单词</a>的手机应用,帮助我弄清了这个问题。</p>
<p>人有一种既贪婪又懦弱的通性。人做一件事情,是由欲望驱使的,欲望总是使人在得到的同时还想得到更多。人的能力有限,无法驾驭所有,当欲望发展成贪婪,无法得到完全的满足时,人的懦弱的本性就开始产生作用:放弃掉全部追求。</p>
<p>这就是贪婪导致毁灭的道理。在之前的背单词过程中,我渴望着把每一个单词记住记牢,用法、特例、近义词、反义词等等通通收入脑子里。实际上我做不到,也没有产生什么益处。这样背完一百个单词后,我感觉不到任何提高。我觉得自己在做没有意义的事,于是放弃就成了轻易且自然的事情。</p>
<p>所有智慧都在说一件事,克制住欲望。背单词也一样,我不需要记住那么多,甚至不需要记住每一个单词的拼写。我只需要对这些单词有种“我接触过”的印象,就能在英文阅读中有<strong>能力提升的感觉</strong>。然后,当我发现某些单词需要经常书写时,再去记住它们的拼写。如此循序渐进,总能获取到学习的正面反馈,也就有了不断坚持下去的<strong>动力</strong>。</p>
<p>在<a href="https://itunes.apple.com/app/id888483369/">墨墨背单词</a>这个应用中,“记住一个单词”的定义是<strong>由用户决定</strong>的。用户只需要告诉应用,对于某个单词,在一次测试过程中,是记住了还是忘记了,或是模模糊糊记不清。然后应用根据用户的记忆反馈,为用户提供这个单词在下次出现的时机和频率。至于用户怎么定义“记住了”,完全由用户根据自己追求的目标进行判断。对于我来说,看到一个英文单词,能想到它的中文意思,那就是记住了。就这么简单。</p></content><category term="others"></category><category term="英语"></category><category term="背单词"></category></entry><entry><title>如何正确编写苹果健康程序</title><link href="https://funcman.me/how_to_save_data_to_apple_health_correctly.html" rel="alternate"></link><published>2015-10-27T10:45:00+08:00</published><updated>2015-10-27T10:45:00+08:00</updated><author><name>funcman</name></author><id>tag:funcman.me,2015-10-27:/how_to_save_data_to_apple_health_correctly.html</id><summary type="html"><p>苹果健康使用HealthKit接收数据的提交和读取,很多时候,我们从程序编写的角度,把两者看做同一个东西,下文简称HK。</p>
<p>HK在权限管理方面,有个很特(guai)别(yi)的地方,也是一 …</p></summary><content type="html"><p>苹果健康使用HealthKit接收数据的提交和读取,很多时候,我们从程序编写的角度,把两者看做同一个东西,下文简称HK。</p>
<p>HK在权限管理方面,有个很特(guai)别(yi)的地方,也是一个坑,即:读权限和写权限是完全分离的,存在可写但不可读的情况。由于权限控制权完全在用户手中,即使程序同时请求读写权限,也可能只是获得到写权限,而无读权限。这就造成一个问题:程序无法直接知道HK里已经存有哪些数据,如果不想办法规避这个坑,同一条数据有可能被重复写入HK。</p>
<p>为了避免重复写入数据,我们需要在APP端甚至云端的数据存储中,对每条需要同步到HK的数据,进行标记。这样,才能将HK的数据同步做到最佳。</p>
<p>我个人对HK本身的设计有个想法:HK应该自动把同一个数据源提交来的相同数据进行合并。如果几个数据,它们的起止时间,数值等,都是相同的,把它们看做同一个数据,是完全合理的。其实HK对于重复数据,是有一个融合算法的,并不会把数据进行累加。但是,用户点开“显示所有数据”时,看到有数据重复的情况,多多少少心里会有些膈应,会怪罪APP端。苹果对HK的数据隐私,看得格外重要,才设计出如此怪异的权限机制,牺牲了不少的可用性。</p></content><category term="programming"></category><category term="苹果健康"></category><category term="HealthKit"></category></entry><entry><title>谈一谈个人在接下来几年的游戏技术方向</title><link href="https://funcman.me/talk_about_my_next_interests_in_game_technologys.html" rel="alternate"></link><published>2015-01-08T14:46:00+08:00</published><updated>2015-01-08T14:46:00+08:00</updated><author><name>funcman</name></author><id>tag:funcman.me,2015-01-08:/talk_about_my_next_interests_in_game_technologys.html</id><summary type="html"><p>我对目前火爆的手游和之前火爆的端游、页游通通不感兴趣。除非项目非常nice,或者团队的人非常有趣,否则我不太会积极 …</p></summary><content type="html"><p>我对目前火爆的手游和之前火爆的端游、页游通通不感兴趣。除非项目非常nice,或者团队的人非常有趣,否则我不太会积极地回到游戏行业中。理想的状态是,我是一群酷爱游戏且专业的人的之一,做着一款看着就好玩的电视、电脑游戏。虽然Xbox ONE已经来中国了,PS4也要上市了,但是中国电视游戏的春天恐怕还要等上几年。所以,我考虑在这几年积攒一下技术。</p>
<p>所谓的积攒技术,就是做一个2D引擎,其实也就是造轮子。如果是即刻投身商业开发,那么造轮子,一定是件可耻的事情。Unity或者cocos2d-x已经足够好了。而这对于我来说,只是个人私事。理由不具体直接解释了,反正我就是爱折腾。</p>
<p>我个人很喜欢Unity的快捷和强大,但也感到如果Unity和cocos2d-x一样开源,很多事会好办很多。而对于后者,我不太喜欢它的设计。cocos2d吸收了很多Cocoa的特点,但对于一个游戏引擎来说,有些地方还是简洁点好。</p>
<p>对于一个2D引擎来说,图形底层并不复杂,只要把Sprite做出来,这个引擎就能用来做游戏了。一个成熟的2D引擎,必不可少要有一套动画系统。以前的2D游戏多采用序列帧,现在基本上是实时演算的关键帧插值动画。动画编辑器的开发量是不小的,我考虑使用Flash作为动画编辑器,用JSFL做数据导出。Flash可做的事情非常多,比如还可以作为UI布局器使用,甚至粒子特效编辑器也省了。使用通用编辑器来取代内部编辑器,是节省成本的一个好方法。</p>
<p>我个人是很喜欢Unity的,尤其是它的Editor,和Component系统。UnityEditor级别的开发,不是几个人能完成的,所以就不是我要借鉴的对象。我要学习的是Component系统。游戏是OO设计最经典的应用场景之一(另一个是GUI),Component系统是我见过的最好理解的游戏OOD。好理解意味着好用,开发快捷方便。</p>
<p>我考虑在这个引擎中深度应用Lua语言,而不是像我以前的项目那样,只是把Lua作为写回调功能的工具。我偏向C/C++只为Lua提供回调功能,单纯去解决真正的性能问题。引擎应用程序员是面向Lua工作的,C/C++只需要做好核心的即可。</p>
<p>以上说的是2D引擎技术,此外,我还对服务端技术感兴趣,这是我没有真正做过的,需要去常识的。我曾经了解过一段时间node.js,并且网易的一群人还弄出了一个Pomelo,但我不考虑node。JavaScript的爱好者很喜欢摆弄异步回调,node上也充满了这种风格的东西。但异步毕竟是个烧脑的东西,我更喜欢简单直接的。我对go和云风的skynet很感兴趣,尤其后者是我喜欢的“C+Lua”。</p></content><category term="gamedev"></category><category term="技术选型"></category></entry><entry><title>关于有理数、无理数中的“理”字</title><link href="https://funcman.me/about_logos_of_the_rational_number_and_irrational_number.html" rel="alternate"></link><published>2014-12-17T15:00:00+08:00</published><updated>2014-12-17T15:00:00+08:00</updated><author><name>funcman</name></author><id>tag:funcman.me,2014-12-17:/about_logos_of_the_rational_number_and_irrational_number.html</id><summary type="html"><p>我们知道有理数就是可以用整数比值表示的数字。但对于无理数,我们就不好下定义,只好说不能用整数比值表示的数字 …</p></summary><content type="html"><p>我们知道有理数就是可以用整数比值表示的数字。但对于无理数,我们就不好下定义,只好说不能用整数比值表示的数字就是无理数。</p>
<p>对于有理数、无理数中间这个“理”字,我们也不太容易理解,数字不具备位格,怎么还能讲道理。其实这两个名词,来自日文。我们知道,很多汉语里采用的日式外来词,和汉语字词意思多少有些出入,理解起来就会有点“无厘头”。</p>
<p>我在网上随便搜索了一下,据说这个日式外来词,在翻译时也受到了徐光启和利玛窦翻译的《几何原本》的影响。根据网上的说法,在徐、利版本的《几何原本》中,将比值作为“理”字翻译出来。但是我不太确信这样的解释,我不清楚文言文中的“理”字可作比值的意思。</p>
<p>我所知道的,这个“理”字,在最早希腊文中,是用logos表示。logos大概就是事物本质规律的意思,也有话语、可被语言表达的事物的意思。圣经里,面向希腊语地区读者的《约翰福音》,用logos来关联上帝的话语,中文版本圣经里翻作“道”字。而拉丁文,logos被转译为ratio,也就是英文中的比值和rational(可理解的)的词根。我不知道logos有没有比值的意思,retio既有比值的意思,也有可被理解的意思。</p>
<p>所以,至少我们可以把有理数和无理数,在字面上理解为可以理解的数和不可理解的数,这样多少可以说得通。</p>
<p>无论在圣经中,还是在发现有理数的古希腊毕达哥拉斯学派的眼里,logos都是一个高大上的词。所以,发现无理数这样不logos的数的希伯索斯,被认为亵渎了神圣,最后被狂热的毕门学者投入了地中海。当然,这和基督教没有关系,基督教是在其后几百年才有的。很多科学史上的迫害,问题不在于信仰什么,而是拜宗教狂热所赐,不管是基督教下的,还是这种拜科学教背景的。</p>
<p>乱七八糟讲了这么多,回过头来,我们还是很难通过字面和简单的语言,来提供一种本文第一段第二句之外的对无理数的定义或解释。摊手~</p></content><category term="mathematics"></category><category term="有理数"></category><category term="数学"></category></entry><entry><title>关于std::list的排序</title><link href="https://funcman.me/about_sqrt_of_std_list.html" rel="alternate"></link><published>2010-09-04T12:58:00+08:00</published><updated>2010-09-04T12:58:00+08:00</updated><author><name>funcman</name></author><id>tag:funcman.me,2010-09-04:/about_sqrt_of_std_list.html</id><summary type="html"><p>std::list和std::vector不同,无法使用STL算法库里的sort(),因为std::list不支持随机访问(random access)。</p>
<div class="highlight"><pre><span></span><code><span class="c1">// std::list排序示例</span>
<span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;stdio.h&gt;</span>
<span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;stdlib.h&gt;</span>
<span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;time.h&gt;</span>
<span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;list&gt;</span>
<span class="c1">// 它是个仿函数</span>
<span class="k">struct</span><span class="w"> </span><span class="nc">mycmp</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kt">bool</span><span class="w"> </span><span class="nf">operator</span><span class="p">()(</span><span class="kt">int</span><span class="o">*</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">int</span><span class="o">*</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="o">*</span><span class="n">a</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="o">*</span><span class="n">b</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">};</span><span class="w"> </span>
<span class="kt">int</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">std</span><span class="o">::</span><span class="n">list</span><span class="w"> </span><span class="n">il</span><span class="p">;</span>
<span class="w"> </span><span class="kt">int …</span></code></pre></div></summary><content type="html"><p>std::list和std::vector不同,无法使用STL算法库里的sort(),因为std::list不支持随机访问(random access)。</p>
<div class="highlight"><pre><span></span><code><span class="c1">// std::list排序示例</span>
<span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;stdio.h&gt;</span>
<span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;stdlib.h&gt;</span>
<span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;time.h&gt;</span>
<span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;list&gt;</span>
<span class="c1">// 它是个仿函数</span>
<span class="k">struct</span><span class="w"> </span><span class="nc">mycmp</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kt">bool</span><span class="w"> </span><span class="nf">operator</span><span class="p">()(</span><span class="kt">int</span><span class="o">*</span><span class="w"> </span><span class="n">a</span><span class="p">,</span><span class="w"> </span><span class="kt">int</span><span class="o">*</span><span class="w"> </span><span class="n">b</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="o">*</span><span class="n">a</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="o">*</span><span class="n">b</span><span class="p">;</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">};</span><span class="w"> </span>
<span class="kt">int</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">std</span><span class="o">::</span><span class="n">list</span><span class="w"> </span><span class="n">il</span><span class="p">;</span>
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">ia</span><span class="p">[</span><span class="mi">10</span><span class="p">];</span>
<span class="w"> </span><span class="kt">time_t</span><span class="w"> </span><span class="n">t</span><span class="p">;</span>
<span class="w"> </span><span class="n">time</span><span class="p">(</span><span class="o">&amp;</span><span class="n">t</span><span class="p">);</span>
<span class="w"> </span><span class="n">srand</span><span class="p">((</span><span class="kt">unsigned</span><span class="w"> </span><span class="kt">int</span><span class="p">)</span><span class="n">t</span><span class="p">);</span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">&quot;Before: &quot;</span><span class="p">);</span>
<span class="w"> </span><span class="k">for</span><span class="p">(</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="o">&lt;</span><span class="mi">10</span><span class="p">;</span><span class="w"> </span><span class="o">++</span><span class="n">i</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">ia</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">rand</span><span class="p">();</span>
<span class="w"> </span><span class="n">il</span><span class="p">.</span><span class="n">push_back</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ia</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">&quot;%d &quot;</span><span class="p">,</span><span class="w"> </span><span class="n">ia</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">&quot;</span><span class="se">\n</span><span class="s">&quot;</span><span class="p">);</span>
<span class="w"> </span><span class="n">il</span><span class="p">.</span><span class="n">sort</span><span class="p">(</span><span class="n">mycmp</span><span class="p">());</span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">&quot;After: &quot;</span><span class="p">);</span>
<span class="w"> </span><span class="n">std</span><span class="o">::</span><span class="n">list</span><span class="o">::</span><span class="n">iterator</span><span class="w"> </span><span class="n">i</span><span class="p">;</span>
<span class="w"> </span><span class="k">for</span><span class="p">(</span><span class="w"> </span><span class="n">i</span><span class="o">=</span><span class="n">il</span><span class="p">.</span><span class="n">begin</span><span class="p">();</span><span class="w"> </span><span class="n">i</span><span class="o">!=</span><span class="n">il</span><span class="p">.</span><span class="n">end</span><span class="p">();</span><span class="w"> </span><span class="o">++</span><span class="n">i</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">&quot;%d &quot;</span><span class="p">,</span><span class="w"> </span><span class="o">**</span><span class="n">i</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">&quot;</span><span class="se">\n</span><span class="s">&quot;</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></content><category term="programming"></category><category term="STL"></category><category term="list"></category><category term="容器"></category></entry><entry><title>一个Win32控制台类</title><link href="https://funcman.me/win32_console_class.html" rel="alternate"></link><published>2010-06-06T10:54:00+08:00</published><updated>2010-06-06T10:54:00+08:00</updated><author><name>funcman</name></author><id>tag:funcman.me,2010-06-06:/win32_console_class.html</id><summary type="html"><p>对于编写Win32程序,尤其是游戏这种实时性高的程序来说,断点调试未必是最合适的调试方法。很多时候,我们更渴望看到实时打 …</p></summary><content type="html"><p>对于编写Win32程序,尤其是游戏这种实时性高的程序来说,断点调试未必是最合适的调试方法。很多时候,我们更渴望看到实时打印的调试信息。</p>
<p>一种方法是使用VC的调试函数OutputDebugString(),将信息输出给VC调试控制台。还有一种方法是在Win32控制台工程的基础上创建窗口程序(入口为main而非WinMain)。可以用宏来控制编译,使Debug版本时有控制台,Release版本没有控制台,像这样:</p>
<div class="highlight"><pre><span></span><code><span class="c1">// 一个简单的方法</span>
<span class="cp">#ifndef _DEBUG</span>
<span class="kt">int</span><span class="w"> </span><span class="n">WINAPI</span><span class="w"> </span><span class="nf">WinMain</span><span class="p">(</span><span class="n">HINSTANCE</span><span class="w"> </span><span class="n">hInstance</span><span class="p">,</span><span class="w"> </span><span class="n">HINSTANCE</span><span class="w"> </span><span class="n">hPrevInstance</span><span class="p">,</span><span class="w"> </span><span class="n">LPSTR</span><span class="w"> </span><span class="n">lpCmdLine</span><span class="p">,</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">nCmdShow</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="cp">#else</span>
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">main</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">argc</span><span class="p">,</span><span class="w"> </span><span class="kt">char</span><span class="o">**</span><span class="w"> </span><span class="n">argv</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="cp">#endif</span><span class="c1">//_DEBUG</span>
</code></pre></div>
<p>但这种方法并不灵活,这里介绍一个直接用Win32 API获得控制台的方法。一切皆在代码中:</p>
<div class="highlight"><pre><span></span><code><span class="c1">// DbgCon.h</span>
<span class="cp">#ifndef DBGCON_H</span>
<span class="cp">#define DBGCON_H</span>
<span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;windows.h&gt;</span><span class="c1">;</span>
<span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;stdio.h&gt;</span><span class="c1">;</span>
<span class="k">class</span><span class="w"> </span><span class="nc">DbgCon</span><span class="w"> </span><span class="p">{</span>
<span class="k">public</span><span class="o">:</span>
<span class="w"> </span><span class="n">DbgCon</span><span class="p">();</span>
<span class="w"> </span><span class="o">~</span><span class="n">DbgCon</span><span class="p">();</span>
<span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">Show</span><span class="p">();</span>
<span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">Hide</span><span class="p">();</span>
<span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">Top</span><span class="p">();</span>
<span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">NoTop</span><span class="p">();</span>
<span class="k">private</span><span class="o">:</span>
<span class="w"> </span><span class="n">HWND</span><span class="w"> </span><span class="n">HWnd_</span><span class="p">;</span>
<span class="w"> </span><span class="kt">FILE</span><span class="o">*</span><span class="w"> </span><span class="n">ConIn_</span><span class="p">;</span>
<span class="w"> </span><span class="kt">FILE</span><span class="o">*</span><span class="w"> </span><span class="n">ConOut_</span><span class="p">;</span>
<span class="p">};</span>
<span class="cp">#endif</span><span class="c1">//DBGCON_H</span>
</code></pre></div>
<div class="highlight"><pre><span></span><code><span class="c1">// DbgCon.cpp</span>
<span class="cp">#include</span><span class="w"> </span><span class="cpf">&quot;DbgCon.h&quot;</span>
<span class="n">DbgCon</span><span class="o">::</span><span class="n">DbgCon</span><span class="p">()</span>
<span class="o">:</span><span class="w"> </span><span class="n">HWnd_</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="p">,</span><span class="w"> </span><span class="n">ConIn_</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span>
<span class="p">,</span><span class="w"> </span><span class="n">ConOut_</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="c1">// 申请一个控制台</span>
<span class="w"> </span><span class="k">if</span><span class="p">(</span><span class="w"> </span><span class="n">AllocConsole</span><span class="p">()</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">HWnd_</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">GetConsoleWindow</span><span class="p">();</span>
<span class="w"> </span><span class="c1">// 把管道转入该控制台</span>
<span class="w"> </span><span class="n">freopen_s</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ConIn_</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;CONIN$&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;r+t&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">stdin</span><span class="p">);</span>
<span class="w"> </span><span class="n">freopen_s</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ConOut_</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;CONOUT$&quot;</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;w+t&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">stdout</span><span class="p">);</span>
<span class="w"> </span><span class="c1">// 去掉控制台窗口上的关闭功能</span>
<span class="w"> </span><span class="c1">//(控制台窗口上的关闭操作会关掉整个进程)</span>
<span class="w"> </span><span class="n">HMENU</span><span class="w"> </span><span class="n">HMenu</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">GetSystemMenu</span><span class="p">(</span><span class="n">HWnd_</span><span class="p">,</span><span class="w"> </span><span class="n">FALSE</span><span class="p">);</span>
<span class="w"> </span><span class="n">RemoveMenu</span><span class="p">(</span><span class="n">HMenu</span><span class="p">,</span><span class="w"> </span><span class="n">SC_CLOSE</span><span class="p">,</span><span class="w"> </span><span class="n">MF_BYCOMMAND</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">}</span>
<span class="n">DbgCon</span><span class="o">::~</span><span class="n">DbgCon</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">FreeConsole</span><span class="p">();</span>
<span class="w"> </span><span class="k">if</span><span class="p">(</span><span class="w"> </span><span class="n">ConOut_</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">fclose</span><span class="p">(</span><span class="n">ConOut_</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="k">if</span><span class="p">(</span><span class="w"> </span><span class="n">ConIn_</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">fclose</span><span class="p">(</span><span class="n">ConIn_</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">}</span>
<span class="kt">void</span><span class="w"> </span><span class="n">DbgCon</span><span class="o">::</span><span class="n">Show</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="c1">// 显示控制台窗口(恢复到正常状态)</span>
<span class="w"> </span><span class="n">ShowWindow</span><span class="p">(</span><span class="n">HWnd_</span><span class="p">,</span><span class="w"> </span><span class="n">SW_SHOWNORMAL</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">void</span><span class="w"> </span><span class="n">DbgCon</span><span class="o">::</span><span class="n">Hide</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="c1">// 隐藏控制台窗口</span>
<span class="w"> </span><span class="n">ShowWindow</span><span class="p">(</span><span class="n">HWnd_</span><span class="p">,</span><span class="w"> </span><span class="n">SW_HIDE</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">void</span><span class="w"> </span><span class="n">DbgCon</span><span class="o">::</span><span class="n">Top</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="c1">// 置顶控制台窗口</span>
<span class="w"> </span><span class="n">SetWindowPos</span><span class="p">(</span><span class="n">HWnd_</span><span class="p">,</span><span class="w"> </span><span class="n">HWND_TOPMOST</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="n">SWP_NOMOVE</span><span class="o">||</span><span class="n">SWP_NOSIZE</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">void</span><span class="w"> </span><span class="n">DbgCon</span><span class="o">::</span><span class="n">NoTop</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="c1">// 取消置顶</span>
<span class="w"> </span><span class="n">SetWindowPos</span><span class="p">(</span><span class="n">HWnd_</span><span class="p">,</span><span class="w"> </span><span class="n">HWND_NOTOPMOST</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="n">SWP_NOMOVE</span><span class="o">||</span><span class="n">SWP_NOSIZE</span><span class="p">);</span><span class="w"> </span>
<span class="p">}</span>
</code></pre></div></content><category term="programming"></category><category term="C++"></category><category term="Win32"></category><category term="控制台"></category></entry><entry><title>C语言对数据的隐藏封装</title><link href="https://funcman.me/data_hiding_in_c.html" rel="alternate"></link><published>2010-05-16T19:48:00+08:00</published><updated>2010-05-16T19:48:00+08:00</updated><author><name>funcman</name></author><id>tag:funcman.me,2010-05-16:/data_hiding_in_c.html</id><summary type="html"><p>在C++中,如果我们想把数据结构隐藏起来,只为用户提供接口,可以通过实现继承接口类的方式到达目的。那C中,可以采用什 …</p></summary><content type="html"><p>在C++中,如果我们想把数据结构隐藏起来,只为用户提供接口,可以通过实现继承接口类的方式到达目的。那C中,可以采用什么方式呢。下面有一例:</p>
<div class="highlight"><pre><span></span><code><span class="c1">// demo.h</span>
<span class="cp">#ifndef DEMO_H</span>
<span class="cp">#define DEMO_H</span>
<span class="k">typedef</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">Demo</span><span class="o">*</span><span class="w"> </span><span class="n">Demo</span><span class="p">;</span>
<span class="k">extern</span><span class="w"> </span><span class="n">Demo</span><span class="w"> </span><span class="nf">DemoCreate</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">num</span><span class="p">);</span>
<span class="k">extern</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">DemoDestory</span><span class="p">(</span><span class="n">Demo</span><span class="w"> </span><span class="n">demo</span><span class="p">);</span>
<span class="k">extern</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">DemoShow</span><span class="p">(</span><span class="n">Demo</span><span class="w"> </span><span class="n">demo</span><span class="p">);</span>
<span class="cp">#endif</span><span class="c1">//DEMO_H</span>
</code></pre></div>
<div class="highlight"><pre><span></span><code><span class="c1">// demo.c</span>
<span class="cp">#include</span><span class="w"> </span><span class="cpf">&quot;demo.h&quot;</span>
<span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;stdio.h&gt;</span>
<span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;stdlib.h&gt;</span>
<span class="k">typedef</span><span class="w"> </span><span class="k">struct</span><span class="w"> </span><span class="nc">Demo_</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">num_</span><span class="p">;</span>
<span class="p">}</span><span class="w"> </span><span class="n">Demo_</span><span class="p">;</span>
<span class="n">Demo</span><span class="w"> </span><span class="nf">DemoCreate</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">num</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">Demo_</span><span class="o">*</span><span class="w"> </span><span class="n">demo</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">Demo_</span><span class="o">*</span><span class="p">)</span><span class="n">malloc</span><span class="p">(</span><span class="k">sizeof</span><span class="p">(</span><span class="n">Demo_</span><span class="p">));</span>
<span class="w"> </span><span class="n">demo</span><span class="o">-&gt;</span><span class="n">num_</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">num</span><span class="p">;</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="p">(</span><span class="n">Demo</span><span class="p">)</span><span class="n">demo</span><span class="p">;</span>
<span class="p">}</span>
<span class="kt">void</span><span class="w"> </span><span class="nf">DemoDestory</span><span class="p">(</span><span class="n">Demo</span><span class="w"> </span><span class="n">demo</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">free</span><span class="p">(</span><span class="n">demo</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">void</span><span class="w"> </span><span class="nf">DemoShow</span><span class="p">(</span><span class="n">Demo</span><span class="w"> </span><span class="n">demo</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">&quot;%d</span><span class="se">\n</span><span class="s">&quot;</span><span class="p">,</span><span class="w"> </span><span class="p">((</span><span class="n">Demo_</span><span class="o">*</span><span class="p">)</span><span class="n">demo</span><span class="p">)</span><span class="o">-&gt;</span><span class="n">num_</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div>
<div class="highlight"><pre><span></span><code><span class="c1">// main.c</span>
<span class="cp">#include</span><span class="w"> </span><span class="cpf">&quot;demo.h&quot;</span>
<span class="kt">int</span><span class="w"> </span><span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">Demo</span><span class="w"> </span><span class="n">demo</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">DemoCreate</span><span class="p">(</span><span class="mi">777</span><span class="p">);</span>
<span class="w"> </span><span class="n">DemoShow</span><span class="p">(</span><span class="n">demo</span><span class="p">);</span>
<span class="w"> </span><span class="n">DemoDestory</span><span class="p">(</span><span class="n">demo</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></content><category term="programming"></category><category term="C语言"></category><category term="接口"></category><category term="封装"></category></entry><entry><title>艾未未作品《4851》观后感</title><link href="https://funcman.me/review_of_4851_by_ai_weiwei.html" rel="alternate"></link><published>2010-01-10T02:44:00+08:00</published><updated>2010-01-10T02:44:00+08:00</updated><author><name>funcman</name></author><id>tag:funcman.me,2010-01-10:/review_of_4851_by_ai_weiwei.html</id><summary type="html"><p>艾未未先生的《4851》是我看到的最深刻的艺术作品。</p>
<p>87分钟的片长,可能没几个人会真的花87分钟完整地看完这部片子。</p>
<p>没关系,你的观看方式已经是这部艺术作品 …</p></summary><content type="html"><p>艾未未先生的《4851》是我看到的最深刻的艺术作品。</p>
<p>87分钟的片长,可能没几个人会真的花87分钟完整地看完这部片子。</p>
<p>没关系,你的观看方式已经是这部艺术作品的一部分。</p>
<p>这是部完美的艺术作品,像一味催化剂。</p>
<p>只要你对5·12的任何一点有所思考,它都会从理性和感性两方面触发你。</p>
<p>你可以通过YouTube观看:<a href="https://www.youtube.com/watch?v=4IZpRHDOJpg">《4851》</a>(中国用户访问YouTube需要跨越GFW)</p></content><category term="others"></category><category term="艺术"></category><category term="艾未未"></category></entry><entry><title>动物保护的逻辑──小论《海豚湾》的政治正确性</title><link href="https://funcman.me/logic_of_animal_protectionism.html" rel="alternate"></link><published>2009-12-21T19:34:00+08:00</published><updated>2009-12-21T19:34:00+08:00</updated><author><name>funcman</name></author><id>tag:funcman.me,2009-12-21:/logic_of_animal_protectionism.html</id><summary type="html"><p>一、某些动物性情趋向于人,人们能从这些动物身上获得人道情感,保护这些动物是在维护人道主义。之所有争取一些动物 …</p></summary><content type="html"><p>一、某些动物性情趋向于人,人们能从这些动物身上获得人道情感,保护这些动物是在维护人道主义。之所有争取一些动物不作为牲畜屠宰,争取一些牲口在屠宰时更快死亡以减少受虐程度,是基于这样一个泛功利的动机。</p>
<p>二、野生动物是地球生态的重要组成部分,保护野生动物,是为了地球生态能够正常运行下去。地球生态的正常运作,才能让人们和他们的后台得以更好地生存。这也是一个功利的理由。</p>
<p>所以即使抛弃崇高的理念,以现实利益为名,动物保护主义也是完全站得住脚的。</p>
<p>《海豚湾》作为一部动物保护主义题材的环保纪录片,它的拍摄,是在遵从上面两个重要逻辑的前提下展开的。海豚保护组织最终在国际规则制定会议(法的层面)上,展出血淋淋的证据,给日本滥杀海豚的恶行以有力一击。这种行事方式十分成熟。</p></content><category term="others"></category><category term="电影"></category><category term="纪录片"></category><category term="动物保护"></category><category term="政治正确"></category></entry><entry><title>应该在何时判断一个指针是否为NULL</title><link href="https://funcman.me/when_should_determine_whether_a-pointer_is_null.html" rel="alternate"></link><published>2007-10-12T11:20:00+08:00</published><updated>2007-10-12T11:20:00+08:00</updated><author><name>funcman</name></author><id>tag:funcman.me,2007-10-12:/when_should_determine_whether_a-pointer_is_null.html</id><summary type="html"><p>今天看到一篇Blog:<a href="http://fsfoundry.org/codefreak/2007/09/16/a-couple-of-things-about-pointer-to-null/">《NULL指标两三事》</a>,文中谈的主题即是我这篇Blog的题目,我这里也只是对原文的做些简单的记录。</p>
<p>文中谈到的问题,也是我们平常 …</p></summary><content type="html"><p>今天看到一篇Blog:<a href="http://fsfoundry.org/codefreak/2007/09/16/a-couple-of-things-about-pointer-to-null/">《NULL指标两三事》</a>,文中谈的主题即是我这篇Blog的题目,我这里也只是对原文的做些简单的记录。</p>
<p>文中谈到的问题,也是我们平常出现的问题,即在操作一个指针前,我们常常在不需要判断指针是否为NULL时做了判断,而在需要判断时却想当然地把它漏掉了。</p>
<p>例如:delete时,完全可以对一个NULL指针进行操作,而我们却要多余地加上一个<code>if(ptr!=NULL)</code>判断;
又如:strlen时,如果参数给了一个NULL指针,就可能造成整个程序崩掉,我们却以为strlen自己会做NULL指针判读,并在获得一个NULL参数时return。</p>
<p>另外,在原文的评论栏中有一问一答,合理地解释了为什么strlen这样的接受指针参数的函数,会不做<code>if(NULL==ptr)return;</code>这样的操作。嗯,为了效率,这么做充分体验了C的精神。想像一下,对于一个错误的参数,检测函数的返回难道比在调用前就检测参数更省事么;何况如果同一个参数穿过层层的函数调用,一个个检测函数返回是多么可怕的事,不如在调用函数事先来个参数检测。实际上,像strlen这样的函数,也不是没有在函数内部做参数检测,只不过它用的是非常合理的assert断言机制,即满足了功能,又满足了效率。</p>
<p>以后再操作指针时,要记得有这么回事~</p></content><category term="programming"></category><category term="C"></category><category term="C++"></category><category term="指针"></category><category term="pointer"></category></entry></feed>