<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>技术人生 - 编程技术 - JESSE人生</title>
 <link href="http://www.luzexi.com/atom.xml" rel="self"/>
 <link href="http://www.luzexi.com/"/>
 <updated>2025-12-31T03:28:42+08:00</updated>
 <id>http://www.luzexi.com</id>
 <author>
   <name>Jesse Lu</name>
   <email>jesse_luzexi@163.com</email>
 </author>

 
 <entry>
   <title>《图解游戏引擎》新书即将出版</title>
   <link href="http://www.luzexi.com/2025/01/07/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E%E5%8D%B3%E5%B0%86%E5%87%BA%E7%89%88"/>
   <updated>2025-01-07T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2025/01/07/图解游戏引擎即将出版</id>
   <content type="html">&lt;h1 id=&quot;图解游戏引擎新书即将出版---深入浅出系统性学习游戏引擎的宝典&quot;&gt;《图解游戏引擎》新书即将出版 - 深入浅出，系统性学习游戏引擎的宝典！&lt;/h1&gt;

&lt;p&gt;本书荣获腾讯游戏的知识沉淀奖，并被收入腾讯游戏知识库，同时这个系列的文章在腾讯内部的阅读量排名第一，感谢对书籍的认可。书中汇聚了商业引擎Unity和虚幻引擎的关键技术框架结构以及作者的实战解剖，为读者提供了一站式的游戏引擎学习解决方案。通过这本书，你可以快速掌握游戏引擎的核心技术，为未来的游戏开发之路打下坚实的基础。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/GoijAWoznGNmZGIWmig1jg&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/GoijAWoznGNmZGIWmig1jg&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/GoijAWoznGNmZGIWmig1jg&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/engine1/face.jpeg&quot; alt=&quot;封面&quot; /&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>UWADay游戏行业开发者大会公开演讲 - 游戏性能分析与挖掘</title>
   <link href="http://www.luzexi.com/2024/09/11/WUADay%E6%B8%B8%E6%88%8F%E8%A1%8C%E4%B8%9A%E5%BC%80%E5%8F%91%E8%80%85%E5%A4%A7%E4%BC%9A%E5%85%AC%E5%BC%80%E6%BC%94%E8%AE%B2"/>
   <updated>2024-09-11T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2024/09/11/WUADay游戏行业开发者大会公开演讲</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/uahLUd2leerGB4CkN_tHwg&quot;&gt;发布宣传文章&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/p0-0RcxjPgatQahIcBsgfw&quot;&gt;UWADay大会官方回顾&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://edu.uwa4d.com/course-intro/1/583?entrance=3&quot;&gt;主题演讲视频录制&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - 引擎基础知识（5）字符串管理与容器</title>
   <link href="http://www.luzexi.com/2024/07/29/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-%E5%BC%95%E6%93%8E%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%865%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%AE%A1%E7%90%86%E4%B8%8E%E5%AE%B9%E5%99%A8"/>
   <updated>2024-07-29T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2024/07/29/图解游戏引擎-引擎基础知识5字符串管理与容器</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/M1Vd_tLz2TTTQthfD8KtQA&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/M1Vd_tLz2TTTQthfD8KtQA&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/M1Vd_tLz2TTTQthfD8KtQA&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - 引擎基础知识（4）修饰符、原子操作与内存屏障</title>
   <link href="http://www.luzexi.com/2024/07/22/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-%E5%BC%95%E6%93%8E%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%864%E4%BF%AE%E9%A5%B0%E7%AC%A6-%E5%8E%9F%E5%AD%90%E6%93%8D%E4%BD%9C-%E5%86%85%E5%AD%98%E5%B1%8F%E9%9A%9C"/>
   <updated>2024-07-22T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2024/07/22/图解游戏引擎-引擎基础知识4修饰符-原子操作-内存屏障</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/Wud6nl2wOzoXRUH52SiSOQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/Wud6nl2wOzoXRUH52SiSOQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/Wud6nl2wOzoXRUH52SiSOQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - 引擎基础知识（3）构造与重构</title>
   <link href="http://www.luzexi.com/2024/07/15/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-%E5%BC%95%E6%93%8E%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%863%E6%9E%84%E9%80%A0%E4%B8%8E%E9%87%8D%E6%9E%84"/>
   <updated>2024-07-15T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2024/07/15/图解游戏引擎-引擎基础知识3构造与重构</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/VBx5NP8eMlzeBUPi0NrFlQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/VBx5NP8eMlzeBUPi0NrFlQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/VBx5NP8eMlzeBUPi0NrFlQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - 引擎基础知识（2）编译链接</title>
   <link href="http://www.luzexi.com/2024/07/08/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-%E5%BC%95%E6%93%8E%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%862%E7%BC%96%E8%AF%91%E9%93%BE%E6%8E%A5"/>
   <updated>2024-07-08T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2024/07/08/图解游戏引擎-引擎基础知识2编译链接</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/DV0LFX7MA2Ma7QArE02cAg&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/DV0LFX7MA2Ma7QArE02cAg&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/DV0LFX7MA2Ma7QArE02cAg&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - 引擎基础知识（1）对象模型</title>
   <link href="http://www.luzexi.com/2024/06/25/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-%E5%BC%95%E6%93%8E%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%861%E5%AF%B9%E8%B1%A1%E6%A8%A1%E5%9E%8B"/>
   <updated>2024-06-25T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2024/06/25/图解游戏引擎-引擎基础知识1对象模型</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/wQPnR2fdETCwLLfjFcMjJg&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/wQPnR2fdETCwLLfjFcMjJg&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/wQPnR2fdETCwLLfjFcMjJg&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - Unity引擎渲染管线（4）渲染线程与Shader编译</title>
   <link href="http://www.luzexi.com/2024/06/12/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-Unity%E5%BC%95%E6%93%8E%E6%B8%B2%E6%9F%93%E7%AE%A1%E7%BA%BF4"/>
   <updated>2024-06-12T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2024/06/12/图解游戏引擎-Unity引擎渲染管线4</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/pfYIsqo9pHMrsUi6UkeFjg&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/pfYIsqo9pHMrsUi6UkeFjg&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/pfYIsqo9pHMrsUi6UkeFjg&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - Unity引擎渲染管线（3）动态合批原理与SRP</title>
   <link href="http://www.luzexi.com/2024/05/27/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-Unity%E5%BC%95%E6%93%8E%E6%B8%B2%E6%9F%93%E7%AE%A1%E7%BA%BF3"/>
   <updated>2024-05-27T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2024/05/27/图解游戏引擎-Unity引擎渲染管线3</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/uoOzaTXVQwvZOP9H-0Begw&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/uoOzaTXVQwvZOP9H-0Begw&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/uoOzaTXVQwvZOP9H-0Begw&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - Unity引擎渲染管线（2）</title>
   <link href="http://www.luzexi.com/2024/05/20/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-Unity%E5%BC%95%E6%93%8E%E6%B8%B2%E6%9F%93%E7%AE%A1%E7%BA%BF2"/>
   <updated>2024-05-20T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2024/05/20/图解游戏引擎-Unity引擎渲染管线2</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/NnqC518W2ejUlpFBB_jfTg&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/NnqC518W2ejUlpFBB_jfTg&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/NnqC518W2ejUlpFBB_jfTg&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - Unity引擎渲染管线（1）</title>
   <link href="http://www.luzexi.com/2024/05/09/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-Unity%E5%BC%95%E6%93%8E%E6%B8%B2%E6%9F%93%E7%AE%A1%E7%BA%BF1"/>
   <updated>2024-05-09T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2024/05/09/图解游戏引擎-Unity引擎渲染管线1</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/3w1DdRTH01iq3C4oNM8v1A&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/3w1DdRTH01iq3C4oNM8v1A&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/3w1DdRTH01iq3C4oNM8v1A&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - 虚幻引擎渲染管线（2）</title>
   <link href="http://www.luzexi.com/2024/04/28/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-%E8%99%9A%E5%B9%BB%E5%BC%95%E6%93%8E%E6%B8%B2%E6%9F%93%E7%AE%A1%E7%BA%BF2"/>
   <updated>2024-04-28T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2024/04/28/图解游戏引擎-虚幻引擎渲染管线2</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/6WH-OVBVu__K0abPstJpuw&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/6WH-OVBVu__K0abPstJpuw&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/6WH-OVBVu__K0abPstJpuw&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - 虚幻引擎渲染管线（1）</title>
   <link href="http://www.luzexi.com/2024/04/22/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-%E8%99%9A%E5%B9%BB%E5%BC%95%E6%93%8E%E6%B8%B2%E6%9F%93%E7%AE%A1%E7%BA%BF1"/>
   <updated>2024-04-22T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2024/04/22/图解游戏引擎-虚幻引擎渲染管线1</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/Az3bh5GBj4KxO76KXhxRiA&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/Az3bh5GBj4KxO76KXhxRiA&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/Az3bh5GBj4KxO76KXhxRiA&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - 渲染管线概述（2）</title>
   <link href="http://www.luzexi.com/2024/04/17/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-%E6%B8%B2%E6%9F%93%E7%AE%A1%E7%BA%BF%E6%A6%82%E8%BF%B02"/>
   <updated>2024-04-17T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2024/04/17/图解游戏引擎-渲染管线概述2</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/g9Z9QZFoYi_D2RGcfGNwew&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/g9Z9QZFoYi_D2RGcfGNwew&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/g9Z9QZFoYi_D2RGcfGNwew&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《如何精彩演讲》#11 - 我在中区赛中失误了</title>
   <link href="http://www.luzexi.com/2024/04/14/%E5%A6%82%E4%BD%95%E7%B2%BE%E5%BD%A9%E6%BC%94%E8%AE%B211-%E6%88%91%E5%9C%A8%E4%B8%AD%E5%8C%BA%E8%B5%9B%E4%B8%AD%E5%A4%B1%E8%AF%AF%E4%BA%86"/>
   <updated>2024-04-14T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2024/04/14/如何精彩演讲11-我在中区赛中失误了</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/pNamQraNGTqriUeffhUtEA&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/pNamQraNGTqriUeffhUtEA&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/pNamQraNGTqriUeffhUtEA&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - 渲染管线概述（1）</title>
   <link href="http://www.luzexi.com/2024/04/07/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-%E6%B8%B2%E6%9F%93%E7%AE%A1%E7%BA%BF%E6%A6%82%E8%BF%B01"/>
   <updated>2024-04-07T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2024/04/07/图解游戏引擎-渲染管线概述1</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/VdJekYsGuR2jc8F9UPypYQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/VdJekYsGuR2jc8F9UPypYQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/VdJekYsGuR2jc8F9UPypYQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《如何精彩演讲》#10 - 差一点获得冠军</title>
   <link href="http://www.luzexi.com/2024/04/01/%E5%B7%AE%E4%B8%80%E7%82%B9%E8%8E%B7%E5%BE%97%E5%86%A0%E5%86%9B"/>
   <updated>2024-04-01T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2024/04/01/差一点获得冠军</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/4d_9J9is8t2SgPh_Fy3MFQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/4d_9J9is8t2SgPh_Fy3MFQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/4d_9J9is8t2SgPh_Fy3MFQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - Unity引擎粒子系统原理（3）</title>
   <link href="http://www.luzexi.com/2024/03/19/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-Unity%E5%BC%95%E6%93%8E%E7%B2%92%E5%AD%90%E7%B3%BB%E7%BB%9F%E5%8E%9F%E7%90%863"/>
   <updated>2024-03-19T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2024/03/19/图解游戏引擎-Unity引擎粒子系统原理3</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/cWKFQtfKX9c19Pna7tgvlQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/cWKFQtfKX9c19Pna7tgvlQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/cWKFQtfKX9c19Pna7tgvlQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - Unity引擎粒子系统原理（2）</title>
   <link href="http://www.luzexi.com/2024/03/10/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-Unity%E5%BC%95%E6%93%8E%E7%B2%92%E5%AD%90%E7%B3%BB%E7%BB%9F%E5%8E%9F%E7%90%862"/>
   <updated>2024-03-10T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2024/03/10/图解游戏引擎-Unity引擎粒子系统原理2</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/Uqfez9_JjPANM_P6f4XBrQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/Uqfez9_JjPANM_P6f4XBrQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/Uqfez9_JjPANM_P6f4XBrQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - Unity引擎粒子系统原理（1）</title>
   <link href="http://www.luzexi.com/2024/02/20/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-Unity%E5%BC%95%E6%93%8E%E7%B2%92%E5%AD%90%E7%B3%BB%E7%BB%9F%E5%8E%9F%E7%90%861"/>
   <updated>2024-02-20T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2024/02/20/图解游戏引擎-Unity引擎粒子系统原理1</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/Wah0ngevoUTUiHoM6xQd6w&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/Wah0ngevoUTUiHoM6xQd6w&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/Wah0ngevoUTUiHoM6xQd6w&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - 虚幻引擎粒子系统（2）</title>
   <link href="http://www.luzexi.com/2024/02/02/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-%E8%99%9A%E5%B9%BB%E5%BC%95%E6%93%8E%E7%B2%92%E5%AD%90%E7%B3%BB%E7%BB%9F2"/>
   <updated>2024-02-02T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2024/02/02/图解游戏引擎-虚幻引擎粒子系统2</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/O6Q9Prfu9oU0XhnUQA6Jjg&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/O6Q9Prfu9oU0XhnUQA6Jjg&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/O6Q9Prfu9oU0XhnUQA6Jjg&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - 虚幻引擎粒子系统（1）</title>
   <link href="http://www.luzexi.com/2024/01/22/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-%E8%99%9A%E5%B9%BB%E5%BC%95%E6%93%8E%E7%B2%92%E5%AD%90%E7%B3%BB%E7%BB%9F1"/>
   <updated>2024-01-22T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2024/01/22/图解游戏引擎-虚幻引擎粒子系统1</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/WuLQCSNwlubUHpnmP58VlA&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/WuLQCSNwlubUHpnmP58VlA&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/WuLQCSNwlubUHpnmP58VlA&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - 粒子系统原理</title>
   <link href="http://www.luzexi.com/2024/01/04/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-%E7%B2%92%E5%AD%90%E7%B3%BB%E7%BB%9F%E5%8E%9F%E7%90%86"/>
   <updated>2024-01-04T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2024/01/04/图解游戏引擎-粒子系统原理</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/f5REzvfMYDM1KftSKX6QXQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/f5REzvfMYDM1KftSKX6QXQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/f5REzvfMYDM1KftSKX6QXQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《如何精彩演讲》#9 - 冠军演讲的心路历程</title>
   <link href="http://www.luzexi.com/2024/01/01/%E5%86%A0%E5%86%9B%E6%BC%94%E8%AE%B2%E7%9A%84%E5%BF%83%E8%B7%AF%E5%8E%86%E7%A8%8B"/>
   <updated>2024-01-01T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2024/01/01/冠军演讲的心路历程</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/BfaPWDqNMol7OP3qtPbO-w&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/BfaPWDqNMol7OP3qtPbO-w&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/BfaPWDqNMol7OP3qtPbO-w&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - Unity引擎虚拟机技术（3）</title>
   <link href="http://www.luzexi.com/2023/12/28/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-Unity%E5%BC%95%E6%93%8E%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%8A%80%E6%9C%AF3"/>
   <updated>2023-12-28T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2023/12/28/图解游戏引擎-Unity引擎虚拟机技术3</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/GRmUV0UTXm2krfSff4iuKw&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/GRmUV0UTXm2krfSff4iuKw&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/GRmUV0UTXm2krfSff4iuKw&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - Unity引擎虚拟机技术（2）</title>
   <link href="http://www.luzexi.com/2023/12/20/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-Unity%E5%BC%95%E6%93%8E%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%8A%80%E6%9C%AF2"/>
   <updated>2023-12-20T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2023/12/20/图解游戏引擎-Unity引擎虚拟机技术2</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/w7-Ae3_JCSmsSDgO_kZTZw&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/w7-Ae3_JCSmsSDgO_kZTZw&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/w7-Ae3_JCSmsSDgO_kZTZw&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - Unity引擎虚拟机技术（1）</title>
   <link href="http://www.luzexi.com/2023/12/11/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-Unity%E5%BC%95%E6%93%8E%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%8A%80%E6%9C%AF1"/>
   <updated>2023-12-11T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2023/12/11/图解游戏引擎-Unity引擎虚拟机技术1</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/xvvDgyo_FiLJTXtqy5QwzQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/xvvDgyo_FiLJTXtqy5QwzQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/xvvDgyo_FiLJTXtqy5QwzQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - 虚幻引擎虚拟机技术（3）</title>
   <link href="http://www.luzexi.com/2023/12/03/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-%E8%99%9A%E5%B9%BB%E5%BC%95%E6%93%8E%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%8A%80%E6%9C%AF3"/>
   <updated>2023-12-03T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2023/12/03/图解游戏引擎-虚幻引擎虚拟机技术3</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/YipTeTw-T7dSwvhS5a5JLA&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/YipTeTw-T7dSwvhS5a5JLA&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/YipTeTw-T7dSwvhS5a5JLA&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - 虚幻引擎虚拟机技术（2）</title>
   <link href="http://www.luzexi.com/2023/11/25/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-%E8%99%9A%E5%B9%BB%E5%BC%95%E6%93%8E%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%8A%80%E6%9C%AF2"/>
   <updated>2023-11-25T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2023/11/25/图解游戏引擎-虚幻引擎虚拟机技术2</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/1FdaNvxZv2jP0kw0PutTqA&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/1FdaNvxZv2jP0kw0PutTqA&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/1FdaNvxZv2jP0kw0PutTqA&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - 虚幻引擎虚拟机技术（1）</title>
   <link href="http://www.luzexi.com/2023/11/10/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-%E8%99%9A%E5%B9%BB%E5%BC%95%E6%93%8E%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%8A%80%E6%9C%AF1"/>
   <updated>2023-11-10T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2023/11/10/图解游戏引擎-虚幻引擎虚拟机技术1</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/MLFO2mhYSki5plD6g_rcig&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/MLFO2mhYSki5plD6g_rcig&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/MLFO2mhYSki5plD6g_rcig&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - 虚拟机技术（3）Lua</title>
   <link href="http://www.luzexi.com/2023/10/22/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%8A%80%E6%9C%AF3"/>
   <updated>2023-10-22T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2023/10/22/图解游戏引擎-虚拟机技术3</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/Y2RPVnuI0r8oBXq-4MAymw&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/Y2RPVnuI0r8oBXq-4MAymw&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/Y2RPVnuI0r8oBXq-4MAymw&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - 虚拟机技术（2）</title>
   <link href="http://www.luzexi.com/2023/09/27/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%8A%80%E6%9C%AF2"/>
   <updated>2023-09-27T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2023/09/27/图解游戏引擎-虚拟机技术2</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/C49uRj28IrO8Fs14fSg8WQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/C49uRj28IrO8Fs14fSg8WQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/C49uRj28IrO8Fs14fSg8WQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - 虚拟机技术（1）</title>
   <link href="http://www.luzexi.com/2023/08/25/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%8A%80%E6%9C%AF1"/>
   <updated>2023-08-25T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2023/08/25/图解游戏引擎-虚拟机技术1</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/_EKZVd_qTpLgeyUbOP6B3g&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/_EKZVd_qTpLgeyUbOP6B3g&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/_EKZVd_qTpLgeyUbOP6B3g&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - Unity引擎的UGUI原理（3）</title>
   <link href="http://www.luzexi.com/2023/08/10/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-Unity%E7%9A%84UGUI%E5%8E%9F%E7%90%863"/>
   <updated>2023-08-10T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2023/08/10/图解游戏引擎-Unity的UGUI原理3</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/uNjEDBMgsKNYnAWlMew8Ww&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/uNjEDBMgsKNYnAWlMew8Ww&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/uNjEDBMgsKNYnAWlMew8Ww&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - Unity引擎的UGUI原理（2）</title>
   <link href="http://www.luzexi.com/2023/07/26/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-Unity%E7%9A%84UGUI%E5%8E%9F%E7%90%862"/>
   <updated>2023-07-26T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2023/07/26/图解游戏引擎-Unity的UGUI原理2</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/uW1uQYM6ria0i17dKIcupQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/uW1uQYM6ria0i17dKIcupQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/uW1uQYM6ria0i17dKIcupQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - Unity引擎的UGUI原理（1）</title>
   <link href="http://www.luzexi.com/2023/07/15/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-Unity%E7%9A%84UGUI%E5%8E%9F%E7%90%861"/>
   <updated>2023-07-15T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2023/07/15/图解游戏引擎-Unity的UGUI原理1</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/-IJCosaBw_GLGY1KQxE68A&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/-IJCosaBw_GLGY1KQxE68A&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/-IJCosaBw_GLGY1KQxE68A&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - Unreal引擎UI框架解析（3）</title>
   <link href="http://www.luzexi.com/2023/07/01/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-Unreal%E5%BC%95%E6%93%8EUI%E6%A1%86%E6%9E%B6%E8%A7%A3%E6%9E%903"/>
   <updated>2023-07-01T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2023/07/01/图解游戏引擎-Unreal引擎UI框架解析3</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/puJnWG2gLrz7c8bjzD2C8g&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/puJnWG2gLrz7c8bjzD2C8g&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/puJnWG2gLrz7c8bjzD2C8g&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - Unreal引擎UI框架解析（2）</title>
   <link href="http://www.luzexi.com/2023/06/24/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-Unreal%E5%BC%95%E6%93%8EUI%E6%A1%86%E6%9E%B6%E8%A7%A3%E6%9E%902"/>
   <updated>2023-06-24T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2023/06/24/图解游戏引擎-Unreal引擎UI框架解析2</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/M3Ty2uHlJm22S5Y97WwS5A&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/M3Ty2uHlJm22S5Y97WwS5A&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/M3Ty2uHlJm22S5Y97WwS5A&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - Unreal引擎UI框架解析（1）</title>
   <link href="http://www.luzexi.com/2023/06/17/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-Unreal%E5%BC%95%E6%93%8EUI%E6%A1%86%E6%9E%B6%E8%A7%A3%E6%9E%901"/>
   <updated>2023-06-17T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2023/06/17/图解游戏引擎-Unreal引擎UI框架解析1</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/0ZzqqVu6w5bk5zAGqd61YQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/0ZzqqVu6w5bk5zAGqd61YQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/0ZzqqVu6w5bk5zAGqd61YQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - IMGUI解析</title>
   <link href="http://www.luzexi.com/2023/06/04/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-IMGUI%E8%A7%A3%E6%9E%90"/>
   <updated>2023-06-04T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2023/06/04/图解游戏引擎-IMGUI解析</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/6PHSrPYOd9V5DWwEfA65dg&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/6PHSrPYOd9V5DWwEfA65dg&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/6PHSrPYOd9V5DWwEfA65dg&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - Unity动画原理（2）</title>
   <link href="http://www.luzexi.com/2023/05/22/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-Unity%E5%8A%A8%E7%94%BB%E5%8E%9F%E7%90%862"/>
   <updated>2023-05-22T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2023/05/22/图解游戏引擎-Unity动画原理2</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/P8Xda8XQL5dVoNl6-qPlUA&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/P8Xda8XQL5dVoNl6-qPlUA&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/P8Xda8XQL5dVoNl6-qPlUA&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - Unity动画原理（1）</title>
   <link href="http://www.luzexi.com/2023/05/20/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-Unity%E5%8A%A8%E7%94%BB%E5%8E%9F%E7%90%861"/>
   <updated>2023-05-20T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2023/05/20/图解游戏引擎-Unity动画原理1</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/Tq2LNqqJSeuInLeQgBHXGA&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/Tq2LNqqJSeuInLeQgBHXGA&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/Tq2LNqqJSeuInLeQgBHXGA&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - UE引擎动画框架2</title>
   <link href="http://www.luzexi.com/2023/05/11/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-UE%E5%BC%95%E6%93%8E%E5%8A%A8%E7%94%BB%E6%A1%86%E6%9E%B62"/>
   <updated>2023-05-11T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2023/05/11/图解游戏引擎-UE引擎动画框架2</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/qg0Mt0hoUqAElChkQWnUFw&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/qg0Mt0hoUqAElChkQWnUFw&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/qg0Mt0hoUqAElChkQWnUFw&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - UE引擎动画框架1</title>
   <link href="http://www.luzexi.com/2023/05/10/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-UE%E5%BC%95%E6%93%8E%E5%8A%A8%E7%94%BB%E6%A1%86%E6%9E%B61"/>
   <updated>2023-05-10T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2023/05/10/图解游戏引擎-UE引擎动画框架1</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/1i5FJDjZESwCo7JXylWo_g&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/1i5FJDjZESwCo7JXylWo_g&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/1i5FJDjZESwCo7JXylWo_g&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程：主程手记》庆祝第4次印刷</title>
   <link href="http://www.luzexi.com/2023/05/05/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B-%E4%B8%BB%E7%A8%8B%E6%89%8B%E8%AE%B0-%E7%AC%AC4%E6%AC%A1%E5%8D%B0%E5%88%B7"/>
   <updated>2023-05-05T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2023/05/05/《Unity3D高级编程：主程手记》第4次印刷</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/xPiIBFruPO4NFZoJNeEmRQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/xPiIBFruPO4NFZoJNeEmRQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/xPiIBFruPO4NFZoJNeEmRQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>图解游戏引擎 - 动画（2）- IK动画</title>
   <link href="http://www.luzexi.com/2023/04/23/%E5%9B%BE%E8%A7%A3%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E-%E5%8A%A8%E7%94%BB2-IK%E5%8A%A8%E7%94%BB"/>
   <updated>2023-04-23T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2023/04/23/图解游戏引擎-动画2-IK动画</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/-604qy6erl7wY-HkgKGlmA&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/-604qy6erl7wY-HkgKGlmA&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/-604qy6erl7wY-HkgKGlmA&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《如何精彩演讲》#8 - 半决赛复盘</title>
   <link href="http://www.luzexi.com/2023/04/15/%E4%B8%AD%E5%8C%BA%E6%BC%94%E8%AE%B2%E6%AF%94%E8%B5%9B%E6%80%BB%E7%BB%93"/>
   <updated>2023-04-15T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2023/04/15/中区演讲比赛总结</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/aESSSLK3U25mk3rBADcT9g&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/aESSSLK3U25mk3rBADcT9g&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/aESSSLK3U25mk3rBADcT9g&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《如何精彩演讲》#7 - 复赛亚军复盘（第三次亚军）</title>
   <link href="http://www.luzexi.com/2023/03/09/%E6%BC%94%E8%AE%B2%E6%AF%94%E8%B5%9B%E4%BA%9A%E5%86%9B%E6%80%BB%E7%BB%933"/>
   <updated>2023-03-09T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2023/03/09/演讲比赛亚军总结3</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/SsX_e3b19LPov1Oi6ozcvQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/SsX_e3b19LPov1Oi6ozcvQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/SsX_e3b19LPov1Oi6ozcvQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>睡眠的原理和技巧#3 - 技巧和策略</title>
   <link href="http://www.luzexi.com/2023/02/21/%E7%9D%A1%E7%9C%A0%E7%9A%84%E5%8E%9F%E7%90%86%E5%92%8C%E6%8A%80%E5%B7%A73"/>
   <updated>2023-02-21T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2023/02/21/睡眠的原理和技巧3</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/uBv_VWQdzONO6HtyUKrO3g&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/uBv_VWQdzONO6HtyUKrO3g&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/uBv_VWQdzONO6HtyUKrO3g&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>睡眠的原理和技巧#2 - 影响睡眠的因素</title>
   <link href="http://www.luzexi.com/2023/02/14/%E7%9D%A1%E7%9C%A0%E7%9A%84%E5%8E%9F%E7%90%86%E5%92%8C%E6%8A%80%E5%B7%A72"/>
   <updated>2023-02-14T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2023/02/14/睡眠的原理和技巧2</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/USy6jg6YDwgdaDtoTj5Ypg&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/USy6jg6YDwgdaDtoTj5Ypg&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/USy6jg6YDwgdaDtoTj5Ypg&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>睡眠的原理和技巧#1 - 睡眠的原理</title>
   <link href="http://www.luzexi.com/2023/02/06/%E7%9D%A1%E7%9C%A0%E7%9A%84%E5%8E%9F%E7%90%86%E5%92%8C%E6%8A%80%E5%B7%A71"/>
   <updated>2023-02-06T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2023/02/06/睡眠的原理和技巧1</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/AnqJoalnCdFgDndVdZ2gQQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/AnqJoalnCdFgDndVdZ2gQQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/AnqJoalnCdFgDndVdZ2gQQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>游戏引擎架构#7 - 动画（1）</title>
   <link href="http://www.luzexi.com/2023/01/09/%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E%E6%9E%B6%E6%9E%847%E5%8A%A8%E7%94%BB1"/>
   <updated>2023-01-09T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2023/01/09/游戏引擎架构7动画1</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/YaKTpdBwlKpjhtJcI1E63w&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/YaKTpdBwlKpjhtJcI1E63w&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/YaKTpdBwlKpjhtJcI1E63w&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《如何精彩演讲》#6 演讲比赛亚军总结2</title>
   <link href="http://www.luzexi.com/2022/12/25/%E6%BC%94%E8%AE%B2%E6%AF%94%E8%B5%9B%E4%BA%9A%E5%86%9B%E6%80%BB%E7%BB%932"/>
   <updated>2022-12-25T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/12/25/演讲比赛亚军总结2</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/zArO2uQGEFz7h0SQZnWXdg&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/zArO2uQGEFz7h0SQZnWXdg&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/zArO2uQGEFz7h0SQZnWXdg&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;背景&quot;&gt;背景&lt;/h1&gt;

&lt;p&gt;经历上次比赛后，我又一次参加了另一个俱乐部的比赛，同样获得了第2名，但这次的经历有点不同了。&lt;/p&gt;

&lt;p&gt;（梦想行动家中文国际演讲俱乐部比赛亚军-图）&lt;/p&gt;

&lt;p&gt;下面我来说一说这次比赛的复盘以及背后的故事。&lt;/p&gt;

&lt;h1 id=&quot;目录&quot;&gt;目录&lt;/h1&gt;

&lt;p&gt;1、心理建设
2、演讲稿的改进
3、演讲技巧的改进
4、赛前准备工作的改进&lt;/p&gt;

&lt;h1 id=&quot;内容&quot;&gt;内容&lt;/h1&gt;

&lt;p&gt;第二次俱乐部演讲比赛，是在第一次演讲比赛之上做二次准备的，因此比第一次比赛的准备工作会更加充分些。
由于我希望不断打磨我的演讲练习的连续性，因此我选择两次演讲都使用同一个主题。我在第一次的演讲比赛稿之上做了很多改进，同时在演讲技巧和心理活动建设上也做得很多改进。
在这里做一个回顾和总结，以便自己把这些经验和技巧沉淀下来，为以后更多的上台做些准备。&lt;/p&gt;

&lt;p&gt;这次我从四个方面来沉淀经验：心理建设、稿件改进，技巧改进，准备工作改进。&lt;/p&gt;

&lt;h2 id=&quot;1心理建设&quot;&gt;1、心理建设&lt;/h2&gt;

&lt;p&gt;我连续参加了两次演讲比赛（相隔2周时间），可以说是无心插柳。&lt;/p&gt;

&lt;p&gt;比赛前，我为自己做了三点的心理建设。
第一，由于比赛前我已经得知自己在上次比赛中获得了亚军并获得了参加小区复赛的名额，所以我有更多的信心去为第二次比赛做准备。在第二次比赛前，我跟自己说，“陆泽西，你是一个不错的的演讲者，你可以通过训练和精心准备去获得好的名次”。这样的自我激励，让我在比赛前的准备工作时信心满满。&lt;/p&gt;

&lt;p&gt;第二，在演讲比赛之前我对上一次演讲比赛做了一个完整的复盘，这让我对这次的演讲比赛的信心十足。我从复盘中看到了自己演讲时的问题，从而在练习时可以去做更多的调整，这些改进让我又增添了不少信心，上台前我信心满满且有种志在必得的感觉。&lt;/p&gt;

&lt;p&gt;第三，在演讲比赛之前我告知了几位朋友，说我已经拿到获得了其他俱乐部的第二名并拿到了小区赛的入场券，希望得到他们的支持，并希望这次的演讲比赛能充分的准备并拿到更好成绩。这些话抬高了我对自己的预期，为比赛准备提供了压力的同时也提供了动力，接下去的1周时间里，我把很多精力都投入进来，为这次比赛做好充分准备。&lt;/p&gt;

&lt;h2 id=&quot;2演讲稿的改进&quot;&gt;2、演讲稿的改进&lt;/h2&gt;

&lt;p&gt;在第二次比赛前我已经有了第一次比赛的演讲稿，演讲稿内容中我专注于自己的真实故事，把真实的经历、真实的体会写入到这故事当中，使得故事里有很多细节，包括我的表情、动作，对话，周围的环境描述以及情绪等，通过细节描写让观众在听故事的时候能够有身临其境的感觉，同时能展开他们自己的想象去感受这个故事。
同时我发现个问题，在演讲比赛上做演讲与以往的不同，它毕竟是场竞赛，如果你的故事不够精彩就会落败，所以除了故事的细节外，你还需要深度挖掘故事中很多惊奇的细节来丰富演讲内容。比如围绕你的细节加入一些梗或笑点去阐述你的故事，使内容更精彩。
我在观看了自己演讲比赛录屏后，了解到自己的稿子虽然故事性不错，但很多细节没有打磨的太好，同时很多部分是直来直去的讲述，没有带任何修饰的文字，于是我开始修改演讲稿。&lt;/p&gt;

&lt;p&gt;我从两个方面去打磨演讲稿：&lt;/p&gt;

&lt;h3 id=&quot;第一打磨故事的转折&quot;&gt;第一，打磨故事的转折。&lt;/h3&gt;

&lt;p&gt;由于故事转折过硬，我加入了些过渡内容，比如说在内容开头我拉拢大家注意力后，进入第一个故事前加入了一个笑点梗。&lt;/p&gt;

&lt;p&gt;我在三处地方植入了一些梗，&lt;/p&gt;

&lt;p&gt;第一处是演讲开头，加入了一个自嘲梗，老婆说我是丧偶式育儿时，女儿说我们家没有藕。&lt;/p&gt;

&lt;p&gt;第二处是在第二个故事结束后的末尾上增加了一个语音梗，突然从正常语气改为了搞怪的语气，用反差的方式来营造幽默的氛围。&lt;/p&gt;

&lt;p&gt;第三处是在演讲结束时，加入了我与女儿的对话，女儿跟我说我有一点变化，我问是什么变化，她说今天没有丧偶。用自嘲的方式来营造幽默的氛围做一个收尾梗。&lt;/p&gt;

&lt;h3 id=&quot;第二每个字都要有自己的目标&quot;&gt;第二，每个字都要有自己的目标。&lt;/h3&gt;

&lt;p&gt;整个讲稿在改进的时，我仔仔细细的一个字一个字的去斟酌，让每个字都有意义都有自己的目标，每个字都必须围绕演讲主题这个大目标或故事意义的小目标去改，也只有这样我的演讲稿才能够有精心打磨的效果。
当我发现某一个字或某一句话对演讲主题目标没有作用时，可能就需要斟酌一下。&lt;/p&gt;

&lt;p&gt;同时我在改演讲稿时有一个这样的体会：演讲稿是演讲的核心，只有演讲内容好了，演讲效果才有发挥的空间，因此花大的精力去打磨演讲稿是值得的。&lt;/p&gt;

&lt;h2 id=&quot;3演讲技巧的改进&quot;&gt;3、演讲技巧的改进&lt;/h2&gt;

&lt;p&gt;在第一次演讲比赛复盘的时候，我观察到了自己在演讲时一个比较大的问题，就是我的演讲听起来不是很流畅，虽然整体还可以，但是能明的感觉到一直在诡异的停顿，似乎是在想下一句台词，于是我回忆自己在台上的表现，我的大部分精力都用在想下一句台词从而损害了整体表现。虽然台词我早就背下来，但一上台就会磕磕绊绊，时常断片想不起来。&lt;/p&gt;

&lt;p&gt;（演讲比赛中-图）&lt;/p&gt;

&lt;p&gt;我仔细思考了一番并做出了一些改进：&lt;/p&gt;

&lt;h3 id=&quot;首先我需要更多的练习&quot;&gt;首先，我需要更多的练习。&lt;/h3&gt;

&lt;p&gt;练到什么程度呢？练到我自己根本不需要思考顺口溜的就能从嘴巴里就说出来了，练到台词不用思考就能出来那种自然反射。我知道只有练到这种程度的时候，才能够把自己其他方面的表现技巧展现出来。
由于练习时没有反馈和标准，很多时候我们在练习时会产生一种错觉，觉得自己练习的已经够多够好了，但对于一个完美的表演来说，10次20次的练习根本就不算什么，大师都是上百次的演讲练习。
这让我想到了，有时候我以为是我运气不好，努力了却没有收获，实际上我的那一点努力根本不算什么，它完全够不上收获的标准。&lt;/p&gt;

&lt;h3 id=&quot;其次把注意力集中在演讲上&quot;&gt;其次，把注意力集中在演讲上。&lt;/h3&gt;

&lt;p&gt;我观察到自己在演讲当中经常会尬笑，这个事情让我引起了警觉，以前我不知道自己在笑，我察觉不到这个习惯性动作。后来我才发现这个尬笑的动作给人们的感觉不好，一是让人看起来很不专业，二是看起来有种轻浮和肤浅的感觉。&lt;/p&gt;

&lt;p&gt;有一天在台上练习的时候，有一位前辈指出了我在尬笑的问题，她说，“你知不知道自己是在笑，有意无意把笑容挂在嘴边，无论是你讲的是悲伤还是平淡还是搞笑的事情，你的脸上好像永远都硬撑着笑容”。听到这话我才恍然大悟。后来我仔细复盘了一下自己以往的练习视频，我发现这个问题真的真的非常严重。&lt;/p&gt;

&lt;p&gt;我回顾当初刚开始练习时，由于上台非常恐惧害怕，所以我想到了一个技巧，就是在台上演讲时使用笑的方式来掩盖自己的尴尬和紧张。
随着我在演讲上的深入学习，对恐惧进一步的化解了，同时技巧也在不断提升，笑的掩饰动作已经不再符合我当下的演讲了。
以前我所误的认为笑可以缓解紧张和尴尬的方法已经不再适用，它只会把紧张和尴尬情绪传递给观众，同时也让自己显得更尴尬和紧张。&lt;/p&gt;

&lt;p&gt;该如何改进呢？我直击问题核心，核心问题不是笑，而是紧张和恐惧的情绪处理方法。&lt;/p&gt;

&lt;p&gt;我观察到自己紧张和恐惧的情绪是因为常常分散的注意力，注意力常敏感的转移到观众们以及环境变化上，比如我会注意观众的动作，是不是有人转动了身子，是不是观众有一些不耐烦，有人叹气了会不会是我的内容有问题，有人进入了会场，有人掉落了手机等等。这些环境上的变化以及我对变化的过分关注，使得我无法集中注意力在自己的演讲内容和技巧上，一分散注意力我就忘记要讲的内容，也无法把我事前准备要使用的技巧运用起来，同时这种情况让我感到紧张和害怕。&lt;/p&gt;

&lt;p&gt;于是我把精力集中到演讲时的注意力训练上来，我要求自己在练习时不要被轻易打断，即使自己讲错了忘词了也要努力集中注意力把练习的流程完整的走完。这样的练习过程我获得了一个经验，只要我把内容认认真真的讲完就能把注意力集中起来，演讲效果就很棒，台上的表现看起来也很从容，很认真、很专注。&lt;/p&gt;

&lt;h3 id=&quot;接着稳定我的姿势&quot;&gt;接着，稳定我的姿势。&lt;/h3&gt;

&lt;p&gt;我观察到演讲时自己一直是处于不稳定状态，要么走来走去，要么左右摇晃。
这是我以前的坏习惯，习惯用走动的方式来思考和记忆。实际上这种左右走前后走的行为，会分散我的注意力，同时让观众感觉不舒服，有种焦虑不稳定的感觉。&lt;/p&gt;

&lt;p&gt;在这次改进中我更加关注这个表现，要求自己再演讲时尽量稳定姿态，把步子的脉动少一些，有必要时再迈腿。把手势的表达也更精简一些，有必要时再做适当的手势表达。
当我尝试这样做的时候，我发现它让我的注意力更集中了，当我的状态稳定时，我能把注意力完完全全的集中在表演技巧和内容上。
原来少就是多：姿势越稳定，动作越少，越显得从容，越能集中注意力在演讲上。这个技巧让我分外惊喜。稳定的姿态，这个技巧的改进，我能很明显的感觉到我的演讲水平又提高了一大截。&lt;/p&gt;

&lt;h3 id=&quot;最后改进了声音的变化&quot;&gt;最后，改进了声音的变化。&lt;/h3&gt;

&lt;p&gt;声音的变化我在尝试中学习到了很多。
我以前用心观察自己的声音，这次经过我反复听自己的演讲声音后发现，我的声音有些单调。当我模仿某个人说话时，我以为在模仿，其实没有，只是换了一种语调而已。
所以，在这次的比赛中加入了些语音变化的技巧，特意在练习中训练了一下，比赛时效果还不错。比如生气时和不生气时的语音区别，还会用一些阴阳怪气的语气去表达演讲效果。
通过语音语调的练习，我发现演讲的故事让人感觉更加身临其境，有趣有料。&lt;/p&gt;

&lt;h2 id=&quot;4赛前准备工作的改进&quot;&gt;4、赛前准备工作的改进&lt;/h2&gt;

&lt;p&gt;比赛那天我坐飞机从成都飞到深圳来比赛，比完第二天再飞回成都，因此我的赛前准备工作从早上就开始了。那天我早起准备东西然后赶飞机，飞到深圳后坐地铁到比赛点，接着休息一下，然后再做一些练习和准备工作。&lt;/p&gt;

&lt;p&gt;首先，我到了深圳后，知道自己演讲前不能进食，一旦进食血糖就会上来，脑袋就嗡嗡的，状态很差。因此我预估会在7:40钟左右开始演讲，所以提前三个小时去吃饭来补充下能量。我到地点时差不多四点钟，给自己补充一些能量以支撑能够支撑自己一整晚的演讲。&lt;/p&gt;

&lt;p&gt;然后，我知道补充完食物后会处于一个混沌的状态，因此花了大概一个多小时的时间来休息，闭上眼睛冥想排除杂念同时恢复精力。我告诉自己必须在这一个小时里彻底排除杂念并恢复精力。所以我啥都没干，吃完就坐在那儿，一直坐着闭上眼睛冥想、休息，一直到一个小时左右才起身走动一下。&lt;/p&gt;

&lt;p&gt;接着，开始实地练习。先在空旷的地上练习演讲，发现周围有人在聊天，谈话的声音干扰了我。同时我也担心演讲练习被周围的关注到，然后会有一些尴尬，这让我观察到我不够专注，脸皮也比较薄的问题。后来，我进了会场里在台上去做实地练习，在台上完完整整的讲了两遍自己的演讲内容。在台上实地练习后，我对这个舞台更有信心了。当自己熟悉这个环境时，我就不慌了，这是非常好的赛前准备工作。&lt;/p&gt;

&lt;p&gt;最后，晚上赛前跟几个朋友一起也聊很开心的。一边是我精神上的放松，一边是我练习上的巩固，一边是对环境的熟悉，这么多的赛前准备，让原本陌生的环境变成了我的天时地利人和。&lt;/p&gt;

&lt;h3 id=&quot;我总结下赛前的准备工作流程熟悉场地实地练习提前补充体力提前恢复精力跟周围人聊聊放松情绪做好这几样就能主动打造的天时地利人和的赛前准备工作当熟悉这些准备工作的流程和要点时我在赛前和赛中显得更加自信和从容&quot;&gt;我总结下赛前的准备工作流程，熟悉场地，实地练习，提前补充体力，提前恢复精力，跟周围人聊聊放松情绪，做好这几样就能主动打造的“天时地利人和“的赛前准备工作。当熟悉这些准备工作的流程和要点时，我在赛前和赛中显得更加自信和从容。&lt;/h3&gt;

&lt;p&gt;以上是这次的比赛的复盘总结，我又了解了自己很多。加油各位！每次的经历都是人生旅途的体验，值得被记录。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《如何精彩演讲》#5 演讲比赛亚军总结1</title>
   <link href="http://www.luzexi.com/2022/12/17/%E6%BC%94%E8%AE%B2%E6%AF%94%E8%B5%9B%E4%BA%9A%E5%86%9B%E6%80%BB%E7%BB%931"/>
   <updated>2022-12-17T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/12/17/演讲比赛亚军总结1</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/QrYBt3sxdc96jKl9Juvx1g&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/QrYBt3sxdc96jKl9Juvx1g&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/QrYBt3sxdc96jKl9Juvx1g&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>当一个程序员决定出书</title>
   <link href="http://www.luzexi.com/2022/12/08/%E5%BD%93%E4%B8%80%E4%B8%AA%E7%A8%8B%E5%BA%8F%E5%91%98%E5%86%B3%E5%AE%9A%E5%87%BA%E4%B9%A6"/>
   <updated>2022-12-08T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/12/08/当一个程序员决定出书</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/gPFed8fQJnjPSE_dSDRuiA&quot;&gt;公众号发布地址&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/gPFed8fQJnjPSE_dSDRuiA&quot;&gt;公众号发布地址&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/gPFed8fQJnjPSE_dSDRuiA&quot;&gt;公众号发布地址&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>腾讯技术工程分享 - 游戏技术框架剖析</title>
   <link href="http://www.luzexi.com/2022/11/17/%E8%85%BE%E8%AE%AF%E7%9B%B4%E6%92%AD%E5%88%86%E4%BA%AB-%E6%B8%B8%E6%88%8F%E6%8A%80%E6%9C%AF%E6%A1%86%E6%9E%B6%E5%89%96%E6%9E%90"/>
   <updated>2022-11-17T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/11/17/腾讯直播分享-游戏技术框架剖析</id>
   <content type="html">&lt;h3 id=&quot;内容概要&quot;&gt;内容概要：&lt;/h3&gt;

&lt;p&gt;11月17日（周四）晚上19点半到21点，在腾讯技术工程公众号上的一次分享。&lt;/p&gt;

&lt;p&gt;分享主题为《游戏技术框架剖析》，分享围绕游戏技术框架这个话题来聊聊自己在技术框架中的实践经验和技术关键点和难点拆解过程。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/heywA9TT98K8r51GETHOOQ&quot;&gt;公众号发布地址&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;录播&quot;&gt;录播：&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/heywA9TT98K8r51GETHOOQ&quot;&gt;视频号录播地址&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;打开微信应用程序，点击回放&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.zhihu.com/zvideo/1576695970477084672&quot;&gt;知乎录播地址&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>游戏引擎架构#6 - 渲染管线</title>
   <link href="http://www.luzexi.com/2022/11/09/%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E%E6%9E%B6%E6%9E%846%E6%B8%B2%E6%9F%93%E7%AE%A1%E7%BA%BF"/>
   <updated>2022-11-09T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/11/09/游戏引擎架构6渲染管线</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/vfsOhmuZLA6KHZWNoSBGcQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/vfsOhmuZLA6KHZWNoSBGcQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/vfsOhmuZLA6KHZWNoSBGcQ&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>UWA Day 行业大会分享《如何快速进阶主程》</title>
   <link href="http://www.luzexi.com/2022/10/26/%E6%BC%94%E8%AE%B2-%E5%A6%82%E4%BD%95%E5%BF%AB%E9%80%9F%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B"/>
   <updated>2022-10-26T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/10/26/演讲-如何快速进阶主程</id>
   <content type="html">&lt;h3 id=&quot;内容概要&quot;&gt;内容概要：&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485608&amp;amp;idx=1&amp;amp;sn=e9e297668d8b9bd592080ea871e47aba&amp;amp;chksm=fc226dafcb55e4b9718ff8fd3c10991f02dabff15e9a3bc8a162fdffdd03bde18dfe54f704fc&amp;amp;token=925049509&amp;amp;lang=zh_CN#rd&quot;&gt;公众号发布地址&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/FOoi8ZYsE8EDgQeoHZG03w&quot;&gt;大会媒体报道&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;录播&quot;&gt;录播：&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://edu.uwa4d.com/course-intro/1/446&quot;&gt;UWA学堂录播地址&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>给女儿写信(二十九) 压力下的表现</title>
   <link href="http://www.luzexi.com/2022/10/15/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A129"/>
   <updated>2022-10-15T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/10/15/给女儿的信29</id>
   <content type="html">
&lt;hr /&gt;

&lt;p&gt;导读：喜欢跟孩子们一起，可惜一直在外地工作，因此我给孩子写点信并录成视频，跟孩子聊聊自己的故事以及最近的感受&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;要点&quot;&gt;要点&lt;/h2&gt;

&lt;p&gt;人在压力下保持专注就能化解压力&lt;/p&gt;

&lt;h2 id=&quot;大纲&quot;&gt;大纲&lt;/h2&gt;

&lt;p&gt;1、我最近的情况&lt;/p&gt;

&lt;p&gt;2、我怎么想&lt;/p&gt;

&lt;p&gt;3、我怎么做&lt;/p&gt;

&lt;h2 id=&quot;结构&quot;&gt;结构&lt;/h2&gt;

&lt;p&gt;1、最近的压力情况&lt;/p&gt;

&lt;p&gt;2、近几年我对压力的感受和领悟&lt;/p&gt;

&lt;p&gt;3、最近我在压力下的表现&lt;/p&gt;

&lt;h2 id=&quot;内容&quot;&gt;内容&lt;/h2&gt;

&lt;p&gt;Hello，秀恩、安妮，爸爸喜欢你们哟。&lt;/p&gt;

&lt;p&gt;又到了爸爸跟你们讲故事的时间了，给你们讲讲爸爸最近的状况，好不好？最近压力挺大的，因为爸爸要去做一个演讲，而且这么大型的演讲爸爸从来没有去做过，可能会有很多观众，所以爸爸有一些紧张。&lt;/p&gt;

&lt;p&gt;在国庆时我就开始了练习，先去了制作PPT，然后去练习自己的演讲，通过练习爸爸想能更加熟悉演讲的内容，同时能够让自己讲得更好。&lt;/p&gt;

&lt;p&gt;同时这对我来说是比较难的，因为这次的演讲给我的压力很大，所以我有点紧张，而且有时候有一点乱的阵脚。&lt;/p&gt;

&lt;p&gt;其实这两年爸爸压力一直都是蛮大的，不仅是项目给我的压力，工作给我的压力，同时我自己也给自己有很多学习目标上的压力，所以其实我对压力的体会和感受挺多的。所以借着最近去演讲这个事，我总结了一些我对压力的理解。&lt;/p&gt;

&lt;p&gt;我们知道压力下很容易让人会崩溃，为什么呢？&lt;/p&gt;

&lt;p&gt;因为压力很容易让我们能够产生一些紧张、焦虑的情绪，这是导致我们的行动被扭曲，可以简单来说就是没有了节奏，当我们没有节奏乱了阵脚，不知道自己该做什么的时候，人就会崩溃。因此压力下面我们很容易做不好事情，这是个糟糕的情况。&lt;/p&gt;

&lt;p&gt;那么压力下好的状态是怎样的呢？其实在压力下面，我们如果说能够保持专注的话，就会让事情变得越来越好，比如，专注在这件事情的技巧上，或者说专注在这个事情实施行动上，或者专注在如何去实现这个目标的方法上，专注会让压力慢慢化解开来。&lt;/p&gt;

&lt;p&gt;简单来说，化解压力的一个最主要的手段就是专注，只有当你专注的时候，这个压力就会慢慢就化解，因为你不断的去熟悉这个事情，不断的去强化自己的技能，就会让这个事情变得更加简单，从而化解压力也就化解了。&lt;/p&gt;

&lt;p&gt;就像爸爸最近的演讲一样，爸爸只有通过不断的去练习自己要讲的内容，把这个内容理解得更加深刻，同时融入自己的经历，把自己的故事和演讲的内容跟连接起来，才能把演讲做得更好，同时也就化解了压力。&lt;/p&gt;

&lt;p&gt;最近我有很多事情要处理，同时还要为上台演讲准备，很有压力，为了化解它我专注于技巧的练习，这两天一直在练，今天也练，明天也要练。通过练习，专注于演讲技巧，专注于思考演讲内容，化解了压力，而且在上台时还有不错的效果。因此我学会了专注。&lt;/p&gt;

&lt;p&gt;这是爸爸最近状况，喜欢你们哟，今天就聊到这里，爱你们比心，跟爸爸一起加油吧，加油。&lt;/p&gt;

&lt;p&gt;回来跟你们一起玩，跟你们一起学习哟，爸爸喜欢跟你们一起玩，跟你们一起疯，跟你们一起学习，一起专注，这是一件非常开心的事情。今天就到这里，拜拜。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(六十六) 如何做软件分析4 - 策略、用例、接口</title>
   <link href="http://www.luzexi.com/2022/09/12/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B066"/>
   <updated>2022-09-12T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/09/12/读书笔记66</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485593&amp;amp;idx=1&amp;amp;sn=11f282bcd666b1fd29a267bc095e1d15&amp;amp;chksm=fc226d9ecb55e488d854395bde94f4459208d8ab4c5b4fbd430f72750e010061226f8218e1cf&amp;amp;token=546801700&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485593&amp;amp;idx=1&amp;amp;sn=11f282bcd666b1fd29a267bc095e1d15&amp;amp;chksm=fc226d9ecb55e488d854395bde94f4459208d8ab4c5b4fbd430f72750e010061226f8218e1cf&amp;amp;token=546801700&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485593&amp;amp;idx=1&amp;amp;sn=11f282bcd666b1fd29a267bc095e1d15&amp;amp;chksm=fc226d9ecb55e488d854395bde94f4459208d8ab4c5b4fbd430f72750e010061226f8218e1cf&amp;amp;token=546801700&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>给女儿写信(二十八) 语速</title>
   <link href="http://www.luzexi.com/2022/09/06/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A128"/>
   <updated>2022-09-06T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/09/06/给女儿的信28</id>
   <content type="html">
&lt;hr /&gt;

&lt;p&gt;导读：喜欢跟孩子们一起，可惜一直在外地工作，因此我给孩子写点信并录成视频，跟孩子聊聊自己的故事以及最近的感受&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;要点&quot;&gt;要点&lt;/h2&gt;

&lt;p&gt;语速缓和后，可以让自己有更好的表达。&lt;/p&gt;

&lt;h2 id=&quot;大纲&quot;&gt;大纲&lt;/h2&gt;

&lt;p&gt;1、聊自己的语速问题&lt;/p&gt;

&lt;p&gt;2、语速太快的故事 - 我急着表达的故事&lt;/p&gt;

&lt;p&gt;3、缓和语速后的感受&lt;/p&gt;

&lt;h2 id=&quot;结构&quot;&gt;结构&lt;/h2&gt;

&lt;p&gt;1、解释自己语速的问题&lt;/p&gt;

&lt;p&gt;2、当我急于表达想法时的情况&lt;/p&gt;

&lt;p&gt;3、我降低语速后的感受&lt;/p&gt;

&lt;h2 id=&quot;内容&quot;&gt;内容&lt;/h2&gt;

&lt;p&gt;Hello 秀恩、Hello 安妮：&lt;/p&gt;

&lt;p&gt;爸爸好想你们，我喜欢你们。&lt;/p&gt;

&lt;p&gt;日子很快，你们从成都回杭州后到现在已经一个半月快两个月了，爸爸很想你们，还想跟你们一起去玩耍。&lt;/p&gt;

&lt;p&gt;今天爸爸跟你们说说爸爸最近遇到的事好不好？爸爸最近有一个发现，我发现自己说话说得太快，像机关枪一样说得很快。&lt;/p&gt;

&lt;p&gt;这会有两个问题，第一个是别人听不懂我在讲什么，因为我说的太快了，所以别人思维跟不上，导致我说的内容别人听不清楚也听不明白，我说了等于白说。&lt;/p&gt;

&lt;p&gt;第二个，说的太快我的脑子跟不上，因为说太快，我一直需要去思考我下面的内容，但是有时候我脑袋的思考的速度没有我嘴巴说的快，所以就会有语塞的时候不知道该下面该说什么，有时候就会语塞得很严重，就会发出“额…”“然后…”“但是…”这些词语。&lt;/p&gt;

&lt;p&gt;爸爸观察到了自己这个毛病，我记得有一次我在工作会议上，大家都落座在了，会议室里面大家都发表了自己的想法和建议，我特别想要表达自己的想法，于是会去插嘴别人的说的话，然后自己表达的也非常快，表达得非常急，导致我说的内容没有说清楚，同时别人也不知道我在说什么，这样又说不清楚又得罪了人。&lt;/p&gt;

&lt;p&gt;我后来回来以后就很懊悔，因为那天是一个非常重要的会议，我想表达的内容我认为也是非常重要的，但是我表达的太急了，太着急了，太想要表达了，我的语速很快，没人听的清楚，这引起很多的误会。
后来爸爸记得了这个教训，慢慢试着的把自己的语速降低，用停顿来调整自己，让自己表达的更加从容一些。就像我现在这样，我故意放慢自己的语速，让我能够边思考下面的内容，能够边表达，让我表达的更顺畅一些。&lt;/p&gt;

&lt;p&gt;同时我发现我降低语速以后，我的那些赘词，比如“但是”，“然后”，“额…”也随之消失了，我觉得这种感觉很好，我想要在说话的时候能够更舒适一些，更有逻辑一些，同时也能够表达我想要表达的重要的思想，重要的内容。&lt;/p&gt;

&lt;p&gt;爸爸现在把这些放慢语速的习惯融入到了爸爸平常的生活说话当中去，表达过程当中表达中去，我觉得慢慢会好起来。&lt;/p&gt;

&lt;p&gt;我一直在练习表达，这里的技巧爸爸在书里面看到了很多，我也试着去学这些技巧，比如说降低语速，在表达内容上面有结构有序有亮点，内容有故事有细节等。希望自己能够未来能够说话和表达能够更好。因为我们人需要表达，需要跟大家交流，所以表达非常重要。&lt;/p&gt;

&lt;p&gt;今天就说到这里，喜欢你们哟，秀恩、安妮，爸爸10月1号就回来看你们，爱你们，拜拜。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(六十五) 如何做软件分析3 - 分析的目标与层次</title>
   <link href="http://www.luzexi.com/2022/08/26/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B065"/>
   <updated>2022-08-26T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/08/26/读书笔记65</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485559&amp;amp;idx=1&amp;amp;sn=c1e0179ea5cf96c0e4f747e2716eab94&amp;amp;chksm=fc226d70cb55e4666e71c519db815a5434b07ae1698b132fed8225bedd87b6d7b0660031b202&amp;amp;token=546801700&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485559&amp;amp;idx=1&amp;amp;sn=c1e0179ea5cf96c0e4f747e2716eab94&amp;amp;chksm=fc226d70cb55e4666e71c519db815a5434b07ae1698b132fed8225bedd87b6d7b0660031b202&amp;amp;token=546801700&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485559&amp;amp;idx=1&amp;amp;sn=c1e0179ea5cf96c0e4f747e2716eab94&amp;amp;chksm=fc226d70cb55e4666e71c519db815a5434b07ae1698b132fed8225bedd87b6d7b0660031b202&amp;amp;token=546801700&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>给女儿写信(二十七) 为人父母</title>
   <link href="http://www.luzexi.com/2022/08/02/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A127"/>
   <updated>2022-08-02T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/08/02/给女儿的信27</id>
   <content type="html">
&lt;hr /&gt;

&lt;p&gt;导读：喜欢跟孩子们一起，可惜一直在外地工作，因此我给孩子写点信并录成视频，跟孩子聊聊自己的故事以及最近的感受&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;要点&quot;&gt;要点&lt;/h2&gt;

&lt;p&gt;为人父母不容易，生活各方面难平衡，我能改变的只有自己&lt;/p&gt;

&lt;h2 id=&quot;大纲&quot;&gt;大纲&lt;/h2&gt;

&lt;p&gt;1、表达有多爱两个女儿的故事&lt;/p&gt;

&lt;p&gt;2、两个女儿闹矛盾时心里纠结的故事&lt;/p&gt;

&lt;p&gt;3、想支持女儿但无能为力的故事&lt;/p&gt;

&lt;p&gt;4、我能改变的只有我自己&lt;/p&gt;

&lt;h2 id=&quot;结构&quot;&gt;结构&lt;/h2&gt;

&lt;p&gt;1、每天都把爱挂在嘴边&lt;/p&gt;

&lt;p&gt;2、看到两个女儿就抱着他们，牵着他们的手&lt;/p&gt;

&lt;p&gt;3、每次回家时都想着给女儿买点什么礼物好让她们开心&lt;/p&gt;

&lt;p&gt;4、两个女儿爱吵架，有次老大用脚踹小的下床&lt;/p&gt;

&lt;p&gt;5、我看在眼里心疼，想劝但又适得其反，老大更不开心，以为我偏袒小的&lt;/p&gt;

&lt;p&gt;6、想多牵一下老大的手，但老大不要我牵&lt;/p&gt;

&lt;p&gt;7、老大的学习专注力比较好，所以做作业学习更高效，但小的完全专心不下来&lt;/p&gt;

&lt;p&gt;8、我想了很多办法，比如跟孩子们一起学习，与他们一起练习一个东西&lt;/p&gt;

&lt;p&gt;9、我远在其他城市，看到孩子们每天发生的事情，都感觉到自己无能为力&lt;/p&gt;

&lt;p&gt;10、我能做的就是管好自己，我是什么样的人，孩子就会成为什么样的人&lt;/p&gt;

&lt;h2 id=&quot;内容&quot;&gt;内容&lt;/h2&gt;

&lt;p&gt;Hello Sharon, hello Annie.爸爸很想你们哦，你们还没有回去多久，我又开始想你们了。每次跟你们在一起爸爸都很开心，一起开心的学习，一起开心的玩儿，太好了。&lt;/p&gt;

&lt;p&gt;今天爸爸想跟你们讲讲爸爸最近的一些思考。&lt;/p&gt;

&lt;p&gt;爸爸很爱你们，每天都把爱挂在嘴边，把‘喜欢’挂在嘴边，一见到你们就‘喜欢你，喜欢你，喜欢你哦，爱你’。看到你们两个就想抱着你们两，左手一个右手一个，想牵着你们的手一起走。&lt;/p&gt;

&lt;p&gt;当我抱着你们牵着你们的手一起走时爸爸感觉非常的幸福也很开心。无论是你们还是小的时候，还是现在慢慢长大了，爸爸都觉得你们非常可爱，是爸爸的小宝贝。&lt;/p&gt;

&lt;p&gt;每次我回家的时候，总是想要买一点什么礼物好让你们开心一下，所以你们开心爸爸就很开心，你们难过的时候爸爸也会很难过。&lt;/p&gt;

&lt;p&gt;不过同时呢，每次你们吵架的时候爸爸都会很心痛，我记得有一次你们两个都在床上，秀恩硬是要把安妮给踹下床，我不知道什么原因，但是这个举动我看在眼里就很心疼，心疼安妮，也心疼秀恩。你们两个关系不好的时候，我都很难受。那时我每次都会劝秀恩，说秀恩不要踢了不要打了，但是我发现了，其实这样劝没有用。因为这样劝就更不开心了，秀恩会以为我在偏袒安妮。其实我很想多牵一下秀恩的手，秀恩老不让我牵手，哈哈哈。&lt;/p&gt;

&lt;p&gt;我很关注秀恩，关心生活关心学习也关心玩，我发现Sharon的专注力非常好，在做作业的时候会把所有的注意力集中在要做的事情上。学习的效率比较高，做作业的速度比较快，同时安妮不是这样，安妮还不能控制自己的注意力无法很好的集中起来。我想了很多的办法，比如说跟你们一起学习，也跟你们一起看书，跟你们一起练习表达练习演讲，一起玩一起学习的日子真开心。&lt;/p&gt;

&lt;p&gt;我记得有一次我跟秀恩一起，秀恩要跳舞我也跟着跳，爸爸虽然跳舞很糟糕但是我还是跟秀恩一起玩，因为女儿想要跳，所以我也要跟女儿一起练，这样做会让她开心。&lt;/p&gt;

&lt;p&gt;我在很远的城市，每次我都想回家，想着什么时候能够回到杭州来工作，但是真的是事与愿违，杭州的工作机会对我来说不够好，或许这就是我的命，生活和工作很多时候真的很难平衡，我在成都在深圳，看到孩子们每天发生的事情，都感觉到有一些无力，因为我在他乡触摸不到你们，碰不到你们，所以有时候非常想你们，想到有些难过。&lt;/p&gt;

&lt;p&gt;不过我自己也想想明白了，其实这个世界上作为父母的处境都是一样的，有很多的无能为力的地方，当我还是孩子的时候，我想我的父母也是同样的感受，也是有很多时候他们只能顾好自己，顾不好顾完全顾不了我。&lt;/p&gt;

&lt;p&gt;想到这里我理解了我的父母同时呢，同时也理解了我自己，其实孩子们会变成什么样，最重要的是我是什么样的人，我是一个专注的，自律的，友爱的，大方的，体贴的，温柔的，善良的这样一个人的话，那么我的孩子就会成为这样一个人，所以我思来想去，我能发挥的最大的能量，就是做好自己，不断的去改变我自己，让我自己变成一个能够被孩子们成为榜样的人。用我自己的行动，用我自己的努力去影响孩子们，也影响我周围的人。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>游戏引擎架构#5 - 剔除与合批</title>
   <link href="http://www.luzexi.com/2022/08/01/%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E%E6%9E%B6%E6%9E%845%E5%89%94%E9%99%A4%E4%B8%8E%E5%90%88%E6%89%B9"/>
   <updated>2022-08-01T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/08/01/游戏引擎架构5剔除与合批</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485541&amp;amp;idx=1&amp;amp;sn=1adc3d4bd3573b58dbc56e81be963abf&amp;amp;chksm=fc226d62cb55e474965abb9a2fbbff8eee8daaa2e541310279178af01984c0882060273fe026&amp;amp;token=546801700&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485541&amp;amp;idx=1&amp;amp;sn=1adc3d4bd3573b58dbc56e81be963abf&amp;amp;chksm=fc226d62cb55e474965abb9a2fbbff8eee8daaa2e541310279178af01984c0882060273fe026&amp;amp;token=546801700&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485541&amp;amp;idx=1&amp;amp;sn=1adc3d4bd3573b58dbc56e81be963abf&amp;amp;chksm=fc226d62cb55e474965abb9a2fbbff8eee8daaa2e541310279178af01984c0882060273fe026&amp;amp;token=546801700&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>给女儿写信(二十六) 即兴演讲</title>
   <link href="http://www.luzexi.com/2022/07/13/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A126"/>
   <updated>2022-07-13T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/07/13/给女儿的信26</id>
   <content type="html">
&lt;hr /&gt;

&lt;p&gt;导读：喜欢跟孩子们一起，可惜一直在外地工作，因此我给孩子写点信并录成视频，跟孩子聊聊自己的故事以及最近的感受&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;要点&quot;&gt;要点&lt;/h2&gt;

&lt;p&gt;任何一种练习都需要坚持，即兴演讲也是，它不仅带给我很好的表达，我在沟通时也能注意到对话的细节和对方的情绪，是一种很好练习表达和沟通的方法。&lt;/p&gt;

&lt;h2 id=&quot;大纲&quot;&gt;大纲&lt;/h2&gt;

&lt;p&gt;1、最近的即兴演讲训练的感受&lt;/p&gt;

&lt;p&gt;2、自己练习即兴演讲的效果&lt;/p&gt;

&lt;p&gt;3、收尾鼓励&lt;/p&gt;

&lt;h2 id=&quot;结构&quot;&gt;结构&lt;/h2&gt;

&lt;p&gt;1、最近练习即兴演讲的情况&lt;/p&gt;

&lt;p&gt;2、我对即兴演讲练习的感受&lt;/p&gt;

&lt;p&gt;3、我在练习时的困境&lt;/p&gt;

&lt;p&gt;4、练习演讲并且练习点评的感受&lt;/p&gt;

&lt;p&gt;5、一起练习的日子的感受&lt;/p&gt;

&lt;p&gt;6、把即兴演讲运用到生活中去的感悟&lt;/p&gt;

&lt;p&gt;7、收尾鼓励自己也鼓励大家&lt;/p&gt;

&lt;h2 id=&quot;内容&quot;&gt;内容&lt;/h2&gt;

&lt;p&gt;Hello Sharon, hello Anne, 晚上好，爸爸喜欢你们哟，爸爸爱你们。&lt;/p&gt;

&lt;p&gt;今天，我给你们讲讲最近一段日子我练习即兴演讲的感受。
最近一段时间我花了很多的精力去练习表达，练习沟通，练习即兴演讲。我已经有大概有三个月的时间，每天都在练习即兴演讲了，而且我每天给自己出一道题目，然后跟跟自己说这个标题该如何做即兴。
这些日子里面训练过来，有时候就会很很累很不想讲，我太累了，每天上完班做完学习的功课还要练习即兴演讲。
但是，我觉得应该要坚持下去，因为我知道很多事情只有坚持下去，才能看到最终的效果，所以我坚持下来了。
三个月到现在，大概已经讲了109次了，也就是说每天一次总共有109天，中间还不包括没有记录下来的那些进行演讲。&lt;/p&gt;

&lt;p&gt;我有一种感受，就是我练习了即兴演讲后，发现在生活和工作当中，我在表达的时候能够表达的更加顺畅。比如说我想表达一个内容的时候，我能够组织自己的表达内容，在我的口中说出来时更加清晰，自己总结的也更精简简要，能够清晰易懂的让呈现出来，这是我比较大的收获。&lt;/p&gt;

&lt;p&gt;我在继续演讲的这段日子里面，在这109天的日子里面，我遇到过一些瓶颈，比如说嗯一开始的时候，我发现很多时候我想不出来一个主题，然后我就随便想一个，我看到这里有本书，我就按这个书的题目来做即兴演讲。
有时候呢，我看到那个被子，就用被子为题作即兴演讲。所以呢，有时候感觉自己想不出来题目，这个时候，旁边的人，身边的人就可以帮助我说，可不可以，你来出一个题目，你有什么事情对你来说比较关心的，说出来我来即兴演讲。&lt;/p&gt;

&lt;p&gt;一开始，我我就向妈妈求助了，我说妈妈你能不能给我一些题目，每天给我个题目，无论是什么题目你就告诉我，我就按照这个题目来做演讲，嗯，做了一段时间以后我发现，这个是个特别好的主意，因为我妈妈说出来的题目都是我，我没有想到的一些题目，这对我来说的进行演讲是一种比较大的锻炼。&lt;/p&gt;

&lt;p&gt;当然这中间也有很多很痛苦的时候，很痛苦的时光，因为我有时候会讲不出来即兴演讲，但是呢，我发现，我坚持练着练着，就越来越会了讲的越来越好了，所以我现在很喜欢做即兴演讲，对我来说很自然。
而且我发现哈，我的即兴演讲还在逐步提高，在未来的日子里我想，我的即兴演讲会更好，同时我也发现我的生活当中我的表达能力更强了，跟朋友们聊天的时候，在跟同事们聊天的时候。我表达的更清楚了，能讲更多的故事，能讲更多的有趣的事，故事里面充满了很多的细节和场景，整个表达让人赏心悦目。&lt;/p&gt;

&lt;p&gt;后来有秀恩和安妮加入，我觉得更好了，因为我在练习自己表达的同时还能听到你们对我的点评，我觉得这是个非常棒的主意。
其实点评跟演讲是两条路线，是两个技巧，这两个技巧都在我们平时的生活和工作当中用到非常多。
我觉得我跟秀恩和安妮一起即兴演讲时，我学习到了很多东西，我在很用心的去点评时，这种互动的过程，让我们一起能够一起去努力一起去加油，特别好。
而且如果细心的你就会发现，你练习了即兴演讲以后你在平时的在学校里面，或者说在跟小朋友一起玩的时候，会发现自己的表达能力就更好了，一旦你的表达能力更好时，你跟小朋友一起玩的时候就有更有趣，心情更愉悦也有更多的成就感。&lt;/p&gt;

&lt;p&gt;所以呀，我特别鼓励我自己同时也鼓励我身边的人一起去练习即兴演讲。我们在演讲后会去点评对方，那其实点评有很多的技巧，其中一个关键技巧就是要找到对方的亮点，什么是亮点呢？就是，特别突出的地方，这个特别突出的地方就是，他讲的好的点，能让你吸引你的点，能让你记住的地方，把它讲说出来。
通过这样的方式了，我们在对话过程当中，能够细心的观察对方的表情，声音，故事，情节，还有动作等等这些，能观察的很细致，只要我们观察的很细致，我们就能够很好的进行沟通和表达了。&lt;/p&gt;

&lt;p&gt;那今天的信就到这了，爸爸很喜欢你们啊，爸爸也很想你们，过两天你们就来成都了。希望你们这个旅途平平安安的，到成都以后呢，我们玩的开开心心。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(六十三) 如何做软件分析2 - 分析的模型</title>
   <link href="http://www.luzexi.com/2022/07/10/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B063"/>
   <updated>2022-07-10T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/07/10/读书笔记63</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485499&amp;amp;idx=1&amp;amp;sn=bff40ccff3934df847c03f170680fb34&amp;amp;chksm=fc226d3ccb55e42a91b4ba0e59ee9902b29541fbdd6f5e78a5088eab624f944ecb66ce3a9ff7&amp;amp;token=546801700&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485499&amp;amp;idx=1&amp;amp;sn=bff40ccff3934df847c03f170680fb34&amp;amp;chksm=fc226d3ccb55e42a91b4ba0e59ee9902b29541fbdd6f5e78a5088eab624f944ecb66ce3a9ff7&amp;amp;token=546801700&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485499&amp;amp;idx=1&amp;amp;sn=bff40ccff3934df847c03f170680fb34&amp;amp;chksm=fc226d3ccb55e42a91b4ba0e59ee9902b29541fbdd6f5e78a5088eab624f944ecb66ce3a9ff7&amp;amp;token=546801700&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《如何精彩演讲》#4 演讲比赛复盘总结</title>
   <link href="http://www.luzexi.com/2022/07/03/%E6%BC%94%E8%AE%B2%E6%AF%94%E8%B5%9B%E5%A4%8D%E7%9B%98%E6%80%BB%E7%BB%93"/>
   <updated>2022-07-03T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/07/03/演讲比赛复盘总结</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485456&amp;amp;idx=1&amp;amp;sn=a69f45ddb80b9c31bd79c9eb9cfb84e3&amp;amp;chksm=fc226d17cb55e4016569080d868884610cd9fc49c2abba0ac7cb3e7eafbd19fa672201a53349&amp;amp;token=1541713099&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;背景&quot;&gt;背景：&lt;/h1&gt;

&lt;p&gt;对演讲和表达仍然在继续学习中，前面参加了一次线下的课程，最近参加了一次线上的演讲训练营，两次课程的最后都有比赛，一次是线下的比赛，一次是线上的比赛。&lt;/p&gt;

&lt;p&gt;我认真的完成了两次训练营的课程，同时把自己的所思所想，所练所感记录下来，总结、反思、调整、完善，为以后的演讲打好基础做好复盘，提升学习效率。&lt;/p&gt;

&lt;h1 id=&quot;概述&quot;&gt;概述：&lt;/h1&gt;

&lt;p&gt;训练营分为5节课，都是看视频，看完会布置作业，按照作业上传视频，导师根据视频点评反馈，我们将反馈的内容运用到下一个课程。&lt;/p&gt;

&lt;p&gt;练习与反馈的好处是，我们能通过练习观察自己的演讲，通过反馈得知正确的改进方向，再通过练习来修补自己的缺失，这种学习方式非常有效。这也是生活和工作中，高效学习的方式方法。&lt;/p&gt;

&lt;p&gt;第一课讲紧张的原理和应对技巧，第二课讲故事的结构，第三课讲故事如何表现，第四课讲声音的变化，第五课讲肢体语言，最后一天是一个线上比赛，由评委导师打分给出比赛名次。&lt;/p&gt;

&lt;p&gt;整个过程7天内完成，由于我本身在前面练习过演讲一段时间，所以虽然强度比较大，但对这种学习方式和学习效率比较认可。
同时最后一天的比赛吸引了我，因为我知道比赛是学习效率最高的机会，通过比赛我可以学习到更多东西，同时比赛的形式也能让自己有一个可见的目标。&lt;/p&gt;

&lt;h1 id=&quot;目录&quot;&gt;目录：&lt;/h1&gt;

&lt;ul&gt;
  &lt;li&gt;如何克服紧张&lt;/li&gt;
  &lt;li&gt;故事的结构力和故事力&lt;/li&gt;
  &lt;li&gt;语音语速语调技巧&lt;/li&gt;
  &lt;li&gt;表情、手势、移动技巧&lt;/li&gt;
  &lt;li&gt;比赛与准备&lt;/li&gt;
&lt;/ul&gt;

&lt;h1 id=&quot;内容&quot;&gt;内容：&lt;/h1&gt;

&lt;p&gt;我用一节课一节课的内容来复述整个过程。&lt;/p&gt;

&lt;p&gt;首先是开营仪式，开营仪式为我们介绍了课程的大致内容和计划，并且为我们提供了一个介绍自我的机会。
我把每次上台都视为一次机会，并绝不放过任何一次上台练习的机会，所以即使是开营仪式，我也会在上台介绍自己的时候拉满注意力。&lt;/p&gt;

&lt;p&gt;下面是5节课的经验和分享，每节课都凝结了导师的心血，所以每个字每句话都是他们的心得体会，细心琢磨就会发现其中的奥秘。&lt;/p&gt;

&lt;h1 id=&quot;如何克服紧张&quot;&gt;如何克服紧张&lt;/h1&gt;

&lt;p&gt;第一节课主要讲紧张的原理，从根源上去认知紧张，这对我们在面对紧张的时候，去克服紧张回更有效果。
如果我们很理性的观察到自己身体的反应，就能做出调整，从而化解掉这个紧张。&lt;/p&gt;

&lt;p&gt;(杏仁体大脑原理图-图来源网络)&lt;/p&gt;

&lt;p&gt;那么原理是什么呢？&lt;/p&gt;

&lt;p&gt;当我们紧张时，脑袋中的杏仁体处于劫持状态，当它感知到危险时，会挣脱大脑的控制，向肾上腺发出警告，
肾上腺素分泌，流向我们的全身，此时你的呼吸会急促，为你的血液补充氧气，你的心跳会加快，为你的行动做准备。
你的血液会流向四肢，为战斗或逃跑做好准备，从而导致大脑失血。（《即兴演讲》书中有类似的生理原理）&lt;/p&gt;

&lt;p&gt;（压力下和正常脑部图-图来源网络）&lt;/p&gt;

&lt;p&gt;其生理反应表现为：&lt;/p&gt;

&lt;p&gt;当我们的大脑血液流网四肢的时候，我们的大脑就会一片空白，因此我们会忘了准备的内容。
这就是杏仁体直接挣脱大脑，直接指挥身体的状态。
简单说就是，我们人对危险情况启动的应激反应，是人的正常状态。
一紧张血液流往了四肢，手脚乱动，手舞足蹈，站上舞台动来动去。&lt;/p&gt;

&lt;p&gt;（紧张大脑状态图-图来源网络）&lt;/p&gt;

&lt;p&gt;导师提出了，消除紧张的关键：消除陌生感&lt;/p&gt;

&lt;h2 id=&quot;三熟悉&quot;&gt;三熟悉&lt;/h2&gt;

&lt;p&gt;三熟悉，即熟悉环境（提前熟悉）、熟悉观众（台上台下连接观众）、熟悉内容（多次练习）&lt;/p&gt;

&lt;p&gt;一、在演讲前，我们要首先提前到达会场，熟悉环境，如果可以的话，与台上台下的人聊聊天，熟悉周围的环境。&lt;/p&gt;

&lt;p&gt;二、上台时，不要急着讲自己的内容，这样容易让自己处于陌生状态，容易紧张，要先跟大家互动一下，连接一下台上的人和台下的人，这样做的目的就是让自己熟悉这些人，与大家打成一片。&lt;/p&gt;

&lt;p&gt;三、熟悉内容，在上台前不断练习自己的稿子，可以通过读稿的方式来加深肌肉记忆。&lt;/p&gt;

&lt;p&gt;除了消除陌生感，我们还需要在舞台上用一些技巧来消除紧张。
这个技巧实际上是消除观众的紧张的，只有观众放松了，我们才会放松，所以观众很重要。
导师用三定来消除观众紧张，让观众在看和听同时保持放松状态，前面我们说了，只有观众放松了，我们才会轻松下来，紧张自然就不见了。&lt;/p&gt;

&lt;h2 id=&quot;三定&quot;&gt;三定&lt;/h2&gt;

&lt;p&gt;三定，即身定（挺身站稳）、眼定（看的到稳）、笑定（保持微笑）&lt;/p&gt;

&lt;p&gt;一、身定，即挺身站稳，只有挺身站稳了，才能让大家看到精神抖擞的你，用积极自信的体态面对大家，是对大家的一种尊重。&lt;/p&gt;

&lt;p&gt;二、眼定，看人要稳，直接看着对方，时不时的切换看的对象，同时眼睛不要飘来飘去，显得慌张&lt;/p&gt;

&lt;p&gt;三、笑定，要保持微笑，让大家感觉到轻松自在，而不是板着脸让人难受。&lt;/p&gt;

&lt;p&gt;导师编了一首词，让大家好记住这些。&lt;/p&gt;

&lt;p&gt;身定：头顶一碗水，背靠一堵墙，胸提一公分，两腿一线直，手是一把刀，放在大腿的两侧。&lt;/p&gt;

&lt;p&gt;眼定：模糊看，或直视看着对方，练习时可以找个人与对方深情凝望5分钟，点视（看给予支持的人，线上时看镜头），扫视（注意停顿，并扫视全场）&lt;/p&gt;

&lt;p&gt;笑定：嘴角上扬成一条直线（做到极限，有种酸酸的感觉，习惯了就会更放松），可以用咬筷子方式练习&lt;/p&gt;

&lt;p&gt;说实话第一堂课很充实，让我知道了演讲紧张的原理以及如何去应对的具体技巧，我有一种“啊~原来是这样，我也可以通过练习来达到”的感觉。&lt;/p&gt;

&lt;p&gt;同时我通过原理与技巧的练习，每次练习时都注意三定，同时回忆起自己以前台上演讲时的表现，明显能感觉到自己在各个细节技巧上有所提升，感觉到自己可以通过这样的训练来达到自己理想的状态。&lt;/p&gt;

&lt;p&gt;这次的课让我对紧张有了更深层次的理解。&lt;/p&gt;

&lt;h1 id=&quot;故事的结构力和故事力&quot;&gt;故事的结构力和故事力&lt;/h1&gt;

&lt;p&gt;第二、三节课连起来是一个整体，主要讲两个内容，故事的结构和内容。
其目标是，告诉大家在台上，我们该如何讲好一个故事。&lt;/p&gt;

&lt;p&gt;在导师讲这些内容之前，我听过很多人介绍过如何讲故事的方法，并且自己专门买了一本书《如何讲好一个故事》来专门学习这门技巧，因此对这部分内容有自己的理解。&lt;/p&gt;

&lt;p&gt;（讲故事图-图来源网络）&lt;/p&gt;

&lt;h2 id=&quot;结构力&quot;&gt;结构力&lt;/h2&gt;

&lt;p&gt;首先，讲的顺序很重要，如果演讲没有顺序，那么全乱套了，大家听不懂，自己也很难记得自己讲到哪了。
所以一般讲故事，都要有一个主线在，比如以时间为主线，比如以距离为主线，比如以地点为主线等等。
我们常常以时间为主线较多，因为这样大家比较容易理解，比如以前发生了XXX，后来到了现在，发生了XXX。&lt;/p&gt;

&lt;p&gt;说到故事，其实我们每天都在讲故事，只是我们不知道。
当我们讲话时，时常会用故事来表达自己的感想和当时的情景，有时候故事短，有时候故事长。
同时我们讲故事时也是时好时坏，有时逻辑清晰有时混乱。
如果我们能在平时，在重要的说话前，打个草稿，那么我们在说话时就会更加有掌控感，知道自己要说什么，该怎么说。&lt;/p&gt;

&lt;p&gt;同时，这样的草稿和练习，也给自己在平时工作和生活中，一个比较好的场景，让自己能够更好的去练习表达练习沟通，这就是聊天写稿，沟通写稿的意思。&lt;/p&gt;

&lt;p&gt;当然，更多的时候是没有时间打草稿的，而是即兴讲，那怎么办呢，也有对策，如果你每天讲故事，每天写故事的话，总会那么几个故事，你可以讲很多遍。&lt;/p&gt;

&lt;p&gt;所以想要故事讲的好，想要随口就来、张口就上的地步，你就要每天都在练习讲故事，把讲故事的能力深深印刻在自己肌肉里。&lt;/p&gt;

&lt;p&gt;（讲故事框架图）&lt;/p&gt;

&lt;p&gt;接着来说说故事内容本身，实际上，故事内容本身结构上有很多套路。&lt;/p&gt;

&lt;p&gt;比如说“勇难成，勇敢出发、艰难取胜、成长回归”是用的最多的一种，也可以“是什么、为什么、怎么办”，同时也可以是“结果、阻碍、努力、结果”等。
都是值得借鉴的模式，这些模式让我们在说故事和准备故事时有了可以矫正的参考尺。&lt;/p&gt;

&lt;p&gt;拿“勇难成”举个例子：&lt;/p&gt;

&lt;h3 id=&quot;勇&quot;&gt;勇：&lt;/h3&gt;

&lt;p&gt;我一想到高考承载着我未来的希望，是我通往梦想的桥梁，我就行动起来了，为了上好大学努力学习。&lt;/p&gt;

&lt;h3 id=&quot;难&quot;&gt;难：&lt;/h3&gt;

&lt;p&gt;可是经过几个月没日没夜的努力，自己的成绩依然没有变动，洁白的考试纸上赫然写着一个数字65分，我的脸色苍白。
紧接着，我开始思考我的学习效率问题，终于我发现我练习的太快太多却没有复习和订正，这导致自己学了后面忘了前面。
于是我开始放慢脚步，每次写完作业，考完试，在批改后都仔仔细细的订正和复习，让遗漏的知识点，一个个的挑出来逐项学习理解加深印象。
终于期末考试结束了，我拿到批改过的考卷时，手里有些颤抖，眼里喊着泪水，我的努力没有白费，我得到了自己想要的分数，太棒了。&lt;/p&gt;

&lt;h3 id=&quot;成&quot;&gt;成：&lt;/h3&gt;

&lt;p&gt;我高高兴兴的把试卷拿回了家。这次的经历给了一个深刻的印象，很多时候我们都太快了，实际上快并不能代表什么，没有头脑的努力只会浪费精力和时间。这次的经历让我明白，只有当我们观察到自己的问题，分析问题、调整方向后，我们才能获得更有效的行动。&lt;/p&gt;

&lt;p&gt;拿“是什么、为什么、怎么办”举例子：&lt;/p&gt;

&lt;h3 id=&quot;是什么&quot;&gt;是什么：&lt;/h3&gt;

&lt;p&gt;2017年时我阅读了很多书，而且我发现我阅读的速度越来越快，同时发现我的阅读效率并不高，读了很多书但记不住，有时候连书名都忘了。&lt;/p&gt;

&lt;h3 id=&quot;为什么&quot;&gt;为什么：&lt;/h3&gt;

&lt;p&gt;阅读效率为什么会这么低呢？我一直在琢磨着。后来我发现，我读的书太快了，囫囵吞枣，就跟看小说一样，一页页翻过去，从眼睛里进，从耳朵里出，脑子过一遍然后就忘记了。&lt;/p&gt;

&lt;h3 id=&quot;怎么办&quot;&gt;怎么办：&lt;/h3&gt;

&lt;p&gt;于是我特意找了三本关于如何阅读的书，其中包括《如何阅读》、《如何有效阅读》、《如何阅读一本书》，经过对阅读书籍技巧的学习，我开始注重阅读效率。比如，我开始培养自己在书上记笔记的习惯，每次看书时我都会在书本上写下自己的感想，记下与作者有共鸣的部分。同时，我开始用不同的阅读速度来对待不同类型的书籍，有的书不用细看因为知识点不是很深，快速阅读一遍就能快速总结出来书中的内容，而有的书则思想深刻需要我去细细品味。又比如，我会为每一本书写一篇文章，经典的书籍会写三四篇用于加深理解。再比如，我开始用演讲的方式输出书中所学，把自己所学的所理解的分享出来，跟大家一起学习同时加深我对这方面知识的理解。&lt;/p&gt;

&lt;p&gt;拿“结果、阻碍、努力、结果”举例子：&lt;/p&gt;

&lt;h3 id=&quot;结果&quot;&gt;结果：&lt;/h3&gt;

&lt;p&gt;这些年经过我的学习和努力，我赢得了女儿的信任。&lt;/p&gt;

&lt;h3 id=&quot;阻碍&quot;&gt;阻碍：&lt;/h3&gt;

&lt;p&gt;在女儿刚出生时那3年，我并不清楚如何当一个好爸爸，我还一直用管员工的方式来管孩子，所以那段时间孩子跟我相处的很不愉快。每天到家我问孩子的第一句话便是，今天听妈妈的话了吗？或者今天有学习功课吗？还时不时武力威慑孩子，这让孩子很痛苦，时常会选择逃避，同时感觉压力很大。我与女儿的关系很难融洽的相处。&lt;/p&gt;

&lt;h3 id=&quot;努力&quot;&gt;努力：&lt;/h3&gt;

&lt;p&gt;于是我开始学习一些教育方面的知识。一开始我到处乱翻资料，有网上的评论，抖音上的资料，知乎上的介绍等等，看的我眼花缭乱，但一点都没学进去。于是开始看书，我找了《成长心理学》《自卑与超越》《终身成长》等来看，还觉得意犹未尽，又看了《非暴力沟通》《沟通的方法》等，发现书中的知识点和技巧与我现在当下的情况非常符合。我还为他们每本书写了一片总结文章，让自己的想法和感受沉淀下来，我对沟通和成长的知识理解更深刻了。我又一点点的落实技巧，比如我每个月给孩子写一封信，每封信都会录一个视频，跟孩子聊聊天讲讲我遇到的故事。&lt;/p&gt;

&lt;h3 id=&quot;结果-1&quot;&gt;结果：&lt;/h3&gt;

&lt;p&gt;经过1年多的不断学习和努力，我和孩子的关系终于又开始亲密了，女儿很喜欢和我在一起，也喜欢跟我一起看书，一起学习。我赢得了女儿的信任。&lt;/p&gt;

&lt;p&gt;除了故事结构，故事本身内容也非常重要，只有故事内容吸引人，才能让故事体现出价值。&lt;/p&gt;

&lt;p&gt;因此，导师把这个“故事的吸引力”定义为故事力。&lt;/p&gt;

&lt;h2 id=&quot;故事力&quot;&gt;故事力&lt;/h2&gt;

&lt;p&gt;故事力表现为分布在三个地方，故事的开头，故事的内容，故事的收尾。&lt;/p&gt;

&lt;p&gt;要把故事讲好，首先讲话的开头很重要，可能并不是故事的开头，但一定是你演讲的开头。&lt;/p&gt;

&lt;p&gt;好的演讲的开头，能提起观众的兴趣，能吸引观众的注意力，能吊起观众的好奇心。&lt;/p&gt;

&lt;p&gt;总而言之就是吸引他们关注你下面的内容，这称为“龙头”。&lt;/p&gt;

&lt;p&gt;故事的内容上，我们不仅仅要考虑故事的结构，更我们同样要考虑到观众，考虑观众的兴趣点，考虑观众的思考点，考虑观众的共鸣点。只有从观众角度考虑故事的内容和编排，我们的演讲才能最大限度的发挥其效果。&lt;/p&gt;

&lt;p&gt;另外，我们在讲故事时也有技巧，这在《如何讲好一个故事》这本书的总结中我写过讲故事的关键。&lt;/p&gt;

&lt;p&gt;简单来说就是用五感法描述场景和细节，听觉、触觉、嗅觉、视觉、味觉，把这五感描述出来，与这五感无关的评判都去除，让观众发挥想象的空间，对故事的画面自由想象。&lt;/p&gt;

&lt;p&gt;最后故事的收尾也非常重要，一个好的收尾可以让演讲由差变好，由好变精彩。&lt;/p&gt;

&lt;p&gt;收尾是至关重要的环节，也是给观众留下最大印象的环节，因此要认真对待。&lt;/p&gt;

&lt;p&gt;导师说，这个叫“凤尾”，我们可以用很多种技巧来收尾，比如排比就很好。&lt;/p&gt;

&lt;p&gt;我拿最近一篇演讲稿的收尾来示范：&lt;/p&gt;

&lt;p&gt;最后我想告诉大家，其实生活和远方是可以兼顾的，虽然很辛苦，但却真的很值得，可能过程中，你会面对诸多的压力，诸多的困扰，但是请相信我，只要你坚持下去，一直朝着远方前进，前进、前进，我们就一定能够成为更美好的自己。&lt;/p&gt;

&lt;p&gt;其中“前进、前进、前进”三个重复词，让演讲的收尾变得很燃，鼓动了大家澎湃的心。&lt;/p&gt;

&lt;h1 id=&quot;语音语速语调技巧&quot;&gt;语音语速语调技巧&lt;/h1&gt;

&lt;p&gt;这一课时我的弱项，因为我站在演讲台上时，时常控制不了自己的音量和语气。&lt;/p&gt;

&lt;p&gt;我记得我最近一次在台上演讲时，就发现自己全程都用一个高音量在台上演讲，当我回顾自己的演讲时听的我鸡皮疙瘩都起来了，高音量就像是吵架一样很难听。&lt;/p&gt;

&lt;p&gt;这节课的内容非常适合我，对于我来说，语速、音量、语调都是需要锻炼的地方。&lt;/p&gt;

&lt;p&gt;（声音美化要点图）&lt;/p&gt;

&lt;p&gt;导师提出了核心观点：要富于变化，且要变化的合适。&lt;/p&gt;

&lt;p&gt;什么是富于变化且合适呢？它分为三个方面，语速、音量、语调。&lt;/p&gt;

&lt;h2 id=&quot;语速&quot;&gt;语速&lt;/h2&gt;

&lt;p&gt;语速决定这段内容的基调，它有快节奏，慢节奏，快慢节奏融合的情况。&lt;/p&gt;

&lt;p&gt;我常常上台时会紧张，能从嘴巴里蹦出内容来已经非常不容易了，因此常常忽略语速。&lt;/p&gt;

&lt;p&gt;有一次上台，主持人给了我7分钟时间来讲我自己的故事，我为此准备了1300字左右的稿子，并且在台下已经背的滚瓜烂熟。&lt;/p&gt;

&lt;p&gt;上台后说完第一句话“大家晚上好”就观察到自己的脑袋紧绷，左右太阳穴上两条神经拉紧着，无法思考太多。&lt;/p&gt;

&lt;p&gt;这时我急切的想说稿子的内容，于是就像背书那样，文字内容急速的从我的嘴巴里冲了出来。不到5分钟，我就把1300字的内容全说完了，下面的观众一脸茫然。&lt;/p&gt;

&lt;p&gt;当时我并不以为然，我的敏感度还没有关注到语速上，所以感知不到速度带给我的影响，但观众的感觉和反应是实实在在的，结束后有点评人给我反馈，只顾着快速的输出完全没有顾及观众的感受。&lt;/p&gt;

&lt;p&gt;（语速变化图-图来源网络）&lt;/p&gt;

&lt;p&gt;举个节奏变化的例子：&lt;/p&gt;

&lt;p&gt;我们加快了脚本，三步并成两步走着，不一会就到达了港口，并且马不停蹄的上了船回到了房间。（快语速）&lt;/p&gt;

&lt;p&gt;这时的我们心中的石头才渐渐落下，眉头开始舒展，紧张的情绪慢慢舒缓。（慢语速）&lt;/p&gt;

&lt;p&gt;小明吐了口气，道“好惊险，差点没赶上”，天天回到，“如果有辆车就好了，这样我们可以慢慢来”。（中语速）&lt;/p&gt;

&lt;p&gt;此时，小天打开了窗户，只听见“轰轰轰”三声船鸣声，游轮开始出发了，但是没过多久，“咣机”震了一下，大家的心又被提上来了。（先慢后快）&lt;/p&gt;

&lt;p&gt;小明立刻推开门，朝船的西面望去，我们游轮尽然搁浅在了港口边。（先快后中）&lt;/p&gt;

&lt;p&gt;大家要注意，语速也是节奏，是演讲的关键点，我们不能一直快同样也不能一直慢，要记住我们提到过的核心：富于变化且合适。&lt;/p&gt;

&lt;h2 id=&quot;语音&quot;&gt;语音&lt;/h2&gt;

&lt;p&gt;音量分为大、中、小三档，用来表达不同场景中的人物情绪和现场情景。&lt;/p&gt;

&lt;p&gt;举个例子，我们在表达激昂情绪时就会用大音量，特别是在演讲收尾时，我们时常需要做出一些行动呼吁，就需要大音量来带动观众的情绪。&lt;/p&gt;

&lt;p&gt;再举个例子，表达宁静安详的场景时就会用中音量，演讲中我们运用在叙述周围的环境周围的景物，例如我们在看日出，描写缓缓升起的太阳，或在办公室里环视着周围的布置等等。&lt;/p&gt;

&lt;p&gt;再举个例子，表达温柔，含蓄，谨慎时就会用到低音量，例如我轻轻的告诉身边的同事，台上的主持人是我初中同学。例如妻子双手轻轻的搭在我的肩上，凑到我的耳边低声说道“老公，我们出去吃吧”。&lt;/p&gt;

&lt;p&gt;（语音变化图-图来源网络）&lt;/p&gt;

&lt;p&gt;不同大小的音量表达了不同的情景，富于变化的音量给故事增添不少色彩，这会在演讲中给观众和自己非常强烈的印象。&lt;/p&gt;

&lt;h2 id=&quot;语调&quot;&gt;语调&lt;/h2&gt;

&lt;p&gt;语调用来塑造语境，这方面我可以说完全没有经验，这次训练也是初步接触。&lt;/p&gt;

&lt;p&gt;语调分为三种，升调、平调、曲调。&lt;/p&gt;

&lt;p&gt;升调一般用在肯定、赞美、祝福等，例如，“你做的真棒！”，“你的未来会更美好！”，“太棒了吧，你刚才的那番话引起了大家共鸣”等等。&lt;/p&gt;

&lt;p&gt;平调一般用于陈述、庄严、冷淡等，例如，“新华社消息”，”这里是北京朝阳区中关村，我在2022年6月30日在此为大家报道”，“当前任务正在执行中”等等。&lt;/p&gt;

&lt;p&gt;曲调通常用于讽刺、厌恶等，例如，“你的吃干饭的吗”，“我们都在太阳底下干活，而你，却在办公室里躺着睡觉”，“他突然考了这么高的分数，会不会是作弊”等等。&lt;/p&gt;

&lt;p&gt;（语调变化图-图来源网络）&lt;/p&gt;

&lt;p&gt;语调随着内容的变化而变化，让我们在讲出情节的同时，能够身临其境的体会当时的氛围，让观众感受到并发挥想象空间。&lt;/p&gt;

&lt;h1 id=&quot;表情手势移动技巧&quot;&gt;表情、手势、移动技巧&lt;/h1&gt;

&lt;p&gt;以前对姿态了解的并不是很深刻，这是由于自己无法评估自己的姿态带给观众的感受。&lt;/p&gt;

&lt;p&gt;但自从自己开始复盘自己的演讲视频后，逐渐发现姿态的重要性，一个好的姿态呈现给别人的感觉是完全不一样的，即是你不说话，或者说错了话，好姿态与坏姿态给观众的感受时完全两个样子，好姿态给观众感受时优雅从容，坏姿态给观众感受是“蹩脚”“糟心”甚至“猥琐”。因此后来我特别在意姿态，但始终没有养成好习惯，这次训练让我能在这方面得到提高，把好姿态融入到习惯中去，而且生活和工作中也需要好姿态。&lt;/p&gt;

&lt;p&gt;（表情手势动作要点图）&lt;/p&gt;

&lt;p&gt;导师说这是“台风”，从视觉方面呈现出，我们的风度和气质。&lt;/p&gt;

&lt;p&gt;其实在第一课里说过“三定”就是姿态里的技巧，我们在台上时身体要站稳挺直、眼睛要扫视观众、脸上要有微笑，这不仅让观众在视觉上有很好的体验，感受到我们站台上的优雅，而且我们自身在保持这种姿态时也会给自己一个非常大的信心，为后面即将进行的演讲打好基础。&lt;/p&gt;

&lt;h3 id=&quot;导师说台风包括三部分面部表情手势移动&quot;&gt;导师说台风包括三部分：面部表情，手势，移动&lt;/h3&gt;

&lt;h2 id=&quot;面部表情&quot;&gt;面部表情&lt;/h2&gt;

&lt;p&gt;面部表情关键是，脸部要放松，切记僵硬，可以时不时微笑，也可以做各种与表达内容相匹配的表情动作。&lt;/p&gt;

&lt;p&gt;我时常能感觉到在上台时，脸部肌肉的抽搐，这是我紧张和紧绷导致的。我试图去化解很多词，但任然掌握的不熟练。&lt;/p&gt;

&lt;p&gt;紧张情绪的化解方法我们在前面第一部分内容中已经提及，可以回头去看下三熟三定部分的内容，我认为这是非常好的化解紧张的技巧，同时是基础能力也是核心能力。&lt;/p&gt;

&lt;p&gt;我清晰的记得我最近一次上台分享我的技术，也就是上周的事，上台后由于过分紧张且没有很好的运用技巧去化解自己的紧张，导致我整个演讲都语速超快，逻辑混乱，会前想好要着重强调的内容一个没讲，还把不该讲的讲进去了。回头看了我自己录制视频（我在分享ppt时，特意用电脑录制了我上半身的动作和表情），观察到自己的表情僵硬且死板，脸上没有挂着一丝笑容，从脸部表情上就能感觉到我的紧张和不知所措。&lt;/p&gt;

&lt;p&gt;所以我想说，面部表情真的非常关键，脸部的肌肉放不放松这是完全的两种状态。&lt;/p&gt;

&lt;h2 id=&quot;手势&quot;&gt;手势&lt;/h2&gt;

&lt;p&gt;手势是我在演讲时一直在关注的东西，可惜前面没有去系统性的学习，导致我在平时讲话和演讲时手势都是乱的，常常与内容不符，观众看到的只是手舞足蹈的样子，这导致两个结果，第一发挥不稳定，当不知道该用什么手势和动作表达时就会乱作一团，第二呈现混乱，该有动作时没有动作，不该有动作时乱动，内容与动作不匹配，观众的注意力无法集中。&lt;/p&gt;

&lt;p&gt;（手势图-图来源网络）&lt;/p&gt;

&lt;h2 id=&quot;指掌拳&quot;&gt;指、掌、拳&lt;/h2&gt;

&lt;p&gt;导师说手势可分为，指、掌、拳。&lt;/p&gt;

&lt;p&gt;实际上，手势的作用非常大，可以丰富表达的同时也更有趣了。&lt;/p&gt;

&lt;p&gt;手势在恰当的时候可以弥补我们口语的不足，为情节内容增添色彩和动感。&lt;/p&gt;

&lt;p&gt;它不仅可以激发听众的热情，还能加深观众对内容的理解。&lt;/p&gt;

&lt;p&gt;导师说可以把做手势的区域拆分一下，上去、中区、下区。&lt;/p&gt;

&lt;p&gt;上区，表达激昂慷慨的情况&lt;/p&gt;

&lt;p&gt;中区，叙事事物，说明事理，较为平静的表达&lt;/p&gt;

&lt;p&gt;下区，表达负面情绪，憎恨、反对、批评、嘲讽等&lt;/p&gt;

&lt;p&gt;举例上区手势，&lt;/p&gt;

&lt;p&gt;“相信我，我们一定能赢！（握拳举手）”，&lt;/p&gt;

&lt;p&gt;“我们的目标是奔向全球，奔向世界（双手举头顶）”。&lt;/p&gt;

&lt;p&gt;举例中区手势，&lt;/p&gt;

&lt;p&gt;“我走到了经理室门前，深深吸了口气，自己倒数3、2、1（拿出手指笔画321）”，&lt;/p&gt;

&lt;p&gt;“我看到她的眼睛里泪水在打转（模拟打转的手势），手上还一刻不停的在缝补衣裳（模拟缝补的手势）”。&lt;/p&gt;

&lt;p&gt;举例下区手势，&lt;/p&gt;

&lt;p&gt;“他们就是这样践踏我们的尊严的（手指向下的手势）”，&lt;/p&gt;

&lt;p&gt;“那能怎么办，我已经无计可施了（两手掌摊开来）”，&lt;/p&gt;

&lt;p&gt;“你还是走吧（向外挥手），这里不需要你（左右摇手）”。&lt;/p&gt;

&lt;p&gt;三个区域的手势对应上表达的场景和情绪，让内容更添色彩。&lt;/p&gt;

&lt;h2 id=&quot;手势六道菜记忆法&quot;&gt;手势六道菜记忆法&lt;/h2&gt;

&lt;p&gt;（手势图-图来源网络）&lt;/p&gt;

&lt;p&gt;导师用六道菜的记忆法，解剖了我们做手势时的动作，即点菜、切菜、锤菜、炒菜、感恩菜。&lt;/p&gt;

&lt;p&gt;点菜，与身子保持45度，伸出手指，1、2、3&lt;/p&gt;

&lt;p&gt;切菜，拇指与手掌垂直，上手臂和身子45度&lt;/p&gt;

&lt;p&gt;锤菜，与身子保持45度，上锤、下锤&lt;/p&gt;

&lt;p&gt;炒菜，从外到内，从下到上&lt;/p&gt;

&lt;p&gt;感恩菜，摸着胸口&lt;/p&gt;

&lt;p&gt;这是手势基本功，单手点、单手或双手切、单手或双手锤、两手炒，摸胸口，我们可以在平时的演讲中在做手势时多注意练习，切记死板的运用，而是灵活的组合和变化。还记得我们说的核心吗，富于变化且合适。&lt;/p&gt;

&lt;p&gt;举例台上的手势配合内容：&lt;/p&gt;

&lt;h3 id=&quot;点菜&quot;&gt;点菜，&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;“我现在要讲3个方面内容（手指1、2、3），第一…第二…第三…”，&lt;/li&gt;
  &lt;li&gt;“我们的业绩每年都在上升（食指手指向上）”，&lt;/li&gt;
  &lt;li&gt;“希望就在前方（食指向前）”，&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;切菜&quot;&gt;切菜，&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;“从12年前的杭州，到8年前的上海，再到3年前的深圳，我一直在不断努力的去寻找上升通道（手势从12切到8切到3）”，&lt;/li&gt;
  &lt;li&gt;“保持节奏，稳住情绪，步步向前（切手势从上到中到切移动）”，&lt;/li&gt;
  &lt;li&gt;“你过去的思维方式和现在的思维方式已经完全不同了（切手势从左移动到中间位置）”，&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;锤菜&quot;&gt;锤菜，&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;“我们能挺的过去（上方做锤手势）”，&lt;/li&gt;
  &lt;li&gt;“我可不是个轻易服输的人（中间做锤手势）”，&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;炒菜&quot;&gt;炒菜,&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;“这是一个相辅相成的过程（顺时针炒菜）”，&lt;/li&gt;
  &lt;li&gt;“工作就是修行，内修外炼（顺时针炒菜）”，&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;感恩菜&quot;&gt;感恩菜，&lt;/h3&gt;
&lt;ul&gt;
  &lt;li&gt;“他的一举一动我都看在眼里，我很感动（摸胸口）”，&lt;/li&gt;
  &lt;li&gt;“我带着喜悦的心情踏上了回杭州的火车（摸胸口）”，&lt;/li&gt;
  &lt;li&gt;“他从我侧面走过没有理我，我很沮丧，心里想着我该怎么办（摸胸口）”，&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;手势除了配合内容外，还可以用于表达情绪的强弱，即调整手势的强弱，如：刚强的风格（迅速有力），温柔的风格（缓慢优雅）。&lt;/p&gt;

&lt;p&gt;注意：舞台上不能用手指指着观众，可以用上菜手势请观众。&lt;/p&gt;

&lt;h2 id=&quot;移动步伐&quot;&gt;移动步伐&lt;/h2&gt;

&lt;p&gt;我以前在舞台上一直会有来回走动的习惯，这是我的一个坏习惯，我通常会用不停来回走动的方式来回想我想讲的内容，或思考我想讲的内容。但我后来发现，这个习惯非常不好，首先不停的在观众眼前幌来幌去，给观众的视觉体验很差，其次让人感觉我没有准备的充分。&lt;/p&gt;

&lt;p&gt;所以后来我不再走来走去去了，稳稳的站着讲，虽然培养这个习惯并不容易，但稳稳的站着讲也有问题。没有动感，让人觉得死板。&lt;/p&gt;

&lt;p&gt;最后才领悟到前面说的关键，要富于变化且合适，不要乱动也不要不动，要根据内容需要动，下面就来说说移动的技巧。&lt;/p&gt;

&lt;p&gt;（步伐图-图来源网络）&lt;/p&gt;

&lt;h2 id=&quot;上台五步走&quot;&gt;上台五步走&lt;/h2&gt;

&lt;p&gt;导师说上台可以拆分成五步，即：起身、上台、致敬、交接、开场，下场。&lt;/p&gt;

&lt;p&gt;起身稳健，起身时不要轻佻这会让人有随意的印象，要时刻准备着，时刻有相机对准的准备，稳健让人看着舒服。&lt;/p&gt;

&lt;p&gt;上台注意台阶，上台由于紧张、路滑等原因，时常容易绊倒，因此我们要准备一些绊倒的句子，这样不仅巧妙化解了尴尬同时也能为自己加分。&lt;/p&gt;

&lt;p&gt;挥手致意，眼神传情，（这是大方，得体的表现，我们在压力下表现的越得体，就越能体现我们的综合素质）。&lt;/p&gt;

&lt;p&gt;微笑示意，主动接麦，（这是大方，得体的表现，我们在压力下表现的越得体，就越能体现我们的综合素质）。&lt;/p&gt;

&lt;p&gt;扫视震场，一鸣惊人，上台不必急着讲内容，这会让自己过早进入紧张状态，我们可以先练习三定再扫视全场，说出的第一句话一定要一鸣惊人，因为第一句话是大家的注意力最集中的时候。&lt;/p&gt;

&lt;p&gt;下场时，挥手示意，眼神传情，得体收尾。&lt;/p&gt;

&lt;p&gt;这五步不容易，拥有大量丰富经验的人有时也做不好，或出意外或有遗漏，可想而知，基础能力（核心能力）有多重要。
舞台走动&lt;/p&gt;

&lt;p&gt;当我在舞台上走动时，时常不知道我为什么要走动，以前我的走动都是随意的，没有目的的。&lt;/p&gt;

&lt;p&gt;这导致一个问题，观众弄不清我为什么要走动，即使我的内容吸引了观众的注意力，同时他们的注意力会随时被我的小动作分散，最后被我的走动弄的精疲力劲，失去兴趣。&lt;/p&gt;

&lt;p&gt;所以走动也有技巧，也需要符合内容和情节以及目的的需要。&lt;/p&gt;

&lt;p&gt;导师说，走动技巧有，横走，纵走，侧身跨步。&lt;/p&gt;

&lt;p&gt;当我们需要观众记住内容的时候，横着走，比如，强调知识点&lt;/p&gt;

&lt;p&gt;当我们需要拉近与听众距离的时候，纵着走，比如，哪位伙伴能回答这个问题&lt;/p&gt;

&lt;p&gt;当我们模仿不同角色对话时，可以左右侧身来跨步来模拟，比如，模仿两人对话时，左右侧身&lt;/p&gt;

&lt;p&gt;注意：不要动太多，容易引起视觉疲劳，忽视了内容。&lt;/p&gt;

&lt;h1 id=&quot;比赛与准备&quot;&gt;比赛与准备&lt;/h1&gt;

&lt;p&gt;（比赛图-图来源网络）&lt;/p&gt;

&lt;p&gt;小树林演讲比赛是比较正规的，有主持人有评委有时间官。&lt;/p&gt;

&lt;p&gt;比赛给人的历练最大，提高提升的最快，前面如果实在太忙可以不参加，但比赛一定要全力以赴，他能一下子拔高你的好几级。&lt;/p&gt;

&lt;p&gt;我曾有一次在小树林精英训练营中，由于实在太忙，没时间做前面的培训，但依然硬着头皮来到了最后的比赛日。&lt;/p&gt;

&lt;p&gt;比赛日前我还没写稿，逼着自己早上6点多起床，写演讲稿，写到9点半，差不多了，跟导师请教下演讲稿的调整和更改。&lt;/p&gt;

&lt;p&gt;然后开始练习和演绎演讲稿，顾不上吃饭，中途为了让自己形象更好一些，回去换了件衣服，来回路上都不忘在练稿，就这样折腾到下午2点半开始上台演讲。&lt;/p&gt;

&lt;p&gt;上台时，我脑子里转的都是如何把我学到的技巧运用到这次演讲中去，我豁出去了，所有我能想到的技巧都使出来，听朋友们说，相当精彩，可惜最后超时无法参与评分（反面教材）。&lt;/p&gt;

&lt;p&gt;最后我很清楚的看到自己收获非常大，有种蜕变的感觉，这是我参加现场比赛努力认真对待的结果。最后的最后我对我的比赛视频进行了复盘，并对其关键问题做了调整和完善，运用在未来的&lt;/p&gt;

&lt;p&gt;（比赛要点图）&lt;/p&gt;

&lt;h2 id=&quot;赛前准备&quot;&gt;赛前准备&lt;/h2&gt;

&lt;p&gt;我经历过几次比赛，所以对比赛前的准备有自己的心得，下面我来阐述一下。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;1.比赛前首当其冲的是打磨演讲和讲稿，我们要花大量时间来想结构、故事、过渡和情节描述。&lt;/li&gt;
  &lt;li&gt;2.其次是练习演讲，把所学的内容全部运用进去，包括姿态、动作、声音、故事、表情等。练习是时务必把手、眼、声音、走的技巧运用进去，用情景再现的方式去演绎故事和场景。&lt;/li&gt;
  &lt;li&gt;3.再者上台前，我喜欢用冥想的方式先让自己冷静下来，让紧张的情绪得到舒缓。&lt;/li&gt;
  &lt;li&gt;4.接着，我会不断的念演讲稿，这时我肯定已经背了很多遍了不用再背了，就用念稿的方式来增加脸部的肌肉记忆。&lt;/li&gt;
  &lt;li&gt;5.最后上台前3分钟，我喜欢不停的踱步，让自己保持兴奋且不致于过度兴奋。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;如果你没有比赛经验可以参考这份准备顺序，如果有已经有比赛经验则可以参考这份提高自己的赛前准备效率。&lt;/p&gt;

&lt;h2 id=&quot;演讲稿大纲&quot;&gt;演讲稿大纲&lt;/h2&gt;

&lt;p&gt;我举例最近一次比赛演讲稿的大纲，讲的是我在上海时的一段经历。&lt;/p&gt;

&lt;p&gt;大纲：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;1.开头，问题 + 我的感受&lt;/li&gt;
  &lt;li&gt;2.刚进入公司，很兴奋 + 忙碌&lt;/li&gt;
  &lt;li&gt;3.项目成型的同时发现问题&lt;/li&gt;
  &lt;li&gt;4.谋求转型但转型出现问题&lt;/li&gt;
  &lt;li&gt;5.描述自己如何转型，工作中争取时间，疲惫但坚持着&lt;/li&gt;
  &lt;li&gt;6.转型成功，把握机会，获得认可，达成目标&lt;/li&gt;
  &lt;li&gt;7.发起感悟，虽然艰辛但很值得，努力前进，终获新生&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485456&amp;amp;idx=1&amp;amp;sn=a69f45ddb80b9c31bd79c9eb9cfb84e3&amp;amp;chksm=fc226d17cb55e4016569080d868884610cd9fc49c2abba0ac7cb3e7eafbd19fa672201a53349&amp;amp;token=1541713099&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(六十一) 如何更好的沟通 - 沟通风格</title>
   <link href="http://www.luzexi.com/2022/06/15/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B061"/>
   <updated>2022-06-15T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/06/15/读书笔记61</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485405&amp;amp;idx=1&amp;amp;sn=cc17252f23ec91046532269d7bd6b22d&amp;amp;chksm=fc2262dacb55ebccb0c8f300db3cbed376d35ba1cfe6bb0061982c7f807b376c3ea6d8de2c93&amp;amp;token=1541713099&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;背景&quot;&gt;背景：&lt;/h1&gt;

&lt;p&gt;10年前，我对能说会道的人很厌恶，当时的我认为它是一个花里胡哨的东西，是一个专门糊弄人的技巧，特别讨厌。但是10年后的今天，我开始越来越关注它，首先是它对我的生活和工作产生了巨大的影响，我终于意识到10几年来我对它的疏忽导致自己在很多方面受到了巨大的限制。其次，我发现它是一门可训练的技巧，就像写作、编程、打鼓、修车一样是一门技能一种技巧，可以通过刻意的学习和训练来提高。&lt;/p&gt;

&lt;p&gt;对这两点的认知转变，对我极其重要，从此以后我开启了沟通技巧的刻意练习，并在生活和工作中观察自己的沟通技巧，越来越把沟通当作第一要素来认真对待。&lt;/p&gt;

&lt;p&gt;同时我看了7本书来做主题阅读，系统性的学习沟通技巧，它们包括《非暴力沟通》、《沟通的方法》、《社交与礼节》、《关键对话》、《高难度谈话》、《沟通的艺术》、《自信表达》，读完后写出自己的理解和总结，在生活和工作中去落地实践并总结经验，接着制作成一次讲座与大家分享我的实践经验。&lt;/p&gt;

&lt;p&gt;本篇主要围绕《沟通的方法》来提取知识和技巧，书本有370页左右，平时的书我看的很快基本一周一本，但这本书我花了将近1个月，因为书本中每个字对我来说都是醍醐灌顶，我深深被它吸引并仔仔细细的看完了每个字每句话，看完时我用四个字表达了我对此书的震撼：太强了吧。&lt;/p&gt;

&lt;h1 id=&quot;目录&quot;&gt;目录：&lt;/h1&gt;

&lt;p&gt;1.如何倾听
2.沟通风格
3.回应技巧&lt;/p&gt;

&lt;h1 id=&quot;概述&quot;&gt;概述：&lt;/h1&gt;

&lt;p&gt;沟通首先是倾听，其次每个人都有自己的沟通风格和沟通习惯，最后我们可以用一些通用技巧来与对方沟通，这样会更有方法些也更轻松一些。很多时候我们忽视了倾听，以为专注的听对方讲了什么就是倾听，其实不是的。不要小看倾听，倾听需要技巧，它是众多沟通技巧中的核心技巧之一，倾听不单单只是听，还要听懂对方背后的意思，只有听懂了背后的意思我们所说出的话才能真正达成我们想要的效果，我们也才能知道我们该如何去说。作者用结构化倾听，情绪、事实、期待，三方面来说明我们在沟通中如何做好倾听这件事。沟通对于每个人都是不一样的，每个人的沟通习惯有千千万万种，同时千千万万种沟通从整体上来看可以分成几类，作者就总结了这几类沟通的风格，同时跟我们阐述了我们在平时交流中所遇到的人，面对不同风格的人沟通该如何应对。最后作者说了几个即兴回应的的技巧，这些技巧都是常用的一些沟通手法，平时我们也常用只是没有注意到，当被以书面形式总结出来时，就会觉得它非同小可。&lt;/p&gt;

&lt;h1 id=&quot;内容&quot;&gt;内容：&lt;/h1&gt;

&lt;p&gt;很多人看到沟通中有强势主动的一方和弱势被动的一方，就会认定沟通是一方搞定另外一方的有限游戏。其实不是的，沟通是一场无限游戏。在沟通后，具体的沟通事项可能结束了，但双方的关系依然存在，每次沟通都会让双方的关系产生变化，其中沟通高手就能够让双方的关系在今后很长一段时间里持续发展下去，甚至永远发展下去。&lt;/p&gt;

&lt;p&gt;这本书的作者把沟通拆解程了18个基本场景，通过这些场景“积木”的组合可以应付各种各样的复杂情况。通过系统的学习，我们完全有机会实现从抗拒沟通到掌控沟通的脱变。这里是第一部分，主要讲倾听、沟通风格和回应技巧。
如何倾听&lt;/p&gt;

&lt;p&gt;沟通的起点是倾听，但是很不幸，我们平时沟通中有一半的信息会被自动忽略。常常我们并没有听出对方真实的意图，从而沟通中出现不少的问题。&lt;/p&gt;

&lt;p&gt;事实上，别人在表达时，无论是有意还是无意，都会隐藏一些信息。我们应该像侦探一样倾听，全神贯注，把所有隐藏线索都挖掘出来，拽在手里。所以只有全力以赴的去听，我们才能听出对方的真实意图，才能辨识出对方可能并不会说出口的那些潜在需求。
同时也只有让对方看到我们已经全力以赴地去听了，我们给他们的回应才能被友好地接受。&lt;/p&gt;

&lt;p&gt;这里作者列出了结构化倾听的是那个要点：情绪、事实、期待。&lt;/p&gt;

&lt;p&gt;（情绪、事实、期待图）&lt;/p&gt;

&lt;p&gt;首先是情绪，我们在沟通中，对方有时可能不会把情绪展露在表情上，但话语里隐藏了情绪。&lt;/p&gt;

&lt;p&gt;这时我们可以捕捉到这些词汇来辨识他们的内在情绪，例如：总是、老是、每次、经常、永远，千万别，等等情绪路标词。
这些词汇都是说明对方是在宣泄情绪。当路标词一旦出现，我们就要意识到，对方没有在陈述事实，而是在发泄情绪。&lt;/p&gt;

&lt;p&gt;其次是事实，我们只有在表达那些不受主观判断影响，可考证、可追溯的内容时，才会说它是一个事实。&lt;/p&gt;

&lt;p&gt;因此如果能用4个W（Who、Where、What、Why）还原实际场景，那么对方所言大概率是事实。
相反，如果对这些要素语焉不详，而仅仅从诸如“我觉得”、“我判断”、“我认为”的主观推论出发，那我们听到的陈述很可能不是事实。&lt;/p&gt;

&lt;p&gt;最后是期待，期待就是找出对方内心真正想要的东西，这里我们需要结合情绪和事实来判断对方的期待。
只有当我们发现对方的期待后，我们才能做出正确的反应。&lt;/p&gt;

&lt;h2 id=&quot;倾听后的回应&quot;&gt;倾听后的回应&lt;/h2&gt;

&lt;p&gt;我们在倾听时有一个重要的技能就是总结，当对方说完时我们需要做一些回应，其中总结对方说话的内容非常重要，如果对方说太多时对方自己也记不住，这时我们做一个总结，对方会觉得我是真的很认真在听他说话，他说的话起到了效果，他得到了尊重。
这个方法作者称为“反向叙述”，我称它为“倾听总结”，都是一个意思，即按照自己理解的逻辑，重新描述一遍前面结构化倾听获得的信息，请对方做个确认。通过反向叙述的过程，双方有机会把对方的信息彻底听明白，更知悉彼此的意图。&lt;/p&gt;

&lt;p&gt;反向叙述也要掌握方法，这样才能做到正确的反向叙述，做到有效的沟通。作者把这一个技巧分成三步，响应情绪、确认事实、明确行动。&lt;/p&gt;

&lt;p&gt;（回应总结三步骤）&lt;/p&gt;

&lt;h2 id=&quot;第一步响应情绪&quot;&gt;第一步响应情绪&lt;/h2&gt;

&lt;p&gt;这是一个排雷的步骤，首先我们要意识到对话前有很多雷在等着我们，一不小心踩到了就会有很多麻烦，效率和效果也就大打折扣，所以首先要排雷，这里的雷就是对方的情绪。&lt;/p&gt;

&lt;p&gt;对方和我们一样身上有很多情绪，很多时候连我们自己都无法察觉到（察觉到也无法化解它们），所以首先要解决情绪。这里有个关键，做事前先解决情绪，这个我们后面再细聊，但它确实很重要我先点出来。&lt;/p&gt;

&lt;p&gt;我们在沟通中之所以会不清楚对方到底想要什么，很多时候是因为情绪的阻隔。对方的情绪会影响他的思考和表达，这会增加我们倾听的难度，因此我们应该预先排除干扰信息，提前处理情绪这个地雷。&lt;/p&gt;

&lt;p&gt;然而情绪是对方的，就算我们识别出来了，怎么帮助对方把它从沟通中剥离呢？这里有个技巧：点破和接纳对方的情绪&lt;/p&gt;

&lt;p&gt;例如：我说，“我知道，这个时候你肯定特着急”&lt;/p&gt;

&lt;p&gt;这个技巧的核心是让对方感觉到他的情绪被你接纳了，这样他就会慢慢回归到理性状态，从而让沟通变得更加顺畅。&lt;/p&gt;

&lt;h2 id=&quot;第二步确认事实&quot;&gt;第二步确认事实&lt;/h2&gt;

&lt;p&gt;信息挖掘主要在这一步完成，我们可以先把听到的事实用自己的话描述一遍，此时如果正确，对方就会给你一个肯定的确认，如果不对，那他就会补充更多的信息。如果完全没听懂也不要逃避，借由一些提问技巧去追问。&lt;/p&gt;

&lt;p&gt;例如，我说，“您刚才说的这几点，我的理解是…”，“您能跟我再展开讲讲吗？”&lt;/p&gt;

&lt;h2 id=&quot;第三步明确行动&quot;&gt;第三步明确行动&lt;/h2&gt;

&lt;p&gt;按照前面的所有信息，把对方的期待翻译成接下来可实施的行动，让对方清晰地感受到，你确实听懂了他的意思，并按照他的期待规划出了行动。
例如，我说，“针对你说的情况我微调一下文档再明确一下方案，明天下午4点，我们讨论一下可以吗？”。&lt;/p&gt;

&lt;p&gt;到这一步，就完成了反向叙述，拿到了所有信息，也让对方放心的全过程。&lt;/p&gt;

&lt;p&gt;反向叙述的作用，当信息不全时，就要跟对方反述一遍，挖掘出更多隐含信息，判断出对方真实的期待。&lt;/p&gt;

&lt;p&gt;这里作者聊了下倾听工具，比如用手写笔记本记笔记、录音等都是非常好的倾听工具。&lt;/p&gt;

&lt;p&gt;作者举例自己记笔记的方法，空白页上左边记录事实，右边记录情绪。并且在记录时边听边问自己，什么是我需要做的？我该怎么行动？最后把行动清单整理出来，用特殊记号标记。另外，用手写笔记本的方法的好处之一是让对方看到你的尊重。&lt;/p&gt;

&lt;p&gt;录音软件也是一种方法，在记录下来后，回去一定要复听，这样可以复盘自己的沟通能力和信息的总结能力，同时可以复盘我们的沟通效果和情况。
沟通风格&lt;/p&gt;

&lt;p&gt;沟通对于每个人都是不一样的，因为我们的沟通习惯有千千万万种，同时千千万万种沟通从整体上来看可以分成几类，作者就总结了这4类的沟通风格，同时跟我们阐述了我们在平时交流中所遇到的人，面对不同风格的人沟通该如何应对。&lt;/p&gt;

&lt;h1 id=&quot;四种类型的沟通风格&quot;&gt;四种类型的沟通风格&lt;/h1&gt;

&lt;p&gt;首先我想阐述一下，作者所说的四类沟通风格是自己思考后总结出来的，实际上代表了4类具有鲜明性格的人，这里面所用的每种动物都是一种代表，我们也可以用自己的方式去实践、思考、总结，构建出一套属于自己的分类。不过在还没有这么丰富的经验前，不妨听听作者是如何总结的，学习了她的经验后再融入我们的经验最后转化成属于自己的总结。&lt;/p&gt;

&lt;h3 id=&quot;四种沟通类型代表了控制型表现型谨慎型温和型分别用老虎孔雀猫头鹰考拉来标注&quot;&gt;四种沟通类型代表了，控制型、表现型、谨慎型、温和型，分别用老虎、孔雀、猫头鹰、考拉来标注。&lt;/h3&gt;

&lt;p&gt;老虎型的特点是，目标感强，总是当机立断，能打仗但控制欲强。&lt;/p&gt;

&lt;p&gt;孔雀型的特点是，自来熟，经常把“关系维护”放在第一位，不自觉地取悦对方，具体沟通事项可能被搁置。而且他们非常愿意跟别人发生肢体接触。&lt;/p&gt;

&lt;p&gt;猫头鹰型的特点是，处事周全，善于观察，讲究事实依据。给人一种“聊天兴致不高”的感觉，但他们没有走神，一直在细心观察。迷恋结构、系统、流程，经常在不经意间流露出对严谨系统的崇拜。&lt;/p&gt;

&lt;p&gt;考拉型的特点是，相对被动，不愿意得罪人，时常展露出毫无攻击性的善意。&lt;/p&gt;

&lt;p&gt;这四种沟通风格可分为两类，主动型和被动型。&lt;/p&gt;

&lt;p&gt;老虎和孔雀就是主动型，老虎主要是为了掌控局面，会不由自主的成为控场者，而孔雀并没有主持意识，只想表现自己，取悦对方。&lt;/p&gt;

&lt;p&gt;猫头鹰和考拉虽然都经常表现得非常被动，但考拉的初衷是不得罪人，猫头鹰则是为了收集足够多的证据，决定接下来怎么做。&lt;/p&gt;

&lt;h2 id=&quot;不同风格怎样沟通&quot;&gt;不同风格怎样沟通&lt;/h2&gt;

&lt;p&gt;首先我们要明白两点：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;第一，不同沟通风格的人是均衡分布的。&lt;/li&gt;
  &lt;li&gt;第二，不同沟通风格的人之间会“不可避免”的产生矛盾。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;因此我们在工作、生活中会遇见各种沟通风格的人，和他们打照面时，我们要先摘除”喜欢不喜欢“的偏见，并通过学习，掌握“对他们最有效的沟通方式”。&lt;/p&gt;

&lt;p&gt;下面就来聊聊如何与不同风格的人沟通。&lt;/p&gt;

&lt;p&gt;（四种风格以及沟通的方法图）&lt;/p&gt;

&lt;h2 id=&quot;如何与老虎沟通&quot;&gt;如何与老虎沟通&lt;/h2&gt;

&lt;p&gt;第一，老虎喜欢直切主题&lt;/p&gt;

&lt;p&gt;所以沟通时就别做过多铺垫，也不要循序渐进，争取在一个回合里把他的疑惑解释清楚。&lt;/p&gt;

&lt;p&gt;第二，老虎喜欢有掌控感，所以别让老虎觉得你是一个“黑箱”，要给他足够的掌控感。&lt;/p&gt;

&lt;p&gt;虽然让老虎知道你的工作进度非常重要，但不等于天天汇报，和他约定一个时间定期汇报即可。&lt;/p&gt;

&lt;p&gt;第三，老虎的目标感很强，如果你发现对方没有设立目标，就表明他出于各种考虑憋着不表达自己真实意图。&lt;/p&gt;

&lt;p&gt;这时，就要让他提提意见，请求他把目标说出来，这才真正听懂老虎的无声呐喊。&lt;/p&gt;

&lt;h2 id=&quot;如何与孔雀沟通&quot;&gt;如何与孔雀沟通&lt;/h2&gt;

&lt;p&gt;第一，孔雀喜欢表现自己&lt;/p&gt;

&lt;p&gt;所以只要表达对孔雀的喜爱就能让他特别满意，不过你要坚持不懈的表达喜爱，你得绞尽脑汁，变着花样赞美。&lt;/p&gt;

&lt;p&gt;第二，孔雀喜欢比较重视感受&lt;/p&gt;

&lt;p&gt;如果孔雀一直没表现出喜悦，则赶紧问他“现在有什么感受？”，不是问有什么想法、建议，而是感受。&lt;/p&gt;

&lt;p&gt;可以说人人都需要被看见，孔雀则特别需要被看见，他对感受很敏感，最受不了的就是他的感受被忽略。&lt;/p&gt;

&lt;h2 id=&quot;如何与猫头鹰沟通&quot;&gt;如何与猫头鹰沟通&lt;/h2&gt;

&lt;p&gt;第一，猫头鹰处事周全，谨言慎行，所以他不会表扬人，他们要得出一个人“是否靠谱”的结论，要建立在足够多的证据上。&lt;/p&gt;

&lt;p&gt;所以如果猫头鹰没表扬你，不代表他不喜欢你，他只是在收集信息。而且猫头鹰还会经常提出负面的反馈，他们会不断指出任务中潜在的风险点，不过他们不认为自己在反对你。&lt;/p&gt;

&lt;p&gt;此时只要你给足正面的证据，用它们来“覆盖”负面反馈，猫头鹰就会马上改变态度相信你。&lt;/p&gt;

&lt;p&gt;第二，猫头鹰迷恋流程、结构、系统&lt;/p&gt;

&lt;p&gt;所以为人处事要结构化、系统化，让他看到你的严谨和思维。&lt;/p&gt;

&lt;p&gt;第三，猫头鹰时常给人一种“聊天兴致不高”的感觉，所以如果他对某事很积极，先表态，可能是迫于生活经验在客气，想尽快结束对话。
这种情况，你要重新找个时间与他沟通，解决潜在的问题，别让他收集完证据后成为你坚定的反对派。&lt;/p&gt;

&lt;h2 id=&quot;如何与考拉沟通&quot;&gt;如何与考拉沟通&lt;/h2&gt;

&lt;p&gt;考拉比较被动，但他们是特别需要去维护和关照的一类人。&lt;/p&gt;

&lt;p&gt;当你和考拉沟通时，他说“都行”时，可能没讲真心话，也一定要去探寻“都行”背后的压力和顾虑。&lt;/p&gt;

&lt;p&gt;如果你是考拉，你要知道自己的“都行”是会让别人，特别是老虎抓狂的。要有意识的做出调整。&lt;/p&gt;

&lt;p&gt;一个人的复合沟通风格，大多是后天环境使然。这四种类型的人是极具鲜明的人格，但大部分人都是复合型人格，比如很多人具有老虎为主、孔雀为辅的复合特点，或者猫头鹰为主，老虎为辅的复合特点，或者猫头鹰为主，考拉为辅的特点，等等。这是由于他们生活的场景逼迫他们形成了这样或那样的习惯。
比如，老虎为了获得更多掌控感，渐渐培养出了煽动周遭人的“孔雀特征”。比如，猫头鹰难以忍受混乱，把自己逼成了老虎。&lt;/p&gt;

&lt;p&gt;另外，当我们面对一个职位很高，年龄偏大、经验又非常丰富的领导时，你其实很难读出他的沟通风格。这不是你的问题，而是对方随着“战斗值”的提升，把自己修炼成了非常均衡的风格，随机应变，顺势而为。&lt;/p&gt;

&lt;h2 id=&quot;小结&quot;&gt;小结&lt;/h2&gt;

&lt;p&gt;作者点出了两个核心要点，一是，与他人沟通时，要承认任何一种沟通类型的合理性，用最有效的方式与之沟通；二是，我们自己要努力修炼，学会在各种风格间切换，不让沟通能力“偏科”。当我们可以为了某个沟通任务，扮演一个与自己截然不同的角色时，我们的沟通能力就会得到显著提升。变化自己去适应环境。&lt;/p&gt;

&lt;h1 id=&quot;回应技巧&quot;&gt;回应技巧&lt;/h1&gt;

&lt;p&gt;在我们平时聊天中，不知道你有没注意到，很多时候我们回应的方式决定了事态的发展，因此作者特地把回应技巧单独提取出来，聊了一下这里面的技巧。她把回应技巧分为三部分，理念、技巧、习惯。&lt;/p&gt;

&lt;p&gt;我们首先来看看，核心理念有两个：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;核心理念一：有话要直说，方式要恰当。&lt;/li&gt;
  &lt;li&gt;核心理念二：真话不全说，假话绝不说。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;“有话要直说，方式要恰当”意思是，我们不可能只接受那些能处理的问题，任何问题我们都要给予积极的、善意的回应，只不过回应的技巧我们需要不断优化和改进。&lt;/p&gt;

&lt;p&gt;所以作者说，我们要记住一句话：一个沟通高手，应该有这样的觉悟，可能有我们解决不了的问题，但没有沟通不了的问题。&lt;/p&gt;

&lt;p&gt;“真话不全说，假话绝不说”意思是，无论用什么技巧，沟通中假话绝不说。&lt;/p&gt;

&lt;p&gt;一个谎言需要用10个谎言去掩盖，这会耗费我们大量的精力，效率低下。所以说真话是效率最高的，同时绝不说假话也是我们的底线。&lt;/p&gt;

&lt;p&gt;这里强调一下，任何一种工具、任何一种能力都有限制的，也都有边界的，包括沟通能力，实际上说真话就是沟通的能力边界。&lt;/p&gt;

&lt;p&gt;真话不全说，即“我不见得要把所有的真话都表达出来”。俗话说“直言也应该有讳”，我可以有选择的和对方交流，不合适的信息不说，不合适的场景也不说。&lt;/p&gt;

&lt;p&gt;假话绝不说，即“我可以用各种技巧来回应对方，但过程中绝不说假话”。我们说的任何一句话都可能被传出去，被公开化，我的职场信用很快就消失，此时我的沟通技巧再好也无用。&lt;/p&gt;

&lt;p&gt;沟通能力越强，就越得要求自己绝对不说假话，这是高效沟通的首要原则。&lt;/p&gt;

&lt;p&gt;（回应技巧图）&lt;/p&gt;

&lt;p&gt;下面我们来介绍下回应中的一些技巧，包括换口径、换时间、换场合、换角色。&lt;/p&gt;

&lt;h2 id=&quot;换口径&quot;&gt;换口径&lt;/h2&gt;

&lt;p&gt;换口径是指，对方问你的是A问题，但你偷换个概念，用B口径来回应他。&lt;/p&gt;

&lt;p&gt;例如，对方说，“您可真是大忙人呢”，我说，“我就佩服你这样的大拿，什么事到你手上都能解决”&lt;/p&gt;

&lt;p&gt;当然这个方法有一定风险，分寸很难拿捏，特别容易被人看成抖机灵。&lt;/p&gt;

&lt;h2 id=&quot;换时间&quot;&gt;换时间&lt;/h2&gt;

&lt;p&gt;换时间是指，另找时间来解决对方的问题。&lt;/p&gt;

&lt;p&gt;当遇到突发问题时，一点心里准备都没有，而且只能回答行或不行。很多人在这个时候一定特别紧张，此时我们可以用换时间的方法，化被动为主动。&lt;/p&gt;

&lt;p&gt;例如，我说，“领导，这么重要的事情，我得认真想想。下周一我来给您汇报，您看行吗？”&lt;/p&gt;

&lt;p&gt;实际上给多长时间一点都不重要，重要的是，当你再次发起沟通时，你已经带上了你想好的目标、条件、计划、要求，来与领导沟通。通过这个举措，你把沟通的掌控权拿回到自己手上，变成了一个主动发起沟通的人。&lt;/p&gt;

&lt;p&gt;换时间，就是为自己争取机会，主动制造一个时间窗口。&lt;/p&gt;

&lt;h2 id=&quot;换场合&quot;&gt;换场合&lt;/h2&gt;

&lt;p&gt;换场合指，一句话换个场合，把事情降级或升级。&lt;/p&gt;

&lt;p&gt;如果你希望升级一件事的重要性，你就要把场合做扩大化的处理。&lt;/p&gt;

&lt;p&gt;比如，多叫两人一起，等于把这个场合扩大了，这个问题的重要程度也就升级了。&lt;/p&gt;

&lt;p&gt;注意：叫之前先说清楚，他们为什么来参与。&lt;/p&gt;

&lt;p&gt;又比如，会议上对话马上要演变成一场激烈冲突时，就说“等散会后来跟进”，从而把事情降级。&lt;/p&gt;

&lt;h2 id=&quot;换角色&quot;&gt;换角色&lt;/h2&gt;

&lt;p&gt;换角色是指，把球往回踢一下交由对方判断。即接受到问题时，先问一个问题，进而从回应者变成了提问者。&lt;/p&gt;

&lt;p&gt;如果是现场有很多人的话，可以用换角色大法，从这个尴尬问题的回应者变成为主持人。&lt;/p&gt;

&lt;p&gt;在把球传出去之前，我们至少对以下两个问题有足够清晰的认识：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;第一，团队关系到底怎么样，你心里是有答案的。&lt;/li&gt;
  &lt;li&gt;第二，团队成员能不能接住提问，在了解他们的情况下，你肯定也会有基本判断。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;团队氛围如何自己该知道，这样我才能做到心里有数，接下来该如何去行动。而且在提问时，肯定要给能把问题接住人，比如把疑难问题交给团队里情商比较高，平时就接得住话的伙伴。对他来说，这不是个锅，而是在大领导面前展示的机会。&lt;/p&gt;

&lt;h2 id=&quot;主持习惯&quot;&gt;主持习惯&lt;/h2&gt;

&lt;p&gt;在职场沟通中要为自己种下一个意识，养成这样的习惯，一旦进入多人沟通场合，如果没人做主持，我就是主持人，此时不管今天我们职位高低，我就是球场上的“中场发动机”，所有的球都送到我这里，同时我也要不断发出去。&lt;/p&gt;

&lt;p&gt;例如，领导问，“这次的例会西西你给打几分”，我说，“东东，咱们团队你呆的最久资格最老，你觉得这次的例会你应该打几分”，然后说，“我很同意你的看法，我也又这样的体会，北北，你的想法是什么？”。&lt;/p&gt;

&lt;p&gt;所以下次遇到有机会当会议主持人时，一定要告诉自己，你对自己要求高，对自己期许更高，所以才接下这个话茬，主动做主持人，掌控全场接球，传球，要球。在全场会议中，派发提问，请求协助，总结提炼。&lt;/p&gt;

&lt;h2 id=&quot;肯定反射&quot;&gt;肯定反射&lt;/h2&gt;

&lt;p&gt;我们在回应时要养成这样的习惯，把第一句话处理成一个对方的肯定，这个叫肯定反射。&lt;/p&gt;

&lt;p&gt;例如，我说，“您刚刚说的这些太有启发了”，“您提的这个问题，对我们的帮助很大”等等。&lt;/p&gt;

&lt;p&gt;当我们传递肯定，不是为了取悦对方讨好对方。事实上，越是积极的肯定对方，就越是在彰显我很自信，我很有把握，我对接下来的局面很有掌控感，这对于我们后面的沟通是非常有价值的。&lt;/p&gt;

&lt;p&gt;假设有一天我们要表达一些负面情况，那么第1句话传递的肯定就更重要了。因为从长远来看我们还是希望互动能持续下去，这种时候肯定，对方其实为双方留下一个态度意义上的余地。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485405&amp;amp;idx=1&amp;amp;sn=cc17252f23ec91046532269d7bd6b22d&amp;amp;chksm=fc2262dacb55ebccb0c8f300db3cbed376d35ba1cfe6bb0061982c7f807b376c3ea6d8de2c93&amp;amp;token=1541713099&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>给女儿写信(二十五) 面对变化</title>
   <link href="http://www.luzexi.com/2022/06/12/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A125"/>
   <updated>2022-06-12T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/06/12/给女儿的信25</id>
   <content type="html">
&lt;hr /&gt;

&lt;p&gt;导读：喜欢跟孩子们一起，可惜一直在外地工作，因此我给孩子写点信并录成视频，跟孩子聊聊自己的故事以及最近的感受&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;要点&quot;&gt;要点&lt;/h2&gt;

&lt;p&gt;我们应该积极的面对变化，勇敢的去寻找方法并积累经验，同时让自己更自信。&lt;/p&gt;

&lt;h2 id=&quot;大纲&quot;&gt;大纲&lt;/h2&gt;
&lt;p&gt;1.从深圳转到成都的故事
2.曾经面临挑战的故事
3.当下面对挑战的故事
4.期待未来的展望&lt;/p&gt;

&lt;h2 id=&quot;结构&quot;&gt;结构&lt;/h2&gt;

&lt;p&gt;1.在深圳2年半的时间，成长了很多，同时也认识到当下的项目组对我来说已经到了瓶颈点，
2.为了更好的未来，爸爸需要寻找一个适合自己的平台
3.到成都让我想起了，初到深圳时的窘迫
4.面临新的环境，并没有很好的对新环境进行认真的审视，于是出现了很多问题
5.这次来到成都，我特别对新环境进行了认真的观察
6.同时学习了一位女科学家的访谈内容，访谈中她聊了下自己在变化中的一些应对技巧
7.希望未来自己能够学到更多应对变化的技巧，面对新环境、挑战和未知时，能更加自信丛容。&lt;/p&gt;

&lt;h2 id=&quot;内容&quot;&gt;内容&lt;/h2&gt;

&lt;p&gt;Hi 秀恩，Hi 安妮，爸爸好想你们哟。&lt;/p&gt;

&lt;p&gt;爸爸已经到成都啦，这里的环境还不错哟。
今天爸爸跟你们讲一讲我来成都的事。
爸爸在深圳带了2年半的时间，这两年半时间里我学习到了超级多的东西，好开心。
只是最近爸爸发现我在那个项目组里遇到了巨大的瓶颈，爸爸想要突破这个瓶颈。
比如，爸爸的学习效率变慢了，爸爸的学习空间变小了等等，于是爸爸想着如何才能让自己达到未来自己想要达到的目标呢？
爸爸想到了改变，唯有改变自己才能让我对未来更又信心，于是我选择了在公司内部转岗来到了，成都的一个很好的团队。&lt;/p&gt;

&lt;p&gt;爸爸一飞到成都就抓紧时间找房子，
我给你们讲讲找房子的故事，哈哈哈。
我在还没有来之前在网上看了几处房子，一开始的时候看了一个公寓楼，好像还不错的样子，价格也适合。
不过爸爸觉得信息还不够全，于是爸爸调查了一下周边出租的房子，原来还有性价比更高的房子，最后找到了一个漂亮又实惠的，这个过程太棒了。就因为爸爸多深入调查了一下，找到了更好更实惠的房子。&lt;/p&gt;

&lt;p&gt;来到成都后，爸爸又遇到了新的挑战。
这次挑战跟初到深圳的挑战如出一辙。都是因为新环境引起的挑战。
幸好我这次观察的很仔细，因此非常认真的对待了这次新环境的挑战。
我观察到，大家对我的期待都很高。于是我在想我该如何面对当下的局面。&lt;/p&gt;

&lt;p&gt;最近，公司里有一个女科学家做了一个访谈节目，节目里她聊了下自己面对变化时的技巧。
我把她的访谈内容总结了一下。
我们在面对新环境、新变化、新挑战时，恐惧害怕是正常的生理反应。
就因为是正常的才需要我们去认真对待，有技巧有方法的去对待变化和挑战。
第一，我们应该更多的去了解新环境的信息和情况，尽量全面的调查信息、收集信息。
第二，有了这么多信息之后，我们应该对这些信息做个整理和总结，然后做出自己的规划
第三，聚焦最关键的目标，围绕目标去行动，有目标、有计划、有行动，就会有好效果。
除了这三个技巧外，我们应该更多的去积极面对挑战和变化，越多的尝试就会积累越多的经验，有了经验有了技巧，在面对未知和挑战时，我们就会变得更加自信和丛容。&lt;/p&gt;

&lt;p&gt;爸爸希望自己能够多练习面对未知和挑战时的技巧，这个世界是在不断变化的，新的挑战新的变化时时刻刻都在向我们袭来，爸爸要积极勇敢的面对他们，让他们成为爸爸成就未来的机遇而非阻碍。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(六十) 如何做软件分析1 - 建模</title>
   <link href="http://www.luzexi.com/2022/05/18/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B060"/>
   <updated>2022-05-18T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/05/18/读书笔记60</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485392&amp;amp;idx=1&amp;amp;sn=cbabddfd5843d957cdfbe4b147462e21&amp;amp;chksm=fc2262d7cb55ebc13ba52c468d750a3310db535c0598369ad9f6b3ad8ca33ecd36f11642be6a&amp;amp;token=284980735&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;背景&quot;&gt;背景：&lt;/h1&gt;

&lt;p&gt;从入行以来自己做了很多项目，也分析了很多项目的程序架构。这次我希望能够更系统性的学习如何做架构分析和软件系统分析。&lt;/p&gt;

&lt;p&gt;我本身对游戏项目中的业务架构、引擎架构、操作系统架构都有所了解，10多年来从业务架构一步步的深入探索到达了较为底层的架构。最近我突然有感触，自己在分析、解剖这些架构时发现，自己的速度慢效率低，所以我想学习一下，看看业内大佬是如何做架构设计、架构分析、系统解剖的。&lt;/p&gt;

&lt;p&gt;找了3本书看《Thinking in UML》，《重构》，《系统分析师》这三本书。专门针对这一个主题做一个系统性的学习。由于内容过多，我只能一本本总结，完毕后再写个整体的总结。本篇主要总结《Thinking in UML》这本书。&lt;/p&gt;

&lt;p&gt;《Thinking in UML》这本书内容丰富，近80万字很厚，尽管我认为他有点啰嗦，但从书本文字内涵上看，作者对软件分析、架构分析、架构设计、系统设计有比较深度的理解，值得去阅读。&lt;/p&gt;

&lt;h1 id=&quot;概述&quot;&gt;概述：&lt;/h1&gt;

&lt;p&gt;概述一下我的理解，首先我不想讲UML，因为UML只是工具，我们有很多种工具可以替代它，所以讲它没什么用，最好的结果是我们理解更深层次的分析和建模方法以及它的本质和原理，这对我们来说才是最好的学习内容。&lt;/p&gt;

&lt;p&gt;开头作者对建模方法做了些说明，从建模方式上看有两种，面向过程的建模方法和面向对象的建模方法，然后分别阐述了它俩的区别和优劣点，得出了一个结论，面向过程的建模方法更适合复杂度较低的系统，面向对象的建模方法更适合复杂度较高的系统。&lt;/p&gt;

&lt;p&gt;然后就开始说建模了，讲了从现实世界到业务模型应该如何建模，从业务模型到概念模型要如何转换，从概念模型到设计模型要转换，中间抛出很多建模的要素，对这些要素进行了一一说明。&lt;/p&gt;

&lt;p&gt;这其中有5个关键点，一是，抽象角度，二是，抽象层次，三是，用例，四是，参与者，五是，视图。&lt;/p&gt;

&lt;p&gt;抽象角度要全才能分析的彻底，抽象层次要适度才能分析的到位，用例的捕获和驱动是设计和执行的根本，参与者是所有建模的中心，视图是建模分析后的产物。&lt;/p&gt;

&lt;p&gt;下面就来详细说一下，以上概述的内容。&lt;/p&gt;

&lt;h1 id=&quot;目录&quot;&gt;目录：&lt;/h1&gt;

&lt;pre&gt;&lt;code&gt;1.软件分析为什么要建模？
2.建模过程是怎样的？
3.建模的核心元素
&lt;/code&gt;&lt;/pre&gt;

&lt;h1 id=&quot;内容&quot;&gt;内容：&lt;/h1&gt;

&lt;p&gt;人们做了很多努力积累了很多知识和经验，这些就是最佳实践。&lt;/p&gt;

&lt;p&gt;对于软件产品来说，最佳实践来自两个方面：一方面是技术类，如设计模式等。另一方面是过程类，如需求方法、分析方法、设计方法等。&lt;/p&gt;

&lt;h2 id=&quot;为什么要建模建模的方法有哪些&quot;&gt;为什么要建模？建模的方法有哪些？&lt;/h2&gt;

&lt;p&gt;（面向过程和面向对象分析的图）&lt;/p&gt;

&lt;p&gt;我们要分析这个世界，并用计算机来模拟它，首要的工作是将这个过程描绘出来，把它们的因果关系都定义出来；再通过结构化的设计方法，将这些过程进行细化，形成可以控制的、范围较小的部分。&lt;/p&gt;

&lt;p&gt;这里有两种方法，一种是面向过程的分析，一种是面向对象的分析。&lt;/p&gt;

&lt;p&gt;面向过程的分析方法是找到过程的起点，然后顺藤摸瓜，分析每一个部分，直至达到过程的终点。&lt;/p&gt;

&lt;p&gt;但是呢，面向过程有巨大的问题，随着需求越来越复杂，系统越来越庞大，功能点越来越多，一份数据经常被多个过程共享，这些过程对同一份数据的创建和读取要求越来越趋于复杂和多样，经常出现相矛盾的数据需求，因此分析和设计也变的越来越困难。&lt;/p&gt;

&lt;p&gt;面向过程遇到的困难，本质上是因为面向过程方法将世界看作是过程化的，一个个紧密相连的小系统，构成这个系统的各个部分之间有着密不可分的因果关系。&lt;/p&gt;

&lt;p&gt;这种分析方法在需求复杂度较低的时候非常管用，但当系统如此复杂如同蝴蝶效应那般轻轻扇动一下翅膀就能颠覆变得面目全非时，面向过程分析就不再能进行的下去了。&lt;/p&gt;

&lt;p&gt;实际上，并非面向过程的方法不正确，只是因为构成一个系统的因素太多，要把所有可能的因素都考虑到，把所有因素的因果关系都分析清楚，再把这个过程模拟出来实在是太困难了。&lt;/p&gt;

&lt;p&gt;我们的精力有限，计算能力有限，只能放弃对整个过程的了解，重新寻找一个方法，能够将复杂的系统转化成一个个我们可以控制的小单元。&lt;/p&gt;

&lt;p&gt;这个方法的转换正如：如果一次成型一辆汽车太过困难，我们可以将汽车分解为很多零件，分布制造，再依据预先设计好的接口把它们安装起来，形成最终的产品。&lt;/p&gt;

&lt;p&gt;而面向对象的方法则不同，汽车不再被看作一个一次成型的整体，而是被分解成了许多标准的功能部件来分布设计制造。&lt;/p&gt;

&lt;p&gt;面向对象方法将世界看作一个个互相独立的对象，相互之间并无因果关系，它们平时是“鸡犬之声相闻，老死不相往来”的，只有在某个外部力量驱动下，对象之间才会依据某种规律相互传递信息。&lt;/p&gt;

&lt;p&gt;对象从外部来看，对象与外界有消息通道，对象内部是一个黑匣子，什么也看不到，我们称为封装。当对象聚集在一起形成新的对象，结合后的对象具有前两者特性的总和，这称为聚合。对象繁殖产下孩子拥有父辈的全部本领，这称为继承。每个对象都有多个外貌，在不同情况下可以展现不同的外貌，但本质只有一个，这称为接口。多个对象可能长着相同的脸，但背后对象不同，且它们有着不同的行为，这称为多态。&lt;/p&gt;

&lt;p&gt;从宏观角度说，对象是“短视”的，它不知道也无法理解它所处的宏观环境，也不知道它的行为会对整个宏观环境造成怎样的影响。它只知道与它有着联系的身边的一小群伙伴，这称为依赖，并与小伙伴间保持着信息交流的关系，这称为耦合。&lt;/p&gt;

&lt;p&gt;一旦我们在对象之间确定了一系列的规则，把符合规则要求的对象组织起来形成特定的结构，它们就能拥有某些特定的能力；给这个结构一个推动力，它们就能做出规则要求的行为。&lt;/p&gt;

&lt;h2 id=&quot;建模过程是怎样的&quot;&gt;建模过程是怎样的？&lt;/h2&gt;

&lt;p&gt;（从用例建模到概念建模到设计建模图）&lt;/p&gt;

&lt;p&gt;建模过程首先要从现实世界出发，唯有从现实世界出发才是真正解决问题的出发点。接着从现实世界到业务模型，再从业务模型到概念模型，最后概念模型到设计模型。&lt;/p&gt;

&lt;p&gt;从现实世界到业务模型&lt;/p&gt;

&lt;p&gt;（从现实世界到业务模型图）&lt;/p&gt;

&lt;p&gt;建立模型的过程是一个抽象的过程，首先要知道我们应该如何抽象现实世界。&lt;/p&gt;

&lt;p&gt;当我们站在很高的抽象层次，以高度归纳的视角来看这个世界的运作时就会发现，&lt;/p&gt;

&lt;p&gt;现实世界无论多复杂，无论是哪个行业，无论做什么业务，其本质无非是由人、事、物、规则所组成。&lt;/p&gt;

&lt;p&gt;人是一切的中心，人要做事，做事就会使用一些物，同时做事需要遵循一定的规则。&lt;/p&gt;

&lt;p&gt;有了事、物、规则就会逐渐形成系统，事、物、规则、系统它们是相辅相成的，&lt;/p&gt;

&lt;p&gt;于是就有了，人驱动系统，事体现过程，物记录结果，规则代表控制。&lt;/p&gt;

&lt;p&gt;因此建立模型的关键就是弄明白有什么人，什么人做什么事，什么事产生什么物，中间有什么规则，再把人、事、物之间的关系定义出来，一个模型也就基本成型了。&lt;/p&gt;

&lt;p&gt;这里头最关键的就是参与者，也就是说，我们要建立的这个模型的意义，完全是被参与者所决定，所建立的这个模型也是完全为参与者所服务的，参与者是整个建模过程的中心。&lt;/p&gt;

&lt;p&gt;而用例是一种用元模型来表示驱动者的业务目标的这么一个东西，也就是说，用例代表了参与者想要做什么并且最终获得什么的模型。&lt;/p&gt;

&lt;p&gt;我们所说的这个业务目标就是现实世界中的“事”。而这件事是怎么做的，依据什么规则，则通过称之为业务场景和用例场景的视图来描绘，这些场景就是现实世界中的“规则”。&lt;/p&gt;

&lt;p&gt;UML通过元模型和视图，捕获现实世界的人、事、物和规则，于是现实信息转化成了业务模型，这也是面向对象方法中的第一步。&lt;/p&gt;

&lt;p&gt;从业务模型到概念模型&lt;/p&gt;

&lt;p&gt;（业务模型到概念模型图）&lt;/p&gt;

&lt;p&gt;得到业务模型仅仅是一个开始，要想将业务模型转化到计算机能理解的模型，还有重要的一步，概念模型。&lt;/p&gt;

&lt;p&gt;我们可以通过概念化的过程来建立适合计算机理解和实现的模型，这个模型称为分析模型。&lt;/p&gt;

&lt;p&gt;分析模型向下为计算机实现规定了一种高层次的抽象，这种抽象是一种指导也是一种约束，计算机实现过程非常容易遵循这种指导和约束来完成可执行代码的设计工作。&lt;/p&gt;

&lt;p&gt;从业务模型到概念模型的过程有个特点，就是：&lt;/p&gt;

&lt;p&gt;所有的操作都通过边界来决定，即能做什么不能做什么由边界决定。&lt;/p&gt;

&lt;p&gt;所以边界类实际上代表了原始需求中的“事”，实体类代表了现实世界中的“物”，控制类体现了现实世界中的“规则”，再加上由参与者转化而来的系统的“用户”，这样一来都有了。&lt;/p&gt;

&lt;p&gt;这个阶段，我们可以对这些分析类在不同的视角上进行归纳和整理，以表达软件所要求的一些信息。&lt;/p&gt;

&lt;p&gt;可以说，从业务模型到概念模型这一过程，正是我们需要的一种从对象世界来描述现实世界的方法。&lt;/p&gt;

&lt;p&gt;从概念模型到设计模型&lt;/p&gt;

&lt;p&gt;（概念模型到设计模型图）&lt;/p&gt;

&lt;p&gt;概念模型只是纸上谈兵，真正的对象世界行为是由java类、c++类、EJB、COM等这些可执行代码构成的，如果缺少了从概念模型到设计模型这个过程，java类和c++类的行为就无法得到验证。&lt;/p&gt;

&lt;p&gt;也就是说，设计模型是概念模型在特定环境和条件下的“实例”化，实例化后的对象行为“执行”了概念模型描述的那些信息，因此设计模型得以通过概念模型追溯到原始需求来验证，对象世界是否正确反映了现实世界。&lt;/p&gt;

&lt;p&gt;设计模型的工作就是建造零部件，组装汽车的过程。&lt;/p&gt;

&lt;p&gt;这里从概念到设计模型的转化有个技巧，即概念模型的边界类可以被转化为操作界面或系统接口；控制类可以被转化为计算机程序或控制程序；，例如工作流、算法等；实体类可以转化为数据库表、XML文档或者其他带有持久化特征的类。&lt;/p&gt;

&lt;h2 id=&quot;建模的核心元素&quot;&gt;建模的核心元素&lt;/h2&gt;

&lt;p&gt;不论在需求分析、系统分析还是系统设计上，我们一定要学会采用面向对象的方法。&lt;/p&gt;

&lt;p&gt;在面对问题领域的时候首先不要决定去通盘考虑，而是找出问题领域里包含的抽象角度。&lt;/p&gt;

&lt;p&gt;如果你把抽象角度都找全了，并且这些角度都分析清楚了，问题领域也就解决了。&lt;/p&gt;

&lt;p&gt;例如做需求时不要先去弄清楚业务是如何一步步完成的，而是要弄清楚，有多少业务参与者？每个参与者的目标是什么？参与者的目标就是做需求的抽象角度。&lt;/p&gt;

&lt;p&gt;那建模的“模”到底是什么呢？作者给出了答案，模就是“人、事、物、规则”，它是由抽象角度确定的，需要由静态事物加上特定条件产生的场景。&lt;/p&gt;

&lt;p&gt;也就是说，我们在建模时，是根据问题领域来建模的，针对问题领域抽象成N个角度，每个抽象角度都可以用一个业务用例来解释，每个业务用例都可以用N个特定场景来描述，每个特定场景都是由事物、规则、行为构成的。&lt;/p&gt;

&lt;p&gt;如下图：&lt;/p&gt;

&lt;p&gt;（问题领域图）&lt;/p&gt;

&lt;p&gt;问题领域 = N个抽象角度&lt;/p&gt;

&lt;p&gt;（抽象角度图）&lt;/p&gt;

&lt;p&gt;抽象角度 = 参与者的业务目标 = 业务用例&lt;/p&gt;

&lt;p&gt;（业务用例图）&lt;/p&gt;

&lt;p&gt;业务用例 = N个特定场景&lt;/p&gt;

&lt;p&gt;（用例场景图）&lt;/p&gt;

&lt;p&gt;用例场景 = 事物 + 规则 + 行为&lt;/p&gt;

&lt;p&gt;汇总起来：&lt;/p&gt;

&lt;p&gt;问题领域 = N个抽象角度&lt;/p&gt;

&lt;p&gt;抽象角度 = 参与者的业务目标 = 业务用例&lt;/p&gt;

&lt;p&gt;业务用例 = N个用例场景&lt;/p&gt;

&lt;p&gt;用例场景 = 事物 + 规则 + 行为&lt;/p&gt;

&lt;p&gt;这个建模公式可以帮助我们理清思路。这在业务建模、分析建模、设计建模时都能遵循同样的公式。&lt;/p&gt;

&lt;p&gt;这里我们来介绍下这里的一些元素，包括用例、抽象层次、抽象角度、视图、参与者。&lt;/p&gt;

&lt;h3 id=&quot;用例&quot;&gt;用例&lt;/h3&gt;

&lt;p&gt;前面我们说过，用例代表了参与者想要做什么并且最终获得什么的模型，也就是说，用例是一种把现实世界的需求捕获下来的方法。&lt;/p&gt;

&lt;p&gt;例如我们要描述一个系统的功能性需求，就要找到对这个系统有愿望的人，让他们来说明他们会在这个系统里做什么事，想要什么结果。&lt;/p&gt;

&lt;p&gt;捕获功能性需求，就是用例的作用，而且通过用例，我们可以驱动软件架构的建立。&lt;/p&gt;

&lt;p&gt;一个软件要实现的功能可以通过用例来捕获，接下来的所有分析、设计、实现、测试都可以由用例来驱动，也就是以实现用例为目标。一个用例就是一个分析单元、设计单元、开发单元、测试单元和部署单元。&lt;/p&gt;

&lt;p&gt;（解释用例和用例场景图）&lt;/p&gt;

&lt;p&gt;除了用例，还有用例场景，就是做一件事可以有很多不同的办法和步骤，也会遇到各种意外，因此这件事由很多不同情况的集合构成，这就是用例场景。&lt;/p&gt;

&lt;p&gt;用例的颗粒度在建模的不同阶段有所不同。&lt;/p&gt;

&lt;p&gt;在概念建模阶段，用例的粒度以每个用例能描述一个完整的事件流为宜。可理解为一个用例描述一项完整业务中的一个步骤。&lt;/p&gt;

&lt;p&gt;在系统建模阶段，用例视角是针对计算机的，因此用例的粒度以一个用例能够描述操作者与计算机的一次完整交互为宜。&lt;/p&gt;

&lt;p&gt;而且不论粒度如何选择，必须把握的原则是，在同一个需求阶段，所有用例的粒度应该是同一个量级的。&lt;/p&gt;

&lt;p&gt;当我们使用用例来捕获需求时，可以从以下三个观点出发：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;这个事物是什么？
这个事物能做什么？
人们能够用这个事物做什么？
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;对于正在准备开发的软件，最好的方法就是从使用者的观点去描述它。&lt;/p&gt;

&lt;p&gt;包括使用者可以怎么使用它，得到什么样的利益。&lt;/p&gt;

&lt;p&gt;至于功能性观点和结构性观点，则可以通过使用者观点推导出来。&lt;/p&gt;

&lt;h3 id=&quot;抽象层次&quot;&gt;抽象层次&lt;/h3&gt;

&lt;p&gt;抽象层次在面向对象方法中极其重要，学会站在不同抽象层次考虑问题是建立好模型的基础。&lt;/p&gt;

&lt;p&gt;首先我们要明白，抽象层次越高具体信息越少，但概括能力就越强；反之，具体信息越丰富，结果越确定，但相应的概括能力越弱。&lt;/p&gt;

&lt;p&gt;这个很好理解，就如公司里有员工、组长、部门长、总监、经理、总裁、总经理，越往上你越对公司整体的运作状况越清晰，但你对具体的执行细节知道的越少，相反你对整体的运作状况越模糊，细节则知道的越清楚。&lt;/p&gt;

&lt;p&gt;（抽象层次图）&lt;/p&gt;

&lt;p&gt;也就得到一条结论：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;抽象的层次越高，被屏蔽的信息也就越多，信息量越少，也就越容易理解和处理。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;也就说，反应到抽象对象上来说，在高层次抽象之上的对象，它的描述很粗略但适应能力大，在低层次抽象之上的对象，它的描述很精确但适应能力小。
而当抽象的层次太高，信息量就会过少，人们实施起来就会产生新的困难。&lt;/p&gt;

&lt;p&gt;要关注一点，对象参与的场景越多，对象越有抽象的价值，反之则越没有抽象价值。因此在分析过程中，应该关注于那些参与了很多场景的对象，它们往往是分析设计中的重点以及成败的关键。&lt;/p&gt;

&lt;p&gt;（抽象方法图）&lt;/p&gt;

&lt;p&gt;抽象有两种方法，一种是自顶向下，一种是自底向上。&lt;/p&gt;

&lt;p&gt;自顶向下的方法适用于人们从头开始认识一个事物。&lt;/p&gt;

&lt;p&gt;例如刚开始学习引擎时，如果直接跟你讲引擎程序执行细节你就听不懂，但我换种方法，用比较高的层次的抽象概念来讲就会比较容易明白。&lt;/p&gt;

&lt;p&gt;自底向上的方法则适用于在实践中改进和提高认识。&lt;/p&gt;

&lt;p&gt;例如当我们迈过引擎的初级门槛后想要学习更多引擎知识时，就需要去拆解引擎渲染管线，拆解引擎的内存管理方式，拆解引擎资产管理等等，一个个拆解从底层开始学起了解整个引擎的运作方式。&lt;/p&gt;

&lt;h3 id=&quot;视图&quot;&gt;视图&lt;/h3&gt;

&lt;p&gt;视图的准确应用是建立好模型的一个重要组成部分。&lt;/p&gt;

&lt;p&gt;视角是人们观察事物的角度，不同的人或者同一个人出于不同的目的会对同一个信息从不同的角度来审视和评估。&lt;/p&gt;

&lt;p&gt;视角是针对每个视图来说的，不同的视角展示了同样信息的不同认知角度，以便理解。&lt;/p&gt;

&lt;p&gt;（视图的价值图）&lt;/p&gt;

&lt;p&gt;从信息的展示角度来说，恰当的视角可以让观察者更容易抓住信息的本质；&lt;/p&gt;

&lt;p&gt;另一方面，从观察者角度说，观察者只会关心信息中他感兴趣的那一部分，其他视角的信息对他是没有多少用处的。&lt;/p&gt;

&lt;p&gt;为特定的信息选择正确的视图，为特定的干系人展示正确的视角并不容易，需要因时因地因人制宜。&lt;/p&gt;

&lt;p&gt;这里提供两个问题，可以在制图时，帮助大家更好知道该做什么图以及该如何作图。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;问题1，应该为哪些软件信息绘制哪些视图。
问题2，应该给哪些干系人展示哪些视角。
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;参与者&quot;&gt;参与者&lt;/h3&gt;

&lt;p&gt;参与者在建模过程中处于核心地位，建模是从寻找参与者开始的，有了人就有了事，有了事就有了物，有了事和物就有了规则，这样整个问题就清晰了。&lt;/p&gt;

&lt;p&gt;（参与者中心图）&lt;/p&gt;

&lt;p&gt;将所有的参与者找出来，是建模的重要关键点，因为人是一切的中心。&lt;/p&gt;

&lt;p&gt;在寻找参与者过程中，我们可以问自己以下问题来帮助确定参与者。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;谁负责提供、使用或删除信息？
谁将使用此功能？
谁对某个特定功能感兴趣？
在组织中的什么地方使用系统？
谁负责支持和维护系统？
系统有哪些外部资源？
其他还有哪些系统将需要与该系统进行交互？
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;有助于检查发现参与者是否正确的问题：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;是否已找到所有的参与者？
每个参与者是否至少涉及到一个用例？
能否列出至少两名可以作为特定参与者的人员？
是否有参与者担任与系统相关的相似角色？
是否有两个参与者担任与用例相关的同一角色？
特定的参与者是否将以几种完全不同的方式使用系统？
参与者是否有直观名称和描述性名称？
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;参考资料&quot;&gt;参考资料：&lt;/h3&gt;

&lt;p&gt;《Thinking in UML》作者 谭云杰&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485392&amp;amp;idx=1&amp;amp;sn=cbabddfd5843d957cdfbe4b147462e21&amp;amp;chksm=fc2262d7cb55ebc13ba52c468d750a3310db535c0598369ad9f6b3ad8ca33ecd36f11642be6a&amp;amp;token=284980735&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>游戏引擎架构#4 低阶渲染器（6）</title>
   <link href="http://www.luzexi.com/2022/05/03/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B059"/>
   <updated>2022-05-03T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/05/03/读书笔记59</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485343&amp;amp;idx=1&amp;amp;sn=78ee7bce86f53c7ed5cb59d08a6a29f2&amp;amp;chksm=fc226298cb55eb8e89b22caa781fa0ed21dbb196720d633f62855bfd4d3a4959b78c343ac54b&amp;amp;token=39636778&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;背景&quot;&gt;背景：&lt;/h1&gt;

&lt;p&gt;作为游戏开发从业者，从业务到语言到框架到引擎，积累了一些知识和经验，特别是在看了几遍《游戏引擎架构》、《游戏引擎原理与实践》后对引擎架构的理解又深入了些。近段时间有对引擎剖析的想法，正好借这两本书对游戏引擎的架构做一个完整的分析。&lt;/p&gt;

&lt;p&gt;《游戏引擎架构》用简明、清楚的方式阐述引擎架构，知识覆盖了游戏引擎架构的庞大领域，巧妙地平衡了广度与深度，并且提供了足够的细节。《游戏引擎原理与实践》有两册内容比较详尽，代码示例展示比较多，内容比较丰富。&lt;/p&gt;

&lt;p&gt;我结合这两本书以及自己的经验，写一些自己的知识总结以帮助自己学习引擎知识，文章中我会深入分析游戏引擎的历史、架构、模块，并通过引擎开发实践来完成对引擎知识的掌握。&lt;/p&gt;

&lt;p&gt;游戏引擎知识面深而广，所以对这系列的文章书编写范围做个保护，不对细节进行过多的阐述，重点剖析的是架构、流程以及模块的运作原理。&lt;/p&gt;

&lt;h1 id=&quot;概述&quot;&gt;概述：&lt;/h1&gt;

&lt;p&gt;本系列文章对引擎中的重要的模块和库进行详细的分析，我挑选了十五个库和模块来分析：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;时间库&lt;/li&gt;
  &lt;li&gt;自定义容器库&lt;/li&gt;
  &lt;li&gt;字符串散列库&lt;/li&gt;
  &lt;li&gt;内存管理框架&lt;/li&gt;
  &lt;li&gt;RTTI与反射模块&lt;/li&gt;
  &lt;li&gt;图形计算库&lt;/li&gt;
  &lt;li&gt;资产管理模块&lt;/li&gt;
  &lt;li&gt;低阶渲染器&lt;/li&gt;
  &lt;li&gt;剔除与合批模块&lt;/li&gt;
  &lt;li&gt;动画模块&lt;/li&gt;
  &lt;li&gt;物理模块&lt;/li&gt;
  &lt;li&gt;UI底层框架&lt;/li&gt;
  &lt;li&gt;性能剖析器的核心部分&lt;/li&gt;
  &lt;li&gt;脚本系统&lt;/li&gt;
  &lt;li&gt;视觉效果模块&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;本篇内容为列表中的第8个部分的第6节。&lt;/p&gt;

&lt;h1 id=&quot;正文&quot;&gt;正文：&lt;/h1&gt;

&lt;p&gt;简单回顾下前文&lt;/p&gt;

&lt;p&gt;前几篇文章我们聊了GPU在计算机硬件主板中的位置与结构，知道了CPU、GPU的通信介质，手机上的主板结构。然后聊了下显卡的历史，图形驱动的历史，知道了GPU和图形接口在历史长河汇总的来龙去脉。接着聊了CPU的硬件架构，GPU硬件架构，以及GPU软件架构中的各个细节，其中还对比了GPU与CPU的相似与差异，大体明白了GPU是如何工作的。&lt;/p&gt;

&lt;p&gt;下面我们开始这篇内容&lt;/p&gt;

&lt;p&gt;本次内容会围绕GPU来写，从硬件架构到软件驱动再到引擎架构，目标是帮大家理解GPU硬件的运作原理，理解图形接口的架构，理解引擎低阶渲染器的架构。&lt;/p&gt;

&lt;p&gt;目录：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;主板结构中的显卡&lt;/li&gt;
  &lt;li&gt;GPU功能发展史&lt;/li&gt;
  &lt;li&gt;GPU与CPU的差异&lt;/li&gt;
  &lt;li&gt;GPU硬件特点&lt;/li&gt;
  &lt;li&gt;图形驱动程序架构&lt;/li&gt;
  &lt;li&gt;引擎渲染架构&lt;/li&gt;
  &lt;li&gt;封装关系&lt;/li&gt;
  &lt;li&gt;图形API分类&lt;/li&gt;
  &lt;li&gt;引擎渲染流程拆解&lt;/li&gt;
  &lt;li&gt;渲染器的设计结构&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;上篇聊了下图形驱动程序架构，它封装了各类显卡的驱动程序，根据描述的GPU指令和数据的流程图，我们能更好的理解驱动程序与GPU之间的责任划分与配合。接着为了能更好的理解低阶渲染器，先以统计和描述OpenGL接口的方式来了解和熟悉图形接口，本篇就继上一篇内容。&lt;/p&gt;

&lt;h2 id=&quot;二引擎渲染架构&quot;&gt;二、引擎渲染架构&lt;/h2&gt;

&lt;p&gt;本节重点介绍引擎中低阶渲染器的架构，包括渲染的封装关系、图形API的分类、引擎渲染流程的拆解、以及渲染器的设计结构，层层理解渲染架构是如何设计的。&lt;/p&gt;

&lt;h2 id=&quot;一封装关系&quot;&gt;一、封装关系&lt;/h2&gt;

&lt;p&gt;游戏的渲染从业务到引擎到图形API到操作系统到驱动程序到硬件经过层层的封装，每一层封装都是有意义的，每一层的封装的目的都有相通之处，都是更好的为上层提供服务。&lt;/p&gt;

&lt;p&gt;游戏引擎需要封装图形API，让引擎开发者不用关心底层的图形API，图形API则要封装硬件驱动API，让引擎渲染器开发者不需要关心底层的硬件驱动API。如下图：&lt;/p&gt;

&lt;p&gt;（渲染API层层封装图）&lt;/p&gt;

&lt;p&gt;从上图中，我们看到每种硬件都有自己的驱动程序，因此操作系统中有很多硬件的驱动程序，图形API就封装了所有的硬件驱动接口，为引擎调用提供了方便。而图形驱动程序又有很多种，这使得图形API在调用时也会遇到不同平台带来的麻烦，因此引擎封装了所有平台的图形API，为引擎其他模块使用图形API时提供了方便。就这样层层封装，图形驱动程序封装了硬件驱动，引擎封装了图形驱动，最终更方便了开发者使用游戏引擎开发游戏。&lt;/p&gt;

&lt;h2 id=&quot;二图形api分类&quot;&gt;二、图形API分类&lt;/h2&gt;

&lt;p&gt;前文中我们描述了很多OpenGL的接口，我们可以通过对OpenGL接口的统计和描述，可以将所有的图形API可以划分成四部分：&lt;/p&gt;

&lt;p&gt;（图形API模块划分图）&lt;/p&gt;

&lt;p&gt;上图展示了四部分的内容，四部分包括：资源、状态、缓冲、接口&lt;/p&gt;

&lt;p&gt;资源：
1.Shader着色器，相关图形接口
2.纹理贴图，相关图形接口
3.采样器，相关图形接口&lt;/p&gt;

&lt;p&gt;状态：
1.视口，相关图形接口
2.光栅化，相关图形接口
3.Blend混合，相关图形接口&lt;/p&gt;

&lt;p&gt;缓冲：
1.缓冲区，包括颜色缓冲、深度缓冲、模板缓冲，相关图形接口
2.顶点，相关图形接口
3.帧缓冲区对象（FBO），相关图形接口&lt;/p&gt;

&lt;p&gt;接口：
1.图元绘制，相关图形接口
2.开关、查询、判断等通用接口，相关图形接口
3.其他非通用特殊接口&lt;/p&gt;

&lt;p&gt;对其精简后可理解为如下：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;资源：Shader着色器、纹理贴图、采样器&lt;/li&gt;
  &lt;li&gt;状态：视口、光栅化、Blend混合&lt;/li&gt;
  &lt;li&gt;缓冲：顶点、缓冲区、帧缓冲对象（FBO）&lt;/li&gt;
  &lt;li&gt;接口：图元绘制、通用接口（开关、查询、判断等）、其他特殊接口&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;现在，我们大致了解了图形API接口以及这些接口的分类，这样我们就能知道它到底可以做哪些事情，又是怎么去封装和定义驱动程序接口的。接着下面我们就来拆解下渲染流程以及渲染器。&lt;/p&gt;

&lt;h2 id=&quot;三引擎渲染流程拆解&quot;&gt;三、引擎渲染流程拆解&lt;/h2&gt;

&lt;p&gt;渲染流程是整个引擎的核心，使用UE4可以轻松实现好的渲染效果，但它的架构较封闭难以实现自定义光照模型，而Unity渲染架构比较开放，但默认渲染效果一般，用户需要按照Unity的规则自己去实现一套好的效果。对于渲染流程和材质上的设计两个引擎都有优缺点，如果我们想得到希望的结果，还要看我们对游戏引擎的掌握程度。&lt;/p&gt;

&lt;p&gt;首先，我们来拆解下渲染的简易流程，对一个物体从加入渲染到最终渲染的过程进行拆解。&lt;/p&gt;

&lt;p&gt;(引擎中简易的渲染流程拆解示意图)&lt;/p&gt;

&lt;p&gt;如上图所示，一个物体的传统渲染流程如下：
1.渲染前置裁剪
2.前期数据整理
3.裁剪相机外物体
4.渲染准备工作
5.提交渲染指令&lt;/p&gt;

&lt;p&gt;人们常说GPU中的渲染管线，实际上在GPU之前，引擎中的渲染管线非常重要，它就是引擎的渲染程序的执行过程。在引擎代码执行过程中，一个物体从加入渲染到最终提交渲染，这中间要经过以上5个阶段，包括前置裁剪，前期数据整理，裁剪相机外物体，渲染准备工作，提交渲染指令。&lt;/p&gt;

&lt;h3 id=&quot;渲染前置裁剪有很多方法包括遮挡剔除裁剪九宫格剔除裁剪远距离物体剔除裁剪等这些裁剪算法的应用可以根据业务场景不同而有所选择&quot;&gt;渲染前置裁剪有很多方法，包括遮挡剔除裁剪，九宫格剔除裁剪，远距离物体剔除裁剪等。这些裁剪算法的应用可以根据业务场景不同而有所选择。&lt;/h3&gt;
&lt;p&gt;前置数据整理，包括视口参数，节点整理，物体排序等。&lt;/p&gt;

&lt;h3 id=&quot;裁剪相机外物体引擎一般用aabb包剔除因为这是最快的剔除方法而且还可以多线程计算剔除每个要渲染的物体都会计算一个长方体aabb范围与相机的正交或透视范围进行相交计算&quot;&gt;裁剪相机外物体，引擎一般用AABB包剔除，因为这是最快的剔除方法而且还可以多线程计算剔除，每个要渲染的物体都会计算一个长方体AABB范围，与相机的正交或透视范围进行相交计算。&lt;/h3&gt;

&lt;h3 id=&quot;渲染准备工作为渲染做准备包括数据检查shader解析深度图准备色彩空间设置相机设置等在真正渲染场景内物体前做好准备工作&quot;&gt;渲染准备工作，为渲染做准备，包括数据检查，Shader解析，深度图准备，色彩空间设置，相机设置等，在真正渲染场景内物体前做好准备工作。&lt;/h3&gt;

&lt;h3 id=&quot;提交渲染指令把所有渲染的指令提交给gpu这里有两种情况一种是立即调用图形api设置渲染状态并最后告诉gpu指令提交完毕另一种是开线程提交&quot;&gt;提交渲染指令，把所有渲染的指令提交给GPU，这里有两种情况，一种是立即调用图形API设置渲染状态并最后告诉GPU指令提交完毕，另一种是开线程提交。&lt;/h3&gt;

&lt;p&gt;（引擎逻辑渲染流程图）&lt;/p&gt;

&lt;p&gt;上图为商业引擎中的逻辑渲染管线（实际上还有很多节点为了编辑和展示场景服务的，我暂时忽略它们），这里我用红色标出了3个关键节点，包括剔除相机外物体，渲染ShowMaps，调用渲染指令，它们在这个逻辑渲染管线中是比较重要的。&lt;/p&gt;

&lt;h3 id=&quot;引擎会从循环总入口开始执行逻辑管线先检查数据与参数防止后面的逻辑执行出现异常接着针对每个视口做渲染多视口通常用于编辑器中多个窗口的渲染然后由于收集了所有camera对每个camera都执行一次渲染逻辑紧接着是ui渲染由于ui的特殊结构设计它会与普通物体分开渲染这里主要涉及到ui的渲染合批以及合批算法会在后面的章节中详细展开分析最后渲染结束清理数据&quot;&gt;引擎会从循环总入口开始执行逻辑管线，先检查数据与参数防止后面的逻辑执行出现异常，接着针对每个视口做渲染（多视口通常用于编辑器中多个窗口的渲染），然后由于收集了所有Camera，对每个Camera都执行一次渲染逻辑，紧接着是UI渲染，由于UI的特殊结构设计，它会与普通物体分开渲染（这里主要涉及到UI的渲染合批以及合批算法，会在后面的章节中详细展开分析），最后渲染结束清理数据。&lt;/h3&gt;

&lt;p&gt;（逻辑渲染管线分两种的图）&lt;/p&gt;

&lt;p&gt;这里我着重提一下渲染Camera这个节点，它有两条线路可走。&lt;/p&gt;

&lt;h3 id=&quot;条是我们自定义的渲染管线这在unity商业引擎中就有这样的可编程逻辑渲染管线通过可编程逻辑渲染管线我们可以制定包括渲染物体的顺序针对某些特殊物体的渲染各种渲染效果的自定义处理这么做的结果可以让我们去除掉多余的渲染操作提高性能增加定制的渲染功能让逻辑渲染管线更符合我们当下的项目同时unity引擎在此基础上打造了更加方便更加易于使用的逻辑渲染管线即urp和hdrp当然可编程渲染管线不是万能因为引擎没有完全放手这块逻辑它保留了通用的和固定逻辑的功能模块从而减轻大家的编程负担如果要重写整个管线工作量就太大了&quot;&gt;条是我们自定义的渲染管线，这在Unity商业引擎中就有这样的可编程逻辑渲染管线，通过可编程逻辑渲染管线，我们可以制定包括渲染物体的顺序、针对某些特殊物体的渲染、各种渲染效果的自定义处理，这么做的结果可以让我们去除掉多余的渲染操作提高性能，增加定制的渲染功能，让逻辑渲染管线更符合我们当下的项目。同时Unity引擎在此基础上打造了更加方便更加易于使用的逻辑渲染管线，即URP和HDRP。当然可编程渲染管线不是万能，因为引擎没有完全放手这块逻辑，它保留了通用的和固定逻辑的功能模块，从而减轻大家的编程负担（如果要重写整个管线工作量就太大了）。&lt;/h3&gt;

&lt;p&gt;另一条是引擎固定的逻辑渲染管线，这条管线上包含了渲染需要的所有操作，包括数据准备、节点整理、相机外物体裁剪、渲染参数设置、深度图渲染、阴影图渲染、渲染状态提交等，通常是引擎根据所有渲染功能汇总编写的一套通用逻辑渲染管线。&lt;/p&gt;

&lt;p&gt;(渲染管线中的三个要点图)&lt;/p&gt;

&lt;p&gt;这里的逻辑渲染管线都有三个要点，第一个是剔除相机外物体，引擎会根据物体大小计算一个AABB包围盒，通过包围盒与相机正交或透视的计算得出它是否需要渲染。第二个是ShdowMaps绘制，在渲染物体前我们需要知道物体之间的遮挡关系才能计算出阴影，所以先让GPU跑一遍渲染管线计算每个像素点上物体的遮挡关系（这里简单介绍一下，后面在渲染效果章节再详细说明）。&lt;/p&gt;

&lt;p&gt;第三个是设置和提交渲染状态，引擎把物体归类为不透明物体、半透明物体、以及天空盒和其他，把相同的几何物体放在一起渲染减少了渲染指令重复提交的开销，提高了运行效率。在提交渲染指令时有两个方法，两个方法最终都是调用相应平台的图形API，一是直接调用图形API，每当需要设置渲染状态、提交渲染指令时，直接调用相应平台的图形API，将操作立即告诉GPU（GPU中也有自己的队列）。二是将要改变的渲染状态和指令推入进线程队列中，再在另一个线程里取出队列中的渲染状态和指令，接着调用图形API通知GPU，这样做就是为了不阻塞主线程。&lt;/p&gt;

&lt;p&gt;通常渲染引擎会把渲染线程和引擎主线程拆分开来，渲染线程单独处理渲染状态改变的指令，主线程和渲染线程之间则用队列通信，这个逻辑功能在Unity和Unreal中都有使用。&lt;/p&gt;

&lt;h2 id=&quot;四渲染器的设计结构&quot;&gt;四、渲染器的设计结构&lt;/h2&gt;

&lt;p&gt;前面我们介绍了逻辑渲染管线，主要说明的是逻辑渲染管线中做了什么事情，有哪些关键点，逻辑渲染管线最终都会嗲用图形API去提交渲染指令，这些都是引擎中渲染器要做的事情，下面我们来介绍下渲染器的封装与结构设计。&lt;/p&gt;

&lt;p&gt;不同硬件通过不同驱动程序进行管理，图形API封装了很多驱动程序让开发者不必关心硬件驱动程序。引擎也是同样的作用，由于平台有很多种，图形API也有很多种，因此引擎需要封装图形API为渲染器，让开发者不用关心底层的图形API，这样引擎制作的游戏都可以发布到多个平台。&lt;/p&gt;

&lt;p&gt;（图形API封装图）&lt;/p&gt;

&lt;p&gt;为了拥有一个易用的多平台的可扩展的渲染器，引擎必须将图形API封装，也就是用对象的方式将部分渲染数据存储起来，在执行时立即使用并能减少不少渲染指令。&lt;/p&gt;

&lt;p&gt;由于每个平台的API不同，因此封装时首先将不同平台的API封装成统一的接口，这要求我们在封装时不仅仅只是检查参数这么简单，还需要我们针对性的用面向对象的方式去设计图形API封装结构。其次每个平台都有自己特殊的功能和接口，需要额外做一些封装提供给引擎使用。&lt;/p&gt;

&lt;p&gt;其中所有封装都可以归纳分类为4种类型，资源、缓冲、状态、接口。&lt;/p&gt;

&lt;h3 id=&quot;渲染资源属于api层的资源类型原始的api使用起来比较繁琐为了简化它的使用游戏引擎都会再封装一次资源类型的接口包括创建资源设置资源加载资源销毁资源&quot;&gt;渲染资源属于API层的资源类型，原始的API使用起来比较繁琐，为了简化它的使用，游戏引擎都会再封装一次。资源类型的接口包括创建资源、设置资源、加载资源、销毁资源。&lt;/h3&gt;

&lt;h3 id=&quot;缓冲对象的目的是更方便开发者把内存数据复制到显存中状态对象的目的是为了记录当前状态并保留一份在主存中以减少同一个绘制管线中的重复设置接口的目的就是为了更方便开发者调用图形api&quot;&gt;缓冲对象的目的是更方便开发者把内存数据复制到显存中，状态对象的目的是为了记录当前状态并保留一份在主存中，以减少同一个绘制管线中的重复设置，接口的目的就是为了更方便开发者调用图形API。&lt;/h3&gt;

&lt;p&gt;（渲染指令的对象设计图）&lt;/p&gt;

&lt;p&gt;渲染器是对图形API的进一步封装，用于提高资源的使用效率。通常我们用图形API实现一个功能要经过多个步骤。经过整合的渲染器接口，可以通过一两个步骤就可以实现原本需要多个步骤的功能。渲染器功能一般包括渲染窗口相关信息、创建资源、设置资源、销毁资源、获得显卡信息、完成渲染流程等。因此渲染器需要抽象出各种接口，以供子类去实现，这样就可以兼容各种渲染API。&lt;/p&gt;

&lt;p&gt;上图中详细举例了渲染状态、渲染缓冲、渲染资源、逻辑状态信息。将他们在引擎中封装成对象，就是为了更好的加快渲染器中处理资源的速度，减少设置资源时耗费的CPU时间。同时为了达到这个目标，我们就需要管理这些资源对象。&lt;/p&gt;

&lt;p&gt;额外话题，前面说到不同图形API不同平台设备有很多差异，这里就举个例子，简单介绍下DX12中的指令队列原理。&lt;/p&gt;

&lt;p&gt;（DX12与GPU引擎协作原理）&lt;/p&gt;

&lt;h3 id=&quot;现在大部分新式gpu都包含多个功能的独立引擎例如复制引擎计算引擎3d引擎把原本提交的gpu指令大杂烩拆分并交给不同的引擎这样各大引擎可以根据自身情况做优化从而使得执行效率变得更高而且拆分后引擎可以并行执行指令也使得整体指令执行效率变高&quot;&gt;现在大部分新式GPU都包含多个功能的独立引擎，例如复制引擎、计算引擎、3D引擎，把原本提交的GPU指令大杂烩拆分并交给不同的引擎，这样各大引擎可以根据自身情况做优化从而使得执行效率变得更高，而且拆分后引擎可以并行执行指令也使得整体指令执行效率变高。&lt;/h3&gt;

&lt;p&gt;在使用DX12时，每个线程都会填入复制、计算和3D队列这三个队列，其中3D队列可以驱动另外两个GPU引擎，计算队列可以驱动计算和复制引擎，而复制队列只能驱动复制引擎。&lt;/p&gt;

&lt;p&gt;（GPU中三大引擎工作原理，来源：微软官网）&lt;/p&gt;

&lt;p&gt;上图说明了DX中的多引擎队列设计是如何跨多个GPU引擎协同工作的。它包括在必要时进行引擎间同步处理，因为引擎间有相互依赖的情况。
复制引擎会复制转译所需的几何，3D引擎会等候这些复本完成，并透过几何呈现预先传递。计算引擎会使用此结果并分派结果，同时复制引擎上一直在拷贝需要的资源，最终被3d引擎使用并进行最终绘制。&lt;/p&gt;

&lt;h2 id=&quot;参考资料&quot;&gt;参考资料：&lt;/h2&gt;

&lt;p&gt;《OpenGL ES3.0 编程指南》作者：金斯伯格&lt;/p&gt;

&lt;p&gt;《游戏引擎架构》作者：杰森.格雷戈瑞&lt;/p&gt;

&lt;p&gt;《游戏引擎原理与实践》作者：程东&lt;/p&gt;

&lt;p&gt;《GPU 引擎》&lt;/p&gt;

&lt;p&gt;https://docs.microsoft.com/zh-tw/windows/win32/direct3d12/user-mode-heap-synchronization&lt;/p&gt;

&lt;p&gt;《CPU体系结构》&lt;/p&gt;

&lt;p&gt;https://my.oschina.net/fileoptions/blog/1633021&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485343&amp;amp;idx=1&amp;amp;sn=78ee7bce86f53c7ed5cb59d08a6a29f2&amp;amp;chksm=fc226298cb55eb8e89b22caa781fa0ed21dbb196720d633f62855bfd4d3a4959b78c343ac54b&amp;amp;token=39636778&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>给女儿写信(二十四) 沟通</title>
   <link href="http://www.luzexi.com/2022/05/02/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A124"/>
   <updated>2022-05-02T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/05/02/给女儿的信24</id>
   <content type="html">
&lt;hr /&gt;

&lt;p&gt;导读：喜欢跟孩子们一起，可惜一直在外地工作，因此我给孩子写点信并录成视频，跟孩子聊聊自己的故事以及最近的感受&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;要点&quot;&gt;要点&lt;/h1&gt;

&lt;p&gt;沟通很重要，我在工作中学会了两个技巧，第一是沟通非常重要，我们要告诉对方我们的困难，需要怎样的帮助，同时也要去了解对方有什么困难，需要怎样的帮助。第二是沟通技巧，说话时常常带有情绪，例如，总是，老是，已经，等等，这些情绪会让人很不舒服，如果我们能去除这些情绪的词语，我们表达说话时，会让人感觉到如沐春风。&lt;/p&gt;

&lt;h1 id=&quot;大纲&quot;&gt;大纲&lt;/h1&gt;

&lt;pre&gt;&lt;code&gt;1.调节女儿间问题，引来女儿生气
2.与女儿对话，引来厌烦
3.与女儿讲自己的故事
4.与女儿分享自己的成长
&lt;/code&gt;&lt;/pre&gt;

&lt;h1 id=&quot;结构&quot;&gt;结构&lt;/h1&gt;

&lt;pre&gt;&lt;code&gt;1.给女儿讲自己在家里的故事
2.两个女儿闹矛盾了我调节时，另一个女儿又生气了
3.女儿生气了，躲在房间里不肯说话
4.我走到女儿边上，想跟女儿对话，但女儿不肯开口
5.我对着女儿开始讲自己的工作的故事
6.故事发生在我1年半前，我刚到公司，对公司的流程和规则很不熟悉。
7.工作中沟通并不好，于是就出现了问题。
8.后来慢慢爸爸学习和练习沟通技巧，工作也变得顺利了。
9.总结这个故事中学习到的事情
&lt;/code&gt;&lt;/pre&gt;

&lt;h1 id=&quot;内容&quot;&gt;内容&lt;/h1&gt;

&lt;p&gt;hi秀恩，安妮，爸爸好想你们。&lt;/p&gt;

&lt;p&gt;上周爸爸回家虽然短暂，能看到你们很开心。&lt;/p&gt;

&lt;p&gt;今天爸爸给你们讲讲，我在家里的故事，好不好。&lt;/p&gt;

&lt;p&gt;上上次回杭州时，在家里跟我的女儿聊了好久，我很喜欢跟女儿聊天，&lt;/p&gt;

&lt;p&gt;其中有一天，妈妈不在家，于是我跟两个女儿一起在家，我刚看了会书，&lt;/p&gt;

&lt;p&gt;大女儿和小女儿就吵起来了，我想劝她们，&lt;/p&gt;

&lt;p&gt;但效果并不好，反而大女儿又伤心起来，于是我想上去安慰她，&lt;/p&gt;

&lt;p&gt;但她似乎不想跟我说话。&lt;/p&gt;

&lt;p&gt;一开始，她睡在房间里，我坐在她边上，我一直邀请她跟我说说话，&lt;/p&gt;

&lt;p&gt;后来，她又做到了沙发上，我也跟着来了，但她还是不理我，&lt;/p&gt;

&lt;p&gt;于是我开始自说自话，跟她讲了我自己的故事。&lt;/p&gt;

&lt;p&gt;这是一个我真实的故事，&lt;/p&gt;

&lt;p&gt;这个故事告诉我，沟通是非常重要的，&lt;/p&gt;

&lt;p&gt;故事发生在2年前，我刚到深圳，&lt;/p&gt;

&lt;p&gt;一开始，我认识的并人不多，工作也还算轻松，也不需要跟太多人打交道。&lt;/p&gt;

&lt;p&gt;后来领导派了一个重要的任务给我，但我并没有做好，&lt;/p&gt;

&lt;p&gt;于是我开始回顾和总结，我发现我平时跟人打交道并不多，&lt;/p&gt;

&lt;p&gt;这是直接导致我不善于沟通的一个直接原因。&lt;/p&gt;

&lt;p&gt;后来我慢慢学会了沟通，并且在沟通中找到了技巧，&lt;/p&gt;

&lt;p&gt;这些技巧让我在工作中更加顺利，并且与同事们和朋友们交流的非常好。&lt;/p&gt;

&lt;p&gt;在这期间我学到了很多沟通方法，其中又两个点比较重要。&lt;/p&gt;

&lt;p&gt;第一，我们经常在说话时，会使用一些情绪化的词语，&lt;/p&gt;

&lt;p&gt;比如总是、老是、已经、老早、早就等等，这些词语都是带有情绪的词语，&lt;/p&gt;

&lt;p&gt;让人听了很不舒服。如果我们能去掉这些词语，用一些比较理性的词语代替的话，&lt;/p&gt;

&lt;p&gt;沟通交流会舒服很多，就像被温柔的春风吹拂那样，如沐春风。&lt;/p&gt;

&lt;p&gt;第二，沟通很重要，每个人收到的信息是不对等的，&lt;/p&gt;

&lt;p&gt;我需要怎样的帮助，我的困难是什么，我们需要告诉同伴，&lt;/p&gt;

&lt;p&gt;同时也要通过友好的沟通，去了解对方需要怎样的帮助，遇到了什么困难。&lt;/p&gt;

&lt;p&gt;通过沟通，我们可以更加了解现在的情况是怎样的，我们可以怎么行动。&lt;/p&gt;

&lt;p&gt;讲完这个故事后，我的女儿愿意跟我说话了，我们两后来聊的很开心，相处的也很愉快。&lt;/p&gt;

&lt;p&gt;今天的故事就讲到这里，秀恩、安妮，爸爸爱你们哟。&lt;/p&gt;

&lt;p&gt;我们下次再见。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(五十八) 如何讲好一个故事 - 点燃观众的想象力</title>
   <link href="http://www.luzexi.com/2022/04/22/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B058"/>
   <updated>2022-04-22T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/04/22/读书笔记58</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485311&amp;amp;idx=1&amp;amp;sn=e1acb7b446484043ae6541166172a48a&amp;amp;chksm=fc226278cb55eb6e2633e99ff0b86f047eb5d36e4ef0a9bda7bfe10036c843af64c8919298dd&amp;amp;token=39636778&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;背景&quot;&gt;背景：&lt;/h1&gt;

&lt;p&gt;最近比较关注写故事的能力，于是看了《如何讲好一个故事》这本书。&lt;/p&gt;

&lt;p&gt;起初原因是我在做备稿演讲时，发现自己写故事的能力较弱，每次写故事绞尽脑汁都很难想出内容来。&lt;/p&gt;

&lt;p&gt;随着时间的推移，我越发觉得故事的用处很多，故事能力很需要实践落地到我的生活和工作当中去。&lt;/p&gt;

&lt;p&gt;通过这次学习，我希望提高自己讲故事的能力，并用于各种场合中，包括演讲、交流、分享、家庭生活、以及工作当中去。&lt;/p&gt;

&lt;h3 id=&quot;最终我希望我能以故事的形式将生活中的所有枯燥都转化为有趣把一切刻板转化为生动让生活变得丰富多彩&quot;&gt;最终我希望我能以故事的形式将生活中的所有“枯燥”都转化为“有趣”，把一切“刻板”转化为“生动”，让生活变得丰富多彩。&lt;/h3&gt;

&lt;h1 id=&quot;概述&quot;&gt;概述：&lt;/h1&gt;

&lt;p&gt;本篇总结《如何讲好一个故事》的下半部分，主要内容是：
1.讲述祖母的故事
2.“发生了什么”相机故事法
3.如何描述细节&lt;/p&gt;

&lt;h1 id=&quot;内容&quot;&gt;内容：&lt;/h1&gt;

&lt;p&gt;简单回顾下前文，前面我们讲了障碍的倾听与释放，以及倾听和讲述的关系。这些都为我们创作故事和讲故事做好准备，通过识别障碍再释放障碍，可以让我们更好讲故事，通过了解倾听和讲述的关系，我们知道了讲述时的关键就是：与观众沟通。这篇文章主要来讲一讲，如何写故事。&lt;/p&gt;

&lt;h2 id=&quot;讲述祖母的故事&quot;&gt;讲述祖母的故事&lt;/h2&gt;

&lt;p&gt;作者提倡朋友们讲述祖母的故事，他建议通过讲祖母的故事来入门故事法。对于这种方法，我认为可以作为团队成员沟通的培训课程，至于用在练习写故事上恐怕效果不大。&lt;/p&gt;

&lt;p&gt;他认为，我们可以将自己想象成祖父母，然后做自我介绍。以第一人称“我”来讲述，告诉我们你的名字。通过站在祖母的立场去了解她的生活和状态，这使得祖母变得更加人性化。&lt;/p&gt;

&lt;p&gt;他说，“讲述祖母练习让我站在我祖母的立场去了解，她眼中的生活是怎样的，她认为自己的生活代表了什么，她经历了什么。我觉得我们大部分时候把祖父母看作是某种象征，而不是真实的人。我们可以通过设想自己就是她，再来谈论她的生活会有很大的不同。在我眼中，她变得更人性化。”&lt;/p&gt;

&lt;p&gt;（祖母故事的启发图）&lt;/p&gt;

&lt;p&gt;通过讲述祖母故事的练习，我们可以得到7个启发：
1.倾听和讲述之间有一种互相关系
2.倾听是一种才能
3.每个人都有自己的故事
4.每个人都有讲故事的能力
5.讲故事的基本原则对任何人都适用
6.故事可以用信息片段来构建
7.无论你认为自己多么不擅长讲故事，都会发现讲故事是我们天生就具备的能力。&lt;/p&gt;

&lt;p&gt;（在工作中讲故事的好处图）&lt;/p&gt;

&lt;p&gt;书中在这里提到很多案例，是关于在工作中讲故事的案例。大部分案例中的主角都是因为沟通不顺利而导致团队难以形成好的氛围。这种情况我们可以通过讲故事的方式化解工作中的难题。&lt;/p&gt;

&lt;h3 id=&quot;作者说讲故事能够推动变革这其中一些力量在于如何通过意识到倾听和说话之间的互相关系来重构我们的沟通有些则来自故事本身故事为我们与工作中的同事建立新关系提供了桥梁也使我们多维度化它们通过层层剥离来揭示普遍性可以在任何团队的沟通以及持续的富有成效的关系中发挥催化作用&quot;&gt;作者说讲故事能够推动变革，这其中一些力量在于，如何通过意识到倾听和说话之间的互相关系来重构我们的沟通，有些则来自故事本身。故事为我们与工作中的同事建立新关系提供了桥梁，也使我们多维度化。它们通过层层剥离来揭示普遍性，可以在任何团队的沟通以及持续的富有成效的关系中发挥催化作用。&lt;/h3&gt;

&lt;p&gt;在工作中讲故事可以让我们通过倾听和讲述与对方建立沟通，同时建立起了新的关系桥梁，也使得我们有更多维度化的交流和思考。富有远见的管理者通过故事提升洞察力、分享知识，并利用故事建立真正的联系。&lt;/p&gt;

&lt;p&gt;（为什么要分享真实的故事图）&lt;/p&gt;

&lt;p&gt;然后作者聊了聊，我们为什么要分享自己的真实故事。&lt;/p&gt;

&lt;h3 id=&quot;其实分享真实的故事是打破为保护自己而筑起的防御墙的一种强有力的方式通过分享自己的真实故事故事改编后更精彩我们不仅可以审视自己不断调整情绪调整认知也能做到影响周围的人&quot;&gt;其实分享真实的故事是打破“为保护自己而筑起的防御墙”的一种强有力的方式，通过分享自己的真实故事（故事改编后更精彩）我们不仅可以审视自己，不断调整情绪调整认知，也能做到影响周围的人。&lt;/h3&gt;

&lt;p&gt;书中介绍了许多这种分享自己故事的案例，他们把自己的真实遭遇讲给大家听，同时他们打破了局面迎接恐惧，用真实的人性去展现脆弱与强大打破了关系僵局，让倾听者更喜欢更了解讲述者，同时也释放了自己。&lt;/p&gt;

&lt;h2 id=&quot;发生了什么相机故事法&quot;&gt;“发生了什么”相机故事法&lt;/h2&gt;

&lt;p&gt;作者多次提到叙事法，这个方法是贯穿故事法始终的方法，其中的技巧就是用“发生了什么”相机。&lt;/p&gt;

&lt;p&gt;我们在运用叙事法时，创作的故事是对“发生了什么”这个问题作回答，这个方法指导讲述者，只讲述能够看到、听到、品尝到、摸到、或闻到的事实。用“发生了什么”方法讲故事指的就是，将实际发生的事件与人们看到、听到、闻到、尝到或触摸到的事件联系起来。&lt;/p&gt;

&lt;p&gt;（为什么要剔除判断-图）&lt;/p&gt;

&lt;h3 id=&quot;那么为什么要这么做呢原因是当我们说出想法和感受时会遭到观众的不同观点的反对这会引起不必要的倾听障碍从而导致整个故事情节没有被完全的倾听因此在这种讲故事的方法中判断评论观点和批判都必须被排除在外要完全依赖于事实描述能力来带领听众在故事场景中进行感官探索&quot;&gt;那么为什么要这么做呢？原因是，当我们说出想法和感受时会遭到观众的不同观点的反对，这会引起不必要的倾听障碍，从而导致整个故事情节没有被完全的倾听。因此在这种讲故事的方法中，判断、评论、观点和批判都必须被排除在外，要完全依赖于事实描述能力来带领听众在故事场景中进行感官探索。&lt;/h3&gt;

&lt;p&gt;简单来说就是，说出实际发生了什么，而不是你对所发生事情的想法或感受。即不带判断、评论观点，批判，说出实际发生了什么，而不是对所发生的事的想法和感受。&lt;/p&gt;

&lt;p&gt;作者称这种方法为，“发生了什么”照相机。&lt;/p&gt;

&lt;p&gt;（发生了什么相机图）&lt;/p&gt;

&lt;p&gt;1.你听到了什么？
外部的声音？还是你对别人说了什么？还是别人对你说了什么？&lt;/p&gt;

&lt;p&gt;2.你看到了什么？
描述你看到的周围的东西，包括环境、颜色、形状、布料等。&lt;/p&gt;

&lt;p&gt;3.你尝到了什么？
你尝到了什么？描述食物、饮料、味道。&lt;/p&gt;

&lt;p&gt;4.你闻到了什么？
周围有什么气味？&lt;/p&gt;

&lt;p&gt;5.你触摸了什么？
描述触摸到的材质和感受到的温度。&lt;/p&gt;

&lt;p&gt;回到自己的身体，描述这种感觉。&lt;/p&gt;

&lt;p&gt;（相机无法拍摄到的内容图）&lt;/p&gt;

&lt;p&gt;感受：情绪，感受和内在体验（不是想法）。&lt;/p&gt;

&lt;p&gt;解释：主要基于我们对事物的理解。&lt;/p&gt;

&lt;p&gt;判断：基于我们对某些思考方式或观察他人的倾向，对某人进行客观化、限定或描述的倾向。&lt;/p&gt;

&lt;p&gt;观点：未必基于事实或知识的观点或判断，一般性看法。&lt;/p&gt;

&lt;p&gt;辩解：试图用逻辑推理证明（行动或态度）。&lt;/p&gt;

&lt;p&gt;评论：对某一事件或情况所表达的意见或做出的解释。&lt;/p&gt;

&lt;p&gt;内心想法：这些想法包括以“我心里想”或“我对自己说过”开头的评论。&lt;/p&gt;

&lt;p&gt;这些是讲述“发生了什么”值钱常用语句。当你使用“发生了什么”相机时，&lt;/p&gt;

&lt;p&gt;就不需要这些口头禅了。“你可以说‘我们去院子里吃午饭’，我说‘享受美好的天气吧’。”&lt;/p&gt;

&lt;p&gt;在“发生了什么”照相机镜头下，能拍到许多真实细节，包括听到的、看到的、尝到的、闻到的、摸到的，同时照相机也无法拍摄到一些精神层面的东西，包括感受、解释、判断、观点、内心想法。&lt;/p&gt;

&lt;h3 id=&quot;作者说我们所有人都有这样一个坏习惯我们习惯在故事中添加解释和判断等内心活动这导致了听众在听故事时障碍了他们身临其境的感觉&quot;&gt;作者说，我们所有人都有这样一个坏习惯，我们习惯在故事中添加解释和判断等内心活动，这导致了听众在听故事时障碍了他们身临其境的感觉。&lt;/h3&gt;

&lt;h3 id=&quot;发生了什么相机法故意强加了一个限制要求你公平真诚地看待你的经历剔除我们所有人在讲故事时添加解释和判断等内心活动的习惯&quot;&gt;“发生了什么”相机法，故意强加了一个限制，要求你公平、真诚地看待你的经历，剔除我们所有人在讲故事时添加解释和判断等内心活动的习惯。&lt;/h3&gt;

&lt;p&gt;“专业”是能抛开偏见、判断、意见和成见，客观地分析事物。“发生了什么”是帮助我们成为“专业”的一个好方法。采用这种方法需要大量训练，因为陷入解释和判断的倾向犹如呼吸一样浸入在我们的习惯中。&lt;/p&gt;

&lt;p&gt;当大家都暂停了所有的判断和意见，只倾听接下来发生的事情，这相当于提供了一个足够宽广的倾听安全网。消除判断和意见，为倾听提供一个宽广的安全区。&lt;/p&gt;

&lt;p&gt;(听众感受障碍与无障碍对比图)&lt;/p&gt;

&lt;p&gt;设想一下，当别人用歪曲事实解释时，你能无动于衷吗？意见会产生意见，判断会导致判断。&lt;/p&gt;

&lt;h3 id=&quot;有且只有听到听看尝闻摸这五个感官方面的细节时听众才听得清楚明白才不会产生意见和分歧才不会障碍听众身临其境这五个感官细节为听众对故事的想象创造了空间&quot;&gt;有且只有听到，听、看、尝、闻、摸，这五个感官方面的细节时，听众才听得清楚明白，才不会产生意见和分歧，才不会障碍听众身临其境，这五个感官细节为听众对故事的想象创造了空间。&lt;/h3&gt;

&lt;h3 id=&quot;那么听众对故事的感觉为什么会增强呢因为我们人自然而然地把自己的情绪和感觉投射到由事实组成的故事上当我们想象自己沉浸在风景中事实组成的故事时直觉被激活洞察力被加强&quot;&gt;那么听众对故事的感觉为什么会增强呢？因为我们人自然而然地把自己的情绪和感觉投射到由事实组成的故事上，当我们想象自己沉浸在风景中（事实组成的故事）时，直觉被激活，洞察力被加强。&lt;/h3&gt;

&lt;p&gt;（故事与观众的沟通图）&lt;/p&gt;

&lt;p&gt;我们回头再看下作者所阐述的讲故事中最重要的事情，专注于一件事：与观众沟通。&lt;/p&gt;

&lt;h3 id=&quot;根据发生了什么相机法来讲述是倾听和讲述的最佳互动这种方式让我们与听众保持了沟通共同创作了一个故事同时让我们在写故事时就能感知到观众的倾听障碍从而更好的调整&quot;&gt;根据“发生了什么”相机法来讲述，是倾听和讲述的最佳互动，这种方式让我们与听众保持了沟通，共同创作了一个故事。同时让我们在写故事时就能感知到观众的倾听障碍，从而更好的调整。&lt;/h3&gt;

&lt;p&gt;我们选择讲述的、强调的、指向的内容都影响了故事的曲线，影响了倾听。当看到事物的落脚点，观察到听众如何倾听，我们就可以根据这些反馈来调整故事。&lt;/p&gt;

&lt;p&gt;在工作和生活中，我们可以用“发生了什么”的方式分享故事，这会以一种容易理解、令人难忘、强而有力的方式体现价值和目标，这样能更容易理解并令人难忘。不只作者，我也建议你，现在就可以尝试下，用“发生了什么”相机法，来描述最近的经历，写下你看到、听到、闻到、尝到或接触到的东西。&lt;/p&gt;

&lt;p&gt;如果你想看看那些精通用“发生了什么”相机法讲故事的人是如何讲故事的，只需看乔布斯在斯坦福大学的毕业典礼上的演讲就知道了。在开场白中，他定下了基调。他没有告诉我们他是怎么想的，只说了发生在他身上的事。他知道听众会有一种亲身经历的感觉和想法。他就是运用这种方法，陈述事实且不带想法和评判，从而让观众去体会这种亲身经历。&lt;/p&gt;

&lt;p&gt;他毫无保留的描述细节，从而点燃观众的想象力。&lt;/p&gt;

&lt;h2 id=&quot;如何描述细节&quot;&gt;如何描述细节&lt;/h2&gt;

&lt;p&gt;叙事法的核心任务之一，就是运用感官感受的方法客观地讲述所发生的事情。它的技巧就是我们所说的，用“发生了什么”相机法来描述由事实组成的故事。其中讲述细节最大的困难就是要选择保留什么，放弃什么。&lt;/p&gt;

&lt;p&gt;其实很简单，细节的选择标准是，细节必须为故事服务。
它不能只是为了增添色彩，它必须能推动情节发展，扩大我们对环境的理解，增强我们对角色的理解。&lt;/p&gt;

&lt;p&gt;细节可以故意放慢故事情节，提供短暂休息，或加快情节主线。它的作用类似于照相机的变焦镜头，让听众更接近你的世界观。&lt;/p&gt;

&lt;h2 id=&quot;常用细节对话&quot;&gt;常用细节，对话&lt;/h2&gt;

&lt;p&gt;我们在描述细节时，对话的方式比较常见。
叙事法与电影剧本使用的手法非常相似，剧本创作时，对话是揭示人物性格的重要媒介。&lt;/p&gt;

&lt;h3 id=&quot;我们不能诉诸角色刻画例如他很慷慨她很邪恶他缺乏勇气这些描述都是一种解释&quot;&gt;我们不能诉诸角色刻画，例如“他很慷慨”，“她很邪恶”，“他缺乏勇气”，这些描述都是一种解释。&lt;/h3&gt;

&lt;h3 id=&quot;我们不能去解释而是要去描述从而让观众自己去体会人物性格情节感受&quot;&gt;我们不能去解释，而是要去描述，从而让观众自己去体会，人物性格、情节感受。&lt;/h3&gt;

&lt;p&gt;（对话的功能图）&lt;/p&gt;

&lt;p&gt;对话有以下功能：
1.体现人物性格
2.推动故事情节发展
3.向观众传达事实和信息
4.建立关系
5.揭示冲突和权力动态
6.表达人物的情感状态&lt;/p&gt;

&lt;p&gt;提示：还有一个技巧是，说没发生的事情，有时候说没发生的事情要胜过说发生的事情。&lt;/p&gt;

&lt;h2 id=&quot;常用细节误区情感&quot;&gt;常用细节误区，情感&lt;/h2&gt;

&lt;p&gt;我们前面说到很多关于情绪情感，这里罗列下错误的认知：
1.错误的认为讲“发生了什么”这句话排除了表达情感的可能。
2.错误的认为讲述情感就是表达情感。&lt;/p&gt;

&lt;h3 id=&quot;用发生了什么的描述填充了故事画面从而让听众产生故事需要的所有情感我们可以享受选择细节来激活并参与这些情感以达到戏剧性的效果我们会发现我们越是坚持不去解释和评判就越能消除这些阻碍我们交流的障碍&quot;&gt;用“发生了什么”的描述，填充了故事画面，从而让听众产生故事需要的所有情感，我们可以享受选择细节来激活并参与这些情感，以达到戏剧性的效果。我们会发现我们越是坚持不去解释和评判，就越能消除这些阻碍我们交流的障碍。&lt;/h3&gt;

&lt;p&gt;通过讲述所发生的事情和详细描述经过，我们可以给自己和他人展示更清晰的事件画面，而不会用情感的语言蒙蔽他们的倾听。
我们的情感，判断，评价，蒙蔽了大家的倾听。我们允许情感出现，但要识别它们，释放它们，不能让它们牵引我们，所以要用“发生了什么”当作指引的原则。&lt;/p&gt;

&lt;h2 id=&quot;关于细节的总结&quot;&gt;关于细节的总结&lt;/h2&gt;

&lt;p&gt;（细节总结图）&lt;/p&gt;

&lt;p&gt;要点：&lt;/p&gt;

&lt;p&gt;1.根据五种感官感觉讲述“发生了什么”。
如果你听不到、尝不到、闻不到、摸不到、看不到，就向相当于什么都没发生。&lt;/p&gt;

&lt;p&gt;2.把所有的解释、意见、判断、感情的陈述，以及“发生了什么”相机拍摄不到的一切东西都去除。
可以在打磨稿件时，检查每个解释、判断和感情陈述之词，接着修改和调整。&lt;/p&gt;

&lt;p&gt;3.没有道德或经验教训方面的评判，这些不属于故事。
让倾听者中自己构建感受。&lt;/p&gt;

&lt;p&gt;4.相信听众有能力理解你的信息。
信息来源是从你对发生在的事情描述。&lt;/p&gt;

&lt;p&gt;5.这种方法需要大量练习，坚持练习。
将经验与复杂的解释、观点和判断联系起来的习惯需要被改变，这种习惯已经成为讲好一个故事的最大障碍。练习时，一开始可能需要花些精力和时间，逐渐就会走上正轨。&lt;/p&gt;

&lt;p&gt;6.故事的目的在于，与听众建立联系。
说出所发生的一切就能让这种联系产生，用故事连接听众。&lt;/p&gt;

&lt;h3 id=&quot;通过发生了什么照相机的故事讲述能力你的创造力会不断提升同时你会惊讶于自己看待周围事件对象或人物越来越准确维度更趋于多样化&quot;&gt;通过“发生了什么”照相机的故事讲述能力，你的创造力会不断提升，同时你会惊讶于自己看待周围事件、对象或人物越来越准确，维度更趋于多样化。&lt;/h3&gt;

&lt;h3 id=&quot;通过这种练习作为演讲者在进行演讲时会更加专注方向也更加明确最重要的是它使我们熟悉我习惯中的判断意见和评论我们会意识到我们在谈到一件事或同事的某种行为时总是从意见和判断出发发生了什么这个方法会逐渐的让我们看清事情的来龙去脉练习的越多你就收获的越多&quot;&gt;通过这种练习，作为演讲者在进行演讲时会更加专注，方向也更加明确。最重要的是，它使我们熟悉我习惯中的，判断、意见和评论。我们会意识到，我们在谈到一件事或同事的某种行为时，总是从意见和判断出发。“发生了什么”这个方法，会逐渐的让我们看清事情的来龙去脉，练习的越多，你就收获的越多。&lt;/h3&gt;

&lt;p&gt;巩固下故事创作过程：&lt;/p&gt;

&lt;p&gt;1.思考要讲故事的2个问题
	1.为什么要讲这个故事？
	2.为什么要现在讲？&lt;/p&gt;

&lt;p&gt;2.先识别障碍，再释放障碍。
通过“发生了什么”来做沟通释放&lt;/p&gt;

&lt;p&gt;3.通过“发生了什么”相机收集故事各个部分的细节&lt;/p&gt;

&lt;p&gt;实际中，倾听和讲述的动态互动过程并不总是线性的。
通过挖掘可以很快地进入创作，在创作过程中，可能会发现有必要进行更多的挖掘。&lt;/p&gt;

&lt;p&gt;很多领导者都会认识到讲故事能力是团队成功的基础。某种意义上说，他们爬上通往成功的阶梯时，动力来自于对自己讲述的故事。很多领导者意识到，必须有一个“更大的目标”，把每个人拉到同一条船上，把他们连在一起，使他们成为一个有凝聚力的整体。故事比数字更能表达这一点。&lt;/p&gt;

&lt;h2 id=&quot;其他&quot;&gt;其他&lt;/h2&gt;

&lt;p&gt;(其他方面的总结图)&lt;/p&gt;

&lt;h2 id=&quot;如何开头&quot;&gt;如何开头&lt;/h2&gt;

&lt;p&gt;根据叙事法的经验法则，开头就是故事事实的起点，每个故事都有一个起点，它是事实存在的。&lt;/p&gt;

&lt;p&gt;开头很重要，如果故事的第一句就能吸引听众的注意力，那么他们会跟着你的故事走。&lt;/p&gt;

&lt;h3 id=&quot;强有力的故事开头总是以发生了什么开始你的故事的第一句话就可以把观众带入故事由于不知道前因后果被带入故事中会激发我们的好奇心&quot;&gt;强有力的故事开头总是以“发生了什么”开始。你的故事的第一句话就可以把观众带入故事，由于不知道前因后果，被带入故事中会激发我们的好奇心。&lt;/h3&gt;

&lt;p&gt;随着时间推移，你会对开头和结尾之间的关系有更敏锐的认识。你的故事开头变得不再那么随意，并且可以为结尾埋下微妙的伏笔。&lt;/p&gt;

&lt;h2 id=&quot;如何结尾&quot;&gt;如何结尾&lt;/h2&gt;

&lt;h3 id=&quot;你可能想总结自己故事中包含的教训请不要这么做让听众自己得出结论从你的起源故事中得到教训会更有说服力&quot;&gt;你可能想总结自己故事中包含的教训，请不要这么做，让听众自己得出结论，从你的起源故事中得到教训，会更有说服力。&lt;/h3&gt;

&lt;p&gt;寻找故事结尾，就是拟合故事创作和故事讲述之间的鸿沟。在故事创作过程中，结尾是一座里程碑，是要到达的彼岸，需要于故事的开头形成必要的张力。&lt;/p&gt;

&lt;h2 id=&quot;如何与听众建立联系&quot;&gt;如何与听众建立联系&lt;/h2&gt;

&lt;p&gt;从询问“为什么要讲故事？为什么要现在讲”那一刻起，你已经在开始思考和听众的关系。&lt;/p&gt;

&lt;p&gt;这种询问的方式用开放的模式和框架去引发我们的思考，接着就是挖掘故事并精心设计开头、情感转折点和结尾。&lt;/p&gt;

&lt;p&gt;为了故事的效果需要，你可以故意走到某个角落，或靠近观众。&lt;/p&gt;

&lt;p&gt;记住每一个选择都是为了引起别人的注意，吸引注意力是演示的关键。&lt;/p&gt;

&lt;h2 id=&quot;关于演讲的体会&quot;&gt;关于演讲的体会&lt;/h2&gt;

&lt;p&gt;从本质上讲，演讲应该带你走出舒适区。你会发现，离开那个区域的每一步，都是迈向真实和个性化故事的一步。&lt;/p&gt;

&lt;p&gt;上台演讲的步骤：
1.开始说之前与身体沟通，站稳、自然呼吸，扫视听众，偶尔将目光停留在某个人身上。
2.与听众保持目光接触。演讲过程中，与5-6个人进行单独的眼神交流。
3.放慢节奏。说的太快，会失去听众。
4.注意音量。如果不确定后排是否能听到，就问问他们，这有利于增加信心，并与听众建立直接的联系。
5.故事结束时要泰然自若，保持镇定，保持与听众的眼神接触，你的故事可以启发他们。&lt;/p&gt;

&lt;p&gt;注意，永远不要背诵故事，因为这会妨碍你的创造力和真实表现能力。一旦你练习通过大脑和身体讲述故事，并且已经充分准备好倾听环境，就没有什么需要记忆的了。
我们要通过大脑和身体来讲述故事，而不是仅仅凭借记忆。&lt;/p&gt;

&lt;p&gt;最后，改变不是那么简单的，旧习惯的消除非一朝一夕，需要坚持和磨练。任何技能，都需要我们去实践。通过实践练习，会培养出更强的自我意识以及相应的能力，并在出现认知和情感障碍时能够注意到这些障碍。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485311&amp;amp;idx=1&amp;amp;sn=e1acb7b446484043ae6541166172a48a&amp;amp;chksm=fc226278cb55eb6e2633e99ff0b86f047eb5d36e4ef0a9bda7bfe10036c843af64c8919298dd&amp;amp;token=39636778&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(五十七) 如何讲好一个故事 - 识别与释放倾听障碍</title>
   <link href="http://www.luzexi.com/2022/04/11/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B057"/>
   <updated>2022-04-11T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/04/11/读书笔记57</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485242&amp;amp;idx=1&amp;amp;sn=127e82b7566e4463304ee972e56a2aa9&amp;amp;chksm=fc22623dcb55eb2b5457161c985457302bc94ad3a9b3311938b54959fab9f79571127bb5501d&amp;amp;token=39636778&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;背景&quot;&gt;背景：&lt;/h2&gt;

&lt;p&gt;最近比较关注写故事的能力，于是看了《如何讲好一个故事》这本书。&lt;/p&gt;

&lt;p&gt;起初原因是我在做备稿演讲时，发现自己写故事的能力较弱，每次写故事绞尽脑汁都很难想出内容来。&lt;/p&gt;

&lt;p&gt;随着时间的推移，我越发觉得故事的用处很多，故事能力很需要实践落地到我的生活和工作当中去。&lt;/p&gt;

&lt;p&gt;通过这次学习，我希望提高自己讲故事的能力，并用于各种场合中，包括演讲、交流、分享、家庭生活、以及工作当中去。&lt;/p&gt;

&lt;p&gt;最终我希望我能以故事的形式将生活中的所有“枯燥”都转化为“有趣”，把一切“刻板”转化为“生动”，让生活变得丰富多彩。&lt;/p&gt;

&lt;h2 id=&quot;概述&quot;&gt;概述：&lt;/h2&gt;

&lt;p&gt;本篇总结《如何讲好一个故事》的上半部分，主要内容是：
1.挖掘故事
2.倾听与讲述的关系
3.识别倾听障碍
4.释放倾听障碍&lt;/p&gt;

&lt;h2 id=&quot;内容&quot;&gt;内容：&lt;/h2&gt;

&lt;p&gt;简单回顾下前面的内容，上篇主要说的是挖掘故事并阐述了倾听和讲述的关系。我们在创作故事的时候有很多误区，很多人都认为自己没有故事也写不出故事，实际上我们每个人都有很多故事可以写，而且故事会随着时间推移不断变化。&lt;/p&gt;

&lt;p&gt;作者说，写故事时最重要的是，专注于与听众沟通。&lt;/p&gt;

&lt;p&gt;这里介绍了两个技巧：&lt;/p&gt;

&lt;p&gt;第一问自己两个问题，“为什么要讲故事？为什么要现在讲”，通过回答这两个问题来挖掘故事，它会带来方向性和紧迫性，回答的越准确、越深入，讲故事的专注度就越高。&lt;/p&gt;

&lt;p&gt;第二问自己“发生了什么”，用不带评判的词语来挖掘故事，例如，“然后发生了什么”，“接着发生了什么”，“后来发生了什么”，“发生了什么”等等，我们带着好奇心并不带任何评判的寻求完整的故事情境再现。&lt;/p&gt;

&lt;p&gt;接着讲了倾听和讲述的关系，倾听和讲述是相互影响的，倾听（听觉与感受）会被内心和外界的事情影响，因此保持倾听的纯洁性是非常重要的一步。&lt;/p&gt;

&lt;p&gt;具体技巧是，我们叙述内容不加入任何评判，不加判断的讲述是一种技能和技巧，可以通过练习获得。然后通过倾听来识别障碍，接着释放障碍，为后面的沟通铺平道路。&lt;/p&gt;

&lt;p&gt;这篇我们来讲讲有哪些障碍，如何识别障碍，以及释放障碍。&lt;/p&gt;

&lt;h2 id=&quot;三识别倾听障碍&quot;&gt;三、识别倾听障碍&lt;/h2&gt;

&lt;p&gt;首先来介绍下倾听冥想，它可以帮助我们识别倾听障碍。冥想倾听步骤为：&lt;/p&gt;

&lt;p&gt;1.闭上眼睛，开始倾听冥想
2.把注意力放在室内可能出现的任何声音上。室内听到了什么。
3.把意识带到室外。室外有什么声音。
4.把注意力集中到身边的人。身边听到了什么。
5.把意识转回到自己身上，用大脑去倾听内脏。
6.用脚去倾听，做几次呼吸放松一下。
7.简单记下这些障碍倾听的事情，从非判断角度来认识它们。&lt;/p&gt;

&lt;p&gt;除了冥想倾听，我们也可以主动寻求倾听障碍，例如在沟通中我们可以用“发生了什么”来主动寻求反馈，询问周围的人的倾听障碍，“阿张，最近常减你独自沉默，发生了什么？”，“阿明，看到你比较忧郁，发生了什么？”。&lt;/p&gt;

&lt;p&gt;想要真正了解别人的倾听障碍，需要的是倾听，而不是判断、评价和解释。&lt;/p&gt;

&lt;p&gt;倾听而不评判是种技巧，逐渐你会意识到，试图把某些想法推开会适得其反，它们会变得更强大。换句话说，不要评判自己的判断，不要评判别人的判断，只要意识到它们的存在并加以观察便可。&lt;/p&gt;

&lt;p&gt;这种判断，虽然可能会让我们得到一种优越感和独立性，让自己独立出来。但它牺牲的是自己和他人的联系，失去了与他人互相理解的机会。&lt;/p&gt;

&lt;p&gt;归根结底，很多阻碍倾听的障碍源自恐惧。如果能够在自己倾听冥想中识别恐惧的障碍，对我们有很大好处。&lt;/p&gt;

&lt;p&gt;很多时候我们对事物的本能反应都是基于不被承认的恐惧。那是因为我们没有认识到自己内心的恐惧，而是把恐惧投射到其他人或其他物体上。这部分具体内容将在后面文章中详细讲述。&lt;/p&gt;

&lt;h3 id=&quot;总之不管有多少障碍在倾听冥想或活动时显露出来我们的立场都应该是倾听障碍本身没有任何问题仅此而已现在我们注意到了障碍仅仅注意到这些障碍有时也需要真正的自我意识&quot;&gt;总之，不管有多少障碍在倾听冥想或活动时显露出来，我们的立场都应该是，倾听障碍本身没有任何问题，仅此而已。现在，我们注意到了障碍，仅仅注意到这些障碍，有时也需要真正的自我意识。&lt;/h3&gt;

&lt;p&gt;下面将列举所有的倾听障碍，讲倾听障碍分为5类:&lt;/p&gt;

&lt;p&gt;1.外部的倾听障碍
2.身体上的倾听障碍
3.内部的倾听障碍
4.心理方面的倾听障碍
5.人际关系方面的倾听障碍&lt;/p&gt;

&lt;h2 id=&quot;外部的倾听障碍&quot;&gt;外部的倾听障碍&lt;/h2&gt;

&lt;h3 id=&quot;在生物原理中听觉是对声音的物理感知在这种感知中声波被耳膜接收然后通过一套涉及细小骨骼的复杂机械互相作用转换成神经脉冲传递到大脑在大脑中被解释为意义&quot;&gt;在生物原理中，听觉是对声音的物理感知，在这种感知中，声波被耳膜接收，然后通过一套涉及细小骨骼的复杂机械互相作用，转换成神经脉冲传递到大脑，在大脑中被解释为意义。&lt;/h3&gt;

&lt;p&gt;语句的声音和音调，在进入身体时，会产生广泛的影响。声音的质量可以引起身体的感觉。有些声音悦耳动听、抚慰人心，而另一些则嘈杂刺耳、令人不安。&lt;/p&gt;

&lt;h3 id=&quot;视觉是通过眼睛对视觉刺激的物理感知在视觉刺激中图像被感知并传递给大脑进行解释&quot;&gt;视觉是通过眼睛对视觉刺激的物理感知，在视觉刺激中，图像被感知并传递给大脑进行解释。&lt;/h3&gt;

&lt;p&gt;你所看到的东西都会影响你的倾听能力，例如，讲故事的人看起来很悲伤，会影响你的倾听。又比如，讲故事的人看起来衣冠不整、蓬头垢面，会影响你的倾听。因此你所有看到的情形都会影响你的判断。&lt;/p&gt;

&lt;h3 id=&quot;嗅觉是通过鼻子对进入大脑的嗅觉刺激的物理感知&quot;&gt;嗅觉是通过鼻子对进入大脑的嗅觉刺激的物理感知。&lt;/h3&gt;

&lt;p&gt;例如，房间里的气味，会影响你的倾听。强烈的香水或香蜡烛，会影响你的倾听。壁炉里啪啪作响的烟火，会影响你的倾听。你所闻到的都会影响你的判断。&lt;/p&gt;

&lt;h3 id=&quot;身体方面的倾听障碍&quot;&gt;身体方面的倾听障碍&lt;/h3&gt;

&lt;p&gt;包括饥饿、想上厕所、疲倦、身体疼痛、性刺激、衣服太紧不合身、皮疹、甚至发型都会障碍倾听。&lt;/p&gt;

&lt;h3 id=&quot;内部的倾听障碍&quot;&gt;内部的倾听障碍&lt;/h3&gt;

&lt;p&gt;包括思想、记忆、情感、感觉。这些都是噪声，指的是我们脑中的‘胡思乱想’。这种噪声主要由自由浮动的想法组成，这些想法的强度各不相同。&lt;/p&gt;

&lt;h3 id=&quot;心理方面的倾听障碍&quot;&gt;心理方面的倾听障碍&lt;/h3&gt;

&lt;p&gt;心里方面障碍较多，原因主要是我们对他人和我们自己的判断，使得倾听和发挥创造力变格外的困难。&lt;/p&gt;

&lt;p&gt;倾听者情感上的喜欢或不喜欢，强烈认同或非常反感，都会对倾听产生干扰。因此，例如宗教、政治或其他话题的观点可能会成为倾听的障碍。
我们在倾听故事时，要避免自己做出分析和判断，因为对他人和我们自己的判断使我们很难有创造力。&lt;/p&gt;

&lt;h3 id=&quot;关系方面的倾听障碍&quot;&gt;关系方面的倾听障碍&lt;/h3&gt;

&lt;p&gt;我们和别人的关系也会影响我们的倾听。常见的是两个人中有一个人比另一个人地位高，父母与孩子，兄长与弟妹，上级与下级等。因此我们必须意识到，等级制度影响倾听，只有这样才不会影响开放真诚的倾听。&lt;/p&gt;

&lt;p&gt;总之，我们需要理解并贯彻了倾听和讲述故事之间的相互关系，这样才能从中受益。在倾听的时候，不要试图解决或抓住障碍，而是要释放障碍以便能更好的倾听和参与。
不加判断的倾听绝非易事，但是做得越多，你的收获就会越多。&lt;/p&gt;

&lt;p&gt;要释放倾听障碍，同样需要练习。下面文章内容将介绍如何释放倾听障碍。&lt;/p&gt;

&lt;h2 id=&quot;四释放倾听障碍&quot;&gt;四、释放倾听障碍&lt;/h2&gt;

&lt;p&gt;作者说，倾听自己是一种自审，学会倾听自己会提高自己倾听他人的能力。&lt;/p&gt;

&lt;p&gt;再来回顾一下前面说的，讲好故事的关键，只要专注于一件事：与观众沟通。&lt;/p&gt;

&lt;p&gt;什么是与观众沟通？就是在看和听你故事的人，他们的情绪、想法、动作、噪声，都是与你沟通中的要素。一个好故事要与观众保持良好的沟通，而不是自顾自说话。&lt;/p&gt;

&lt;p&gt;你在讲和写故事的时候，可能会对观众产生恐惧、不适、喜悦、怨恨，这些都妨碍了我们倾听和表现，我们要去识别并承认这些障碍，而不是压抑或使用其他常见的心理策略。&lt;/p&gt;

&lt;p&gt;（与观众沟通障碍图）&lt;/p&gt;

&lt;p&gt;从某种意义上说，意识到这些障碍的存在强化了我们讲故事的能力，因为不再有任何隐藏的东西阻碍我们前进。提出这些障碍需要一点勇气，但这种勇气为讲故事创造了一个广阔的天地。&lt;/p&gt;

&lt;p&gt;因此通过识别听众，然后识别障碍，最后通过释放障碍来分析如何沟通，这是很有裨益的，让你可以继续前进。&lt;/p&gt;

&lt;p&gt;实际上障碍也可以成为我们故事的素材。很多障碍都与过去的经历和记忆有关，我们没有压制障碍，只是记下它们，让它们成为现实，原因之一就是其中一些障碍可以成为故事的素材。
识别倾听障碍不仅为释放障碍铺平了道路，而且可能会从一些经历和记忆中挖掘出故事。&lt;/p&gt;

&lt;p&gt;通过识别障碍，再向对方讲述我所识别到的障碍来释放障碍，这是比较常见的释放障碍的方法。&lt;/p&gt;

&lt;p&gt;特别是领导角色，大家特别关注你的反应，因此经常会加上他们个人的评判。&lt;/p&gt;

&lt;p&gt;通过识别障碍、释放障碍练习，你可以把注意力集中在当下，产生更纯粹的注意力。&lt;/p&gt;

&lt;p&gt;作者以开会为例来说明我们讲故事时的障碍和释放方法。&lt;/p&gt;

&lt;p&gt;开会与讲故事有什么关联呢？它们都需要与听众沟通。&lt;/p&gt;

&lt;p&gt;与听众沟通过程中，有很多障碍需要识别和释放，并且最后都需要达到一些目标。&lt;/p&gt;

&lt;p&gt;开会和讲故事很相似，拿开会来做比喻非常恰当，我们来看看：如何开好一个会？&lt;/p&gt;

&lt;p&gt;首先，当你不断问自己，为什么要开这个会议？为什么要现在开？这样做，你就可以从头到尾都集中精力。&lt;/p&gt;

&lt;p&gt;其次，你要通知大家会议信息，安排专属会议地点，减少空间和时间中的干扰因素。&lt;/p&gt;

&lt;p&gt;接着，你要管理会议时间，计划会议的内容以及精确到分钟的时间，这样我们就能知道什么时间做什么事情，并密切关注时间是否合理。&lt;/p&gt;

&lt;p&gt;训练有素的时间管理创造了井井有条的环境，人们觉得自己都得到了倾听，因为每个人被分配了适当的发言时间和评论时间。这也是为了消除“注意力缺失”，让人们知道这次会议的发言是有时间限制的。&lt;/p&gt;

&lt;p&gt;最后，你的会议要有目标或产出，在为会议开始前，就要让大家知道会议的期望是什么。&lt;/p&gt;

&lt;p&gt;另外我们需要引导参与者讲述所发生的事，&lt;/p&gt;

&lt;p&gt;我们在讲述故事时不一定要带着感情，因为听众很容易就能理解讲述者的感受。&lt;/p&gt;

&lt;p&gt;不管沟通内容是什么，多问问“发生了什么”，这会改变会议的模式，影响会议的结果。&lt;/p&gt;

&lt;p&gt;当你向讲述者提出问题时，不要问“为什么”，问题要从“什么（what）”开始，例如“发生了什么”，“然后呢”，“还发生了什么”，“当时你做了什么”。&lt;/p&gt;

&lt;p&gt;通过保持故事的真实性，可以减少过多解释和判断导致的混乱。&lt;/p&gt;

&lt;p&gt;最后，记录者必须重视发生了什么重要的事情：会议本身的细节，即决定的行动和谁将对这些行动负责。这样我们就可以跟踪会议期间做的行动决定，并选择适合自己的方案。&lt;/p&gt;

&lt;h3 id=&quot;这里说一下改编改造故事其实是很好的事改变意味着放弃旧的故事创造新的故事或寻求新的方法来讲述故事&quot;&gt;这里说一下，改编、改造故事其实是很好的事，改变意味着放弃旧的故事，创造新的故事，或寻求新的方法来讲述故事。&lt;/h3&gt;

&lt;p&gt;有时候我们在倾听时会走神，那一刻失去对故事的倾听和理解，通过观察障碍将自己拉回来，然后让障碍从脑海中消失。&lt;/p&gt;

&lt;p&gt;这里面整理写一下，总共有7个关键：
1.深度思考“为什么？”，即“为什么要？为什么现在要？”
2.减少干扰因素
3.多问发生了什么，来引导讲故事
4.保持真实性，减少判断和混乱
5.积极改编故事，让故事更适合当下
6.计划内容、安排时间和篇幅，让结构清晰
7.有目标的产出，提高效果&lt;/p&gt;

&lt;p&gt;这7个关键点也是如何讲好一个故事所具备的点。&lt;/p&gt;

&lt;h3 id=&quot;作者说识别和释放障碍并不意味着抑制或拒绝某些东西而是承认这些障碍的存在然后将其搁置在一边以便我们能从沟通中得到最大的收获&quot;&gt;作者说，识别和释放障碍并不意味着抑制或拒绝某些东西，而是承认这些障碍的存在，然后将其搁置在一边，以便我们能从沟通中得到最大的收获。&lt;/h3&gt;

&lt;h3 id=&quot;识别障碍和释放障碍这两者是不可或缺的通过识别自己的障碍我们可以知道我们在写故事时存在的问题通过识别他人的障碍你可以了解到你无需对他们的障碍负责他们需要面对自己的障碍而不是你释放障碍可以帮助我们更好的讲故事更好的与观众沟通&quot;&gt;识别障碍和释放障碍这两者是不可或缺的，通过识别自己的障碍，我们可以知道我们在写故事时存在的问题，通过识别他人的障碍，你可以了解到你无需对他们的障碍负责，他们需要面对自己的障碍，而不是你。释放障碍可以帮助我们更好的讲故事，更好的与观众沟通。&lt;/h3&gt;

&lt;p&gt;这里简单聊下反馈，反馈的核心是鼓励对方做出改变，正面的肯定反馈有助于缓和内心自我评价和自我批评的声音。&lt;/p&gt;

&lt;p&gt;倾听与反馈指导原则&lt;/p&gt;

&lt;p&gt;1.反馈永远是对内容的评论，而不是对讲述者的评论&lt;/p&gt;

&lt;p&gt;2.反馈不是评价、判断、解释或提意见。更确切的说，它是为了让讲述者对发生的事情做出说明。&lt;/p&gt;

&lt;p&gt;所有的反馈都应该从给予正面的肯定开始。从对方刚分享的内容里，总能找到一些有用的东西。及时给予认可是维持倾听的有效方式，因为它有助于缓和内心自我评价或自我批评的声音。&lt;/p&gt;

&lt;p&gt;下面是一些团队方面的障碍，因为作者是为公司做培训的，所以他有很多这方面的经验。我简单数偶一下，书本在这块的内容比较杂，比较乱，逻辑性不强，我自己对它进行了梳理。&lt;/p&gt;

&lt;p&gt;（沟通培训图）&lt;/p&gt;

&lt;p&gt;1.关系，无论是友谊关系还是权利关系都会导致倾听障碍。解决方案是通过挖掘倾听障碍，用讲故事的方式来释放和解决这些障碍。&lt;/p&gt;

&lt;p&gt;2.情感，情感会成为倾听障碍，无论是高兴还是悲伤还是惊讶都会造成沟通障碍，我们需要将反应搁置一边，先认真倾听，再讲述自己的故事。这种方式可以促成理解和同情，减轻怨恨和分裂。&lt;/p&gt;

&lt;p&gt;3.在专用的时间和空间下，释放倾听障碍遵循的规则&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;让每个人都有机会表达他的障碍是什么，这至关重要。根据时间长短，给每个人分配时间来做这个练习，这样大家都能被倾听。&lt;/li&gt;
  &lt;li&gt;当别人在和团队成员讲述障碍时，不要评论或打断，不要加入自己的想法和看法。&lt;/li&gt;
  &lt;li&gt;要感谢那些刚发现自己倾听障碍的人，然后继续倾听下一个人的发言。&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;释放障碍的方法&quot;&gt;释放障碍的方法&lt;/h2&gt;

&lt;p&gt;（释放障碍技巧图）&lt;/p&gt;

&lt;p&gt;外部的障碍，&lt;/p&gt;

&lt;p&gt;例如，外面有个人在用钻头打钻&lt;/p&gt;

&lt;p&gt;释放，关上窗户，或到另一个房间，或暂停工作休息一下&lt;/p&gt;

&lt;p&gt;身体方面的障碍&lt;/p&gt;

&lt;p&gt;例如，我很饿，听不进去&lt;/p&gt;

&lt;p&gt;释放，吃点能量棒，或休息一下&lt;/p&gt;

&lt;p&gt;内部的障碍&lt;/p&gt;

&lt;p&gt;例如，我很生气，有人盗用了我的点子&lt;/p&gt;

&lt;p&gt;释放，写下来，讲给自己听或可信赖的人，不断调整认知&lt;/p&gt;

&lt;p&gt;心理方面的障碍&lt;/p&gt;

&lt;p&gt;例如，我对老板产生了恐惧心理&lt;/p&gt;

&lt;p&gt;释放，注意自己的判断，把它写下来，以平述的方式跟自己或信赖的人讨论，直到调整认知。&lt;/p&gt;

&lt;p&gt;问问自己，自己为自己出头会如何，会得到什么，会失去什么？我能采取什么行动来释放障碍。&lt;/p&gt;

&lt;p&gt;关系方面的障碍&lt;/p&gt;

&lt;p&gt;例如，我与主管关系较差&lt;/p&gt;

&lt;p&gt;释放，与主管交谈，让他多多了解他人，描述他对团队的影响。&lt;/p&gt;

&lt;p&gt;问问团队成员，是否也这么看待主管。&lt;/p&gt;

&lt;p&gt;问问自己，采取行动会得到或失去什么。&lt;/p&gt;

&lt;p&gt;保持倾听环境处于最佳状态，其好处就像比赛前清理场地一样大又裨益。&lt;/p&gt;

&lt;p&gt;释放障碍后的好处：&lt;/p&gt;

&lt;p&gt;释放障碍的做法可以让人表达真实存在的东西，而不必害怕评判或批评。&lt;/p&gt;

&lt;p&gt;这样可以培养你的创造力，可以让你从一些本来毫无期待的团队成员那里获得想法。&lt;/p&gt;

&lt;p&gt;在最佳倾听环境中，团队可以识别隐藏的事情或问题。&lt;/p&gt;

&lt;p&gt;在识别障碍后，倾听可以帮助团队解决人际冲突。&lt;/p&gt;

&lt;p&gt;故事可以帮助团队成员建立联系和同理心。&lt;/p&gt;

&lt;p&gt;团队成员可以识别和欣赏其他团队成员独特的声音、风格和贡献。&lt;/p&gt;

&lt;p&gt;在讲故事前的准备工作：&lt;/p&gt;

&lt;p&gt;1.确定听众&lt;/p&gt;

&lt;p&gt;2.找出障碍&lt;/p&gt;

&lt;p&gt;3.释放障碍&lt;/p&gt;

&lt;p&gt;4.讲述发生了什么&lt;/p&gt;

&lt;p&gt;最后，想象一下，在一场会议中，你遇到了什么障碍，是否会因为一个高级经理的到来而感到恐惧，在公共场合讲话会让你不自在吗，计划中的不确定因素让你担忧吗？让这些障碍进入你的意识，然后释放它们，从某种意义上说，你是在释放它，让它出现在你的头脑中，而不是压抑它。这样可以为上台做好准备，让我们知道讲述故事时可能会遇到什么。&lt;/p&gt;

&lt;p&gt;识别听众，是一种提高沟通效果的有效工具。&lt;/p&gt;

&lt;p&gt;我们常常根据自己所知道的开始讲述，但这可能并不总是听众想听的。&lt;/p&gt;

&lt;p&gt;考虑听众的感受，意识到他们的兴趣和需要，反过来也可以塑造我们的故事。&lt;/p&gt;

&lt;p&gt;我们可以对倾听进行预测，而倾听反过来又会影响我们的讲述，其相互关系就像容器和导入容器的液体。&lt;/p&gt;

&lt;p&gt;以上说都是在写故事前的内容，关键是障碍，包括识别障碍、释放障碍，它们的方法和经验，下一篇我们来，实实在在的讲讲，如何用写好一个故事，有一些方法和技巧，这些方法和技巧结合中前面我们所说的内容，障碍。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485242&amp;amp;idx=1&amp;amp;sn=127e82b7566e4463304ee972e56a2aa9&amp;amp;chksm=fc22623dcb55eb2b5457161c985457302bc94ad3a9b3311938b54959fab9f79571127bb5501d&amp;amp;token=39636778&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>给女儿写信(二十三) 调整</title>
   <link href="http://www.luzexi.com/2022/04/06/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A123"/>
   <updated>2022-04-06T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/04/06/给女儿的信23</id>
   <content type="html">
&lt;hr /&gt;

&lt;p&gt;导读：喜欢跟孩子们一起，可惜一直在外地工作，因此我给孩子写点信并录成视频，跟孩子聊聊自己的故事以及最近的感受&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;要点&quot;&gt;要点&lt;/h1&gt;

&lt;p&gt;“调整”是一项技能，这个技能在我们生活当中起到很关键的作用，我希望能更多的练习它。&lt;/p&gt;

&lt;h1 id=&quot;结构&quot;&gt;结构&lt;/h1&gt;

&lt;pre&gt;&lt;code&gt;1.环境变化
2.情绪失控
3.调整自己
4.习得技能
&lt;/code&gt;&lt;/pre&gt;

&lt;h1 id=&quot;大纲&quot;&gt;大纲&lt;/h1&gt;

&lt;pre&gt;&lt;code&gt;1.遇到环境变化，自己被困在家里
2.发现自己在家里一直呆着很闷，情绪很糟糕
3.试图调整自己，让自己去适应环境
4.根据环境去调整自己的作息和饮食，以及学习和工作的方式
&lt;/code&gt;&lt;/pre&gt;

&lt;h1 id=&quot;内容&quot;&gt;内容&lt;/h1&gt;

&lt;p&gt;Sharon、Anne爸爸好想你们，再过两个星期爸爸就回来了哟。&lt;/p&gt;

&lt;p&gt;爸爸在深圳最近遇到疫情了，公司通知说，大家都呆在家里办公。&lt;/p&gt;

&lt;p&gt;没办法，为了不让病毒传播的更厉害，爸爸要遵从政府的要求在深圳家里办公了。&lt;/p&gt;

&lt;p&gt;一开始，爸爸以为会很轻松，毕竟在家里会更自由些。&lt;/p&gt;

&lt;p&gt;没想到在家里时间长了，心情很郁闷，因为出不去啊，心情很糟糕。&lt;/p&gt;

&lt;p&gt;而且爸爸发现，呆在家里时间长了，椅子不舒服，坐得我屁股疼。&lt;/p&gt;

&lt;p&gt;爸爸心里很闷，想出去散散心，但门口保安说，不能出去，出去就回不来啦。于是爸爸被赶回了屋里了。&lt;/p&gt;

&lt;p&gt;被关在家里的前1周，爸爸很难受。我想，我不能这样下去啊，情绪会崩溃的。&lt;/p&gt;

&lt;p&gt;于是想着怎么去调整自己，才能让舒服一些。爸爸开始闭上眼睛做深呼吸，吸气，呼气。&lt;/p&gt;

&lt;p&gt;爸爸开始承认环境在变化，只是自己没有变化，同时发现自己试图改变和突破环境，&lt;/p&gt;

&lt;p&gt;比如我想出去走走，但环境不允许。我想回公司工作，因为这里效率比较低，这些都是环境不允许的。&lt;/p&gt;

&lt;p&gt;由于我一直在对抗环境，而环境比我强大很多，这种对抗永远以我的失败告终的，因此我很难受很痛苦。&lt;/p&gt;

&lt;p&gt;好了，想到这里，爸爸明白了，环境改变不了，需要我自己调整自己去适应环境，才能会更加顺利。&lt;/p&gt;

&lt;p&gt;接着开始做出改变，爸爸先用冥想和跟自己说说话的方式来化解情绪调整认知。&lt;/p&gt;

&lt;p&gt;冥想就是闭上眼睛数自己的呼吸，这样能让注意力从琐碎的事情回到自己身上，跟自己说说，则是用跟自己聊聊天的方式，跟自己聊聊最近的心情，最近发生的事，以及自己的想法，这些想法会发生怎样的后果，怎么样做会更好一些，把这些跟自己说明白了，我的情绪也就彻底释放了，认知也调整过来了。&lt;/p&gt;

&lt;p&gt;后来，由于爸爸还是出不去，所以爸爸只能用这两种方式来化解在家里的情绪。&lt;/p&gt;

&lt;p&gt;慢慢的爸爸发现，我的注意力更加集中了，爸爸借这个关在家里的机会，看了很多书，写了很多文章，学习了好多东西，爸爸发现被关在家里也很有充实呢，太好了。&lt;/p&gt;

&lt;p&gt;Sharon和Anne也要像爸爸一样，当遇到困难的时候，积极去调整自己哟，这样就能让事情更加顺利，让我们自己变的更好更厉害，加油。&lt;/p&gt;

&lt;p&gt;爸爸喜欢你们，照顾好妈妈哟。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(五十六) 如何讲好一个故事 - 挖掘与倾听</title>
   <link href="http://www.luzexi.com/2022/04/05/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B056"/>
   <updated>2022-04-05T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/04/05/读书笔记56</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485230&amp;amp;idx=1&amp;amp;sn=5f2594652488e4c6f6cd95e730256b4a&amp;amp;chksm=fc226229cb55eb3fbc59c616a104c08b1cded6aa293f2685bae16b52dcd400151a75552037b3&amp;amp;token=1152981748&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;背景&quot;&gt;背景：&lt;/h1&gt;

&lt;p&gt;最近比较关注写故事的能力，于是看了《如何讲好一个故事》这本书。&lt;/p&gt;

&lt;p&gt;起初原因是我在做备稿演讲时，发现自己写故事的能力较弱，每次写故事绞尽脑汁都很难想出内容来。&lt;/p&gt;

&lt;p&gt;随着时间的推移，我越发觉得故事的用处很多，故事能力很需要实践落地到我的生活和工作当中去。&lt;/p&gt;

&lt;p&gt;通过这次学习，我希望提高自己讲故事的能力，并用于各种场合中，包括演讲、交流、分享、家庭生活、以及工作当中去。&lt;/p&gt;

&lt;h3 id=&quot;最终我希望我能以故事的形式将生活中所有的枯燥都转化为有趣把一切刻板转化为生动让生活变得丰富多彩&quot;&gt;最终我希望我能以故事的形式将生活中所有的“枯燥”都转化为“有趣”，把一切“刻板”转化为“生动”，让生活变得丰富多彩。&lt;/h3&gt;

&lt;h1 id=&quot;概述&quot;&gt;概述：&lt;/h1&gt;

&lt;p&gt;本篇总结《如何讲好一个故事》的上半部分，主要内容是：&lt;/p&gt;

&lt;p&gt;1.挖掘故事&lt;/p&gt;

&lt;p&gt;2.倾听与讲述的关系&lt;/p&gt;

&lt;p&gt;3.识别障碍&lt;/p&gt;

&lt;p&gt;4.释放障碍&lt;/p&gt;

&lt;h1 id=&quot;内容&quot;&gt;内容：&lt;/h1&gt;

&lt;p&gt;听故事和讲故事之间存在着一种相互的关系，当我们使用叙事型故事倾听和讲述法时，可以让倾听者感受到这一切。&lt;/p&gt;

&lt;p&gt;听和讲的关系认知提醒了我，即所有关系之间有着自然而然的相互性，这些相互关系为深化合作、加强沟通铺平了道路。&lt;/p&gt;

&lt;p&gt;很多时候，我们的倾听和交流能力，并没有得到有效的开发。一旦这种能力被激活，可以帮助我们把分析能力和情绪智力结合起来，从工作和生活中得到更多的满足感。&lt;/p&gt;

&lt;p&gt;说到创造力，与之相关的是洞察力，实际上要提高它们最有效的方法是，学会中止判断。当我们不进行评判时，创造力和洞察力也会随之而来（不做价值判断，不批判），移情和理解亦取决于此。&lt;/p&gt;

&lt;h3 id=&quot;作者说我们在讲述故事时只要专注于一件事与听众沟通&quot;&gt;作者说：我们在讲述故事时，只要专注于一件事：与听众沟通。&lt;/h3&gt;

&lt;p&gt;这是整本书的中心思想，具体如何与听众沟通，下面就来讲讲。&lt;/p&gt;

&lt;h1 id=&quot;一挖掘故事&quot;&gt;一、挖掘故事&lt;/h1&gt;

&lt;p&gt;我们大部分人在写故事时，常常有很多认知的误区。&lt;/p&gt;

&lt;p&gt;比如，我写不好故事。&lt;/p&gt;

&lt;p&gt;比如，我的人生很平淡，没有故事好讲。&lt;/p&gt;

&lt;p&gt;比如，我的经历很倒霉，写不出好故事。&lt;/p&gt;

&lt;p&gt;等等等，实际上这些认知都是错误的。&lt;/p&gt;

&lt;p&gt;图片&lt;/p&gt;

&lt;p&gt;（创作故事的4个原则）&lt;/p&gt;

&lt;p&gt;作者有6个原则来说明如何创作故事的基本原则，我认为可以浓缩为4个原则，即：&lt;/p&gt;

&lt;h3 id=&quot;1任何人通过训练都能讲好故事&quot;&gt;1.任何人通过训练都能讲好故事。&lt;/h3&gt;

&lt;h3 id=&quot;2生活本身就是了不起的故事能从中挖掘出许许多多的故事&quot;&gt;2.生活本身就是了不起的故事，能从中挖掘出许许多多的故事。&lt;/h3&gt;

&lt;h3 id=&quot;3通过细致观察会发现我们的故事一直在演变因此能够创造出各种不同的故事&quot;&gt;3.通过细致观察会发现，我们的故事一直在演变，因此能够创造出各种不同的故事。&lt;/h3&gt;

&lt;h3 id=&quot;4倾听和讲述是相辅相成的&quot;&gt;4.倾听和讲述是相辅相成的。&lt;/h3&gt;

&lt;p&gt;知道了这四个原则后，我们可以用叙述法来创造故事，它有三个步骤，挖掘、创作、展示。&lt;/p&gt;

&lt;p&gt;通过挖掘素材，形成故事的初步设想，接着将故事素材设计成典型的故事结构，即勇敢出发遇到危机逐步解决达成胜利平淡归来，最后将这个故事向观众展示。&lt;/p&gt;

&lt;p&gt;叙事法提供了一个循序渐进的框架。&lt;/p&gt;

&lt;h3 id=&quot;这个框架里首先要求我们在讲故事前先问自己两个问题为什么要讲故事为什么要现在讲这两个问题并不会阻碍我们讲故事恰恰相反它能促进我们讲好故事&quot;&gt;这个框架里首先要求我们在讲故事前，先问自己两个问题：“为什么要讲故事？”，“为什么要现在讲？”。这两个问题并不会阻碍我们讲故事，恰恰相反，它能促进我们讲好故事。&lt;/h3&gt;

&lt;p&gt;设想一下，当我们在写故事前，先回答这两个问题，并将回答的内容写下来，会是怎样的情景。你会发现，我们对想要创作的故事在脑海中变得更清晰了，同时在逐步挖掘和创作的路上变得更加自信，更加勇敢。&lt;/p&gt;

&lt;p&gt;用这两个问题进行故事的探索，可以使得我们更深入的认识所要创作的故事，同时它会带来方向性和紧迫性，从而让故事拥有活力。你对这两个问题的回答越准确、越深入，讲故事的专注度就越高。&lt;/p&gt;

&lt;p&gt;图片&lt;/p&gt;

&lt;p&gt;（挖掘故事的关键）&lt;/p&gt;

&lt;h3 id=&quot;此外在讲故事时如果我们以发生了什么方法作为基础来讲故事会有更好的呈现&quot;&gt;此外，在讲故事时，如果我们以“发生了什么”方法作为基础来讲故事，会有更好的呈现。&lt;/h3&gt;

&lt;p&gt;我们很多时候会一直以为自己是在讲给别人听，用这种方式来挖掘故事通常比较困难。如果讲故事的人没有意识到听众的存在而陷入了自己的语言泡沫，我们也就无法真正倾听故事。&lt;/p&gt;

&lt;h3 id=&quot;实际上我们在挖掘别人的故事时会问发生了什么这个方法非常管用它没有带着任何评判并且带着好奇心寻求完整的情景再现这个方法也同样可以用于自己身上来挖掘我们自己的故事&quot;&gt;实际上我们在挖掘别人的故事时，会问“发生了什么”，这个方法非常管用，它没有带着任何评判并且带着好奇心寻求完整的情景再现，这个方法也同样可以用于自己身上来挖掘我们自己的故事。&lt;/h3&gt;

&lt;p&gt;至于不用“为什么”而是“发生了什么”来挖掘故事的原因，由于这里涉及到了讲述和倾听的关系，放到了下一节里讲解。这里只简单提一句，倾听的重要性绝不亚于讲述。&lt;/p&gt;

&lt;p&gt;整本书中，作者都用“发生了什么”来让，与他对话的人，不断讲述出故事的每个细节，因此我们也可以用同样的手法，用在我们创作故事的时候。&lt;/p&gt;

&lt;h1 id=&quot;二倾听与讲述的关系&quot;&gt;二、倾听与讲述的关系&lt;/h1&gt;

&lt;p&gt;很多人可能并没认知到，倾听和讲述是相互影响的，即讲述影响倾听，倾听也影响讲述&lt;/p&gt;

&lt;p&gt;不过作者并没有解释倾听在书中含义，因此这里容易引起疑问。&lt;/p&gt;

&lt;h3 id=&quot;倾听有两种含义第一种是我作为讲述者听到或感受到的包括周围环境内心心理以及观众等另一种是观众听到或感受到的也同样包括周围环境内心心理以及讲述者的动作语音语调姿势等&quot;&gt;倾听，有两种含义，第一种是‘我’作为讲述者听到或感受到的，包括周围环境，内心心理，以及观众等。另一种是观众听到或感受到的，也同样包括周围环境、内心心理，以及讲述者的动作、语音语调、姿势等。&lt;/h3&gt;

&lt;p&gt;简单来说，倾听实际上是感受，当感受比较复杂或比较糟糕时，就会影响讲述者，以及影响讲述者传达给观众的内容和效果。因此我们在讲述时，尽量不增加倾听的障碍，其中的关键是不评判。&lt;/p&gt;

&lt;p&gt;图片&lt;/p&gt;

&lt;p&gt;(倾听的关键-图)&lt;/p&gt;

&lt;h3 id=&quot;理想的倾听是建立在非判断的基础上的因为就交流的纯洁性而言判断只会搅浑水&quot;&gt;理想的倾听是建立在非判断的基础上的，因为就交流的纯洁性而言，判断只会搅浑水。&lt;/h3&gt;

&lt;p&gt;在叙事法中，不加判断被认为是一种技能和技巧，可以通过练习获得。&lt;/p&gt;

&lt;p&gt;当然不只是讲述者需要不判断，倾听者也需要不判断，但这非常困难，我们能做的只有控制自己，让讲述的内容是在不判断的基础上的。&lt;/p&gt;

&lt;p&gt;我们可以把倾听比作容器，把故事比作倒进容器中的液体。这种对倾听和讲述之间互相关系的认识就像透视镜，可以用来审视任何组织的沟通环境。&lt;/p&gt;

&lt;p&gt;在大型科技公司工作的人往往思想单纯，但雄心勃勃，求胜心切。尽管这些品质与公司企业文化一致，但不符合员工互相支持、彼此成就，在团队中建立更牢固纽带的愿望。&lt;/p&gt;

&lt;p&gt;事实上，一旦事情出了差错，就会出现一种互相指责的倾向，这会导致冲突与怨恨，成员之间的互相理解会崩溃。&lt;/p&gt;

&lt;h3 id=&quot;出现这种问题的关键是人们忽视了协作和沟通依赖于倾听的事实&quot;&gt;出现这种问题的关键是人们忽视了协作和沟通依赖于倾听的事实。&lt;/h3&gt;

&lt;p&gt;很多时候我们的互动所带给我们的感受，直接阻碍了我们更好的互动，究其原因阻碍互动的就是倾听障碍。&lt;/p&gt;

&lt;p&gt;因此想要有一次成功的交流，必须先识别倾听障碍，再释放倾听障碍。先识别听众，然后识别障碍，最后通过释放障碍来分析如何沟通，这是很有裨益的，让你可以继续前进。&lt;/p&gt;

&lt;h3 id=&quot;回到创作故事上当我们认识到倾听和讲述相互影响的关系时我们就能更好的创作故事也就是说为了让我们的故事内容和观点传达的更加有效首要原则是在故事内不做判断并且在讲述前先识别倾听障碍再释放倾听障碍最终让观众的有一个纯粹的倾听环境&quot;&gt;回到创作故事上，当我们认识到倾听和讲述相互影响的关系时，我们就能更好的创作故事。也就是说，为了让我们的故事内容和观点传达的更加有效，首要原则是在故事内不做判断，并且在讲述前先识别倾听障碍，再释放倾听障碍，最终让观众的有一个纯粹的倾听环境。&lt;/h3&gt;

&lt;p&gt;–&lt;/p&gt;

&lt;p&gt;由于内容较多，识别倾听障碍和释放倾听障碍，放在下一篇。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485230&amp;amp;idx=1&amp;amp;sn=5f2594652488e4c6f6cd95e730256b4a&amp;amp;chksm=fc226229cb55eb3fbc59c616a104c08b1cded6aa293f2685bae16b52dcd400151a75552037b3&amp;amp;token=1152981748&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>读书笔记(五十五) 如何做好PPT - PPT内容策略</title>
   <link href="http://www.luzexi.com/2022/03/09/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B055"/>
   <updated>2022-03-09T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/03/09/读书笔记55</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485214&amp;amp;idx=1&amp;amp;sn=26d7841d6ddbb23925b2be5c7d914256&amp;amp;chksm=fc226219cb55eb0f168abb24ffeeaa1f7d899ff43fd2a308de29a67bb20925644d244c07d174&amp;amp;token=537319754&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;背景&quot;&gt;背景：&lt;/h1&gt;

&lt;p&gt;近段时间在练习演讲，一开始只是想练习下自己的口头表达能力和沟通能力，但渐渐的我发现我喜欢上了演讲，它不仅可以锻炼我的表达能力，还能锻炼我对知识的理解和总结能力。&lt;/p&gt;

&lt;p&gt;这段时间里，我发现演讲的其中关键用途是汇报和分享，这让我欣喜若狂。
同时苦于自己不太会写PPT，屡次上台屡次搞砸，这激起了我对PPT学习的渴望。&lt;/p&gt;

&lt;p&gt;实际上观众除了听到还要看到，这种视觉呈现分为两种，一种是身体和语音演绎出来，另一种就是PPT呈现出来的，PPT如同台上的演绎，它让演讲内容呈现的更优雅更人性化，在听觉和视觉双重冲击之下发挥演讲的最大效果。&lt;/p&gt;

&lt;p&gt;我找了四本同类书籍，《PPT设计的艺术》、《你就是干不过做PPT的》、《精进PPT》、《PPT设计思维、技术与实践》进行主题阅读，看完后根据自己的实践经验和对书本知识的理解做回顾和总结。&lt;/p&gt;

&lt;h1 id=&quot;概述&quot;&gt;概述：&lt;/h1&gt;

&lt;p&gt;本篇基于《你就是干不过做PPT的》、《精进PPT》《PPT设计思维、技术与实现》三本书，结合我的经验总结。&lt;/p&gt;

&lt;p&gt;前者对于PPT的策略性会更多一些，后者两者更多是制图上的技巧，&lt;/p&gt;

&lt;p&gt;说实话我对制图技巧不太感兴趣，其方法在前一篇总结过一些，这里不再重复。&lt;/p&gt;

&lt;p&gt;看书前给自己提了几个问题以便在文中解答：&lt;/p&gt;

&lt;p&gt;1.优秀的PPT的核心是什么？&lt;/p&gt;

&lt;p&gt;2.它有哪几个特点？&lt;/p&gt;

&lt;p&gt;3.我在制作过程中需要注意哪些要点？&lt;/p&gt;

&lt;p&gt;4.有哪些技巧我可以使用？&lt;/p&gt;

&lt;h1 id=&quot;目录&quot;&gt;目录：&lt;/h1&gt;

&lt;p&gt;1.如何让观众容易理解&lt;/p&gt;

&lt;p&gt;2.如何抓住观众的注意力&lt;/p&gt;

&lt;p&gt;3.如何增强说服力&lt;/p&gt;

&lt;h1 id=&quot;内容&quot;&gt;内容：&lt;/h1&gt;

&lt;p&gt;前文主要讲了PPT设计，它主要有两个关键&lt;/p&gt;

&lt;p&gt;1.传递一个核心观念&lt;/p&gt;

&lt;p&gt;2.围绕核心观念打造呈现效果&lt;/p&gt;

&lt;p&gt;本文与前文相同点都是，传递一个核心观念。不同的是，前文讲了很多如何用视觉冲击去打造呈现效果，而本文主要讲的如何用策略去打造呈现效果。&lt;/p&gt;

&lt;p&gt;书中多次提到一个词，‘一次成功’，这个词语很吸引我，&lt;/p&gt;

&lt;p&gt;因为我每次做PPT都在想一个问题：我如何才能让这个PPT‘成功’&lt;/p&gt;

&lt;p&gt;具体的技巧文章中会一一介绍，我将这些技巧融入到生活中去，让自己在生活中练习，在练习中生活。&lt;/p&gt;

&lt;p&gt;其实整本书本身的排序和制作就很好的诠释了如何做资料这件事，&lt;/p&gt;

&lt;h3 id=&quot;开头就有一句话让我恍然大悟一般看一个人制作的资料就能大体知道他的工作能力是优秀还是平庸&quot;&gt;开头就有一句话让我恍然大悟：一般看一个人制作的资料就能大体知道他的工作能力是优秀还是平庸。&lt;/h3&gt;

&lt;p&gt;这句话打开了我对整本书的浓厚的兴趣。&lt;/p&gt;

&lt;p&gt;原来一个人的工作能力，可以如实地反映在他所制作的资料当中。&lt;/p&gt;

&lt;p&gt;经过这几年的历练，我觉得这句话说的毫不夸张。&lt;/p&gt;

&lt;p&gt;下面文章中提到的‘PPT’字样实际上完全可以替换为‘资料’二字同样适用。&lt;/p&gt;

&lt;p&gt;先搬出PPT制作流程和打磨流程，以便大家明白这篇文章能帮助你到什么程度：
1.确定主题
2.明确核心观点，观点需用简洁的语句表达
3.为内容写大致的结构，初步思考下表达顺序
4.为每页PPT制作具体的内容，首版内容可以不用太精细
5.整理表达顺序，排序下把重要的提上来先说
6.优化每页内容，包括例子、数据、图片、图表、图标等
7.优化视觉效果，调整排版，精简内容，更多留白，调整颜色数量和饱和度，适当加入动画
8.尝试自己对着PPT阐述一次，并记录下自己阐述的内容成为备稿
9.将备稿内容文字优化并持续练习
10.练习时将稿件内容映射到PPT的图像中
11.持续打磨，练习，调整，直到上台&lt;/p&gt;

&lt;h1 id=&quot;特点&quot;&gt;特点&lt;/h1&gt;

&lt;p&gt;书中说优秀的‘资料’有6个共同的特点：
1.容易理解的表达顺序
2.第一页抓住读者的心，最后一页促成决策
3.用细节增强说服力
4.短小精悍一目了然
5.图表图解引起重视
6.突出重点化繁为简&lt;/p&gt;

&lt;p&gt;（三个核心图）&lt;/p&gt;

&lt;p&gt;我觉得这6点说的不错，同时仍然觉得有点多，人们很难记得住并运用，&lt;/p&gt;

&lt;p&gt;因此我结合四本书对PPT内容策略提出了3个核心：&lt;/p&gt;

&lt;h3 id=&quot;1观众容易理解包括顺序和结构&quot;&gt;1.观众容易理解，包括顺序和结构&lt;/h3&gt;
&lt;h3 id=&quot;2抓住观众的注意力尤其是每一部分的开头和收尾&quot;&gt;2.抓住观众的注意力，尤其是每一部分的开头和收尾&lt;/h3&gt;
&lt;h3 id=&quot;3增强说服力用数字和细节去增强说服力&quot;&gt;3.增强说服力，用数字和细节去增强说服力&lt;/h3&gt;

&lt;p&gt;下面我就围绕这3个核心，结合个人实战和经验，将这四本书合起来做一个综合性的陈述。&lt;/p&gt;

&lt;h1 id=&quot;怎么才能让观众容易理解&quot;&gt;怎么才能让观众容易理解？&lt;/h1&gt;

&lt;p&gt;很多人都按自己喜欢的顺序表达，自己喜欢讲到哪里就是哪里，自己是爽了，别人完全听不懂。&lt;/p&gt;

&lt;p&gt;实际上对于一份听不懂的PPT报告，没人会想做出行动或给予好的决策。&lt;/p&gt;

&lt;p&gt;曾经的我就是这样一个人，喜欢按自己的方式去表达，去做PPT，结果，谁都听不懂，没有任何的结果。&lt;/p&gt;

&lt;p&gt;因此在做PPT时，我们首先要考虑的事情是，如何让对方能够容易理解我们所要表达的内容。&lt;/p&gt;

&lt;p&gt;原理是一些有关倾听和讲述的知识挺多的可以在《如何讲好一个故事里》学到，简单来说，我们一般在听别人讲话时，通常都不愿意听一大堆解释和判断，反过来也是一样，我们在制作PPT的时候，&lt;/p&gt;

&lt;h3 id=&quot;如果能先给结论或要点对方听和看起来就会舒服很多&quot;&gt;如果能先给结论或要点，对方听和看起来就会舒服很多。&lt;/h3&gt;

&lt;p&gt;实际上，想让对方听懂我们所表达的内容，技巧有很多，其中最关键的是表达结构。&lt;/p&gt;

&lt;h2 id=&quot;1表达结构&quot;&gt;1.表达结构&lt;/h2&gt;

&lt;p&gt;金字塔结构常用于降低听众的理解难度，结论先行，归类分组，层层递进，最后强化结论。&lt;/p&gt;

&lt;p&gt;（结论先行图）&lt;/p&gt;

&lt;p&gt;另外表达内容的分类方法有5种，包括二分法（A、B分类）、过程法（时间分类、流程分类）、要素法（上下分类、内外分类、整体到局部分类）、公式法（成功 = 思维方式 x 热情 x 能力）、矩阵法（四象限分类，重要的、不重要的、紧急的、不紧急的）。这些分类方法具体不细说了，大家也略有所闻。&lt;/p&gt;

&lt;p&gt;还有些分析方法，PDCA、SWOT等，不再列举，&lt;/p&gt;

&lt;h3 id=&quot;他们核心都是结构表达即有次序有分类有总结&quot;&gt;他们核心都是：结构表达，即有次序、有分类、有总结。&lt;/h3&gt;

&lt;p&gt;同时也可以使用‘问题与方案’的方式，先描述问题再提出方案的方式来制作PPT。
在描述问题需要些技巧，否则很容易变得单调乏味，将在后面的抓住注意力内容里说明。&lt;/p&gt;

&lt;h2 id=&quot;2需要注意的细节技巧&quot;&gt;2.需要注意的细节技巧：&lt;/h2&gt;

&lt;p&gt;结构表达时，观点不能超过3个&lt;/p&gt;

&lt;p&gt;对比方案，能让方案本身更清晰&lt;/p&gt;

&lt;p&gt;尽量不使用晦涩难懂的说法，因为只有能力不足的人才直接使用专业术语&lt;/p&gt;

&lt;p&gt;最大限度的精简词汇，删除重复内容、修饰词、介词&lt;/p&gt;

&lt;p&gt;留白，一页内容尽量少，只表达重要内容&lt;/p&gt;

&lt;p&gt;用突出（上色和圈线），排序，连接（箭头和连线）的方式引导观众目光轨迹&lt;/p&gt;

&lt;p&gt;用树状图，能将复杂的结构拆解的更容易理解&lt;/p&gt;

&lt;p&gt;当需要在多个选项中选一个时，矩阵图可以将其优点和缺点表现出来&lt;/p&gt;

&lt;p&gt;明确目的，让大家都知道当前展示的目的，然后以此为前提，聆听你的讲解&lt;/p&gt;

&lt;p&gt;鲜明的标题，让大家知道该页要讲的内容&lt;/p&gt;

&lt;p&gt;其实以上所有的结构和技巧都是为了：突出重点，化繁为简。&lt;/p&gt;

&lt;p&gt;让观众的注意力从纷繁复杂的内容中解脱出来，更聚焦于我们想要表达的核心观点上。&lt;/p&gt;

&lt;h2 id=&quot;3用总结页帮助观众理清内容&quot;&gt;3.用总结页帮助观众理清内容&lt;/h2&gt;

&lt;p&gt;很多长文PPT都需要总结页，因为总结页可以强调结论，同时也帮助观众梳理内容。&lt;/p&gt;

&lt;p&gt;总结页在制作时也同样可以帮助自己梳理结构、理清思路。&lt;/p&gt;

&lt;p&gt;注意：先把PPT（资料）的结构确定好之后，再集中精力去思考内容。&lt;/p&gt;

&lt;h1 id=&quot;如何抓住对方的注意力&quot;&gt;如何抓住对方的注意力?&lt;/h1&gt;

&lt;p&gt;一份优秀的PPT，开头一定会有吸引人的内容。&lt;/p&gt;

&lt;p&gt;首先换位思考下，通常我们对于自己感兴趣的话题，以及与自己相关的语言会比较感兴趣。&lt;/p&gt;

&lt;p&gt;反过来也是一样，当我们成为讲述者时，要考虑对方感兴趣的事情，以及与对方有关的话题，才能引起对方的注意。有时候需要我们在对方和我之间找到一个共同点或共同利益点，让彼此能够链接起来，比如我们的目标和对方的想法相结合就能碰撞出精彩的火花。&lt;/p&gt;

&lt;p&gt;我们可以用三个角度去审视我们的PPT：&lt;/p&gt;

&lt;p&gt;1.对方视角，对方做决策时重视什么？
2.自身视角，有没有充分体现自己的见解？
3.数字视角，有没有具体的数字和方案？&lt;/p&gt;

&lt;h2 id=&quot;一从决策者的角度想问题&quot;&gt;一，从决策者的角度想问题&lt;/h2&gt;

&lt;p&gt;由于最终是由决策者来决定是否使用我们的方案，因此我们必须以终为始，思考决策者想要什么?&lt;/p&gt;

&lt;p&gt;这需要我们观察对方重视的是什么，这样可以站在对方的角度来思考我们制作的资料。&lt;/p&gt;

&lt;p&gt;例如，他在阅读汇报资料时，想知道的是‘结果’还是‘解决方案’，还是其他的比如某类知识。&lt;/p&gt;

&lt;h3 id=&quot;实际上善于制作优秀ppt的人在日常工作中就会仔细观察其他人究竟重视什么&quot;&gt;实际上，善于制作优秀PPT的人，在日常工作中就会仔细观察其他人究竟重视什么？&lt;/h3&gt;

&lt;p&gt;只有站在决策者的角度想问题，我们才能提出更好的抓住决策者注意力的资料。&lt;/p&gt;

&lt;p&gt;我们可以从决策者重视的东西，反推出我应该如何与我们的情况相结合。&lt;/p&gt;

&lt;p&gt;例如，我们制定的目标是否与决策者想要的一致，我们遇到的问题是否与决策者的想要的有相关性，我们的计划是否与决策者重视的有密切联系。&lt;/p&gt;

&lt;h2 id=&quot;二自己独特的见解&quot;&gt;二、自己独特的见解&lt;/h2&gt;

&lt;p&gt;除了考虑对方重视什么，还要考虑自己的独特见解。&lt;/p&gt;

&lt;h3 id=&quot;很多时候决策者不单单要选出好方案更要选出能胜任的人&quot;&gt;很多时候，决策者不单单要选出好方案，更要选出能胜任的人。&lt;/h3&gt;

&lt;p&gt;因此要展现出自己的独特见解以及热情，让决策者的决策方向偏向于我们。&lt;/p&gt;

&lt;p&gt;（独特的见解示例图）&lt;/p&gt;

&lt;p&gt;在PPT中加入自己的思考并讲述独特之处：&lt;/p&gt;

&lt;p&gt;1.我的方案与别人相比有哪些独特之处，
2.遇到问题时我有怎样的独特方式，
3.计划中是如何根据自己独特理解来调整的&lt;/p&gt;

&lt;p&gt;总之，论述自己独特的见解，可以让人眼前一亮的感觉，这会让决策者相信我们深入思考了这些问题，并拥有自己的理解。&lt;/p&gt;

&lt;h2 id=&quot;三放大问题&quot;&gt;三、放大问题&lt;/h2&gt;

&lt;p&gt;通过放大问题，可以让决策者产生危机感，从而吸引对方的注意力。&lt;/p&gt;

&lt;p&gt;当决策者认识到该问题的重要性，并且产生危机意识时，就表明他已经准备好继续聆听下一步的‘提出解决方案’了。&lt;/p&gt;

&lt;p&gt;虽然放大问题是抓住决策者注意力的好方法，但也要注意我们找出的问题必须是真正需要解决的，而不是凭空捏造的或者虚假的。&lt;/p&gt;

&lt;p&gt;（放大问题的核心图）&lt;/p&gt;

&lt;h3 id=&quot;放大问题的核心是找出真正需要解决的问题&quot;&gt;放大问题的核心是，找出真正需要解决的问题。&lt;/h3&gt;

&lt;p&gt;为了能找出，真正需要解决的问题，就需要我们提出一个好问题&lt;/p&gt;

&lt;p&gt;好的问题提出具备3个要素&lt;/p&gt;

&lt;p&gt;目的，问题点，根本原因&lt;/p&gt;

&lt;p&gt;问题的目标是什么？问题点在哪？根本原因是什么？&lt;/p&gt;

&lt;p&gt;目标要符合决策者想要的，问题点要尽可能找到要害点，根本原因要深入挖掘。&lt;/p&gt;

&lt;p&gt;放大问题的三个要素：&lt;/p&gt;

&lt;h3 id=&quot;1通过目标里的内容让决策者产生兴趣&quot;&gt;1.通过目标里的内容让决策者产生兴趣&lt;/h3&gt;
&lt;h3 id=&quot;2通过问题点指出问题的要害&quot;&gt;2.通过问题点指出问题的要害&lt;/h3&gt;
&lt;h3 id=&quot;3通过根本原因找出问题背后的真正原因&quot;&gt;3.通过根本原因找出问题背后的真正原因&lt;/h3&gt;

&lt;p&gt;目标很重要，这能让决策者知道，最终的结果会如何，会产生怎样的效果。&lt;/p&gt;

&lt;p&gt;如果首先在资料开头目的中阐述一点提案背景，再通过达成目标来说明方案被采纳后会带来什么样的效果，这样的效果更佳，观众的注意力会一点点的被吸引直到全部被吸住。&lt;/p&gt;

&lt;p&gt;一旦观众们知道结果和最终效果，他们就会对我们的PPT产生浓厚的兴趣。&lt;/p&gt;

&lt;p&gt;接着通过问题点指出要害，方法可以对比应有（或理想）状态和当前的现状来形成差距，从而指出要害。&lt;/p&gt;

&lt;p&gt;接着找出根本原因，可以从这8个方面来思考：规则、效率、技术、人和时间、资金、检查、交流、职责。&lt;/p&gt;

&lt;p&gt;同时，我们要多次深入思考，最好先列出已知原因，再逐个思考，接着根据原因继续深入问自己‘为什么’。通过问自己三层‘为什么’来深入挖掘出问题背后的真正的原因。&lt;/p&gt;

&lt;h1 id=&quot;如何增强说服力&quot;&gt;如何增强说服力&lt;/h1&gt;

&lt;p&gt;增强说服力很重要，它是我们说服观众的有效手段。&lt;/p&gt;

&lt;p&gt;这里介绍几种方式，大家在平时使用制作PPT时可以混合使用。&lt;/p&gt;

&lt;h3 id=&quot;增强说服力的主要核心是细节&quot;&gt;增强说服力的主要核心是，细节。&lt;/h3&gt;

&lt;p&gt;如何用细节？下面介绍有几种方法。&lt;/p&gt;

&lt;p&gt;（用细节增强说服力的几种方式图）&lt;/p&gt;

&lt;h2 id=&quot;1用具体数字增加说服力&quot;&gt;1.用具体数字，增加说服力&lt;/h2&gt;

&lt;p&gt;以数字切入，会让人感觉，你把握了整体情况，确实仔细进行过调查。&lt;/p&gt;

&lt;p&gt;数字可以大大增加说服力，同时要注意展示的数字必须能让对方感觉到自己是经过仔细确认的。&lt;/p&gt;

&lt;p&gt;如何让观众感觉数字是经过仔细确认的呢？&lt;/p&gt;

&lt;p&gt;1.数字经过严格的筛选，能够作为具体的理论和依据来支持你观点的信息。
2.对数据的理解有自己的思考和表达。
3.有多个数字进行关联。&lt;/p&gt;

&lt;p&gt;有说服力的数据包括：现状数据，方案比较数据，关键数据，目标成果数据，因果关系数据&lt;/p&gt;

&lt;p&gt;我们在列举数字时，要先找到关键点，即对事实关系进行慎密的调查&lt;/p&gt;

&lt;p&gt;接着，用定量数字去描述它，让人感觉你仔细调查过，&lt;/p&gt;

&lt;p&gt;再用，定性数据让数据更加立体化。&lt;/p&gt;

&lt;h2 id=&quot;2用对比增强说服力&quot;&gt;2.用对比，增强说服力&lt;/h2&gt;

&lt;p&gt;通过对比，能够让观众更容易理解，我们想要表达的内容。同时帮助我们找出最佳方案，并向决策者们传递，我们的方案是最佳的。&lt;/p&gt;

&lt;p&gt;对比方式：&lt;/p&gt;

&lt;p&gt;可以通过列举应有状态和现有状态的对比差异。&lt;/p&gt;

&lt;p&gt;也可以先全列出方案，再进行方案混合，再做出差异比较。&lt;/p&gt;

&lt;p&gt;通过对比，不但可以抓住观众的注意力，同时让观众更容易理解我们所要表达的内容。&lt;/p&gt;

&lt;h2 id=&quot;3举成功的例子增强说服力&quot;&gt;3.举成功的例子，增强说服力&lt;/h2&gt;

&lt;p&gt;特别是当成功的例子是一个事实时，会大大增强说服力。&lt;/p&gt;

&lt;h3 id=&quot;实际上对于一个提案来说在决策者中尝鲜的人是极少的大多数人是跟进者而且他们只有看到成功的例子才会愿意跟进通过举成功的例子来说服跟进者就能说服大部分人&quot;&gt;实际上，对于一个提案来说，在决策者中尝鲜的人是极少的，大多数人是跟进者，而且他们只有看到成功的例子才会愿意跟进。通过举成功的例子来说服跟进者，就能说服大部分人。&lt;/h3&gt;

&lt;p&gt;这里有3个关键点：
1.符合常理的内容
2.具体的事实
3.成功的例子&lt;/p&gt;

&lt;p&gt;所谓说服力，指的就是道理+具体的事例。符合常理的内容，再加上具体的事例，能够极大提高资料的说服力。&lt;/p&gt;

&lt;h2 id=&quot;4列举问题与方案增强说服力&quot;&gt;4.列举问题与方案，增强说服力&lt;/h2&gt;

&lt;p&gt;这种方式比较常用，其关键是要抛出能吸引决策者的问题，也就是前面说的放大问题。&lt;/p&gt;

&lt;p&gt;用抛出问题和陈述方案的方式会引起决策者对问题和答案的兴趣，&lt;/p&gt;

&lt;p&gt;在讲述时可以用1、2、3按步讲解，先展现结构和顺序，再展现内容，让表达更容易接受。&lt;/p&gt;

&lt;h2 id=&quot;5明确目标拆解目标制定计划增强说服力&quot;&gt;5.明确目标拆解目标制定计划，增强说服力&lt;/h2&gt;

&lt;p&gt;所谓目标，就是用一个具体的数值表现出最终的效果。&lt;/p&gt;

&lt;p&gt;我们在制定目标时，目标要是一个可量化的具体数字。&lt;/p&gt;

&lt;p&gt;接着，拆解目标，把大目标拆解成小目标，因为这样更容易实现，也更容易制定计划。&lt;/p&gt;

&lt;p&gt;通过将目标拆解，决策者也能做出更加具体的判断。如果决策者对每个拆解后的小目标都认可，那么方案通过的可能性将极大提升。&lt;/p&gt;

&lt;h3 id=&quot;最后制定计划通过制定详细的计划能让决策者了解方案的可行性和可靠性最终说服对方行动&quot;&gt;最后制定计划，通过制定详细的计划，能让决策者了解方案的可行性和可靠性，最终说服对方行动。&lt;/h3&gt;

&lt;h2 id=&quot;6其他增强说服力&quot;&gt;6.其他，增强说服力&lt;/h2&gt;

&lt;p&gt;用真实图片增强说服力：可以用真实的照片，加大说服力效果，特别是当现场照片，当两张照片进行差异比较时，能提升真实感，加大反差效果。&lt;/p&gt;

&lt;p&gt;用图表增强说服力：&lt;/p&gt;

&lt;p&gt;当具体数字放入图表中更有立体感，包括：&lt;/p&gt;

&lt;p&gt;饼状图，适合表示明细，&lt;/p&gt;

&lt;p&gt;柱状图，适合表示比较差异（项目较多时可以选择横向），&lt;/p&gt;

&lt;p&gt;折线图，适合表示变化。&lt;/p&gt;

&lt;p&gt;制作图表时应当尽量简化内容，突出重点&lt;/p&gt;

&lt;p&gt;（饼状图，柱状图，折线图示例图）&lt;/p&gt;

&lt;p&gt;除了图，还有表。好的表格，最大的特点是，只表达重点信息，这需要我们先理清思路，思考表达的重点。&lt;/p&gt;

&lt;p&gt;用说服策略，增强说服力：&lt;/p&gt;

&lt;p&gt;先说服部分方案，再逐步说服对方&lt;/p&gt;

&lt;p&gt;先提出部分提案，再提出整体方案，或先提出预案，再提出详细方案&lt;/p&gt;

&lt;h3 id=&quot;注意ppt上切记不要太细只要大致流程便可&quot;&gt;注意：PPT上切记不要太细，只要大致流程便可&lt;/h3&gt;

&lt;h2 id=&quot;其他制作小细节&quot;&gt;其他制作小细节：&lt;/h2&gt;

&lt;p&gt;1.通过图的并、连来表达各信息之间的关系
2.流程图适合表达工作流程，关键是不要过于详细，可以划分模块和步骤
3.各种图形代表意义，方形-&amp;gt;具体事实，圆形-&amp;gt;概念和关键词，圆角形-&amp;gt;抽象概念，三角形-&amp;gt;上下关系通过简单的图形可以增加美观，统一风格，增加冲击感
4.字体的选择要舒适，易于阅读，市面上更有几种常用的字体组合。&lt;/p&gt;

&lt;h2 id=&quot;参考资料&quot;&gt;参考资料：&lt;/h2&gt;

&lt;p&gt;《PPT设计的艺术》  作者：林屹&lt;/p&gt;

&lt;p&gt;《你就是干不过做PPT的》 译者：朱悦玮&lt;/p&gt;

&lt;p&gt;《PPT设计思维、技术与实践》 作者：周庆麟等&lt;/p&gt;

&lt;p&gt;《精进PPT-成为PPT高手》 作者：周庆麟、胡子平&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>游戏引擎架构#4 低阶渲染器（5）</title>
   <link href="http://www.luzexi.com/2022/02/20/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B054"/>
   <updated>2022-02-20T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/02/20/读书笔记54</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485174&amp;amp;idx=1&amp;amp;sn=b8321c77f937537ce74fed145268f6c2&amp;amp;chksm=fc2263f1cb55eae7d64f1343f6a8298e520cad81b574283dbd543970d59d60fb29716817d9aa&amp;amp;token=537319754&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;背景&quot;&gt;背景：&lt;/h1&gt;

&lt;p&gt;作为游戏开发从业者，从业务到语言到框架到引擎，积累了一些知识和经验，特别是在看了几遍《游戏引擎架构》后对引擎架构的理解又深入了些。&lt;/p&gt;

&lt;p&gt;近段时间有对引擎剖析的想法，正好借这书本对游戏引擎架构做一个完整分析。&lt;/p&gt;

&lt;p&gt;此书用简明、清楚的方式覆盖了游戏引擎架构的庞大领域，巧妙地平衡了广度与深度，并且提供了足够的细节。&lt;/p&gt;

&lt;p&gt;借助《游戏引擎架构》这本书、结合引擎源码和自己的经验，深入分析游戏引擎的历史、架构、模块，最后通过实践简单引擎开发来完成对引擎知识的掌握。&lt;/p&gt;

&lt;p&gt;游戏引擎知识面深而广，所以对这系列的文章书编写范围做个保护，即不对细节进行过多的阐述，重点剖析的是架构、流程以及模块的运作原理。&lt;/p&gt;

&lt;p&gt;同时《游戏引擎架构》中部分知识太过陈旧的部分，会重新深挖后总结出自己的观点。&lt;/p&gt;

&lt;h1 id=&quot;概述&quot;&gt;概述：&lt;/h1&gt;

&lt;p&gt;本系列文章对引擎中的重要的模块和库进行详细的分析，我挑选了十五个库和模块来分析：&lt;/p&gt;

&lt;p&gt;1.时间库
2.自定义容器库
3.字符串散列库
4.内存管理框架
5.RTTI与反射模块
6.图形计算库
7.资产管理模块
8.低阶渲染器
9.剔除与合批模块
10.动画模块
11.物理模块
12.UI底层框架
13.性能剖析器的核心部分
14.脚本系统
15.视觉效果模块&lt;/p&gt;

&lt;p&gt;本篇内容为列表中的第8个部分的第5、6节。&lt;/p&gt;

&lt;h1 id=&quot;正文&quot;&gt;正文：&lt;/h1&gt;

&lt;p&gt;简单回顾下前文&lt;/p&gt;

&lt;p&gt;前文我们聊了下显卡在计算机硬件主板中的位置与结构，知道了CPU、GPU的通信介质，并简单介绍了手机上的主板结构。本篇开头对上一篇做一些内容补充，PC和手机的不同硬件组织，以及CPU与其他芯片的通信过程。&lt;/p&gt;

&lt;p&gt;下面我们开始这篇内容&lt;/p&gt;

&lt;p&gt;本次内容会围绕GPU来写，从硬件架构到软件驱动再到引擎架构，目标是帮大家理解GPU硬件的运作原理，理解图形接口的架构，理解引擎低阶渲染器的架构。&lt;/p&gt;

&lt;p&gt;目录：&lt;/p&gt;

&lt;p&gt;主板结构中的显卡&lt;/p&gt;

&lt;p&gt;GPU功能发展史&lt;/p&gt;

&lt;p&gt;GPU与CPU的差异&lt;/p&gt;

&lt;p&gt;GPU硬件特点&lt;/p&gt;

&lt;p&gt;图形驱动程序架构&lt;/p&gt;

&lt;p&gt;引擎低阶渲染架构&lt;/p&gt;

&lt;p&gt;前面我们说了关于GPU硬件上的原理和运作机制，下面我们来讲一讲软件上的架构，尤其是渲染架构。&lt;/p&gt;

&lt;p&gt;指令和数据从CPU到GPU最终到帧缓冲的这个过程中，有三种类型的架构，第一种是图形驱动程序的架构，第二种是引擎上的低阶渲染器架构，第三种是GPU上的软件架构。&lt;/p&gt;

&lt;p&gt;这三种架构在互相配合，同时也是三个模块互相调用的过程。这也是为什么前面要提到这么多CPU与GPU硬件交互过程的原因。&lt;/p&gt;

&lt;p&gt;为了更好的了解这三者，我又研读了一遍《OpenGL ES3.0 编程指南》，通过对OpenGL接口的解读和分析，逐步剖析图形驱动程序架构、引擎低阶渲染器架构、以及GPU软件架构，我认为这种方式是最合适的。&lt;/p&gt;

&lt;h2 id=&quot;一图形驱动程序架构&quot;&gt;一、图形驱动程序架构&lt;/h2&gt;

&lt;p&gt;我们使用的图形接口如OpenGL、Metal、DX的原理是基于驱动程序接口做的封装。因此，图形驱动调用接口有三步骤：
检查调用参数、检查硬件是否支持
接着调用硬件驱动程序接口
将数据推入缓冲Buffer&lt;/p&gt;

&lt;p&gt;数据被推入缓冲，后续的详细路线如下图：&lt;/p&gt;

&lt;p&gt;（GPU中的数据流向图）&lt;/p&gt;

&lt;p&gt;GPU有线程来执行程序，线程通过Core来执行指令，每个线程的程序都是一样的，只是数据不同。
因此很好理解，GPU中有许多线程，每个线程执行的指令是一样的，从三角形处理到光栅化以及片元处理，都是顺序执行的指令。因此数据流也跟着顺序的指令走。最后到达帧缓存。&lt;/p&gt;

&lt;p&gt;图形API适配了许多GPU驱动程序的接口，驱动程序先检查参数，再将指令推入队列，再刷新时将队列地址发送给GPU，GPU开始处理队列，由线程处理队列中的每个指令，每个线程拥有同样的程序，数据不断被处理，最后到达帧缓存。&lt;/p&gt;

&lt;p&gt;在最上层的图形接口之上，引擎低阶渲染器会调用很多不同类型的图形接口，将数据塞入到缓冲中。下面就来详细描述下，引擎低阶渲染器的架构是怎样的。&lt;/p&gt;

&lt;h2 id=&quot;二引擎低阶渲染器架构&quot;&gt;二、引擎低阶渲染器架构&lt;/h2&gt;

&lt;p&gt;前面我们说，图形API封装了硬件驱动程序，会先检查再调用。&lt;/p&gt;

&lt;p&gt;实际上，低阶渲染器也是一个封装图形API接口的程序，不同的是它封装的更适合渲染对象。&lt;/p&gt;

&lt;p&gt;其封装的目的是让散乱的图形API变得更方便使用，同时还能够优化掉重复的计算。&lt;/p&gt;

&lt;p&gt;这里首先我们来看下OpenGL ES图形API中的接口类型，通过了解图形API接口，能够想象出如果是我们自己来做低阶渲染器架构我们应该如何封装。所以，了解熟悉图形API对于低阶渲染器的架构非常重要。&lt;/p&gt;

&lt;p&gt;下面是一些OpenGL的接口描述和统计，有些枯燥，如已熟悉可以直接跳到描述架构部分。&lt;/p&gt;

&lt;p&gt;OpengGL接口描述与统计：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;状态开启和关闭，包括开启三角绘制、开启混合、开启模板等
glEnableXXX、glDisableXXX
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;获取或查询某数据，包括获取数据，获取日志，获取结果等
glGetXXX
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;绑定数据，包括绑定数据到着色器等
glBindXXX
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;开始和结束某处理，包括开始变换反馈，查询数据等
glBeginXXX
glEndXXX
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;清除缓冲
glClearXXX
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;创建某对象，包括缓冲，数组，纹理，采样器对象等等
glGenXXX，glCreateXXX
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;删除某对象，包括着色器对象，数组，纹理对象，采样器对象等等
glDeleteXXX
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;还有其他的如:
glDrawXXX，绘制相关
glCopyXXX，拷贝复制相关
glCheckXXX，检查相关
glIsXXX，判断是否有效
......
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;下面按相关性划分为：&lt;/p&gt;

&lt;p&gt;（着色器相关接口图）&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;着色器相关：
创建着色器：glCreateShader
加载着色器代码：glShaderSource
编译着色器：glCompileShader
创建着色器程序：glCreateProgram
绑定着色器：glAttachShader
解绑着色器：glDetachShader
链接着色器程序：glLinkProgram
使用着色器程序：glUseProgram
删除着色器程序：glDeleteProgram
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;（视口相关接口图）&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;视口相关：
设置视口：glViewport
设置视口裁剪深度：glDepthRangef
清除颜色缓冲：glClear
置换缓冲区：eglSwapBuffers
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;（顶点相关接口图）&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;顶点相关：
通过创建缓冲区，可以把顶点数据加载到缓冲区，从而绘制顶点。

加载顶点属性：glVertexAttribXXX
绘制三角形：glDrawArrays，glDrawElements
启用/禁用顶点属性数组：glEnable/DisableVertexAttribArray
申请缓冲：glGenBuffers
指定缓冲：glBindBuffer
向缓冲填入数据：glBufferData，glBufferSubData
删除缓冲：glDeleteBuffers
创建顶点数组对象：glGenVertexArrays
绑定顶点数组：glBindVertexArray
删除顶点数组：glDeleteVertexArrays
映射并返回缓冲区数据：glMapBufferRange
取消映射缓冲区：glUnmapBuffer
刷新映射缓冲区：glFlushMappeBufferRange
复制缓冲区数据：glCopyBufferSubData
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;（图元绘制相关接口图）&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;图元绘制相关：
绘制几何形状对象：glDrawArrays, glDrawElements, glDrawRangeElements,
绘制几何Instance：glDrawArraysInstanced, glDrawElementsInstanced
绘制线段：glLineWidth
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;（光栅化相关接口图）&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;光栅化相关：
指定正面顺序：glFrontFace
剔除反面：glCullFace
多边形偏移：glPolygonOffset
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;遮挡查询：glBeginQuery, glEndQuery, glGenQueries, glDeleteQueries, glGetQueryObjectuiv
遮挡查询用查询对象来跟踪通过深度测试的片段或样本。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;顶点着色器相关：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;内建特殊变量：
gl_VertexID，当前顶点整数索引
gl_InstanceID，当前图元实例编号
gl_Position，输出顶点
gl_PointSize，点精灵尺寸
gl_FrontFacing，是否正面的布尔值
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;内建常量：
gl_MaxVertexAttribs，顶点属性最大数量
gl_MaxVertexUniformVectors，使用vec4统一变量的最大数量
gl_MaxVertexOutputVectors，输出向量的最大数量
gl_MaxVertexTextureImageUnits，可用纹理单元的最大数量
gl_MaxCombinedTextureImageUnits，顶点和片元着色器中可用纹理单元的最大数量总和
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;（变换反馈相关接口图）&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;变换反馈TransformFeedback相关：
glTransformFeedbackVaryings，指定变换反馈时捕捉的顶点属性
glBeginTransformFeedback，开始变换反馈（需先创建变换反馈缓冲区，再绑定到顶点索引）
glEndTransformFeedback，结束变换反馈
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;（纹理相关接口图）&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;纹理相关：
4种纹理类型：2D纹理、2D纹理数组、3D纹理、立方图纹理
其中2D纹理数组和3D纹理有点相似，2D纹理数组常用于帧动画。两者区别为过滤和mipmap不同。

glGenTextures，创建纹理对象
glDeleteTextures，删除纹理对象
glBindTexture，绑定到一个特定的纹理目标
glTexImage2D，加载2D和立方图的纹理数据
glTexSubImageXXX，加载部分纹理图像数据
glTexImage3D，加载3D纹理数据
glPixelStorei，设置解包对齐
glTexParameteri，设置贴图的过滤模式
glGenerateMipmap，自动生成mip贴图
glActiveTexture，设置当前的纹理单元，以便后续将纹理绑定该单元
glCompressedTexImageXXX，加载2D、立方图、3D等纹理的压缩图像数据（ETC、ASTC等压缩格式）
glCompressedTexSubImageXXX，加载部分压缩纹理图像

glReadBuffer，设置拷贝图像数据来源的颜色缓冲区
glCopyTexImageXXX，从颜色缓冲区拷贝数据到纹理
glCopyTexSubImageXXX，拷贝部分颜色缓冲区的数据到纹理
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;（采样器相关接口图）&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;采样器相关：
glGenSamplers，生成采样器对象
glDeleteSamplers，删除采样器对象
glBindSampler，绑定纹理到采样器对象
glSamplerParameterXXX，设置采样器对象参数
glTexStorageXXX，分配纹理内存
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;片段着色器相关：&lt;/p&gt;

&lt;p&gt;很久以前在固定功能管线中，使用3种输入：顶点颜色插值、纹理颜色、常量颜色，再使用一些公式的组合实现有趣的特效，包括：A&lt;em&gt;B、A+B、A+B-0.5、A&lt;/em&gt;C+B*(1-C)、A-B等等。&lt;/p&gt;

&lt;p&gt;支持可编程管线后，我们可以通过可编程管线来实现固定功能管线的效果。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;现在的可编程管线的输入由四部分构成：
顶点属性插值（顶点上的颜色、uv、法线等）
统一变量（全局常量）
采样纹理
代码常量
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;内建特殊变量：
gl_FragCoord，片段的窗口相对坐标（可用于噪声贴图计算）
gl_FrontFacing，是否正面朝向
gl_PointCoord，点精灵的纹理坐标
gl_FragDepth，输出变量，覆盖片段的固定功能深度值（会导致深度测试优化失效）
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;内建常量：
gl_MaxFragmentInputVectors，输入的最大数量
gl_MaxTextureImageUnits，可用纹理图像单元的最大数量
gl_MaxFragmentUniformVectors，使用vec4统一变量项目的最大数量
gl_MaxDrawBuffers，多重渲染目标（MRT）的最大支持数量
gl_(Min/Max)ProgramTexelOffset，通过内建ESSL函数texture*Offset()偏移参数支持的最大和最小偏移量
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;（缓冲区相关接口图）&lt;/p&gt;

&lt;p&gt;缓冲区相关：&lt;/p&gt;

&lt;p&gt;缓冲区有三种，颜色缓冲区，深度缓冲区，模板缓冲区。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;缓冲清除相关：
glClear，清除指定缓冲区
glClearColor，清除颜色缓冲区中的指定颜色
glClearDepth，清除深度缓冲区中的指定深度
glClearStencil，清除模板缓冲区中的指定掩码
glClearBufferXXX，清除指定缓冲区中的部分区域
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;缓冲写入相关：
glColorMask，像素写入颜色缓冲区时，哪些分量会被更新
glDepthMask，深度写入深度缓冲区时，哪些深度可以修改
glStencilMask，掩码写入模板缓冲区时，哪些掩码可以被修改
glStencilMaskSeparate，根据正面和背面的图元使用不同的掩码
glDrawBuffers，渲染指定颜色数组到多重渲染目标中
（多重渲染目标允许一次渲染多个颜色缓冲区，从而实现高级渲染算法，如延迟渲染）
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;（裁剪与测试相关图）&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;模板缓冲裁剪与测试：
glScissor，指定裁剪矩形区域
glStencilFuncXXX，用指定公式指定值指定掩码测试比较模板缓冲
glStencilOpXXX，将测试结果用于深度缓冲区的操作

深度缓冲测试：glDepthFunc，设置深度测试的运算公式
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;（混合相关接口图）&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;混合相关：
glBlendFuncXXX，设置混合系数
glBlendColor，设置常量颜色
glBlendEquationXXX，设置运算公式
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;颜色缓冲区中读写像素：
glReadPixels，从颜色缓冲区中取出数据返回到指定数组中，
数据传输时会启动DMA传输，此时CPU会空出来。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;（帧缓冲区对象相关接口图）&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;帧缓冲区对象（FBO）相关：
可用于颜色、深度、模板纹理或渲染目标。
glGenRenderbuffers，分配n个渲染缓冲区对象，返回到指针中。
glBindRenderbuffer，绑定渲染缓冲区对象
glRenderbufferStorageXXX，指定渲染缓冲区对象大小和格式
glBindFramebuffer，设置当前帧缓冲区对象（渲染目标）
glFramebufferRenderbuffer，将一个渲染缓冲区对象连接到帧缓冲区附着点
glFramebufferTextureXXX，将纹理的某个mip级别连接到帧缓冲附着点
glCheckFramebufferStatus，验证帧缓冲区对象是否完整

glBlitFramebuffer，高效的将矩形区域的像素从一个帧缓冲区复制到另一个
glInvalidate(Sub)Framebuffer，让整个帧缓冲区或子区域失效
glDeleteRenderbuffers，删除指定渲染缓冲区对象，先断开缓冲区对象才能删除
glDeleteFramebuffers，删除指定帧缓冲对象
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;未完，最近忙新书和工作以及演讲训练，后续继续写……&lt;/p&gt;

&lt;h2 id=&quot;参考资料&quot;&gt;参考资料：&lt;/h2&gt;

&lt;p&gt;《OpenGL ES3.0 编程指南》DanGinsburg等著&lt;/p&gt;

&lt;p&gt;《GPU 引擎》
https://docs.microsoft.com/zh-tw/windows/win32/direct3d12/user-mode-heap-synchronization
《CPU体系结构》
https://my.oschina.net/fileoptions/blog/1633021&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>给女儿写信(二十二) 专注于技巧</title>
   <link href="http://www.luzexi.com/2022/02/12/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A122"/>
   <updated>2022-02-12T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/02/12/给女儿的信22</id>
   <content type="html">
&lt;hr /&gt;

&lt;p&gt;导读：喜欢跟孩子们一起，可惜一直在外地工作，因此我给孩子写点信并录成视频，跟孩子聊聊自己的故事以及最近的感受&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;要点&quot;&gt;要点：&lt;/h1&gt;

&lt;p&gt;把公司晋升答辩当作一次有目标的练习，从中汲取经验，让自己专注于每件事的技巧。&lt;/p&gt;

&lt;h1 id=&quot;大纲&quot;&gt;大纲：&lt;/h1&gt;

&lt;pre&gt;&lt;code&gt;1.想要在公司晋升，规则是通过PPT演讲陈述的方式晋升
2.晋升很重要，但不是我的最终目标，我的最终目标是让自己变得更好
3.我拆解晋升需要的技巧，包括，编程技巧，PPT技巧，演讲技巧，表达技巧
4.我想完成任务的同时，更好的练习技巧，专注于技巧的学习和练习
5.于是我去演讲俱乐部练习演讲，学习编程技术，学习PPT演示技巧，学习表达技巧
6.在这个练习过程中，爸爸不断的靠近目标，而且感觉到自己越来越有信心
7.可惜晋级没有成功，但无论怎么样，爸爸知道自己成长了，变成了一个更好的自己
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;同时，这些练习的技巧，可以用于未来的日子当中去，让自己变得更好&lt;/p&gt;

&lt;h1 id=&quot;内容&quot;&gt;内容：&lt;/h1&gt;

&lt;p&gt;Hello Sharon，Hello Anne，爸爸好想你们，爸爸爱你们。&lt;/p&gt;

&lt;p&gt;最近杭州和深圳的疫情又有严重起来了，要多加小心哟。&lt;/p&gt;

&lt;p&gt;爸爸给你们讲一讲爸爸最近遇到的事好不好？&lt;/p&gt;

&lt;p&gt;爸爸最近在做一个晋升的演讲，爸爸想通过演讲来达成自己的目标，但是遇到了一些困难。&lt;/p&gt;

&lt;p&gt;第一个困难是爸爸不知道这个目标能不能达成，心里很恐慌，很慌，有可能达成，有可能达不成。&lt;/p&gt;

&lt;p&gt;所以爸爸想办法怎么更靠近这个目标，接着爸爸就把这个目标拆分成几个技巧。&lt;/p&gt;

&lt;p&gt;比如说上台演讲的技巧，比如说即兴演讲的技巧，比如说表达沟通的技巧，还有一些爸爸工作上的技巧。&lt;/p&gt;

&lt;p&gt;这些技巧爸爸一个个去练习，一个每天的都去练习。&lt;/p&gt;

&lt;p&gt;就拿平时的即兴演讲技巧来说，爸爸会叫Sharon跟安妮给爸爸出题，有时候讲的好，有时候讲的不好，&lt;/p&gt;

&lt;p&gt;不断的练习过程当中也会做一些总结和复盘，这样让自己学习效率会更高一些。&lt;/p&gt;

&lt;p&gt;通过这样的练习，每个技巧都去练习一下，爸爸能够更靠近目标，&lt;/p&gt;

&lt;p&gt;这些技巧都指向这个目标，在最后要上台的日子的日子里，爸爸更加有信心了，因为爸爸练习了这么多技巧，这个技巧我觉得也练得还可以，就上台了，虽然但是最后的结果不怎么样，最后结果没有达成，所以爸爸昨天心情很糟糕，很难受。昨天调整了一整天，终于在今天早上的时候心情好多了。&lt;/p&gt;

&lt;p&gt;爸爸想纵使结果没有达成，但是爸爸练习了这么多技巧，这些技巧都可以运用到未来的日子里面去。&lt;/p&gt;

&lt;p&gt;想到这里，爸爸又充满了力量，因为爸爸知道爸爸成长了，完善了自己，每个技能都学习到了新的知识，自己整个能力都提升了，成长了，爸爸感到很开心，未来更美好了。&lt;/p&gt;

&lt;p&gt;想到这里爸爸这个情绪就释放掉了，爸爸想跟安妮和Sharon说，跟爸爸一起来练习技巧好不好？&lt;/p&gt;

&lt;p&gt;我们在达成目标的过程当中有很多的技巧，每一个可以逐个的去练习，练习完毕以后我们会发现我们变得更好了，目标也达成了，太棒了，即使目标没有达成，就像爸爸这样，练习的技巧也可以运用到未来的日子里面去，希望跟Sharon和Anne一起加油。&lt;/p&gt;

&lt;p&gt;记得照顾好妈妈哟，笔芯。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>给女儿写信(二十一) 早上运动的力量</title>
   <link href="http://www.luzexi.com/2022/01/23/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A121"/>
   <updated>2022-01-23T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/01/23/给女儿的信21</id>
   <content type="html">
&lt;hr /&gt;

&lt;p&gt;导读：喜欢跟孩子们一起，可惜一直在外地工作，因此我给孩子写点信并录成视频，跟孩子聊聊自己的故事以及最近的感受&lt;/p&gt;

&lt;hr /&gt;

&lt;h1 id=&quot;主题&quot;&gt;主题：&lt;/h1&gt;

&lt;p&gt;早上运动让我一天活力满满&lt;/p&gt;

&lt;h1 id=&quot;结构&quot;&gt;结构：&lt;/h1&gt;

&lt;pre&gt;&lt;code&gt;1.以前我每天工作到下午时都精疲力尽
2.我发现特别是下午4点后很疲惫，
3.一直保持每天都锻炼，所以通常会选择晚上，或下午
4.晚上常常会因为太疲惫而放弃锻炼
5.中午常常会因为太忙而忘记锻炼
6.运动一停下来，常常一停就是好几个月，非常苦恼
7.于是把运动放到早上，尝试早上运动
8.一开始很艰难，早上运动，要早起，很痛苦
9.咬牙坚持，仍然会有波动，时常起不来，睡过头
10.坚持了2个月后，发现人越来越精神
11.于是满满的坚持了1整年，每天早上运动
12.每次早上运动完毕后，发现自己的活力满满，可以维持一整天的高强度工作
13.呼吁，孩子们，跟我一起早上起来运动30分钟
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;哈喽，秀恩，安妮。爸爸到深圳了，一离开你们就好想你们。&lt;/p&gt;

&lt;p&gt;今天爸爸想跟你们讲一讲关于爸爸运动的事。&lt;/p&gt;

&lt;p&gt;爸爸以前小时候很喜欢运动的，但是长大了以后，特别是开始上班了以后每天忙于工作，运动的越来越少了。&lt;/p&gt;

&lt;p&gt;一开始爸爸觉得没什么问题，但是随着日子的过去，时间的流逝，爸爸发现自己的体力越来越差了。&lt;/p&gt;

&lt;p&gt;这是为什么呢？我想了好久，终于想明白了。&lt;/p&gt;

&lt;p&gt;因为爸爸没有很好地锻炼自己的身体，自己的身体慢慢变差了，俗话说得好，留得青山在不怕没材烧，身体是革命的本钱，我们应该好好的去加强自己的身体，锻炼自己的身体，从而让我们的学习和工作更有效率，也更容易集中注意力。&lt;/p&gt;

&lt;p&gt;最近这几年爸爸一直坚持锻炼身体，感觉到自己的身体恢复了以前的活力，但是这中间还有一些细节爸爸想跟你们讲一讲。&lt;/p&gt;

&lt;p&gt;以前爸爸虽然一直保持锻炼，但是通常都会选择晚上或者下午去锻炼。&lt;/p&gt;

&lt;p&gt;当我在晚上去锻炼的时候，由于工作和学习了一整天常常会感觉到疲惫，经常会想着哎呀算了，不要锻炼了。&lt;/p&gt;

&lt;p&gt;中午的时候也是，会很忙，经常会忘记锻炼。&lt;/p&gt;

&lt;p&gt;每次锻炼一停下来，常常就是好几个月非常苦恼。&lt;/p&gt;

&lt;p&gt;哎呀，没有把身体搞好，自己的工作和学习效率又下降了。&lt;/p&gt;

&lt;p&gt;于是想着说，如何才能让自己每天都坚持运动，同时又不会忘记呢？&lt;/p&gt;

&lt;p&gt;后来爸爸想到了早上锻炼，如果说在早上锻炼的话，我就不会忘记了接下来一整天的锻炼了。&lt;/p&gt;

&lt;p&gt;锻炼好再学习再工作，这样的话，我就不会忘记了，哈哈。&lt;/p&gt;

&lt;p&gt;一开始爸爸选择早上锻炼的时候，非常的痛苦非常的难，虽然爸爸咬牙坚持，但仍然时常起不来，睡过头。&lt;/p&gt;

&lt;p&gt;接着，爸爸继续咬牙坚持，坚持了两个月后慢慢发现，爸爸越来越精神了，活力越来越充足了。&lt;/p&gt;

&lt;p&gt;爸爸发现，每天早上动运动锻炼完后，一整天都是活力满满的，这个活力可以维持高强度的学习和工作一整天。&lt;/p&gt;

&lt;p&gt;太棒了，孩子们爸爸终于获得了这个技能。&lt;/p&gt;

&lt;p&gt;爸爸可以通过锻炼来加强自己的身体，让自己更有精神，体力更好，也更容易集中注意力，同时又能更好地坚持下去，让自己在高强度的学习和工作下，仍然能保持活力满满的一整天。&lt;/p&gt;

&lt;p&gt;希望你们也和我一起来早上运动，让自己有更强壮的身体，同时让自己有活力满满的一整天，进而让学习更高效。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(五十三) 《如何做好PPT》- PPT设计的艺术</title>
   <link href="http://www.luzexi.com/2022/01/10/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B053"/>
   <updated>2022-01-10T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/01/10/读书笔记53</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485154&amp;amp;idx=1&amp;amp;sn=0e60371fd95e61debce8fa3518510470&amp;amp;chksm=fc2263e5cb55eaf38998592671e335e143d1864119737b7c3ab5b02bc8fcebd7998e9c3d8d20&amp;amp;token=1275740756&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;背景&quot;&gt;背景：&lt;/h2&gt;

&lt;p&gt;近段时间在练习演讲，一开始只是想练习下自己的口头表达能力和沟通能力，但渐渐的我发现我爱上了演讲。又过了一段时间，我发现演讲的其中一个关键用途是汇报和培训，于是我又渐渐爱上了汇报和培训。&lt;/p&gt;

&lt;p&gt;但是光讲还不行，还得有演示的PPT，这样才能让观众不但有听觉上得冲击，还有视觉上冲击，双重冲击之下才能发挥最大效果。&lt;/p&gt;

&lt;p&gt;于是找了3本同类书籍读，《PPT设计的艺术》、《精进PPT》、《PPT设计思维、技术与实践》进行主题阅读，看完后根据自己的理解做总结。&lt;/p&gt;

&lt;h2 id=&quot;概述&quot;&gt;概述：&lt;/h2&gt;

&lt;p&gt;PPT的核心关键是什么？
如何达到最佳呈现效果？
有哪些设计需要注意？
有哪些搜图站点？&lt;/p&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结：&lt;/h2&gt;

&lt;p&gt;少就是多
结构：结论先行、理由和论据在后、金字塔结构
排版：统一、整齐、平衡
小技巧：做图、色彩饱和度、图标、图表&lt;/p&gt;

&lt;h2 id=&quot;内容&quot;&gt;内容：&lt;/h2&gt;

&lt;p&gt;本篇是针对《PPT设计的艺术》这本书做的一次总结，很不错的一本讲PPT的书，推荐。&lt;/p&gt;

&lt;h2 id=&quot;ppt的核心关键是什么&quot;&gt;PPT的核心关键是什么？&lt;/h2&gt;

&lt;p&gt;经过一段时间的PPT练习和演讲练习，再加上对理论知识的学习，我得出了PPT的核心关键：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;传递核心观点（1个核心观点）&lt;/li&gt;
  &lt;li&gt;围绕核心观点打造呈现效果（清晰地传递信息，视觉冲击增强观众体验）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;做每件事的要素其实都很简单，简单的道理背后有复杂的经历和技巧。&lt;/p&gt;

&lt;p&gt;下面我就围绕这两个核心关键做更详细的技巧阐述。&lt;/p&gt;

&lt;h2 id=&quot;如何达到最佳呈现效果&quot;&gt;如何达到最佳呈现效果？&lt;/h2&gt;

&lt;p&gt;为了达到最佳呈现效果，我们需要突出重点，用结构思维去传递信息，用视觉效果增强体验。&lt;/p&gt;

&lt;h2 id=&quot;少就是多&quot;&gt;少就是多&lt;/h2&gt;

&lt;p&gt;人大脑的记忆是有限的，当展现在我们眼前的事物太多时，通常我们一个都记不住。当入眼的东西少了，我们反而记得更清楚。&lt;/p&gt;

&lt;p&gt;少即是多的本质就是，突出重点。&lt;/p&gt;

&lt;h2 id=&quot;结构结论先行理由和论据在后金字塔结构&quot;&gt;结构：结论先行、理由和论据在后、金字塔结构&lt;/h2&gt;

&lt;p&gt;我们在面对问题时，通过拆解可以把问题分成一个个你能解决的部分，&lt;/p&gt;

&lt;p&gt;高手最本质的不同就是事先花时间用心思考，再动手去实现。&lt;/p&gt;

&lt;p&gt;（高手制作PPT的五大步骤图）&lt;/p&gt;

&lt;p&gt;多思考，如何说别人才肯听，如果做才能印象深刻，如果讲才能理解我们的核心观点，这种思考显得尤为重要。&lt;/p&gt;

&lt;p&gt;用金字塔结构汇报工作&lt;/p&gt;

&lt;p&gt;用结构化思维做汇报工作，把信息整理、筛选成结论、理由和事实三部分。&lt;/p&gt;

&lt;p&gt;（结论先行金字塔结构）&lt;/p&gt;

&lt;p&gt;通过层层递进论述，将你的汇报变成一个金字塔结构。结论先行，再用理由来支撑结论，最后阐述事实和详情来支撑理由，层层递进。&lt;/p&gt;

&lt;p&gt;除了结论先行，还有一些成熟的表达架构:
标准式：SCA，情景-冲突-答案
开门见山式：ASC，答案-情景-冲突
突出忧虑式：SCA，冲突-情景-答案
突出信心式：QSCA，问题-情景-冲突-答案&lt;/p&gt;

&lt;p&gt;小结一下，有结构的表达中，适合汇报的结构为：以结论为开头，层层论证结论，最后强化结论的过程。&lt;/p&gt;

&lt;h2 id=&quot;mece分析法则&quot;&gt;MECE分析法则&lt;/h2&gt;

&lt;p&gt;简单回顾下金字塔原理中的分析方法MECE法则：互相独立，完全穷尽&lt;/p&gt;

&lt;p&gt;这8个字可以理解为：不重不漏 – 保证信息分类中的独立性和完整性。&lt;/p&gt;

&lt;p&gt;独立性强调每项工作之间要独立，每项工作之间不要有交叉重叠。完整性说的是，在信息分类过程中不要遗漏掉某项，要保证其完整性。&lt;/p&gt;

&lt;p&gt;在分类信息和分析事物时，如果能做到不重不漏可以让事物变得更加清晰可控。&lt;/p&gt;

&lt;p&gt;在具体执行时可分为两步：
1.确定主体，是大类还是复杂问题，区分对待
2.把主体分为几个部分，再把小的部分拆解成更小的部分&lt;/p&gt;

&lt;p&gt;拆解问题直到拆解的小问题可以直接通过一次行动解决，其中分析和拆解时不能存在遗漏和重复的情况。&lt;/p&gt;

&lt;p&gt;总之，分析不重不漏，分析时的分类方法可以是时间线分类、流程线分类、以上统下、由内而外、从整体到局部。&lt;/p&gt;

&lt;h2 id=&quot;有哪些设计需要注意&quot;&gt;有哪些设计需要注意？&lt;/h2&gt;

&lt;p&gt;（图：PPT制作技巧）&lt;/p&gt;

&lt;p&gt;PPT设计有了思维结构和逻辑外，还需要在制作上下功夫，这里有3点需要注意：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;排版&lt;/li&gt;
  &lt;li&gt;色彩&lt;/li&gt;
  &lt;li&gt;图片&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;排版&lt;/p&gt;

&lt;p&gt;（图：PPT排版技巧）&lt;/p&gt;

&lt;p&gt;PPT在设计中要注意的排版问题，统一、整齐、平衡，总之就是让人舒服不要有强迫症。&lt;/p&gt;

&lt;p&gt;各元素对齐能让视觉更舒适，包括边缘对齐、等距分布、总体兼顾。&lt;/p&gt;

&lt;p&gt;大脑不喜欢混乱，因此统一的样式是不可或缺的，同时这也是提高制作PPT效率的好方法。&lt;/p&gt;

&lt;p&gt;平衡需要建立在页面中要点的主次优先级上，突出主要信息，减弱次要信息，同时做好页面的视觉平衡。&lt;/p&gt;

&lt;p&gt;色彩&lt;/p&gt;

&lt;p&gt;（图：PPT色彩技巧）&lt;/p&gt;

&lt;p&gt;PPT的色彩有两大关键点：
颜色选择
颜色饱和度&lt;/p&gt;

&lt;p&gt;颜色和主题一样，多了会让人眼花缭乱，分不清主次，看不清记不住。&lt;/p&gt;

&lt;p&gt;颜色的选择有一个建议：除黑白之外最好不要超过两种颜色。&lt;/p&gt;

&lt;p&gt;颜色饱和度是指，颜色的鲜艳程度，饱和度低到一定程度，颜色就会变成灰色。&lt;/p&gt;

&lt;p&gt;优秀的PPT中，颜色饱和度普遍不高，这样看上去显得沉稳、专业。&lt;/p&gt;

&lt;p&gt;在颜色饱和度的选择上，尽量使用柔和的、适中的、不刺眼的颜色，作为同一章节甚至整个PPT的主色调。&lt;/p&gt;

&lt;p&gt;图片&lt;/p&gt;

&lt;p&gt;图片能帮助你快速引导观众的注意力并传递核心观点。&lt;/p&gt;

&lt;p&gt;同样的，图片不在于多，而在于精。我们在制图选图时需要特别留意，图片是否契合主题，色彩饱和度，尤其是在关键页。&lt;/p&gt;

&lt;p&gt;图片的制作有4个技巧&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;形状图&lt;/li&gt;
  &lt;li&gt;图标&lt;/li&gt;
  &lt;li&gt;选图&lt;/li&gt;
  &lt;li&gt;图表&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;形状图&lt;/p&gt;

&lt;p&gt;在PPT软件中可插入丰富形状的图形，再通过组合、描边、填充、编辑顶点的制作有趣生动的图形。&lt;/p&gt;

&lt;p&gt;（图：生动的形状图-来源网络）&lt;/p&gt;

&lt;p&gt;通过形状图可以让PPT中的元素更有趣，也能达到吸引观众的效果。&lt;/p&gt;

&lt;p&gt;图标&lt;/p&gt;

&lt;p&gt;图标能让观众，快速、生动形象的表达我们想表达的意思，这比文字的力量要强大的多。&lt;/p&gt;

&lt;p&gt;因此用好图标也很关键。&lt;/p&gt;

&lt;p&gt;文章的后面介绍了一些图标网站可帮助大家快速搜索到自己称心的图标。&lt;/p&gt;

&lt;p&gt;（图：PPT中的图标-来源网络）&lt;/p&gt;

&lt;p&gt;图标选择的关键是：
消除歧义
图文主题吻合
创意展示&lt;/p&gt;

&lt;p&gt;选图&lt;/p&gt;

&lt;p&gt;（图：选图的关键）&lt;/p&gt;

&lt;p&gt;选图的关键是，吸睛、吸睛、吸睛&lt;/p&gt;

&lt;p&gt;什么样的图片才能吸引我们的注意力呢？
舒适
有趣
愉悦&lt;/p&gt;

&lt;p&gt;这里分别为这三种类型图片举常用的例子：
婴儿图让人舒适
幽默图让人有趣
美女图让人愉悦&lt;/p&gt;

&lt;p&gt;在保证吸睛的同时，为了更好的呈现，图片还要保证，高清、简单、精炼：
首先高清图保证不模糊
其次选图要简单且紧扣主题
最后文字要精炼才能记得住&lt;/p&gt;

&lt;p&gt;图表&lt;/p&gt;

&lt;p&gt;为什么要用图表？提供有理有据的数据、突出页面重点、简化表达逻辑、强化重点。&lt;/p&gt;

&lt;p&gt;（图：丰富的图标-来源网络）&lt;/p&gt;

&lt;p&gt;数据本身没有意义，我们要的是数据背后的事实，它为要点提供支撑。所有不能提高核心信息传递的元素都可以被精简。&lt;/p&gt;

&lt;p&gt;遇到一页多表的情况时，每个表都要支撑同一个观点，并且从不同侧重点展示各自维度的数据来支撑观点。&lt;/p&gt;

&lt;p&gt;图表修炼三招：
用颜色突出重点
文字辅助说明
特别标注突显细节&lt;/p&gt;

&lt;p&gt;关键不是数据和图表，而是如何用感性的图片刺激观众的大脑。如果你想说服谁，那么尽量少用理性分析的数据堆砌图表，多用刺激大脑感性认知的可视化图片。&lt;/p&gt;

&lt;p&gt;因此如何用图表去刺激观众的大脑，向观众传递核心观点才是关键。&lt;/p&gt;

&lt;p&gt;图表从来不是重点，它背后所传达的核心观点才是重中之重。&lt;/p&gt;

&lt;p&gt;有哪些搜图站点？&lt;/p&gt;

&lt;p&gt;快速搜索适合的配图&lt;/p&gt;

&lt;p&gt;无版权图片素材网站：&lt;/p&gt;

&lt;p&gt;pixabay.com
支持中文搜索&lt;/p&gt;

&lt;p&gt;pexels.com
偏商务风的图片网站&lt;/p&gt;

&lt;p&gt;unsplash.com
以自然风景为主的无版权图片网站&lt;/p&gt;

&lt;p&gt;gratisography.com
创意图片网站&lt;/p&gt;

&lt;p&gt;vectorportal.com
二次元，扁平化风格图片网站&lt;/p&gt;

&lt;p&gt;visualhunt.com
根据颜色找图的网站&lt;/p&gt;

&lt;p&gt;foodiesfeed.com
食物图片网站&lt;/p&gt;

&lt;p&gt;其他：
polayoutu.com
ssyer.com&lt;/p&gt;

&lt;p&gt;好的icon图标网站：&lt;/p&gt;

&lt;p&gt;微软的PPT中有一些自带的图标&lt;/p&gt;

&lt;p&gt;iconfont.cn
阿里图标&lt;/p&gt;

&lt;p&gt;icons8.cn
扁平风格的图标&lt;/p&gt;

&lt;p&gt;flat-icon-design.com
受女生欢迎的日本图标网站&lt;/p&gt;

&lt;p&gt;flaticon.com
扁平化风格的图标网站&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(五十二) 《如何精彩演讲》#3 如何即兴演讲</title>
   <link href="http://www.luzexi.com/2022/01/06/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B052"/>
   <updated>2022-01-06T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/01/06/读书笔记52</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485133&amp;amp;idx=1&amp;amp;sn=6e8acd0c1b8f3cbf6c048d4ba4120776&amp;amp;chksm=fc2263cacb55eadcd1533d68ab7f226020292b3bc8ec4bd5684ae07daa0dd8f41b05936c6600&amp;amp;token=353183005&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;背景&quot;&gt;背景&lt;/h2&gt;

&lt;p&gt;不知不觉看完了6本关于演讲的主题，按推荐顺序排列分别是《高效演讲》、《演讲与口才》、《即兴演讲》、《关键对话》、《TED演讲的力量》、《说话的艺术》。&lt;/p&gt;

&lt;p&gt;我希望自己能够对演讲有一个系统性的学习，于是自己在平常的生活和工作中做了很多实践和训练。&lt;/p&gt;

&lt;p&gt;目标是，改善平常的工作、生活中表达的流畅和清晰度，能让人感觉到舒服，让人感觉有活力。&lt;/p&gt;

&lt;p&gt;同时在公众演讲上，希望自己能够很好的呈现所思所想以及所知所学。&lt;/p&gt;

&lt;p&gt;因此开启这个精彩演讲系列的总结文章，我将对这六本书（其实不止六本）的内容加上我的实践经验做一次全面的系统性的总结。&lt;/p&gt;

&lt;h2 id=&quot;概述&quot;&gt;概述：&lt;/h2&gt;

&lt;h3 id=&quot;1即兴演讲与普通演讲的区别&quot;&gt;1.即兴演讲与普通演讲的区别&lt;/h3&gt;

&lt;h3 id=&quot;2即兴演讲的框架&quot;&gt;2.即兴演讲的框架&lt;/h3&gt;

&lt;h3 id=&quot;3即兴演讲的练习方法&quot;&gt;3.即兴演讲的练习方法&lt;/h3&gt;

&lt;h2 id=&quot;内容&quot;&gt;内容：&lt;/h2&gt;

&lt;p&gt;如果你仔细一些，就会发现我们生活当中会用到很多很多的机械演讲，很多人并不在意进行演讲认为进行演讲，只是脑袋里迸发出了一些火花，或者说自己总结的那些方法。那其实在我们平常的沟通沟通跟表达过程当中进行演讲，在我们沟通表达当中占据的位置非常重要。&lt;/p&gt;

&lt;p&gt;（即兴演讲运用场景）&lt;/p&gt;

&lt;p&gt;特别是作为领导人，作为一个想要做更多有效工作的人。更多的需要激情演讲来，让自己的沟通效率更高，让自己的工作效率更高，让自己的生活效率更高。&lt;/p&gt;

&lt;p&gt;欣欣的我们会发现，在我们生活当中经常会有一些即兴演讲。讲的不好的情况让。让对话让对方让大家感觉到非常不舒服。&lt;/p&gt;

&lt;p&gt;其中一些高调的领导者，会在即兴时刻跟着感觉走，结果让自己。悔恨终生一些管理者在会议上被点名发言却无语无伦次，舌头就像打了结一样，还有一些管理者一开口就滔滔不绝没完没了，最后连自己讲了什么都忘记了。另一些领导者呢，在回答问题时没有主题，东拉西扯，最后拼命解释说我的意思是这种体验非常非常的不好。&lt;/p&gt;

&lt;p&gt;因此进行演讲，需要引起我们足够多的重视，这在我们平常沟通工作生活当中，有非常非常多的场景可以运用，并且它是我们。提高效率。让自己生活工作更美好的一个非常非常好的技巧。&lt;/p&gt;

&lt;h2 id=&quot;即兴演讲与普通演讲的区别&quot;&gt;即兴演讲与普通演讲的区别&lt;/h2&gt;

&lt;p&gt;（普通演讲特点）&lt;/p&gt;

&lt;p&gt;我们可以用几个星期甚至几个月的时间来准备我们的演讲稿，从计划、研究、大纲、讨论、草拟、完善的步骤来为即将到来的演讲准备演讲稿。这是一件非常有趣的事情，也非常值得去准备。&lt;/p&gt;

&lt;p&gt;（即兴演讲特点）&lt;/p&gt;

&lt;p&gt;只是平时的生活和工作当中，我们90%情况都没有任何准备，大部分人都会“临时抱佛脚”或“跟着感觉走”说两句，这让我们错失了很多关键时刻。&lt;/p&gt;

&lt;p&gt;对于普通的有预期的演讲，我们可以提前花时间去准备去练习，而即兴演讲大部分情况都不在我们的预料范围内，难道即兴演讲就只能靠天赋吗？并不是，它也是有方法可寻，并且可以通过练习来加强和巩固的。&lt;/p&gt;

&lt;h3 id=&quot;实际上优秀的即兴演讲它的秘密就在于准备&quot;&gt;实际上，优秀的即兴演讲，它的秘密就在于准备。&lt;/h3&gt;

&lt;h3 id=&quot;也就说你必须为看起来像是很自然的即兴发挥而做充分的准备&quot;&gt;也就说，你必须为“看起来像是很自然的即兴发挥”而做充分的准备。&lt;/h3&gt;

&lt;p&gt;（即兴演讲准备工作）&lt;/p&gt;

&lt;p&gt;我们要为此准备的东西很多，包括选择合适的时间和地点，在什么样的场合说什么样的话，会让对方感觉更自然。与此同时，在讲话前需要快速整理思路，知道自己要说的内容，围绕自己的中心思想讲话。同时自己的讲话的主题和内容要有价值，这是能够让周围的人愿意倾听且被你吸引的关键。最后要意识到我们的“麦克风”始终是开着的，要保持这种麦克风始终开着的状态，不管在办公室内还是办公室外，都要得体的沟通，语言可以激发人也可以威胁人，说符合自己身份的话。&lt;/p&gt;

&lt;p&gt;大量的练习让乔布斯可以在讲话时基本不用草稿。他在展示产品时，虽然也会小心遮住笔记不让观众看见，但他从来不会逐字逐句念出来。如果你什么都不准备就跟着感觉走，很容易让自己陷入尴尬的境地，到时候你只能胡乱说一些你不了解的东西。&lt;/p&gt;

&lt;p&gt;（即兴内容知识分类图）&lt;/p&gt;

&lt;p&gt;对于要说的主题人们总是期望你展示出科学、丰富、经验的知识内容。因此知识内容，通常分为三类：
科学知识，讲话的内容有扎实的知识背景
一般性知识，丰富的观点让你更有说服力
经验性知识，讲述个人经历让讲话更有吸引力&lt;/p&gt;

&lt;p&gt;在我们即兴讲话前，写下这些关键信息和支持性信息，将这些信息记在脑子里，这将构成你即兴演讲草稿的基础。如果所有即兴演讲都事先做好准备、构建扎实的知识基础，牢记关键信息，相信我们会体会到最棒的即兴演讲体验：“最棒的即兴演讲，是当我做足准备，熟知相关材料时，脱口而出的状况。”&lt;/p&gt;

&lt;p&gt;（即兴演讲时的心态）&lt;/p&gt;

&lt;p&gt;演讲的心态非常重要，是与听众连接的通道，也是我们保持真实自我和正念的好方法。&lt;/p&gt;

&lt;p&gt;演讲心态，保持真实、保持专注、保持尊重非常重要，同时也是保持我们保持真实领导力的好方法。&lt;/p&gt;

&lt;p&gt;我们一定要有当领导者的意愿，让自己始终处于领导位置，将每一次讲话和沟通都视为潜在的领导力时刻，为此做出自己的进行领导力即兴讲话。&lt;/p&gt;

&lt;p&gt;真实领导力，让自己专注于当下的对话，真实有勇气的分享你的想法、价值观和信念，分享自己的感受、自己的故事和自己的脆弱。&lt;/p&gt;

&lt;p&gt;成为真正的领导者需要挖掘自己内在，不断与同事、队友和朋友分享你的当下、想法、价值观、信念、感受和故事。&lt;/p&gt;

&lt;h3 id=&quot;通过保持自己的真实性激励鼓舞他人这让我们变得更加温暖同时也能让我们找到更多乐趣&quot;&gt;通过保持自己的真实性，激励鼓舞他人，这让我们变得更加温暖，同时也能让我们找到更多乐趣。&lt;/h3&gt;

&lt;h2 id=&quot;即兴演讲的框架&quot;&gt;即兴演讲的框架&lt;/h2&gt;

&lt;p&gt;很多人会问“为什么要用框架？”，这会让我们感觉是把自己禁锢在一个笼子里，看上去有些死板。当然这也是我一开始接触框架的感受，但练习一段时间后我就改变了我的想法。&lt;/p&gt;

&lt;p&gt;实际上，我们在即兴演讲中，如果没有可依的规则会感觉迷茫。特别是在练习即兴演讲时，没有规则没有方法没有可依的练习方式，很容易迷失在五花八门的“错觉”中。因此框架就起到了重要作用，它为长期技能的练习提供规则，为技能成长提供了依据。&lt;/p&gt;

&lt;h3 id=&quot;用模板去建立自己的即兴演讲框架是一种非常好又非常有效提高自己即兴演讲水平的方法&quot;&gt;用模板去建立自己的即兴演讲框架是一种非常好又非常有效提高自己即兴演讲水平的方法。&lt;/h3&gt;

&lt;p&gt;框架让我们更好的总结完善自己的即兴技巧，通过模板（也有很多人称为模型）和框架，让我们有道可循的思考自己的即兴演讲该如何改进。&lt;/p&gt;

&lt;p&gt;（即兴演讲框架图）&lt;/p&gt;

&lt;p&gt;《即兴演讲》为我们介绍了一种即兴演讲的框架：抓手、要点、结构体、呼吁行动。&lt;/p&gt;

&lt;p&gt;实际上即兴演讲有很多种框架，但都离不开上述这个结构，因此我认为从这个最通用最中心的框架出发来看即兴演讲会更好一些。&lt;/p&gt;

&lt;h3 id=&quot;注意即使有了即兴框架也并不代表我们就万事大吉了这是一个不断打磨和练习的过程通过框架来练习最终会做到将框架融会融入心中做到心中有框架口中无框架&quot;&gt;注意，即使有了即兴框架，也并不代表我们就万事大吉了，这是一个不断打磨和练习的过程。通过框架来练习，最终会做到将框架融会融入心中，做到心中有框架，口中无框架。&lt;/h3&gt;

&lt;p&gt;如果我们可以无论事先还是在讲话现场，都用这个模板去写自己的讲话草稿，通过不断的练习，我们就能让即兴效果更好。&lt;/p&gt;

&lt;p&gt;（即兴模板应用步骤与调整）&lt;/p&gt;

&lt;p&gt;即使完全不允许我们打底草稿，我们也可以借助这个模板去讲话。&lt;/p&gt;

&lt;h3 id=&quot;这里有个关键点即在每讲完每个部分后暂停下来思考下一个部分的内容然后再讲&quot;&gt;这里有个关键点，即在每讲完每个部分后暂停下来，思考下一个部分的内容，然后再讲。&lt;/h3&gt;

&lt;h3 id=&quot;在思考抓手时暂停讲完抓手内容后暂停思考一下讲话的核心要点讲完要点后暂停思考结构体的内容讲完结构体后暂停思考呼吁行动部分最后讲呼吁行动部分&quot;&gt;在思考抓手时暂停；讲完抓手内容后暂停，思考一下讲话的核心要点；讲完要点后暂停，思考结构体的内容；讲完结构体后暂停，思考呼吁行动部分；最后讲呼吁行动部分。&lt;/h3&gt;

&lt;h3 id=&quot;简而言之在讲完每一部分后都要暂停以便思考下一部分内容但你暂停时你会看起来更自信而你讲话的脚本也会更好&quot;&gt;简而言之，在讲完每一部分后都要暂停，以便思考下一部分内容。但你暂停时，你会看起来更自信，而你讲话的脚本也会更好。&lt;/h3&gt;

&lt;h3 id=&quot;注意模板和模型在实际情况中需要我们灵活的去调整例如当你发现讲话的时间已经不允许我们继续完整的讲完所有部分时要即使的调整特别是当你发现对方表现焦虑不安时你就要马上精简模板可能只说要点和呼吁但无论什么情况下都必须包含要点&quot;&gt;注意，模板和模型在实际情况中需要我们灵活的去调整，例如，当你发现讲话的时间已经不允许我们继续完整的讲完所有部分时要即使的调整，特别是当你发现对方表现焦虑不安时，你就要马上精简模板，可能只说要点和呼吁，但无论什么情况下都必须包含要点。&lt;/h3&gt;

&lt;h2 id=&quot;即兴框架要点&quot;&gt;即兴框架：要点&lt;/h2&gt;

&lt;p&gt;（即兴演讲框架-要点的关键点）&lt;/p&gt;

&lt;p&gt;坦率的说，说话没有要点就等于在浪费大家的时间。&lt;/p&gt;

&lt;p&gt;你会发现，当我们没有要点时，语音中会充满很多赘词，“你知道…”，“就是”，“那么”，“然后”，“是吧”等。&lt;/p&gt;

&lt;p&gt;究其原因时语言背后的思路不清。所以，一定要确保你的讲话有核心要点。&lt;/p&gt;

&lt;p&gt;乔布斯越成熟、自信，他就越能与周围实力强大、有想法的管理者自信地对话，而对方在和他争论时也感觉很舒适。&lt;/p&gt;

&lt;p&gt;要点的关键是三个部分：&lt;/p&gt;

&lt;p&gt;1.必须是你的观点&lt;/p&gt;

&lt;p&gt;2.要点要有吸引力&lt;/p&gt;

&lt;p&gt;3.要点必须积极正面&lt;/p&gt;

&lt;p&gt;首先，确保要点是的观点，是承载你信念的一部分，只要这样你才能有信心，并强势、明确的凸显出你的观点，并提炼中心思想，一句话简洁表达你的观点。&lt;/p&gt;

&lt;p&gt;其次，你的观点要有吸引力，这意味着你要了解什么会触动听众的内心，只有这样才能吸引听众的全部注意力，让你的即兴演讲效果发挥到极致。&lt;/p&gt;

&lt;p&gt;最后，要点必须是积极正面的，这是打动整个会场的必备前提。&lt;/p&gt;

&lt;p&gt;注意，你也可以在听众中先建立一种紧迫感，然后再转向更高层次的要点，但一定要确保最后以积极因素结尾。&lt;/p&gt;

&lt;p&gt;通过练习，每次即兴演讲都要有要点，让要点自然的成为你演讲时的第二天性。当你讲话时，胡自然地想讲要点，这在与上级对话，或汇报时尤为重要，它们希望你说话清晰、切中要害，而不浪费他们的时间。&lt;/p&gt;

&lt;h2 id=&quot;即兴框架结构体&quot;&gt;即兴框架：结构体&lt;/h2&gt;

&lt;p&gt;一个合理的结构体，可以让你的讲话举重若轻，它可以让你更清楚地传达要点。&lt;/p&gt;

&lt;p&gt;（结构体组织模式）&lt;/p&gt;

&lt;p&gt;在讲具体内容是，最好用结构体的组织方式去讲话，这样会让讲话更有条理，清晰的表达我们的观点，支撑我们提出的要点。&lt;/p&gt;

&lt;p&gt;结构体组织模式：
1.原因模式，罗列各种原因和理由来支撑我们提出的要点。
2.方法模式，围绕提出的要点，我们可以罗列各种方法来支撑它。
3.情况模式，通过描述当前的状况和对应的实施措施来支撑要点。
4.时间模式，通过时间顺序来描述支撑要点的关键点。&lt;/p&gt;

&lt;p&gt;注意，我们在说完要点后一定要暂停，花一点时间思考后，再选择恰当的结构体模式。&lt;/p&gt;

&lt;p&gt;学习以上四种组织模式，构建讲话核心要点并加上标志词，如果你能做到这些，那么你的即兴演讲将非常有说服力。&lt;/p&gt;

&lt;h2 id=&quot;即兴框架抓手&quot;&gt;即兴框架：抓手&lt;/h2&gt;

&lt;p&gt;我们必须思考如何讲话才能在争夺注意力中胜出，因为“获得整个会场”的注意力是很难的，甚至获得一个人的注意力也很难，如今社会有太多信息干扰我们的注意力，包括智能手机、电子邮件、主流话题、八卦信息等，所以我们更应该仔细思考这个部分。&lt;/p&gt;

&lt;p&gt;也就是说只有抓住大家的注意力，才能让我们的演讲放到最大的效果。&lt;/p&gt;

&lt;p&gt;(图：抓手关键点)&lt;/p&gt;

&lt;h3 id=&quot;把抓手部分看作是一种口头的握手会更好它让你与听众建立联结使听众想要跟随你一旦与听众联结你就可以讲述要点部分而你也将获得听众的注意力并发挥领导力作用&quot;&gt;把抓手部分看作是一种“口头的握手”会更好，它让你与听众建立联结，使听众想要跟随你。一旦与听众联结，你就可以讲述要点部分，而你也将获得听众的注意力并发挥领导力作用。&lt;/h3&gt;

&lt;p&gt;抓手的关键点有三个：
1.抓住听众注意力
2.与听众建立联结
3.避免偏离主题&lt;/p&gt;

&lt;p&gt;可以通过，直呼名称、说关于他们的事情、讲他们曾经的观点、提出与他们的某次对话、询问他们的情况等方式来抓住听众的注意力。&lt;/p&gt;

&lt;h3 id=&quot;总之与听众有关的事情才能将听众的注意力转移到你这里&quot;&gt;总之，与听众有关的事情，才能将听众的注意力转移到你这里。&lt;/h3&gt;

&lt;p&gt;同时，为了能更好的与听众链接，我们需要讲听众感兴趣的事，找到与他们的共同点和共同利益，建立积极、正面、有建设性的对话，来建立与听众的链接。&lt;/p&gt;

&lt;h3 id=&quot;总之通过找对方的兴趣点和彼此共同利益点才能触及听众的内心通过触及内心来建立更好的链接&quot;&gt;总之，通过找对方的兴趣点和彼此共同利益点，才能触及听众的内心，通过触及内心来建立更好的链接。&lt;/h3&gt;

&lt;p&gt;最后对话中避免抵触和反抗，或者让对话时间过长，以及说些无关紧要的话题。&lt;/p&gt;

&lt;h2 id=&quot;即兴框架以呼吁行动结束&quot;&gt;即兴框架：以呼吁行动结束&lt;/h2&gt;

&lt;p&gt;呼吁行动通常要求听众采取行动，一旦后续行动发生，你将实现你的领导力。&lt;/p&gt;

&lt;p&gt;（图：呼吁行动分解）&lt;/p&gt;

&lt;p&gt;即兴演讲的收尾特别重要，我们人能记忆的东西很少，对于一件不是特别重要的事情来说，我们只会记住开头和结尾。&lt;/p&gt;

&lt;p&gt;如果开头和结尾是非常愉悦的，即使中间过程比较枯燥乏味，在人们的心中都是一场还不错的演讲和对话。&lt;/p&gt;

&lt;p&gt;这里罗列下如何收尾的方法：&lt;/p&gt;

&lt;p&gt;1.将话语权交给对方
2.要求做出最终决定
3.介绍具体步骤
4.鼓励他人
5.传达最后通牒&lt;/p&gt;

&lt;p&gt;根据现场实际和即兴的内容来选择收尾会更好一些，特别是在结构体讲完后，应该给自己暂停一下，流一点时间来思考该用什么方式收尾。&lt;/p&gt;

&lt;h2 id=&quot;了解听众&quot;&gt;了解听众&lt;/h2&gt;

&lt;p&gt;当我们了解听众时会让即兴演讲更成功。&lt;/p&gt;

&lt;p&gt;（图：如何了解听众）&lt;/p&gt;

&lt;p&gt;听众分析包括：&lt;/p&gt;

&lt;p&gt;1.事前分析：&lt;/p&gt;

&lt;p&gt;提前了解听众会让你意识到可能存在与你想法完全不同的人，通过事先分析听众可以知道我们会遇到哪些问题，该如何应对。&lt;/p&gt;

&lt;p&gt;听众们关心什么事，把自己的观点建立在他们的想法上，这样才能有一个更稳固的协作式讨论。&lt;/p&gt;

&lt;p&gt;对方背景（组织）文化是如何，文化冲突有哪些，建立在尊重互利的基础上，会让即兴更加舒适。&lt;/p&gt;

&lt;p&gt;2.演讲中调整：&lt;/p&gt;

&lt;p&gt;现在观众的注意力如何&lt;/p&gt;

&lt;p&gt;哪些观点有吸引力可以把听众的注意力拉回来&lt;/p&gt;

&lt;p&gt;哪些听众头脑反应比较迅速是关键决策者，可以去征求他的意见，让他主动发言。&lt;/p&gt;

&lt;h3 id=&quot;最后在讲话结束后要对自己的即兴演讲做一个复盘这是我们提高演讲技能有效快速的方法&quot;&gt;最后，在讲话结束后，要对自己的即兴演讲做一个复盘，这是我们提高演讲技能有效快速的方法。&lt;/h3&gt;

&lt;h2 id=&quot;关于生活中的即兴谈话&quot;&gt;关于生活中的即兴谈话&lt;/h2&gt;

&lt;p&gt;在生活中交谈时尽量找到自己谈话的目标，否则将会成为一场空前浪费时间的闲聊。&lt;/p&gt;

&lt;p&gt;你可以这样想，当人们转身离开时，你想让他们记住关于你的什么信息？&lt;/p&gt;

&lt;p&gt;只要带着这个问题去思考，你将会让每次即兴谈话都畅快淋漓。&lt;/p&gt;

&lt;h3 id=&quot;一位副总裁这样说我发现闲谈非常耗费精力除非是有目的的聊天如果毫无准备就交谈我就会觉得自己迫不及待地想结束谈话但是如果事先准备了谈话就会变得很顺利&quot;&gt;一位副总裁这样说：“我发现闲谈非常耗费精力，除非是有目的的聊天。如果毫无准备就交谈，我就会觉得自己迫不及待地想结束谈话。但是如果事先准备了，谈话就会变得很顺利。”&lt;/h3&gt;

&lt;p&gt;优秀的管理者会问自己，“我想让他们从这次谈话中记住什么？”，或者“人们为什么和我做生意”。&lt;/p&gt;

&lt;p&gt;让我们一起为每一场即兴谈话成为更有效的沟通。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>给女儿写信(二十) 观察自己的情绪</title>
   <link href="http://www.luzexi.com/2022/01/03/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A120"/>
   <updated>2022-01-03T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2022/01/03/给女儿的信20</id>
   <content type="html">
&lt;hr /&gt;

&lt;p&gt;导读：喜欢跟孩子们一起，可惜一直在外地工作，因此我给孩子写点信并录成视频，跟孩子聊聊自己的故事以及最近的感受&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;要点&quot;&gt;要点：&lt;/h2&gt;

&lt;p&gt;用跟自己说说话和闭上眼睛数数的方式，观察自己的情绪，同时化解了自己的情绪&lt;/p&gt;

&lt;h2 id=&quot;结构&quot;&gt;结构：&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;遇到困难的时候心里很难受&lt;/li&gt;
  &lt;li&gt;当遇到枯燥乏味的工作时心里很苦闷&lt;/li&gt;
  &lt;li&gt;爸爸想解决这些困难，但就是提不起劲&lt;/li&gt;
  &lt;li&gt;于是爸爸闭上眼睛观察自己&lt;/li&gt;
  &lt;li&gt;爸爸观察自己的情绪是怎样的&lt;/li&gt;
  &lt;li&gt;是难受还是紧张&lt;/li&gt;
  &lt;li&gt;难受是怎么个难受法，是脑袋嗡嗡的吗，还是整个人紧绷的&lt;/li&gt;
  &lt;li&gt;紧张是怎样的表现，是肌肉很痛吗，还是腿和胳膊在抖动&lt;/li&gt;
  &lt;li&gt;细致的观察自己的在难受和紧张时的身体状况&lt;/li&gt;
  &lt;li&gt;慢慢发现自己的难受和紧张被化解了&lt;/li&gt;
  &lt;li&gt;当我发现，原来紧张和难受时，脑袋时这样的感觉，肌肉时这样在抖动&lt;/li&gt;
  &lt;li&gt;难受和紧张就慢慢消失了，神奇吗&lt;/li&gt;
  &lt;li&gt;原来难受和紧张只是我们想当然的感受，其根本原因是因为肌肉在收缩&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;内容&quot;&gt;内容：&lt;/h2&gt;

&lt;p&gt;Hello 秀恩，Hello Anne。爸爸好想你们爸爸喜欢你们啊，爱你们哟。&lt;/p&gt;

&lt;p&gt;爸爸在深圳又过了2周多的时间了，这里的天气比以往更冷一些。&lt;/p&gt;

&lt;p&gt;爸爸给你们讲一讲爸爸最近的状况好不好？&lt;/p&gt;

&lt;p&gt;最近爸爸遇到一些困难，工作上的和学习上的困难都有。爸爸心里感觉到很难受。&lt;/p&gt;

&lt;p&gt;爸爸发现每次当遇到枯燥的乏味的工作时，心里就很郁闷。除了工作呢，有时候自己想要学习，但是又提不起劲的时候，心里也很难受。&lt;/p&gt;

&lt;p&gt;有时候爸爸觉得自己心里很浮躁，心情很急躁，就是看不进书，写不进东西，心里安静不下来。所以爸爸想着该怎么办呢？怎么解决这个心里难受情绪混乱的状态呢？&lt;/p&gt;

&lt;p&gt;爸爸想了两个办法，第1个办法是跟自己说说话，爸爸每天抽15分钟到20分钟的时间来跟自己说说话，每天跟自己说说今天我遇到了什么困难，我的感受怎样。然后跟自己说说，我应该怎么去解决这个困难，跟自己说说我应该怎样去调整自己的情绪。&lt;/p&gt;

&lt;p&gt;你知道吗？爸爸跟自己说着说着，自己的情绪就真的慢慢好起来了，不那么难受，也不那么急躁了，也不紧张了。所以爸爸觉得跟自己说说话，这个办法非常棒。&lt;/p&gt;

&lt;p&gt;跟自己说说话，不但练习了口头的表达能力和沟通能力，并且还化解了自己的情绪，爸爸觉得这个方法非常非常棒。所以呢，爸爸每天都在坚持跟自己说说话，像跟秀恩聊天一样跟自己说说话。&lt;/p&gt;

&lt;p&gt;第2个办法是。当爸爸感觉到紧张、难受、急躁的时候，就坐下来闭上眼睛去数数，数数的时候，我会观察自己。&lt;/p&gt;

&lt;p&gt;观察自己的哪里难受，哪里紧张，是哪块肌肉很难受，是紧张还是疼痛？细致的观察自己。&lt;/p&gt;

&lt;p&gt;爸爸就这样去观察自己的身体，在这种紧张和难受的状态下，肌肉是怎么收缩和抖动的？&lt;/p&gt;

&lt;p&gt;慢慢地观察着观察着，爸爸发现自己的情绪开始化解了，爸爸发现自己没有这么难受和紧张了，原来只要爸爸闭上眼睛专注地观察他们，观察肌肉的疼痛和收缩，观察脑袋的紧绷，观察整个人的状态的时候，难受和紧张就慢慢的消失了。这种感觉太棒了。就感觉自己重新平静了下来，无论是生气还是难受，情绪都被化解了。&lt;/p&gt;

&lt;p&gt;爸发现用这两种调节情绪的办法非常棒。&lt;/p&gt;

&lt;p&gt;爸爸现在常用这种方式去化解自己的情绪，那些痛苦啊、难受啊、紧张啊、焦虑啊、急躁啊，都可以通过这样的方式去把它化解。&lt;/p&gt;

&lt;p&gt;太棒了，爸爸每天都要坚持跟自己说说，观察自己的状态和身体的变化，化解掉自己的情绪。&lt;/p&gt;

&lt;p&gt;今天的写信就到这里喽，宝宝们喜欢你们，爸爸爱你们哟。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>新书发售《Unity3D高级编程-主程手记》</title>
   <link href="http://www.luzexi.com/2021/12/29/%E6%96%B0%E4%B9%A6%E5%8F%91%E5%94%AE"/>
   <updated>2021-12-29T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/12/29/新书发售</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485108&amp;amp;idx=1&amp;amp;sn=b949ce0d6f031a2108586940aa0eadfa&amp;amp;chksm=fc2263b3cb55eaa5b5c3fe41399d47d7f1e0f66ce5b130c0e31648be035e9535f5c6c3d5ed39&amp;amp;token=330432434&amp;amp;lang=zh_CN#rd&quot;&gt;已发布微信公众号&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/fengmian.jpg&quot; alt=&quot;封面&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;新书发售了感恩朋友们的厚爱&quot;&gt;新书发售了，感恩朋友们的厚爱。&lt;/h3&gt;

&lt;p&gt;这么多年读书与写作给我最大的体会是，&lt;/p&gt;

&lt;p&gt;静下心来保持专注，是最有效率的。&lt;/p&gt;

&lt;p&gt;希望自己继续保持这份专注，并用最真诚的方式对待朋友们。&lt;/p&gt;

&lt;h3 id=&quot;本书共10章每章都是一个独立的知识领域读者可以按照章节顺序阅读本书也可以根据喜好挑选自己感兴趣的章节学习&quot;&gt;本书共10章，每章都是一个独立的知识领域，读者可以按照章节顺序阅读本书，也可以根据喜好挑选自己感兴趣的章节学习。&lt;/h3&gt;

&lt;p&gt;第1章讲了架构的意义、架构的原理以及如何实现架构。&lt;/p&gt;

&lt;p&gt;第2章对C#技术的基础知识做了讲解。&lt;/p&gt;

&lt;p&gt;第3章针对客户端中的表格数据、程序的协作与应用进行讲解。&lt;/p&gt;

&lt;p&gt;第4章讲解了用户界面（UI）的工作原理与优化方法。&lt;/p&gt;

&lt;p&gt;第5章针对3D模型的原理、动画的原理以及两者的优化做了详细的讲解。&lt;/p&gt;

&lt;p&gt;第6章讲解的是网络层的业务架构与底层原理。&lt;/p&gt;

&lt;p&gt;第7章针对各类AI做了详细的讲解。&lt;/p&gt;

&lt;p&gt;第8章主要讲解场景构建与优化、地图构建以及寻路算法优化的相关知识。&lt;/p&gt;

&lt;p&gt;第9章讲解了图形数学、图形学常用算法、渲染管线的相关知识。&lt;/p&gt;

&lt;p&gt;第10章针对客户端各类渲染技术的渲染原理做了详细的解剖。&lt;/p&gt;

&lt;h3 id=&quot;此书非常适合那些上进积极热爱技术的伙伴们&quot;&gt;此书非常适合那些上进、积极、热爱技术的伙伴们。&lt;/h3&gt;

&lt;p&gt;如果你想更上一层楼，想了解主程需要做什么，主程需要哪方面的技术深度，就来看看这本书。&lt;/p&gt;

&lt;p&gt;整本书字里行间都透露着对游戏开发的深度理解，以及对主程工作的感悟。&lt;/p&gt;

&lt;h3 id=&quot;20211228预售开始了后面的日子里会陆续上架各大平台包括新华书店机械工业出版社平台以及当当网&quot;&gt;2021.12.28，预售开始了，后面的日子里会陆续上架各大平台，包括新华书店、机械工业出版社平台、以及当当网。&lt;/h3&gt;

&lt;h2 id=&quot;感恩遇见&quot;&gt;感恩遇见&lt;/h2&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/shudian1.jpg&quot; alt=&quot;淘宝预售&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/shudian3.jpg&quot; alt=&quot;京东预售&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/shudian2.jpg&quot; alt=&quot;淘宝搜索&quot; /&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>游戏引擎架构#4 低阶渲染器（4）</title>
   <link href="http://www.luzexi.com/2021/12/12/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B051"/>
   <updated>2021-12-12T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/12/12/读书笔记51</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485096&amp;amp;idx=1&amp;amp;sn=c027b6af4677515ba1e5950aac8ba68d&amp;amp;chksm=fc2263afcb55eab9204898b39fb77ee04f5767ac450507b6192a5c6d5612a45b6d29b1bcee16&amp;amp;token=853743270&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;背景&quot;&gt;背景：&lt;/h1&gt;

&lt;p&gt;作为游戏开发从业者，从业务到语言到框架到引擎，积累了一些知识和经验，特别是在看了几遍《游戏引擎架构》后对引擎架构的理解又深入了些。&lt;/p&gt;

&lt;p&gt;近段时间有对引擎剖析的想法，正好借这书本对游戏引擎架构做一个完整分析。&lt;/p&gt;

&lt;p&gt;此书用简明、清楚的方式覆盖了游戏引擎架构的庞大领域，巧妙地平衡了广度与深度，并且提供了足够的细节。&lt;/p&gt;

&lt;p&gt;借助《游戏引擎架构》这本书、结合引擎源码和自己的经验，深入分析游戏引擎的历史、架构、模块，最后通过实践简单引擎开发来完成对引擎知识的掌握。&lt;/p&gt;

&lt;p&gt;游戏引擎知识面深而广，所以对这系列的文章书编写范围做个保护，即不对细节进行过多的阐述，重点剖析的是架构、流程以及模块的运作原理。&lt;/p&gt;

&lt;p&gt;同时《游戏引擎架构》中部分知识太过陈旧的部分，会重新深挖后总结出自己的观点。&lt;/p&gt;

&lt;h1 id=&quot;概述&quot;&gt;概述：&lt;/h1&gt;

&lt;p&gt;本系列文章对引擎中的重要的模块和库进行详细的分析，我挑选了十五个库和模块来分析：&lt;/p&gt;

&lt;p&gt;1.时间库
2.自定义容器库
3.字符串散列库
4.内存管理框架
5.RTTI与反射模块
6.图形计算库
7.资产管理模块
8.低阶渲染器
9.剔除与合批模块
10.动画模块
11.物理模块
12.UI底层框架
13.性能剖析器的核心部分
14.脚本系统
15.视觉效果模块&lt;/p&gt;

&lt;p&gt;本篇内容为列表中的第8个部分的第1节。&lt;/p&gt;

&lt;h1 id=&quot;正文&quot;&gt;正文：&lt;/h1&gt;

&lt;p&gt;简单回顾下前文&lt;/p&gt;

&lt;p&gt;前文我们聊了下显卡在计算机硬件主板中的位置与结构，知道了CPU、GPU的通信介质，并简单介绍了手机上的主板结构。本篇开头对上一篇做一些内容补充，PC和手机的不同硬件组织，以及CPU与其他芯片的通信过程。&lt;/p&gt;

&lt;p&gt;下面我们开始这篇内容&lt;/p&gt;

&lt;p&gt;本次内容会围绕GPU来写，从硬件架构到软件驱动再到引擎架构，目标是帮大家理解GPU硬件的运作原理，理解图形接口的架构，理解引擎低阶渲染器的架构。&lt;/p&gt;

&lt;p&gt;目录：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;主板结构中的显卡&lt;/li&gt;
  &lt;li&gt;GPU功能发展史&lt;/li&gt;
  &lt;li&gt;GPU与CPU的差异&lt;/li&gt;
  &lt;li&gt;GPU硬件特点&lt;/li&gt;
  &lt;li&gt;图形驱动程序架构&lt;/li&gt;
  &lt;li&gt;引擎低阶渲染架构&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;内容结构：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;CPU硬件结构&lt;/li&gt;
  &lt;li&gt;GPU硬件结构&lt;/li&gt;
  &lt;li&gt;GPU手机管线与PC管线的差异&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;接着上篇的内容。前面说了CPU、GPU的硬件结构，CPU的构造和GPU的构造，下面我们来聊聊GPU是如何工作的，以及GPU的管线在手机端和PC端的差异。&lt;/p&gt;

&lt;h2 id=&quot;nvidia基于fermi管线的架构&quot;&gt;NVIDIA基于Fermi管线的架构&lt;/h2&gt;

&lt;p&gt;关于GPU的逻辑管线，这篇Nvidia这篇文章《Life of a triangle - NVIDIA’s logical pipeline》说的很清楚。&lt;/p&gt;

&lt;p&gt;（NVIDIA的整体架构图）&lt;/p&gt;

&lt;p&gt;下面我以此为标准进行翻译并重新剖析。&lt;/p&gt;

&lt;p&gt;为了简单起见，省略了几个细节，假设 drawcall 引用了一些已经充满数据并存在于 GPU DRAM 中的索引和顶点缓冲区，并且仅使用顶点和像素着色器（GL：片段着色器）。&lt;/p&gt;

&lt;p&gt;（从图形API调用到图元处理过程图）&lt;/p&gt;

&lt;p&gt;1.引擎或业务程序调用图形 API（DX 或 GL）中的绘图函数，接着驱动程序会被调用，驱动程序会进行一些验证以检查参数是否“合法”，再将指令写入到GPU可读写的缓冲队列中。在这个地方 CPU 方面可能会出现很多瓶颈，这也是为什么程序员要好好使用 API 以利用当今 GPU 的强大功能的技术很重要的原因。&lt;/p&gt;

&lt;p&gt;（绘制接口调用图）&lt;/p&gt;

&lt;p&gt;2.经过一段时间渲染，画面“刷新”被调用，驱动程序在缓冲区中已经缓冲了足够多的工作命令，接着将其发送给 GPU 进行处理（操作系统会参与）。最后 GPU 的主机接口接收命令并交给GPU前端的处理。&lt;/p&gt;

&lt;p&gt;（绘制队列与刷新图）&lt;/p&gt;

&lt;p&gt;3.接着图元分配器（Primitive Distributor）开始分配工作。为了批量处理索引和三角形，将数据发送给多个图形处理集群（GPC）并行处理。&lt;/p&gt;

&lt;p&gt;（SM整体结构图）&lt;/p&gt;

&lt;p&gt;4.在 GPC 中，每个 SM 的 Poly Morph 引擎负责从三角形索引中获取顶点数据(Vertex Fetch)。&lt;/p&gt;

&lt;p&gt;5.在获取数据后，SM中每32个线程为一捆线程束（Warp），它们被调度去处理这些顶点工作。 线程束（Warp）是典型的单指令多线程（SIMT，SIMD单指令多数据的升级）的实现，也就是32个线程同时执行的指令是一模一样的，只是线程数据不一样，这样的好处就是一个Warp只需要一套逻辑对指令进行解码和执行就可以了，芯片可以做的更小更快。&lt;/p&gt;

&lt;p&gt;（线程束与线程束调度器图）&lt;/p&gt;

&lt;p&gt;6.SM的线程束（Warp）调度器会按照顺序分发指令给整个线程束（Warp），单个线程束（Warp）中的线程会锁步(lock-step)执行各自的指令。线程束（Warp）会使用SIMT的方式来做分支预测，每个线程执行的分支会不同，当线程遇到到错误判断的执行情况会被遮蔽(be masked out)。&lt;/p&gt;

&lt;p&gt;（单个GPU线程与存储设备的关系图）&lt;/p&gt;

&lt;p&gt;被遮蔽的原因是SIMT执行中错误预测，例如当前的指令是if(true)的分支，但是当前线程的数据的条件是false，或者循环的次数不一样（比如for循环次数n不是常量，或被break提前终止了但是别的还在走），因此在Shader中的分支会显著增加时间消耗，在一个线程束（Warp）中的分支除非32个线程都走到同一个里面，否则相当于所有的分支都走了一遍，线程不能独立执行指令而是以线程束（Warp）为单位，而这些线程束中的线程之间才是相互独立的。&lt;/p&gt;

&lt;p&gt;（SIMT线程束做分支预测图）&lt;/p&gt;

&lt;p&gt;7、线程束（Warp）中的指令可以被一次完成，也可能经过多次调度，例如通常SM中的LD/ST(加载存取)单元数量明显少于基础数学操作单元。&lt;/p&gt;

&lt;p&gt;8、由于某些指令比其他指令需要更长的时间才能完成，特别是内存加载，线程束（Warp）调度器可能会简单地切换到另一个没有内存等待的线程束（Warp），这是GPU如何克服内存读取延迟的关键，其操作为简单地切换活动线程组。为了使这种切换更快，调度器管理的所有线程束（Warp）在寄存器列阵（Register File）中都有自己的寄存器。这里就会有个矛盾产生，Shader需要的寄存器越多，给线程束（Warp）留下的空间就越少，于是就会导致能用的线程束（Warp）就越少。此时如果碰到指令在内存获取数据等待就只会等待，而没有其他可以运行的线程束（Warp）可以切换。&lt;/p&gt;

&lt;p&gt;（线程与寄存器列阵关系图）&lt;/p&gt;

&lt;p&gt;（线程束调度器调度线程图）&lt;/p&gt;

&lt;p&gt;9、一旦线程束（Warp）完成了顶点着色器（vertex-shader）的所有指令，运算结果会被Viewport Transform模块处理，三角形会被裁剪然后准备光栅化，此时GPU会使用L1和L2缓存来进行顶点着色起（vertex-shader）和片元着色起（pixel-shader）的数据通信。&lt;/p&gt;

&lt;p&gt;（管线中节点、数据、存储器的关系图）&lt;/p&gt;

&lt;p&gt;10、接下来这些三角形将被分割，通过 Work Distribution Crossbar 将三角形再分配给多个GPC，三角形的范围决定着它将被分配到哪个光栅引擎(raster engines)，每个光栅引擎（raster engines）覆盖了多个屏幕上的图块（tile），这等于把三角形的渲染分配到多个图块（tile）上面。也就是说在像素阶段前就把三角形划分成了方块格式范围，三角形处理分成许多较小的工作。&lt;/p&gt;

&lt;p&gt;（三角形被拆分成多个块派发到多个光栅引擎图）&lt;/p&gt;

&lt;p&gt;（图块拆分任务派发图）&lt;/p&gt;

&lt;p&gt;11、SM上的属性安装器（Attribute Setup）保证了从顶点着色器（vertex-shader）生成的数据，经过插值后，在片元着色器（pixel-shade）上是可读的。&lt;/p&gt;

&lt;p&gt;12、GPC上的光栅引擎(raster engines)处理它接收到的三角形，并为它负责的那些部分生成像素信息（同时会处理裁剪Clipping、背面剔除和Early-Z剔除）。&lt;/p&gt;

&lt;p&gt;13、再次做批量处理，32个像素线程被分成一组（或者说8个2x2的像素块），这是在像素着色器上面的最小工作单元（2x2 四边形允许我们计算诸如纹理 mip 贴图过滤之类的导数–四边形内纹理坐标的大变化会导致更高的 mip）。在这个像素线程内，如果没有被任何三角形覆盖就会被剔除。SM中的线程束（Warp）调度器会管理像素着色器的任务。&lt;/p&gt;

&lt;p&gt;（2x2像素组传入到线程束处理像素着色器的图）&lt;/p&gt;

&lt;p&gt;14、接下来是同样的线程束调度策略，和顶点着色器（vertex-shader）中的逻辑步骤完全一样，但是变成了在像素着色器线程中执行。 由于不耗费任何性能从2x2四边形中获取一个像素，这使得锁步执行非常便利，于是所有的线程可以保证指令可以在同一点同步执行。&lt;/p&gt;

&lt;p&gt;（线程束锁步执行图）&lt;/p&gt;

&lt;p&gt;15、最后一步，现在像素着色器已经完成了颜色的计算和深度值的计算。在这个点上，我们必须考虑三角形的调用API顺序，然后才将数据移交给ROP(render output unit，渲染输出单元)，一个ROP内部有很多ROP单元，在ROP单元中处理深度测试和帧缓冲（framebuffer）的混合等，深度和颜色的设置必须是原子操作，否则两个不同的三角形在同一个像素点就会有冲突和错误。NVIDIA 通常应用内存压缩，以减少内存带宽要求，从而增加“有效”带宽。&lt;/p&gt;

&lt;p&gt;（像素着色器后的像素处理过程图）&lt;/p&gt;

&lt;p&gt;以上这些信息有助于我们理解 GPU 中的一些工作和数据流，还可以帮助我们理解CPU与GPU之间的交互。&lt;/p&gt;

&lt;h2 id=&quot;总结cpu和gpu的交互&quot;&gt;总结CPU和GPU的交互&lt;/h2&gt;

&lt;p&gt;GPU是设备，设备都有驱动，CPU可以直接执行二进制指令集，对于GPU设备，图形接口有opengl，directx标准及库封装，计算有cuda和opencl封装。程序代码调用这些图形或计算库，这些库调用驱动，驱动再来对接操作GPU设备，CPU与GPU直接的通信是遵循总线和内存的规则。&lt;/p&gt;

&lt;p&gt;原则上CPU、内存外的设备都属于IO设备，通过总线连上来，它们必须遵守IO总线规范，如显卡就走pcie总线，这里还有ionmu，统一内存等，来共享资源，缩短路径，提升效率等。&lt;/p&gt;

&lt;p&gt;这里专门说下驱动，计算机有专门的程序接口指定一个计算任务到GPU上，这个接口程序就是驱动程序。CPU给GPU下发任务时通过调用驱动程序，不同GPU厂商实现自己的驱动，并且提供了各种的编程接口。图形计算上实现了OpenGL标准接口规范的图形库，它会调用各厂商的驱动，用户可以通过GLSL编写计算任务进行通用计算。后来的CUDA编程模型专门推出用于编写通用计算任务的接口，于是OpenGL就专门用于图形渲染了。而CUDA则是通过kenel函数来编写计算任务，通过cudaLaunch接口来下发任务。&lt;/p&gt;

&lt;p&gt;（从图形库到驱动到GPU指令队列的图）&lt;/p&gt;

&lt;p&gt;从硬件角度看：&lt;/p&gt;

&lt;p&gt;（从硬件角度看指令和数据处理流程图）&lt;/p&gt;

&lt;p&gt;1.GPU设备的配置空间物理地址映射到虚拟地址，可以被程序直接访问；同时建立任务队列缓冲，声明中断等等；&lt;/p&gt;

&lt;p&gt;2.CPU在进程内准备数据和缓冲，基于虚拟地址VA、VM将其转换为显存的物理地址IPA。驱动程序获取任务，再将任务信息填充至任务队列内。&lt;/p&gt;

&lt;p&gt;3.根据虚拟内存绑定的地址信息，将任务队列的指针更新至GPU设备侧，这个端口称为doorbell寄存器；&lt;/p&gt;

&lt;p&gt;4.设备接收到doorbell操作，会触发中断，再读取主存中的任务队列，包括队列内的信息和其指向的任务数据，GPU设备侧读取该数据。&lt;/p&gt;

&lt;p&gt;5.完成后，再将数据发送给CPU侧。一般来说，GPU设备侧发送至CPU的读写请求使用的是虚拟地址，由CPU的IOMMU或SMMU转换为物理地址。&lt;/p&gt;

&lt;h2 id=&quot;gpu手机管线与pc管线的差异&quot;&gt;GPU手机管线与PC管线的差异&lt;/h2&gt;

&lt;p&gt;为什么要了解手机与PC管线的差异？
PC的能耗和发热比手机端可以更大一些，因此PC与手机在硬件架构上有天然的不同，进而使得它们在GPU管线上也有很大的差异，这使得我们在优化手机端时必须了解这种差异再做针对性的做优化。&lt;/p&gt;

&lt;h2 id=&quot;tbdrtile-base-deffered-rendering是现代移动端gpu的设计架构它同传统pc上irimmediate-rendering架构的gpu在硬件设计上有很大的差别&quot;&gt;TBDR(Tile-Base-Deffered-Rendering)是现代移动端GPU的设计架构，它同传统PC上IR（Immediate-Rendering）架构的GPU在硬件设计上有很大的差别。&lt;/h2&gt;

&lt;p&gt;为什么呢？因为功耗是Mobile设备设计的第一考虑因素，而带宽是功耗的第一杀手。&lt;/p&gt;

&lt;p&gt;我们来看PC的GPU管线，即传统的IR（Immediate-Rendering）模式：&lt;/p&gt;

&lt;p&gt;（IMR管线图：源自网络）&lt;/p&gt;

&lt;p&gt;IMR（Immediate Mode Rendering）模式中，GPU直接在主存或显存上读写深度缓存（Depth Buffer）和帧缓存（Frame Buffer），这导致带宽消耗很大，如果在手机上耗电和发热都无法承受。&lt;/p&gt;

&lt;p&gt;手机使用统一内存架构，CPU和GPU都通过总线来访问主存。GPU需要获取三角形数据（Geometry Data）、贴图数据（Texture Data）以及帧缓存（Frame Buffer），它们都在主存中。如果GPU直接从主存频繁地访问这些数据，就会导致带宽消耗大，成为性能瓶颈。&lt;/p&gt;

&lt;h2 id=&quot;tbrtile-based-rendering管线&quot;&gt;TBR（Tile-Based-Rendering）管线&lt;/h2&gt;

&lt;p&gt;基于以上所述原因，手机GPU使用自己的缓存区（SRAM），例如On-Chip深度缓存（On-Chip Depth Buffer）和On-Chip颜色缓存（On-Chip Color Buffer），它们与存取主存相比，速度更快，功耗更低。但它们的存储空间很小。（SRAM不需要充电来保持存储记忆，因此SRAM的读写基本不耗电，缺点是价格昂贵）&lt;/p&gt;

&lt;p&gt;如果手机直接读写帧缓存（Frame Buffer）就相当于让一辆火车在你家和公司之间来回奔跑，非常耗电。于是手机端想要拆分绘制内容，每次只绘制一小部分，再把所有绘制完成的部分拼起来。&lt;/p&gt;

&lt;p&gt;把帧缓存（Frame Buffer）拆分成很多个小块，使得每个小块可以被GPU附近的SRAM容纳，块的多少取决于GPU硬件的SRAM大小。这样GPU就可以分批的一块块的在SRAM上读写帧缓存（Frame Buffer），一整块都读写完毕后，再整体转移回主存上。&lt;/p&gt;

&lt;p&gt;这种模式就叫做TBR（Tile-Based-Rendering），整体管线如下图：&lt;/p&gt;

&lt;p&gt;（TBR管线图：源自网络）&lt;/p&gt;

&lt;p&gt;屏幕分块后的大小一般为16x16或32x32像素 ，在几何阶段之后再执行分块(Tiling)，接着将各个块（Tile）逐个光栅化，最后写入帧缓存中（Frame Buffer）中 。&lt;/p&gt;

&lt;p&gt;这里有一些细节要注意，TBR在接受每个指令（CommandBuffer）时并不立即绘制，而是先对这些数据做顶点处理，把顶点处理的结果暂时保存在主存上，等到非得刷新整个帧缓存时，才真正的用这批数据做光栅化。&lt;/p&gt;

&lt;p&gt;因此，TBR的管线实际可以认为被切分成两部分，前半部分为顶点数据部分，后半部分为片元数据部分：&lt;/p&gt;

&lt;p&gt;（TBR把管线切分为光栅化前和光栅化后）&lt;/p&gt;

&lt;h3 id=&quot;顶点数据先被处理并存储在frame-data中等到必须刷新时例如帧缓存置换调用glflush调用glfinish调用glreadpixels读取帧缓存像素时调用glcopytexiamge拷贝贴图时调用glbitframebuffer获取帧缓存时调用queryingocclusion解绑帧缓存时等等才被集中的拿去处理光栅化&quot;&gt;顶点数据先被处理并存储在Frame Data中，等到必须刷新时（例如帧缓存置换，调用glflush，调用glfinish，调用glreadpixels读取帧缓存像素时，调用glcopytexiamge拷贝贴图时，调用glbitframebuffer获取帧缓存时，调用queryingocclusion，解绑帧缓存时等等）才被集中的拿去处理光栅化。&lt;/h3&gt;

&lt;h3 id=&quot;那么为什么pc不使用tbr呢&quot;&gt;那么为什么PC不使用TBR呢？&lt;/h3&gt;

&lt;p&gt;实际上直接对主存或显存（这里也有多级缓存）进行整块数据的读写速度是最快的，而TBR需要一块块的绘制，然后再回拷给主存。可以简单的认为TBR牺牲了执行效率，换来了相对更难解决的带宽功耗。如果哪一天手机上解决了带宽的功耗问题，或者说SRAM足够大了，可能就没有TBR了。&lt;/p&gt;

&lt;h2 id=&quot;tbdrtile-based-deferred-rendering管线&quot;&gt;TBDR（Tile-based deferred rendering）管线&lt;/h2&gt;

&lt;p&gt;TBR会把顶点数据处理完毕后存储在Frame Data中，那么就会有很多厂商针对Frame Data做优化。&lt;/p&gt;

&lt;p&gt;TBDR整体的管线图如下：&lt;/p&gt;

&lt;p&gt;（TBDR管线图：源自网络）&lt;/p&gt;

&lt;p&gt;我们看到相比TBR，TBDR在光栅化（Raster）后多了一个HSR（Hidden Surface Removal）处理，这部分处理主要剔除无需绘制的元素，减少重绘（Overdraw）数量（高通通过优化划分块（Tile）之后执行顶点着色器（Vertex Shader）之前的节点来达到此目的，称为LRZ）。例如提前对不透明像素做深度测试并剔除，剔除被模板裁剪掉的像素等等，总之它们不会进入到像素着色器阶段（Pixel Shader）。&lt;/p&gt;

&lt;p&gt;因此在TBDR上，不透明物体的排序没有太大意义，Early-Z这种策略也不存在IOS上。这些GPU硬件巧妙的利用TBR的Frame Data队列实现了一种延迟渲染，尽可能只渲染那些会最终影响帧缓存（Frame Buffer）的像素。&lt;/p&gt;

&lt;h3 id=&quot;tbdr和软件上的延迟渲染相比有什么区别呢&quot;&gt;TBDR和软件上的延迟渲染相比有什么区别呢？&lt;/h3&gt;

&lt;p&gt;软件层面的延迟渲染与TBDR不同。软件层面的延迟渲染是针对一个Drawcall，对于从后到前的不透明物体绘制是每次都要绘制的，而硬件层面的延迟渲染，处理的是一整批Drawcall，剔除这一整批Drawcall中不会绘制的像素最后再渲染。可以说现在大部分的移动端的GPU都使用TBDR架构。&lt;/p&gt;

&lt;h3 id=&quot;参考资料&quot;&gt;参考资料：&lt;/h3&gt;

&lt;p&gt;《How Shader Cores Work》
https://engineering.purdue.edu/~smidkiff/KKU/files/GPUIntro.pdf&lt;/p&gt;

&lt;p&gt;《CPU体系结构》
https://my.oschina.net/fileoptions/blog/1633021&lt;/p&gt;

&lt;p&gt;《深入理解CPU的分支预测(Branch Prediction)模型》
https://zhuanlan.zhihu.com/p/22469702&lt;/p&gt;

&lt;p&gt;《分析Unity在移动设备的GPU内存机制（iOS篇）》
https://www.jianshu.com/p/68b41a8d0b37&lt;/p&gt;

&lt;p&gt;《PC与Mobile硬件架构对比》
https://www.cnblogs.com/kekec/p/14487050.html&lt;/p&gt;

&lt;p&gt;《针对移动端TBDR架构GPU特性的渲染优化》
https://gameinstitute.qq.com/community/detail/123220&lt;/p&gt;

&lt;p&gt;《A look at the PowerVR graphics architecture: Tile-based rendering》
https://www.imaginationtech.com/blog/a-look-at-the-powervr-graphics-architecture-tile-based-rendering/&lt;/p&gt;

&lt;p&gt;《A look at the PowerVR graphics architecture: Deferred rendering》
https://www.imaginationtech.com/blog/the-dr-in-tbdr-deferred-rendering-in-rogue/&lt;/p&gt;

&lt;p&gt;《深入GPU硬件架构及运行机制》
https://www.cnblogs.com/timlly/p/11471507.html&lt;/p&gt;

&lt;p&gt;《深入浅出计算机组成原理》
https://time.geekbang.org/column/article/105401?code=7VZ-Md9oM7vSBSE6JyOgcoQhDWTOd-bz5CY8xqGx234%3D&lt;/p&gt;

&lt;p&gt;《Nvidia Geforce RTX-series is born》
https://www.fudzilla.com/reviews/47224-nvidia-geforce-rtx-series-is-born?start=2&lt;/p&gt;

&lt;p&gt;《渲染管线与GPU（Shading前置知识）》
https://zhuanlan.zhihu.com/p/336999443&lt;/p&gt;

&lt;p&gt;《剖析虚幻渲染体系（12）- 移动端专题Part 1（UE移动端渲染分析）》
https://www.cnblogs.com/timlly/p/15511402.html&lt;/p&gt;

&lt;p&gt;《tpc-texture-processing-cluster》
https://gputoaster.wordpress.com/2010/12/11/tpc-texture-processing-cluster/&lt;/p&gt;

&lt;p&gt;《Life of a triangle - NVIDIA’s logical pipeline》
https://developer.nvidia.com/content/life-triangle-nvidias-logical-pipeline&lt;/p&gt;

&lt;p&gt;《Rasterisation wiki》
https://en.wikipedia.org/wiki/Rasterisation&lt;/p&gt;

&lt;p&gt;《PolyMorph engine and Data Caches by Hilbert Hagedoorn》
https://www.guru3d.com/articles-pages/nvidia-gf100-(fermi)-technology-preview,3.html&lt;/p&gt;

&lt;p&gt;《NVIDIA GPU的一些解析》
https://zhuanlan.zhihu.com/p/258196004&lt;/p&gt;

&lt;p&gt;《tensor-core-performance-the-ultimate-guide》
https://developer.download.nvidia.cn/video/gputechconf/gtc/2019/presentation/s9926-tensor-core-performance-the-ultimate-guide.pdf&lt;/p&gt;

&lt;p&gt;《Understanding the Understanding the graphics pipeline》
https://www.seas.upenn.edu/~cis565/LECTURES/Lecture2%20New.pdf&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485096&amp;amp;idx=1&amp;amp;sn=c027b6af4677515ba1e5950aac8ba68d&amp;amp;chksm=fc2263afcb55eab9204898b39fb77ee04f5767ac450507b6192a5c6d5612a45b6d29b1bcee16&amp;amp;token=853743270&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(五十) 《如何精彩演讲》#2 克服演讲时的恐惧</title>
   <link href="http://www.luzexi.com/2021/12/05/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B049"/>
   <updated>2021-12-05T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/12/05/读书笔记49</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485068&amp;amp;idx=1&amp;amp;sn=464d36573e97c69aeab4ab996885ccee&amp;amp;chksm=fc22638bcb55ea9d3e4472e92a760b147dc2a816268e9d3a435842995723d3a02e9a26b0f4b5&amp;amp;token=1416441933&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;背景&quot;&gt;背景&lt;/h1&gt;

&lt;p&gt;不知不觉看完了6本关于演讲的主题，按推荐顺序排列分别是《高效演讲》、《演讲与口才》、《即兴演讲》、《关键对话》、《TED演讲的力量》、《说话的艺术》。&lt;/p&gt;

&lt;p&gt;我希望自己能够对演讲有一个系统性的学习。于是自己在平常的生活和工作中实践了一段时间，前前后后加起来有两个月时间。&lt;/p&gt;

&lt;p&gt;目标是，改善平常的工作、生活中表达的流畅和清晰度，能让人感觉到舒服让人感觉有活力。同时在公众演讲上，希望自己能够很好的呈现自己的所思所想以及所知所学。&lt;/p&gt;

&lt;p&gt;因此开启这个演讲学习系列的总结文章，我将对这6本书的内容加上我的实践经验做一次全面的系统性的总结。&lt;/p&gt;

&lt;h3 id=&quot;概述&quot;&gt;概述：&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;恐惧的原因&lt;/li&gt;
  &lt;li&gt;克服恐惧的原理&lt;/li&gt;
  &lt;li&gt;制定克服恐惧的行动&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;内容&quot;&gt;内容：&lt;/h3&gt;

&lt;p&gt;为什么要了解演讲时的恐惧？&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;只有了解恐惧的原理才能正确认识它&lt;/li&gt;
  &lt;li&gt;它会使得事情更加糟糕&lt;/li&gt;
  &lt;li&gt;它会对我们的心理产生长短期的变化&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;在平时的生活工作学习当中，当我们恐惧时很多时候并不能了解自己的恐惧情绪。由于在很多情况下，我们观察不到自己的情绪，导致我们常常无法在情绪有问题时，及时的调整它。&lt;/p&gt;

&lt;p&gt;因此你会看到很多时候，当出现了恐惧的症状时，我们没有调整它，这直接导致事情发展变得糟糕。&lt;/p&gt;

&lt;p&gt;（恐惧表情图：来源网络）&lt;/p&gt;

&lt;p&gt;不止如此，如果没有去很好的应对恐惧的话，长此以往我们在心里会有比较大的阴影。这使得，我们在一件比较重要的会议上，由于你的恐惧而退缩或退却，会使你在精神上遭受更大的打击。&lt;/p&gt;

&lt;p&gt;如果次数多了，你会发现你对这些重要的场所，重要的机会，会产生习惯性的恐惧，这常常使你对人生产生比较多的怀疑。&lt;/p&gt;

&lt;p&gt;因此无论从长期或短期来看，我们都需要去主动的了解恐惧。这是我们必须去了解恐惧的重要原因。它会将生活弄得更加糟糕，因此我们必须了解他，特别是在演讲时。&lt;/p&gt;

&lt;p&gt;恐惧有很多种，这里我们只关注演讲时的恐惧。但其实演讲的恐惧已经覆盖了生活中很多的方面，它与生活中的恐惧有很多共性。&lt;/p&gt;

&lt;h2 id=&quot;是什么让你恐惧&quot;&gt;是什么让你恐惧？&lt;/h2&gt;

&lt;p&gt;了解恐惧产生的原因，可以让我们对恐惧问题的形成有更深刻的认识。&lt;/p&gt;

&lt;p&gt;不管是普通焦虑还是惊恐障碍，都没有单一的成因，也不可能在消除这个成因后问题就得到彻底根除。
焦虑问题是有多方面，多种原因引起的，包括遗传因素，生物因素，家庭背景和教养方式、心理条件作用、近期生活转变、自我对话和个人信念体系、表达情感的能力、当前环境压力等。&lt;/p&gt;

&lt;p&gt;恐惧的原因：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;面对重要场面，准备不充分&lt;/li&gt;
  &lt;li&gt;对周围的环境很陌生&lt;/li&gt;
  &lt;li&gt;经历不足，导致的对事情没有底气&lt;/li&gt;
  &lt;li&gt;在意别人对自己的看法&lt;/li&gt;
  &lt;li&gt;生理反应，肌肉紧张、心跳加速、头脑晕眩&lt;/li&gt;
  &lt;li&gt;近期自己身边的环境变动较大&lt;/li&gt;
  &lt;li&gt;长期积累的心里因素，过分在意过去发生的事情&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;我们在面对演讲时通常会有很多的恐惧，特别是在上台前一段时间，自己的恐惧心理被放大的特别多。&lt;/p&gt;

&lt;p&gt;其实说来不只是演讲，恐惧在我们平常的生活跟工作当中也非常多，我们心里常常会对某件即将到来的事产生恐惧。&lt;/p&gt;

&lt;p&gt;因此呢。并不是说在演讲时你才会恐惧，在平常的生活当中，你同样会有很多令你焦虑不安的事情。&lt;/p&gt;

&lt;p&gt;演讲跟平常我们生活、工作时产生的恐惧是一样的，并没有说因为演讲而特殊。&lt;/p&gt;

&lt;p&gt;我们究竟为什么而恐惧？我们来分析一下。&lt;/p&gt;

&lt;p&gt;前面列了7条恐惧的原因，但其实这7条可以归类一下变得更精简一些。&lt;/p&gt;

&lt;p&gt;（恐惧原因）&lt;/p&gt;

&lt;p&gt;恐惧的原因总的来说为三方面：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;准备不充分&lt;/li&gt;
  &lt;li&gt;环境变化导致的恐惧&lt;/li&gt;
  &lt;li&gt;长期积累的心理因素&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;（恐惧原因细节图）&lt;/p&gt;

&lt;h3 id=&quot;第一准备不充分&quot;&gt;第一，准备不充分&lt;/h3&gt;

&lt;p&gt;当我们知道我们将要去面对一件事情，但是这件事情并没有充足的把握时我们就会感到恐惧。&lt;/p&gt;

&lt;p&gt;因为这件事情是未知的，而且我预感到他可能不会有太好的结果，因为我对他没有把握，我也没有对他做任何的准备工作。&lt;/p&gt;

&lt;p&gt;可能有这么一件事情你是从来没有做过的，或者说你也不知道他将会发生什么样的情况。甚至在你的预感当中，这件事会发生比较差的或者糟糕的情况，此时你就会对这件事情产生恐惧。因而有逃避去面对这件事情的心理状态。&lt;/p&gt;

&lt;p&gt;在演讲时也是同样的，大部分人没有台上演讲的经验和习惯，因此对于上台演讲这件事情，大部分人都是有恐惧心理的。&lt;/p&gt;

&lt;p&gt;这个恐惧心理是因为我们没有，为这件事情做过任何的准备，也不知道这件事情会发生怎样的情况。此时我们心里隐隐的感觉到这这件事情会比较糟糕。因此，我们对演讲产生了恐惧。&lt;/p&gt;

&lt;p&gt;总之，当你面对一件即将到来，但未知的事情时，当你面对一件你觉得可能会发生比较糟糕的事情时，通常是因为我们没有做好足够的准备工作。这个准备工作可能需要花去几小时、几天、几个月甚至几年的时间，总之我们并没有为这件事做好足够的准备。&lt;/p&gt;

&lt;h3 id=&quot;第二环境变化导致的恐惧&quot;&gt;第二，环境变化导致的恐惧&lt;/h3&gt;

&lt;p&gt;我们周围的环境时常在变化，包括我们的工作，上下级人员调动，以及你身边人的来往，以及你亲人的离去与归来，这些的环境都在时刻变化着，影响着我们的心理。
可能是你最近比较倒霉，碰到了一些小概率事件，或者可能会因为大环境的变化使得感到沮丧。
甚至有可能你出差到了某个陌生的环境，让你感到很不舒服。&lt;/p&gt;

&lt;p&gt;这些环境的变化都会让你产生焦虑甚至恐惧的心理。&lt;/p&gt;

&lt;p&gt;总之，在当前这样的环境下，是你并不熟悉的环境，不是你能掌控而环境，因此导致你对周围的环境有一种隔离感，像是没有依靠的感觉。&lt;/p&gt;

&lt;h3 id=&quot;第三长期积累的心理因素&quot;&gt;第三，长期积累的心理因素&lt;/h3&gt;

&lt;p&gt;你可能会因为曾经的遭遇而感到痛苦，这些遭遇在你的脑中挥之不去，当再次发生或者即将发生这样的事情的时候，你就会感到焦虑和恐惧。&lt;/p&gt;

&lt;p&gt;这些痛苦的场景通常会延伸到我们生活的各个角落，比如说，你可能曾经在小组发言时，磕磕巴巴导致你对公众发言产生了恐惧，致使你在现在上台演讲时都会表现的异常的惊恐，当回忆起自己以前在小组发言时的那些场景，以及当时人们都的表情，你会不自觉的把这些场景延伸到现在的公众演讲以及公众表达上。&lt;/p&gt;

&lt;p&gt;常见比如，人们小时候玩水时掉入水中呛到过，成人后就对游泳产生了恐惧。或者在爬树时掉下来过，就对过山车产生了恐惧等等。&lt;/p&gt;

&lt;p&gt;不仅如此，人们通常会将一种痛苦的经历延展到另一个场景中，比如说，某人会因为小学里遭到过坏学生的殴打，致使他觉得现在自己面对他人安排的工作任务时会感觉自己是被压迫的或受虐待的，因此有很大的抵触情绪。&lt;/p&gt;

&lt;p&gt;总之，人们常常会延展过去的痛苦回忆，将这些痛苦的回忆放入到现在的场景里面去，这使得他们感到恐惧，同时也使他们有一个最佳的逃避借口。&lt;/p&gt;

&lt;h2 id=&quot;如何行动去克服恐惧&quot;&gt;如何行动去克服恐惧？&lt;/h2&gt;

&lt;p&gt;我罗列了所有的方法，有些方法是我自己总结的，有些则是参考《应对焦虑》埃德蒙.伯恩写的这本书。&lt;/p&gt;

&lt;p&gt;（克服恐惧的方法全图）&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;观察自己的恐惧&lt;/li&gt;
  &lt;li&gt;提前做好准备&lt;/li&gt;
  &lt;li&gt;积极变化适应环境&lt;/li&gt;
  &lt;li&gt;冥想练习排除杂念&lt;/li&gt;
  &lt;li&gt;渐进式肌肉放松&lt;/li&gt;
  &lt;li&gt;暴露疗法，应对暴露，完全暴露，想象暴露&lt;/li&gt;
  &lt;li&gt;运动，增加运动频率，增加强度增加抗压力&lt;/li&gt;
  &lt;li&gt;自我关怀，爱的言语、规律作息、简化生活、&lt;/li&gt;
  &lt;li&gt;转移注意力，专注于技巧，找人聊天，体验愉悦的事，听音乐&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;以上这些技巧，可以用在我们生活当中面对焦虑与恐惧时的情况。&lt;/p&gt;

&lt;p&gt;虽然大部分技巧都可以用到演讲中，但我想讲其中在演讲时运用最有效的方法，我们来看下图：&lt;/p&gt;

&lt;p&gt;（克服演讲恐惧的有效方法）&lt;/p&gt;

&lt;h3 id=&quot;观察自己的恐惧&quot;&gt;观察自己的恐惧&lt;/h3&gt;

&lt;p&gt;我们最先要做的是观察自己的恐惧，这是所有技巧的前提。&lt;/p&gt;

&lt;p&gt;（观察恐惧图：来源网络）&lt;/p&gt;

&lt;p&gt;恐惧也是情绪的一种，我们需要观察恐惧发生时候我们的表现，我们的眼神，我们心跳，我们的肌肉，在做怎样的颤抖。通过观察我们身体上的变化，我们可以感知到自己的恐惧。当我们能够观察到恐惧时，恐惧本身就没有这么强烈了，所以我们首先要做的是，用第三者的角度观察自己的恐惧表现。&lt;/p&gt;

&lt;p&gt;通过观察我们能及时的发现我们的精神状态在变化，只要我们观察到恐惧，它就不会再扩散，此时我们就可以想办法逐渐的释放它。&lt;/p&gt;

&lt;p&gt;最糟糕的是你观察不到自己的恐惧，但仍然继续逼迫自己向前对劲，这时你会有一种无力感，同时恐惧会不断的蔓延到你的全身，使你的精神状态陷入更加崩溃的边缘。&lt;/p&gt;

&lt;p&gt;我们只有识别自己的恐惧状态，识别自己的恐惧表现，才能真正的认识到自己的情绪变化，以及事物对我们的影响程度。这为后面的措施，调整打下了好的基础。&lt;/p&gt;

&lt;h3 id=&quot;提前做好准备&quot;&gt;提前做好准备&lt;/h3&gt;

&lt;p&gt;提前做好准备，通常是最明智的选择，也是克服恐惧的最好方法。&lt;/p&gt;

&lt;p&gt;（提前准备图：来源网络）&lt;/p&gt;

&lt;p&gt;它需要我们抽出时间去为这场演讲做准备。&lt;/p&gt;

&lt;p&gt;我需要写演讲稿，并且在演讲前打磨多次。通过自己不断的练习和打磨演讲，将演讲稿打磨成自己认为完美的样子。&lt;/p&gt;

&lt;p&gt;同时，在我们平时的生活中，要为未来的可能的演讲做好准备。所以，在我们平常的生活当中，要注重收集生活的细节，收集演讲的素材，这些素材能够更快更有效的运用在未来的演讲中，让演讲和故事呈现的效果更好。&lt;/p&gt;

&lt;p&gt;因此，提前做好准备，是每个演讲达人的最重要的工作之一。&lt;/p&gt;

&lt;h3 id=&quot;积极拥抱变化适应环境&quot;&gt;积极拥抱变化适应环境&lt;/h3&gt;

&lt;p&gt;（拥抱变化：来源网络）&lt;/p&gt;

&lt;p&gt;很多时候我们避免不了会去自己陌生的地方演讲。这个时候，环境的变化对我们的精神冲击力很大，周围没有熟悉的环境没有熟悉的人，我们感到被隔离脱离群体从而感到恐惧。&lt;/p&gt;

&lt;p&gt;这个时候，我们需要积极的去拥抱变化。与台上台下的工作人员沟通，积极的与嘉宾沟通，与在场的观众沟通。通过这样沟通交流我们能够舒展我们的心情，能够释放我们的紧张情绪，能够释放我们的恐惧情绪。&lt;/p&gt;

&lt;p&gt;如果在现场当我们遇到困难的时候，需要主动寻求帮助。特别是当下环境中的管理者，或者比较熟悉的朋友，通过他们的帮助让自己这份不安的心能够安定下来，减少恐惧的蔓延。&lt;/p&gt;

&lt;p&gt;特别是在演讲开场时，自己在一个陌生的环境下非常容易紧张焦虑和恐惧，此时在开场时需要跟大家有一个沟通交流的时间，留出这样一个前置的时间跟大家沟通交流非常必要，然后再慢慢进入状态，开始自己的演讲。也可以通过说出自己的感受来跟大家做一些互动，这样让自己的紧张情绪和压力能够释放掉，这为后面的事情进展顺利而铺平了道路。&lt;/p&gt;

&lt;h3 id=&quot;放松练习&quot;&gt;放松练习&lt;/h3&gt;

&lt;p&gt;我们可以用身体上的练习和精神的上的练习来放松自己。&lt;/p&gt;

&lt;p&gt;（冥想图：来源网络）&lt;/p&gt;

&lt;p&gt;精神上的练习，可以通过说出感受来接纳自己释放压力。也可以通过就地坐下或稳定站立并闭上眼睛来做冥想，通过专注于自己的呼吸，将杂念排除，也可以通过腹式呼吸来将注意力转移到自己的腹部上，让自己更加专注于当下。&lt;/p&gt;

&lt;p&gt;冥想的要点是专注于呼吸，这会让你排除杂念，专注于当下。&lt;/p&gt;

&lt;p&gt;身体上的练习，也可以起到放松作用。&lt;/p&gt;

&lt;p&gt;（与文无关的放松练习图：来源网络）&lt;/p&gt;

&lt;p&gt;这里有一个叫渐进式肌肉放松法。我们来具体介绍一下：&lt;/p&gt;

&lt;p&gt;腹式呼吸，用腹部呼吸，吸气时肚子吸入空气突出呼气时因气体排除而扁平，呼吸要慢，想象全身的紧张感开始从体内流去。&lt;/p&gt;

&lt;p&gt;撰紧拳头，保持7-10秒，再从开拳头15-20秒，反复循环&lt;/p&gt;

&lt;p&gt;双手前臂抬起，前臂与上臂尽量靠拢，紧绷肱二头肌，保持，然后放松。&lt;/p&gt;

&lt;p&gt;双手手臂向外延展到水平位置，伸肘，拉紧肱三头肌，保持，然后放松。&lt;/p&gt;

&lt;p&gt;尽量抬高眉毛，收缩前额肌肉，保持，然后放松。放松时，想象前额肌肉慢慢舒展、松弛。&lt;/p&gt;

&lt;p&gt;紧闭双眼，绷紧眼周肌肉，保持，然后放松。想象深度放松的感觉在眼镜周围蔓延。&lt;/p&gt;

&lt;p&gt;张大嘴巴，拉伸下颚关节周围的肌肉，绷紧下巴，保持，然后放松。张着嘴，让下巴自然放松。&lt;/p&gt;

&lt;p&gt;头向后仰，尽量靠向后背，收紧脖子后面的肌肉，专注于收紧颈部肌肉的动作，保持，然后放松。&lt;/p&gt;

&lt;p&gt;双肩同时最大限度地向上耸起，绷紧肩部肌肉，保持，然后放松。&lt;/p&gt;

&lt;p&gt;双肩外展，尽量向背部中线靠拢，绷紧肩胛骨周围的肌肉。让肩胛处的肌肉保持绷紧，然后放松。&lt;/p&gt;

&lt;p&gt;深吸一口气，绷紧胸部肌肉，保持10秒，然后慢慢呼气。&lt;/p&gt;

&lt;p&gt;收紧腹部肌肉，保持，然后放松。&lt;/p&gt;

&lt;p&gt;背部弓起，拉紧下背部肌肉，保持，然后放松。&lt;/p&gt;

&lt;p&gt;收紧臀部，保持，然后放松。&lt;/p&gt;

&lt;p&gt;收缩大腿肌肉，保持，然后放松。&lt;/p&gt;

&lt;p&gt;向自己的方向用力伸脚趾，绷紧小腿肌肉，保持，然后放松。&lt;/p&gt;

&lt;p&gt;卷起脚趾，绷紧脚面，保持，然后放松。&lt;/p&gt;

&lt;p&gt;以上是一整套的渐进式肌肉放松法，一整套下来需要20-30分钟，在平时的放松练习可以通过抽取几个对自己比较有效的姿势去做放松练习，这样更容易将这些放松练习融入到我们的生活中，特别是演讲前的放松练习。&lt;/p&gt;

&lt;h3 id=&quot;参考资料&quot;&gt;参考资料：&lt;/h3&gt;

&lt;p&gt;《三招搞定演讲构思》朱林滢&lt;/p&gt;

&lt;p&gt;《应对焦虑》埃德蒙.伯恩&lt;/p&gt;

&lt;p&gt;《高效演讲》彼得.迈尔斯，尚恩.尼克斯&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>给女儿写信(十九) 说出自己的感受积极化解情绪</title>
   <link href="http://www.luzexi.com/2021/11/27/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A119"/>
   <updated>2021-11-27T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/11/27/给女儿的信19</id>
   <content type="html">
&lt;hr /&gt;

&lt;p&gt;导读：喜欢跟孩子们一起，可惜一直在外地工作，因此我给孩子写点信并录成视频，跟孩子聊聊自己的故事以及最近的感受&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;要点&quot;&gt;要点：&lt;/h2&gt;

&lt;p&gt;说出自己的感受，积极化解情绪&lt;/p&gt;

&lt;h2 id=&quot;结构&quot;&gt;结构：&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;半年一次的俱乐部选举即将开始&lt;/li&gt;
  &lt;li&gt;我自己写了ppt，做了选举前的准备&lt;/li&gt;
  &lt;li&gt;选举很激烈，主席选举经过2次平分后终于选出结果，大家也很欢乐&lt;/li&gt;
  &lt;li&gt;接着我上台选举VPE，上台很紧张，自己讲的有点死板&lt;/li&gt;
  &lt;li&gt;由于最近在俱乐部里挺活跃的，抱着一线希望，但最后还是败下阵来&lt;/li&gt;
  &lt;li&gt;接着收拾下心情，继续竞选后面的职位&lt;/li&gt;
  &lt;li&gt;有人提议VPM，我接受了&lt;/li&gt;
  &lt;li&gt;我上台后，先化解自己的情绪，说出了自己的感受&lt;/li&gt;
  &lt;li&gt;同时为VPM制定了目标，并真诚说出自己的内心话，希望跟大家交朋友&lt;/li&gt;
  &lt;li&gt;爸爸认为练习技巧前应该先交朋友，有了朋友技巧才有用，得到大家的认可，最后成功选举获得VPM&lt;/li&gt;
  &lt;li&gt;价值升华，失败不可怕，及时调整心态，说出感受，主动卸下压力，能让事情进展的更加顺利&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;内容&quot;&gt;内容：&lt;/h2&gt;

&lt;p&gt;Hello Sharon and hello，Anne，爸爸喜欢你们。爸爸到深圳了，要再过一个月回来，到时候再跟你玩，爸爸喜欢跟你们玩游戏，喜欢跟你们聊天，喜欢跟你们一起学习，喜欢跟你们一起加油。&lt;/p&gt;

&lt;p&gt;今天爸爸跟你们聊一下，爸爸最近在演讲俱乐部里面做的一些事情好不好？&lt;/p&gt;

&lt;p&gt;最近半年爸爸都在演讲俱乐部里面练习演讲，练习表达，这和Sharon在学校里面练小主播一样，非常像，爸爸每天都会练习那个绕口令，都会练习口步操，是不是跟你们很像？平时的时候爸爸会做一些演讲，上台演讲，这样呢会让爸爸在公众表达上更好一些，平常跟人沟通交流时，也能更自然一些。&lt;/p&gt;

&lt;p&gt;那爸爸想跟确认跟安妮说说说最近爸爸在俱乐部里面的一件事好不好？最近俱乐部里面要选举官员，怎么是官员？呢就是嗯宣传学校里面选班长，选学习委员，选体育委员，这些是一样的，爸爸在俱乐部里面也要选主席，要选副会长，要选秘书长等这些岗位，那这些岗位呢要做竞选演讲。&lt;/p&gt;

&lt;p&gt;于是爸爸上台做了竞选演讲，爸爸上台了两次，做了两次竞选演讲，分别去竞选了两个职位。&lt;/p&gt;

&lt;p&gt;第一个职位的时候爸爸上台很紧张，自己说话也有点很死板，说话的内容不是很清楚，因为爸爸太紧张了，通常紧张的时候爸爸说话都是有一点点啊有语无伦次，虽然抱着一线希望，但是最后还是败下阵来了。&lt;/p&gt;

&lt;p&gt;于是就收拾了一下心情继续的后面的竞选岗位。等爸爸第二次上台的时候，爸爸先做的一件事情就是爸爸先化解情绪，再开始自己的演讲内容，爸爸第二次上台的时候也还是很紧张，于是我上台时先说出自己的感受，说说自己为什么紧张，说自己还在想怎么去改进就第二次上台了。&lt;/p&gt;

&lt;p&gt;在说出自己的感受的时候，爸爸已经慢慢的化解了自己的情绪，接着爸爸就说出自己的内心话，希望能跟大家交朋友，希望能在这个俱乐部里面啊有更好的练习，跟大家一起学习，真心真诚的跟大家交朋友。&lt;/p&gt;

&lt;p&gt;那当爸爸说出这些真心话的时候，得到了大家的认可，爸爸也同时告诉大家，来这里练习演讲，爸爸首先希望能跟大家先交朋友，然后再练习，这种方式。得到了大家的认可，最后成功获得了会员副主席的这个职位。&lt;/p&gt;

&lt;p&gt;爸爸想跟Sharon和安妮说，这次经历告诉爸爸，其实失败并不可怕，在失败以后要及时调整自己的状态，调整自己的心情，怎么去调整呢，就是说出自己的感受，向周围人说出自己的感受，说出自己的看法，这样就能主动卸下自己的压力，让自己放松下来，这杨可以让后面的事情进展的更加顺利。&lt;/p&gt;

&lt;p&gt;今天的故事就讲到这里，爸爸爱你们，喜欢你们了。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>游戏引擎架构#4 低阶渲染器（3）</title>
   <link href="http://www.luzexi.com/2021/11/22/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B050"/>
   <updated>2021-11-22T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/11/22/读书笔记50</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485051&amp;amp;idx=1&amp;amp;sn=e98f1e1971d56ce6543926b4f9bad204&amp;amp;chksm=fc22637ccb55ea6a4c16c5c0d8fd43cff936350490d79f1e377b61e4914300c39c4fc2911da1&amp;amp;token=53415989&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;背景&quot;&gt;背景：&lt;/h1&gt;

&lt;p&gt;作为游戏开发从业者，从业务到语言到框架到引擎，积累了一些知识和经验，特别是在看了几遍《游戏引擎架构》后对引擎架构的理解又深入了些。&lt;/p&gt;

&lt;p&gt;近段时间有对引擎剖析的想法，正好借这书本对游戏引擎架构做一个完整分析。&lt;/p&gt;

&lt;p&gt;此书用简明、清楚的方式覆盖了游戏引擎架构的庞大领域，巧妙地平衡了广度与深度，并且提供了足够的细节。&lt;/p&gt;

&lt;p&gt;借助《游戏引擎架构》这本书、结合引擎源码和自己的经验，深入分析游戏引擎的历史、架构、模块，最后通过实践简单引擎开发来完成对引擎知识的掌握。&lt;/p&gt;

&lt;p&gt;游戏引擎知识面深而广，所以对这系列的文章书编写范围做个保护，即不对细节进行过多的阐述，重点剖析的是架构、流程以及模块的运作原理。&lt;/p&gt;

&lt;p&gt;同时《游戏引擎架构》中部分知识太过陈旧的部分，会重新深挖后总结出自己的观点。&lt;/p&gt;

&lt;p&gt;概述：&lt;/p&gt;

&lt;p&gt;本系列文章对引擎中的重要的模块和库进行详细的分析，我挑选了十五个库和模块来分析：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;时间库&lt;/li&gt;
  &lt;li&gt;自定义容器库&lt;/li&gt;
  &lt;li&gt;字符串散列库&lt;/li&gt;
  &lt;li&gt;内存管理框架&lt;/li&gt;
  &lt;li&gt;RTTI与反射模块&lt;/li&gt;
  &lt;li&gt;图形计算库&lt;/li&gt;
  &lt;li&gt;资产管理模块&lt;/li&gt;
  &lt;li&gt;低阶渲染器&lt;/li&gt;
  &lt;li&gt;剔除与合批模块&lt;/li&gt;
  &lt;li&gt;动画模块&lt;/li&gt;
  &lt;li&gt;物理模块&lt;/li&gt;
  &lt;li&gt;UI底层框架&lt;/li&gt;
  &lt;li&gt;性能剖析器的核心部分&lt;/li&gt;
  &lt;li&gt;脚本系统&lt;/li&gt;
  &lt;li&gt;视觉效果模块&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;本篇内容为列表中的第8个部分的第1节。&lt;/p&gt;

&lt;h1 id=&quot;正文&quot;&gt;正文：&lt;/h1&gt;

&lt;p&gt;简单回顾下前文&lt;/p&gt;

&lt;p&gt;前文我们聊了下显卡在计算机硬件主板中的位置与结构，知道了CPU、GPU的通信介质，并简单介绍了手机上的主板结构。本篇开头对上一篇做一些内容补充，PC和手机的不同硬件组织，以及CPU与其他芯片的通信过程。&lt;/p&gt;

&lt;p&gt;下面我们开始这篇内容&lt;/p&gt;

&lt;p&gt;本次内容会围绕GPU来写，从硬件架构到软件驱动再到引擎架构，目标是帮大家理解GPU硬件的运作原理，理解图形接口的架构，理解引擎低阶渲染器的架构。&lt;/p&gt;

&lt;p&gt;目录：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;主板结构中的显卡&lt;/li&gt;
  &lt;li&gt;GPU功能发展史&lt;/li&gt;
  &lt;li&gt;GPU与CPU的差异&lt;/li&gt;
  &lt;li&gt;GPU硬件特点&lt;/li&gt;
  &lt;li&gt;图形驱动程序架构&lt;/li&gt;
  &lt;li&gt;引擎低阶渲染架构&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;内容结构&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;CPU硬件结构&lt;/li&gt;
  &lt;li&gt;GPU硬件结构&lt;/li&gt;
  &lt;li&gt;GPU手机管线与PC管线的差异&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;简单回顾下前文，前文我们主要讲了显卡的发展历史，知道了显卡功能和管线是如何一步步转变为现在这样子的。&lt;/p&gt;

&lt;h2 id=&quot;cpu结构与工作原理&quot;&gt;CPU结构与工作原理&lt;/h2&gt;

&lt;p&gt;我们知道，CPU运行时有三类元器件构成，取指器、译码器、运算器（逻辑算术运算器、浮点数运算器、单指令多数据运算器等）。&lt;/p&gt;

&lt;p&gt;这三类元器件代表三个阶段取指阶段（Fetch）、指令译码阶段（Decode）、执行阶段（Execute），它们在CPU内执行的步骤如下图：&lt;/p&gt;

&lt;p&gt;（图-取指-译指-执行三步骤）&lt;/p&gt;

&lt;p&gt;取指阶段为从内存或缓存中取得指令并存放到寄存器中的过程。&lt;/p&gt;

&lt;p&gt;接着，译码器会将寄存器中的指令翻译成操作指令，指令译码器按照预定的指令格式，对取回的指令进行拆分和解释，识别区分出不同的指令类别以及各种获取操作数的方法。在组合逻辑控制的计算机中，指令译码器对不同的指令操作码产生不同的控制电位，以形成不同的微操作序列；在微程序控制的计算机中，指令译码器用指令操作码来找到执行该指令的微程序的入口，并从此入口开始执行。&lt;/p&gt;

&lt;p&gt;运算阶段，则根据指令执行不同的运算单元，完成指令所规定的各种操作，具体实现指令的功能。为此，CPU 的不同部分被连接起来，以执行所需的操作。&lt;/p&gt;

&lt;p&gt;（图-控制单元-运算单元-存储单元）&lt;/p&gt;

&lt;p&gt;因此，通常我们将取指器、译码器统称为控制单元，计算器称为算术逻辑单元（ALU），寄存器和高速缓存称为存储单元。&lt;/p&gt;

&lt;p&gt;除了这三个基本单元，当下这样复杂的CPU中还有包括分支预测器、乱序控制器、内存预加载器等等。&lt;/p&gt;

&lt;p&gt;这里简单介绍下CPU指令流水线、分支预测、乱序执行的原理。&lt;/p&gt;

&lt;h2 id=&quot;指令流水线&quot;&gt;指令流水线&lt;/h2&gt;

&lt;p&gt;起初CPU指令执行是线性的，只靠取指、译码、运算顺序执行三个模块，这导致元器件的工作顺序是线性的，当一个元器件执行时，其他元器件是空等待状态，CPU执行效率比较低。&lt;/p&gt;

&lt;p&gt;为了提高效率，提高空等待的元器件的利用率，对指令执行流水线进行了拆分，并同时增加多个流水线不断减少元器件的空等待装填。如下图：&lt;/p&gt;

&lt;p&gt;（拆分多级流水线）&lt;/p&gt;

&lt;p&gt;将原本线性的三个指令执行顺序，拆分成一个个小模块，让这些独立的小模块可以自顾自的循环工作，减少前后的等待时间，从而提高了指令执行效率。&lt;/p&gt;

&lt;p&gt;用这种方式把一个指令拆分成“取指令 - 指令译码 - 执行指令”这样三个部分，这就是一个三级的流水线。进一步把“执行指令”拆分成“ALU 计算（指令执行）- 内存访问 - 数据写回”，就会变成一个五级的流水线。&lt;/p&gt;

&lt;p&gt;继续拆分，将一个长时间的操作步骤，拆分成更多的步骤，让所有步骤需要执行的时间尽量都差不多长。这样，也就可以解决我们在单指令周期处理器中遇到的复杂指令性能问题。（同时多级流水线会出现许多问题，例如模块间寄存器的写入次数太多，多模块读写同一个资源相互冲突等，这里不细说，CPU最终都有解决方案去解决）现代的 ARM 或 Intel 的 CPU，流水线级数都已经到了 14 级。&lt;/p&gt;

&lt;h2 id=&quot;乱序执行&quot;&gt;乱序执行&lt;/h2&gt;

&lt;p&gt;我们在写程序时，常常会发现函数内几个执行操作顺序并不互相依赖，哪个在前哪个在后都没有太大的关系。此时不仅编译器会对这些不相互依赖的计算操作进行重新顺序排序用于优化CPU执行效率（这也是导致线程不同步的其中一个原因），CPU也会将不相互依赖的指令放在不相同的指令流水线上以加快执行速度。&lt;/p&gt;

&lt;p&gt;（指令在不同CPU流水线上乱序执行：图来源网络）&lt;/p&gt;

&lt;p&gt;我们从图中可以看到，在流水线里，当后面的指令不依赖前面的指令时，就不用等待前面的指令执行完毕后再执行，可以另起一个流水线执行，否则就需要用NOP隔周期等待的方式将执行单元延后计算。因此我们所写的代码的执行顺序其实并不是我们所想象的那样，在CPU中大部分时候其实是乱序执行的，这样元器件的利用率更高，执行效率也更高，而依赖而停顿的次数也更少。&lt;/p&gt;

&lt;p&gt;（乱序执行的流程图）&lt;/p&gt;

&lt;p&gt;乱序执行实际的过程比我们想象的要复杂一些，总体上它会先拆分指令，再分发给执行单元，结束后将结果重新排序，最后提交缓冲。&lt;/p&gt;

&lt;h2 id=&quot;分支预测&quot;&gt;分支预测&lt;/h2&gt;

&lt;p&gt;程序中有很多true或false的判断来跳转下文要执行的指令，这种跳转会使得执行流水线发生停顿，因为要依赖前面代码计算的结果再决定要执行哪段程序，因此流水线不中断并等待结果，这会使CPU执行效率降低。&lt;/p&gt;

&lt;p&gt;在CPU中有分支预测器，它是一种数字电路，在分支指令执行前，猜测哪一个分支会被执行，这样能显著提高pipeline的性能。&lt;/p&gt;

&lt;p&gt;可以理解为，分支预测器会主动猜测分支是true还是false。&lt;/p&gt;

&lt;p&gt;如果猜错了，处理器要flush掉pipeline, 回滚到之前的分支，然后重新热启动，选择另一条路径。 
如果猜对了，处理器不需要暂停，继续往下执行。&lt;/p&gt;

&lt;p&gt;也就是说，如果CPU每次都猜错，处理器将耗费大量时间在停止-回滚-热启动这一周期性过程里。反之，如果侥幸每次都猜对了，那么处理器将从不停止、无需等待的执行后面的指令。&lt;/p&gt;

&lt;p&gt;（分支预测图）&lt;/p&gt;

&lt;p&gt;CPU执行指令遇到条件时不知道该读取哪些指令，需要等待判断条件中的计算结果，这样就中断了后面指令执行流水线使得执行效率下降。于是CPU增加了分支预测器，猜if条件中是True还是False，如果猜对了效率就会提高，如果猜错了，则重新计算。&lt;/p&gt;

&lt;p&gt;分支预测的关键是，预测算法能猜对多少。&lt;/p&gt;

&lt;p&gt;分支预测分为动态分支预测和静态分支预测。动态预测在执行过程中统计了通过率，根据通过率去调整预测方向，静态则始终以一个值作为判断标准。动态预测有好几种，最常见的是双模预测，通过四个状态位来动态调整预测结果。其它常见分支预测器如两级自适应预测器，局部/全局分支预测器，融合分支预测器，Agree预测期，神经分支预测器等。&lt;/p&gt;

&lt;h2 id=&quot;cpu原理小结&quot;&gt;CPU原理小结&lt;/h2&gt;

&lt;p&gt;（CPU抽象元件图）&lt;/p&gt;

&lt;p&gt;现在我们知道了CPU指令周期的工作方式，分为三个步骤，取指、译码、运算。运算后需要寄存器和高速缓存来作为存储器，CPU会从内存中获取指令并最终将数据写入内存。&lt;/p&gt;

&lt;p&gt;我们把CPU中的元件抽象成，取指和译码元件、逻辑运算元件、数据缓存，就有了上面这幅简单抽象的CPU结构图。&lt;/p&gt;

&lt;p&gt;下面我们来看看硬件上的元器件是如何分布的：&lt;/p&gt;

&lt;p&gt;（CPU硬件结构图：来源网络）&lt;/p&gt;

&lt;p&gt;我们看到CPU除了基本的控制器、运算器、寄存器、高速缓存外，还额外放置了乱序执行器、分支预测器、内存预装载器等用于提高CPU效率。这些元器件全部加起来，整个就是一个CPU Core。&lt;/p&gt;

&lt;p&gt;（多核架构图：来源网络）&lt;/p&gt;

&lt;p&gt;实际的设备中通常由多个CPU Core组成多核的架构，每个CPU Core都有自己的高速缓存L1，不同CPU Core之间也有共享的高速缓存L2，通常每级缓存的存取速度有10倍的差距，而内存的存取速度比高速缓存差的更多，对于CPU Core来说可以认为它是一个外部存储设备，通过桥接芯片连接。&lt;/p&gt;

&lt;p&gt;GPU硬件结构与原理&lt;/p&gt;

&lt;p&gt;前面介绍了CPU的内部结构，现代无论是手机还是PC机基本都是多核的，每个核就是1个CPU Core，每个CPU Core里都有取指器和译码器，还有逻辑运算器，以及寄存器和高速缓存。除了上述基本元件外还有其他元器件用于优化CPU执行效率，包括乱序执行器、分支预测器、内存预装载器等。&lt;/p&gt;

&lt;p&gt;GPU图形管线的变迁&lt;/p&gt;

&lt;p&gt;我们从GPU历史里知道，原本显卡只是一个数据传输和画面转换接口，在不断的变革下成了主板上一个独立的芯片，之后就有了GPU的概念。CPU将数据传输到显存再通知GPU处理这些数据，GPU则拥有图形图像的处理流水线，专门处理图像。&lt;/p&gt;

&lt;p&gt;起初图形的顶点、片元都在CPU上计算，到了Voodoo FX显卡时已经将图元生成后的步骤拆分到了GPU上，最后再将顶点处理部分的计算合入到GPU上，此时GPU才真正形成了自己的图形管线。如下图：&lt;/p&gt;

&lt;p&gt;（图形计算管线变迁1-1982年前的纯2D时代）&lt;/p&gt;

&lt;p&gt;1982年前，CPU承担大部分的工作，当时还没有GPU的概念，还只能以显示适配器的名称称呼。&lt;/p&gt;

&lt;p&gt;（图形计算管线变迁2-1996年3dfx Voodoo）&lt;/p&gt;

&lt;p&gt;到1996年，GPU已经可以分担CPU的部分功能，只留下顶点处理部分部分给CPU。&lt;/p&gt;

&lt;p&gt;（图形计算管线变迁3-1998年GeForce）&lt;/p&gt;

&lt;p&gt;到1998年，所有顶点处理和片元处理都由GPU来完成了，但没有可编程部分，管线是固定的，传入顶点后无法控制顶点和片元的变化。&lt;/p&gt;

&lt;p&gt;（图形计算管线变迁4-2002年GeForce FX）&lt;/p&gt;

&lt;p&gt;到2002年，正式加入了可编程着色器，让顶点和片元的计算和展示有了更多变化。&lt;/p&gt;

&lt;p&gt;（图形计算管线变迁5-2006年GeForce 8800）&lt;/p&gt;

&lt;p&gt;到2006年，GPU管线中又增加了细分着色器，pre-Z等节点。&lt;/p&gt;

&lt;p&gt;这部分历史我们也可以通过OpenGL的功能变化来看这段历史的发展过程。&lt;/p&gt;

&lt;p&gt;（来源 wiki）&lt;/p&gt;

&lt;p&gt;1.1 1997 年 3 月，纹理对象，顶点数组&lt;/p&gt;

&lt;p&gt;1.2 1998 年 3 月，3D 纹理、BGRA 和打包像素格式&lt;/p&gt;

&lt;p&gt;1.2.1 1998年10月，ARB 扩展概念&lt;/p&gt;

&lt;p&gt;1.3 2001 年 8 月，多重纹理、多重采样、纹理压缩&lt;/p&gt;

&lt;p&gt;1.4 2002 年 7 月，深度图，GLSlang&lt;/p&gt;

&lt;p&gt;1.5 2003 年 7 月，顶点缓冲对象 (VBO)，遮挡查询&lt;/p&gt;

&lt;p&gt;2.0 2004 年 9 月， GLSL 1.1，MRT，两个纹理的非幂，点精灵，双面模板&lt;/p&gt;

&lt;p&gt;2.1 2006 年 7 月 ，GLSL 1.2，像素缓冲对象 (PBO)，sRGB 纹理&lt;/p&gt;

&lt;p&gt;3.0 2008 年 8 月 ，GLSL 1.3，纹理数组，条件渲染，帧缓冲对象 (FBO)&lt;/p&gt;

&lt;p&gt;3.1 2009 年 3 月， GLSL 1.4，Instancing，纹理缓存对象，统一缓存对象，图元重启&lt;/p&gt;

&lt;p&gt;3.2 2009 年 8 月， GLSL 1.5，几何着色器，多重采样纹理&lt;/p&gt;

&lt;p&gt;3.3 2010 年 3 月， GLSL 3.30，从 OpenGL 4.0 规范向后移植尽可能多的功能&lt;/p&gt;

&lt;p&gt;4.0 2010 年 3 月， GLSL 4.00，GPU 上的曲面细分，具有 64 位精度的着色器&lt;/p&gt;

&lt;p&gt;4.1 2010 年 7 月， GLSL 4.10，开发人员友好的调试输出，与 OpenGL ES 2.0 的兼容性&lt;/p&gt;

&lt;p&gt;4.2 2011 年 8 月， GLSL 4.20，带原子计数器的着色器，绘制传输给Feed back实例，着色器打包，性能改进&lt;/p&gt;

&lt;p&gt;4.3 2012 年 8 月， GLSL 4.30，利用 GPU 并行性的计算着色器、着色器存储缓冲区对象、高质量 ETC2/EAC 纹理压缩、增强的内存安全性、多应用程序稳健性扩展、与 OpenGL ES 3.0 的兼容性&lt;/p&gt;

&lt;p&gt;4.4 2013 年 7 月， GLSL 4.40，缓冲区放置控制，高效异步查询，着色器变量布局，高效多对象绑定，Direct3D 应用程序的流线型移植，无绑定纹理扩展，稀疏纹理扩展&lt;/p&gt;

&lt;p&gt;4.5 2014 年 8 月， GLSL 4.50，直接状态访问 (DSA)，刷新控制，鲁棒性，OpenGL ES 3.1 API 和着色器兼容性，DX11 仿真功能&lt;/p&gt;

&lt;p&gt;4.6 2017 年 7 月， GLSL 4.60，更高效的几何处理和着色器执行，更多信息，无错误上下文，多边形偏移钳位，SPIR-V，各向异性过滤&lt;/p&gt;

&lt;p&gt;经过显卡历史、GPU管线的变化历史、OpenGL的功能变迁史，让我们把GPU看的更清楚。&lt;/p&gt;

&lt;h2 id=&quot;gpu-core结构&quot;&gt;GPU Core结构&lt;/h2&gt;

&lt;p&gt;我们知道现代的 CPU 里除了基本的元器件外，还有许多围绕提高执行效率的元器件，以及增加诸多功能的其他元器件。这些元器件在 GPU 里有点多余了，GPU 的整个处理过程是一个流式处理过程，没有那么多分支条件，以及复杂的依赖关系。&lt;/p&gt;

&lt;p&gt;因此我们可以把 GPU 里这些对应的元器件去掉，只留下取指令、指令译码、ALU 以及执行这些计算需要的寄存器和缓存。如图：&lt;/p&gt;

&lt;p&gt;（GPU元器件瘦身图）&lt;/p&gt;

&lt;p&gt;这样看来GPU core比CPU Core的构造简单的多了，由于传输GPU的数据并不相互依赖的，因此我们可以用很多个GPU Core来并行计算这些数据。&lt;/p&gt;

&lt;p&gt;于是就有了，多GPU Core的结构，如下图：&lt;/p&gt;

&lt;p&gt;（多个Core并行工作图）&lt;/p&gt;

&lt;p&gt;多个Core并行工作时它们使用了相同的取指器并且有相同的代码，为什么不把它们并起来呢。&lt;/p&gt;

&lt;p&gt;前面我们说过SIMD，它把4个数据一起提交并用一个指令执行它完成计算。在GPU中借鉴了SIMD，用了一种跟它很像的处理技术叫做SIMT（Single Instruction Multiple Threads），如下图：&lt;/p&gt;

&lt;p&gt;（ 单指令多数据流管线）&lt;/p&gt;

&lt;p&gt;在SIMT中，向GPU Core输入的是8个图元或片元，同时输出8个结果，每次输入多个数据到GPU Core中，并获得多个结果。SIMT 比 SIMD 更加灵活。&lt;/p&gt;

&lt;p&gt;SIMT可以把多条数据，交给不同的线程去处理。各个线程里面执行的指令流程是一样的，但是可能根据数据的不同，走到不同的条件分支。这样，相同的代码和相同的流程，可能执行不同的具体的指令。这个线程走到的是 if 的条件分支，另外一个线程走到的就是 else 的条件分支了。&lt;/p&gt;

&lt;h2 id=&quot;gpu的分支处理&quot;&gt;GPU的分支处理&lt;/h2&gt;

&lt;p&gt;我们CPU有对分支做预测，让流水线停顿更少，GPU Core也会对分支做优化处理。&lt;/p&gt;

&lt;p&gt;（GPU的分支处理）&lt;/p&gt;

&lt;p&gt;常用的GPU分支处理SIMD里，为每个指令都分配一个ALU做并行处理，用多个周期分别计算分支的两种结果。&lt;/p&gt;

&lt;p&gt;这样做就不会让流水线停滞，但是这样做有效率问题，在一个指令周期里，很多ALU是闲置的。&lt;/p&gt;

&lt;p&gt;因此在SIMD之后，SIMT（Single Instruction，Multiple Threads）技术可以变相的做分支的顺序执行，如下图：&lt;/p&gt;

&lt;p&gt;（SIMT 分支预测并行计算）&lt;/p&gt;

&lt;p&gt;在SIMT中，各个线程里面执行的指令流程是一样的，只是走的不同的分支。相同的代码和相同的流程，执行不同的分支。
可能一些线程走到的是 if 的条件分支，而另外一些线程走到的就是 else 的条件分支，这种并行计算使得计算本身无需依赖上文，也让ALU不再空闲停滞。&lt;/p&gt;

&lt;p&gt;这里简单说下解决SIMIT流水线中的卡顿问题&lt;/p&gt;

&lt;p&gt;拆分存储缓存，让上下文依赖的计算在不同时段同时计算，以提高ALU的利用率。&lt;/p&gt;

&lt;p&gt;（卡顿时启动另一条管线）&lt;/p&gt;

&lt;p&gt;（拆分整个缓存为独立缓存）&lt;/p&gt;

&lt;p&gt;GPU为了不等待分支条件而导致的停顿流水线，就要对每个分支做都做计算。分支内的数据仍然会有依赖关系，依赖关系就会造成卡顿，需要等待计算或等待获取资源。&lt;/p&gt;

&lt;p&gt;因此将原来的一整个缓存，拆分为多个缓存，使得流水线在阻塞时能更好的使用闲置ALU计算下一条数据。这样就能更好的利用ALU计算做优化了。&lt;/p&gt;

&lt;p&gt;现实GPU硬件中的物理架构&lt;/p&gt;

&lt;p&gt;前面我们说的都是抽象的GPU Core结构，下面我们来看下实际中的GPU物理架构。&lt;/p&gt;

&lt;p&gt;看到这些GPU架构可以发现它们虽然彼此有差异，但很多概念相同，下面我们俩理清一下这些架构中组建的概念：&lt;/p&gt;

&lt;p&gt;GPC（Graphics Processing Cluster） ： 图形处理集群，GPU划分多个GPC，每个GPC里有多个TPC，每个TPC里包含了多个SM和1个Rester Engine&lt;/p&gt;

&lt;p&gt;TPC（Texture Processing Cluster） ： 图像处理集群，是由若干个SM、1个纹理单元（Texture Unit）和一些逻辑控制和ALU组成。&lt;/p&gt;

&lt;p&gt;RT Core（Ray Trace Core） ： RT Core是SM里面加了一条专用的流水线(ASIC)来计算射线和三角形求交（可以访问BVH，用于光线追踪）。由于是ASIC专用电路逻辑，与shader code做求交计算相比，性能有数量级的提升。&lt;/p&gt;

&lt;p&gt;Rester Engine ： 光栅引擎，处理它接收到的三角形，并为它负责的那些部分生成像素信息（也处理背面剔除和 Z 剔除）。&lt;/p&gt;

&lt;p&gt;PolyMorp Engine：曲面引擎，是一个带有顶点提取器、视口变换的累积集群，它处理属性设置和流输出，这些都合并到了这个处理器中，极大地扩展了曲面细分和（当发送到光栅引擎时）光栅化性能。&lt;/p&gt;

&lt;p&gt;Thread Engine：线程引擎，调度线程到核的引擎&lt;/p&gt;

&lt;p&gt;SM（Stream Multiprocessor）、SMX、SMM ：SM包含GPU Core内核，指令单位，调度程序。&lt;/p&gt;

&lt;p&gt;Warp Scheduler、Dispatch Unit：负责线程束调度，将软件线程按一捆一捆（不是一个一个）的方式分配到计算核上。一个Warp由32个线程组成，Warp Scheduler的指令通过Dispatch Units派送到Core核上执行。&lt;/p&gt;

&lt;p&gt;SP（Streaming Processors）、Core ：SP有时也叫CUDA core，一个 SP 包括多个 ALU 和 FPU。SP是作用于顶点或像素数据的真正处理单元。&lt;/p&gt;

&lt;p&gt;ALU（Arithmetic Logic Unit）、FPU（Float Point Unit）：ALU 是算术和逻辑单元，FPU 是浮点单元。&lt;/p&gt;

&lt;p&gt;INT32，FP32 ：在GPU里支持单精度运算的Single Precision ALU称之为FP32 core或简称core，而把双精度运算的Double Precision ALU称之为DP unit或者FP64 core。第三代的Kepler架构里，FP64单元和FP32单元的比例是高端机1:3或者低端机1:24，到了第五代比例为1:2，低端型号里仍然保持为1:32。&lt;/p&gt;

&lt;p&gt;SFU（Special Function Unit）：执行特殊数学运算（sin、cos、log等）&lt;/p&gt;

&lt;p&gt;TENSO CORE ： 精度混合计算单元，转换不同精度之间的运算结果，用于执行矩阵乘法的计算单元，精度混合分为整数精度和浮点数精度。&lt;/p&gt;

&lt;p&gt;ROP（Render Output Unit） ：渲染输出单元 ，一个ROP内部有很多ROP单元，在ROP单元中有深度测试和Framebuffer混合，深度和颜色的设置必须是原子操作，否则两个不同的三角形在同一个像素点就会有冲突和错误。&lt;/p&gt;

&lt;p&gt;LD/ST（Load/Store Unit）：加载和存储数据&lt;/p&gt;

&lt;p&gt;Share Memory、L1 Data Cache、L1 Cache、L2 Cache ：共享内存，以及多级的高速缓存&lt;/p&gt;

&lt;p&gt;RF（Register File）：寄存器堆，多个寄存器组成的阵列&lt;/p&gt;

&lt;p&gt;Instruction Cache ：指令缓存&lt;/p&gt;

&lt;p&gt;未完待续…&lt;/p&gt;

&lt;p&gt;参考资料：&lt;/p&gt;

&lt;p&gt;《How Shader Cores Work》&lt;/p&gt;

&lt;p&gt;https://engineering.purdue.edu/~smidkiff/KKU/files/GPUIntro.pdf&lt;/p&gt;

&lt;p&gt;《CPU体系结构》&lt;/p&gt;

&lt;p&gt;https://my.oschina.net/fileoptions/blog/1633021&lt;/p&gt;

&lt;p&gt;《深入理解CPU的分支预测(Branch Prediction)模型》&lt;/p&gt;

&lt;p&gt;https://zhuanlan.zhihu.com/p/22469702&lt;/p&gt;

&lt;p&gt;《分析Unity在移动设备的GPU内存机制（iOS篇）》&lt;/p&gt;

&lt;p&gt;https://www.jianshu.com/p/68b41a8d0b37&lt;/p&gt;

&lt;p&gt;《针对移动端TBDR架构GPU特性的渲染优化》&lt;/p&gt;

&lt;p&gt;https://gameinstitute.qq.com/community/detail/123220&lt;/p&gt;

&lt;p&gt;《A look at the PowerVR graphics architecture: Tile-based rendering》&lt;/p&gt;

&lt;p&gt;https://www.imaginationtech.com/blog/a-look-at-the-powervr-graphics-architecture-tile-based-rendering/&lt;/p&gt;

&lt;p&gt;《A look at the PowerVR graphics architecture: Deferred rendering》&lt;/p&gt;

&lt;p&gt;https://www.imaginationtech.com/blog/the-dr-in-tbdr-deferred-rendering-in-rogue/&lt;/p&gt;

&lt;p&gt;《深入GPU硬件架构及运行机制》&lt;/p&gt;

&lt;p&gt;https://www.cnblogs.com/timlly/p/11471507.html&lt;/p&gt;

&lt;p&gt;《深入浅出计算机组成原理》&lt;/p&gt;

&lt;p&gt;https://time.geekbang.org/column/article/105401?code=7VZ-Md9oM7vSBSE6JyOgcoQhDWTOd-bz5CY8xqGx234%3D&lt;/p&gt;

&lt;p&gt;《Nvidia Geforce RTX-series is born》&lt;/p&gt;

&lt;p&gt;https://www.fudzilla.com/reviews/47224-nvidia-geforce-rtx-series-is-born?start=2&lt;/p&gt;

&lt;p&gt;《渲染管线与GPU（Shading前置知识）》&lt;/p&gt;

&lt;p&gt;https://zhuanlan.zhihu.com/p/336999443&lt;/p&gt;

&lt;p&gt;《剖析虚幻渲染体系（12）- 移动端专题Part 1（UE移动端渲染分析）》&lt;/p&gt;

&lt;p&gt;https://www.cnblogs.com/timlly/p/15511402.html&lt;/p&gt;

&lt;p&gt;《tpc-texture-processing-cluster》&lt;/p&gt;

&lt;p&gt;https://gputoaster.wordpress.com/2010/12/11/tpc-texture-processing-cluster/&lt;/p&gt;

&lt;p&gt;《Life of a triangle - NVIDIA’s logical pipeline》&lt;/p&gt;

&lt;p&gt;https://developer.nvidia.com/content/life-triangle-nvidias-logical-pipeline&lt;/p&gt;

&lt;p&gt;《Rasterisation wiki》&lt;/p&gt;

&lt;p&gt;https://en.wikipedia.org/wiki/Rasterisation&lt;/p&gt;

&lt;p&gt;《PolyMorph engine and Data Caches by Hilbert Hagedoorn》&lt;/p&gt;

&lt;p&gt;https://www.guru3d.com/articles-pages/nvidia-gf100-(fermi)-technology-preview,3.html&lt;/p&gt;

&lt;p&gt;《NVIDIA GPU的一些解析》&lt;/p&gt;

&lt;p&gt;https://zhuanlan.zhihu.com/p/258196004&lt;/p&gt;

&lt;p&gt;《tensor-core-performance-the-ultimate-guide》&lt;/p&gt;

&lt;p&gt;https://developer.download.nvidia.cn/video/gputechconf/gtc/2019/presentation/s9926-tensor-core-performance-the-ultimate-guide.pdf&lt;/p&gt;

&lt;p&gt;《Understanding the Understanding the graphics pipeline》&lt;/p&gt;

&lt;p&gt;https://www.seas.upenn.edu/~cis565/LECTURES/Lecture2%20New.pdf&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247485051&amp;amp;idx=1&amp;amp;sn=e98f1e1971d56ce6543926b4f9bad204&amp;amp;chksm=fc22637ccb55ea6a4c16c5c0d8fd43cff936350490d79f1e377b61e4914300c39c4fc2911da1&amp;amp;token=53415989&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>游戏引擎架构#4 低阶渲染器（2）</title>
   <link href="http://www.luzexi.com/2021/11/08/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B048"/>
   <updated>2021-11-08T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/11/08/读书笔记48</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484981&amp;amp;idx=1&amp;amp;sn=b5199f84ed73d28916dbb9143eeeb992&amp;amp;chksm=fc226332cb55ea2417b89e499284ab3a552ce591caba09d637912306503e4b457ed45c2e1d2d&amp;amp;token=1023740201&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;背景&quot;&gt;背景：&lt;/h2&gt;

&lt;p&gt;作为游戏开发从业者，从业务到语言到框架到引擎，积累了一些知识和经验，特别是在看了几遍《游戏引擎架构》后对引擎架构的理解又深入了些。&lt;/p&gt;

&lt;p&gt;近段时间有对引擎剖析的想法，正好借这书本对游戏引擎架构做一个完整分析。&lt;/p&gt;

&lt;p&gt;此书用简明、清楚的方式覆盖了游戏引擎架构的庞大领域，巧妙地平衡了广度与深度，并且提供了足够的细节。&lt;/p&gt;

&lt;p&gt;借助《游戏引擎架构》这本书、结合引擎源码和自己的经验，深入分析游戏引擎的历史、架构、模块，最后通过实践简单引擎开发来完成对引擎知识的掌握。&lt;/p&gt;

&lt;p&gt;游戏引擎知识面深而广，所以对这系列的文章书编写范围做个保护，即不对细节进行过多的阐述，重点剖析的是架构、流程以及模块的运作原理。&lt;/p&gt;

&lt;p&gt;同时《游戏引擎架构》中部分知识太过陈旧的部分，会重新深挖后总结出自己的观点。&lt;/p&gt;

&lt;h2 id=&quot;概述&quot;&gt;概述：&lt;/h2&gt;

&lt;p&gt;本系列文章对引擎中的重要的模块和库进行详细的分析，我挑选了十五个库和模块来分析：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;时间库&lt;/li&gt;
  &lt;li&gt;自定义容器库&lt;/li&gt;
  &lt;li&gt;字符串散列库&lt;/li&gt;
  &lt;li&gt;内存管理框架&lt;/li&gt;
  &lt;li&gt;RTTI与反射模块&lt;/li&gt;
  &lt;li&gt;图形计算库&lt;/li&gt;
  &lt;li&gt;资产管理模块&lt;/li&gt;
  &lt;li&gt;低阶渲染器&lt;/li&gt;
  &lt;li&gt;剔除与合批模块&lt;/li&gt;
  &lt;li&gt;动画模块&lt;/li&gt;
  &lt;li&gt;物理模块&lt;/li&gt;
  &lt;li&gt;UI底层框架&lt;/li&gt;
  &lt;li&gt;性能剖析器的核心部分&lt;/li&gt;
  &lt;li&gt;脚本系统&lt;/li&gt;
  &lt;li&gt;视觉效果模块&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;本篇内容为列表中的第8个部分的第1节。&lt;/p&gt;

&lt;h2 id=&quot;正文&quot;&gt;正文：&lt;/h2&gt;

&lt;p&gt;简单回顾下前文&lt;/p&gt;

&lt;p&gt;前文我们聊了下显卡在计算机硬件主板中的位置与结构，知道了CPU、GPU的通信介质，并简单介绍了手机上的主板结构。本篇开头对上一篇做一些内容补充，PC和手机的不同硬件组织，以及CPU与其他芯片的通信过程。&lt;/p&gt;

&lt;p&gt;下面我们开始这篇内容&lt;/p&gt;

&lt;h3 id=&quot;本次内容会围绕gpu来写从硬件架构到软件驱动再到引擎架构目标是帮大家理解gpu硬件的运作原理理解图形接口的架构理解引擎低阶渲染器的架构&quot;&gt;本次内容会围绕GPU来写，从硬件架构到软件驱动再到引擎架构，目标是帮大家理解GPU硬件的运作原理，理解图形接口的架构，理解引擎低阶渲染器的架构。&lt;/h3&gt;

&lt;p&gt;目录：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;主板结构中的显卡&lt;/li&gt;
  &lt;li&gt;GPU功能发展史&lt;/li&gt;
  &lt;li&gt;GPU与CPU的差异&lt;/li&gt;
  &lt;li&gt;GPU硬件特点&lt;/li&gt;
  &lt;li&gt;图形驱动程序架构&lt;/li&gt;
  &lt;li&gt;引擎低阶渲染架构&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;继续上篇未结束的内容&lt;/p&gt;

&lt;p&gt;说硬件结构时，我们常常从台式 PC 开始说起，因为智能手机本质上是袖珍型计算机，具有一些额外的无线电，并且在功耗、热量输出等方面极其受限的条件下运行。PC 主要由主板芯片组组成（通常分为连接处理器和内存“北桥”（例如 Intel 的 P45）和连接各种外围总线（例如 PCI-Express、USB 等）的“南桥”（例如 Intel 的 ICH10R） )、中央处理器（例如 CPU – Intel 的 Core 2 Duo）、随机存储器内存 (RAM)、永久存储器（硬盘或固态硬盘）、图形处理单元（例如 GPU – ATI 的 Radeon HD5890） 、电源和多个连接到 PCI 或 PCI-Express 总线的外围设备（例如，内置声音芯片组或附加 Wi-Fi 卡）。&lt;/p&gt;

&lt;p&gt;（三星手机拓扑图：图片来源网络）&lt;/p&gt;

&lt;p&gt;（iPhone6p A8 正面图：图片来源网络）&lt;/p&gt;

&lt;p&gt;（iPhone6p A8 背面图：图片来源网络）&lt;/p&gt;

&lt;p&gt;（苹果A8芯片内部结构：图片来源网络）&lt;/p&gt;

&lt;p&gt;智能手机也具有所有这些组件，只是集成度更高而已。一些主要芯片通常可以包含 CPU、GPU、其他专用协处理器、主板总线、内存控制器、LCD 控制器、声音芯片组、CMOS 摄像头接口、板载内存 ，以及一些外围设备（例如 Cell、Wifi 和蓝牙无线电）。 “应用处理器”是负责一般处理的芯片（类似于 CPU + 主板芯片组），并且可能内置了一些其他功能。 “基带处理器”负责蜂窝无线通信。&lt;/p&gt;

&lt;p&gt;智能手机与 PC 如此相似，那么为什么我们今天在智能手机中看不到像 Core 2 Duo 这样的 x86 CPU？答案是，Core 2 Duo 消耗的功率和产生的热量远远超出手机的可接受范围。典型的台式机 Core 2 Duo CPU 的功耗最高可达 65W，并且需要一个主动冷却系统来防止过热。即使是英特尔的 Atom 芯片也消耗 4W（峰值 TDP），而整个手机通常必须满足低于 1W 的功率。&lt;/p&gt;

&lt;p&gt;今天我们很多智能手机都使用 ARM 芯片， ARM 的运作方式与Intel截然不同。 Intel 自己设计和构建所有处理器，而 ARM 则创建指令集，任何跟随它们的 CPU 都将能够运行与 ARM 兼容的代码。 它还为适合其指令集的处理器创建参考设计，这使公司能够围绕 ARM 的核心设计轻松生产自己的芯片组。 一些公司，如英伟达、三星和德州仪器，只是简单地许可并采用 ARM CPU 参考设计，而其他公司，如高通和 Marvell，只许可指令集并创建自己的处理器以适应它们。 第一个 ARMv7 参考设计是 Cortex A8，其架构可在今天的智能手机中找到。很久以前低端智能手机倾向于使用实现 ARMv6 指令集的 ARM11 芯片，现在已经不复存在。&lt;/p&gt;

&lt;p&gt;这里再补充一下前文的硬件通信过程，每个带有处理程序的硬件都有自己的芯片，CPU是整个电脑的主要计算芯片，GPU则被单独拆分出来作为图像的处理芯片，除了这两个大家熟知的芯片，还有内存芯片，硬盘芯片，声卡芯片，网卡芯片等，这些芯片都通过总线进行数据交互。其中前面我们提到南桥和北桥芯片桥接了快慢不同的芯片之间的通信，让不同种类的设备通信效率更高更有序。&lt;/p&gt;

&lt;p&gt;（PC端CPU与GPU通信结构图）&lt;/p&gt;

&lt;p&gt;（手机端CPU与GPU通信结构图）&lt;/p&gt;

&lt;h3 id=&quot;cpu与其他芯片之间的通信步骤可以简单理解为cpu通过总线发送指令数据到其他芯片芯片收到指令并处理后再通过总线将反馈数据给cpu中间处理过程也会与内存芯片或其他芯片通信后再反馈给cpu&quot;&gt;CPU与其他芯片之间的通信步骤，可以简单理解为，CPU通过总线发送指令数据到其他芯片，芯片收到指令并处理后，再通过总线将反馈数据给CPU，中间处理过程也会与内存芯片或其他芯片通信后再反馈给CPU。&lt;/h3&gt;

&lt;p&gt;（CPU用虚拟地址访问内存拓扑图）&lt;/p&gt;

&lt;h3 id=&quot;cpu-在访问内存时需要通过-mmu-把虚拟地址转化为物理地址再通过总线访问内存mmu-开启后-cpu-看到的所有地址都是虚拟地址cpu-把这个虚拟地址发给-mmu-后mmu-会在页表里查出这个虚拟地址对应的物理地址再通过总线去访问ddr内存芯片&quot;&gt;CPU 在访问内存时需要通过 MMU 把虚拟地址转化为物理地址，再通过总线访问内存。MMU 开启后 CPU 看到的所有地址都是虚拟地址，CPU 把这个虚拟地址发给 MMU 后，MMU 会在页表里查出这个虚拟地址对应的物理地址，再通过总线去访问DDR内存芯片。&lt;/h3&gt;

&lt;p&gt;从这个芯片通信角度看，我们可以明白，总线传输数据的速度，限制了计算机的运行速度。因此我们在优化计算机程序时会常常去减少CPU与GPU之间通信量，原因就是在无法增加带宽上限这个大前提下，尽量减少它们之间通信数据量，从而减少访问消耗时间。&lt;/p&gt;

&lt;h2 id=&quot;gpu功能发展史&quot;&gt;GPU功能发展史&lt;/h2&gt;

&lt;p&gt;第一篇中我们介绍过一些图形接口的发展历史，现在我们来介绍一下GPU硬件的发展历史。硬件的发展史其实是一个商业的竞争过程，中间夹杂着很多商业战略与决策。尤其是当大家都看到GPU这块高新的技术’蛋糕’，有一系列公司参与进来相互竞争。因此这里不过多的介绍商业上的活动与竞争，而是专注于介绍GPU功能的发展过程。&lt;/p&gt;

&lt;p&gt;第一个真正的3D图形卡始于早期的显示控制器，即视频移位器和视频地址生成器（video shifters and video address generators）。它们充当主处理器和显示器之间的直通通道（pass-through）。传入的数据流被转换为串行位图视频输出，例如亮度，颜色以及垂直和水平复合同步，这将像素行保持在显示生成中，并同步每条连续行以及消隐间隔（时间间隔为 结束一条扫描线并开始下一条扫描线）。&lt;/p&gt;

&lt;p&gt;（图片来源网络）&lt;/p&gt;

&lt;p&gt;1970年代开始这些视频适配器出现了一系列的改进，主要兼容了不同分辨率以及不同的视频信号。&lt;/p&gt;

&lt;p&gt;1976年，ANTIC使用直接存储器访问（DMA）处理2D显示指令。&lt;/p&gt;

&lt;p&gt;1978年，英特尔82720图形卡芯片，它能够以256x256的分辨率（或以512x512的单色）显示八种颜色数据。其32KB的显示内存足以绘制线，弧，圆，矩形和字符位图。该芯片还提供了缩放，屏幕分区和滚动的功能。&lt;/p&gt;

&lt;p&gt;1979年，SGI推出了用于工作站的IRIS图形卡-GR1.x图形卡板，其中提供了用于颜色选项，几何图形卡，Z缓冲区和覆盖/底层的单独的外接（子）板。&lt;/p&gt;

&lt;p&gt;从第一块图形卡发明到现在，整个过程可以分为，视频适配器时代，2D时代、3D时代启程、3D时代崛起、3D时代巅峰，这4个部分。&lt;/p&gt;

&lt;p&gt;为此我画了一副时序图并标出了重要节点，便于大家理解显卡的发展史，同时以文字形式告知大家图形显卡的发展历史。&lt;/p&gt;

&lt;p&gt;（显卡发展史）&lt;/p&gt;

&lt;h2 id=&quot;2d时代&quot;&gt;2D时代&lt;/h2&gt;

&lt;p&gt;1981年, IBM推出了个人电脑时，它提供了两种显卡，一种是“单色显卡”(简称 MDA), 一种是 “彩色绘图卡” (简称 CGA), 从名字上就可以看出，MDA是与单色显示器配合运用的, 它可以显示80行x25列的文数字, CGA则可以用在RGB的显示屏上, 它可以绘制的图形和文数字资料。&lt;/p&gt;

&lt;p&gt;1982年，IBM又推出了MGA（Monochrome Graphic Adapter）, 又称Hercules Card (大力士卡),  除了能显示图形外，还保留了原来 MDA 的功能。&lt;/p&gt;

&lt;p&gt;这些显卡均为采纳使用数字方式的，直到MCGA（Multi-Color Graphics Array）的出现，才揭开了采纳使用模拟方式的显卡的序幕。MCGA是整合在 PS/2 Model 25和30上的影像系统。它采纳使用了Analog RGA影像信号, 解析度可高达640x480, 数位RGB和类比RGB不同的的方就像是ON-OFF式切换和微调式切换之间的差别。&lt;/p&gt;

&lt;p&gt;1986年ATI推出了第一款产品，即OEM颜色仿真卡。它用于通过9针DE-9连接器将黑色背景的单色绿色，琥珀色或白色磷光体文本输出到TTL监视器，该卡至少配备了16KB的内存。&lt;/p&gt;

&lt;p&gt;1987年，ATI在其OEM产品线中增加了Graphics Solution Plus系列，该产品线将IBM PC / XT ISA 8位总线用于基于Intel 8086/8088的IBM PC。该芯片通过DIP开关支持MDA，CGA和EGA图形卡模式。ATI EGA 800：16色VGA仿真，支持800x600。&lt;/p&gt;

&lt;p&gt;VGA（Video Graphic Array）即显示绘图阵列，它IBM是在其 PS/2 的Model 50, 60和80内建的影像系统。它的数字模式可以达到720x400色, 绘图模式则可以达到640x480x16色, 以及320x200x256色，这是显卡首次可以相应情况下最大限度显示256种色彩，而这些模式更成为其后所有显卡的共同标准。&lt;/p&gt;

&lt;p&gt;1988年，带有游戏控制器端口和复合输出选项的Small Wonder Graphics解决方案问世（用于CGA和MDA仿真），以及具有扩展EGA和16位VGA支持的EGA Wonder 480和800+，以及VGA 新增了VGA和SVGA支持的Wonder和Wonder 16。&lt;/p&gt;

&lt;p&gt;1988年，Trident 8900/9000显卡，它第一次使显卡成为一个独立的配件出现在电脑里，而不再是集成的一块芯片。而后其推出的Trident 9685更是第一代3D显卡的代表。不过真正称得上开启3D显卡大门的却应该是1996年的GLINT 300SX，虽然当时其3D功能非常简单，却具有里程碑的意义。&lt;/p&gt;

&lt;p&gt;1989年推出了更新的VGA Wonder / Wonder 16系列，其中包括降低成本的VGA Edge 16（Wonder 1024系列）。新功能包括一个总线鼠标端口，并支持VESA功能连接器。这是一个金手指连接器，类似于缩短的数据总线插槽连接器，它通过带状电缆链接到另一个视频控制器，以绕过拥挤的数据总线。&lt;/p&gt;

&lt;p&gt;1991年，Wonder系列的更新继续向前发展。WonderXL卡增加了VESA 32K颜色兼容性和Sierra RAMDAC，从而将最大显示分辨率提高到640x480 @ 72Hz 或 800x600 @ 60Hz。&lt;/p&gt;

&lt;p&gt;1991年5月，ATI的Mach系列与Mach8一同推出。它以芯片或电路板的形式出售，可以通过编程接口（AI）卸载有限的2D绘图操作，例如线条画，颜色填充和位图组合（Bit BLIT）.ATI添加了Wonder的一种变体 XL在扩展PCB上集成了Creative Sound Blaster 1.5芯片。它被称为VGA Stereo-F / X，它能够模拟Sound Blaster单声道文件中的立体声，并且其质量接近FM广播质量。ATI图形卡Ultra ISA（Mach8 + VGA）,将Mach8与VGA Wonder +的图形卡核心（28800-2）结合在一起以实现其3D功能。&lt;/p&gt;

&lt;p&gt;1992年1月，Silicon Graphics Inc（SGI）发布了OpenGL 1.0，这是一个针对2D和3D图形卡的多平台供应商不可知的应用程序编程接口（API）。OpenGL是从SGI专有的API（称为IRIS GL（集成的栅格成像系统图形卡库））演变而来的。最初，OpenGL瞄准的是基于UNIX的专业市场，但是由于开发人员对扩展实施的友好支持，很快将其用于3D游戏。同时微软正在开发自己的竞争对手Direct3D API，并没有确保OpenGL在Windows下也能正常运行。&lt;/p&gt;

&lt;p&gt;1993年11月，ATI宣布68890 PC电视解码器芯片的发布，该芯片首次在Video-It卡里面亮相。借助板载Intel i750PD VCP（视频压缩处理器），该芯片能够以320x240 @ 15 fps或160x120 @ 30 fps捕获视频，并能够实时压缩/解压缩。它还能够通过数据总线与图形卡板通信，从而无需使用加密狗或端口和带状电缆。&lt;/p&gt;

&lt;p&gt;1995年，ATI的Mach8发布，创造了许多著名的首创。它成为第一个以Xclaim形式在PC和Mac计算机上使用的图形卡适配器，并且与S3的Trio一起提供了全动态视频播放加速功能。&lt;/p&gt;

&lt;h2 id=&quot;3d时代开启&quot;&gt;3D时代开启&lt;/h2&gt;

&lt;p&gt;1995年5月，nVidia推出了他们的第一款图形卡芯片NV1，并成为首款能够进行3D渲染，视频加速和集成GUI加速的商业图形卡处理器。供应商发布显卡支持规格的主板（Diamond Edge 3D），D3D（Diamond Edge 3D）图形卡API确认依赖于渲染三角形多边形，而NV1则使用四边形纹理映射。nVidia通过驱动程序添加了有限的D3D兼容性，以将三角形包装为二次曲面，但是市面上仍然很少针对NV1量身定制的游戏。&lt;/p&gt;

&lt;p&gt;1995年11月，ATI宣布了他们的首个3D加速器芯片3D Rage（也称为Mach 64 GT）。&lt;/p&gt;

&lt;p&gt;1995年，3Dfx推出了业界的口碑极佳的3D图形加速卡：Voodoo。3Dfx的专利技术Glide引擎接口一度称霸了整个3D世界，直至D3D和OpenGL的出现才改变了这种局面。&lt;/p&gt;

&lt;p&gt;Voodoo标配为4Mb EDO显存，可以提供在640×480分辨率下3D显示速度和最华丽的画面。Voodoo也有硬伤，它只是一块具有3D加速功能的子卡，运用时需搭配一块具有2D功能的显卡，因此当时S3 765+Voodoo是为人津津乐道的黄金组合。&lt;/p&gt;

&lt;p&gt;S3 765显卡是当时兼容机的标准配置，最大限度支持2MB EDO显存，可以实现高分辨率显示，可以支持1024×768的分辨率，并且在低分辨率下支持最大限度32Bit真彩色，而且性能和价格比也较强。&lt;/p&gt;

&lt;p&gt;1995年，VideoLogic开发了一种基于图块的延迟渲染技术（TBDR），该技术在纹理、阴影和光照应用于剩下的渲染之前，抛弃了所有可见的几何形状，从而消除了对大规模z缓冲(在最终渲染中去除遮挡/隐藏的像素)的需求。这个过程产生的框架被分割成矩形块，每个图块都可以自行进行多边形渲染并发送到输出。一旦计算了帧所需的像素并剔除了多余的多边形(z缓冲只在平铺层发生)，对多边形渲染就开始了，这样就只需要进行最基本的计算。&lt;/p&gt;

&lt;p&gt;1995年，Rendition的VéritéV1000成为第一张具有可编程核心的显卡，它使用了基于MIPS的RISC处理器和Pixel Pipelines(像素管线)。处理器负责设置和组织管线的工作负载。 Vérité1000最初于1995年底开发，后来成为Microsoft用于开发Direct3D的主板之一。&lt;/p&gt;

&lt;p&gt;1996年 3DLabs 研制出Glint，该公司诞生于杜邦的Pixel图形卡部门。GLINT 300SX处理器能够进行OpenGL渲染，片段处理和光栅化。GLINT 300SX增加了2MB的内存。它为纹理和Z缓冲区使用了1MB，为帧缓冲区使用了1MB，但是还提供了一个选项，以增加VRAM的Direct3D兼容性。&lt;/p&gt;

&lt;p&gt;1997年3月ATI推出 3D Rage Pro，它在4MB格式下几乎可以媲美Voodoo Graphics的性能。在使用8MB和AGP接口时，其性能则优于3Dfx卡。它扩展了4kB高速缓存并增加了边缘抗锯齿功能，该显卡改进了Rage II的透视校正，纹理处理能力以及三线性滤波性能。还集成了一个浮点单元，以减少对CPU的依赖以及对DVD的硬件加速和显示支持。&lt;/p&gt;

&lt;p&gt;1997年4月，nVidia推出了RIVA 128（实时交互式视频和动画加速器），并通过渲染三角形多边形增加了Direct3D兼容性，使用新的350nm工艺，并开发了RAMDAC和视频转换器，这是nVidia具有里程碑意义的显卡。&lt;/p&gt;

&lt;p&gt;1998年1月，英特尔发布i740，它结合了R3D/100上两种不同的图形和纹理芯片的资源，实现了AGP纹理，纹理被上传到系统内存中(渲染缓冲区也可以存储在RAM中)。此前一些设计会选择使用显卡的帧缓冲区用来保存纹理，如果帧缓冲区饱和或纹理太大而无法存储在本地图形内存中，则纹理交换到系统RAM中。为了最大程度地减少延迟，英特尔的设计使用了AGP Direct Memory Execute（DiME）功能，该功能仅调用光栅化所需的那些纹理，其余的存储在系统RAM中。性能和图像质量可以接受的，性能大致与上一年的高端产品相匹配，因此该产品大放异彩。&lt;/p&gt;

&lt;p&gt;1998年3月，3Dfx推出Voodoo2。Voodoo2本身带有8Mb/12Mb EDO显存，PCI接口，卡上有双芯片，可以做到单周期多纹理运算。缺点是它的卡身很长，并且芯片发热量相当大，依然只作为一块3D加速子卡，需要一块2D显卡的支持。Voodoo2的推出使得3D加速又到达了一个新的里程碑，依仗Voodoo2的效果、画面和速度，征服了不少当时盛行一时的3D游戏，比如Fifa98，NBA98，Quake2等等。&lt;/p&gt;

&lt;p&gt;1998年7月，Matrox公司发布MGA G200继承了自己超一流的2D水准，3D方面有了革命性的提高，不但可以提供和Voodoo2差不多的处理速度和特技效果，另外还支持DVD硬解码和视频输出，并且独一无二的首创了128位独立双重总线技术，大大提高了性能，配合当时相当走红的AGP总线技术，G200也赢得了不少用户的喜爱。&lt;/p&gt;

&lt;p&gt;1998年的S3的野人系列Savage系列显卡，Savage3D采纳使用128位总线结构及单周期三线性多重贴图技术，最大像素填充率达到了125M Pixels/s，三角形生成速率也达到了每秒500万个。通过S3新设计的AGP引擎和S3TC纹理压缩技术，支持Direct3D与OpenGL，最大显存容量可达8MB SGRAM或SDRAM，支持AGP 4×规范。相应情况下也支持当时流行的如反射和散射、Alpha混合、多重纹理、衬底纹理、边缘抗锯齿、16/24位Z-buffering、Tri-linear Filtering（三线性过滤技术）、S3TC纹理压缩技术等技术。可惜就是受到驱动程序不兼容的严重影响，最终在99年时惨淡收场。&lt;/p&gt;

&lt;h2 id=&quot;3d时代崛起&quot;&gt;3D时代崛起&lt;/h2&gt;

&lt;p&gt;1999年2月，S3发布Savage4，成为的第一张支持多重纹理的显卡，也是第一张支持AGP 4x接口的显卡。&lt;/p&gt;

&lt;p&gt;1999年，NVidia推出TNT2 Ultra、TNT2和TNT2 M64三个版本的芯片，后来又有PRO和VANTA两个版本。这种分类方式也促使后来各个生产厂家对同一芯片进行高中低端的划分，以满足不同层次的消费需要。TNT系列配置了8Mb到32Mb的显存，支持AGP2X/4X，支持32位渲染等等众多技术。nVidia能战胜Voodoo3，与3Dfx公司推行的策略迫使众多生产厂家投奔nVidia也不无关系，促进了TNT系列的推广。&lt;/p&gt;

&lt;p&gt;1999年，Matrox推出 MGA G400，它拥有16Mb/32Mb的显存容量，支持AGP 2X/4X，还有支持大纹理以及32位渲染等等，独特漂亮的EMBM硬件凹凸贴图技术，营造出的完美凹凸感并能实现动态光影效果的技术，并且G400拥有优秀的DVD回放能力，只是价格有些昂贵。通过双显示控制器（被Matrox称为DualHead）驱动两台显示器的能力开始了公司的多显示器支持趋势。&lt;/p&gt;

&lt;p&gt;1999年，nVidia终于爆发了，它在99年末推出了当前革命性的显卡—Geforce 256，彻底打败了3Dfx。代号NV10的GeForce 256支持Cube-Environment Mapping，完全的硬件T&amp;amp;L（Transform &amp;amp; Lighting），把原来有CPU计算的数据直接交给显示芯片处理，大大解放了CPU，也提高了芯片的运用效率。GeForce256拥有4条图形纹理通道，单周期每条通道处理两个象素纹理，工作频率120MHz，全速可以达到480Mpixels/Sec，支持SDRAM和DDR RAM，运用DDR的产品能更好的发挥GeForce256的性能。其不足之处就在于采纳使用了0.22纳米的工艺技术，发热量相对其它的来说很高的。&lt;/p&gt;

&lt;p&gt;2000年7月，3Dfx发布的Voodoo 5引入了T-buffer技术，作为变换和照明的一种替代方法，它基本上采用了一些渲染帧并将它们聚合为一个图像的方法。这产生了稍微模糊的图片，当按帧顺序运行时，可以平滑动画的运动。&lt;/p&gt;

&lt;p&gt;2000年末，3Dfx最终被nVidia收购。&lt;/p&gt;

&lt;h2 id=&quot;3d时代巅峰&quot;&gt;3D时代巅峰&lt;/h2&gt;

&lt;p&gt;2000年，ATI依仗T&amp;amp;L技术打开市场，在经历“曙光女神”的失败后，ATI推出了自己的T&amp;amp;L芯片RADEON 256。RADEON和NVIDIA一样具有高低端的版本，完全硬件T&amp;amp;L，Dot3和环境映射凹凸贴图，还有两条纹理流水线，可以相应情况下处理三种纹理。最出彩的是HYPER-Z技术，大大提高了RADEON显卡的3D速度，拉近了与GEFORCE 2系列的距离，ATI的显卡开始在市场占据主导地位。&lt;/p&gt;

&lt;p&gt;2000年4月，nVidia发布了GeForce 2 GTS（GigaTexel Shader），称为Nvidia Shading Rasterizer，它允许将高光阴影，体积爆炸，折射，波浪，顶点混合，阴影体积，凹凸贴图和高程贴图等效果应用于每个像素通过硬件的基础。&lt;/p&gt;

&lt;p&gt;2000年8月，ATI Radeon DDR上市。在卓越的T&amp;amp;L实现和对即将推出的DirectX 8特性的支持下，Radeon DDR与GeForce 2 GTS一起，通过将对接口的支持集成到芯片本身，引入了DVI输出的使用。&lt;/p&gt;

&lt;p&gt;2001年，nVidia推出的Geforce 3系列，这张卡成为了该领域的新王者，GeForce 3显卡增加了可编程T&amp;amp;L功能，可以对几乎所有的画面效果提供硬件支持。GeForce 3总共具有4条像素管道，填充速率最大限度可以达到每秒钟800 Mpixels。支持DirectX 8，多重采样AA，梅花AA（基本上是2xMSAA +后期处理模糊），8x各向异性滤光以及无与伦比的处理8xAF +三线性滤光的能力以及可编程的顶点着色器，用于更紧密地控制多边形网格运动和更流畅的动画序列。还有LMA(光速内存架构)支持——基本上是Nvidia的HyperZ版本——用于剔除隐藏在屏幕上其他像素后面的像素(Z遮挡剔除)，以及压缩和解压数据以优化带宽使用(Z压缩)。最后，Nvidia实现了负载平衡算法，将其称为Crossbar内存控制器，该控制器由四个独立的内存子控制器（与行业标准的单个控制器相对）组成，从而可以更有效地路由传入的内存请求。&lt;/p&gt;

&lt;p&gt;2001年，ATI则推出Radeon 8500/7500系列，采纳使用0.15微米工艺制造，包括6000万个晶体管，采纳使用了不少新技术(如Truform、Smartshader等)。并根据显卡的核心/显存工作频率分成不同的档次——核心/显存分别为275/550MHz的标准版，核心/显存为250/500MHz的RADEON 8500LE，生产核心/显存频率分别为300/600MHz的Ultra版，以及中端的Radeon 7500，低端的Radeon 7200，7000等产品。值得一提的是Radeon 8500还支持双头显示技术。&lt;/p&gt;

&lt;p&gt;2002年，ATI R300 GPU发布，由最初构成ArtX核心的团队开发，交付出色。它是第一个提供DirectX 9.0支持的应用程序，并且扩展了第一个支持着色器模型2.0，顶点着色器2.0和像素着色器2.0的体系结构。&lt;/p&gt;

&lt;p&gt;2002年，nVidia与ATI的竞争更加白热化。为巩固其图形芯片市场霸主的位，nVidia推出了Geforce 4系列，GeForce4 Ti系列择定确定是最具性能和价格比的，其代号是NV25，主要针对当时的高端图形市场，是DirectX 8时代下最劲爆强大的GPU图形处理器。芯片内部包含的晶体管数量高达6千3百万，运用0.15微米工艺生产，采纳使用了新的PBGA封装，运行频率达到了300MHz，配合频率为650MHz DDR显存，可以实现每秒49亿次的采样。GeForce4 Ti核心内建4条渲染流水线，每条流水线包含2个TMU（材质贴图单元）。Geforce 4系列从高到低，横扫了整个显卡市场。&lt;/p&gt;

&lt;p&gt;2002年，ATI推出R9700/9000/9500系列，首次支持DirectX 9，使其在与NVidia的竞争中抢得先机。同时R9700支持AGP 8X、DirectX 9，核心频率是300MHz，显存时钟是550MHz。RADEON 9700实现了可程序化的革命性硬件架构。符合AGP 8X最新标准，配有8个平等处理的渲染管线，每秒可处理25亿个像素，4个并列的几何处理引擎能处理每秒3亿个形迹及光效多边形。R9000则面向低端的产品，R9500则对标Ti4200。&lt;/p&gt;

&lt;p&gt;2003年的显卡市场依旧为N系与A系所统治。nVidia的Gf FX 5800（NV30）系列拥有32位着色，颜色画面有质的提高，在基础上推出的GeForce FX 5900，提高了晶体管数，降低了核心频率与显存频率，改用了256BIT DDR以提高显存带宽。&lt;/p&gt;

&lt;p&gt;2003年7月，nVidia推出了GF FX 5950/5700系列，以取代GF FX 5900/5600。新的Detonator FX驱动程序大大改善了AA和AF。
2003年9月，ATI推出了RADEON 9800/pro/SE/XT，依仗其超强的性能以及较低的售价，再次打败GF GX 5800。&lt;/p&gt;

&lt;p&gt;2004年是ATI大放异彩的一年，其最大的功臣却是来自于面向中低端的Radeon 9550。2004年最具性能和价格比的显卡，让ATI在低端市场呼风唤雨。R9550基于RV350核心，采纳使用0.13微米制程，核心频率为250MHz，显存频率为400MHz，4条渲染管道，1个纹理单元，相应情况下兼容64bit和128bit。&lt;/p&gt;

&lt;p&gt;2005年，Nvidia推出6000系列显卡，特性包括DirectX 9.0c支持、shader model 3.0(尽管显卡从未完全利用这一点)、Nvidia的PureVideo解码和播放引擎，以及SLI支持。&lt;/p&gt;

&lt;p&gt;2006年10月25日，ATI被AMD收购，总价为54亿美元。&lt;/p&gt;

&lt;p&gt;–&lt;/p&gt;

&lt;p&gt;NVIDIA发展表：&lt;/p&gt;

&lt;p&gt;1995年，NV1&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;50K triangles/sec
1M pixel ops/sec
1M transistors
16-bit color
Nearest filtering
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;1997年，Riva 128 (NV3), DX3&lt;/p&gt;

&lt;p&gt;1998 – Riva TNT (NV4), DX5&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;32位颜色,
24位Z缓存,
8位模板缓存
双纹理,
双线性过滤
每时钟2像素 (2 ppc)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;1999 - GeForce 256（NV10）&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;固定管线，
支持DirectX 7.0
硬件T&amp;amp;L（Transform &amp;amp; lighting，坐标变换和光照）
立方体环境图（Cubemaps）
DOT3 – bump mapping
2倍各向异性过滤
三线性过滤
DXT纹理压缩
4ppc
引入“GPU”术语
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;2001 - GeForce 3&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;DirectX 8.0
Shader Model 1.0
可编程渲染管线
顶点着色器
像素着色器
3D纹理
硬件阴影图
8倍各向异性过滤
多采样抗锯齿（MSAA）
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;2003 - GeForce FX系列（NV3x）&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;DirectX 9.0
Shader Model 2.0
256顶点操作指令
32纹理 + 64算术像素操作指令
512像素操作指令
着色语言：HLSL、CGSL、GLSL
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;2004 - GeForce 6系列 (NV4x)&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;DirectX 9.0c
Shader Model 3.0
动态流控制
分支、循环、声明等
顶点纹理读取
高动态范围（HDR）
64位渲染纹理（Render Target）
FP16*4 纹理过滤和混合
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;2006 - GeForce 8系列 (G8x)&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;DirectX 10.0
Shader Model 4.0
几何着色器（Geometry Shaders）
没有上限位（No caps bits）
统一的着色器（Unified Shaders）
Vista系统全新驱动
基于GPU计算的CUDA问世
GPU计算能力以GFLOPS计量。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;2010 - GeForce 405（GF119）&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;DirectX 11.0
曲面细分（Tessellation）
外壳着色器（Hull Shader）
镶嵌单元（tessellator）
域着色器（Domain Shader）
计算着色器（Compute Shader）
支持Stream Output
Shader Model 5.0
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;DirectX 11的渲染管线。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;多线程支持
改进的纹理压缩
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Shader Model 5.0&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;更多指令、存储单元、寄存器
面向对象着色语言
曲面细分
计算着色器
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;2014 - GeForceGT 710（GK208）&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;DirectX 12.0
轻量化驱动层
硬件级多线程渲染支持
更完善的硬件资源管理
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;2016 - GeForceGTX 1060 6GB&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;首次支持RTX和DXR技术，即光线追踪
引入RT Core（光线追踪核心）
支持RTX光线追踪的显卡列表
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;2018 - TITAN RTX（TU102）&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;DirectX 12.1，OpenGL 4.5
6GPC，36TPC，72SM，72RT Core，...
8K分辨率，1770MHz主频，24G显存，384位带宽
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;参考资料：&lt;/p&gt;

&lt;p&gt;《GPU历史系列》&lt;/p&gt;

&lt;p&gt;https://new.qq.com/omn/20200506/20200506A0GP6H00.html?pc&lt;/p&gt;

&lt;p&gt;《GPU发展史》&lt;/p&gt;

&lt;p&gt;http://www.360doc.com/content/16/0607/16/30123241_565810074.shtml&lt;/p&gt;

&lt;p&gt;《显卡发展史1》&lt;/p&gt;

&lt;p&gt;http://www.360doc.com/content/20/0504/17/32196507_910185372.shtml&lt;/p&gt;

&lt;p&gt;《显卡发展史2》&lt;/p&gt;

&lt;p&gt;http://www.360doc.com/content/20/0504/17/32196507_910185435.shtml&lt;/p&gt;

&lt;p&gt;《显卡发展史3》&lt;/p&gt;

&lt;p&gt;http://www.360doc.com/content/20/0504/17/32196507_910187350.shtml&lt;/p&gt;

&lt;p&gt;《Making Sense of Smartphone Processors: The Mobile CPU/GPU Guide》&lt;/p&gt;

&lt;p&gt;https://www.techautos.com/2010/03/14/smartphone-processor-guide/&lt;/p&gt;

&lt;p&gt;《移动设备GPU架构知识汇总》&lt;/p&gt;

&lt;p&gt;https://zhuanlan.zhihu.com/p/112120206&lt;/p&gt;

&lt;p&gt;《针对移动端TBDR架构GPU特性的渲染优化》&lt;/p&gt;

&lt;p&gt;https://gameinstitute.qq.com/community/detail/123220&lt;/p&gt;

&lt;p&gt;《A look at the PowerVR graphics architecture: Tile-based rendering》&lt;/p&gt;

&lt;p&gt;https://www.imaginationtech.com/blog/a-look-at-the-powervr-graphics-architecture-tile-based-rendering/&lt;/p&gt;

&lt;p&gt;《A look at the PowerVR graphics architecture: Deferred rendering》&lt;/p&gt;

&lt;p&gt;https://www.imaginationtech.com/blog/the-dr-in-tbdr-deferred-rendering-in-rogue/&lt;/p&gt;

&lt;p&gt;《深入GPU硬件架构及运行机制》&lt;/p&gt;

&lt;p&gt;https://www.cnblogs.com/timlly/p/11471507.html&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484981&amp;amp;idx=1&amp;amp;sn=b5199f84ed73d28916dbb9143eeeb992&amp;amp;chksm=fc226332cb55ea2417b89e499284ab3a552ce591caba09d637912306503e4b457ed45c2e1d2d&amp;amp;token=1023740201&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>给女儿写信(十八) 面对困难时该如何去做</title>
   <link href="http://www.luzexi.com/2021/11/01/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A118"/>
   <updated>2021-11-01T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/11/01/给女儿的信18</id>
   <content type="html">
&lt;hr /&gt;

&lt;p&gt;导读：喜欢跟孩子们一起，可惜一直在外地工作，因此我给孩子写点信并录成视频，跟孩子聊聊自己的故事以及最近的感受&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;要点&quot;&gt;要点：&lt;/h2&gt;

&lt;p&gt;遇到困难时，如果我们积极的去突破，则会让情况更加明朗，反之则会陷入消沉。&lt;/p&gt;

&lt;h2 id=&quot;结构&quot;&gt;结构：&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;阐述与孩子们有一样的情景&lt;/li&gt;
  &lt;li&gt;举例遇到的困难，以及自己的行动&lt;/li&gt;
  &lt;li&gt;呼吁孩子们跟我一起勇敢的面对困难&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;大纲&quot;&gt;大纲：&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;在练习演讲和表达后，自己的表达和公众说话能力有了些进步&lt;/li&gt;
  &lt;li&gt;开始原地踏步，甚至感觉有点倒退，很疑惑，也很难受&lt;/li&gt;
  &lt;li&gt;面对困难和困境的几种做法。&lt;/li&gt;
  &lt;li&gt;激励自己勇敢面对困难，积极寻找解决问题的途径和方法，专注提高解决问题的技巧。&lt;/li&gt;
  &lt;li&gt;鼓励孩子们跟我一起勇敢面对困难。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;内容：&lt;/p&gt;

&lt;p&gt;Hello Sharon, hello Annie.爸爸好想你们啊。爸爸喜欢你们。&lt;/p&gt;

&lt;p&gt;又到了一个月一次的视频时间了。
爸爸跟你们说说爸爸最近遇到的事情好不好？&lt;/p&gt;

&lt;p&gt;最近爸爸一直在练习演讲和表达，因为爸爸发现自己在表达跟上台演讲时。表达的不够清楚，有很多紧张感。&lt;/p&gt;

&lt;p&gt;于是爸爸就找了很多的资料去练习自己的表达，以及上台说话的能力，让自己不那么紧张，在表达时也表达得更清楚一些，让别人听得更容易一些。&lt;/p&gt;

&lt;p&gt;经过一段时间的练习以后，爸爸发现自己的表达能力提升了不少。爸爸很开心，经过练习了以后，爸爸自己的说话能力提高了。&lt;/p&gt;

&lt;p&gt;然后又过了一段时间，爸爸发现爸爸在原地停了，停留了好久。甚至感觉自己的表达能力以及演讲能力有点倒退。爸爸好难受，感到很困惑，心里也很难受，爸爸。前面学习到的技巧慢慢的又退化了，又没有进步了。&lt;/p&gt;

&lt;p&gt;于是爸爸在想，其实我们生活当中常常遇到这样跟那样的困难以及阻碍。每当爸爸遇到这种困难跟阻碍的时候，我该怎么去做才能解决这个困难，才能化解现在的困境呢？&lt;/p&gt;

&lt;p&gt;爸爸想到通常我们面对困难的时候总是会选择退缩和逃避。因为每个人都害怕未知的挑战和困难。&lt;/p&gt;

&lt;p&gt;但是退缩和逃避不仅仅没有解决问题，而且让问题变得更加糟糕。&lt;/p&gt;

&lt;p&gt;于是爸爸就在想，我可不可以积极的面对困难，当困难来临时，我要主动的去寻找解决问题的方法和途径，把自己的注意力专注于解决问题的技巧。&lt;/p&gt;

&lt;p&gt;比如说爸爸最近遇到的表达上无法进步的困难。爸爸先仔细观察了自己表达上的问题，发现爸爸在说话的时候老是用喉咙和脑袋去说话，这个让爸爸感觉到自己在喉咙震动的时候脑袋也在震动，说多了就感觉自己的脑袋嗡嗡的，自己的脑袋嗡嗡的就会让自己陷入一种晕眩的状态。&lt;/p&gt;

&lt;p&gt;于是爸爸就想让在想可不可以不用喉咙去发声，不用脑袋去发声，用腹部去发声，这样的话我在说话的时候用力的是腹部，而不是脑袋和喉咙，这个让我说话变得更加的简单，更加的轻松，不必费这么大的力气。&lt;/p&gt;

&lt;p&gt;爸爸也在慢慢的练习腹部说话，练习一段时间以后吧，就会有进步，那个时候爸爸就会又开心了。&lt;/p&gt;

&lt;p&gt;雪儿跟安妮要像爸爸一样跟爸爸一起勇敢的面对困难哦，当我们面对困难时，我们要专注于寻找解决困难的途径和方法，把注意力集中在解决问题的技巧上。改善我们的技巧。寻找解决问题的途径。这样困难就变得不困难了。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>给女儿写信(十七) 在挫折中前进</title>
   <link href="http://www.luzexi.com/2021/10/29/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A117"/>
   <updated>2021-10-29T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/10/29/给女儿的信17</id>
   <content type="html">
&lt;hr /&gt;

&lt;p&gt;导读：喜欢跟孩子们一起，可惜一直在外地工作，因此我给孩子写点信并录成视频，跟孩子聊聊自己的故事以及最近的感受&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Hi, Sharon Annie, 爸爸好想你。&lt;/p&gt;

&lt;p&gt;爸爸跟你们说说，爸爸最近，的学习跟工作的状况好不好？&lt;/p&gt;

&lt;h2 id=&quot;要点&quot;&gt;要点：&lt;/h2&gt;

&lt;p&gt;爸爸最近遇到些挫折，但爸爸没有气馁，一直在想办法如何改善自己的技巧，下次不让它失败。&lt;/p&gt;

&lt;h2 id=&quot;结构&quot;&gt;结构：&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;观察到自己的表达能力比较弱&lt;/li&gt;
  &lt;li&gt;很难受也很困惑，不知道该怎么办&lt;/li&gt;
  &lt;li&gt;寻找练习的方法并练习&lt;/li&gt;
  &lt;li&gt;单一的练习方法很快无效了，寻找另一种练习方法&lt;/li&gt;
  &lt;li&gt;学习速度又变快了&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;内容&quot;&gt;内容：&lt;/h2&gt;

&lt;p&gt;爸爸最近遇到一些挫折，爸爸没有气馁，一直在想办法，怎么去完善自己的技巧，来让这个事情进行的更顺利一些。&lt;/p&gt;

&lt;p&gt;爸爸观察到自己的表达能力比较弱。观察到自己的在表达的时候的逻辑不是非常清晰，再跟别人讲事情或者说我想表达我的意思的时候，别人经常听不懂我在讲什么，或者说要听好几遍才能听明白。&lt;/p&gt;

&lt;p&gt;甚至有时候，别人听了好几遍，也没有听明白，但是假装明白了，最后把事情搞得很糟糕。&lt;/p&gt;

&lt;p&gt;所以，爸爸想改进一下自己的表达能力，希望自己在跟别人说话的时候，能够更清楚的表达爸爸的想说的意思。&lt;/p&gt;

&lt;p&gt;爸爸有时候会讲故事给别人听。其实讲故事里有很多的技巧，爸爸对于这些技桥都不太了解，所以爸爸在跟别人讲故事的时候，我发现讲的不清楚也不精彩，原本一个很好的故事，被爸爸讲的非常烂。&lt;/p&gt;

&lt;p&gt;于是爸爸很困惑，很难受。我想清楚的表达自己的意思，也很想把故事讲好，把故事讲得更有趣。&lt;/p&gt;

&lt;p&gt;爸爸就从平常的练习开始寻找方法，我在每天的打卡中加入了一个练习，想在平时每天都练习一下，如何说好一个故事，如何去表达的更清楚。&lt;/p&gt;

&lt;p&gt;爸爸每天早上都给自己布置一个任务，练习绕口令，练习发音。绕口令和发音是嘴巴的肌肉最基础的运动部分，爸爸希望自己在嘴巴的肌肉上能够运用的更好一些。&lt;/p&gt;

&lt;p&gt;在这样不断的练习过程当中，爸爸进步速度不是非常快。这个让爸爸受到很大的挫折感，爸爸花了很多时间去练习，但是效果不好，爸爸很难受啊。&lt;/p&gt;

&lt;p&gt;于是爸爸想办法在改进练习的方式，因为爸爸知道只有改进了练习的方式才能让练习的效果更好，让爸爸的练习效率更高。&lt;/p&gt;

&lt;p&gt;于是爸爸换了几种方法，第1种方法是爸爸有目标的去练习，比如说，爸爸想先把自己的，嗯啊这个词这个发音给去掉，因为爸爸在说话的时候经常会说啊额嗯、这个、然后、这些、那些等等。爸爸想把这些填充词给去掉，称它为哼哈词。&lt;/p&gt;

&lt;p&gt;做的第1步目标就是把这些填充词，在说话的时候去掉，用别的方式去代替，爸爸用沉默的方式去代替这些词语，当爸爸想发，嗯跟R的时候，想说然后、这个、那个时，用沉默去代替。&lt;/p&gt;

&lt;p&gt;爸爸发现练习了一段时间以后有比较大的效果，很开心。&lt;/p&gt;

&lt;p&gt;接着，练习着练习着，爸爸又遇到了瓶瓶颈。第1种练习方式，变得没有效果了，又变得低效了。&lt;/p&gt;

&lt;p&gt;于是爸爸又换了一种方法。第2种方法，爸爸想让自己说话的语气放慢一些，这样爸爸说话的时候，别人接受到的信息就会更清楚一些，也能听得更懂一些。&lt;/p&gt;

&lt;p&gt;于是爸爸就每天跟自己说话，并且跟自己说话的时候给自己定一个主题。比如爸爸想说今天学习到了新的知识，我把学习到的新知识说给别人听，在说话的时候，我要故意放慢语气，慢慢说慢慢说，慢慢说的时候对方也接收的也很一些。&lt;/p&gt;

&lt;p&gt;就这样爸爸，每天想一个主题，围绕这个主题去说自己的故事，在说故事的时候让自己的语速放慢一些。&lt;/p&gt;

&lt;p&gt;过了一段时间，真的起了效果，爸爸的表达能力越来越好了，很开心噢。&lt;/p&gt;

&lt;p&gt;不断的更换方法去练习，去有目标的练习自己的技能。当我发现遇到瓶颈的时候去主动更换练习方式，这让爸爸的学习效率很高，提升的效果也非常好。&lt;/p&gt;

&lt;p&gt;爸爸很开心，在这样不断更换练习方式的过程当中，爸爸的学习能力越来越强了，学习速度也越来越快了，学习效率越来越高了，爸爸很开心哦，希望Sharon跟安妮，也跟爸爸一样，当发现自己的进步少的时候主动去更换自己的练习方式，当你发现自己的练习方式进步速度比较慢时，可能是因为你的练习方式有问题哦。&lt;/p&gt;

&lt;p&gt;爸爸爱你们哦，爸爸喜欢你们，爸爸爱你们，我们来一起加油。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>游戏引擎架构#4 低阶渲染器（1）</title>
   <link href="http://www.luzexi.com/2021/10/26/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B047"/>
   <updated>2021-10-26T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/10/26/读书笔记47</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484964&amp;amp;idx=1&amp;amp;sn=99fd35baa5ad829cbae99753f14aa64b&amp;amp;chksm=fc226323cb55ea3598d74dd2508841f01e149176b2c857c54c077a2d8a3f493033ce8f57b56f&amp;amp;token=1534435063&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;背景&quot;&gt;背景：&lt;/h2&gt;

&lt;p&gt;作为游戏开发从业者，从业务到语言到框架到引擎，积累了一些知识和经验，特别是在看了几遍《游戏引擎架构》后对引擎架构的理解又深入了些。&lt;/p&gt;

&lt;p&gt;近段时间有对引擎剖析的想法，正好借这书本对游戏引擎架构做一个完整分析。&lt;/p&gt;

&lt;p&gt;此书用简明、清楚的方式覆盖了游戏引擎架构的庞大领域，巧妙地平衡了广度与深度，并且提供了足够的细节。&lt;/p&gt;

&lt;p&gt;我将借助《游戏引擎架构》这本书、结合引擎源码和自己的经验，深入分析游戏引擎的历史、架构、模块，最后通过实践简单引擎开发来完成对引擎知识的掌握。&lt;/p&gt;

&lt;p&gt;游戏引擎知识面深而广，所以对这系列的文章书编写范围做个保护，即不对细节进行过多的阐述，重点剖析的是架构、流程以及模块的运作原理。&lt;/p&gt;

&lt;p&gt;同时《游戏引擎架构》中部分知识太过陈旧的部分，会重新深挖后总结出自己的观点。&lt;/p&gt;

&lt;h2 id=&quot;概述&quot;&gt;概述：&lt;/h2&gt;

&lt;p&gt;本系列文章对引擎中的重要的模块和库进行详细的分析，我挑选了十五个库和模块来分析：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;时间库&lt;/li&gt;
  &lt;li&gt;自定义容器库&lt;/li&gt;
  &lt;li&gt;字符串散列库&lt;/li&gt;
  &lt;li&gt;内存管理框架&lt;/li&gt;
  &lt;li&gt;RTTI与反射模块&lt;/li&gt;
  &lt;li&gt;图形计算库&lt;/li&gt;
  &lt;li&gt;资产管理模块&lt;/li&gt;
  &lt;li&gt;低阶渲染器&lt;/li&gt;
  &lt;li&gt;剔除与合批模块&lt;/li&gt;
  &lt;li&gt;动画模块&lt;/li&gt;
  &lt;li&gt;物理模块&lt;/li&gt;
  &lt;li&gt;UI底层框架&lt;/li&gt;
  &lt;li&gt;性能剖析器的核心部分&lt;/li&gt;
  &lt;li&gt;脚本系统&lt;/li&gt;
  &lt;li&gt;视觉效果模块&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;本篇内容为列表中的第8个部分。&lt;/p&gt;

&lt;h2 id=&quot;正文&quot;&gt;正文：&lt;/h2&gt;

&lt;p&gt;简单回顾下前文&lt;/p&gt;

&lt;p&gt;前文简单提了下C++编译过程、C++内存布局、文件内核读取原理、图形计算的常用库内容，以及大篇幅的引擎资产管理。&lt;/p&gt;

&lt;p&gt;引擎资产管理简单回顾下：&lt;/p&gt;

&lt;p&gt;首先，资产分为DCC资产、中间格式资产、引擎资产，这三者是从制作到导入到使用的顺序。&lt;/p&gt;

&lt;p&gt;其次，引擎资产分为元数据和引擎资产文件，引擎不仅需要资产也需要资产的配置数据。&lt;/p&gt;

&lt;p&gt;接着，引擎内存中的资产有资源映射关系，资产路径、资产GUID、资产实例、资产实例ID。&lt;/p&gt;

&lt;p&gt;最后，引擎通常都有资源包，资源包的格式通常都差不多，头信息（包括索引信息和数据概要信息）+数据文件，压缩可以是整体压缩也可以是每个独立数据单独压缩，通过解剖资源包的数据格式，我们可以知道如何去做差量更新。&lt;/p&gt;

&lt;p&gt;这里补充一点，引擎在读取资产时，与读取资产配置的原理是一样的，它可以有自己的数据格式也可以用标准格式，引擎通常使用自定义格式。通过区分读取不同类型的资产数据来实例化引擎内不同的资源对象，包括网格、材质、动画、粒子、节点、组件配置等。&lt;/p&gt;

&lt;h3 id=&quot;下面我们开始这篇内容&quot;&gt;下面我们开始这篇内容&lt;/h3&gt;

&lt;p&gt;本次内容会围绕GPU来写，从硬件架构到软件驱动再到引擎架构，目标是帮大家理解GPU硬件的运作原理，理解图形接口的架构，理解引擎低阶渲染器的架构。&lt;/p&gt;

&lt;h2 id=&quot;目录&quot;&gt;目录：&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;主板结构中的显卡&lt;/li&gt;
  &lt;li&gt;GPU功能发展史&lt;/li&gt;
  &lt;li&gt;GPU与CPU的差异&lt;/li&gt;
  &lt;li&gt;GPU硬件特点&lt;/li&gt;
  &lt;li&gt;图形驱动程序架构&lt;/li&gt;
  &lt;li&gt;引擎低阶渲染架构&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;主板结构中的显卡&quot;&gt;主板结构中的显卡&lt;/h2&gt;

&lt;p&gt;我们讨论GPU和显卡时经常混为一谈，严格来说GPU是显卡（Video card、Display card、Graphics card）最核心的部件。
显卡除了GPU，还有扇热器、通讯元件、与主板和显示器连接的各类插槽。&lt;/p&gt;

&lt;p&gt;图1&lt;/p&gt;

&lt;p&gt;（主板结构图）&lt;/p&gt;

&lt;p&gt;早期计算机设备中的CPU都是由系统总线来与所有其他设备通信，当图形芯片需要跟CPU和内存大量交换数据时就出现了巨大瓶颈，于是人们设计了高速的北桥芯片和低速的南桥芯片，一来协调CPU、内存、图形设备之间的高速通信，二来专门处理磁盘、USB、键盘鼠标等低速设备。&lt;/p&gt;

&lt;p&gt;从上图中我们看到，离CPU越近，存取数据越快，最近的就是CPU内的组件了，其次是高速缓存总线，再是本地总线，接着是内存总线，最后是高速总线。
我们的GPU被安排在高速总线上，而且总线还有其他设备需要兼顾。因此从通信速度上来说，高速总线并不是那么快，这也是CPU与GPU的通信带宽通常成为瓶颈的其中一个原因。&lt;/p&gt;

&lt;p&gt;图1&lt;/p&gt;

&lt;p&gt;（现实中主板结构图：图片来自网络）&lt;/p&gt;

&lt;p&gt;这里重点介绍下北桥芯片，一个主板上最重要的部分可以说就是主板的芯片组了，主板的芯片组一般由北桥芯片和南桥芯片组成，两者共同组成主板的芯片组。北桥芯片主要负责实现与CPU、内存、AGP接口之间的数据传输，同时还通过特定的数据通道和南桥芯片相连接。南桥芯片主要负责和IDE设备、PCI设备、声音设备、网络设备以及其他的I/O设备的通信。&lt;/p&gt;

&lt;p&gt;主板和CPU发展都现在，北桥芯片慢慢开始消失被集成了CPU中。往后发展，主板芯片组可以看成是以往南桥芯片组的加强版，CPU与主板芯片采用DMI总线进行通信。&lt;/p&gt;

&lt;p&gt;虽然从Lynnfield Core i5/i7开始把北桥集成到CPU上，但是其内部仍是采用QPI总线来通讯，而外部与主板芯片组通讯，其实就是以往主板上南桥与北桥通讯，采用的是DMI总线。因此不能说Lynnfield Core i5/i7是精简了，只是集成度更高而已。&lt;/p&gt;

&lt;h3 id=&quot;手机主板结构与pc不同&quot;&gt;手机主板结构与PC不同&lt;/h3&gt;

&lt;p&gt;以下图片均来源网络&lt;/p&gt;

&lt;p&gt;图1&lt;/p&gt;

&lt;p&gt;图2&lt;/p&gt;

&lt;p&gt;图3&lt;/p&gt;

&lt;p&gt;图4&lt;/p&gt;

&lt;p&gt;手机的主板布局则不同，由于主板不能横向扩展，所以采用双或三层叠层的主板，GPU内嵌在处理器上与CPU同框，且它们周围布满了内存，这样通信更快。
同时由于空间小，功率和高速缓存大小也受到限制。&lt;/p&gt;

&lt;p&gt;多层主板虽然看上去设计巧妙，同时也将几个发热大户都贴在了一起，发热量和速度加倍。&lt;/p&gt;

&lt;p&gt;现代CPU都有一种过热自动降频或者关闭核心的保护措施，因此当玩王者或吃鸡等大型游戏时，就会因为发热而导致它更快的达到阈值温度而降频，这也是很多游戏优化后仍然会卡顿的主要原因。&lt;/p&gt;

&lt;p&gt;因此很多手机引入了水冷（热导管技术）CPU散热技术，用超大一块热导管来散热降温，让CPU不会因为过热而降频或锁核，从而能更长时间甚至全时运行在最大工作频率上，从而发挥出手机宣传时100%的性能。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484964&amp;amp;idx=1&amp;amp;sn=99fd35baa5ad829cbae99753f14aa64b&amp;amp;chksm=fc226323cb55ea3598d74dd2508841f01e149176b2c857c54c077a2d8a3f493033ce8f57b56f&amp;amp;token=1534435063&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;参考资料&quot;&gt;参考资料：&lt;/h2&gt;

&lt;p&gt;《主板北桥百科》&lt;/p&gt;

&lt;p&gt;https://baike.baidu.com/item/%E4%B8%BB%E6%9D%BF%E5%8C%97%E6%A1%A5/10252486?fr=aladdin&lt;/p&gt;

&lt;p&gt;《总线百科》&lt;/p&gt;

&lt;p&gt;https://baike.baidu.com/item/%E6%80%BB%E7%BA%BF/108823&lt;/p&gt;

&lt;p&gt;《iPhone 11/Pro Max内部结构示意图》&lt;/p&gt;

&lt;p&gt;http://www.itqianyan.com/m/view.php?aid=5479&lt;/p&gt;

&lt;p&gt;《拆解 iPhone 12 系列》&lt;/p&gt;

&lt;p&gt;https://www.igao7.com/news/202010/L2RP34HNtTEZ2yYC.html&lt;/p&gt;

&lt;p&gt;《iPhone 8和X拆解》&lt;/p&gt;

&lt;p&gt;https://m.iphonediule.com/hot/qiangxian.html&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>读书笔记(四十六) 《如何精彩演讲》#1 演讲的结构</title>
   <link href="http://www.luzexi.com/2021/10/17/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B046"/>
   <updated>2021-10-17T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/10/17/读书笔记46</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484952&amp;amp;idx=1&amp;amp;sn=aa014404eb8b71222820f1f3bbff437e&amp;amp;chksm=fc22631fcb55ea0981c20cab77f66da58a2697ab2e8a792f4b4c6ba15a3bc6b57dd2ba7309a1&amp;amp;token=1211671052&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;背景&quot;&gt;背景&lt;/h1&gt;

&lt;p&gt;不知不觉看完了6本关于演讲的主题，按推荐顺序排列分别是《高效演讲》、《演讲与口才》、《即兴演讲》、《关键对话》、《TED演讲的力量》、《说话的艺术》。&lt;/p&gt;

&lt;p&gt;我希望自己能够对演讲有一个系统性的学习。于是自己在平常的生活和工作中实践了一段时间，前前后后加起来有两个月时间。&lt;/p&gt;

&lt;p&gt;目标是，改善平常的工作、生活中表达的流畅和清晰度，能让人感觉到舒服让人感觉有活力。同时在公众演讲上，希望自己能够很好的呈现自己的所思所想以及所知所学。&lt;/p&gt;

&lt;p&gt;因此开启这个演讲学习系列的总结文章，我将对这6本书的内容加上我的实践经验做一次全面的系统性的总结。&lt;/p&gt;

&lt;h1 id=&quot;内容&quot;&gt;内容&lt;/h1&gt;

&lt;p&gt;这一篇主要讲演讲的结构。&lt;/p&gt;

&lt;p&gt;一场演讲分为三个部分：&lt;/p&gt;

&lt;p&gt;第1个部分是，开场。
第2个部分是，核心内容。
第3个部分是，收尾。&lt;/p&gt;

&lt;p&gt;这三个部分都有各自的技巧，而此篇主要来说说具体的演讲内容中的其中一点，即演讲结构。&lt;/p&gt;

&lt;h2 id=&quot;首先来说一下为什么我们需要结构&quot;&gt;首先来说一下为什么我们需要结构？&lt;/h2&gt;

&lt;p&gt;我们在平常的表达过程当中，一般是不会去刻意在意逻辑。
因此，我们在表述过程当中通常是按照时间线来进行表述。
表达过程当中，通常只是说出了自己脑中不断冒出的想法。
在这样的表达下，对方是很难get到我们想要说的内容的。
一旦我们说的内容稍微多一些时，对方很容易进入完全听不懂我们在说了什么情况。&lt;/p&gt;

&lt;p&gt;那么如何才能让对方听清楚，听明白，听得懂我们在说什么呢？&lt;/p&gt;

&lt;p&gt;我们就得用演讲结构去规范我们的演讲内容，让我们的演讲内容能够更清晰的呈现在听众的脑中，让脑袋更容易去记住我们的要点以及中心思想。&lt;/p&gt;

&lt;h2 id=&quot;那什么才是好的演讲结构呢&quot;&gt;那什么才是好的演讲结构呢？&lt;/h2&gt;

&lt;p&gt;首先演讲的内容必须围绕着一个中心思想去讲。
如果中心思想太多，很难让听众记得住，就不会是一个好的演讲。
因此一个演讲最好只有一个中心思想。
中心思想最好是能引起观众共鸣的。
（在后面的内容中再讲如何让中心思想引起共鸣）&lt;/p&gt;

&lt;p&gt;其次你的演讲内容要有层次感。&lt;/p&gt;

&lt;p&gt;在我们表达时最好有意识的按简单顺序进行表达，这样听众们的大脑才能记得住。
比如第一第二第三；过去现在未来；昨天，今天，明天；最重要其次最后等等。&lt;/p&gt;

&lt;p&gt;除了这种层次方式，还有另一种方式，即故事方式。
（后面的文章中再解剖如何讲好一个故事）&lt;/p&gt;

&lt;p&gt;这里先简单介绍一下一个好故事设计要有4个部分，情景带入、遇到危机、冲突和转折、大好结局。&lt;/p&gt;

&lt;p&gt;除了设计故事内容还要注意好故事有4个要点，篇幅小、通俗易懂、简单容易记、有共鸣。&lt;/p&gt;

&lt;p&gt;接着你的演讲内容，最好以总分总的形式去部署。
即提出观点、剖析观点、总结观点。
在开头时阐述演讲要点，结尾时进行要点总结，中间内容分成三个论点，每个论点都由三个句子组成演讲结构。&lt;/p&gt;

&lt;p&gt;这样的演讲结构即“1-3-3-3-1”形式组成的结构，让人听了更舒服也更容易记得住。&lt;/p&gt;

&lt;p&gt;最后在你平时的讲话和沟通表达的过程中，有意识的将自己说话的内容以层次感的形式表现出来。
一开始你可以在讲话前先先打好附稿或准备稿，按123规律来讲，注意不要把第3点和第1点说重复了。
经过长时间的练习，你可以自如的应对各种各样的演讲模式以及各种各样的即兴发挥。&lt;/p&gt;

&lt;h2 id=&quot;那么怎样才能练好演讲结构呢&quot;&gt;那么怎样才能“练”好演讲结构呢？&lt;/h2&gt;

&lt;p&gt;这里给出三个要点。&lt;/p&gt;

&lt;p&gt;第一，每次讲话前先打好腹稿再开口。&lt;/p&gt;

&lt;p&gt;为每次的公众演讲以及平时的沟通交流都打好腹稿，这是一种非常好的练习方式。
这让你时时刻刻提醒自己，在讲话时要有层次感，将这种方法印刻在脑海里，形成肌肉记忆。
时间长了，你自然而然就有了说话的层次感，形成了强有力的肌肉记忆。&lt;/p&gt;

&lt;p&gt;第二，多上台，多在台上运用演讲层次结构。&lt;/p&gt;

&lt;p&gt;台上的感觉和台下的感觉是完全不同的。
在台上你会更加紧张，也更加容易出错，同时更加在意别人对你的看法，也更加害怕。
多上台，多在台上运用演讲的层次结构，会让你在面对陌生场合，在面对激烈、紧张、恐惧时，也能够自如的运用层次感肌肉记忆，让演讲内容流畅清晰的表达出来。&lt;/p&gt;

&lt;p&gt;第三，发现生活中的主题进行层次训练。&lt;/p&gt;

&lt;p&gt;生活和工作中，周围每天都会发生很多的事情，我们可以通过仔细观察我们生活当中发生的事情，叙述或表达我们的观点。
表达时运用层次结构去跟自己对话，这样的训练每天都可以进行，有助于我们更快更好的形成肌肉记忆。&lt;/p&gt;

&lt;h2 id=&quot;最后告诉大家如何获得练习反馈&quot;&gt;最后告诉大家，如何获得练习反馈。&lt;/h2&gt;

&lt;p&gt;这里有三个方法告诉大家。&lt;/p&gt;

&lt;p&gt;1.录制后自我复盘。&lt;/p&gt;

&lt;p&gt;我们演讲的反馈可以通过自己录制视频，录制音频后进行复听来复盘自己的演讲。&lt;/p&gt;

&lt;p&gt;2.通过他人获得评价。&lt;/p&gt;

&lt;p&gt;可以通过同事、朋友、家人、以及专门演讲圈的人对自己的评价来复盘自己的演讲，从而获得自己改进的方向。&lt;/p&gt;

&lt;p&gt;但要注意，别人对你的负面评价要小心。
通常那些不专业的人给你的评价，都是打击你自信心的一个常见途径。
我们需要的是鼓励，而不是打击。鼓励让我们更有动力前进。而打击则适得其反。&lt;/p&gt;

&lt;p&gt;3.通过专业演讲训练师来反馈。&lt;/p&gt;

&lt;p&gt;最好是通过专业的演讲导师来纠正你的演讲问题，从而给予你正确的演讲练习方式和修正方向。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484952&amp;amp;idx=1&amp;amp;sn=aa014404eb8b71222820f1f3bbff437e&amp;amp;chksm=fc22631fcb55ea0981c20cab77f66da58a2697ab2e8a792f4b4c6ba15a3bc6b57dd2ba7309a1&amp;amp;token=1211671052&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(六十六) 人和事</title>
   <link href="http://www.luzexi.com/2021/10/12/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A866"/>
   <updated>2021-10-12T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/10/12/思路探讨66</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484939&amp;amp;idx=1&amp;amp;sn=e3df251c9cb4733784e5df9ccd21e3ab&amp;amp;chksm=fc22630ccb55ea1a27f035c062122bf9e40a504d0dac7143b0a383b4f7dc729dc8596cc10958&amp;amp;token=254199611&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;有两周时间没有发文章了，最近有些忙，自己也遇到了瓶颈，正在突破。&lt;/p&gt;

&lt;p&gt;前几个月发现我的表达能力有些弱，表达逻辑混乱，表达时没有自信。&lt;/p&gt;

&lt;p&gt;所以呢，我特别花了一些精力去加强这方面的训练。&lt;/p&gt;

&lt;p&gt;自己做了一些训练，去演讲俱乐部练习演讲，去读书聚会的地方做演讲，以及做主持人的表达工作。&lt;/p&gt;

&lt;p&gt;我希望通过这样的锻炼，提升表达能力。也看了几本书，暂时还没有去总结它们，希望后面能够挤出时间来写些关于这个主题书的读书总结。&lt;/p&gt;

&lt;p&gt;为什么要去做沟通跟表达演讲的训练呢？我内心声音是，我要解决人与人之间的关系，人与人之间的沟通。&lt;/p&gt;

&lt;p&gt;前面10几年都在做练习解决事情的技巧，比如我要写好程序，写书，写文章，都是去总结一些解决事情的经验。&lt;/p&gt;

&lt;p&gt;不管是程序还是项目还是工作分配工作拆解，都是需要我们去解决事情。这是我前面12年做的事情。&lt;/p&gt;

&lt;p&gt;我最近领悟到，其实解决事情只占了30%的部分，剩余的70%的部分并不是靠解决事情来解决的，而是靠做人，解决人与人之间关系来搞定的。&lt;/p&gt;

&lt;p&gt;一个典型的例子，在工作当中如果我们跟上级相处不好的话，跟家人的关系不好，你即使把成绩做出来了，也得不到家人的肯定，和领导的赏识，这对我们的感受有巨大影响，甚至会有强烈的挫败感。&lt;/p&gt;

&lt;p&gt;因此现阶段的我认为，解决人与人之间的关系比解决事情更重要。&lt;/p&gt;

&lt;p&gt;最重要的其实不是事情本身，而是与事情有关的人，这些人才是关键。例如事情提出的人是领导，或与事件有关的人是同事，在他们提出问题时，先确立这是不是个事实。&lt;/p&gt;

&lt;p&gt;我相信大部分时候是事实，也可能是被夸大的事实。当我们去做事时，其实应该首先跟他们聊一聊，跟老板聊一聊，跟这些部门的人来聊一聊，这样的才能体会到对方的感受，以及看到对方究竟想要的。&lt;/p&gt;

&lt;p&gt;跟人联系、寒暄后，才能知道对方的情绪、想法、需要，这时再想办法来解决问题&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484939&amp;amp;idx=1&amp;amp;sn=e3df251c9cb4733784e5df9ccd21e3ab&amp;amp;chksm=fc22630ccb55ea1a27f035c062122bf9e40a504d0dac7143b0a383b4f7dc729dc8596cc10958&amp;amp;token=254199611&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>游戏引擎架构#3 链接、图形计算库、资产管理模块</title>
   <link href="http://www.luzexi.com/2021/09/28/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B045"/>
   <updated>2021-09-28T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/09/28/读书笔记45</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484932&amp;amp;idx=1&amp;amp;sn=13c96ed5d6a24abe43d9a63a5e2aca23&amp;amp;chksm=fc226303cb55ea1527fead1e2d90b435a6eb911cb54e58ffbde95b674f1982859fa6a66f3837&amp;amp;token=1405609858&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;背景&quot;&gt;背景：&lt;/h1&gt;

&lt;p&gt;作为游戏开发从业者，从业务到语言到框架到引擎，积累了一些知识和经验，特别是在看了好几遍《游戏引擎架构》后对引擎的架构感触颇深。&lt;/p&gt;

&lt;p&gt;近段时间对引擎剖析的想法也较多，正好借着书本对游戏引擎架构做一个完整分析。此书用简明、清楚的方式覆盖了游戏引擎架构的庞大领域，巧妙地平衡了广度与深度，并且提供了足够的细节，使得初学者也能很容易地理解其中的各种概念。&lt;/p&gt;

&lt;p&gt;我的目标是掌握游戏引擎架构知识，我的方法是借助《游戏引擎架构》这本书、结合引擎源码和自己的经验，深入分析游戏引擎的历史、架构、模块。最后通过实践简单引擎开发来完成对引擎知识的掌握。&lt;/p&gt;

&lt;p&gt;游戏引擎知识面深而广，所以对这系列的文章书编写范围做个保护，即不对细节进行过多的阐述，重点剖析的是架构、流程以及模块的运作原理。&lt;/p&gt;

&lt;p&gt;由于《游戏引擎架构》此书的部分知识太浅或太过陈旧，所以不得不将部分知识重新深挖后总结出自己的观点。&lt;/p&gt;

&lt;h1 id=&quot;概述&quot;&gt;概述：&lt;/h1&gt;

&lt;p&gt;本章开始对引擎中的重要的模块和库进行详细的分析，我挑选了十五个库和模块来分析：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;时间库&lt;/li&gt;
  &lt;li&gt;自定义容器库&lt;/li&gt;
  &lt;li&gt;字符串散列库&lt;/li&gt;
  &lt;li&gt;内存管理框架&lt;/li&gt;
  &lt;li&gt;RTTI与反射模块&lt;/li&gt;
  &lt;li&gt;图形计算库&lt;/li&gt;
  &lt;li&gt;资产管理模块&lt;/li&gt;
  &lt;li&gt;低阶渲染器&lt;/li&gt;
  &lt;li&gt;剔除与合批模块&lt;/li&gt;
  &lt;li&gt;动画模块&lt;/li&gt;
  &lt;li&gt;物理模块&lt;/li&gt;
  &lt;li&gt;UI核心框架&lt;/li&gt;
  &lt;li&gt;性能剖析器的核心部分&lt;/li&gt;
  &lt;li&gt;脚本系统&lt;/li&gt;
  &lt;li&gt;视觉效果模块&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;本篇内容为列表中的6、7。&lt;/p&gt;

&lt;h1 id=&quot;正文&quot;&gt;正文：&lt;/h1&gt;

&lt;p&gt;简单回顾下前文，前面我们聊了时间库、自定义容器、字符串、内存管理这四个模块的技术原理和特点，它们都是大型软件架构所必备的模块，同时简单讲述了它们在Unreal和Unity中存在的特点。&lt;/p&gt;

&lt;h2 id=&quot;编译链接过程与内存布局&quot;&gt;编译链接过程与内存布局&lt;/h2&gt;

&lt;p&gt;图1&lt;/p&gt;

&lt;p&gt;图2&lt;/p&gt;

&lt;p&gt;简单回顾一下C++编译过程：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;源文件.cpp文件被编译成.o文件后由链接器链接成可执行文件或库文件。&lt;/li&gt;
  &lt;li&gt;库分为静态库和动态库。其中静态库为只是简单集合了.o文件，而动态库则是一个完整的编译、链接产物。&lt;/li&gt;
  &lt;li&gt;头文件.h文件不是编译的必需品，它只是包含在源文件中的声明文件。&lt;/li&gt;
  &lt;li&gt;Linux的so和Windows的DLL虽然都是ELF文件格式，但最终格式差了很多，因此不能互相使用。&lt;/li&gt;
  &lt;li&gt;编译器开启优化后（一般是Release时），会优化代码，包括内联、调换代码顺序、更改代码为最优等。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;具体可以看我前面写的《链接、装载与库》&lt;/p&gt;

&lt;p&gt;http://luzexi.com/2021/06/20/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B029&lt;/p&gt;

&lt;p&gt;也同时回顾下C++内存布局：
    1.C++内存布局中有，类、变量、内存对齐、虚表、RTTI
    2.类和结构在C++中差异较少
    3.每个变量内存占用量不同，int（32bit）、short int（16bit）、long long int（64bit）、float（4bit）、double（8bit）、char（8bit）等
    4.默认按4字节（32bit）对齐，不足4字节的编译器会补齐
    5.虚函数或虚继承的类有虚表及虚表指针
    6.没有RTTI的情况下，虚表只有当前的虚函数指针
    7.有RTTI的情况下，虚表中有type_info指针
    8.有RTTI的情况下，虚指针指向虚表中的第二个格子，即虚指针，第一个格子为type_info指针&lt;/p&gt;

&lt;p&gt;具体可以看我前面写的《深度探索C++对象模型-总结》&lt;/p&gt;

&lt;p&gt;http://luzexi.com/2020/11/20/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B014&lt;/p&gt;

&lt;h2 id=&quot;图形计算库&quot;&gt;图形计算库&lt;/h2&gt;

&lt;p&gt;图形库涉及内容有图形元素和运算两种，它们分别包含，点、矢量、矩阵、四元数、图形对象以及相关的运算（包括SIMD）。&lt;/p&gt;

&lt;p&gt;下面我们来介绍一下：&lt;/p&gt;

&lt;p&gt;矢量运算包括：矢量加减法、模、归一化、点积、投影、叉积、线性插值等。&lt;/p&gt;

&lt;p&gt;矩阵有，单位矩阵、转置矩阵、逆矩阵、齐次坐标。矩阵运算，包括矩阵乘法、加减法、除、与、或等。&lt;/p&gt;

&lt;p&gt;四元数运算包括，四元数乘法、加减法、逆运算、旋转矢量、等价的四元数矩阵、旋转线性插值、球面线性插值等。&lt;/p&gt;

&lt;p&gt;各图形转换操作，包括旋转、缩放、投影、平移、LookAt等。&lt;/p&gt;

&lt;p&gt;图形对象包括，直线、线段、球体、平面、包围盒、平截头体、圆形、矩形、三角形等。&lt;/p&gt;

&lt;p&gt;这部分内容可以在Unreal的Engine\Source\Runtime\Core\Public\Math中找到。&lt;/p&gt;

&lt;h3 id=&quot;下面简单介绍下simd&quot;&gt;下面简单介绍下SIMD：&lt;/h3&gt;

&lt;p&gt;硬件加速SIMD运算（Single instruction multiple data），单指令多数据，是指，现代微处理器用一个指令并行地对多个数据执行数学运算，它能帮助我们加速运算。&lt;/p&gt;

&lt;p&gt;游戏引擎中最常用的是SSE模式（Streaming SIMD extensions，SSE），它包裹了4个32位float值，它们都被打包进了一个128位寄存器。&lt;/p&gt;

&lt;p&gt;单个指令可对4对浮点数进行并行运算，如加法或乘法。在计算四元矢量和4x4矩阵相乘时特别有用。&lt;/p&gt;

&lt;p&gt;需要注意的是
1.由于在浮点运算器和SSE寄存器之间传输数据很糟糕，所以不要混合使用普通浮点数和SIMD运算，这样会使得CPU整个指令执行流水线停顿，浪费CPU周期。
2.在VS中用SIMD数据类型__m128声明的临时变量或参数，编译器通常会把它们直接置于SSE寄存器中而非内存栈。
3.动态分配SIMD结构时要注意内存按16字节对齐&lt;/p&gt;

&lt;h2 id=&quot;资产管理模块&quot;&gt;资产管理模块&lt;/h2&gt;

&lt;p&gt;先说文件系统&lt;/p&gt;

&lt;h3 id=&quot;文件系统&quot;&gt;文件系统&lt;/h3&gt;

&lt;p&gt;游戏引擎中的文件系统相对比较简单：
1.每个平台的路径、API不同，对平台需要做些封装。
2.文件读取的阻塞方式分同步和异步。&lt;/p&gt;

&lt;p&gt;其中同步需要阻塞当前进程来等待IO，异步则通过分线程阻塞等待IO，其两者原理是一样，都是调用内核读取文件且都需要等待IO。&lt;/p&gt;

&lt;p&gt;以前写过一篇关于操作系统内核中文件操作的底层原理《链接、装载与库 - 内核运行库》大家可以参考下。&lt;/p&gt;

&lt;p&gt;这里顺便简单回顾一下文件内核原理：&lt;/p&gt;

&lt;p&gt;图1&lt;/p&gt;

&lt;p&gt;图2&lt;/p&gt;

&lt;p&gt;这两张图清晰的表达了内核文件的读写原理：
1.操作系统内核中对每个打开的文件都有个内核对象
2.所有文件内核对象都被集中索引到一个数组中，称为文件打开表
3.文件表数组前三个元素填充的是stdin、stdout、stderr这三个内核对象
4.为了增加读写效率，内核已经实现了文件读取缓冲，会读取一段一段的读取
5.读取步骤，用户程序先开辟一段内存，内核程序则利用缓冲读取，不足时多次读取，结束时返回数据。&lt;/p&gt;

&lt;h2 id=&quot;资产管理器&quot;&gt;资产管理器&lt;/h2&gt;

&lt;p&gt;早前很多引擎都有独立的资产管理器，它被制作成了一个独立的软件，专门用于管理游戏资产，包括网格、材质、纹理、着色器程序、动画、音频、配置等。&lt;/p&gt;

&lt;p&gt;资产管理器本身是一个具有清晰设计、统一、中心化的子系统，负责管理游戏中用到的所有类型的资产，只是现代大多引擎已经将资产管理整合到引擎编辑器中。&lt;/p&gt;

&lt;h3 id=&quot;资产管理器解决了什么问题答案是&quot;&gt;资产管理器解决了什么问题？答案是：&lt;/h3&gt;
&lt;p&gt;1.资产预览，资产在引擎中快速预览，并对不同资产类型区分展示。
2.资产查找，通过查找功能快速查找到资产。
3.资产组合，通过组合信息管理资产各个依赖。
    资产通常组合在一起使用，因此组合信息是资产管理的一部分。
    加载时需要依赖多个资产，引擎通过资产元数据将这些信息保存下来。
4.资产转换，将外部资产导入到引擎中使用。
    外部资产需要经过一定的转换才能在引擎中使用。
    引擎通常有自己的资产导入系统，将外部资产转换为自身使用的数据格式。
5.运行时资产管理，引擎向应用层提供加载和释放资产接口，并管理已加载资产对象。
    资产的加载和释放，引擎需要提供给应用层加载和释放资产的接口，引擎本身也需要对这些资产进行管理。&lt;/p&gt;

&lt;p&gt;下面我们从资产管道、资产类型、运行时资产管理、元文件、资产包，四个方面介绍下引擎中的资产管理器。&lt;/p&gt;

&lt;h3 id=&quot;资产管道&quot;&gt;资产管道：&lt;/h3&gt;

&lt;p&gt;每个资产都需要通过资产管道才能最终被游戏引擎所使用。每个资产管道的始端都是DCC原生格式的源资产（Maya的.ma或.mb、3DMax的.max或.obj、Photoshop的.psd文件等）。资产经过资产管道的导出器、资产编译器、资产链接器，最终生成了游戏引擎可以使用的数据格式。&lt;/p&gt;

&lt;p&gt;第一步，通常DCC工具需要撰写自定义插件（大都已提供现成统一的插件），把DCC里的数据导出为某种中间格式（例如.fbx格式），一般DCC工具都会提供接口或脚本供程序员写导出插件。
第二步，中间格式数据仍然需要经过一定的转换才能被引擎使用，因此引擎通过资产编译器转换中间格式。
第三步，通常多个资产组合后才成为一个完整资产，例如网格文件、材质文件、动画文件、贴图文件等，它们经过资产链接器连接后组合成为完整的资产。&lt;/p&gt;

&lt;h3 id=&quot;资产类型&quot;&gt;资产类型：&lt;/h3&gt;

&lt;p&gt;资产经过资产管道后会生成相应的资产相关文件，包括：
1.资产源文件
2.资产配置文件（元数据文件）
3.资产目标文件&lt;/p&gt;

&lt;p&gt;资产也分为外部和内部资产，外部资产由DCC导出，内部资产则通过引擎生成，例如材质球、动画控制器、蓝图、粒子等。
资产配置文件记录了资产在引擎中的配置信息和依赖关系。
资产目标文件是引擎根据资产源文件和配置信息生成的符合引擎使用格式的资产文件。&lt;/p&gt;

&lt;p&gt;生成资产元数据文件和目标文件的目的是：
1.在保留原资产文件格式的前提下，生成引擎能使用的格式文件。
2.便于引擎获取每个资产文件的配置及依赖关系。
3.便于引擎统一管理资产，管理资产的目的是，提供例如建立资产数据库、查找、同步、打包等功能。&lt;/p&gt;

&lt;p&gt;Unity引擎使用Mate文件存储资产的配置和依赖关系。当引擎导入资产时就会生成相应的Mate文件，并根据这个Mate文件在Library文件夹下生成目标文件，同时根据修改的Mate文件配置来调整或重新生成目标文件。&lt;/p&gt;

&lt;p&gt;Unreal引擎则稍稍有些不同，资产源文件仍然会转换成资产目标文件，只是它把资产配置文件和资产目标文件合并在一个uasset文件中，这样导入后源文件就不再需要。如果你想要获取元数据，UE4也提供了python接口和蓝图接口。&lt;/p&gt;

&lt;h3 id=&quot;运行时资产管理&quot;&gt;运行时资产管理：&lt;/h3&gt;

&lt;p&gt;运行时资产管理通常包括，资产对象管理、资产对象映射管理。&lt;/p&gt;

&lt;p&gt;资产从加载到实例化，在引擎内部必须有一个有效的管理机制，其职责为：
1.同一份资源只会存在一个副本
2.管理资产生命周期，确保不需要时卸载
3.处理复合资产，复合资产依赖多个资产组合而成。
4.维护引用，确保复合资产在内存中的引用关系正确。
5.资产接口，提供资产载入与卸载接口，包含同步和异步的载入方式。&lt;/p&gt;

&lt;p&gt;基于这五个职责，资产在运行时的引擎中必须拥有信息为，资产地址，资产对象，资产对象ID（运行时ID）。&lt;/p&gt;

&lt;p&gt;部分引擎对每个资源都配备了资产唯一ID（GUID），让资产地址与资产唯一ID有绑定关系，这使得引擎在资产迁移时能发挥更好的作用。&lt;/p&gt;

&lt;p&gt;资产对象映射管理包括：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;资产路径与资产唯一ID的映射关系&lt;/li&gt;
  &lt;li&gt;资产唯一ID与资产对象ID的映射关系&lt;/li&gt;
  &lt;li&gt;资产对象ID与资产对象的映射关系&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;有了这些映射关系，引擎就能通过资产路径查找到资产对象，从而保证不重复加载，并维护好各资产之间的引用关系。&lt;/p&gt;

&lt;p&gt;在UE4和Unity上也同样做了这种类型的资产映射关系的管理。&lt;/p&gt;

&lt;h3 id=&quot;资产加载和卸载&quot;&gt;资产加载和卸载&lt;/h3&gt;

&lt;p&gt;资产加载与卸载接口必不可少，通常引擎都会定制一些依赖数据，例如前面提到的资产配置文件（也可以说是资产元数据），多个资产组合达成资源包时，资源包之间的依赖关系也同样需要有数据来维护。&lt;/p&gt;

&lt;p&gt;引擎都会有资源加载的统一接口，包括加载和卸载，同步和异步，资源包和非资源包形式。&lt;/p&gt;

&lt;p&gt;图&lt;/p&gt;

&lt;p&gt;引擎的资产加载和卸载框架各引擎之间稍有不同，不过总体差不了太多，或直接IO调用，或用开启线程后做IO调用，然后通过存储资产对象与映射关系来搭建资产加载和释放的框架，由于各个引擎接口都不一样，不做详细介绍。&lt;/p&gt;

&lt;h3 id=&quot;资源包数据格式&quot;&gt;资源包数据格式&lt;/h3&gt;

&lt;p&gt;通常引擎都会提供类似AssetBundle的资源组合包，便于外部资源下载和更新。&lt;/p&gt;

&lt;p&gt;在UE4和Unity上都有相同的功能，只是命名不同，UE4为Pak，Unity为AssetBundle。&lt;/p&gt;

&lt;p&gt;Pak或AssetBundle中存放着多个复合资产，通过引擎接口加载指定资产。&lt;/p&gt;

&lt;p&gt;图&lt;/p&gt;

&lt;p&gt;为了方便理解，我把数据格式从头往下画。实际中的Pak和AssetBundle数据格式要倒一下，头信息在最底部，资产数据块在最前头。&lt;/p&gt;

&lt;p&gt;资源包数据格式：
1.文件头信息与数据块拆分
2.可整体压缩或部分压缩
3.通过依赖配置加载外部资源包&lt;/p&gt;

&lt;p&gt;资源包可以通俗的认为是一个多文件的组合，它可以自己做压缩，也可以让资产压缩后再组合成文件。&lt;/p&gt;

&lt;p&gt;通常资源包的数据格式由文件头和数据块两部分组成。文件头信息中包含了资产信息和偏移量，通过加载文件头，就能知道资产在文件中的位置、类型、名称、大小等。&lt;/p&gt;

&lt;p&gt;这种资产组织方式使得我们通过差量方式更新资产成为可能。&lt;/p&gt;

&lt;p&gt;图&lt;/p&gt;

&lt;p&gt;根据资源包的数据格式特点，可以规划差量更新步骤：
1.打差量包，包头保持完整，数据块则只加入差量部分
2.下载差量包
3.合并两个资源包文件
4.合并时使用差量包头作为文件头
5.合并时提取原资源包中的不更新部分和差量包中的更新部分放入新的资源包文件中
6.更新完成生成新的资源文件&lt;/p&gt;

&lt;p&gt;这部分内容Unity和Unreal并无太大差异。&lt;/p&gt;

&lt;h2 id=&quot;资产规范&quot;&gt;资产规范&lt;/h2&gt;

&lt;p&gt;资产规范目标：
规范命名，防止程序报错，方便自动化检测，方便筛选找寻。
优化内容，每个项目资源都应该有存在目的。
减短路径，路径简短易寻。&lt;/p&gt;

&lt;p&gt;图&lt;/p&gt;

&lt;p&gt;网上有同学分享的很详细了，这里就不赘述，参考《UE4工程规范》：&lt;/p&gt;

&lt;p&gt;https://github.com/skylens-inc/ue4-style-guide/blob/master/README.md#12-%E8%B5%84%E6%BA%90%E7%B1%BB%E5%9E%8B%E8%A1%A8-&lt;/p&gt;

&lt;h3 id=&quot;参考资料&quot;&gt;参考资料：&lt;/h3&gt;

&lt;p&gt;《游戏引擎架构》叶劲峰 译&lt;/p&gt;

&lt;p&gt;《游戏引擎原理与实践》 程东哲 著&lt;/p&gt;

&lt;p&gt;《vmath》&lt;/p&gt;

&lt;p&gt;https://github.com/BlackMATov/vmath.hpp#Matrix-Transform-3D&lt;/p&gt;

&lt;p&gt;《从虚函数表到RTTI》&lt;/p&gt;

&lt;p&gt;https://zhuanlan.zhihu.com/p/150579874&lt;/p&gt;

&lt;p&gt;《虚幻引擎4文档》&lt;/p&gt;

&lt;p&gt;https://docs.unrealengine.com/4.27/zh-CN/Basics/AssetsAndPackages/AssetMetadata/&lt;/p&gt;

&lt;p&gt;《UE4工程规范》&lt;/p&gt;

&lt;p&gt;https://github.com/skylens-inc/ue4-style-guide/blob/master/README.md#12-%E8%B5%84%E6%BA%90%E7%B1%BB%E5%9E%8B%E8%A1%A8-&lt;/p&gt;

&lt;p&gt;《链接、装载与库 - 内核运行库》&lt;/p&gt;

&lt;p&gt;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484809&amp;amp;idx=1&amp;amp;sn=89091ecce47229ebf10e4855c0ccceca&amp;amp;chksm=fc22608ecb55e998cdff3952057e6d1c6f099463797d57458ec1f4d37a432d46dc3c5bfcfbb5&amp;amp;token=557108361&amp;amp;lang=zh_CN#rd&lt;/p&gt;

&lt;p&gt;《链接、装载与库 - 静态链接》&lt;/p&gt;

&lt;p&gt;http://luzexi.com/2021/06/20/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B029&lt;/p&gt;

&lt;p&gt;《深度探索C++对象模型-总结》&lt;/p&gt;

&lt;p&gt;http://luzexi.com/2020/11/20/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B014&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484932&amp;amp;idx=1&amp;amp;sn=13c96ed5d6a24abe43d9a63a5e2aca23&amp;amp;chksm=fc226303cb55ea1527fead1e2d90b435a6eb911cb54e58ffbde95b674f1982859fa6a66f3837&amp;amp;token=1405609858&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(四十四) 《心流》#3 专注时人们的表现</title>
   <link href="http://www.luzexi.com/2021/09/22/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B044"/>
   <updated>2021-09-22T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/09/22/读书笔记44</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484913&amp;amp;idx=1&amp;amp;sn=9589d58b41c85d01d1aaa68872260539&amp;amp;chksm=fc2260f6cb55e9e08222aa5096a65949a05c98edc73eb005e289e11010c68a4816ed35a50035&amp;amp;token=134603822&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;背景&quot;&gt;背景：&lt;/h1&gt;

&lt;p&gt;在2020年初就买了《心流》，直到2021年1月才读完第一遍，读完的那一刻，我知道我又打开了一个新世界。&lt;/p&gt;

&lt;p&gt;不得不承认就是这本书让我的专注力提高了整整一个级别，这是最让我非常兴奋的地方。而我只是读了第一遍，吸收不到10%的结果，可想而知它的魔力有多么强大。&lt;/p&gt;

&lt;p&gt;于是决定好好回顾总结一下整本书，便开启了第二遍、第三遍的读书之旅。&lt;/p&gt;

&lt;p&gt;读此书的起源是由于我在平时工作、交流、学习时，常常难以集中注意力，因此带着好奇心阅读了这本书。&lt;/p&gt;

&lt;p&gt;我猜大家应该也和我一样，常常为自己难以击中注意力而烦恼，其实我们都希望自己在专注做一件事情的时候能够拥有更高的效率。&lt;/p&gt;

&lt;p&gt;本书的核心是心流的最优体验，简单来说就是“如何让你高兴地专注”。&lt;/p&gt;

&lt;p&gt;作者想表达的是，当你的心流会的体验最佳时，也是学习效率最高的时候，同时也是你最幸福的时刻。&lt;/p&gt;

&lt;p&gt;书本中讲到了关于学习、工作、运动、社交等各方面的心流原理和技巧，我作为知识的搬运工，做了一些精炼和总结的工作。&lt;/p&gt;

&lt;h1 id=&quot;正文&quot;&gt;正文：&lt;/h1&gt;

&lt;p&gt;简单回顾下前文：&lt;/p&gt;

&lt;p&gt;前文我们说了专注力能带给我们幸福，生活让我们感到痛苦的一个重要原因是人们欲望无法填满，我们生活在这个社会被这个的规则所牵动，已经沦为社会的奴隶，在这种环境培养独立意识非常重要同时也非常艰难。&lt;/p&gt;

&lt;h2 id=&quot;控制意识能改善体验的品质&quot;&gt;控制意识能改善体验的品质&lt;/h2&gt;

&lt;p&gt;想要控制意识，首先要了解它。那么意识究竟是如何运作的呢？&lt;/p&gt;

&lt;p&gt;“意识”简单来说就是，某些我们能感觉到的，且有能力引导其方向的东西，诸如情绪、感觉、思想、企图等。&lt;/p&gt;

&lt;p&gt;首先，一个人可以不管外界不管发生什么事，只靠改变意识的内涵，使自己快乐或悲伤。&lt;/p&gt;

&lt;h3 id=&quot;其次意识的力量可以把无助的境况转变为有机会反败为胜的挑战&quot;&gt;其次，意识的力量可以把无助的境况转变为有机会反败为胜的挑战。&lt;/h3&gt;

&lt;p&gt;我们口中所说的奋斗不辍、克服万难的毅力，它不但是成功的要素，也是享受人生的不二法门。&lt;/p&gt;

&lt;h3 id=&quot;最后控制意识需要毅力而培养毅力应该从建立意识的秩序控制感觉与思想着手且最好不要企图走捷径&quot;&gt;最后，控制意识需要毅力，而培养毅力应该从建立意识的秩序、控制感觉与思想着手，且最好不要企图走捷径。&lt;/h3&gt;

&lt;p&gt;瑜伽行者就是控制意识的高手，但跟任何高手一样，他们下了多年功夫苦练，一刻都不懈怠。&lt;/p&gt;

&lt;p&gt;唯有这样投注所有时间与心力，提升操纵内在体验的技巧，方能成为专家。&lt;/p&gt;

&lt;h2 id=&quot;意识的极限&quot;&gt;意识的极限&lt;/h2&gt;

&lt;p&gt;生活中有太多的事情需要我们的注意，但我们顶多同时应付七组资讯，诸如分辨声音、影像、情绪或思想中可辨识的弦外之音等。&lt;/p&gt;

&lt;p&gt;大多数人在每天的三分之一的闲暇时间里，都尽可能避免用脑子，这段空档一半以上都在电视前、杂志、抖音、微信聊天、看app资讯上消磨掉。&lt;/p&gt;

&lt;p&gt;因此对于我们来说，准许哪些资讯进入意识就显得格外重要，这实际上，就决定了我们生活的内涵与品质。&lt;/p&gt;

&lt;h3 id=&quot;神经系统在特定时间内能处理的资讯极为有限每次只能认知和回应一定数量的事件而新涌进来的会把旧的挤掉所以思绪必须井然有序否则就会混乱&quot;&gt;神经系统在特定时间内能处理的资讯极为有限，每次只能认知和回应一定数量的事件，而新涌进来的会把旧的挤掉。所以思绪必须井然有序，否则就会混乱。&lt;/h3&gt;

&lt;h2 id=&quot;注意力是无价的资源&quot;&gt;注意力是无价的资源&lt;/h2&gt;

&lt;p&gt;注意力再强大也无法超越我们前面谈到的限制，它只能在一定时间内处理一定数量的资讯。&lt;/p&gt;

&lt;p&gt;从记忆库中取出资讯，理解、比较、评估，然后做决定，都需要心灵有限的资讯处理能力。&lt;/p&gt;

&lt;h3 id=&quot;控制意识最明显的指标就是能随心所欲地集中注意力不因任何事情而分心若能做到这一点就能在日常生活中找乐趣&quot;&gt;控制意识最明显的指标就是能随心所欲地集中注意力，不因任何事情而分心。若能做到这一点，就能在日常生活中找乐趣。&lt;/h3&gt;

&lt;p&gt;作者举例一位学员E女士：&lt;/p&gt;

&lt;p&gt;她无时无刻不在写作、解决问题、阅读报纸、翻阅当日行程，或只是提出问题，仔细观察周遭事物，并计划下一步的工作。&lt;/p&gt;

&lt;p&gt;她只花很少的时间在日常例行公事上。&lt;/p&gt;

&lt;p&gt;她每天都会抽出时间来位心灵充电，如在湖畔伫立15分钟，闭上眼睛让阳光洒在脸上，或牵着狗在镇外山坡上散步。&lt;/p&gt;

&lt;p&gt;她能充分控制注意力，可以随时把意识关闭，打个盹，然后恢复精神。&lt;/p&gt;

&lt;p&gt;另一位学员R先生：&lt;/p&gt;

&lt;p&gt;说话时目光深邃；听人说话时，往往从多方面分析对话的话语。&lt;/p&gt;

&lt;p&gt;一般人视为理所当然的事常令他感到困惑；在用原创但十分贴切的方式详加分析前，他绝不让任何事轻易溜走。&lt;/p&gt;

&lt;p&gt;R先生尽管不断地磨炼知性，外表却给人一种沉着、宁静的感觉。他似乎永远能察觉到周遭最微小的变动。&lt;/p&gt;

&lt;p&gt;他注意一件事，目的不在于改善或批判它，只要能够观察和了解事实，并表达自己的看法，他就心满意足了。&lt;/p&gt;

&lt;p&gt;R先生不像E女士那样对社会造成立刻的冲击，但他的意识同样复杂而有条理。&lt;/p&gt;

&lt;p&gt;他把注意力尽可能延伸，跟周遭的世界密切结合起来。&lt;/p&gt;

&lt;p&gt;跟E女士一样，他也能充分享受人生。&lt;/p&gt;

&lt;h2 id=&quot;像探照灯那样集中注意力&quot;&gt;像探照灯那样集中注意力&lt;/h2&gt;

&lt;p&gt;大多数人都不能像E女士或R先生那样，把有限的注意力像探照灯一般集中成一道光束，而是任它毫无章法的散开。&lt;/p&gt;

&lt;h3 id=&quot;这说明生活中人们应用注意力的方式足以决定人生的外观与内涵从而反应人们为人处世的现象&quot;&gt;这说明生活中人们应用注意力的方式足以决定人生的外观与内涵，从而反应人们为人处世的现象。&lt;/h3&gt;

&lt;p&gt;例如，在同一个宴会上，外向的人热衷于与人交际；成就不凡的人寻求有用的商界人脉；偏执狂则随时警惕，怕碰到危险等等。&lt;/p&gt;

&lt;p&gt;每种人都有自己的注意力探照灯，这也是不同人之间的差异。&lt;/p&gt;

&lt;p&gt;实际上工作生活中，我们每个职业都在训练自己的集中注意力的方式，例如，软件工程师会更多关注计算机软件运行过程，销售人员更关注人与人之间的交流与谈判，银行家更专注财务状况，运动员更关注动作协调与身体素质等等等等。&lt;/p&gt;

&lt;p&gt;这种探照灯一样的集中方式正是他们在专业领域中学会了如何抓住领域中易受忽略的讯号。&lt;/p&gt;

&lt;h2 id=&quot;内在失序与精神熵&quot;&gt;内在失序与精神熵&lt;/h2&gt;

&lt;p&gt;内在失序的意思是，外来资讯跟当下的意图发生冲突，使我们分心，使我们无法为现实意图而努力，造成对意识极为不利的影响。&lt;/p&gt;

&lt;p&gt;作者举了一个例子，胡里欧破轮胎的故事。他的汽车轮胎破了，于是他一大早小心翼翼地把车开到加油站，把轮胎的气打满，又尽快开到工厂。下班时轮胎气又漏光了，他再到工厂附近加油站打满气后再开回家。胡里欧如法炮制了三天，一整天他都在担心，今晚是否能回家以及明天是否能到岗。这使得他无法专心工作，情绪也变得烦躁不安。&lt;/p&gt;

&lt;h3 id=&quot;每当周围的环境对意识的目标构成威胁就会发生内在的失序现象这称为精神熵&quot;&gt;每当周围的环境对意识的目标构成威胁，就会发生内在的失序现象，这称为精神熵。&lt;/h3&gt;

&lt;h3 id=&quot;它会导致自我解体使效率大打折扣&quot;&gt;它会导致自我解体，使效率大打折扣。&lt;/h3&gt;

&lt;h3 id=&quot;这种状况若持续太久对自身造成严重的损害使自我再也不能集中注意力实现任何目标&quot;&gt;这种状况若持续太久，对自身造成严重的损害，使自我再也不能集中注意力实现任何目标。&lt;/h3&gt;

&lt;h2 id=&quot;井然有序的意识&quot;&gt;井然有序的意识&lt;/h2&gt;

&lt;p&gt;精神熵的反面就是最优体验。&lt;/p&gt;

&lt;h3 id=&quot;当我们专注于资讯与目标时精神能量就会源源不断涌上来这时我们就没有了忧虑情绪也不再猜疑自己的能力&quot;&gt;当我们专注于资讯与目标时，精神能量就会源源不断涌上来，这时我们就没有了忧虑情绪，也不再猜疑自己的能力。&lt;/h3&gt;

&lt;p&gt;我们不再怀疑自己，也是因为我们得到了明确的鼓励，你做的很好。这种积极的反馈，强化了自我，使我们能投入更多的注意力，照顾内心与外在环境的平衡。&lt;/p&gt;

&lt;p&gt;作者举了一个例子。麦德林的工作是装配线上的工人，每天要重复600次的工作，像这样枯燥的工作，他做了五年，但还是觉得很快乐。因为他对工作的态度跟一名奥运选手差不多，常常思索如何打破纪录。他像外科医生一般一丝不苟的设计工具的安放顺序和每一步动作，经过五年的努力，他最好的成绩是28秒装配完一个单元。&lt;/p&gt;

&lt;p&gt;心流的最优体验出现时，一个人可以投入全部的注意力以求实现目标。没有失序现象需要整顿，自我也没有受到任何威胁，因此不需要分心防卫。&lt;/p&gt;

&lt;h3 id=&quot;一个人若能充分掌控意识尽可能创造心流体验生活品质势必会提高&quot;&gt;一个人若能充分掌控意识，尽可能创造心流体验，生活品质势必会提高。&lt;/h3&gt;

&lt;p&gt;在心流中，我们是精神能量的主宰，无论做什么事，都能使意识更有秩序。&lt;/p&gt;

&lt;p&gt;一位攀岩专家简要说明了自己的心流感觉：&lt;/p&gt;

&lt;h3 id=&quot;越来越完美的自我控制产生一种痛快的感觉&quot;&gt;越来越完美的自我控制，产生一种痛快的感觉。&lt;/h3&gt;

&lt;h3 id=&quot;你不断逼迫身体发挥所有极限直到全身隐隐作痛&quot;&gt;你不断逼迫身体发挥所有极限，直到全身隐隐作痛。&lt;/h3&gt;

&lt;h3 id=&quot;然后你会满怀敬畏地回顾自己回顾你所做的一切&quot;&gt;然后你会满怀敬畏地回顾自己，回顾你所做的一切。&lt;/h3&gt;

&lt;h3 id=&quot;那种佩服的感觉简直无法形容&quot;&gt;那种佩服的感觉，简直无法形容。&lt;/h3&gt;

&lt;h3 id=&quot;它带给你一种狂喜一种自我满足&quot;&gt;它带给你一种狂喜，一种自我满足。&lt;/h3&gt;

&lt;h3 id=&quot;只要在这种战役中战胜过自己人生其他战场的挑战也就变得容易多了&quot;&gt;只要在这种战役中战胜过自己，人生其他战场的挑战也就变得容易多了。&lt;/h3&gt;

&lt;p&gt;其实战斗中对抗的不是自己，而是使意识失序的精神熵。&lt;/p&gt;

&lt;h3 id=&quot;体验过心流的人都知道那份深沉的快乐是严格的自律集中注意力换来的&quot;&gt;体验过心流的人都知道，那份深沉的快乐，是严格的自律、集中注意力换来的。&lt;/h3&gt;

&lt;h2 id=&quot;独特性与复杂性的完美结合&quot;&gt;独特性与复杂性的完美结合&lt;/h2&gt;

&lt;p&gt;复杂性是由两种广泛的心理过程造成的，一种是独特化，另一种是整合。&lt;/p&gt;

&lt;p&gt;1.独特化是把自己与他人区分开来，朝独一无二的方向发展。&lt;/p&gt;

&lt;p&gt;2.整合则恰好相反，是借着超越自我的观念和实体与他人连结。&lt;/p&gt;

&lt;p&gt;独特化和整合，两者都是使不可或缺的。如果独特化而不整合，体系就会出现一片混乱，反之也是一样。&lt;/p&gt;

&lt;h3 id=&quot;当我们只有独特化的自我时虽然也能获得极高的成就但会陷入以自我为中心的危险&quot;&gt;当我们只有独特化的自我时，虽然也能获得极高的成就，但会陷入以自我为中心的危险。&lt;/h3&gt;

&lt;h3 id=&quot;同样的如果一个人的自我若是完全建立在整合上固然也能有良好的人际关系和安全感却缺乏独立的个性&quot;&gt;同样的，如果一个人的自我若是完全建立在整合上，固然也能有良好的人际关系和安全感，却缺乏独立的个性。&lt;/h3&gt;

&lt;h3 id=&quot;只有一个人把精神能量平均投注在这两方面既不过分自私也不盲从才算达到自我所追求的复杂性&quot;&gt;只有一个人把精神能量平均投注在这两方面，既不过分自私，也不盲从，才算达到自我所追求的复杂性。&lt;/h3&gt;

&lt;p&gt;复杂的自我能够成功地融合这两种乍看矛盾的过程。心流体验会使自我变得比过去更复杂，这可以说是一种成长。&lt;/p&gt;

&lt;h3 id=&quot;我们在克服挑战时必然会使我们变得更有能力和技巧因此心流就是经由这种过程加深自我独特化的当我们每经历一次心流后我们就会变得更独特更难预测并拥有非凡的技能&quot;&gt;我们在克服挑战时，必然会使我们变得更有能力和技巧，因此心流就是经由这种过程加深自我独特化的。当我们每经历一次心流后，我们就会变得更独特，更难预测，并拥有非凡的技能。&lt;/h3&gt;

&lt;p&gt;选定一个目标，投入全部的注意力，不论做什么事情，都会觉得乐趣无穷。&lt;/p&gt;

&lt;h3 id=&quot;一旦尝到这种快乐我们就会加倍努力重温它的滋味自我就这样开始成长&quot;&gt;一旦尝到这种快乐，我们就会加倍努力，重温它的滋味，自我就这样开始成长。&lt;/h3&gt;

&lt;p&gt;心流之所以重要，不仅是因为他能使现在更快乐，也是因为它会强化我们的自信心。&lt;/p&gt;

&lt;p&gt;虽然心流没有捷径可走，但只要我们了解它的运作方式，就有可能使生活改观。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484913&amp;amp;idx=1&amp;amp;sn=9589d58b41c85d01d1aaa68872260539&amp;amp;chksm=fc2260f6cb55e9e08222aa5096a65949a05c98edc73eb005e289e11010c68a4816ed35a50035&amp;amp;token=134603822&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>游戏引擎架构#2 容器、内存、RTTI与反射</title>
   <link href="http://www.luzexi.com/2021/09/17/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B043"/>
   <updated>2021-09-17T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/09/17/读书笔记43</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484906&amp;amp;idx=1&amp;amp;sn=70443040745649b082a93650d180959e&amp;amp;chksm=fc2260edcb55e9fb318468bd6f0ce4b2df2277338c3aeb1d0f31b00e4dcd01c18fba4b75581b&amp;amp;token=2034706848&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;背景&quot;&gt;背景：&lt;/h1&gt;

&lt;p&gt;作为游戏开发从业者，从业务到语言到框架到引擎，积累了一些知识和经验，特别是在看了好几遍《游戏引擎架构》后对引擎的架构感触颇深。&lt;/p&gt;

&lt;p&gt;近段时间对引擎剖析的想法也较多，正好借着书本对游戏引擎架构做一个完整分析。此书用简明、清楚的方式覆盖了游戏引擎架构的庞大领域，巧妙地平衡了广度与深度，并且提供了足够的细节，使得初学者也能很容易地理解其中的各种概念。&lt;/p&gt;

&lt;p&gt;目标是掌握游戏引擎架构知识，方法是跟随《游戏引擎架构》这本书、结合引擎源码、自己的经验，分析游戏引擎的历史、架构、模块。最后通过实践自主引擎的开发来完成对引擎知识的掌握。&lt;/p&gt;

&lt;p&gt;游戏引擎知识面深而广，所以对这系列的文章书编写范围做个保护，即不对细节进行过多的阐述，重点剖析的是架构、流程以及模块的运作原理。&lt;/p&gt;

&lt;p&gt;虽然参考了《游戏引擎架构》这本书，但由于它的部分知识太陈旧，所以我不得不将这些知识重新深挖后总结自己的观点。&lt;/p&gt;

&lt;h1 id=&quot;概述&quot;&gt;概述：&lt;/h1&gt;

&lt;p&gt;本章开始对引擎中的重要的模块和库进行详细的分析，我挑选了十五个库和模块来分析：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;时间库&lt;/li&gt;
  &lt;li&gt;自定义容器库&lt;/li&gt;
  &lt;li&gt;字符串散列库&lt;/li&gt;
  &lt;li&gt;内存管理框架&lt;/li&gt;
  &lt;li&gt;RTTI与反射模块&lt;/li&gt;
  &lt;li&gt;图形计算库&lt;/li&gt;
  &lt;li&gt;资产管理模块&lt;/li&gt;
  &lt;li&gt;低阶渲染器&lt;/li&gt;
  &lt;li&gt;剔除与合批模块&lt;/li&gt;
  &lt;li&gt;动画模块&lt;/li&gt;
  &lt;li&gt;物理模块&lt;/li&gt;
  &lt;li&gt;UI核心框架&lt;/li&gt;
  &lt;li&gt;性能剖析器的核心部分&lt;/li&gt;
  &lt;li&gt;脚本系统&lt;/li&gt;
  &lt;li&gt;视觉效果模块&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;本篇内容为列表中的前五个。&lt;/p&gt;

&lt;h1 id=&quot;正文&quot;&gt;正文：&lt;/h1&gt;

&lt;p&gt;由易到难，我们从最简单的说起。&lt;/p&gt;

&lt;h2 id=&quot;时间库&quot;&gt;时间库&lt;/h2&gt;

&lt;p&gt;时间库最简单使用的也最多，在引擎中的每个模块都会使用到。其时间信息包括：真实时间、游戏时间、全局时间、相对时间，时间缩放因子。
其常见问题为各平台的时间获取方式不同，因此基本都会针对每个平台分别实现一个时间获取函数。&lt;/p&gt;

&lt;p&gt;测量时间在时间库中占重要位置，游戏中循环调用间隔、帧率、以及移动速度都会使用时间测量的单位来进行。&lt;/p&gt;

&lt;p&gt;通常引擎都会用帧率调控的方法来稳定帧率，例如我们在引擎上设置了30帧/s，那么当本帧耗时小于33ms时，则在主循环结束时让线程在剩下的时间里休眠。
反之，如果主循环耗时大于33ms，则等待到下一帧再执行。通过这样调控的方式来稳定帧率。&lt;/p&gt;

&lt;p&gt;垂直同步是另一种帧率调控方法，由于前置缓冲区和后置缓冲区在交换时会有部分消隐问题导致画面撕裂，因此垂直同步会等待消隐时间，错过了则等待下一次消隐区间，这会让画面更加稳定，但并不保证以某个特定帧率运行而且时常会降低帧率，因此很少有游戏使用这种技术。&lt;/p&gt;

&lt;p&gt;当使用测量时间时，通常都会以帧的形式更新时间跨度，例如计算每帧之间的时间、计算动画当前帧、移动速度下当前帧的移动距离等。除此之外，最常用的如最大帧率、固定帧率、全局时间缩放等，这些因子在很多引擎模块中也常会用到，例如动画时间、音频时间，粒子生命时间等。&lt;/p&gt;

&lt;h2 id=&quot;自定义容器库&quot;&gt;自定义容器库&lt;/h2&gt;

&lt;p&gt;我们在使用的各式各样集合型数据结构也被称为容器，它们的任务都是一样的，存储及管理多个数据元素。
然而细节上各种容器运行方式会有一些差异，它们各自也有各自的优缺点。它们包括但不仅限于：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;数组（Array）&lt;/li&gt;
  &lt;li&gt;动态数组（Dynamic Array）&lt;/li&gt;
  &lt;li&gt;链表（Linked List）&lt;/li&gt;
  &lt;li&gt;堆栈（Stack）&lt;/li&gt;
  &lt;li&gt;队列（Queue）&lt;/li&gt;
  &lt;li&gt;双端队列（Double-ended Queue）&lt;/li&gt;
  &lt;li&gt;优先队列（Priority Queue）&lt;/li&gt;
  &lt;li&gt;树（Tree）&lt;/li&gt;
  &lt;li&gt;二叉查找树（Binary Search Tree，BST）&lt;/li&gt;
  &lt;li&gt;二叉堆（Binary Heap）&lt;/li&gt;
  &lt;li&gt;字典（Dictionary）&lt;/li&gt;
  &lt;li&gt;集合（Set）&lt;/li&gt;
  &lt;li&gt;图（Graph）&lt;/li&gt;
  &lt;li&gt;有向无环图（Directed Acyclic Graph，DAG）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;我们在操作容器时，常用的操作有：插入、移除、顺序访问（迭代）、随机访问、查找、排序。&lt;/p&gt;

&lt;p&gt;这里介绍下第三方标准库的优缺点：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;STL，功能丰富、可移植能力强，但内存分配效率差一些，部分容器算法性能较差。&lt;/li&gt;
  &lt;li&gt;Boost，在STL上更多功能、效率高，发布许可证有些问题。&lt;/li&gt;
  &lt;li&gt;Loki，功能丰富，复杂度较高，性能未知。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;许多引擎都会提供常见的自定义容器实现，建立自定义容器类一般都处于如下原因：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;完全掌控：控制数据结构的内存需求、使用的算法、内存分配规则。&lt;/li&gt;
  &lt;li&gt;优化性能：针对某个业务做出适合性的调整，或借助某些硬件功能可以优化数据结构和算法。&lt;/li&gt;
  &lt;li&gt;可定制性：根据业务需要增加第三方库没有的功能、例如容器性能调试、内存统计、内存快照等。&lt;/li&gt;
  &lt;li&gt;消除外部依赖：当第三方库出现问题时，需要依赖外部的团队，这可能无法提供及时的服务。自定义容器，能在库出现问题时做到可自行修复。&lt;/li&gt;
  &lt;li&gt;并发同步：常用第三方容器在线程间并发同步上的操作可能没有你想的那么完美，用自定义容器就能为自己定制更合适的同步机制。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Unreal容器一看名字就知道是什么，它们包括：
TArray、TArrayView、TBasicArray、FBinaryHeap、TBitArray、TChunkedArray、TCircularBuffer、TCircularQueue、
TDiscardableKeyValueCache、TResourceArray、FHashTable、TIndirectArray、TLinkedList、TIntrusiveLinkedList、TDoubleLinkedList、TList、
TMapBase、TSortableMapBase、TQueue、FScriptArray、TSet、TSortedMap、TSparseArray、TStaticArray、TStaticBitArray、TTripleBuffer、TUnion等等&lt;/p&gt;

&lt;p&gt;Unity容器方面基本上与Unreal差不多，Unreal有的Unity也基本有，各自也都有一些特殊用途的容器，只是这些容器代码分散较开，说明容器部分的架构Unity编排的相对混乱一些。&lt;/p&gt;

&lt;h2 id=&quot;字符串散列库&quot;&gt;字符串散列库&lt;/h2&gt;

&lt;p&gt;字符串在程序中占据了很大的内存，通常有这三个问题，&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;拷贝多&lt;/li&gt;
  &lt;li&gt;拼接多&lt;/li&gt;
  &lt;li&gt;判断相同字符串&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;拷贝多原因&quot;&gt;拷贝多，原因：&lt;/h3&gt;

&lt;p&gt;在函数的形参和返回值上，常常会使用实例的方式去做，这导致字符串拷贝变的频繁。每次字符串拷贝都需要经历，内存分配，内存拷贝，内存销毁这三步骤，可想而知字符串在拷贝上的消耗非常大。&lt;/p&gt;

&lt;h3 id=&quot;拷贝多解决方案&quot;&gt;拷贝多，解决方案：&lt;/h3&gt;

&lt;p&gt;通常是由于业务代码引起的，因此也只有调整业务代码才能缓解。包括修改函数形参类型和返回值类型。&lt;/p&gt;

&lt;h3 id=&quot;拼接多原因&quot;&gt;拼接多，原因：&lt;/h3&gt;

&lt;p&gt;每个字符串在拼接完毕后通常都会生成一个临时的新的字符串，这导致拼接的那几个字符串内存被丢弃而浪费。&lt;/p&gt;

&lt;h3 id=&quot;拼接多解决方案&quot;&gt;拼接多，解决方案：&lt;/h3&gt;

&lt;p&gt;1.编码规范问题，具体业务具体分析
2.用对象池方式重复利用内存&lt;/p&gt;

&lt;h3 id=&quot;判断相同字符串原因&quot;&gt;判断相同字符串，原因：&lt;/h3&gt;

&lt;p&gt;相同字符串的判断逻辑在代码中占比通常比较大，特别是在业务逻辑中。如果只是单纯的比较两个字符串的每个字符，效率会变得非常低下&lt;/p&gt;

&lt;h3 id=&quot;判断相同字符串解决方案&quot;&gt;判断相同字符串，解决方案：&lt;/h3&gt;

&lt;p&gt;使用HashID就能解决这个问题，即把字符串计算成Hash值，用数字比较来代替字符串比较。&lt;/p&gt;

&lt;p&gt;注意，并不是一定要让字符串做Hash才能增加效率，如果相同字符的判断操作比较少，而Hash计算和加入容器的操作比较多，那么就会得不偿失。&lt;/p&gt;

&lt;h4 id=&quot;unreal引擎的字符串fstring构建&quot;&gt;Unreal引擎的字符串FString构建：&lt;/h4&gt;

&lt;p&gt;1.用TArray&lt;TCHAR&gt;动态数组作为容器
2.通过TArray&lt;TCHAR&gt;扩容构建和拼接字符串
3.TArray&lt;TCHAR&gt;使用专门的内存块作为管理，能做到提前分配和释放回池&lt;/TCHAR&gt;&lt;/TCHAR&gt;&lt;/TCHAR&gt;&lt;/p&gt;

&lt;h3 id=&quot;unreal引擎字符串的fname&quot;&gt;Unreal引擎字符串的FName：&lt;/h3&gt;

&lt;p&gt;1.FName和FString有些不同，FName有HashID，是含有一个uint32整数的结构体，而FString则没有
2.FName的字符串存储在FNameEntry实例中
3.每个FName都有自己的HashID，用于比较相同的字符串
4.FNameEntry是一个Char数组+HashID+Header的实例
5.通过FNamePool存储FNameEntry，FNamePool就是一个字典容器，存储着所有的FNameEntry实例
6.FName通过HashID从对象池中获得字符串实体FNameEntry
7.也可以直接通过HashID比较两个字符串是否相同，从而提高效率&lt;/p&gt;

&lt;h3 id=&quot;unity与unreal不同使用对象池方式重复利用字符串内存并且没有字符串散列机制因为引擎内部不需要&quot;&gt;Unity与Unreal不同，使用对象池方式重复利用字符串内存，并且没有字符串散列机制（因为引擎内部不需要）。&lt;/h3&gt;

&lt;h2 id=&quot;内存管理框架&quot;&gt;内存管理框架&lt;/h2&gt;

&lt;p&gt;先介绍下操作系统自身的内存管理方式：&lt;/p&gt;

&lt;p&gt;1.操作系统以进程为单位来运行每个程序。
2.同时为每个进程分配了一个独立的虚拟空间。
3.每个虚拟空间里有内核空间和用户空间之分。
4.内核空间为共享库和内核程序使用的堆栈空间。
5.每个虚拟空间都会拆分成多个段来存储各类数据和程序指令
6.虚拟内存和物理内存之间使用页表进行映射，因此在虚拟空间中连续的内存在物理空间中不一定连续。
7.操作系统会将不使用的内存块交换（Swap）出去成为硬盘空间的一部分，当需要访问时再交换（Swap）回来。&lt;/p&gt;

&lt;p&gt;下面我画了3个图，用图来解释会更容易理解些：&lt;/p&gt;

&lt;p&gt;图1&lt;/p&gt;

&lt;p&gt;图2&lt;/p&gt;

&lt;p&gt;图3&lt;/p&gt;

&lt;p&gt;以上三张图完整的体现了操作系统内存的运作方式。&lt;/p&gt;

&lt;p&gt;1.图1描述了，每个进程都有各自的独立虚拟空间，分为用户空间和内核空间，并且32位和64的空间大小不同。
2.图2描述了，一个虚拟空间中有很多个段，其中包括栈段、堆段、代码段、数据段等。
3.图3描述了，虚拟内存和物理内存通过页表映射，物理内容与硬盘会有一个Swap机制。&lt;/p&gt;

&lt;p&gt;写程序时我们比较关心堆内存，那么操作系统是如何管理堆内存的呢？&lt;/p&gt;

&lt;p&gt;我们来了解下堆内存的分配和释放机制&lt;/p&gt;

&lt;p&gt;图1：&lt;/p&gt;

&lt;p&gt;图2：&lt;/p&gt;

&lt;p&gt;图3：&lt;/p&gt;

&lt;p&gt;为了快速学习，画了三张图方便大家理解堆内存分配机制：
1.图1描述了，堆内存会切割成不同的块
2.图2描述了，堆内存分配设计为大堆、小堆和缓冲堆
3.图3描述了，堆内存分配流程，先找索引再从缓冲区中找最后切割大块内存&lt;/p&gt;

&lt;p&gt;一句话概括为，堆内存以分块方式切割设计，并分为大堆、小堆、和缓冲堆，通过索引和缓冲区来加速内存的分配和释放。&lt;/p&gt;

&lt;h3 id=&quot;游戏引擎中通常不依赖操作系统内存分配机制原因是对于引擎来说操作系统的内存分配效率太差因此每个引擎都有自建的内存管理框架&quot;&gt;游戏引擎中通常不依赖操作系统内存分配机制，原因是对于引擎来说操作系统的内存分配效率太差，因此每个引擎都有自建的内存管理框架。&lt;/h3&gt;

&lt;p&gt;自建的内存管理框架通常由一些分配规则构成，这些分配规则通常会写成内存分配器（Allocator）被用于引擎的各个模块中。&lt;/p&gt;

&lt;p&gt;虽然内存管理框架有很多种，但内存分配的规则都是相似的，每一种内存分配规则我们称为内存分配器（Allocator），下面我们简单列举一下内存分配器的种类及其规则。&lt;/p&gt;

&lt;p&gt;这里列举了11种（还有更多）内存分配器并分别用一句话概括它们：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;线性内存分配器，分配一块大内存，并不断向前分配。&lt;/li&gt;
  &lt;li&gt;环形内存分配器，支持循环利用的内存分配器。&lt;/li&gt;
  &lt;li&gt;双端内存分配器，两个模块共享一个线性内存分配，并从两端分别进行分配。&lt;/li&gt;
  &lt;li&gt;固定大小内存分配器，把一大块内存拆分成固定大小的N个内存块，每次分配一块。&lt;/li&gt;
  &lt;li&gt;泛化的固定大小内存分配器，拆分成M个大块内存，每个大块内存都有自己固定大小的N个内存块， 且不同块间的小内存块的大小不同。&lt;/li&gt;
  &lt;li&gt;散列式页内存分配器，按某个固定大小的页拆分内存块，用多叉树索引方式连接内存块，一次分配多页并调整树形索引。&lt;/li&gt;
  &lt;li&gt;栈型内存分配器，不断向前分配，并按先进后出的原则回收内存。&lt;/li&gt;
  &lt;li&gt;动态合并内存分配器，从一大块内存块开始分配，分配时不断切割，并在回收时合并相邻的内存块。&lt;/li&gt;
  &lt;li&gt;大小池内存分配器，有大内存池和小内存池之分，大小内存池的分配策略不同，小内存池通常使用泛型固定大小内存分配规则，大内存池由于分配频率低因此分配方式更自由些，可切割可固定可合并。&lt;/li&gt;
  &lt;li&gt;批量内存分配器，多次连续分配多个小内存，先临时分配并立即使用，最后提交锁定内存区间。&lt;/li&gt;
  &lt;li&gt;线程安全的内存分配器，在分配时加入了更多的原子操作或同步锁，让多个同线程可以共享一个内存块。&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;引擎内存管理框架&quot;&gt;引擎内存管理框架&lt;/h2&gt;

&lt;p&gt;不同种类的引擎中的内存框架其实都是大同小异：&lt;/p&gt;

&lt;p&gt;1.大都使用内存分配器来搭建内存框架
2.多种类型的内存分配器混合使用很常见
3.每个模块都有自己的内存分配器
4.经常多个模块共享一个内存分配器&lt;/p&gt;

&lt;h2 id=&quot;unreal引擎的内存分配框架&quot;&gt;Unreal引擎的内存分配框架&lt;/h2&gt;

&lt;p&gt;Unreal有个HAL（Hardware Abstraction Layer）存放了大部分内存管理内容。&lt;/p&gt;

&lt;p&gt;用图来表示为：&lt;/p&gt;

&lt;p&gt;Unreal的内存管理框架总结：&lt;/p&gt;

&lt;p&gt;1.Unreal有统一的内存分配和释放接口FMemory
2.内存统计，使用的是获取堆栈、回溯堆栈信息的方式
3.虽然内存分配器有很多种，但主内存分配器只能选择一种
4.容器有自己专属的分配器，它封装了FMemory接口
5.UObject等业务逻辑有专属的分配器，它封装了FMemory接口，并在此基础上做了垃圾回收设计。
6.引擎各模块大部分使用了FMemory接口来分配和释放内存，少数封装了自己的分配器。
7.主内存分配器中FMallocBinned为主内存分配器（1、2、3代），其分配原则为大小内存池。&lt;/p&gt;

&lt;p&gt;Unity与Unreal稍有不同，它将GC和引擎内存分开管理，并且引擎独立模块有独立内存分配器自己管理。理论上来说这种做法会更好一些，每个模块需要有合适的内存分配算法。&lt;/p&gt;

&lt;h2 id=&quot;rtti与反射模块&quot;&gt;RTTI与反射模块&lt;/h2&gt;

&lt;p&gt;RTTI(Run-Time Type Information)运行时类型检查，它提供了运行时确定对象类型的方法。&lt;/p&gt;

&lt;p&gt;C++内建的RTTI通常很难满足我们的业务需求，特别是在需要做反射的业务上尤其明显，因此引擎通常都需要自建RTTI并增加反射系统。&lt;/p&gt;

&lt;p&gt;先说C++内建的RTTI。与内建RTTI相关的运算符为：typeid 和 dynamic_cast&lt;/p&gt;

&lt;p&gt;快速回顾下typeid 和 dynamic_cast两个运算符的原理&lt;/p&gt;

&lt;p&gt;我在前面的文章中详细介绍过C++内存模型，可以参考下&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://luzexi.com/2020/11/20/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B014&quot;&gt;《深度探索C++对象模型》&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;每个有虚继承或虚函数的C++类都会在运行时有一个type_info数据，通过虚表中的指针指向type_info数据。&lt;/p&gt;

&lt;p&gt;type_info数据结构包含了类名字和父类指针，因此我们可以通过typeid来获得多态类的type_info数据。&lt;/p&gt;

&lt;p&gt;dynamic_cast就是借助type_info来做的功能，它通过多态的type_info来识别是否可以转换类型。&lt;/p&gt;

&lt;p&gt;例如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Point2D pt2d = dynamic_cast&amp;lt;Point2D&amp;gt;(pt);

可以拆解为：
Point2D pt2d = NULL;
type_info type_pt2d = typeid(Point2D);
type_info type_pt = typeid(Point);
if(type_pt2d == type_pt || type_pt.before(&amp;amp;type_pt2d))
{
        pt2d = (Point2D)pt;
}
return pt2d;
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;自建rtti&quot;&gt;自建RTTI&lt;/h2&gt;

&lt;p&gt;由于内置C++的RTTI时常无法满足业务需求，所以通常人们都会自己去建立自己的RTTI。&lt;/p&gt;

&lt;p&gt;人们自建RTTI通常是因为：&lt;/p&gt;

&lt;p&gt;1.有反射需求，例如查找类、调用函数、获取变量、遍历属性等。
2.优化性能，包括减少RTTI内存，提高查找type_info效率等。&lt;/p&gt;

&lt;p&gt;Unreal引擎内部RTTI是默认被禁用的，它通过枚举或整数的方式来定制需要识别的类型。
Unity也是一样，在引擎内核中，无法使用内置的RTTI。&lt;/p&gt;

&lt;p&gt;禁用仅限于引擎实时运行库上，在工具套件和编辑器上仍然被使用。&lt;/p&gt;

&lt;h2 id=&quot;反射模块&quot;&gt;反射模块&lt;/h2&gt;

&lt;p&gt;反射模块是建立在自建RTTI之上的，因为反射需要通过RTTI来获取足够多的类、变量、函数的信息。&lt;/p&gt;

&lt;p&gt;通常当我们通过RTTI来建立反射框架时，通常需要对type信息结构做些规划。例如在type结构中增加动态数组或字典容器将变量和函数的名字和类型存储起来。&lt;/p&gt;

&lt;p&gt;示例代码例如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;struct RTTR_LOCAL class_data
{
    class_data(get_derived_info_func func, std::vector&amp;lt;type&amp;gt; nested_types)
    :   m_derived_info_func(func),
        m_nested_types(nested_types),
        m_dtor(create_invalid_item&amp;lt;destructor&amp;gt;())
    {}
    get_derived_info_func       m_derived_info_func;
    std::vector&amp;lt;type&amp;gt;           m_base_types;
    std::vector&amp;lt;type&amp;gt;           m_derived_types;
    std::vector&amp;lt;rttr_cast_func&amp;gt; m_conversion_list;
    std::vector&amp;lt;property&amp;gt;       m_properties;
    std::vector&amp;lt;method&amp;gt;         m_methods;
    std::vector&amp;lt;constructor&amp;gt;    m_ctors;
    std::vector&amp;lt;type&amp;gt;           m_nested_types;
    destructor                  m_dtor;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;如上代码中我们看到，type实例结构中通常有属性结构实例容器、类型结构实例容器、函数结构实例容器等，目的就是为了存储变量和函数的信息。在实时运行过程中，当我们需要用字符串查找某个函数，或者查找某个变量时，则会从这些容器中去查找。&lt;/p&gt;

&lt;p&gt;自建RTTI是通过对象信息来构建RTTI信息集合代码的，因此每个需要RTTI类的实例都需要写相应的编码，通常都会用宏和自动化来代替繁琐的实例化RTTI编码。&lt;/p&gt;

&lt;p&gt;其基本思想是采用宏来代替常规的变量定义，这样我们就可以在宏函数中将定义的变量添加至自建的反射系统。&lt;/p&gt;

&lt;p&gt;例如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#include &amp;lt;rttr/registration&amp;gt;
using namespace rttr;

struct MyStruct { MyStruct() {}; void func(double) {}; int data; };

RTTR_REGISTRATION
{
    registration::class_&amp;lt;MyStruct&amp;gt;(&quot;MyStruct&quot;)
         .constructor&amp;lt;&amp;gt;()
         .property(&quot;data&quot;, &amp;amp;MyStruct::data)
         .method(&quot;func&quot;, &amp;amp;MyStruct::func);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;unreal的rtti和反射用途较多主要包括蓝图和垃圾回收&quot;&gt;Unreal的RTTI和反射用途较多，主要包括蓝图和垃圾回收。&lt;/h3&gt;

&lt;p&gt;与其他所有引擎一样，Unreal生成反射代码的步骤是：&lt;/p&gt;

&lt;p&gt;1.利用特殊的宏来对变量做标记
2.对C++代码文件进行语法分析
3.用指定的宏提取出对应的数据
4.扫描工具生成RTTI代码
5.最后在初始化时运行生成的代码
6.启动时将收集到的数据保存&lt;/p&gt;

&lt;p&gt;UE4定义了一系列的宏，来帮助开发者将自定义的字段和函数添加至反射系统：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;UCLASS，告诉UE这个类是一个反射类。类必须派生自UObject
USTRUCT，可以不用派生自UObject。不支持GC，也不能包含函数
UPROPERTY，定义一个反射的变量
UFUNCTION，定义一个反射的函数
UENUM，告诉UE这是一个反射的枚举类。支持enum, enum class, enum namespace
UINTERFACE，定义一个反射接口类，只能包含函数
UMETA，反射的一些元数据定义，可以通过标签定义一些该变量的属性
UPARAM，定义函数的参数属性。主要就是显示名字和Ref属性
UDELEGATE，告诉UE这是一个可反射的delegate

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;例如：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;// 一定要声明UCLASS
UCLASS()
class MYGAME_API UMyClass : public UObject
{
    GENERATED_BODY()
    public:
        // 定义一个可反射的函数
        UFUNCTION(BluprintCallable)
        void MyFunc();private:

        // 定义一个可反射的变量
        UPROPERTY(EditAnywhere, BlueprintReadWrite, meta=(AllowPrivateAccess = &quot;true&quot;))
        int MyIntValue;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;通过声明反射将数据结构、变量和函数添加到反射中。&lt;/p&gt;

&lt;p&gt;UE4的UHT（Unreal Header Tool）模块扫描之后生成的代码反射两个代码文件：&lt;/p&gt;

&lt;p&gt;.generated.h文件：重载各种操作符函数，声明各种构造函数。
.gen.cpp文件：单例实现，构造UClass（提取信息并注册）&lt;/p&gt;

&lt;p&gt;Unity与Unreal的稍有不同，一部分为C++调用Mono，另一部分为C#利用反射调用具体的函数。&lt;/p&gt;

&lt;p&gt;1.C++通过Mono获取C#接口
2.反过来C#调用C++则不需要反射。可以通过数组将C++句柄存储起来，用索引获取句柄的方式调用。
3.C#使用反射调用C#代码则比较常见，在编辑器与业务逻辑交互之间会比较多。&lt;/p&gt;

&lt;h3 id=&quot;参考资料&quot;&gt;参考资料：&lt;/h3&gt;

&lt;p&gt;《动态链接与装载》&lt;/p&gt;

&lt;p&gt;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484788&amp;amp;idx=1&amp;amp;sn=8e4f7f36e4ddf6dd2f7c086463c089de&amp;amp;chksm=fc226073cb55e965e5ab259f04293c48f718a2fd72858b11397852d8ba8c7fab656b0f46e248&amp;amp;token=283688006&amp;amp;lang=zh_CN#rd&lt;/p&gt;

&lt;p&gt;《深度探索C++对象模型》&lt;/p&gt;

&lt;p&gt;http://luzexi.com/2020/11/20/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B014&lt;/p&gt;

&lt;p&gt;《malloc和free的实现原理解析》&lt;/p&gt;

&lt;p&gt;https://jacktang816.github.io/post/mallocandfree/&lt;/p&gt;

&lt;p&gt;《UE4 反射系统详细剖析》&lt;/p&gt;

&lt;p&gt;https://cloud.tencent.com/developer/article/1606872&lt;/p&gt;

&lt;p&gt;《UE4内存分配器概述》&lt;/p&gt;

&lt;p&gt;https://www.cnblogs.com/kekec/p/12012537.html&lt;/p&gt;

&lt;p&gt;《RTTR C++ Reflection Library》&lt;/p&gt;

&lt;p&gt;https://github.com/rttrorg/rttr&lt;/p&gt;

&lt;p&gt;《UE4 MallocBinned2分配器》&lt;/p&gt;

&lt;p&gt;https://zhuanlan.zhihu.com/p/79715624&lt;/p&gt;

&lt;p&gt;《FMallocBinned2内存分配器》&lt;/p&gt;

&lt;p&gt;https://www.cnblogs.com/kekec/p/14675228.html&lt;/p&gt;

&lt;p&gt;《UE4垃圾回收》&lt;/p&gt;

&lt;p&gt;https://zhuanlan.zhihu.com/p/67055774&lt;/p&gt;

&lt;p&gt;《UE4蓝图》&lt;/p&gt;

&lt;p&gt;https://zhuanlan.zhihu.com/p/67683606&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484906&amp;amp;idx=1&amp;amp;sn=70443040745649b082a93650d180959e&amp;amp;chksm=fc2260edcb55e9fb318468bd6f0ce4b2df2277338c3aeb1d0f31b00e4dcd01c18fba4b75581b&amp;amp;token=2034706848&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>给女儿写信(十六) 如何练习表达</title>
   <link href="http://www.luzexi.com/2021/09/13/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A116"/>
   <updated>2021-09-13T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/09/13/给女儿的信16</id>
   <content type="html">
&lt;hr /&gt;

&lt;p&gt;导读：喜欢跟孩子们一起，可惜一直在外地工作，因此我给孩子写点信并录成视频，跟孩子聊聊自己的故事以及最近的感受&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Hi Sharon Hi Anne，爸爸好想你们哟。&lt;/p&gt;

&lt;p&gt;爸爸给你们讲讲爸爸最近的状况好不好。&lt;/p&gt;

&lt;p&gt;爸爸最近在练习表达，爸爸最近觉得表达能力很重要，过去几年爸爸对这方面没有重视。&lt;/p&gt;

&lt;p&gt;现在才明白过来，表达是生活的第一要素，所以爸爸想更多的学习表达，并且在平时的时候运用表达。&lt;/p&gt;

&lt;p&gt;爸爸给自己设定了一个习惯，每天早上起来，做一次表达训练。&lt;/p&gt;

&lt;p&gt;怎么做表达训练呢，爸爸跟你说说。&lt;/p&gt;

&lt;p&gt;爸爸先做一个自我介绍，爸爸对着镜子介绍自己，&lt;/p&gt;

&lt;p&gt;大家好，我是陆泽西，大家可以叫我西西，很高兴见到大家。&lt;/p&gt;

&lt;p&gt;然后介绍我的爱好，我来自哪里，我喜欢看什么书。&lt;/p&gt;

&lt;p&gt;介绍完自己后，爸爸会自己跟自己说说自己昨天做了什么，今天打算做什么，最后要感恩谁。&lt;/p&gt;

&lt;p&gt;就这样，每天早起早起跟自己练习一遍，最近爸爸的表达能力提高不少呢。&lt;/p&gt;

&lt;p&gt;爸爸希望Sharon和Anne在平时的生活中能够流畅、清晰、舒服的表达出自己想要说的内容。&lt;/p&gt;

&lt;p&gt;跟爸爸一起加油好吗。加油Sharon、加油Anne，爸爸爱你们。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>游戏引擎架构#1 历史、现状、架构、团队</title>
   <link href="http://www.luzexi.com/2021/08/29/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B042"/>
   <updated>2021-08-29T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/08/29/读书笔记42</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484890&amp;amp;idx=1&amp;amp;sn=1d319050c82fed232d22f1f3dfce0cd3&amp;amp;chksm=fc2260ddcb55e9cbbda832e90def1e8e7440b7ffc85c60264be83d9c6bc62162c5b05839839b&amp;amp;token=834275592&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;背景&quot;&gt;背景：&lt;/h1&gt;

&lt;p&gt;作为游戏开发从业者，从业务到算法到语言到框架到引擎，积累了一些知识和经验，特别是在看了好几遍《游戏引擎架构》后对引擎的架构感触颇深。&lt;/p&gt;

&lt;p&gt;近段时间对引擎剖析的想法也较多，正好借着书本对游戏引擎架构做一个完整分析。此书用简明、清楚的方式覆盖了游戏引擎架构的庞大领域，巧妙地平衡了广度与深度，并且提供了足够的细节，使得初学者也能很容易地理解其中的各种概念。&lt;/p&gt;

&lt;p&gt;目标是掌握游戏引擎架构知识，方法是跟随《游戏引擎架构》这本书、结合引擎源码、自己的经验，分析游戏引擎的历史、架构、模块。最后通过实践自主引擎的开发来完成对引擎知识的掌握。&lt;/p&gt;

&lt;p&gt;游戏引擎知识面深而广，所以对这系列的文章书编写范围做个保护，即不对细节进行过多的阐述，重点剖析的是架构、流程以及模块的运作原理。&lt;/p&gt;

&lt;h1 id=&quot;概述&quot;&gt;概述：&lt;/h1&gt;

&lt;p&gt;文章分为五个部分：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;游戏引擎历史&lt;/li&gt;
  &lt;li&gt;现代引擎概览&lt;/li&gt;
  &lt;li&gt;计算机图形技术发展史&lt;/li&gt;
  &lt;li&gt;游戏制作团队&lt;/li&gt;
  &lt;li&gt;运行时引擎架构概述&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;正文&quot;&gt;正文：&lt;/h1&gt;

&lt;p&gt;此篇分四个主题来概述一下游戏引擎的基础知识，我会分别从历史、现状、架构、团队，这四个方面来阐述。&lt;/p&gt;

&lt;h2 id=&quot;游戏引擎历史&quot;&gt;游戏引擎历史&lt;/h2&gt;

&lt;p&gt;有些同学会问，为什么要讲历史？为什么要讲现状？那是因为了解历史和现状能帮助我们更好的理解引擎的每个部分。&lt;/p&gt;

&lt;p&gt;实际上，每每当你回顾历史，就会发现它是由很多巧合构成的，当知道它的来龙去脉时，我们也就能更好的理解“今天为什么它成为这个样子”。&lt;/p&gt;

&lt;p&gt;我认为游戏引擎这个词语用的非常好，它很好的概括它在游戏开发中的地位。&lt;/p&gt;

&lt;p&gt;我们说所有的程序都可以用工具两个字来描述，那么游戏引擎到底带给了我们什么，才让它成为今天人们普遍关注的焦点呢。&lt;/p&gt;

&lt;p&gt;十几年前的游戏都很简单，容量大小都是以兆计的，通常一款游戏的开发周期在8到10个月左右，最主要的是每款游戏开发都需要重头编写代码，期间存在着大量的重复劳动，耗时耗力。&lt;/p&gt;

&lt;p&gt;慢慢地开发人员总结出些规律，某些游戏逻辑总是有些相似的代码，可以做成模块重复使用，这样就可以大大减少游戏开发周期和开发费用。&lt;/p&gt;

&lt;p&gt;一开始，只是做些低耦合的通用方法，然后逐渐形成了模块，接着演变成了复合型框架，最后形成了大型软件工程，即现在的游戏引擎。&lt;/p&gt;

&lt;h3 id=&quot;id-tech-引擎发展史&quot;&gt;Id Tech 引擎发展史&lt;/h3&gt;

&lt;p&gt;说到引擎发展史不得不说ID software这家游戏公司，很早就开始开发起游戏，从一个小游戏到游戏移植再到《Wolfenstein 3D》，游戏技术让这家公司从无到有，不断创造出一个又一个技术巅峰。&lt;/p&gt;

&lt;p&gt;最后《Wolfenstein 3D》成了游戏引擎的始祖，它不但开创了3D 射击游戏世界，而且程序也被其他游戏所使用。&lt;/p&gt;

&lt;p&gt;对ID公司来说《Wolfenstein 3D》只是小试身手，DOOM（毁灭战士）游戏引擎才是引擎技术的代表，DOOM使用的引擎正是Id Tech 1。&lt;/p&gt;

&lt;p&gt;在DOOM发布后，获得了350万的销量，为ID公司带来了滚滚财源。&lt;/p&gt;

&lt;p&gt;更重要的是DOOM成了ID公司第一款用于商业授权的引擎，这为ID公司增加了一条创收之路，即商业引擎之路从此开始。&lt;/p&gt;

&lt;p&gt;引擎从Id Tech 1到Id Tech 5，每一代升级都是质的飞越。游戏Id Tech引擎也制作出了很多优秀的游戏，包括《Quake》、《半条命》、《反恐精英》等。&lt;/p&gt;

&lt;h3 id=&quot;unreal-引擎发展史&quot;&gt;Unreal 引擎发展史&lt;/h3&gt;

&lt;p&gt;EPIC公司在1998年发布了Unreal引擎，同时《虚幻》这款FPS游戏也发布出来，画面精致绚丽得到大众的认可。&lt;/p&gt;

&lt;p&gt;不仅如此，重要的是Unreal引擎得益于画面精美和DX规范的通用性，很快就得到了许多游戏公司的支持。而且，由于Unreal引擎的通用性，除了游戏，它还广泛使用在3D建模、建筑设计、动作捕捉、电影特效等等领域。&lt;/p&gt;

&lt;p&gt;Unreal引擎一战成名后，便开启了升级之路，引擎从Unreal 1到 Unreal 5，凭借着优秀的画质表现、适中的显卡要求、强大的自定义工具和一站式配套开发使得Unreal具备了各个平台各种游戏的适应性，因此越来越多的游戏采用Unreal引擎。目前已成为全球最流行的游戏引擎之一。&lt;/p&gt;

&lt;h3 id=&quot;unity-引擎发展史&quot;&gt;Unity 引擎发展史&lt;/h3&gt;

&lt;p&gt;Unity在2005年6月时发布了1.0版本的引擎。&lt;/p&gt;

&lt;p&gt;最初，是在2005年苹果公司的全球开发者大会上对外公布并开放使用，当时只是一款面向Mac OS X平台的游戏引擎。&lt;/p&gt;

&lt;p&gt;由于其引擎的易用性，受到广大开发者的喜爱，之后Unity公司便开启了快速升级之路，用了10年时间从1.0一路升级到5.0。&lt;/p&gt;

&lt;p&gt;鉴于引擎的更新速度逐渐加快，Unity官方决定不再在其版本号中标注纯数字，而改用年份与版本号的复合形式，如Unity 2018.2，发布时间为2018年7月10日。&lt;/p&gt;

&lt;p&gt;截止目前使用Unity引擎开发出了的手机游戏，无论是在数量上还是在流行度上，都是所有引擎中最多也是最受欢迎的。&lt;/p&gt;

&lt;p&gt;随着显卡性能越来越强，游戏的画质越来越高，游戏开发周期也越来越长，通常都会达到3到5年，自行开发游戏引擎的话时间还会更长，所以大多数游戏公司还是选择购买现成的游戏引擎，简化游戏的开发过程。&lt;/p&gt;

&lt;h2 id=&quot;现代引擎概览&quot;&gt;现代引擎概览&lt;/h2&gt;

&lt;p&gt;雷神之锤引擎，与游戏业务密切融合，它的第一款游戏是《德军总部》，后相继开发了《毁灭战士》、《雷神之锤》等游戏。Source引擎中的技术也能追溯到雷神之锤的技术。原始雷神之锤引擎的架构相当优秀并且整洁。这些代码都是非常好的例子，能说明工业级游戏引擎是怎样炼成的。&lt;/p&gt;

&lt;p&gt;虚幻引擎，从《虚幻》游戏开始进化，进化到虚幻4代引擎时，号称是业界最好的工具和最丰富的引擎功能。它有方便且强大的着色器制作工具，还提供一个方便快捷的图形用户界面编辑器，虚幻引擎以其全面的功能及内聚易用的工具著称。但虚幻引擎并非完美，大部分开发者都需要用不同方式优化它，才能在具体的目标硬件上运行。可贵的是虚幻引擎拥有极为强大的原型制作工具和商业游戏平台，可用来制作几乎任何3D游戏。&lt;/p&gt;

&lt;p&gt;Source引擎，《半条命2》起家的游戏引擎，其图形能力和工具套件可与虚幻引擎4娉美。&lt;/p&gt;

&lt;p&gt;寒霜引擎，是从《战地：叛逆连队》开发开始的，之后成为艺电应用最广泛的引擎。寒霜引擎拥有强大的统一资产创作工具FrostEd，以及强大的Backend Services工具管道。&lt;/p&gt;

&lt;p&gt;CryEngine，原本是为NVIDIA开发的技术演示程序。确立这个技术具有潜力后，就把演示程序改为了完整的游戏《孤岛惊魂》，最后演化成了游戏引擎。经过多年迭代，CryEngine3已经是一个强大的资产创作工具，具有丰富的功能并拥有高品质的图形渲染引擎。&lt;/p&gt;

&lt;p&gt;索尼的PhyreEngine，专为支持索尼的PS2，PS3，PS4等平台提供技术服务。PhyreEngine3.5让开发者能使用PS3 Cell架构的高度并行能力、PS4的高级演算能力，以及包括其他世界编辑器和开发工具。&lt;/p&gt;

&lt;p&gt;微软的XNA Game Studio，XNA基于微软的C#语言并使用Visual Studio管理游戏项目资产。&lt;/p&gt;

&lt;p&gt;Unity，支持广泛的平台，它的主要设计目标是容易开发及跨平台游戏开发。因此它提供了容易使用的整合编辑环境，我们可以在此环境中创建及处理游戏世界中的资产及实体，并能快速地在编辑器中浏览游戏运行的样子，同时也提供了直接在目标硬件上运行的功能。不仅如此它还提供了全面的资产调节管道，可以为每个平台独立调节性能与品质以达到两者的平衡。&lt;/p&gt;

&lt;h2 id=&quot;计算机图形技术发展史&quot;&gt;计算机图形技术发展史&lt;/h2&gt;

&lt;p&gt;我把计算机图形技术发展史分为了四个部分，它们分别是，硬件发展史、图形API发展史、图形软件发展史、以及图形技术发展史。&lt;/p&gt;

&lt;h3 id=&quot;图形硬件发展史&quot;&gt;图形硬件发展史&lt;/h3&gt;

&lt;p&gt;图形硬件，最初由SGI公司（Silicon Graphics Inc）开发的IRIS（综合光栅成像系统）受到市场热捧，它使用2MB内存的8 MHz M68000处理器，定制1024x1024帧缓冲器和几何引擎。其最初的市场定位为3D图形显示终端。随着市场策略的推进，SGI公司在电影、电视等领域的发展迅速并且它们的产品受到欢迎。&lt;/p&gt;

&lt;p&gt;接着是Sun微系统公司，它于1982年由安迪·贝托尔斯海姆与其他同门研究生在斯坦福大学成立。贝托尔斯海姆最初将SUN计算机设计为斯坦福大学网络（因此缩写为“SUN”）的个人CAD工作站。它是围绕Motorola 68000处理器设计的，具有Unix操作系统和虚拟内存，并且与SGI一样，具有嵌入式帧缓冲区。后来的发展包括计算机服务器和基于RISC处理器架构和一系列软件产品（如Solaris操作系统和Java平台）构建的工作站。&lt;/p&gt;

&lt;p&gt;GPU英文全称Graphic Processing Unit，20世纪70年代末GPU概念被提出，它的优点是具有高并行结构，所以GPU在处理图形数据和复杂算法方面由比CPU更高的效率。CPU大部分面积为控制器和寄存器，与之相比，GPU拥有更多的ALU（Arithmetic Logic Unit,逻辑运算单元）用于数据处理，这样的结构适合对密集型数据进行并行处理。GPU采用流式并行计算模式，可对每个数据进行独立的并行计算，所谓“对数据进行独立计算”即：流内任意元素的计算不依赖于其他同类型数据，例如，计算一个顶点的世界位置坐标，不依赖于其他顶点的位置，所谓“并行计算”是指“多个数据可以同时被使用，多个数据并行运算的时间和1个数据单独执行的时间是一样的”。所以，在顶点处理程序中，可以同时处理N个顶点数据。目前，线性代数，物理仿真和光线跟踪算法都已经成功的一直到GPU上。&lt;/p&gt;

&lt;p&gt;第一代GPU，NVIDIA于1998年宣布Modern GPU研发成功，这标志着第一代Mondern GPU的诞生，第一代GPU包括NVIDIA TNT2,ATI的Rage和3Dfx的Voodoo。这些GPU可以独立CPU进行像素缓存区的更新，并可以光栅化三角面片以及进行纹理操作，但是缺乏三维顶点的空间坐标变换能力，这意味着“必须依赖与CPU执行顶点坐标变换的计算”。这一时期的GPU功能非常有限，只能用于纹理组合的数学计算或者像素颜色值得计算。&lt;/p&gt;

&lt;p&gt;第二代GPU，NVIDIA于1999年推出一款显示核心代号NV10的geforce 256，率先将硬体T&amp;amp;L整合到显示核中。T&amp;amp;L原先由CPU负责，或者由另一个独立处理机处理。这次整合T&amp;amp;L是一大进步，原因是显视核心从CPU接管了大量工作。硬件T&amp;amp;L引擎带来的效果是3D 模型可以用更多的多边形来描绘，这样就拥有了更加细腻的效果。而对光照来说，CPU不必在计算大量的光照数据，直接通过显卡就能获得更好的效能。同时，这一阶段的GPU对于纹理的操作也扩展到了立方体纹理（Cube Map）。NVIDIA的GeForce MAX,ATI的Radeon 7500等都是在这一阶段研发的。&lt;/p&gt;

&lt;p&gt;第三代GPU，2001年是第三代Modern GPU的发展时期，这一时期研发的GPU提供Vertex Programmability (顶点编程能力)，如GeForce 3，GeForce 4Ti，ATI 的8500等。这些GPU允许应用程序指定一个序列的指令进行顶点操作控制，这是一个具有开创意义的时期，这一时期确立的GPU编程思想一直延续到今天，不但深入到工程领域帮助改善人类日常生活（医疗、地质勘探、游戏、电影），而且开创或延伸了计算机科学的诸多研究领域（人体绘制、光照模拟、人群动画、通用计算等）。同时Direct8和OpenGL都本着与时俱进的精神，提供了支持Vertex Programmability的发展。不过，这一时期的GPU还不支持像素级的编程能力，即Fragment Programmability(片段编程能力)。&lt;/p&gt;

&lt;p&gt;第四代GPU，发展时期从2002年末到2003年。NVIDIA的GeForceFX和ATI Radeon 9700同时在市场的舞台上闪亮登场，这两种GPU都支持顶点编程和片段编程。同时DriectX和OpenGL也扩展了自身的API，用以支持顶点编程和片段编程。自2003年起，可编程图形处理器正式诞生，并且由于DirectX和OpenGL契而不舍的追赶潮流，导致基于图形硬件的编程技术，简称GPU编程，也宣告诞生。&lt;/p&gt;

&lt;h3 id=&quot;图形api发展史&quot;&gt;图形API发展史&lt;/h3&gt;

&lt;p&gt;在 1980 年代，开发可与各种图形硬件配合使用的软件是一项真正的挑战。软件开发人员为每个硬件编写自定义接口和驱动程序。这个代价是昂贵的，将导致工作量成倍增加。到 1990 年代初，Silicon Graphics (SGI) 是工作站 3D 图形领域的领导者。他们的 IRIS GL API成为行业标准，比基于开放标准的 PHIGS 使用更广泛。&lt;/p&gt;

&lt;p&gt;OpenGL、DirectX、Vulkan、Metal等都属于图形API，它的本质是封装好的显卡接口，用来将应用程序与显卡驱动联系起来。有了图形API我们可以仅仅调用它的函数来更改渲染状态、缓存顶点数据、开始绘制等等，而如果没有它，我们就需要直接调用显卡驱动的接口来进行渲染，这会涉及到非常多的硬件接口，例如对寄存器的操作，相当繁琐。一般来说我们都不会希望在渲染的同时还要去关注对硬件的控制。&lt;/p&gt;

&lt;p&gt;目前比较常用的图形API：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;OpenGL&lt;/li&gt;
  &lt;li&gt;OpenGL ES&lt;/li&gt;
  &lt;li&gt;OpenCV&lt;/li&gt;
  &lt;li&gt;DirectX&lt;/li&gt;
  &lt;li&gt;Metal&lt;/li&gt;
  &lt;li&gt;Vunkan&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;OpenGL（Open Graphics Library），是一个跨平台、跨语言的编程图形框架，主要用于windows、mac等PC端。它将计算机资源抽象成一个个OpenGL对象，对这些资源的操作抽象为一个个OpenGL指令。&lt;/p&gt;

&lt;p&gt;OpenGL ES（OpenGL for Embedded Systems），OpenGL三维图像API的子集，主要是针对嵌入式设备的图形处理，比如安卓、iOS等。它在OpenGL之上去除了许多不必要的接口，以及性能较低的接口，并且兼容了GLSL着色器语法。&lt;/p&gt;

&lt;p&gt;OpenCV（Open Source Computer Vision Library），它与 OpenGL是图形API中两个大的分支，类似于iOS中的OC和swift，是两个不同的方向。OpenCV主要用于识别技术，例如人脸识别、身份识别、物体识别等，该API的使用需要与人工智能相结合。目前市面上用的最多的识别三方是 face++，且是收费的，支付宝等大厂的识别功能也是集成的face++。&lt;/p&gt;

&lt;p&gt;DirectX，仅支持Windows平台，由很多API组成，是一个多媒体处理框架，并不是单一的图形API。该框架可以大致分为4类，显示、声音、输入、网络。&lt;/p&gt;

&lt;p&gt;Metal，是苹果为了解决3D渲染问题而推出的框架，是为游戏开发者提供的新的平台技术，并且它将3D渲染图像的性能提高了原来的10倍。苹果系统中的CoreGraphics、CoreAnimation、CoreImage框架在2018年以前是基于OpenGL ES封装的，在2018年以后是基于Metal封装的。&lt;/p&gt;

&lt;p&gt;Vunkan，Vulkan API 最初被 Khronos 称为“下一代 OpenGL 计划”，与 OpenGL、Direct3D 11 和 Metal 相比，Vulkan 旨在提供更高的性能和更平衡的 CPU 和 GPU 使用，并为应用程序提供相当低级别的 API 和并行任务。&lt;/p&gt;

&lt;h3 id=&quot;opengl历史&quot;&gt;OpenGL历史&lt;/h3&gt;

&lt;p&gt;SGI 的竞争对手（包括 Sun Microsystems、Hewlett-Packard 和 IBM）也能够将 PHIGS 标准扩展支持的 3D 硬件推向市场，这迫使 SGI 将 IrisGL 版本作为公共标准开源，称为 OpenGL。&lt;/p&gt;

&lt;p&gt;然而，SGI的许多客户从 IrisGL 到 OpenGL 的转变需要大量投资。此外，IrisGL 具有与 3D 图形无关的 API 函数。例如，它包括一个窗口、键盘和鼠标 API等。其原因是，它是在 X Window System 和 Sun 的 NeWS 之前开发的。而且，由于许可和专利问题，IrisGL的库不适合开放。因此这个转变难度相当大，SGI公司付出了艰辛的努力，得到了市场的认可。&lt;/p&gt;

&lt;p&gt;IrisGL 的限制之一是它只提供对底层硬件支持的功能的访问。如果图形硬件本身不支持某个功能，则应用程序将无法使用它。OpenGL 通过提供硬件不支持的功能的软件实现来克服这个问题，允许应用程序在相对低功耗的系统上使用高级图形。&lt;/p&gt;

&lt;p&gt;OpenGL标准化了对硬件的访问，将硬件接口程序（设备驱动程序）的开发责任推给了硬件制造商，并将窗口功能委托给了底层操作系统。有了这么多不同种类的图形硬件，通过为软件开发人员提供一个用于 3D 软件开发的更高级别的平台，让它们以这种方式使用同一种语言产生了显着的影响。&lt;/p&gt;

&lt;p&gt;OpenGL在1992年由SGI（Silicon Graphics Inc）发布。起初它是作为Iris GL的一个替代品而出现的。Iris GL是SGI的专用图形API，由于它有一些与3D图形无关的API以及一些专利等原因，不适合作为行业的广泛标准，于是SGI编写了OpenGL 1.0规范。在之后，每隔一段时间OpenGL便会出现新的增量更新。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;1992年，SGI发布了OpenGL 1.0。&lt;/li&gt;
  &lt;li&gt;2003年7月，OpenGL ES 1.0发布，它是专为手机、平板电脑、游戏主机等嵌入式系统设计的一个OpenGL API子集。&lt;/li&gt;
  &lt;li&gt;2004年9月，SGI发布了OpenGL 2.0，这其中就有GLSL 1.0（OpenGL Shading Language）的面世，这是一种类C语言，可以用它对固定管线的顶点/片段着色器进行编程。与之相伴的还支持了MRT（Multiple render targets）和NPOT（Non-power-of-two textures）。&lt;/li&gt;
  &lt;li&gt;2006年7月，OpenGL API规范的控制权转交给非盈利组织Khronos团队。&lt;/li&gt;
  &lt;li&gt;2008年8月，OpenGL 3.0发布，支持FBO（Framebuffer Objects）以及VAO（Vertex Array Objects）等多个功能，同时支持了非常多的特性，并且从这版开始OpenGL支持了向后兼容。&lt;/li&gt;
  &lt;li&gt;2009年8月，OpenGL 3.2发布，废弃了固定渲染管线，发布了核心模式（Core Profile），这种模式的灵活性与效率更高，之后的迭代也都是基于这个核心架构，同时版本中增加了新的着色器阶段：几何着色器。&lt;/li&gt;
  &lt;li&gt;2010年3月，OpenGL 3.3和OpenGL 4.0一起发布，带来了新的标准规范，自此之后GLSL的版本号与OpenGL版本号保持一致，即GLSL 3.3/4.0，同时新增了曲面细分着色器。&lt;/li&gt;
  &lt;li&gt;2017年7月，OpenGL 4.6发布，增加了SPIR-V 着色器，SPIR-V 使高级语言前端能够以标准化的中间形式发出程序，供 Vulkan、OpenGL 或 OpenCL 驱动程序摄取。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;vulkan历史&quot;&gt;Vulkan历史&lt;/h3&gt;

&lt;p&gt;Vulkan于Khronos在GDC 2015上首次公开，它也曾被叫做是“Next Generation OpenGL Initiative”(glNext)，旨在对OpenGL和OpenGL ES的一次重新设计，将两者统一到一个API中。&lt;/p&gt;

&lt;p&gt;与OpenGL相同它也是一个跨平台的2D、3D图形API，不同的是它是一个Low Level图形API，即会开放出更多与内存控制相关的接口。&lt;/p&gt;

&lt;p&gt;Vulkan能较好的支持多线程并行（OpenGL4.0+是为单线程设计的），这使得它的操作和维护比OpenGL复杂得多。另外OpenGL使用GLSL来编写着色器，每个显卡商需要为GLSL单独编写编译器来转换为在显卡上运行的机器码。在这点上Vulkan不同，它会先由操作系统把GLSL预编译为一种名为SPIR-V（Standard Portable Intermediate Representation）的中间码，再由显卡商编写Vulkan驱动程序对GPU进行一些特定的优化，加载速度变快了还节省了显卡商的开发及维护成本。这和Direct3D的HLSL预编译很像。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;2016年2月，Vulkan 1.0发布。&lt;/li&gt;
  &lt;li&gt;2018年2月，Khronos发布MoltenVK，可以理解为MoltenVK是用Metal实现了Vulkan的API，这使得macOS和iOS也能支持Vulkan了。虽然绕了趟远路，但从Dota2的表现上来看，依旧比OpenGL高出了50%的帧率。&lt;/li&gt;
  &lt;li&gt;2018年3月，Khronos和GPUOpen公开V-EZ项目，它是“Easy Mode”的缩写，是一个基于Vulkan的中间层，相比于Vulkan它保留了Vulkan的大部分API，提供了更简单的内存管理API，封装了诸多底层细节。旨在开发者更容易上手Vulkan。&lt;/li&gt;
  &lt;li&gt;2018年3月，Vulkan 1.1发布。支持了HLSL语法以及光线追踪。可以更好的模拟DirectX 12。spirv更新至1.3。&lt;/li&gt;
  &lt;li&gt;2020年1月，Vulkan 1.2发布。新增了23项扩展以及更好的支持HLSL。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;direct3d历史&quot;&gt;Direct3D历史&lt;/h3&gt;

&lt;p&gt;Direct3D 是 Microsoft Windows 的图形应用程序编程接口 (API)。作为 DirectX 的一部分，Direct3D 用于在性能很重要的应用程序（例如游戏）中渲染 3D 图形。Direct3D 使用硬件加速（如果它在显卡上可用），允许对整个 3D 渲染管道进行硬件加速，也支持仅部分加速。&lt;/p&gt;

&lt;p&gt;Direct3D 公开了 3D 图形硬件的高级图形功能，包括 Z 缓冲、W 缓冲、模板缓冲、空间抗锯齿、alpha 混合、颜色混合、Mipmapping、纹理混合，剪裁、剔除、大气效果、透视校正纹理映射、可编程 HLSL 着色器等其他图形效果。与其他 DirectX 技术的集成使 Direct3D 能够提供视频映射、2D 覆盖平面中的硬件 3D 渲染甚至精灵等功能，从而在交互式媒体关系中提供 2D 和 3D 图形的使用。&lt;/p&gt;

&lt;p&gt;Direct3D 是一种立即模式图形 API。它为每个显卡的 3D 功能（转换、剪辑、照明、材质、纹理、深度缓冲等）提供了一个低级接口。立即模式呈现三个主要抽象：设备、资源和交换链。&lt;/p&gt;

&lt;p&gt;设备有四种类型，HAL（硬件抽象层）设备（支持硬件加速）、参考设备（模拟不可用的新功能）、软件设备（执行软件渲染）、空设备。&lt;/p&gt;

&lt;p&gt;每个设备都有一个交换链，交换链由一个或多个后台缓冲区组成，渲染发生在后台缓冲区中。&lt;/p&gt;

&lt;p&gt;资源有四个属性，资源类型（表面网格、立方体纹理、索引缓冲、顶点缓冲等）、池（内存管理与存储位置）、格式（资源在内存中的布局）、用法（用标志位方式确定如何使用资源）。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;1996年6月，第一版Direct3D 2.0跟随DirectX 2.0发布，有”Retained mode”和”Immediate mode”。&lt;/li&gt;
  &lt;li&gt;1998年8月，Direct3D 6.0发布，增加了许多新特性，如Multitexture、模板缓冲区等。&lt;/li&gt;
  &lt;li&gt;1999年9月，Direct3D 7.0支持了T&amp;amp;L硬件加速。&lt;/li&gt;
  &lt;li&gt;2000年11月，Direct3D 8.0发布，引进了可编程管道概念，新增了顶点/片段着色器，这个时候Direct3D完全超越了OpenGL。&lt;/li&gt;
  &lt;li&gt;2002年12月，Direct3D 9.0发布，发布了Shader Model 2.0。支持浮点纹理格式、MRT（Multiple render targets），9.0是最后一个支持WindowsXP的版本。&lt;/li&gt;
  &lt;li&gt;2006年11月，Direct3D 10.0发布，仅支持Windows Vista以上操作系统。固定功能管线被淘汰，取而代之的是可编程渲染管线。新增了几何着色器阶段，发布了Shader Model 4.0。&lt;/li&gt;
  &lt;li&gt;2009年7月，Direct3D 11.0与Windows7一块被发布，Direct3D 11.0新增了曲面细分着色器和Computer Shader，支持了多线程处理技术，发布了Shader Model 5.0。&lt;/li&gt;
  &lt;li&gt;2015年1月，Direct3D 12.0包含在Windows10一块被发布，更好支持多核心、多线程的优化，降低GPU处理瓶颈，并提升硬件功耗表现。简化了Direct3D 11中的管道状态对象（Pipeline state objects）。&lt;/li&gt;
  &lt;li&gt;2018年10月，发布了Direct3D 12光线追踪DirectX Raytracing (DXR)的扩展包。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;metal历史&quot;&gt;Metal历史&lt;/h3&gt;

&lt;p&gt;Metal是一款Low Level、Low Overhead的3D图形API，由苹果公司开发，并在2014年6月随iOS8公开。相比于OpenGL，Metal对底层拥有更大的操控性，同时又有着多线程上大量的优化。Metal Shader使用的是Metal Shader Language（MSL），是一种基于C++ 14的语言。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;2014 年 6 月，Metal发布。在由 Apple A7 或更高版本驱动的 iOS 设备上可用，并自 2015 年 6 月 8 日起在运行 OS X El Capitan 的 Mac（2012 型号或更高版本）上可用。&lt;/li&gt;
  &lt;li&gt;2017 年 6 月，Metal第二个版本发布，Metal 2 可以在 Xcode 中实现更高效的分析和调试、加速机器学习、降低 CPU 工作负载、支持 macOS 上的虚拟现实，以及 Apple A11 GPU 的特性。&lt;/li&gt;
  &lt;li&gt;2020 年，Apple 宣布将 Mac 迁移到 Apple 芯片，使用 Apple 芯片的 Mac 将配备 Apple GPU，其功能集结合了以前在 macOS 和 iOS 上可用的功能，并将能够利用为 Apple GPU 的基于图块的延迟渲染 (TBDR) 架构量身定制的功能。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;图形软件发展史&quot;&gt;图形软件发展史&lt;/h3&gt;

&lt;p&gt;20世纪80年代，出现了许多著名的新型商业软件产品，例如Autodesk公司的AutoCAD专注于平面设计，以及它的另一个作品3D Studio专注于3D模型设计，Alias Research公司开发的Alias专注于3D模型设计。Softimage公司开发的Softimage 3D专注于集成建模、动画、渲染并成为业内标准的动画解决方案，副作用软件公司开发的Side Effects软件将程序化建模和运动产品开发成高度紧密集成的2D / 3D动画软件，并融合了多项技术突破。&lt;/p&gt;

&lt;p&gt;到了20世纪90年代，随着硬件技术的提升，图形软件技术也开始蓬勃发展，产生一批顶级的3D图形软件，例如CAD、3MAX、MAYA、SOFTIMAGE、HOUDINI等。借助这些软件，可以大大降低制作模型和动画的成本。&lt;/p&gt;

&lt;p&gt;因此90年开始，图形技术发展进入了重大的改革，不仅在技术上大举突破了各类瓶颈，在图形软件上有了大的进化，而且计算机图形技术开始渗透影视、动画、游戏等各行各业。&lt;/p&gt;

&lt;h2 id=&quot;图形技术发展史&quot;&gt;图形技术发展史&lt;/h2&gt;

&lt;p&gt;群体动物移动行为，群体行为的数学模型首先由Craig Reynolds于1986年在计算机上模拟，并很快在3D动画开始大规模使用。&lt;/p&gt;

&lt;p&gt;动作捕捉，80年代就开始基于计算机的动作捕作为摄影测量分析工具，表演者佩戴标记每个关节附近的标记之间的位置或角度来识别运动。&lt;/p&gt;

&lt;p&gt;运动跟踪，90年代中期，匹配移动（也称为运动跟踪或摄像头跟踪）虽然与动作捕捉有关，但却是一种完全不同的技术。除了使用特殊的相机和传感器来记录主体的运动外，还会将移动作品与预先存在的实时镜头相匹配，并单独使用计算机软件通过多个框架跟踪场景中的特定点，从而允许插入CGI元素以相对于现有材料的正确位置、比例、方向和运动进入镜头。&lt;/p&gt;

&lt;p&gt;虚拟演播室，虚拟演播室是一个工作室，这种虚拟场景在90年代电视节目中很普遍，它可以以无缝方式，实时组合人或其他真实物体，包括计算机生成的环境和物体。它要求3D CGI环境自动锁定，进而精确跟踪角色在实时相机和镜头前的任何移动。这种系统的精髓在于它使用某种形式的相机来跟踪创建一个实时的数据流，同时会使用CGI渲染软件，软件使用相机跟踪数据并生成完美拼接的虚拟合成图像。&lt;/p&gt;

&lt;p&gt;法线贴图，1996年，Krishnamurty和Levoy发明了法线贴图，这是对Jim Blinn 凹凸贴图的改进。&lt;/p&gt;

&lt;p&gt;PBR&amp;amp;卡通材质，这两个技术都是围绕画风的，PBR 指的是”Physically Based Rendering”， 简化了物理材质的计算流程，用接近物理正确的效果来渲染材质表面，材质包括绝缘体和金属材质两种。卡通渲染（英语：Toon Shading）是一种非真实感绘制（NPR），卡通渲染又称赛璐珞渲染（Cel-shading），旨在使电脑生成的图像呈现出手绘般的效果。为了使图像可以与漫画或者卡通达到形似的效果，专业人员通常使用卡通渲染着色器进行处理。&lt;/p&gt;

&lt;p&gt;高动态光照（HDRR，High-Dynamic Range rendering），人在黑暗的地方，为了看清楚对象，瞳孔会放大，以吸收更多光线；当突然走到光亮的地方，瞳孔来不及收缩，所以眯起眼睛，保护视网膜上的视神经。而电脑无法判断光线明暗，唯有靠HDRR技术模拟这效果——人眼自动适应光线变化的能力。方法是快速将光线渲染得非常光亮，然后将亮度逐渐降低。HDRR的最终效果是亮处的效果是鲜亮，而黑暗处的效果是能分辨物体的轮廓和深度，而不是以往的一团黑。&lt;/p&gt;

&lt;p&gt;体积光，可以让用户看到光束效果，例如在一个画面中用户能看到太阳光束射入敞开的窗户，这就是典型的体体积光技术体现。&lt;/p&gt;

&lt;p&gt;early-z-test，发生在顶点着色器后像素着色器之前的深度测试，原本深度测试在填充率、光照、纹理或像素着色器成为主要瓶颈的情况下已经成为了一个很好的优化，early-z-test这种技术将深度测试更进一步，提前到顶点着色器之后去完成，它节省了片元着色器的计算，但前提是片元着色器中不会有alpha混合和discard操作。&lt;/p&gt;

&lt;p&gt;光线追踪（Ray Tracing），是三维计算机图形学中的特殊渲染算法，追踪光线从来源开始照射到物体上，再由物体反射的光线“路径”，由于完整运算所有路径十分消耗运算资源，因此现有光线追踪技术仅运算“目所能及”的光线路径，由于是从玩家视角开始进行运算，有时被误解为光线追踪是追踪“从眼睛发出的光线”。通过这样一项技术生成编排好的场景的数学模型显现出来。这样得到的结果类似于光线投射与扫描线渲染方法的结果，但是这种方法有更好的光学效果，例如对于反射与折射有更准确的模拟效果，并且效率非常高，所以当追求高质量的效果时经常使用这种方法。 在物理学中，光线追迹可以用来计算光束在介质中传播的情况。在介质中传播时，光束可能会被介质吸收，改变传播方向或者射出介质表面等。我们通过计算理想化的窄光束（光线）通过介质中的情形来解决这种复杂的情况。 在实际应用中，可以将各种电磁波或者微小粒子看成理想化的窄波束（即光线），基于这种假设，人们利用光线追迹来计算光线在介质中传播的情况。光线追迹方法首先计算一条光线在被介质吸收，或者改变方向前，光线在介质中传播的距离，方向以及到达的新位置，然后从这个新的位置产生出一条新的光线，使用同样的处理方法，最终计算出一个完整的光线在介质中传播的路径。&lt;/p&gt;

&lt;p&gt;其他等等渲染技术非常多，这里点到为止不再延伸开来。&lt;/p&gt;

&lt;h2 id=&quot;游戏制作团队&quot;&gt;游戏制作团队&lt;/h2&gt;

&lt;p&gt;这里简单介绍下游戏制作团队，虽然大多数人都知道这些知识，但不妨碍我们回顾与总结。&lt;/p&gt;

&lt;p&gt;艺术家：
概念艺术家、三维建模师、纹理艺术家、灯光师、动画师、动捕演员、音效师、配音演员、作曲家等&lt;/p&gt;

&lt;p&gt;产品设计：
游戏设计师、数值设计师、关卡设计师、系统设计师、AI设计师、剧情设计师、操作设计师、技能设计师、制作人等&lt;/p&gt;

&lt;p&gt;软件工程师：
引擎工程师、渲染工程师、游戏系统工程师、AI工程师、网络工程师、构建工程师、工具开发工程师、架构工程师、效能工程师、中间件工程师、安全工程师、测试工程师等&lt;/p&gt;

&lt;h2 id=&quot;运行时引擎架构概述&quot;&gt;运行时引擎架构概述&lt;/h2&gt;

&lt;p&gt;游戏引擎通常由工具套件和运行时组件两部分构成。这里我们主要来说说运行时组件部分。&lt;/p&gt;

&lt;p&gt;与所有软件系统一样，游戏引擎也是由软件层构建的，即我们通常说的，上层依赖下层，下层不依赖上层，每层中各模块之间不相互依赖，这样的低耦合、高重用度架构在大型软件系统架构中是非常关键的设计。&lt;/p&gt;

&lt;p&gt;《游戏引擎架构》一书中对整体架构做了介绍，但我认为内容过于粗浅，也有点过时了，于是我自己重新画了架构图。&lt;/p&gt;

&lt;p&gt;我把整个运行时引擎架构分为十三个层级，每个层级都是只依赖下层，这十三个层级分别为：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;目标硬件层&lt;/li&gt;
  &lt;li&gt;设备驱动层&lt;/li&gt;
  &lt;li&gt;操作系统层&lt;/li&gt;
  &lt;li&gt;平台独立层&lt;/li&gt;
  &lt;li&gt;第三方中间件&lt;/li&gt;
  &lt;li&gt;核心库&lt;/li&gt;
  &lt;li&gt;资产管理层&lt;/li&gt;
  &lt;li&gt;底层渲染器&lt;/li&gt;
  &lt;li&gt;引擎核心层&lt;/li&gt;
  &lt;li&gt;引擎应用层&lt;/li&gt;
  &lt;li&gt;业务基础层&lt;/li&gt;
  &lt;li&gt;游戏框架层&lt;/li&gt;
  &lt;li&gt;游戏业务层&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;下面来简单解释一下每个层级的拆解理念：&lt;/p&gt;

&lt;p&gt;目标硬件层，代表用来执行游戏的主机硬件。&lt;/p&gt;

&lt;p&gt;设备驱动层，是操作系统或硬件商提供的最低阶软件组件。负责管理硬件资源、也隔离了操作系统及上层引擎。&lt;/p&gt;

&lt;p&gt;操作系统层，管理协调整台计算机上多个程序的执行工作，进程通过调用操作系统的接口来访问操作系统。&lt;/p&gt;

&lt;p&gt;平台独立层，包装了常用的库和操作系统接口，供所有平台使用。&lt;/p&gt;

&lt;p&gt;第三方中间件，提供了独立的解决方案，简化开发流程，缩短开发时间。包括标准库、图形接口等。&lt;/p&gt;

&lt;p&gt;核心库，是由许许多多独立的软件工具库组成。这些库常常被引擎上层频繁使用，同时也是非常实用的软件工具。&lt;/p&gt;

&lt;p&gt;资产管理层，提供资源增删改查的完整解决方案，通常会提供一组统一的接口，供引擎和业务去访问任意的游戏资源。&lt;/p&gt;

&lt;p&gt;底层渲染器，提供最原始的渲染功能，根据应用层的数据调用图形API进行渲染。它通常会经过封装，从业务渲染提交的过程中拆解出来。&lt;/p&gt;

&lt;p&gt;引擎核心层，为应用层提供了最核心的功能，包括音频、动画、物理、剔除与合批、手柄设备接口等。&lt;/p&gt;

&lt;p&gt;引擎应用层，在核心层之上建立起来的引擎业务，包括性能剖析工具、UI核心框架、粒子系统、后处理系统等。&lt;/p&gt;

&lt;p&gt;业务基础层，引擎为游戏框架搭建而做的基础功能，是提供给实现游戏逻辑的重要功能与接口。&lt;/p&gt;

&lt;p&gt;游戏框架层，通过调用业务基础层来搭建的游戏框架，是游戏实现自身逻辑的框架，也是游戏逻辑的核心。&lt;/p&gt;

&lt;p&gt;游戏业务层，是实现具体游戏业务的逻辑，通过调用游戏框架来实现具体的业务逻辑。&lt;/p&gt;

&lt;h3 id=&quot;游戏引擎架构图&quot;&gt;游戏引擎架构图&lt;/h3&gt;

&lt;p&gt;在拆解了整个引擎的架构时，由于模块太多，因此只列了一些主要的模块，保持了基本架构的完整性：&lt;/p&gt;

&lt;p&gt;架构图中的重要模块，将在后面的文章中做详细的分析和解剖。&lt;/p&gt;

&lt;h3 id=&quot;参考资料&quot;&gt;参考资料：&lt;/h3&gt;

&lt;p&gt;《游戏引擎架构》 译者：叶劲峰&lt;/p&gt;

&lt;p&gt;《3D游戏引擎发展史》&lt;/p&gt;

&lt;p&gt;https://www.bilibili.com/read/cv6499569/&lt;/p&gt;

&lt;p&gt;《游戏引擎wiki》&lt;/p&gt;

&lt;p&gt;https://zh.wikipedia.org/wiki/%E6%B8%B8%E6%88%8F%E5%BC%95%E6%93%8E&lt;/p&gt;

&lt;p&gt;《你不能不知道的计算机图形技术发展史》1、2&lt;/p&gt;

&lt;p&gt;https://www.bilibili.com/read/cv196689&lt;/p&gt;

&lt;p&gt;https://www.bilibili.com/read/cv221830&lt;/p&gt;

&lt;p&gt;《了解图形接口》&lt;/p&gt;

&lt;p&gt;https://www.jianshu.com/p/9dea5f163837&lt;/p&gt;

&lt;p&gt;《图形API发展历史》&lt;/p&gt;

&lt;p&gt;https://blog.csdn.net/ce1061183126/article/details/106159169&lt;/p&gt;

&lt;p&gt;《卡通渲染 Wiki》&lt;/p&gt;

&lt;p&gt;https://zh.wikipedia.org/wiki/%E5%8D%A1%E9%80%9A%E6%B8%B2%E6%9F%93&lt;/p&gt;

&lt;p&gt;《高动态光照渲染》&lt;/p&gt;

&lt;p&gt;https://zh.wikipedia.org/wiki/%E9%AB%98%E5%8A%A8%E6%80%81%E5%85%89%E7%85%A7%E6%B8%B2%E6%9F%93&lt;/p&gt;

&lt;p&gt;《Metal Wiki》&lt;/p&gt;

&lt;p&gt;https://en.wikipedia.org/wiki/Metal_(API)#History&lt;/p&gt;

&lt;p&gt;《Direct3D Wiki》&lt;/p&gt;

&lt;p&gt;https://en.wikipedia.org/wiki/Direct3D#Direct3D_12&lt;/p&gt;

&lt;p&gt;《OpenGL Wiki》&lt;/p&gt;

&lt;p&gt;https://en.wikipedia.org/wiki/OpenGL&lt;/p&gt;

&lt;p&gt;《Vulkan Wiki》&lt;/p&gt;

&lt;p&gt;https://en.wikipedia.org/wiki/Vulkan_(API)&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484890&amp;amp;idx=1&amp;amp;sn=1d319050c82fed232d22f1f3dfce0cd3&amp;amp;chksm=fc2260ddcb55e9cbbda832e90def1e8e7440b7ffc85c60264be83d9c6bc62162c5b05839839b&amp;amp;token=834275592&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>读书笔记(四十一) 《心流》#2 为何要培养独立意识</title>
   <link href="http://www.luzexi.com/2021/08/22/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B041"/>
   <updated>2021-08-22T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/08/22/读书笔记41</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484880&amp;amp;idx=1&amp;amp;sn=869424bd26f3c3fd8524ede6ced2d7a0&amp;amp;chksm=fc2260d7cb55e9c16f0a6d8c314dcb211bd43e7e6143d5f2dceee208ded17bb9bcd5d662854f&amp;amp;token=1516544318&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;背景&quot;&gt;背景：&lt;/h2&gt;

&lt;p&gt;在2020年初就买了《心流》，直到2021年1月才读完第一遍，读完的那一刻，我知道我又打开了一个新世界。&lt;/p&gt;

&lt;p&gt;不得不承认就是这本书让我的专注力提高了整整一个级别，这是最让我非常兴奋的地方。而我只是读了第一遍，吸收不到10%的结果，可想而知它的魔力有多么强大。&lt;/p&gt;

&lt;p&gt;于是决定好好回顾总结一下整本书，便开启了第二遍、第三遍的读书之旅。&lt;/p&gt;

&lt;p&gt;读此书的起源是由于我在平时工作、交流、学习时，常常难以集中注意力，因此带着好奇心阅读了这本书。&lt;/p&gt;

&lt;p&gt;我猜大家应该也和我一样，常常为自己难以击中注意力而烦恼，其实我们都希望自己在专注做一件事情的时候能够拥有更高的效率。&lt;/p&gt;

&lt;p&gt;本书的核心是心流的最优体验，简单来说就是“如何让你高兴地专注”。&lt;/p&gt;

&lt;p&gt;作者想表达的是，当你的心流会的体验最佳时，也是学习效率最高的时候，同时也是你最幸福的时刻。&lt;/p&gt;

&lt;p&gt;书本中讲到了关于学习、工作、运动、社交等各方面的心流原理和技巧，我作为知识的搬运工，做了一些精炼和总结的工作。&lt;/p&gt;

&lt;h2 id=&quot;正文&quot;&gt;正文：&lt;/h2&gt;

&lt;p&gt;简单回顾下前文，前文我们说，结果其实并不重要，过程才是我们幸福的来源，专注就是注重过程保持幸福的一种方法。我们常常心灵处于混乱状态，专注能让我们保持有序的心流。人生要的不是最后的终点，而是每时每刻点点滴滴成长的过程，成长本身就是我们的目的。&lt;/p&gt;

&lt;h2 id=&quot;为何我们在为拥有幸福而追求幸福&quot;&gt;为何我们在为拥有幸福而追求幸福&lt;/h2&gt;

&lt;p&gt;作者说我们常常为拥有幸福而追求幸福，同时我们也在追求其他目标，例如健康、美貌、金钱、权利，追求这些无非也是因为我们以为拥有这些就能得到幸福。&lt;/p&gt;

&lt;p&gt;维克多在《活出意义来》序言里写道：&lt;/p&gt;

&lt;h3 id=&quot;不要以成功为目标你越是对它念念不忘就越有可能错过它&quot;&gt;不要以成功为目标，你越是对它念念不忘，就越有可能错过它。&lt;/h3&gt;

&lt;h3 id=&quot;因为成功如同幸福不是追求就能得到它必须因缘际会是一个人全心全意投入并把自己置之度外时意外获得的副产品&quot;&gt;因为成功如同幸福，不是追求就能得到；它必须因缘际会，是一个人全心全意投入并把自己置之度外时，意外获得的副产品。&lt;/h3&gt;

&lt;p&gt;经研究表明，运动、游戏、音乐、绘画、嗜好等都是比较容易产生心流的活动，但我们不能光靠游戏和艺术来改善我们的生活啊，所以我们要学会如何将枯燥的工作转化为产生心流的活动。&lt;/p&gt;

&lt;p&gt;我们大多数人几乎一生时间都花在工作和社交上，因此学习把工作转化为产生心流的活动，以及设法与父母、配偶、儿女、朋友相处得更愉快，是格外重要的事。&lt;/p&gt;

&lt;h2 id=&quot;如何反转苦难的生活&quot;&gt;如何反转苦难的生活&lt;/h2&gt;

&lt;p&gt;不仅如此，生活还不断给我们各种挑战和苦难，这阻碍了我们享受幸福享受人生。&lt;/p&gt;

&lt;p&gt;那么如何不为苦难所阻，继续享受人生呢？&lt;/p&gt;

&lt;p&gt;人生的悲剧在所难免，即使是幸运儿也难免遭逢压力，但遭受这些打击未必就是与幸福绝缘。&lt;/p&gt;

&lt;h3 id=&quot;人在压力下的反应往往决定他们是否能转祸为福否则只能徒然地受苦受难&quot;&gt;人在压力下的反应，往往决定他们是否能转祸为福，否则只能徒然地受苦受难。&lt;/h3&gt;

&lt;h3 id=&quot;如果我们能做到在困难和压力下转祸为福就能对人生掌控自如使生命丰富璀璨&quot;&gt;如果我们能做到在困难和压力下转祸为福，就能对人生掌控自如，使生命丰富璀璨。&lt;/h3&gt;

&lt;h3 id=&quot;此时是否苗条是否富裕是否掌权都已无关紧要甚至最单调的体验也会变得趣味盎然&quot;&gt;此时，是否苗条、是否富裕、是否掌权都已无关紧要，甚至最单调的体验也会变得趣味盎然。&lt;/h3&gt;

&lt;h2 id=&quot;幸福之所以难求是因为我们的贪得无厌的欲望难以填满&quot;&gt;幸福之所以难求是因为我们的贪得无厌的欲望难以填满&lt;/h2&gt;

&lt;h3 id=&quot;我们说一个人如果不靠任何支持试图仅凭自己的力量去追求幸福很可能他会从极致的生理快乐和社会公认的最具吸引力的事物开始着手&quot;&gt;我们说一个人如果不靠任何支持，试图仅凭自己的力量去追求幸福，很可能他会从极致的生理快乐和社会公认的最具吸引力的事物开始着手。&lt;/h3&gt;

&lt;h3 id=&quot;因此财富权势性就成为很多人奋斗的主要目标然而大多数人并不明白这些并不能改善我们生活的品质&quot;&gt;因此财富、权势、性就成为很多人奋斗的主要目标，然而大多数人并不明白这些并不能改善我们生活的品质。&lt;/h3&gt;

&lt;p&gt;那该怎么办呢？作者说唯有直接控制体验感受，从我们所做的每一件事、每一个此时此刻中汲取快乐，才能克服欲望的障碍，从而得到满足。&lt;/p&gt;

&lt;p&gt;各种证据表明，大多数人会陷入期望值不断升高的恶性循环中无力自拔，但也有不少人能从中逃脱出来。&lt;/p&gt;

&lt;h3 id=&quot;这些人或许物质条件不够优越却仍然能改善生活品质他们不但自己知足常乐也常能使周遭的人生活得比其他人更快乐&quot;&gt;这些人或许物质条件不够优越，却仍然能改善生活品质，他们不但自己知足常乐，也常能使周遭的人生活得比其他人更快乐。&lt;/h3&gt;

&lt;h3 id=&quot;这种人充满活力愿意接纳各式各样的经历活到老学到老而且对别人及周遭的环境有强烈的责任感&quot;&gt;这种人充满活力，愿意接纳各式各样的经历，活到老学到老，而且对别人及周遭的环境有强烈的责任感。&lt;/h3&gt;

&lt;p&gt;那么到底有多少人热爱自己的工作，满意自己的运气，不追悔过去，对前途满怀信心呢？答案是：极少的。&lt;/p&gt;

&lt;p&gt;这种普遍的“病态”并非直接由外界因素引起。不满的根源存乎于一心，自己的问题唯有依靠自己才能解决。&lt;/p&gt;

&lt;p&gt;我们从小到大，在渐渐长大的过程中，从满怀希望的无知少年，长成冷静沉稳的大人。&lt;/p&gt;

&lt;p&gt;童年或许令人痛苦，青春期或许令人困惑，但对大多数人而言，痛苦与困惑的背后，至少还有个长大后一切会好转的希望。&lt;/p&gt;

&lt;p&gt;然而，长大后，各种老化的迹象明明白白地告诉我们“你的时间快到了，准备动身吧”，但很少人在这时候已经准备妥当。&lt;/p&gt;

&lt;p&gt;人们会反问，“等一下，不可能是我吧，我还没开始生活呢，我该赚的那些钱在哪儿，我该享受的那些好时光在哪儿”&lt;/p&gt;

&lt;p&gt;这番觉悟使我们有种受骗上当的感觉，似乎是慈悲的命运为我们安排好的一切。&lt;/p&gt;

&lt;p&gt;当这番觉悟来临时，每个人都会以不同的方式面对这个问题。有些人选择继续奋斗不息，金钱、名誉、权利，不得到誓不罢休。有些人则选择对症下药，节食、健身、跳舞、整容、上提高自信的课、参加权贵人士的午餐聚会等等。&lt;/p&gt;

&lt;p&gt;但不久人们就会发现，这些零星的解决方案根本发挥不了作用。&lt;/p&gt;

&lt;p&gt;无论下多少功夫，老化的定律不会因此而改写。&lt;/p&gt;

&lt;p&gt;提升了自信，无形中却疏远了朋友。&lt;/p&gt;

&lt;p&gt;花太多时间结交新朋友，又疏忽了配偶和家人。&lt;/p&gt;

&lt;p&gt;人们会发现，堤坝太多缺口，濒临溃决，根本来不及抢救。&lt;/p&gt;

&lt;p&gt;在无法同时满足太多要求的挫折之下，有些人干脆投降认输，躲进自己的小天地。&lt;/p&gt;

&lt;p&gt;他们可能培养一种高雅的嗜好，如搜集抽象画或陶瓷人像，甚至也会沉溺于酒精或麻醉品构筑的迷幻堡垒里。&lt;/p&gt;

&lt;p&gt;这些异国情调的娱乐和所费不菲的消遣活动，纵然能使人暂时忘记根本的疑问，却不能提供答案。&lt;/p&gt;

&lt;h2 id=&quot;我们是如何沦为社会控制的奴隶&quot;&gt;我们是如何沦为社会控制的奴隶&lt;/h2&gt;

&lt;h3 id=&quot;心理学家指出文明就是建立在压抑个人欲望基础上的社会成员不论乐意与否都被迫接受既定的习惯与技能否则就不可能维持社会秩序和复杂的分工制度&quot;&gt;心理学家指出，文明就是建立在压抑个人欲望基础上的。社会成员不论乐意与否，都被迫接受既定的习惯与技能，否则就不可能维持社会秩序和复杂的分工制度。&lt;/h3&gt;

&lt;p&gt;所以对于我们，个人的社会化是必然的。社会化的真谛在于使个人依赖社会的控制，并对赏罚有既定的反应。社会化的最高境界就是使每个人都完全认同社会秩序，根本不想触犯任何规则。&lt;/p&gt;

&lt;h3 id=&quot;我们应该了解到寻求快乐是基因为物种延续而设的一种即时反射其目的并非个人利益例如美食的快乐性爱的快乐娱乐的快乐都其实是人类基因的布局&quot;&gt;我们应该了解到，“寻求快乐”是基因为物种延续而设的一种即时反射，其目的并非个人利益。例如，美食的快乐、性爱的快乐、娱乐的快乐都其实是人类基因的布局。&lt;/h3&gt;

&lt;p&gt;跟随基因的反应，享受自然的乐趣，并没有什么不好，但我们应该认清事实真相，在必要的时候，按照自己的优先顺序，做自己的主人。&lt;/p&gt;

&lt;h3 id=&quot;彻底社会化的人只追求周遭他人认定的应该期望的东西这些东西往往都是与天性密切结合的欲望&quot;&gt;彻底社会化的人，只追求周遭他人认定的、应该期望的东西，这些东西往往都是与天性密切结合的欲望。&lt;/h3&gt;

&lt;h3 id=&quot;他可能会有机会经历到许多难能可贵的事但由于这些事与他们的欲望不符而被他们完全忽略掉&quot;&gt;他可能会有机会经历到许多难能可贵的事，但由于这些事与他们的欲望不符而被他们完全忽略掉。&lt;/h3&gt;

&lt;h3 id=&quot;其实他们在意的并非他们现在拥有什么而是满足别人的要求后能获得什么&quot;&gt;其实他们在意的并非他们现在拥有什么，而是满足别人的要求后能获得什么。&lt;/h3&gt;

&lt;h3 id=&quot;这种沦为社会控制的奴隶人只知道周而复始地追逐一到手就化为泡影的奖赏他们的局限性太强被社会和他人牢牢地控制&quot;&gt;这种沦为社会控制的奴隶人，只知道周而复始地追逐“一到手”就化为泡影的奖赏，他们的局限性太强，被社会和他人牢牢地控制。&lt;/h3&gt;

&lt;p&gt;虽然在这个复杂的社会求生，绝对有必要实现外在的目标而去暂时牺牲一些自我意识，但不必因此而成为傀儡。&lt;/p&gt;

&lt;h3 id=&quot;为了不让自己成为傀儡最好的方法是不以社会的奖赏为念试着以自己所能控制的奖赏取而代之&quot;&gt;为了不让自己成为傀儡，最好的方法是不以社会的奖赏为念，试着以自己所能控制的奖赏取而代之。&lt;/h3&gt;

&lt;h3 id=&quot;当然这也并不表示我们要完全放弃社会认可的每项目标而是在社会用以利诱我们的目标之外另行建立自己的目标&quot;&gt;当然这也并不表示，我们要完全放弃社会认可的每项目标，而是在社会用以利诱我们的目标之外，另行建立自己的目标。&lt;/h3&gt;

&lt;p&gt;然而，从社会制约下解放自我，最重要的步骤就是，时时刻刻发掘每一事件中的回馈。&lt;/p&gt;

&lt;p&gt;如果我们学会在不断向前推进的体验中找到快乐与意义，社会制约的重担就会从肩上自动滑落。&lt;/p&gt;

&lt;p&gt;当奖赏不再受外在力量管制时，生命的权力就回到了个人手中。&lt;/p&gt;

&lt;p&gt;社会根据生物倾向而设计的“刺激–反应”模式，就是受外界所控制的。&lt;/p&gt;

&lt;p&gt;无论是迷人的广告使我们对产品垂涎不已，还是老板皱一下眉头就使我们整天提心吊胆，都代表着我们没有决定体验内涵的自由。&lt;/p&gt;

&lt;h3 id=&quot;人们害怕的其实是自己对事物的看法而非事物本身外界事物令你痛苦并不是因为它们打扰了你而是归因于你对它们的判断而你有能力立刻消除这种判断&quot;&gt;人们害怕的其实是自己对事物的看法，而非事物本身。外界事物令你痛苦并不是因为它们打扰了你，而是归因于你对它们的判断，而你有能力立刻消除这种判断。&lt;/h3&gt;

&lt;h2 id=&quot;培养独立意识有多难&quot;&gt;培养独立意识有多难&lt;/h2&gt;

&lt;p&gt;在亚洲有很多控制意识的技巧，能使人达到高层次的满足感。&lt;/p&gt;

&lt;p&gt;例如，印度瑜伽、佛教禅宗、宗教祷告等，目的都在于摆脱混沌的威胁和生理冲动的制约，从而释放内在生命。&lt;/p&gt;

&lt;p&gt;但各派的哲学都依赖于当时所创生的环境，这些额外的成分若不能从基本要素中剔除，那么通往自由的路上就不免要长出许多莠草。形式一旦逾越内容，追寻者就只得重回起点。&lt;/p&gt;

&lt;h3 id=&quot;所以想要培养我们的独立意识对意识的控制就不能予以制度化一旦成为社会规则与标准它就失去了原有的作用&quot;&gt;所以想要培养我们的独立意识，对意识的控制就不能予以制度化，一旦成为社会规则与标准，它就失去了原有的作用。&lt;/h3&gt;

&lt;h3 id=&quot;在时代更迭频繁的时代每隔几年培养独立意识所需的条件就会变化我们必须重新加以考虑与组合&quot;&gt;在时代更迭频繁的时代，每隔几年，培养独立意识所需的条件就会变化，我们必须重新加以考虑与组合。&lt;/h3&gt;

&lt;p&gt;既然掌握人生是永远的中心议题，现代知识对此有什么看法呢？一个人又如何消除焦虑与恐惧，摆脱对社会奖赏的患得患失之心呢？&lt;/p&gt;

&lt;h3 id=&quot;作者给出了简短的回答控制意识才能控制体验的品质任何在这方面的进步都能够提升生活的品质使快乐更快乐也更有意义&quot;&gt;作者给出了简短的回答，控制意识才能控制体验的品质，任何在这方面的进步都能够提升生活的品质，使快乐更快乐、也更有意义。&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484880&amp;amp;idx=1&amp;amp;sn=869424bd26f3c3fd8524ede6ced2d7a0&amp;amp;chksm=fc2260d7cb55e9c16f0a6d8c314dcb211bd43e7e6143d5f2dceee208ded17bb9bcd5d662854f&amp;amp;token=1516544318&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>给女儿写信(十五) 如何表达</title>
   <link href="http://www.luzexi.com/2021/08/19/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A115"/>
   <updated>2021-08-19T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/08/19/给女儿的信15</id>
   <content type="html">
&lt;hr /&gt;

&lt;p&gt;导读：喜欢跟孩子们一起，可惜一直在外地工作，因此我给孩子写点信并录成视频，跟孩子聊聊自己的故事以及最近的感受&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;hi Sharon、Anne，爸爸喜欢你们哟。&lt;/p&gt;

&lt;p&gt;爸爸最近在学习如何表达。爸爸知道表达很重要，但由于爸爸前些年没有注意到自己表达上的问题，所以表达到现在还是很烂。&lt;/p&gt;

&lt;p&gt;爸爸最近在看有关表达方面的书，看了后发现自己在平时说话时有很多问题，但是以前一直没有察觉到。&lt;/p&gt;

&lt;p&gt;爸爸也在不断努力改进自己说话的能力，让自己说话更清楚，说出来的话让别人更容易懂。&lt;/p&gt;

&lt;p&gt;爸爸想让大家与我交流时更舒服，听的也更清楚。&lt;/p&gt;

&lt;p&gt;此外，爸爸也在看演讲类的书，爸爸想在表达上更进一步。&lt;/p&gt;

&lt;p&gt;爸爸参加了一个演讲培训的课程，在这个课程上，大家都要上去演讲，通过不断的上台练习演讲，爸爸现在进步了一些，但离真正的好的演讲还差很远，爸爸还要多努力，多学习，多上台。&lt;/p&gt;

&lt;p&gt;Sharon和Anne，我们一起加油。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(四十) 《非暴力沟通》#3 - 沟通中如何爱自己</title>
   <link href="http://www.luzexi.com/2021/08/17/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B040"/>
   <updated>2021-08-17T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/08/17/读书笔记40</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484873&amp;amp;idx=1&amp;amp;sn=b7d9986ffd0f259ec3dc25bc50f528a5&amp;amp;chksm=fc2260cecb55e9d8dec522275320a2211e2ebb4f2025ff415c6e730c66d0ce07b9aaa0892adb&amp;amp;token=1516544318&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;背景&quot;&gt;背景：&lt;/h2&gt;

&lt;p&gt;表达能力和沟通技巧有诸多问题，工作和生活上出现了比较大的瓶颈，于是看了些表达和沟通方面的书，其中三本最为经典《非暴力沟通》、《关键对话》、《说话的艺术》。细细品读了这三本书并且收获满满，分享出来希望大家一同进步。&lt;/p&gt;

&lt;p&gt;看完书后反观自己以前的沟通技巧和表达方式，明白了以前自己在这些方面都有比较大的认知错误，回想起来可以用愚蠢来形容。&lt;/p&gt;

&lt;p&gt;既然有这么好的沟通技巧和表达方式，我希望将这些方法和技巧实实在在的落地到实际生活中，让生活、工作、学习更美好。&lt;/p&gt;

&lt;p&gt;下面我就以《非暴力沟通》这本书为主线，来阐述我们在沟通过程中遇到的一些问题和对应的解决方法。&lt;/p&gt;

&lt;h2 id=&quot;正文&quot;&gt;正文：&lt;/h2&gt;

&lt;p&gt;简单回顾下前文，前面我们说在提出的请求要明确，请求越详细越具体，才能让别人更清楚的了解我们的想法。&lt;/p&gt;

&lt;p&gt;同时说了，倾听很重要，它能治愈身心也能预防潜在的暴力，倾听后向对方做出良好的反馈使得对话能够保持顺畅温和。&lt;/p&gt;

&lt;p&gt;这节也是非暴力沟通学习的最后一篇，主题是在沟通时如何爱自己。&lt;/p&gt;

&lt;h3 id=&quot;爱自己而不是自责&quot;&gt;爱自己而不是自责&lt;/h3&gt;

&lt;p&gt;非暴力沟通最重要的应用在于 - 爱护自己&lt;/p&gt;

&lt;p&gt;我们常常对自己做出负面的评价，严厉的苛责自己。&lt;/p&gt;

&lt;p&gt;但负面的自我评价使我们看不到生命的美，让我们忘记自己是“特殊的生命”，而把自己当做一个工具。&lt;/p&gt;

&lt;h3 id=&quot;如果我们习惯于将自己视为工具充满各种缺陷的工具那么自我憎恨是自然的&quot;&gt;如果我们习惯于将自己视为工具，充满各种缺陷的工具，那么自我憎恨是自然的。&lt;/h3&gt;

&lt;p&gt;其实我们都希望自己所做的任何事情都是有益的，良好的自我评价的方式就要有助于学习，使我们的选择符合生命的需要。&lt;/p&gt;

&lt;h3 id=&quot;然而不幸的是我们的自我评价方式往往是负面这直接导致了自我憎恨而无助于学习&quot;&gt;然而不幸的是，我们的自我评价方式往往是负面，这直接导致了自我憎恨，而无助于学习。&lt;/h3&gt;

&lt;p&gt;许多人会陷入自责，他们认为自己所做的事情是错的，并且应当为此感到痛苦。&lt;/p&gt;

&lt;p&gt;虽然我们犯的错误揭示了我们的局限性从而引导我们成长，但陷入自责使得我们无法从错误中获益。&lt;/p&gt;

&lt;p&gt;作者认为我们的改变最好要出于对生命的爱，而不是出于羞愧或内疚这些具有负面影响的心理。因为羞愧是自我憎恨的一种形式，出于羞愧的行为不是自由快乐的行为。即使我们试图更加友善和体贴，一旦人们意识到我们行为背后的羞愧或内疚，他们对这些行为的欣赏也就比不上那些只是出于爱的行为了。&lt;/p&gt;

&lt;p&gt;我们的话语中常常带有一些容易引起羞愧和内疚的词，例如，“应该”，“不得不”，“必须”，举个简单的例子：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;我应该早点知道&lt;/li&gt;
  &lt;li&gt;我真的应该戒烟了&lt;/li&gt;
  &lt;li&gt;我必须加强锻炼&lt;/li&gt;
  &lt;li&gt;我真的受不了自己，我必须改改&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;一旦我们顺从了这些羞愧和内疚的命令生活也就会失去了乐趣如果我们经常责备自己强迫自己也使我们更像工具而不像人从而引起自我的憎恨&quot;&gt;一旦我们顺从了这些羞愧和内疚的命令，生活也就会失去了乐趣。如果我们经常责备自己、强迫自己，也使我们更像工具而不像人，从而引起自我的憎恨。&lt;/h3&gt;

&lt;p&gt;因此当发现自己在做错误的事时，其实我们的挑战是如何对需要和价值观保持清醒的认识，这促进我们转变。&lt;/p&gt;

&lt;p&gt;其中专注于需要能帮助我们从自我评价中获益，那么怎样才算是“专注于需要”呢？下面作者为我们做了解释。&lt;/p&gt;

&lt;h3 id=&quot;我们应该尽可能的直面人生的苦难在遇到挫折时充分体会人生的悲哀和内心的渴望感到遗憾是难免的无须责备自己它能帮助我们从经历中学习&quot;&gt;我们应该尽可能的直面人生的苦难：在遇到挫折时，充分体会人生的悲哀和内心的渴望。感到遗憾是难免的，无须责备自己，它能帮助我们从经历中学习。&lt;/h3&gt;

&lt;h3 id=&quot;专注于需要&quot;&gt;专注于需要&lt;/h3&gt;

&lt;h3 id=&quot;如果我们能专注于尚未满足的需要我们就能从中获益并考虑如何满足它&quot;&gt;如果我们能专注于尚未满足的需要，我们就能从中获益并考虑如何满足它。&lt;/h3&gt;

&lt;h3 id=&quot;只要我们对自己的需要保持清醒的认识我们就能建设性地满足它们&quot;&gt;只要我们对自己的需要保持清醒的认识，我们就能建设性地满足它们。&lt;/h3&gt;

&lt;h3 id=&quot;反之如果用苛刻的语言指责自己我们不仅难以找到解决办法而且容易陷于自我惩罚的痛苦中&quot;&gt;反之，如果用苛刻的语言指责自己，我们不仅难以找到解决办法，而且容易陷于自我惩罚的痛苦中。&lt;/h3&gt;

&lt;p&gt;那么什么是专注于需要呢？作者举了一个自己的例子：&lt;/p&gt;

&lt;p&gt;有一天他买了一件漂亮的外衣出席会议，在外衣口袋里放了一只钢笔，到达会议才发现钢笔帽忘盖了，外套上染上了一片黑色。他不停的训斥自己“你怎么可以这么粗心！”，此刻其实正是作者需要得到体谅的时刻。幸运的是他很快就意识到了自己的自责，于是他停下来，回想了下，发现其实他是想要照顾好自己，在匆忙地回应他人的请求时，多留意些自己的需要让自己更体面更舒适一些。想到这里，他的心情也开始变化，不再感到恼怒、羞愧和内疚，身体也放松了下来。&lt;/p&gt;

&lt;p&gt;故事中，作者把恼怒和内疚的情绪转移为观察自己“去了解自己，需要什么”，了解到了自己的真实需求后，心情也开始发生了变化，最后身体放松了下来。
如果我们能致力于满足他人及自己健康成长的需要，那么即使艰难的工作也不乏乐趣。&lt;/p&gt;

&lt;p&gt;反之，如果我们的行为是出于义务、职责、恐惧、内疚或羞愧，那么即使有意思的事情也会变得枯燥无味。&lt;/p&gt;

&lt;p&gt;在我们的社会中内疚常常被运用来控制人，这造成指责他人常常成为一种习惯。有时为了使这种手段奏效，我们可能就会认为一个人可以主导另一个人的情绪。这是极其错误的想法。&lt;/p&gt;

&lt;p&gt;对于每个人来说，同一件事情有不同的需要，也导致了不同的感受。&lt;/p&gt;

&lt;p&gt;如果意识不到自己尚未满足的需要，一心考虑别人的过错，我们难免就会生气。&lt;/p&gt;

&lt;h3 id=&quot;其实我们无须压抑愤怒只要我们专注于他人的感受和需要愤怒也就不再存在&quot;&gt;其实我们无须压抑愤怒，只要我们专注于他人的感受和需要，愤怒也就不再存在。&lt;/h3&gt;

&lt;p&gt;作者说出了愤怒的根本原因，即愤怒其实是我们的思维方式造成的，正是由于我们的思维方式无法满足我们的需要导致了我们的愤怒。
如果我们能够借助愤怒来提醒自己，我们的需要没有得到满足，这表明当下我们的思维方式有问题，因为它难以满足我们的需要，从这个角度思考的话愤怒就是有价值的。&lt;/p&gt;

&lt;p&gt;因此我们有必要了解自己的需要再采取行动。&lt;/p&gt;

&lt;p&gt;但是做到这一点并不容易，因为愤怒驱使我们去惩罚他人，而不是去满足需要，这是人性使然。&lt;/p&gt;

&lt;p&gt;作者建议我们有意识的去培养发现对方和自己需要的习惯，与其沉浸于“合理的愤怒”，不如倾听自己和他人的需要。&lt;/p&gt;

&lt;p&gt;当然，这需要一个过程，我们需要通过不断实践去培养这个思维习惯，有意识的将“我生气是因为他们xxx”代替为“我生气是因为我需要xxx，对方需要xxx”。&lt;/p&gt;

&lt;p&gt;这里又举了一个作者生活中的例子，&lt;/p&gt;

&lt;p&gt;有一次他去一所学校提供咨询服务时，被两个孩子各打了两拳，第一个孩子打他时他差点打回去，第二个孩子打他时他则没有。他回想起这段经历说，自己生气的原因并不在于别人到底做了什么，而在于自己怎么看待对方及其行为。因为第一个孩子作者一开始就认为对方是个调皮的坏孩子，所以打他时他很生气，而第二个孩子作者从一开始就认为是个可爱的孩子，所以打他时他能从容的面对。&lt;/p&gt;

&lt;p&gt;由此可知，如果人们认为自己的痛苦是由他人造成的，并认为那人应该受到谴责或惩罚，那么我们就播下了暴力的种子。&lt;/p&gt;

&lt;p&gt;同样的，如果我们满脑子是非对错，把某些人看作是贪婪的人、不负责任的人、骗子或其他类型的坏人，那么，我们就很难与他们建立良好的关系。&lt;/p&gt;

&lt;h3 id=&quot;说到底我们通常生气的原因并不在于别人到底做了什么而是我们自己怎么看待对方及其行为&quot;&gt;说到底，我们通常生气的原因，并不在于别人到底做了什么，而是我们自己怎么看待对方及其行为。&lt;/h3&gt;

&lt;h3 id=&quot;指责他人有时可以使我们达到目的例如他们出于害怕内疚或惭愧使他们改变了自己的行为&quot;&gt;指责他人有时可以使我们达到目的，例如他们出于害怕、内疚或惭愧，使他们改变了自己的行为。&lt;/h3&gt;

&lt;h3 id=&quot;然而以这样的方式来满足我们的需要其实我们也是在使用暴力&quot;&gt;然而，以这样的方式来满足我们的需要，其实我们也是在使用暴力。&lt;/h3&gt;

&lt;p&gt;那么当我们愤怒时到底应该怎么做才能让表达更流畅呢？&lt;/p&gt;

&lt;p&gt;作者给出了，正确表达愤怒的四个步骤：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;停下来，除了呼吸，什么都别做&lt;/li&gt;
  &lt;li&gt;想一想是什么想法使我们生气了&lt;/li&gt;
  &lt;li&gt;试着去了解自己的需要&lt;/li&gt;
  &lt;li&gt;表达当下的感受和尚未满足的需要&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;有时在步骤4表达前需要先倾听了解别人的需要，当我们专注于他人的感受和需要时，我们就会发现彼此作为人的共同点。&lt;/p&gt;

&lt;p&gt;根据生活经验，在与人交往的过程中，我们的第一反应常常是习惯性的反应，因此运用非暴力沟通其实是很别扭的事。只要人们认为自己受到了指责，他们就很难体会到别人的痛苦。因此专注他人的感受和需要并不容易，我们需要有足够的耐心来学习和运用非暴力沟通。&lt;/p&gt;

&lt;p&gt;这里列举了几个生活中的方法，来练习非暴力沟通：&lt;/p&gt;

&lt;p&gt;方法1，把非暴力沟通的表达形式写在一张卡片上，每次有人批评时，就把卡片拿出来提醒自己用非暴力沟通方式表达。&lt;/p&gt;

&lt;p&gt;方法2，刚开始运用非暴力沟通时，把节奏放慢些，在说话前先想一想，有时甚至停下来，什么也不说。&lt;/p&gt;

&lt;p&gt;方法3，留意我们脑袋中“我不喜欢抽烟的人…”之类的想法，然后问自己“我不喜欢他们…，是因为我什么样的需要没有得到满足”，通过这样的方式，我
们就把注意力放在了尚未得到满足的需要，而不是考虑他人有什么过错。&lt;/p&gt;

&lt;h3 id=&quot;运用强制力避免伤害&quot;&gt;运用强制力避免伤害&lt;/h3&gt;

&lt;p&gt;作者列举了几个不够成熟的表现，它认为当人们不够成熟的时候我们可能会有以下的表现&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;我们意识不到自己行为的后果&lt;/li&gt;
  &lt;li&gt;我们认识不到，我们并不需要通过惩罚他人来满足自己的需要&lt;/li&gt;
  &lt;li&gt;我们相信，我们有“权利”去惩罚或伤害他人，因为他们是罪有应得。&lt;/li&gt;
  &lt;li&gt;我们产生了幻觉，例如，听到“某种声音”叫我们去杀人。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;在使用惩罚性的强制力时，我们在心里认为某些人是邪恶的，为了让他们悔改，必须给他们一点颜色看看。&lt;/p&gt;

&lt;p&gt;此时的我们希望痛苦能让他们，意识到自己的过程，或感到懊悔，或改变行为。然而，实际生活中，惩罚往往加强了对方的敌意和抵触心理，使双方的关系更加疏远。&lt;/p&gt;

&lt;p&gt;例如在我们面对的孩子教育时：&lt;/p&gt;

&lt;p&gt;有时候，孩子拒绝做一件对他们有益的事情，只是因为他们不想在父母的压力面前屈服。
其次，即使体罚能带来立竿见影的效果，这也并不意味着，其他方法无法达到同样的效果。
体罚孩子会造成不良的社会影响。除了体罚外，指责或否定他人也是常见的惩罚方式。&lt;/p&gt;

&lt;h3 id=&quot;惩罚的代价&quot;&gt;惩罚的代价&lt;/h3&gt;

&lt;h3 id=&quot;我们要清醒的认识到惩罚是有代价的当我们为了回避惩罚去做事情时我们可能会忽视事情本身的价值而仅仅陷入了对失败的忧虑如果员工的表现只是在服从管理层的命令士气就会受到影响工作效率迟早都会降低惩罚也将导致关系的疏远一旦我们被看作是施暴的人我们就很难得到友善的回应&quot;&gt;我们要清醒的认识到惩罚是有代价的，当我们为了回避惩罚去做事情时，我们可能会忽视事情本身的价值，而仅仅陷入了对失败的忧虑。如果员工的表现只是在服从管理层的命令，士气就会受到影响，工作效率迟早都会降低。惩罚也将导致关系的疏远，一旦我们被看作是施暴的人，我们就很难得到友善的回应。&lt;/h3&gt;

&lt;p&gt;举个例子，
作者有一次去校园培训，校长看到操场上有个学生在打人，校长抓住那个学生，重重推了他一下并训斥道他。
作者告诉校长，他并没有达到目的，学生只是学会了在打比他弱小的人时要注意附近是否有比他强壮的人，并且强化了他们通过打架来解决问题的意识。&lt;/p&gt;

&lt;p&gt;当我们想要影响他人的行为时，存在两个问题：&lt;/p&gt;

&lt;p&gt;第一，当我不喜欢他现在的行为时，我希望他怎么做？&lt;/p&gt;

&lt;p&gt;第二，我希望他基于怎样的原因去做我想要他做的事情？&lt;/p&gt;

&lt;h3 id=&quot;威胁和惩罚确实可以影响第一个问题我希望他怎么做但我们可以发现它难以实现第二个问题我希望他基于怎样的原因去做这就是惩罚的局限性&quot;&gt;威胁和惩罚确实可以影响第一个问题（我希望他怎么做），但我们可以发现它难以实现第二个问题（我希望他基于怎样的原因去做），这就是惩罚的局限性。&lt;/h3&gt;

&lt;p&gt;再举了个例子，
在一所秩序比较混乱的学校里，作者通过沟通了解到，上课时有些学生听不进去但又没有地方去，所以在课堂上捣乱。
经过跟学生的一番讨论，他们主动提出，设置一个空教室，听不进课的学生可以选择去那个教室玩自己的事，这样课堂上其他想要学习的学生就不会被打扰了。&lt;/p&gt;

&lt;p&gt;于是作者请求校长设置这样一个空教室，并且在教室里安排一个熟练掌握非暴力沟通技巧的老师和他们交谈，最后慢慢地学校恢复了正常的秩序。&lt;/p&gt;

&lt;h3 id=&quot;了解别人基于什么样的原因来满足我们的需要是至关重要的因为我们彼此相互依存我们的幸福与他人的幸福息息相关&quot;&gt;了解别人基于什么样的原因来满足我们的需要是至关重要的，因为我们彼此相互依存，我们的幸福与他人的幸福息息相关。&lt;/h3&gt;

&lt;p&gt;生活中我们也许需要使用强制力来保护自己和他人，但这样做只是为了避免伤害而不是为了惩罚他人。&lt;/p&gt;

&lt;h3 id=&quot;如果我们通过威胁和惩罚手段来制裁他人人们常常会产生敌意和抵触心理这样彼此的关系将会疏远&quot;&gt;如果我们通过威胁和惩罚手段来制裁他人，人们常常会产生敌意和抵触心理。这样，彼此的关系将会疏远。&lt;/h3&gt;

&lt;h3 id=&quot;重获生活的热情&quot;&gt;重获生活的热情&lt;/h3&gt;

&lt;h3 id=&quot;倾听内心的声音&quot;&gt;倾听内心的声音&lt;/h3&gt;

&lt;p&gt;我们在社会文化影响下形成了许多不良的习惯，渗透到了生活的各个方面，以致于我们很难察觉到它们的存在。&lt;/p&gt;

&lt;p&gt;前面我们认识到，情感的根源在于个人的需要和想法，并且我们需要以建设性的语言提出明确的请求。在《精神病学的革命》一书中提出，“沮丧是因为一个人处于激烈的内心冲突之中，无所适从”，就像我们的内心中有两个声音在呼喊那样，一个说要这样做，另一个说要那样做，我们陷入了僵局。
因此沮丧意味着，我们不了解自己的需要，我们不知道自己到底要什么以及如何满足愿望。&lt;/p&gt;

&lt;p&gt;举了一位女士的例子，
这位女士很沮丧，在孩子出生后一直在家里照顾孩子，她很想出去工作，这两种声音一直在内心中冲突。
于是她用非暴力沟通的方式表达出来，“为了照顾孩子我放弃了工作，我有点心灰意冷，因为我需要成就感，我想出去找份工作”，“我一想到上班，就很害怕，因为我要确保孩子得到很好的照顾，我想我需要一位好保姆来照顾孩子，而且我还要确保下班时有精力陪伴孩子”。
这位女士用两次非暴力沟通与两个不同的自己对话，说出心里话后让她大大的松了一口气。&lt;/p&gt;

&lt;p&gt;这个故事告诉我们，当我们正确表达出我们的需要和感受时，我们会更爱护自己同时也会让自己“松一口气”。&lt;/p&gt;

&lt;h3 id=&quot;心灵环保&quot;&gt;心灵环保&lt;/h3&gt;

&lt;h3 id=&quot;现实告诉我们如果我们时常以苛刻的态度对人对己我们的心情也好不到哪里去&quot;&gt;现实告诉我们，如果我们时常以苛刻的态度对人对己，我们的心情也好不到哪里去。&lt;/h3&gt;

&lt;p&gt;运用非暴力沟通，我们能不再试图分析自己或他人有什么毛病，而是用心去了解我们的需要，这样，我们的内心就会逐渐变得平和。&lt;/p&gt;

&lt;p&gt;作者举了两个学生的例子，
以前他每次头痛时都会回忆前几天的经历来找到原因，现在则不同，他会先问自己需要做什么来缓解头痛，然后坐起来做一些柔和的头部运动，走到外面做一些别的事情，让自己放松下来。他观察到了自己的状态，并了解了自己的需要，通过这样的方式他有了重大的突破。
另一个学生，他每次开车都有路怒症，看到别人开车不守规矩就会愤怒，现在他更多的会去关注自己的感受和需要，以及对方司机的感受和需要。“是的，我看到他们这样开车，我真的很害怕，我希望他们开车时能注意安全”，“前面的那位司机有点不知所措，希望得到后面司机的谅解。路渐宽时我超车看到司机是位老太太，她看上去确实是惊慌失措，我很庆幸，我曾用心去体会她的感受和需要。”&lt;/p&gt;

&lt;h3 id=&quot;用非暴力沟通代替诊断&quot;&gt;用非暴力沟通代替诊断&lt;/h3&gt;

&lt;p&gt;作者相信可以用非暴力沟通来代替诊断和治疗。他相信，在治疗过程中，表达个人的感受和需要是有益的。&lt;/p&gt;

&lt;p&gt;于是在诊所诊断病人时，作者不再根据他所学习的心理学理论来分析来访者的心理特点，而是用心去体会他们的话，并表达自己内心的感受。结果效果非常好，无论是来访者还是我自己，都十分满意。&lt;/p&gt;

&lt;p&gt;作者解释到，自己不会去分析病人有什么毛病，而是问自己以下问题，“她现在什么心情？有什么需要？和她在一起我是什么心情？我的心情反映了我怎样的需要？我想请她做出什么决定或采取什么行动，以使她能快乐些？”在回答这样的问题时，我们会揭示自己的内心活动以及个人需要。与平常的诊断相比，我们能深深体会到我们作为人的弱点。&lt;/p&gt;

&lt;p&gt;一旦我们不把人当作诊断的对象，而是专注于彼此作为人的感受和需要，人们通常都会有积极的反应。&lt;/p&gt;

&lt;p&gt;小节，在情绪低落时，我们也许会怨天尤人。如果我们以苛刻的态度对人对己，我们的心情也好不到哪里去。&lt;/p&gt;

&lt;p&gt;通过运用非暴力沟通，我们不再试图分析自己或他人有什么毛病，而是用心去了解我们的需要，这样，我们的内心就会逐渐变得平和。
一旦我们发现自己心底深处的愿望，并采取积极的行动，我们将会重获生活的热情。&lt;/p&gt;

&lt;h3 id=&quot;如何正确的表达感激&quot;&gt;如何正确的表达感激&lt;/h3&gt;

&lt;p&gt;赞扬也可能造成人与人之间的隔阂。这听起来似乎很奇怪。不过请注意，在赞扬他人时，我们很少揭示内心活动，而把自己放在了裁判的位置。
我们认为，赞扬并不总是有助于深化彼此的联系。&lt;/p&gt;

&lt;p&gt;如果学生或员工一旦意识到经理和老师赞扬的目的是为了操纵他们，员工和学生很可能就会产生逆反心理。&lt;/p&gt;

&lt;p&gt;当发现这些赞扬的潜在目的之后，人们对于由衷的感谢也会心存疑虑。&lt;/p&gt;

&lt;p&gt;同样的，如果我们利用赞扬来施加影响，对方还可能误解我们的意思。&lt;/p&gt;

&lt;p&gt;用非暴力沟通方式表达感激时，我们只是为了庆祝他人的行为提升我们的生活品质，而不是想得到任何回报。&lt;/p&gt;

&lt;p&gt;非暴力沟通表达感激的方式包含三部分：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;对方做了什么事情时我们的生活得到了改善？&lt;/li&gt;
  &lt;li&gt;我们有哪些需要得到了满足？&lt;/li&gt;
  &lt;li&gt;得到满足后，我们的心情怎么样？&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;我们常常假定人们已经知道我的情意然而这是想当然的即使人们在听到感激时也会感到尴尬他们会想听到明确的表达而不是泛泛而谈的感激&quot;&gt;我们常常假定人们已经知道我的情意，然而这是想当然的。即使人们在听到感激时也会感到尴尬，他们会想听到明确的表达，而不是泛泛而谈的感激。&lt;/h3&gt;

&lt;p&gt;在别人表达感激时，人们通常有两种截然不同的反应。一种是自我膨胀，相信我们比别人优越；另一种是假谦虚，否定别人的欣赏，耸耸肩“哦，这没什么”。如果我们意识到我的能力是生命赋予我的，我就能够同时避免自我膨胀和假谦虚。&lt;/p&gt;

&lt;h3 id=&quot;当别人我们的感激时我们可以与对方一起庆祝生命的美这样既不自大也不假谦虚&quot;&gt;当别人我们的感激时，我们可以与对方一起庆祝生命的美，这样既不自大也不假谦虚。&lt;/h3&gt;

&lt;p&gt;以色列前总理哥达曾说，“不要那么谦虚，因为你没有那么伟大”。作家玛丽安也说，“我们让自己的光芒闪耀，也允许他人散发光芒。一旦我们从自我的恐惧中解放出来，我们的存在，也会让他人得到解放。”&lt;/p&gt;

&lt;p&gt;最后回顾下非暴力沟通模式，观察、感受、需要、请求&lt;/p&gt;

&lt;p&gt;1.观察&lt;/p&gt;

&lt;p&gt;我所观察到的有助于我的福祉的具体行为&lt;/p&gt;

&lt;p&gt;2.感受&lt;/p&gt;

&lt;p&gt;对于这些行为，我有什么样的感受&lt;/p&gt;

&lt;p&gt;3.需要&lt;/p&gt;

&lt;p&gt;什么样的需要或价值导致我那样的感受&lt;/p&gt;

&lt;p&gt;4.请求&lt;/p&gt;

&lt;p&gt;清楚地请求那些能丰富我生命的具体行为&lt;/p&gt;

&lt;p&gt;我们在表达时要注意两点：&lt;/p&gt;

&lt;p&gt;一.诚实地表达自己，而不批评、指责&lt;/p&gt;

&lt;p&gt;二.关切地倾听他人，而不解读为批评或指责&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484873&amp;amp;idx=1&amp;amp;sn=b7d9986ffd0f259ec3dc25bc50f528a5&amp;amp;chksm=fc2260cecb55e9d8dec522275320a2211e2ebb4f2025ff415c6e730c66d0ce07b9aaa0892adb&amp;amp;token=1516544318&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>读书笔记(三十九) 《非暴力沟通》#2 - 请求帮助、用心倾听</title>
   <link href="http://www.luzexi.com/2021/08/10/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B039"/>
   <updated>2021-08-10T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/08/10/读书笔记39</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484864&amp;amp;idx=1&amp;amp;sn=da81a32d8fc50ce730259e03ea8344b1&amp;amp;chksm=fc2260c7cb55e9d1ad4c9322f9d6f04c234fc72554d9a40050ff61639f80bbe701501151ae86&amp;amp;token=326491610&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;背景&quot;&gt;背景：&lt;/h2&gt;

&lt;p&gt;表达能力和沟通技巧有诸多问题，工作和生活上出现了比较大的瓶颈，于是看了些表达和沟通方面的书，其中三本最为经典《非暴力沟通》、《关键对话》、《说话的艺术》。&lt;/p&gt;

&lt;p&gt;细细品读了这三本书并且收获满满，分享出来希望大家一同进步。&lt;/p&gt;

&lt;p&gt;看完书后反观自己以前的沟通技巧和表达方式，明白了以前自己在这些方面都有比较大的认知错误，回想起来可以用愚蠢来形容。&lt;/p&gt;

&lt;p&gt;既然有这么好的沟通技巧和表达方式，我希望将这些方法和技巧实实在在的落地到实际生活中，让生活、工作、学习更美好。&lt;/p&gt;

&lt;p&gt;下面我就以《非暴力沟通》这本书为主线，来阐述我们在沟通过程中遇到的一些问题和对应的解决方法。&lt;/p&gt;

&lt;h2 id=&quot;概述&quot;&gt;概述：&lt;/h2&gt;

&lt;p&gt;作者认为我们在沟通过程中，提出请求时要明确详尽，谈话的目标要明确，他用生活中的例子来说明人们在平时沟通中常常不明确请求，或不明确谈话的目标，导致沟通的诸多问题，于是阐述了如何让请求明确，让谈话目标明确。然后又说了倾听的重要性，如何倾听以及倾听后如何反馈，做了通俗易懂的说明。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;提出明确的请求&lt;/li&gt;
  &lt;li&gt;如何让谈话更加明确&lt;/li&gt;
  &lt;li&gt;如何用心倾听&lt;/li&gt;
  &lt;li&gt;倾听的力量&lt;/li&gt;
  &lt;li&gt;倾听后如何给他人反馈&lt;/li&gt;
  &lt;li&gt;当我们痛苦到无法倾听时怎么办&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;正文&quot;&gt;正文：&lt;/h2&gt;

&lt;h3 id=&quot;提出明确的请求&quot;&gt;提出明确的请求&lt;/h3&gt;

&lt;p&gt;作者说我们在提出请求时，应该清楚地告诉对方，我们希望他们做什么。而不是告诉他们，不要做什么，因为这很容易引起别人的反感。&lt;/p&gt;

&lt;p&gt;他举了两个例子：&lt;/p&gt;

&lt;p&gt;第一个例子是在越战期间，作者被请去辩论会上辩论，他告诫自己“这次辩论，绝不能这么被动”，但在辩论开始时他就发现他回应的方式和上次一模一样。&lt;/p&gt;

&lt;h3 id=&quot;这个教训让作者明白了如果只是提醒要避免什么而不是清楚的告诉需要做什么的话结果大都是失败的&quot;&gt;这个教训让作者明白了：如果只是提醒要避免什么，而不是清楚的告诉需要做什么的话，结果大都是失败的。&lt;/h3&gt;

&lt;p&gt;第二个例子是在学校里，学生们对校长很不满意，但每次对校长提出提意见时，只说了不希望他做什么，并且提出了一些比较抽象的要求，比如“公平对待”，因此双方的关系一直无法调和。&lt;/p&gt;

&lt;h3 id=&quot;作者告诉他们要说出希望对方做的事并且在请求时提出具体的行动这样他们就较有可能得到积极的回应和较满足的回答最后化解了矛盾&quot;&gt;作者告诉他们要说出希望对方做的事，并且在请求时提出具体的行动，这样他们就较有可能得到积极的回应和较满足的回答，最后化解了矛盾。&lt;/h3&gt;

&lt;p&gt;当我们提出请求时，请求越具体越好，如果我们的意思含糊不清，别人就难以了解我们到底想要什么。&lt;/p&gt;

&lt;p&gt;此外，在提出请求时，如果我们使用的词语比较抽象，就无法让别人清楚的了解我们的想法，所以要在请求时要说具体的行动，这样就能清楚的表达我们需要。&lt;/p&gt;

&lt;p&gt;可以总结为，当我们提出请求时，请求越具体越好，越详细越好，这样就越能让别人清楚地了解我们的想法。&lt;/p&gt;

&lt;p&gt;作者感慨许多向他求助的人中，那些感到沮丧或灰心的人，很大程度上是因为他们不清楚自己对他人究竟有什么样的期待。&lt;/p&gt;

&lt;h3 id=&quot;如何让谈话更加明确&quot;&gt;如何让谈话更加明确&lt;/h3&gt;

&lt;h3 id=&quot;有时候我们并没有直接提出请求但却妄图他人知道我们想要什么这是我们很多人错误的认知&quot;&gt;有时候我们并没有直接提出请求，但却妄图他人知道我们想要什么，这是我们很多人错误的认知。&lt;/h3&gt;

&lt;p&gt;例如我们常常在说话的时候，表面上是与他人在谈话，但其实是在自言自语，这时对方可能就不知道该如何回应，甚至会感到局促不安。&lt;/p&gt;

&lt;p&gt;举个例子，一个人生气的冲太太说“我从没见过开这么慢的火车”，此时他太太就看起来有点不知所措。&lt;/p&gt;

&lt;p&gt;相信每个人在和对方谈话的时候，总希望得到称心的回应。如果你对自己的认识越深刻、表达越清楚，我们就越有可能得到称心的回应。&lt;/p&gt;

&lt;p&gt;有时我们可以请求对方一些反馈，例如，“我的意思清楚吗”，或者反馈对方“我明白你的意思”，确保双方能充分表达和理解。&lt;/p&gt;

&lt;h3 id=&quot;参加集体会议时说清楚我们希望得到怎样的反馈是至关重要的&quot;&gt;参加集体会议时，说清楚我们希望得到怎样的反馈，是至关重要的。&lt;/h3&gt;

&lt;p&gt;如果不清楚发言的目的，开会只是在浪费时间，而且无法满足任何人的需要。我们在集体讨论时如果漫无目的地发言，那么会议很可能就会毫无成果。&lt;/p&gt;

&lt;p&gt;有时即使我们提出了明确的请求，也同样可能造成交流的困难。例如如果人们认为我们的请求是对他们发出的命令。&lt;/p&gt;

&lt;h3 id=&quot;当一个人听到命令时只能看到两种选择服从或者反抗&quot;&gt;当一个人听到命令时，只能看到两种选择，服从或者反抗。&lt;/h3&gt;

&lt;h3 id=&quot;只要人们认为我们时在强迫他们他们就不会乐于满足我们的需要&quot;&gt;只要人们认为我们时在强迫他们，他们就不会乐于满足我们的需要。&lt;/h3&gt;

&lt;h3 id=&quot;如果我们在过去常常指责他人那么我们的请求很可能就会被看作是命令而一个经常受到指责的人也会倾向于将请求解读为命令&quot;&gt;如果我们在过去常常指责他人，那么，我们的请求很可能就会被看作是命令。而一个经常受到指责的人也会倾向于将请求解读为命令。&lt;/h3&gt;

&lt;p&gt;那么我们在提出请求时，人们是如何区分命令和请求呢？&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;当请求没有得到满足时，提出请求的人如果批评和指责，那就是命令。&lt;/li&gt;
  &lt;li&gt;如果想利用对方的内疚来达到目的，也是命令。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;我们越是将他人的不顺从看作是对我们的排斥我们所表达的愿望就越有可能被看作是命令这将导致恶性循环因为一旦人们认为我们是在强迫他们他们就会不太想满足我们的愿望&quot;&gt;我们越是将他人的不顺从看作是对我们的排斥，我们所表达的愿望就越有可能被看作是命令。这将导致恶性循环，因为一旦人们认为我们是在强迫他们，他们就会不太想满足我们的愿望。&lt;/h3&gt;

&lt;h3 id=&quot;在人们无法满足我们的愿望时我们是否尊重他们的感受和需要最能体现我们提出的是请求还是命令&quot;&gt;在人们无法满足我们的愿望时，我们是否尊重他们的感受和需要，最能体现我们提出的是请求还是命令。&lt;/h3&gt;

&lt;p&gt;如果我们愿意去体会是什么使他们无法说“是”，那么，我们提出的就是请求而非命令。&lt;/p&gt;

&lt;p&gt;通过请求而非命令来表达愿望，并不意味着一旦人们说“不”，我们就不再去满足自己的需要，而是在我们没有充分体会到，什么妨碍了他说“是”之前，不再试图说服他。&lt;/p&gt;

&lt;p&gt;非暴力沟通提倡在诚实和倾听的基础上与他人联系，它希望人们的改变和行动是处于对生命的爱。&lt;/p&gt;

&lt;h3 id=&quot;一旦人们相信我们看重彼此的感情并能兼顾双方的需要那么他们也就会相信我们所表达的愿望是请求而非命令&quot;&gt;一旦人们相信我们看重彼此的感情，并能兼顾双方的需要，那么他们也就会相信我们所表达的愿望是请求而非命令。&lt;/h3&gt;

&lt;p&gt;有时候即使我们以适当的方式提出请求，有些人仍会误以为是命令。特别是当我们处于强势的一方时，那些曾受过权威威胁的人尤其容易作出这样的判断。这需要我们经过一段时间引导才能矫正这种看法。&lt;/p&gt;

&lt;h3 id=&quot;如何用心倾听&quot;&gt;如何用心倾听&lt;/h3&gt;

&lt;p&gt;在沟通过程中，倾听非常重要，为了能倾听他人，我们需要先放下已有的想法和判断，全神贯注地体会对方。&lt;/p&gt;

&lt;h3 id=&quot;倾听意味着全心全意地体会他人的信息这也为他人充分表达痛苦创造了条件这时不要急着做什么就站在那里&quot;&gt;倾听意味着全心全意地体会他人的信息，这也为他人充分表达痛苦创造了条件，这时不要急着做什么，就站在那里。&lt;/h3&gt;

&lt;h3 id=&quot;在倾听他人的痛苦经历时他想要的是别人体会了解他的处境但如果听到的却是安慰和建议那么他就有可能觉得不太舒服&quot;&gt;在倾听他人的痛苦经历时，他想要的是别人体会了解他的处境，但如果听到的却是安慰和建议，那么他就有可能觉得不太舒服。&lt;/h3&gt;

&lt;p&gt;那么哪些行为会妨碍我们体会他人的处境呢，例如下边：&lt;/p&gt;

&lt;p&gt;建议：“我想你应该…”
比较：“这算不了什么，你听听我的经历…”
说教：“如果你这样做…你将会得到很大的好处”
安慰：“这不是你的错；你已经尽最大努力了。”
回忆：“这让我想起…”
否定：“高兴一点。不要这么难过。”
同情：“哦，你这可怜的人…”
询问：“这种情况是什么时候开始的？”
辩解：“我原想早点打电话给你，但昨晚…”
纠正：“事情的经过不是这样的。”&lt;/p&gt;

&lt;h3 id=&quot;我们常常认为在亲友感到痛苦时我们应该想办法使他们好受一些然而急于采取行动使我们无法充分体会他们的状况&quot;&gt;我们常常认为，在亲友感到痛苦时，我们应该想办法使他们好受一些，然而，急于采取行动使我们无法充分体会他们的状况。&lt;/h3&gt;

&lt;p&gt;在非暴力沟通中，倾听他人意味着，放下已有的想法和判断，一心一意体会他人。倾听的这种品质体现了与理解以及同情之间的区别。&lt;/p&gt;

&lt;h3 id=&quot;不论别人说什么我们只需要听到他们此时此刻的观察感受需要请求然后我们可以通过询问来了解我们的猜测是否准确&quot;&gt;不论别人说什么，我们只需要听到他们此时此刻的，观察、感受、需要、请求，然后我们可以通过询问来了解我们的猜测是否准确。&lt;/h3&gt;

&lt;p&gt;不过在解决问题或询问他人的请求前，我们要为他人的充分表达创造条件。&lt;/p&gt;

&lt;p&gt;那么怎样判断对方的感受是否已经充分表达呢？&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;如果一个人觉得别人已经完全明白他的意思，他就会变得轻松。这时，我们也会感到放松。&lt;/li&gt;
  &lt;li&gt;另一个更为明显的标志是，他停止了谈话。&lt;/li&gt;
  &lt;li&gt;如果无法确定对方是否还有话要说，就不妨问一句“你还有什么要告诉我的么？”&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;倾听的力量&quot;&gt;倾听的力量&lt;/h3&gt;

&lt;h3 id=&quot;倾听使身心痊愈我们有没发现每当我们得到人们的倾听和理解时我们就可以用新的眼光看世界并继续前进&quot;&gt;倾听使身心痊愈，我们有没发现，每当我们得到人们的倾听和理解时，我们就可以用新的眼光看世界，并继续前进。&lt;/h3&gt;

&lt;h3 id=&quot;一旦有人倾听我们本来看起来无法解决的问题就有了解决办法千头万绪的思路也会变得清晰起来&quot;&gt;一旦有人倾听我们，本来看起来无法解决的问题就有了解决办法，千头万绪的思路也会变得清晰起来。&lt;/h3&gt;

&lt;p&gt;在组织机构中，我们在听到上级的意见时，通常会把它看作是命令或指示。倾听同事或下级比倾听上级要容易得多。&lt;/p&gt;

&lt;p&gt;在上级面前，我们有时可能会有点紧张，以致于无法静下心来体会他们的感受和需要，并给予反馈。&lt;/p&gt;

&lt;h3 id=&quot;倾听和示弱的能力&quot;&gt;倾听和示弱的能力&lt;/h3&gt;

&lt;h3 id=&quot;我们最不愿意示弱的时候往往是因为担心失去控制想显得强硬的时候&quot;&gt;我们最不愿意示弱的时候往往是因为担心失去控制想显得强硬的时候。&lt;/h3&gt;

&lt;p&gt;当我们表达自己示弱时，有时人们会说一些糟糕的话，如果我们将它看作是羞辱或攻击，我们很可能就会觉得自己受到了伤害，甚至因为过于愤怒或害怕而无法倾听他们。&lt;/p&gt;

&lt;p&gt;遇到这种情况，我们需要离开现场，来倾听自己心底的声音，或向好友倾诉，这将有助于我了解并体贴自己的感受和需要，等平静下来后，再回来与对方交流。&lt;/p&gt;

&lt;p&gt;在痛苦的情形中，为了了解自己深层的需要，建议首先获得必要的倾听，以避免为充斥头脑的各种想法所束缚。&lt;/p&gt;

&lt;p&gt;最后可以通过倾听他们的感受和需要，并给予他们反馈。&lt;/p&gt;

&lt;h3 id=&quot;倾听预防潜在的暴力&quot;&gt;倾听预防潜在的暴力&lt;/h3&gt;

&lt;p&gt;那些被我们看作是恶魔的人，其实也是人，只是有时我们无法看到他们和我们的相同之处。&lt;/p&gt;

&lt;p&gt;我越是专注于他的感受和需要，我就越把他看作因需要没有得到满足而感到绝望的人。&lt;/p&gt;

&lt;p&gt;如果别人说“不”&lt;/p&gt;

&lt;p&gt;当别人说“不”的时候，我们常常会认为他们是在拒绝我们。有时，我们甚至还会觉得受到了伤害。&lt;/p&gt;

&lt;p&gt;然而，如果我们能够体会他人的感受和需要，我们也许就会发现是什么使他们无法答应我们的请求。&lt;/p&gt;

&lt;h3 id=&quot;使谈话生动有趣&quot;&gt;使谈话生动有趣&lt;/h3&gt;

&lt;p&gt;怎样使谈话生动有趣呢？作者建议说话的人留意自己的感受和需要。&lt;/p&gt;

&lt;p&gt;人们常常没有意识到，他们需要的是别人的理解和接纳。他们也没有意识到，直接表达自己的感受和需要，与讲故事相比，更容易得到他们所期待的联系。&lt;/p&gt;

&lt;p&gt;另一种方法是，直接表达我们的愿望。&lt;/p&gt;

&lt;h3 id=&quot;如果别人保持沉默&quot;&gt;如果别人保持沉默&lt;/h3&gt;

&lt;p&gt;有时候我们说了心里话，却发现对方一句话都不说，我们也许会很不安，容易把事情往坏处想，这时很难静下心来体会对方的感受和需要。但我们仍然要鼓起勇气体会对方，用平静的话语表达出来，询问对方当前的状态的原因。&lt;/p&gt;

&lt;p&gt;这里作者举了一个例子：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;在一次工作时，作者在交谈中动了感情，哭了起来，但却发现对方转过身去，默不作声。
这使得作者心里很不安，也很困惑。
于是就问他，“你生气了吗？你希望与你工作的人有很好的自制力？”
对方则回答，“不是这样的。我在想我的太太多么希望我能哭。”他接着讲到他和太太正在闹离婚的事情。
不安和困惑就这样化解了。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;小结下，倾听帮助人们治愈了心灵的创伤，也使我们勇于面对自己的弱点。&lt;/p&gt;

&lt;h3 id=&quot;如何给他人反馈&quot;&gt;如何给他人反馈&lt;/h3&gt;

&lt;p&gt;在倾听他人的观察、感受、需要和请求之后，我们可以主动过表达我们的理解。在提问前，先表达我们的感受和需要，这会鼓励人们主动表达自己。&lt;/p&gt;

&lt;p&gt;那么什么时候需要反馈呢？他人的表达后，我们对他说的没有理解或没有把握时，我们需要与对方的确认。&lt;/p&gt;

&lt;p&gt;当我们在给他人反馈时，语气十分重要。因为当一个人在听别人谈自己的感受和需要时，总会留意其中是否暗含着批评和嘲讽。&lt;/p&gt;

&lt;p&gt;如果我们的语气很肯定，仿佛是在宣布他们的内心世界，那么，通常不会有好的反应。然而，如果对方通过我们的语气意识到我们是在体会他，而不是下结论，他们就不会产生反感了。&lt;/p&gt;

&lt;p&gt;我们在一般谈话时，通常对方并不信任我们，除非对我们的意图有进一步的了解，否则他不会喜欢我们表达的方式。&lt;/p&gt;

&lt;p&gt;然而，只要我们专注于他人的感受和需要，所有的批评、攻击、辱骂或嘲讽就会消失。&lt;/p&gt;

&lt;p&gt;我们越是这样做，就越能体会到一个简单的事实：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;有时，我们认为自己受到了指责，实际上，那些话是他人表达需要和请求的方式。&lt;/li&gt;
  &lt;li&gt;如果意识到这一点，我们就不会认为自己的人格受到了伤害。&lt;/li&gt;
  &lt;li&gt;反之，如果一心分析自己或对方的过错，我们就会认为自己被贬低了。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;当然非暴力沟通并不是公式，有时候我们会机械地运用非暴力沟通模式，而忘记了其目的。这时我们应该问问自己，我们是否关心的是加深与人的联系。&lt;/p&gt;

&lt;h3 id=&quot;当我们痛苦的无法倾听时&quot;&gt;当我们痛苦的无法倾听时&lt;/h3&gt;

&lt;p&gt;有时我们会发现，我们很痛苦，没有心情去关心别人。这时该怎么办？&lt;/p&gt;

&lt;p&gt;联合国前秘书长汉马斯克德曾说“你越是留意自己内心的声音，就越能够听到别人的声音。”
一旦我们能够敏锐地察觉并顾及自己的感受和需要，我们就有能力迅速调整好状态，来倾听他人。&lt;/p&gt;

&lt;p&gt;第一种方式是，体会自己的感受和需要。
另一种方式就是，大声提出请求。
最后一种是换一个环境，用时间和空间来调整状态，等平静下来了，再回来。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484864&amp;amp;idx=1&amp;amp;sn=da81a32d8fc50ce730259e03ea8344b1&amp;amp;chksm=fc2260c7cb55e9d1ad4c9322f9d6f04c234fc72554d9a40050ff61639f80bbe701501151ae86&amp;amp;token=326491610&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(六十六) 与千万营收女企业家的一次午餐交流</title>
   <link href="http://www.luzexi.com/2021/08/08/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A866"/>
   <updated>2021-08-08T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/08/08/思路探讨66</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484857&amp;amp;idx=1&amp;amp;sn=fa886cfca425df55fe3a3e037ea73e5b&amp;amp;chksm=fc2260becb55e9a83ddb0bd8e3d3d1cc170cef9de8af0e76f676d16208f74595c58bd7b9d8d9&amp;amp;token=2087589290&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;背景&quot;&gt;背景：&lt;/h2&gt;

&lt;p&gt;与玉姐聊的很开心，也受益良多。&lt;/p&gt;

&lt;p&gt;玉姐是我在深圳认识的众多牛人中比较特别的一个，自主创业打拼天下，不仅自己成功上岸，还能带动周围的小伙伴一起成功。&lt;/p&gt;

&lt;p&gt;有幸能约到她一起吃午餐，聊了下各自工作、生活上的观点和看法，深受启发。&lt;/p&gt;

&lt;p&gt;颇有听君一席话胜读十年书的感触。&lt;/p&gt;

&lt;h2 id=&quot;概述&quot;&gt;概述：&lt;/h2&gt;

&lt;p&gt;我们边吃边聊，对话中话题的切换比较快，我选择了几个比较有趣的话题，对我们帮助比较大的话题来写总结。&lt;/p&gt;

&lt;p&gt;1.沟通能力是关键&lt;/p&gt;

&lt;p&gt;2.经营公司完善制度和流程&lt;/p&gt;

&lt;p&gt;3.向高人学习&lt;/p&gt;

&lt;p&gt;4.不要在意输赢&lt;/p&gt;

&lt;p&gt;5.争取自由避免被束缚&lt;/p&gt;

&lt;p&gt;6.认可的人&lt;/p&gt;

&lt;h2 id=&quot;正文&quot;&gt;正文：&lt;/h2&gt;

&lt;h3 id=&quot;1沟通能力是关键&quot;&gt;1.沟通能力是关键&lt;/h3&gt;

&lt;p&gt;沟通能力是比较关键的能力，它不仅仅是你能力的放大器还是集思广益、团队合作的重要方法，我们应该在平时更多关注这种能力的培养和复盘。&lt;/p&gt;

&lt;p&gt;玉姐建议在交流过程中，要能感受到对方情绪变化，通过这种变化来转换话题，这样可以让对话更加顺畅。&lt;/p&gt;

&lt;p&gt;不管是陌生人还是普通朋友，或是亲密朋友，都需要有一些沟通技巧来让彼此的交流更加舒畅。&lt;/p&gt;

&lt;p&gt;看的出来玉姐社交能力很强，不管是与朋友、陌生人、下属、老板、亲人都能保持平衡的关系，让对方与自己相处时很舒服。&lt;/p&gt;

&lt;h3 id=&quot;2经营公司完善制度和流程&quot;&gt;2.经营公司完善制度和流程&lt;/h3&gt;

&lt;p&gt;在经营公司方面，玉姐认为自己当下最重要的是去完善公司的制度和流程，让公司运作更加有秩序。&lt;/p&gt;

&lt;p&gt;当前公司已经向成功迈进了很大一步，所以在流程的每个节点上要有有效的保障，目标是保证流程是牢固，当外部因素侵扰时仍然能有效运作。&lt;/p&gt;

&lt;h3 id=&quot;3向高人学习&quot;&gt;3.向高人学习&lt;/h3&gt;

&lt;p&gt;玉姐建议在不同领域中有很多牛人，可以跟他们交流过程中相互输出观点，交换观点来开阔眼界，提升自我的认知。&lt;/p&gt;

&lt;p&gt;找更多机会与职级高的、视野广的、社会等级高的人交流，例如大公司高管、中层、或者企业家等，能学习到很多东西也开阔很多眼界。&lt;/p&gt;

&lt;h3 id=&quot;4不要在意输赢&quot;&gt;4.不要在意输赢&lt;/h3&gt;

&lt;p&gt;过程比结果重要，人生是过程不是结果。过程中奋斗、蜕变，很精彩，结果只是附带的产品，不要太在意，专注过程。（因此玉姐也更喜欢专注于自己领域的男生^_^）&lt;/p&gt;

&lt;p&gt;联想到最近我拿自己的弱项（去学习演讲，学习沟通）跟别人的强项碰撞输的很惨。因此我最近比较痛苦，虽然我认为这种痛苦是值得的。玉姐认为人本身有就有自己擅长的和不擅长的，不用强求自己去做自己不擅长的，学习可以，但在学习的过程中不要太在意输赢，更不要在意别人怎么看。&lt;/p&gt;

&lt;h3 id=&quot;5争取自由避免被束缚&quot;&gt;5.争取自由避免被束缚&lt;/h3&gt;

&lt;p&gt;坚持自由意志对人的思维有很大好处，自发的主动比照搬照抄墨守成规的行动厉害的多。&lt;/p&gt;

&lt;p&gt;比如说我会对理论比较关注，认为理论是基础，但很多时候我被这些理论限制住了，发散不开思维。&lt;/p&gt;

&lt;p&gt;玉姐认为实践出真知，行动中、交流中迸发出智慧的火花，理论学习只是其中一个小部分，更多的要靠交流和实践来得出自己的智慧。&lt;/p&gt;

&lt;h3 id=&quot;6认可的人&quot;&gt;6.认可的人&lt;/h3&gt;

&lt;p&gt;严于律己宽以待人的人。&lt;/p&gt;

&lt;p&gt;聊到我们看到的一些现象，一些人本身家庭底子就很好，通过自己努力破除自身的思想束缚，拉拢身边的人让自己的智慧和能力放大数倍。&lt;/p&gt;

&lt;p&gt;其中严于律己宽以待人的人不仅自己能力强，还能通过高超的沟通能力来吸引周围的人一起将事情做大做强。&lt;/p&gt;

&lt;p&gt;这种人最可贵，自身要求高的同时还能照顾到周围人的情绪，懂得换位思考懂得体谅。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484857&amp;amp;idx=1&amp;amp;sn=fa886cfca425df55fe3a3e037ea73e5b&amp;amp;chksm=fc2260becb55e9a83ddb0bd8e3d3d1cc170cef9de8af0e76f676d16208f74595c58bd7b9d8d9&amp;amp;token=2087589290&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(三十八) 《非暴力沟通》#1 - 观察并表达感受</title>
   <link href="http://www.luzexi.com/2021/08/01/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B038"/>
   <updated>2021-08-01T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/08/01/读书笔记38</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484849&amp;amp;idx=1&amp;amp;sn=3f19b8b6b3295aa952641f62aadce98b&amp;amp;chksm=fc2260b6cb55e9a0079d4f2254fd46699c6e5e345e7e92ba6fb9a15666a39aec9b62279c4de7&amp;amp;token=347679726&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;背景&quot;&gt;背景：&lt;/h2&gt;

&lt;p&gt;我观察到自己表达能力和沟通技巧有诸多问题，工作和生活上出现了比较大的瓶颈。为了帮助自己成长，我看了些表达和沟通方面的书，其中三本最为经典《非暴力沟通》、《关键对话》、《说话的艺术》。细细品读了这三本书并且收获满满，分享出来希望同大家一起学习。&lt;/p&gt;

&lt;p&gt;自从看完书后，反观自己以前的沟通技巧和表达方式，明白了以前自己在这方面的认知有比较大的错误，回想起来可以用愚蠢来形容。&lt;/p&gt;

&lt;p&gt;既然有这么好的沟通技巧和表达方式，我希望将这些方法和技巧实实在在的落地到实际生活中，让生活、工作、学习更美好。&lt;/p&gt;

&lt;p&gt;下面我就以《非暴力沟通》这本书为主线，来阐述我们在沟通过程中遇到的一些问题和对应的解决方法。&lt;/p&gt;

&lt;h2 id=&quot;正文&quot;&gt;正文：&lt;/h2&gt;

&lt;p&gt;开头我同作者一样对这个世界有个疑问，“究竟是什么，使我们难以体会到心中的爱，以致于互相伤害？又是什么，让人在充满敌意的环境中，也能心存爱意？”&lt;/p&gt;

&lt;h3 id=&quot;我认识到我们的话语及表达方式对周遭有着巨大影响也许我们并不认为自己的谈话方式是暴力的但我们的话语确实常常引发自己和他人的痛苦而这种痛苦很大可能就会成为互相伤害的开始&quot;&gt;我认识到我们的话语及表达方式对周遭有着巨大影响，也许我们并不认为自己的谈话方式是“暴力”的，但我们的话语确实常常引发自己和他人的痛苦，而这种痛苦很大可能就会成为互相伤害的开始。&lt;/h3&gt;

&lt;p&gt;非暴力沟通可以指导我们转变谈话和聆听的方式，这样我们就不再条件反射式地做出反应了，而是先去明了自己的观察、感受和愿望，有意识地使用言语。这样，我们既诚实、清晰地表达了自己、又尊重与倾听了他人。每一次互动我们都能聆听到自己和他人心灵深处的呼声。同时，还能促使我们观察、发现正影响我们的行为和事件，并提出明确的请求。&lt;/p&gt;

&lt;h3 id=&quot;在平时的交流中当我们听到批评时我们一般会申辩退缩或反击&quot;&gt;在平时的交流中，当我们听到批评时，我们一般会申辩、退缩或反击。&lt;/h3&gt;

&lt;h3 id=&quot;然而如果我们专注于彼此的观察感受及需要而不反驳他人我们便能发现内心的柔情从而最大限度避免互相伤害避免暴力&quot;&gt;然而，如果我们专注于彼此的观察、感受及需要，而不反驳他人，我们便能发现内心的柔情，从而最大限度避免互相伤害，避免“暴力”。&lt;/h3&gt;

&lt;p&gt;那怎样才能做到在沟通时专注于观察和感受呢？&lt;/p&gt;

&lt;h3 id=&quot;作者提出非暴力沟通的四个要素观察感受需要请求&quot;&gt;作者提出非暴力沟通的四个要素，观察、感受、需要、请求：&lt;/h3&gt;
&lt;ol&gt;
  &lt;li&gt;首先，留意发生的事情，要点是清楚地表达观察结果而不评判或评估。&lt;/li&gt;
  &lt;li&gt;接着，表达感受，例如受伤、害怕、喜悦、兴奋等。&lt;/li&gt;
  &lt;li&gt;然后，说出哪些事情导致那样的感受。&lt;/li&gt;
  &lt;li&gt;最后，提出具体的请求。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;这四要素并不是什么固定的公式它需要我们根据不同情况做出调整其关键是对这四要素的觉察而不在于使用什么字眼进行交流&quot;&gt;这四要素并不是什么固定的公式，它需要我们根据不同情况做出调整，其关键是对这四要素的觉察，而不在于使用什么字眼进行交流。&lt;/h3&gt;

&lt;h2 id=&quot;暴力的表达蒙蔽了爱&quot;&gt;暴力的表达蒙蔽了爱&lt;/h2&gt;

&lt;h3 id=&quot;我们的话语和表达方式中常常会致力于满足我们的某种愿望却倾向于忽视人的感受和需要以致彼此的疏远和伤害&quot;&gt;我们的话语和表达方式中常常会致力于满足我们的某种愿望，却倾向于忽视人的感受和需要，以致彼此的疏远和伤害。&lt;/h3&gt;

&lt;p&gt;这些都是我们说话和表达的习惯造成的问题，我们可以通过观察、记录、觉醒、改善来改变这些习惯。&lt;/p&gt;

&lt;p&gt;下面作者举例了一些我们沟通中的一些习惯性错误：&lt;/p&gt;

&lt;p&gt;1.说话的言语中附带着评判&lt;/p&gt;

&lt;p&gt;我们习惯于将人分为好人和坏人、正常或不正常、负责或不负责任、聪明或愚蠢，诸如此类的评论暗含着我们的价值观及需要。&lt;/p&gt;

&lt;h3 id=&quot;当我们以附带评判的方式提出主张时很可能会招来敌意使我们的愿望更加难以满足即使对方接受批评做出让步通常也不是心甘情愿的&quot;&gt;当我们以附带评判的方式提出主张时，很可能会招来敌意，使我们的愿望更加难以满足。即使对方接受批评，做出让步，通常也不是心甘情愿的。&lt;/h3&gt;

&lt;p&gt;每个人都有自己的看法，我们可以提出自己的价值观但不要夹杂着评判。&lt;/p&gt;

&lt;h3 id=&quot;实际上暴力的根源在于人们忽视彼此的感受与需要而将冲突归咎于对方&quot;&gt;实际上暴力的根源在于人们忽视彼此的感受与需要，而将冲突归咎于对方。&lt;/h3&gt;

&lt;p&gt;例如美国把前苏联看作是致力于摧毁美国生活的“邪恶帝国”。而前苏联则将美国看作是试图征服他们的“帝国主义压迫者”。其实双方都没有承认内心的恐惧。&lt;/p&gt;

&lt;p&gt;与他人比较也是一种评判，当你在言语或表达中夹杂着比较时，例如与他人比较身高、体型、成就、财富，无论自己还是对方都会受到很大的伤害，同时蒙蔽我们对人对己的爱。&lt;/p&gt;

&lt;p&gt;2.话语中带有回避责任的意思&lt;/p&gt;

&lt;p&gt;我们对自己的思想、情感和行动是负有责任的，但是人们却在言语和表达中常常广泛使用“不得不”、“你让我”这类短语来试图回避责任。&lt;/p&gt;

&lt;p&gt;例如：“我不得不打扫房间”、“是老板让我做”、“是学校规定所以我不得不做”、“是朋友们让我抽烟”等等。&lt;/p&gt;

&lt;h3 id=&quot;当我们以这种回避责任的方式表达时常常会让人感到我们在逃避责任是个不负责任的人缺少了爱同时这种表达方式也忽视了我们情感的内在根源&quot;&gt;当我们以这种回避责任的方式表达时，常常会让人感到我们在逃避责任，是个不负责任的人，缺少了爱。同时，这种表达方式也忽视了我们情感的内在根源。&lt;/h3&gt;

&lt;p&gt;3.强人所难&lt;/p&gt;

&lt;p&gt;我们对别人的要求往往暗含着威胁：如果不配合，他们就会受到惩罚。在社会中，这是强者常用的手段。&lt;/p&gt;

&lt;p&gt;例如作为父亲，我的职责就是管教孩子。可是我发现，我可以提出各种要求，但无法强迫孩子们按我的期待生活，实际上盛气凌人并无济于事。&lt;/p&gt;

&lt;p&gt;是的，我们可以通过惩罚来教训他们，但如果我们真的那样做了，他们迟早也会想出办法来对付我们。&lt;/p&gt;

&lt;h3 id=&quot;现实中我们大多数人使用的言语倾向于评判比较命令和指责而不是鼓励我们倾听彼此的感受和需要&quot;&gt;现实中我们大多数人使用的言语倾向于评判、比较、命令和指责，而不是鼓励我们倾听彼此的感受和需要。&lt;/h3&gt;

&lt;h3 id=&quot;更糟糕的是长期以来我们强调人性本恶以及通过教育来控制天性这导致我们对自己的感受和需要常常心存疑虑以致于不愿去体会自己的内心世界&quot;&gt;更糟糕的是长期以来，我们强调人性本恶以及通过教育来控制天性，这导致我们对自己的感受和需要常常心存疑虑，以致于不愿去体会自己的内心世界。&lt;/h3&gt;

&lt;p&gt;举个例子，对贵族、沙皇、国王来说，将臣民训练得具有奴隶般的精神状态符合他们的利益。所以“不应该”、“应该”和“不得不”这些表达方式特别适合这个目的的人。即人们越是习惯于评定是非，他们也就越倾向于追随权威，来获得正确和错误的标准。&lt;/p&gt;

&lt;h2 id=&quot;区分观察与评论&quot;&gt;区分观察与评论&lt;/h2&gt;

&lt;p&gt;第一要素，观察&lt;/p&gt;

&lt;h3 id=&quot;在沟通中我们需要仔细观察正在发生的事情并且清楚地说出观察结果如果将观察与评论混为一谈人们就倾向于听到批评并且产生逆反心理&quot;&gt;在沟通中，我们需要仔细观察正在发生的事情，并且清楚地说出观察结果。如果将观察与评论混为一谈，人们就倾向于听到批评，并且产生逆反心理。&lt;/h3&gt;

&lt;p&gt;举个例子，有人说他是一个懒惰的人，实际上只是他的行为是懒惰的；再如，有人说他是个愚蠢的孩子，实际上只是他懂的事情与我们不一样。&lt;/p&gt;

&lt;p&gt;这些带有负面标签的话语会伤害到对方。&lt;/p&gt;

&lt;p&gt;不仅负面标签会产生消极影响，正面或中性的标签也会妨碍我们全面了解一个人。&lt;/p&gt;

&lt;p&gt;例如，用“厨师”、“客服”、“清洁工”一词来定义一个人。&lt;/p&gt;

&lt;h3 id=&quot;也就是说当我们用标签定义一个人时是有局限性的有时我们会这样定义我们自己造成我们给自己的局限性&quot;&gt;也就是说，当我们用标签定义一个人时，是有局限性的。有时我们会这样定义我们自己，造成我们给自己的局限性。&lt;/h3&gt;

&lt;h3 id=&quot;哲学家克里希那穆提曾说不带评论的观察是人类智力的最高形式&quot;&gt;哲学家克里希那穆提曾说“不带评论的观察是人类智力的最高形式”。&lt;/h3&gt;

&lt;p&gt;在我们观察时，总会不自觉的夹带这些评论，例如在评论句子中，往往会出现“每次”、“曾”、“总是”、“从不”等词语。&lt;/p&gt;

&lt;p&gt;我们不主张绝对化的结论，而提倡在特定的时间和情景中进行观察，并清楚地描述观察结果。&lt;/p&gt;

&lt;h2 id=&quot;体会和表达感受&quot;&gt;体会和表达感受&lt;/h2&gt;

&lt;p&gt;第二要素，感受&lt;/p&gt;

&lt;p&gt;很多人认为感受是无关紧要的，重要的是各种权威主张的“正确思想”。于是我们被鼓励服从权威而非倾听自己，渐渐地我们习惯于考虑，“人们期待我怎么做“，而非我的感受是什么。&lt;/p&gt;

&lt;h3 id=&quot;实际上表达内心的感受不仅可以促进亲情还可以改善工作&quot;&gt;实际上表达内心的感受，不仅可以促进亲情，还可以改善工作。&lt;/h3&gt;

&lt;p&gt;作者在一次管理层协助会议上请求管理层们告诉员工“我们感到不安”，这遭到负责人的反对。于是作者问为什么不能做，主席回答说“一旦我们示弱，他们就会更加盛气凌人。”&lt;/p&gt;

&lt;p&gt;对他的回答作者并不感到意外，毕竟对许多人来说，在工作时表达情感是无法想象的事情。但最终在作者的请求下主席还是尝试将不安的感受告诉了员工，意外的是接受到感受的员工们非常支持，最后项目顺利通过。&lt;/p&gt;

&lt;h3 id=&quot;这种表达自己感受的示弱会产生积极的影响人们通常会通过我们的感受逐渐开始了解我们并且对我们所关心的事物产生兴趣&quot;&gt;这种表达自己感受的“示弱”会产生积极的影响，人们通常会通过我们的感受逐渐开始了解我们，并且对我们所关心的事物产生兴趣。&lt;/h3&gt;

&lt;h2 id=&quot;区分感受和想法&quot;&gt;区分感受和想法&lt;/h2&gt;

&lt;p&gt;在沟通中，我们需要注意区分感受和想法。&lt;/p&gt;

&lt;p&gt;当我们说“我觉得”时，常常并不是在表达感受，而是在表达想法。换成“我认为”或许更恰当。&lt;/p&gt;

&lt;p&gt;还有些词表达想法而非感受的词语，如，被抛弃、被羞辱、被虐待、被打扰、被拒绝、不受重视、被束缚、被欺负、无人理睬、得不到支持、无人赏识、被利用等等。&lt;/p&gt;

&lt;p&gt;举例，我们在说话时表达的想法而非感受的例子：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;我觉得你应该懂的更多&lt;/li&gt;
  &lt;li&gt;我觉得经常被打扰&lt;/li&gt;
  &lt;li&gt;我做的事总是得不到支持&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;实际沟通中当我们要清楚地表达感受时需要丰富的词汇因为简单的词语很难让人明白我们的实际状况&quot;&gt;实际沟通中，当我们要清楚地表达感受时，需要丰富的词汇，因为简单的词语很难让人明白我们的实际状况。&lt;/h3&gt;

&lt;p&gt;为了能更好的沟通情感，非暴力沟通主张使用具体的语言，下面举例来说哪些词语能很好的表达感受。&lt;/p&gt;

&lt;p&gt;表达满足的感受：&lt;/p&gt;

&lt;p&gt;兴奋、喜悦、欣喜、甜蜜、精力充沛、兴高采烈、感激、感动、乐观、自信、振作、振奋、开心、高兴、&lt;/p&gt;

&lt;p&gt;快乐、愉快、幸福、陶醉、满足、欣慰、心旷神怡、喜出望外、平静、自在、舒适、放松、踏实、安全、&lt;/p&gt;

&lt;p&gt;温暖、放心、无忧无虑&lt;/p&gt;

&lt;p&gt;表达没有满足的感受：&lt;/p&gt;

&lt;p&gt;害怕、担心、焦虑、忧虑、着急、紧张、心神不宁、心烦意乱、忧伤、沮丧、灰心、气馁、泄气、绝望、&lt;/p&gt;

&lt;p&gt;伤感、凄凉、悲伤、恼怒、愤怒、烦恼、苦恼、生气、厌烦、不满、不快、不耐烦、不高兴、震惊、失望、&lt;/p&gt;

&lt;p&gt;困惑、茫然、寂寞、郁闷、难过、悲观、沉重、麻木、精疲力尽、萎靡不振、疲惫不堪、昏昏欲睡、无精打采、&lt;/p&gt;

&lt;p&gt;尴尬、惭愧、内疚、妒忌、遗憾、不舒服&lt;/p&gt;

&lt;h3 id=&quot;通过建立表达感受的词汇表我们可以更清楚地表达感受从而使沟通更为顺畅&quot;&gt;通过建立表达感受的词汇表，我们可以更清楚地表达感受，从而使沟通更为顺畅。&lt;/h3&gt;

&lt;h2 id=&quot;感受的根源&quot;&gt;感受的根源&lt;/h2&gt;

&lt;p&gt;感受的根源在于我们自身，我们的需要和期待以及对他人言行的看法，导致了我们的感受。&lt;/p&gt;

&lt;p&gt;平时沟通中，我们常会听到不中听的话，通常我们有四种选择：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;认为自己犯了错&lt;/li&gt;
  &lt;li&gt;指责对方&lt;/li&gt;
  &lt;li&gt;了解我们的感受和需要&lt;/li&gt;
  &lt;li&gt;用心体会他人的感受和需要&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;反过来也是一样的，当我们遇到不安时，会去指责他人，或利用他人的内疚。这时通常采取的办法是，把自己不愉快的感受归咎于对方。&lt;/p&gt;

&lt;p&gt;例如，家长会跟孩子说“你成绩不好让爸妈很伤心”，言下之意是爸妈的不快乐与否是孩子的行为造成的。&lt;/p&gt;

&lt;p&gt;看到父母的痛苦，孩子可能会感到内疚，并因此调整行为来迎合他们。但这种调整只是为了避免内疚，而非出自对学习的热爱，长期来看会造成更加糟糕的情况。&lt;/p&gt;

&lt;h3 id=&quot;通过了解我们的需要愿望期待以及想法用心体会他人的感受和需要承认我们的感受源于自身我们就能做到不再指责他人&quot;&gt;通过了解我们的需要、愿望、期待以及想法，用心体会他人的感受和需要，承认我们的感受源于自身，我们就能做到不再指责他人。&lt;/h3&gt;

&lt;h2 id=&quot;非暴力沟通需要生命健康成长的要素&quot;&gt;非暴力沟通需要：生命健康成长的要素&lt;/h2&gt;

&lt;h3 id=&quot;批评往往暗含着期待对他人的批评实际上间接表达了我们尚未满足的需要&quot;&gt;批评往往暗含着期待，对他人的批评实际上间接表达了我们尚未满足的需要。&lt;/h3&gt;

&lt;h3 id=&quot;如果我们通过批评来提出主张人们的反应常常是申辩或反击&quot;&gt;如果我们通过批评来提出主张，人们的反应常常是申辩或反击。&lt;/h3&gt;

&lt;h3 id=&quot;反之如果我们直接说出需要其他人就较有可能作出积极的回应&quot;&gt;反之，如果我们直接说出需要，其他人就较有可能作出积极的回应。&lt;/h3&gt;

&lt;h3 id=&quot;不幸的是大多数人并不习惯从需求的角度来考虑问题特别是在不顺心的时候我们往往倾向于考虑别人有什么错&quot;&gt;不幸的是，大多数人并不习惯从需求的角度来考虑问题。特别是在不顺心的时候，我们往往倾向于考虑别人有什么错。&lt;/h3&gt;

&lt;p&gt;作者认为，只要我们开始谈论需要而不去指责对方，我们就有可能找到办法来满足双方的需要，让我们双赢。&lt;/p&gt;

&lt;p&gt;非暴力沟通把需要看作是有助于生命健康成长的要素，而不是某种具体的行为。如果我们不看重自己的需要，别人可能也不会。实际上，如果直接说出需要，获得积极回应的可能性就会增加。&lt;/p&gt;

&lt;p&gt;作者举了个例子&lt;/p&gt;

&lt;p&gt;一位朋友说自己小时候为了得到医院里赠送的小礼物，装病让妈妈陪他去看病并且动了手术，最后获得了这个小礼物，在病床时他把这个礼物递给护士看，但护士以为是送给她的于是就拿走了。&lt;/p&gt;

&lt;p&gt;这个不幸的故事告诉我们，如果一个人无法说出自己的需要，会是多么的痛苦。&lt;/p&gt;

&lt;p&gt;从“情感的奴隶”到“生活的主人”&lt;/p&gt;

&lt;p&gt;对于大多数人来说，个人成长分为三个阶段：&lt;/p&gt;

&lt;p&gt;第一阶段，情感的奴隶&lt;/p&gt;

&lt;p&gt;此时我们认为自己有义务使他人快乐。认为别人不高兴是我们的责任。因此我们特别容易把亲人看作是负担。&lt;/p&gt;

&lt;h3 id=&quot;许多人认为爱情就是牺牲自己来满足爱人的需要随着关系变得严肃他们开始认为自己有责任让情人过的开心于是爱情开始沉重起来&quot;&gt;许多人认为，爱情就是牺牲自己来满足爱人的需要。随着关系变得“严肃”，他们开始认为自己有责任让情人过的开心，于是，爱情开始沉重起来。&lt;/h3&gt;

&lt;p&gt;作者举例自己女儿玛拉的经历&lt;/p&gt;

&lt;p&gt;玛拉以前是个“有礼貌的小女孩”，对别人的要求总是百依百顺，她习惯于委屈自己来迎合他人。&lt;/p&gt;

&lt;p&gt;注意这个情况后，作者鼓励她大胆地说出心里话，当作者告诉她自己的看法时，女儿哭了。她很无奈的说，“但是，爸爸，我不想让任何人失望！”。我回答说，真诚待人比委曲求全更为可贵。如果别人感到不安，我们可以认真地倾诉，但无须责备自己。不久后女儿有了些变化，学校规定不允许穿牛仔裤，玛拉没好气的说“见鬼去吧”，作者很高兴玛拉终于能说出自己的心里话，只是她还要学着尊重他人的需要，相信这只是时间问题。&lt;/p&gt;

&lt;p&gt;第二阶段，面目可憎&lt;/p&gt;

&lt;h3 id=&quot;此时我们拒绝考虑他人的感受和需要认为牺牲自己迎合他人的代价实在太大因此拒绝考虑他们&quot;&gt;此时我们拒绝考虑他人的感受和需要。认为牺牲自己迎合他人的代价实在太大，因此拒绝考虑他们。&lt;/h3&gt;

&lt;p&gt;此时如果他人遭遇痛苦，我们可能就会无动于衷，“和我有什么关系！”。虽然不再愿意为他人负责，但我们还心存疑虑。&lt;/p&gt;

&lt;p&gt;第三阶段，生活的主人&lt;/p&gt;

&lt;h3 id=&quot;这个阶段我们乐于互助我们帮助他人是出于爱而不是出于恐惧内疚或惭愧那是自由而快乐的行为&quot;&gt;这个阶段，我们乐于互助。我们帮助他人，是出于爱，而不是出于恐惧、内疚或惭愧。那是自由而快乐的行为。&lt;/h3&gt;

&lt;p&gt;此时我们意识到，我们对自己的意愿、感受和行动负有完全的责任，但无法为他人负责。同时还认识到，我们无法牺牲他人来满足自己的需要。&lt;/p&gt;

&lt;p&gt;这篇到此，希望对大家有所帮助，我们下篇继续&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484849&amp;amp;idx=1&amp;amp;sn=3f19b8b6b3295aa952641f62aadce98b&amp;amp;chksm=fc2260b6cb55e9a0079d4f2254fd46699c6e5e345e7e92ba6fb9a15666a39aec9b62279c4de7&amp;amp;token=347679726&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(三十七) 《心流》#1 - 寻找生命的意义</title>
   <link href="http://www.luzexi.com/2021/07/25/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B037"/>
   <updated>2021-07-25T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/07/25/读书笔记37</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484840&amp;amp;idx=1&amp;amp;sn=a56725615b9e43cdb1f8848b1ef54183&amp;amp;chksm=fc2260afcb55e9b9f3ee8b37a78872623bd55ec5e8151779fa477c30fb9be9c601ad3d78b43d&amp;amp;token=1126834365&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;背景&quot;&gt;背景：&lt;/h2&gt;

&lt;p&gt;在2020年初就买了《心流》，直到2021年1月才读完第一遍，刹那间开阔了我的眼界。&lt;/p&gt;

&lt;p&gt;读完本书让我的专注力提高了一个级别，这让我非常兴奋。而这只是我读了第一遍吸收到的10%都不到结果，我想它的魔力远不止于此，于是决定好好复习总结一下书本中的内容，开启了第二遍、第三遍的读书旅程。&lt;/p&gt;

&lt;p&gt;平时我在工作、生活、交流、学习时，常常很难集中注意力，因此我带着好奇心阅读了这本书。我猜大家应该也和我一样希望自己在专注做一件事情的时候能够拥有更高的效率。&lt;/p&gt;

&lt;p&gt;本书的核心是心流的最优体验，当你的心流体验最佳时是学习效率最高的时候同时也是最幸福的时刻，里面讲到了关于学习、工作、运动、交际等方面的心流原理和技巧，下面开始详细讲解一下。&lt;/p&gt;

&lt;h2 id=&quot;正文&quot;&gt;正文：&lt;/h2&gt;

&lt;p&gt;本书的序是我所读过的书中最长的序，也是有最多名人写感悟的序，整整写了63页的序。&lt;/p&gt;

&lt;p&gt;序中写了几位高知在细品本书后的体会与感悟，这些体会与感悟给了我很多启发，同时让我对本书整体思想和逻辑有了一个大致的了解。于是本不想对序的内容进行总结的我，由于这本书的序实在太精彩，不由自主的将序也纳入了书本的内容范围，同时也侧面反映了这本书的精彩程度。&lt;/p&gt;

&lt;p&gt;作者米哈里是一位心理学家，同时他对生物学和社会学也非常关注，他认为这三大学科都是对幸福研究的重要领域。&lt;/p&gt;

&lt;p&gt;他说，动物的技巧总是能配合实际的需要，因为它们的心灵只容纳当下环境中与它们切身相关、能靠直觉判断的资讯。例如：饥饿的狮子只注意能帮助它猎到羚羊的资讯，吃饱的狮子注意力则完全集中在温暖的阳光上。&lt;/p&gt;

&lt;p&gt;人类与动物最大的差别就在于神经系统过于发达。虽然我们感知和摄取比其他动物更多的信息，这样有利于人类生存，但烦恼也更多，多知也多忧。&lt;/p&gt;

&lt;h3 id=&quot;例如金钱就困扰了我们大部分人那么金钱到底是否能让人幸福呢米哈里认为不是&quot;&gt;例如金钱就困扰了我们大部分人，那么金钱到底是否能让人幸福呢？米哈里认为不是。&lt;/h3&gt;

&lt;h3 id=&quot;虽然很多学者也有相同的看法但这其中一部分人走向了另一个极端即将幸福替代金钱将它放在最重要的位置上以前金钱的位置上他们认为幸福才是人生理当直奔的主题米哈里也否认这种判断&quot;&gt;虽然很多学者也有相同的看法，但这其中一部分人走向了另一个极端，即将幸福替代金钱，将它放在最重要的位置上（以前金钱的位置上），他们认为幸福才是人生理当直奔的主题。米哈里也否认这种判断。&lt;/h3&gt;

&lt;p&gt;事实上，幸福感通常根本不是作为目标存在于人们的追求中，它只不过是表现为目标中附带的现象。
米哈里认为，幸福是你全身心投入一桩事物，达到忘我的程度，并由此获得的内心秩序和安宁的状态。&lt;/p&gt;

&lt;p&gt;例如一位攀岩的选手，不断逼身体发挥所有的极限，直到全身隐隐作痛，然后满怀敬畏的回顾自我，回顾所做的一切，那种佩服的感觉简直无法形容。&lt;/p&gt;

&lt;h3 id=&quot;攀岩最终目的是攀岩正如写作的目的是写作一样你唯一征服的是自己的内心而不是为了到达山顶或乌托邦&quot;&gt;攀岩最终目的是攀岩，正如写作的目的是写作一样，你唯一征服的是自己的内心，而不是为了到达山顶或乌托邦。&lt;/h3&gt;

&lt;h3 id=&quot;当你全力争取胜利时其他就不那么重要了甚至连胜利本身都不重要&quot;&gt;当你全力争取胜利时，其他就不那么重要了，甚至连胜利本身都不重要。&lt;/h3&gt;

&lt;p&gt;专注的过程达到忘我的状态，虽说不是幸福的全部，但也是它很重要的一部分。&lt;/p&gt;

&lt;p&gt;练习专注的过程并不容易，我们往往要花三、五年甚至十年二十年的时间来学习剔除外部干扰、克服紧张，使自己的学习或表演能够放松、自然、快乐。可见学习快乐，并不是一件容易的事。&lt;/p&gt;

&lt;p&gt;有人认为专注某项活动精神消耗会更大，米哈里则不这么认为。&lt;/p&gt;

&lt;p&gt;他认为专注让大脑减轻了负担，心流较强的人在专注时会关闭其他资讯通道，只把注意力集中在接收闪光的刺激上。对于这种懂得如何控制意识的人而言，专注反而更轻松。&lt;/p&gt;

&lt;h3 id=&quot;专注通常伴随着挑战如果挑战不是自愿的通常无法达到心流&quot;&gt;专注通常伴随着挑战，如果挑战不是自愿的，通常无法达到心流。&lt;/h3&gt;

&lt;h3 id=&quot;如果目标太难或者太简单也同样会造成心流的消失降低幸福感和体验&quot;&gt;如果目标太难或者太简单，也同样会造成心流的消失，降低幸福感和体验。&lt;/h3&gt;

&lt;p&gt;因此首先我们要自己去寻找目标，其次要去调整目标高度，让目标有一个适合我们的难度。&lt;/p&gt;

&lt;p&gt;例如历史上贵族阶层脱离生产后，缺少了生活上的挑战，从而面临生命不能承受之轻，一部分贵族陷入物欲不能自拔，另一部分选择体育、音乐、诗歌词赋的艺术化生活方式。这种工作而非无所事事的挑战，例如攀岩、舞蹈、下棋、运动、游戏也可以造就心流。&lt;/p&gt;

&lt;p&gt;其中体育具备造就心流的最佳条件：明确的目标、即时的回馈、易学难精带来的上不封顶的挑战。&lt;/p&gt;

&lt;p&gt;它最大的功能是帮助人们控制自己：学习控制自己的身体，学习控制自己的精神，学习控制自己的注意力。&lt;/p&gt;

&lt;p&gt;这种技巧不持续练习是不会提高的，持续下去主要考的也不是耐心，而是不断发现技巧上的微妙差异，靠的是持续存在的关注点。&lt;/p&gt;

&lt;h3 id=&quot;此外书中提到了熵熵是指系统的混乱程度越混乱熵值越高反之系统内部越有规律结构越清晰熵值就越低&quot;&gt;此外，书中提到了熵，熵是指系统的混乱程度，越混乱，熵值越高。反之，系统内部越有规律，结构越清晰，熵值就越低。&lt;/h3&gt;

&lt;p&gt;例如水变成冰时熵值就很低，变成水蒸气后熵值就很大。&lt;/p&gt;

&lt;p&gt;熵延伸到我们的人身上也适用，人如果没有节制，没有经过训练，大脑常常会处于混乱状态，虽然你能意识到的可能只有少数几个念头，但在潜意识里却有很多念头在互相冲突，它们在争夺你的注意力，抢夺你的大脑的控制权，并且试图引导、影响你往南辕北辙的方向走。但是如果你进入了心流状态，那就不一样了，虽然你的大脑仍然在高速运转，但是所有这些念头都是非常有规律、有秩序的，就像一支有纪律的军队，被井井有条地组织了起来，高效地去完成一个任务。&lt;/p&gt;

&lt;h3 id=&quot;心流就是这样一个降熵的过程把原来混乱的心序重新组合成有规律的集体&quot;&gt;心流就是这样一个降熵的过程，把原来混乱的心序重新组合成有规律的集体。&lt;/h3&gt;

&lt;p&gt;当心熵比较高时一片混乱，大脑的做功能力很低，很多心理能量都浪费在内耗上了。一旦进入心流状态，心理能量就围绕着同一个主题组织起来，向同一个方向高效输出，此时人的表现也最好。&lt;/p&gt;

&lt;h3 id=&quot;如果一个人经常经历心流他的心理就会被训练得越来越有秩序以后进入心流就越来越容易即使平时不在心流状态下也不会像一般人那样心猿意马了&quot;&gt;如果一个人经常经历心流，他的心理就会被训练得越来越有秩序，以后进入心流就越来越容易，即使平时不在心流状态下，也不会像一般人那样心猿意马了。&lt;/h3&gt;

&lt;p&gt;降低心熵最典型的例子就是冥想，在冥想中，你摒除杂念，心灵澄净，如一道清澈的心流。&lt;/p&gt;

&lt;p&gt;经过长期练习之后，哪怕不在冥想之中，你的心灵也会比常人更平静，遇到意外变故时能更快地集中注意力，也就是你的心熵整体降低了。&lt;/p&gt;

&lt;p&gt;例如高僧冥想多年后才能达到波澜不惊，而我们也同样需要反复练习才能更容易进入心流，但这一切都是值得的。&lt;/p&gt;

&lt;p&gt;一些虔诚的基督徒也有同样的修为，当他们遇到生活的苦难时，也会难过，但在祷告和与亲友交流后会坦然接受这个苦难，“上帝爱我，他的安排一定自有他的深意”。&lt;/p&gt;

&lt;p&gt;这样一次次苦难中经历信念的动摇和重固的挑战，最终形成更加坚定的信仰，从而更好地指导自己生活的方方面面，这样全神贯注而又平安喜乐，就等于把整个人生过成了一场大心流。&lt;/p&gt;

&lt;p&gt;至于普通人怎么把一生过成心流体验，米哈里的建议是，首先要找一个终生的目标，其次不要害怕其复杂性，这就是对你人生意义的挑战。&lt;/p&gt;

&lt;p&gt;你可以用“行动式生活”和“反省式生活”相结合来逐步应对挑战实现你的目标，只要个人目标与宇宙心流汇合，意义的问题也就迎刃而解了。&lt;/p&gt;

&lt;p&gt;生命的意义会随着你的心流而升级，从简单舒适，到社会价值，从个人发展，到个人与社会的重新整合，不断升级，挑战也滚滚而来，生命就在这些不断挑战和升级中找到意义。&lt;/p&gt;

&lt;h3 id=&quot;其实降熵的过程有高下心流也有高下原本的混沌越多整合进去的元素越复杂这个心流就越伟大&quot;&gt;其实降熵的过程有高下，心流也有高下，原本的混沌越多，整合进去的元素越复杂，这个心流就越伟大。&lt;/h3&gt;

&lt;h3 id=&quot;也就是说那些能够整合无比复杂的人生找到人生意义整合无比复杂的世界形成自己的价值观的人就会有最大的心流&quot;&gt;也就是说，那些能够整合无比复杂的人生、找到人生意义，整合无比复杂的世界、形成自己的价值观的人，就会有最大的心流。&lt;/h3&gt;

&lt;p&gt;当然，过犹不及，米哈里有时也过分强调了集中注意力的好处。新的研究表明“注意力不集中”也是一种对健康至关重要的状态。在注意力不集中的情况下，我们的大脑大部分时间处于默认模式，这种状态是发散思维和创新的必要条件。如果能在专注一段时候后放松一下准备下一段专注，这样的持续效率会更高。&lt;/p&gt;

&lt;p&gt;说到底心流只是一个方法，其背后的逻辑是，“通过锻炼控制自己的意识，去获得真正的幸福”。&lt;/p&gt;

&lt;h3 id=&quot;如果你直接追求的并不是幸福而是把自己变得更复杂在这个变复杂的过程中你就能找到乐趣此时这个状态就是幸福&quot;&gt;如果你直接追求的并不是幸福，而是把自己变得更复杂，在这个变复杂的过程中，你就能找到乐趣，此时这个状态就是幸福。&lt;/h3&gt;

&lt;h3 id=&quot;人生要的不是最后终点的结果而是每时每刻点点滴滴成长的过程其实成长本身就是我们的目的&quot;&gt;人生要的不是最后终点的结果，而是每时每刻点点滴滴成长的过程，其实成长本身就是我们的目的。&lt;/h3&gt;

&lt;p&gt;据我所知，90%的家长在孩子学习的时候设定了过重的目标，且常常过于重视技巧和速成，使得学习成为了孩子的负担，这样孩子们自然无法从中得到快乐。
如果能把精神集中在过程上，比如优美的画面、欢快的节奏，更多的及时反馈，小目标达成的快乐，这样孩子们的体验就会更好，学习的劲头也更足。&lt;/p&gt;

&lt;p&gt;说了这么多，其实只有学会控制心灵的人才能决定自己的生活品质，而如果具备了这种能力，也就相当于接近幸福的境界了。
快乐与否，取决于内心是否和谐，就得从掌握意识着手，学习从生活中创造乐趣，多加练习就能掌握，化无聊为有趣，也就兴趣盎然了。&lt;/p&gt;

&lt;h3 id=&quot;心流的这个态度是真正的以人为本当我们做事的时候如果不在乎结果能不能给自己带来多大的利益而是专注于做这件事本身就能从中获巨大的乐趣&quot;&gt;心流的这个态度是真正的以人为本，当我们做事的时候如果不在乎结果能不能给自己带来多大的利益，而是专注于做这件事本身，就能从中获巨大的乐趣。&lt;/h3&gt;

&lt;p&gt;最后祝大家通过能心流找到人生的意义，我们下篇继续《心流》&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484840&amp;amp;idx=1&amp;amp;sn=a56725615b9e43cdb1f8848b1ef54183&amp;amp;chksm=fc2260afcb55e9b9f3ee8b37a78872623bd55ec5e8151779fa477c30fb9be9c601ad3d78b43d&amp;amp;token=1126834365&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(三十六) 《张爱玲传记》成就富家才女的落魄家族</title>
   <link href="http://www.luzexi.com/2021/07/17/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B036"/>
   <updated>2021-07-17T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/07/17/读书笔记36</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484831&amp;amp;idx=1&amp;amp;sn=844da82b7868b494c0d71c1713a813f5&amp;amp;chksm=fc226098cb55e98e222bcbeff2646b4fed2bbc6eb2416090a75aece54d9064258674990ccfc0&amp;amp;token=1401123908&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;背景&quot;&gt;背景：&lt;/h1&gt;

&lt;p&gt;这是一本张爱玲的他传《张爱玲-民国临水照花人》，读书前我问过自己，为什么要读这本书。&lt;/p&gt;

&lt;p&gt;大概是因为我有两个女儿的关系，使得我迫切想去了解女性的世界，包括她们的成长、她们的心理、她们的困惑、她们的烦恼等。
因此我陆陆续续读完了国民四大才女的传记，《林徽因》，《陆小曼》、《张爱玲》、《三毛》传记，我希望通过这四个民国才女来了解女性在成长和发展过程中需要经历的困惑与煎熬。&lt;/p&gt;

&lt;h3 id=&quot;直到有了女儿我才体会到女性与男性有着完全不同的路径和体验也因此在这些年读了很多关于女性书籍后更深有体会&quot;&gt;直到有了女儿我才体会到女性与男性有着完全不同的路径和体验，也因此在这些年读了很多关于女性书籍后更深有体会。&lt;/h3&gt;

&lt;p&gt;《张爱玲-民国临水照花人》这本书着实也给了我很多震撼，为此我不得不给自己留下一篇回顾性的文章， 以记录下读完此书后给我带来的感受。&lt;/p&gt;

&lt;h1 id=&quot;正文&quot;&gt;正文：&lt;/h1&gt;

&lt;p&gt;张爱玲这一生有很多成就，同时也伴随着很多人对她的负面评价，但无论怎样她都没有被这个世界改变，她一直在活出自己，一直都是那个有才但又羞涩的少女。&lt;/p&gt;

&lt;h3 id=&quot;张爱玲祖上有许多积累她的祖父外祖父曾外祖父都是当时有名的人物比如曾外祖父就是李鸿章外祖父是张佩纶科举中榜并后来受军机处重用位于重要职务&quot;&gt;张爱玲祖上有许多积累，她的祖父、外祖父、曾外祖父都是当时有名的人物，比如曾外祖父就是李鸿章，外祖父是张佩纶科举中榜并后来受军机处重用位于重要职务。&lt;/h3&gt;

&lt;h3 id=&quot;整个家族从父亲张延重开始没落和当时许多高官的子孙一样由于祖辈只留下遗产而没有留下好的教养和精神力量因此寻花问柳和赌博是这些富家子弟的常态&quot;&gt;整个家族从父亲张延重开始没落，和当时许多高官的子孙一样，由于祖辈只留下遗产而没有留下好的教养和精神力量，因此寻花问柳和赌博是这些富家子弟的常态。&lt;/h3&gt;

&lt;h3 id=&quot;父亲张延重与长江水师提督孙女黄逸梵结婚后很快就生下女儿张爱玲后来他们又生了一个弟弟张子静&quot;&gt;父亲张延重与长江水师提督孙女黄逸梵结婚后很快就生下女儿张爱玲，后来他们又生了一个弟弟张子静。&lt;/h3&gt;

&lt;p&gt;母亲和父亲从小的家庭氛围完全不同，父亲更多的是老派作风，性格刻板拘谨，母亲则接受过开明的新式教育，风情漂亮，思想新潮，因此两人格格不入。&lt;/p&gt;

&lt;p&gt;其实张爱玲从小就在学习氛围浓重的家庭长大，父母很小开始就亲自教她读书写字，而且张爱玲也很喜欢读书，三岁就会背唐诗成了父亲的掌上明珠。&lt;/p&gt;

&lt;p&gt;好景不长，在那个动荡的时代每个家庭都处于风雨摇摆之中，虽然张家家底比较殷实没有那么辛苦，但父亲与母亲的矛盾愈演愈烈。&lt;/p&gt;

&lt;h3 id=&quot;父亲希望母亲能对自己言听计从以弥补自己多年来所承受的束缚母亲则为刚逃脱父权压制又落入夫权专横的家庭感到绝望&quot;&gt;父亲希望母亲能对自己言听计从，以弥补自己多年来所承受的束缚；母亲则为刚逃脱父权压制又落入夫权专横的家庭感到绝望。&lt;/h3&gt;

&lt;p&gt;失落和失望中父亲开始堕落，出入风月场所，吸鸦片，还带姨太太回家。母亲则喜欢洋派的派对并到处旅游，东南亚、日本、欧洲，对父亲的行为很少在意。&lt;/p&gt;

&lt;h3 id=&quot;其实最初他们两彼此也曾试图为了靠近对方改变过自己的价值观守旧的父亲为了能追随妻子的思想看起了洋书一心向往新思想的黄逸梵读起了唐诗但最终还是以失败告终&quot;&gt;其实最初他们两彼此也曾试图为了靠近对方改变过自己的价值观，守旧的父亲为了能追随妻子的思想看起了洋书，一心向往新思想的黄逸梵读起了唐诗，但最终还是以失败告终。&lt;/h3&gt;

&lt;p&gt;母亲对张爱玲的教育一直放在心上，她希望女儿得到新思想的熏陶，所以她精心为女儿安排了钢琴、英文和绘画等课程，这为张爱玲营造出了良好的艺术氛围。&lt;/p&gt;

&lt;h3 id=&quot;最终父亲与母亲还是劳燕分飞了离婚前母亲告诉父亲自己心已如木头一般父亲听了心潮涌动想到对方屡次退步已退无可退不如海阔天空放各自一条生路&quot;&gt;最终父亲与母亲还是劳燕分飞了，离婚前母亲告诉父亲自己心已如木头一般，父亲听了心潮涌动，想到对方屡次退步已退无可退不如海阔天空，放各自一条生路。&lt;/h3&gt;

&lt;p&gt;离婚后母亲过的风生水起，旅游、派对、崇尚欧美风格的时尚着装，奢华洋派的生活再一次影响了张爱玲的人生观。&lt;/p&gt;

&lt;p&gt;这些所有的经历都会使人成长，无论最后定型为哪一种性格，都是自然而然的事情，习惯了分离，就会觉得一个人往前走是种常态。&lt;/p&gt;

&lt;p&gt;在这个乱世里体会过不同的感受，张爱玲小小年纪就学会了将喜怒哀乐都转换为坚强。&lt;/p&gt;

&lt;p&gt;因此她一生对任何事情和情感的漠然令人疑惑，殊不知，张爱玲在幼时就已经修炼成风轻云淡的心态，既然要来的早晚要来，不如看淡，走好眼前的路，才是当下最好的生活。&lt;/p&gt;

&lt;p&gt;父亲对女儿的新思想教育不太支持，所以每次张爱玲向父亲要上课的学费时，父亲都是冷漠以待，因为他无法容忍妻子的影子影射到女儿的身上。&lt;/p&gt;

&lt;p&gt;父亲后来娶了新妻子，继母对张爱玲很讨厌，也很讨厌前任妻子，这让原本曾一度父女相依为命的温情在继母的影响下落到不堪的地步。&lt;/p&gt;

&lt;p&gt;最后张爱玲离家出走去了亲身母亲家里，但其实亲生母亲家中并不富有，只能维持现状，无法给张爱玲更好的环境。&lt;/p&gt;

&lt;p&gt;所以张爱玲很小就知道，唯有知识才能改变命运，当别人还在懵懂迷茫的时候，她早已开始谋划未来。&lt;/p&gt;

&lt;p&gt;当然张爱玲与母亲也有很大的隔阂，由于张爱玲从习惯了小富家小姐的环境，懒散又不太会家务，出门连路都不认得，因此让母亲很不满意，时常斥责张爱玲。&lt;/p&gt;

&lt;h3 id=&quot;饱尝巨大落差的张爱玲心里却是介意的觉得母亲是个自私的女人为了享乐还要挥霍有限的金钱对待自己的女儿却要百般计较&quot;&gt;饱尝巨大落差的张爱玲，心里却是介意的，觉得母亲是个自私的女人，为了享乐还要挥霍有限的金钱，对待自己的女儿却要百般计较。&lt;/h3&gt;

&lt;h3 id=&quot;张爱玲所经历的畸形家庭裂变和身边亲人的冷漠自私尤其是在金钱上遭受过的窘迫和难堪都深深地影响了她的人生观&quot;&gt;张爱玲所经历的畸形家庭裂变，和身边亲人的冷漠自私，尤其是在金钱上遭受过的窘迫和难堪，都深深地影响了她的人生观。&lt;/h3&gt;

&lt;p&gt;后来张爱玲在母亲的帮助下苦读，考上了香港大学。当时香港刚好在战火中，一边读书一边听炮火的经历让张爱玲刻骨铭心。&lt;/p&gt;

&lt;p&gt;张爱玲的姑姑是张爱玲生命中重要的亲人，母亲崇尚自由很早就去了国外，留下姑姑与她相伴。&lt;/p&gt;

&lt;h3 id=&quot;姑姑的品位与学识也深深影响着张爱玲在张爱玲离开上海时姑姑正在英商洋行做事后来听了别人建议投资股票失败又遇时局动荡辞职到电台工作了一段时间后又去了大光明戏院做翻译后来供职于外资企业生活还算过的悠闲&quot;&gt;姑姑的品位与学识也深深影响着张爱玲，在张爱玲离开上海时，姑姑正在英商洋行做事，后来听了别人建议投资股票失败又遇时局动荡，辞职到电台工作了一段时间后，又去了大光明戏院做翻译，后来供职于外资企业，生活还算过的悠闲。&lt;/h3&gt;

&lt;p&gt;在大学里张爱玲仍然需要一边读书一边赚取生活费，写稿的紧张节奏令张爱玲逐渐吃不消，于是她决定退学而正是走上写作的道路。&lt;/p&gt;

&lt;h3 id=&quot;在专注创作后不久张爱玲体会到了中文小说的谋篇布局畅意抒怀的创作更让她淋漓尽致发挥自己的才华曾向往的林语堂方式的英文写作远不如她书写生活中的市井生活和身边人的人生百态&quot;&gt;在专注创作后不久，张爱玲体会到了中文小说的谋篇布局，畅意抒怀的创作更让她淋漓尽致发挥自己的才华，曾向往的林语堂方式的英文写作，远不如她书写生活中的市井生活，和身边人的人生百态。&lt;/h3&gt;

&lt;h3 id=&quot;从小读红楼梦长大的张爱玲又在港大将西方作家的作品统统学过将文学精粹炼进自己的文字里形成独树一帜的风格&quot;&gt;从小读《红楼梦》长大的张爱玲，又在港大将西方作家的作品统统学过，将文学精粹炼进自己的文字里，形成独树一帜的风格。&lt;/h3&gt;

&lt;p&gt;张爱玲感情生活中，最著名也是最有争议的一段感情就是与“国民汉奸”胡兰成。&lt;/p&gt;

&lt;p&gt;胡兰成从桂系军阀到汪精卫伪政府一直走的是邪魔歪道的路线，但对于涉世不深的张爱玲来说她并没有意识到胡兰成的问题所在。&lt;/p&gt;

&lt;p&gt;汪精卫以重金拉拢胡兰成于羽翼之下，胡兰成成为他们的御用文人，汪精卫伪政府成立后胡兰成上任宣传部部长。&lt;/p&gt;

&lt;p&gt;当时的张爱玲在感情上相当于一张白纸，而胡兰成则早已经历了风风雨雨无论情场还是职场都是老手了，加之胡兰成在文学上有一些造诣让张爱玲很快就沦陷了。&lt;/p&gt;

&lt;p&gt;胡兰成一介寒士爬到如今的位置，那些上流社会的美好始终在他心中梦一般的存在着，不曾想到这一切在张爱玲心里竟不值一提。&lt;/p&gt;

&lt;p&gt;高傲的张爱玲自有她的万丈光芒，无须以自己的家世来衬托，她的不屑与他的渴望形成了鲜明的讽刺。&lt;/p&gt;

&lt;p&gt;但是胡兰成的细腻还是将张爱玲的骄傲清冷融化，令她产生出多年来从而有过的归属感。他与她之间共生出的默契是张爱玲从未感受过的强烈洪流。&lt;/p&gt;

&lt;p&gt;坊间的俗语，流氓不可怕，就怕流氓有文化，来形容胡兰成很是贴切。而常年孤僻生活和写作的幻想空间，使得张爱玲脱离了现实，她常常拿小说里的经验来判断胡兰成对待她的真伪。&lt;/p&gt;

&lt;p&gt;胡兰成在张爱玲结束前就有三位妻子，结婚后又与女同事出轨，在日本战败后逃离中又与好友家中的寡妇联结，最后张爱玲忍无可忍分手道别。&lt;/p&gt;

&lt;p&gt;可惜已经太晚，张爱玲已经背负了汉奸之妻的身份，出门也要伪装自己以免被人认出。&lt;/p&gt;

&lt;h3 id=&quot;母亲回国后看到张爱玲的样子认为她沉浸在小城市里裹足不前应该去往更远的地方看看世界提高眼界&quot;&gt;母亲回国后看到张爱玲的样子，认为她沉浸在小城市里裹足不前，应该去往更远的地方看看世界，提高眼界。&lt;/h3&gt;

&lt;h3 id=&quot;此时母亲自身的能力非常出色在国外做尼赫鲁两姐妹的社交秘书因此对于沉浸在小情小调文字中的张爱玲不屑一顾甚至有几分怒其不争的愤慨&quot;&gt;此时母亲自身的能力非常出色，在国外做尼赫鲁两姐妹的社交秘书，因此对于沉浸在小情小调文字中的张爱玲不屑一顾，甚至有几分怒其不争的愤慨。&lt;/h3&gt;
&lt;p&gt;那个年轻风情文雅的母亲早已消失不见，站在眼前的是一个强势又自我的老太婆。&lt;/p&gt;

&lt;p&gt;张爱玲太过了解母亲黄逸梵，从小与父母有隔阂的张爱玲羞于喊出“妈妈”这般亲昵的字眼，母亲当年给她心理造成的阴影，让她一直以“婶婶”相称。&lt;/p&gt;

&lt;h3 id=&quot;同时在母亲回来看女儿的时刻张爱玲提出了要用二两金子还清母女之情母亲得知女儿的意思后潸然泪下后婉拒了女儿又踏上了出国的旅程&quot;&gt;同时在母亲回来看女儿的时刻，张爱玲提出了要用二两金子还清母女之情，母亲得知女儿的意思后潸然泪下后婉拒了女儿，又踏上了出国的旅程。&lt;/h3&gt;

&lt;h3 id=&quot;张爱玲一生未育当然也无法体会母亲年轻时候所承受的一切而只是执拗地刺激着给了她生命的母亲&quot;&gt;张爱玲一生未育，当然也无法体会母亲年轻时候所承受的一切，而只是执拗地刺激着给了她生命的母亲。&lt;/h3&gt;

&lt;h3 id=&quot;其实母女两人都是极端自我的女子不同的是黄逸梵不仅学历高交际手腕也高明还有非常远大的追求张爱玲则大学没有上完因此没有大学毕业证更不擅长为人处世&quot;&gt;其实母女两人都是极端自我的女子，不同的是，黄逸梵不仅学历高，交际手腕也高明，还有非常远大的追求；张爱玲则大学没有上完因此没有大学毕业证，更不擅长为人处世。&lt;/h3&gt;

&lt;p&gt;与胡兰成离婚后，张爱玲用剧本拍了几部电影非常火热，还与导演桑弧热恋，桑弧是个非常擅长人情世故的人，这是因为在电影行业里必定要学会处世才能混得风生水起。&lt;/p&gt;

&lt;h3 id=&quot;但谈到结婚生子时张爱玲是抗拒的她往前看似乎能看到未来的路上有一个身影重叠着自己年少时的孤独和恐惧她不想重蹈母亲的覆辙她恨母亲的自私但同时她也是与母亲一样的女人没有能力去爱护幼小的生命&quot;&gt;但谈到结婚生子时，张爱玲是抗拒的，她往前看似乎能看到未来的路上有一个身影，重叠着自己年少时的孤独和恐惧，她不想重蹈母亲的覆辙，她恨母亲的自私，但同时她也是与母亲一样的女人，没有能力去爱护幼小的生命。&lt;/h3&gt;

&lt;p&gt;与导演桑弧分手后张爱玲回到了香港大学，继续漂泊继续流浪，漂泊对她与母亲来说似乎已经成为了一种信仰。&lt;/p&gt;

&lt;h3 id=&quot;在香港她认识了邝文美邝文美相貌不仅秀丽端庄又是个学识有见地的才女她的温柔像一条薄厚相宜的毯子柔软的话语覆盖过来让张爱玲四肢百骸都放松下来像遇见多年未见的幼年伙伴怎么说话也说不完&quot;&gt;在香港她认识了邝文美，邝文美相貌不仅秀丽端庄，又是个学识、有见地的才女，她的温柔像一条薄厚相宜的毯子，柔软的话语覆盖过来，让张爱玲四肢百骸都放松下来，像遇见多年未见的幼年伙伴怎么说话也说不完。&lt;/h3&gt;

&lt;h3 id=&quot;在那个人人都想出人头地的时代邝文美始终以家庭为重将家庭和事业经营得有声有色她放弃了荣华富贵的机遇不攀附权贵却愿意和远在异乡的张爱玲结为知己&quot;&gt;在那个人人都想出人头地的时代，邝文美始终以家庭为重，将家庭和事业经营得有声有色。她放弃了荣华富贵的机遇，不攀附权贵，却愿意和远在异乡的张爱玲结为知己。&lt;/h3&gt;

&lt;h3 id=&quot;邝文美在生活里应付自如的涵养恰到好处地弥补了张爱玲不理俗事的弱点邝文美本就对张爱玲非常欣赏又加之她深谙人性特点在这样的友谊中她不但能理解张爱玲天真的幼稚亦能包容她不明世故的观点&quot;&gt;邝文美在生活里应付自如的涵养，恰到好处地弥补了张爱玲不理俗事的弱点。邝文美本就对张爱玲非常欣赏，又加之她深谙人性特点，在这样的友谊中，她不但能理解张爱玲天真的幼稚，亦能包容她不明世故的观点。&lt;/h3&gt;

&lt;p&gt;1953年美国放开移民政策，张爱玲在麦卡锡的担保下离开了香港去往美国。在那里她邂逅了共度半生的美国作家普德南.赖雅。&lt;/p&gt;

&lt;p&gt;赖雅是位德国移民后裔，在哈弗大学读到文艺硕士毕业后，从事英文教室工作。在艺术方面天赋极高，不仅文学作品优秀，摄影也非常出色。先后担任欧洲战地记者，自由撰稿人，欧洲采访知名作家等，经历非常丰富。&lt;/p&gt;

&lt;p&gt;但此时赖雅已经65岁了，而张爱玲才36岁，她曾坦言：一向对于年纪大一点的人感到亲切，对于和自己年纪差不多的人则有点看不起。&lt;/p&gt;

&lt;p&gt;在美国几年后，收到了母亲病重的来信，而张爱玲只写了封信，寄上100美元的支票安慰母亲，让她好好治病。&lt;/p&gt;

&lt;p&gt;母亲在她心里一直都是个强大的女人，她在追求自由的道路上，是个快乐的女神，怎么会被疾病轻易击垮？多年来在两人之间的隔膜已经深成了跨不过去的沟壑。&lt;/p&gt;

&lt;h3 id=&quot;张爱玲同时也流掉了与丈夫赖雅的孩子这与张爱玲从小缺乏家庭关爱不无关系&quot;&gt;张爱玲同时也流掉了与丈夫赖雅的孩子，这与张爱玲从小缺乏家庭关爱不无关系。&lt;/h3&gt;

&lt;h3 id=&quot;母亲黄逸梵手术后不久就离开了人世张爱玲默然承受着这个结果没能满足母亲最后的愿望张爱玲为此大病一场&quot;&gt;母亲黄逸梵手术后不久，就离开了人世，张爱玲默然承受着这个结果，没能满足母亲最后的愿望，张爱玲为此大病一场。&lt;/h3&gt;

&lt;h3 id=&quot;母亲走了为女儿留下一箱古董黄逸梵在病体难抗的绝境里完全可以卖掉古董来治病一向有见地的她并没有这么做而是留给了女人张爱玲这是母亲对张爱玲最后的眷恋&quot;&gt;母亲走了为女儿留下一箱古董，黄逸梵在病体难抗的绝境里，完全可以卖掉古董来治病，一向有见地的她并没有这么做，而是留给了女人张爱玲，这是母亲对张爱玲最后的眷恋。&lt;/h3&gt;

&lt;p&gt;黄逸梵从张爱玲四岁起就离开了上海前往国外，从此爱上了独自旅行。&lt;/p&gt;

&lt;h3 id=&quot;她跑到欧洲的学校学绘画又去了马来西亚的华侨学校教书在瑞士的阿尔卑斯山上这个三寸金莲的美丽女子勇敢从山顶往下飞滑在游历过程中所邂逅的情人无不是浪漫多情又绅士的外国男子这让她品尝到了爱情的真谛从家庭角度讲黄逸梵并不是个称职的母亲但却对张爱玲的成长有着很深的影响面对封建旧式家庭的规矩她毅然站出来拒绝女儿缠足还自作主张教导女儿英语钢琴从而也使得张爱玲熏陶到良好的西方文化&quot;&gt;她跑到欧洲的学校学绘画，又去了马来西亚的华侨学校教书，在瑞士的阿尔卑斯山上，这个三寸金莲的美丽女子勇敢从山顶往下飞滑。在游历过程中所邂逅的情人，无不是浪漫多情又绅士的外国男子，这让她品尝到了爱情的真谛。从家庭角度讲，黄逸梵并不是个称职的母亲，但却对张爱玲的成长有着很深的影响，面对封建旧式家庭的规矩，她毅然站出来拒绝女儿缠足，还自作主张教导女儿英语、钢琴，从而也使得张爱玲熏陶到良好的西方文化。&lt;/h3&gt;

&lt;h3 id=&quot;虽然母女俩不常见面张爱玲在心里将母亲当作偶像张爱玲对事业的追求和我行我素的风格以及爱情上的观念甚至是后来漂泊海外的流浪旅途都与母亲有着惊人的吻合她们是相爱相杀的母女黄逸梵的洒脱是留给张爱玲的华丽背影也是投射在她心灵深处的阴影&quot;&gt;虽然母女俩不常见面，张爱玲在心里将母亲当作偶像，张爱玲对事业的追求和我行我素的风格，以及爱情上的观念，甚至是后来漂泊海外的流浪旅途都与母亲有着惊人的吻合，她们是相爱相杀的母女，黄逸梵的洒脱是留给张爱玲的华丽背影，也是投射在她心灵深处的阴影。&lt;/h3&gt;

&lt;p&gt;1967年赖雅去世，张爱玲心情降到冰点。但天亮了，张爱玲还要将写作进行下去，生命的真相有时候没必要去追根究底，因为宿命的安排或是性格上的原因，也许是一个人最简单的寄托。&lt;/p&gt;

&lt;h3 id=&quot;张爱玲的弟弟张子静在新中国成立后在上海浦东一所小学任教先后又更换了几所学校于1986年正式退休搬到江苏路的一个14平方米小屋居住这是父亲败光家产后遗留给他的最后遗产&quot;&gt;张爱玲的弟弟张子静在新中国成立后在上海浦东一所小学任教，先后又更换了几所学校，于1986年正式退休搬到江苏路的一个14平方米小屋居住，这是父亲败光家产后遗留给他的最后遗产。&lt;/h3&gt;

&lt;p&gt;1953年父亲张延重肺病去世，继母所依赖房屋被政府回收后的定息也在文革爆发后停止发放。1970年继母双目失明，弟弟给她请了个保姆，1986年离世。&lt;/p&gt;

&lt;p&gt;1983年弟弟联系到张爱玲并劝姐姐回国，但张爱玲拒绝了弟弟的请求，并告诉他能保持书信就好。&lt;/p&gt;

&lt;h3 id=&quot;到了晚年张爱玲再读红楼梦时在书中读出了整个张氏大家族与贾府相似的命运从而让她对世事产生出悲凉之感&quot;&gt;到了晚年，张爱玲再读《红楼梦》时在书中读出了整个张氏大家族与贾府相似的命运，从而让她对世事产生出悲凉之感。&lt;/h3&gt;

&lt;h3 id=&quot;她年轻时争名夺利的性情如风过已是了无痕往事已过去多年她对人生有了新的认识不再是浮于表面的浅薄&quot;&gt;她年轻时争名夺利的性情，如风过，已是了无痕。往事已过去多年，她对人生有了新的认识，不再是浮于表面的浅薄。&lt;/h3&gt;

&lt;h3 id=&quot;虽是个老太太举止言谈间却完全是一个自闭的少女状态但神清里有说不出的柔和恬静遇事的时候也已经能够泰然自若&quot;&gt;虽是个老太太，举止言谈间却完全是一个自闭的少女状态，但神清里有说不出的柔和恬静，遇事的时候也已经能够泰然自若。&lt;/h3&gt;

&lt;p&gt;晚年整理回忆录《对照记》时，选了100多幅她与家人、朋友的照片，母亲、姑姑、闺蜜和她的风华正茂，都定格在光影里。&lt;/p&gt;

&lt;p&gt;其中最为经典的是那张，最具张爱玲风范，她的脸庞轻轻上扬，眼睛望向侧上方，穿着缎子做的高领短袖，如生在世间的一枝兰，高傲贵气又出尘。&lt;/p&gt;

&lt;p&gt;1995年9月8日，张爱玲去世在自己的公寓里，旁边的书桌上是一叠铺开的稿纸和一支未合上的笔。&lt;/p&gt;

&lt;h3 id=&quot;张爱玲在遗嘱中把所有的遗产都留给了邝文美&quot;&gt;张爱玲在遗嘱中把所有的遗产都留给了邝文美。&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484831&amp;amp;idx=1&amp;amp;sn=844da82b7868b494c0d71c1713a813f5&amp;amp;chksm=fc226098cb55e98e222bcbeff2646b4fed2bbc6eb2416090a75aece54d9064258674990ccfc0&amp;amp;token=1401123908&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(三十五) 《如何高效阅读 - 下》</title>
   <link href="http://www.luzexi.com/2021/07/12/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B035"/>
   <updated>2021-07-12T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/07/12/读书笔记35</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484825&amp;amp;idx=1&amp;amp;sn=72548c4cb8aaf9961b3ea77cc0905ba6&amp;amp;chksm=fc22609ecb55e988c328a3a7b71c9614dd6e5d4b0df369ffd8d5275603ab7afd3ec9e01b036a&amp;amp;token=1401123908&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;背景&quot;&gt;背景：&lt;/h1&gt;

&lt;p&gt;与前两本《如何阅读》、《如何阅读一本书》相比《如何有效阅读一本书》这本书更加注重阅读实用技巧，从选书、读书、笔记、重读四个方面详细讲解了作者自己在实践过程中的经验。&lt;/p&gt;

&lt;h1 id=&quot;正文&quot;&gt;正文：&lt;/h1&gt;

&lt;p&gt;全书11万字分为五个部分，选书、读书、笔记、重读、实用技巧。&lt;/p&gt;

&lt;h1 id=&quot;如何选书&quot;&gt;如何选书？&lt;/h1&gt;

&lt;h3 id=&quot;书有很多种好坏也是天差地别坏书不仅会浪费我们很多时间还会灌输我们错误的观念因此如何选书如何选好书就成提高读书效率的关键&quot;&gt;书有很多种，好坏也是天差地别，坏书不仅会浪费我们很多时间，还会灌输我们错误的观念。因此如何选书，如何选好书就成提高读书效率的关键。&lt;/h3&gt;

&lt;p&gt;作者提到选书最重要的是主动选择而不是被动选择，主动去做筛选会比被动的选择遇到好书的概率要大的多。&lt;/p&gt;

&lt;p&gt;有这四个途径可以提高选到好书的概率：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;生活中大众认可的经典书籍&lt;/li&gt;
  &lt;li&gt;自己看过的好书中提到的书&lt;/li&gt;
  &lt;li&gt;告知人书评中提到的好书&lt;/li&gt;
  &lt;li&gt;知识品位高的朋友提到的好书&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;选书的过程中，我们依然要保持警惕，时时刻刻主动选择而不是被动被推荐无脑接受。&lt;/p&gt;

&lt;p&gt;有时候看到好书不一定要买，其实各领域的好书有很多，如果不是你当下需要的书，即使是好书买来也没有用，堆积在那里会成为你的心里负担。&lt;/p&gt;

&lt;h3 id=&quot;我们应该尽量做到当真正需要的时候才购买买回来尽量立即看起来而不是认为是好书就买了堆积在那里给自己造成心里负担其实买了没看也会给自己造成挫败感&quot;&gt;我们应该尽量做到，当真正需要的时候才购买，买回来尽量立即看起来，而不是认为是好书就买了堆积在那里给自己造成心里负担，其实买了没看也会给自己造成挫败感。&lt;/h3&gt;

&lt;p&gt;作者说除了选好书，我们也需要一些能与自己长期共处的书，当失落时、走投无路时，那本书会成为我们的心灵支柱。&lt;/p&gt;

&lt;h1 id=&quot;如何读书&quot;&gt;如何读书？&lt;/h1&gt;

&lt;p&gt;提高读书效率的一个重要指标就是养成好习惯，这个习惯包括：坚持每日读书的习惯，坚持做读书笔记的习惯，坚持重读巩固复习的习惯。&lt;/p&gt;

&lt;p&gt;其中做笔记的方法和技巧，以及重读的技巧特别重要，这两个话题在下面的内容中会详细介绍。&lt;/p&gt;

&lt;h3 id=&quot;前面说要读好书但其实现实中坚持只选好书只读好书可能不太现实我们可以张弛有度地贯彻好书要细细研读其他书则粗读略读原则这样会让读书效率更高&quot;&gt;前面说要读好书，但其实现实中坚持“只选好书，只读好书”可能不太现实，我们可以张弛有度地贯彻“好书要细细研读，其他书则粗读略读”原则，这样会让读书效率更高。&lt;/h3&gt;

&lt;h3 id=&quot;其次在读书过程中积极的跳读和略读并在比较难理解的部分换上低速齿轮认真品读保持读书张弛有度的节奏可以把更多精力集中在好书的精华内容上这样读书效率会更高&quot;&gt;其次在读书过程中，积极的“跳读”和“略读”，并在比较难理解的部分换上低速齿轮认真品读，保持读书张弛有度的节奏，可以把更多精力集中在好书的精华内容上，这样读书效率会更高。&lt;/h3&gt;

&lt;h3 id=&quot;最后读书的目的是用自己的方式去学习而不是模仿别人的方法因此在读书学习过程中用自己的方法吸收学到的知识才是最重要的再根据书中提到的技巧加以改进这样的学习效率会更高&quot;&gt;最后读书的目的是用自己的方式去学习，而不是模仿别人的方法。因此在读书学习过程中，用自己的方法吸收学到的知识才是最重要的，再根据书中提到的技巧加以改进，这样的学习效率会更高。&lt;/h3&gt;

&lt;p&gt;我们可以看到整个读书流程为：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;主动选择好书&lt;/li&gt;
  &lt;li&gt;只购买真正要看的书&lt;/li&gt;
  &lt;li&gt;读书并记录&lt;/li&gt;
  &lt;li&gt;读后总结与评价&lt;/li&gt;
  &lt;li&gt;复习与巩固&lt;/li&gt;
&lt;/ol&gt;

&lt;h1 id=&quot;为什么要做笔记&quot;&gt;为什么要做笔记？&lt;/h1&gt;

&lt;p&gt;我们要学的东西实在太多，而我们的人脑记忆力又非常有限，无法在同一时间记住太多的知识和信息。&lt;/p&gt;

&lt;h3 id=&quot;学了忘忘了学如此往复效率太差笔记就给予了我们快速恢复记忆的途径它不但可以帮助我们快速恢复记忆还可以帮助我们加深记忆让知识记忆的保存时间更长一些&quot;&gt;学了忘，忘了学，如此往复效率太差。笔记就给予了我们快速恢复记忆的途径，它不但可以帮助我们快速恢复记忆，还可以帮助我们加深记忆，让知识记忆的保存时间更长一些。&lt;/h3&gt;

&lt;p&gt;我们在做笔记时就相当于在做归纳和重读，特别是当我们要把笔记写成文章发表出去时，我们会不自觉的更加仔细、小心并且反复琢磨知识要点。&lt;/p&gt;

&lt;p&gt;即使不发表读书文章，你也会发现，在读完一本书后其实记不住多少内容，只有对书中内容做出反应，进行主动思考，才能真正掌握这本书的内容。&lt;/p&gt;

&lt;p&gt;读书笔记就是这种帮助我们升华的工具，可以让读书活动完成从生搬硬套到独创思维的飞跃。&lt;/p&gt;

&lt;p&gt;其实日常生活中说的所谓“理解精髓”就够了只是个美好的愿望，你不把整个知识理顺了并记录下来，即使长期读书的人也会在很长时间后把看过的书忘得一干二净。&lt;/p&gt;

&lt;h3 id=&quot;如果只是这样读过就等于没有读过如果只是用眼睛看过而没有记在脑子里读再多的书也都会忘记&quot;&gt;如果只是这样读过，就等于没有读过。如果只是用眼睛看过，而没有记在脑子里，读再多的书也都会忘记。&lt;/h3&gt;

&lt;h3 id=&quot;读完每本书如果都能有扎实的收获那么每个月读多少本书一本书花几小时读完的能力在这个目标面前都是苍白无力的&quot;&gt;读完每本书如果都能有扎实的收获，那么每个月读多少本书，一本书花几小时读完的能力，在这个目标面前都是苍白无力的。&lt;/h3&gt;

&lt;h3 id=&quot;认真选择每一本满足自己需求的书然后真挚地与每一本书对话努力从书中学到知识这才是明智的做法&quot;&gt;认真选择每一本满足自己需求的书，然后真挚地与每一本书对话，努力从书中学到知识，这才是明智的做法。&lt;/h3&gt;

&lt;p&gt;当达到这样读书后扎实收获的目标后，再去训练更快的读书速度其实也为时不晚。&lt;/p&gt;

&lt;p&gt;当你以思想输出为前提去读书时，思想输入的质量也会提升，亲手写文章的好处比你想象的大很多。&lt;/p&gt;

&lt;p&gt;当你一遍遍思考如何写文章时，不由自主的就多读了几遍书，尤其是对没有读懂的部分，在不断思考和不断重读过程中，你对书本知识的理解也进入到了更深层次的境界。&lt;/p&gt;

&lt;p&gt;如果你以输出为目的去读书，那么读书的质量和效率会很高。&lt;/p&gt;

&lt;h3 id=&quot;如果读书的目标是写读书笔记那么读书效率会比随便读一读要高出很多如果你以发表自己的理解和总结性文章为目标那么你的读书效率会更高如果你以输出讲座为目标那么你的读书效率会特别高&quot;&gt;如果读书的目标是写读书笔记，那么读书效率会比随便读一读要高出很多，如果你以发表自己的理解和总结性文章为目标，那么你的读书效率会更高，如果你以输出讲座为目标，那么你的读书效率会特别高。&lt;/h3&gt;

&lt;h3 id=&quot;道理很简单因为当我们知道自己要在别人面前表达自己的想法后我们才会去调查那些我们从未在意过的出处资料重新审视自己的观点建立起系统的知识体系这时你会发现这个补充学习的过程就像从零学起一样需要费不少功夫而能够正视这些辛苦工作的人最后都不再对知识一知半解了&quot;&gt;道理很简单因为当我们知道自己要在别人面前表达自己的想法后，我们才会去调查那些我们从未在意过的出处、资料，重新审视自己的观点，建立起系统的知识体系。这时你会发现，这个补充学习的过程就像从零学起一样，需要费不少功夫。而能够正视这些辛苦工作的人，最后都不再对知识一知半解了。&lt;/h3&gt;

&lt;h3 id=&quot;因此掌握再多信息如果不去积极输出也无法形成知识体系&quot;&gt;因此掌握再多信息，如果不去积极输出，也无法形成知识体系。&lt;/h3&gt;

&lt;h1 id=&quot;如何做笔记&quot;&gt;如何做笔记？&lt;/h1&gt;

&lt;p&gt;有时你可能感觉到没什么可写的，但只要你拿起笔来随便写两句，说不定就有惊喜等着你。&lt;/p&gt;

&lt;p&gt;读书笔记三步骤：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;读书&lt;/li&gt;
  &lt;li&gt;划重点&lt;/li&gt;
  &lt;li&gt;写读后总结&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;读书阶段，要在书上做一些简单的记号，为写读书笔记做准备。&lt;/p&gt;

&lt;p&gt;这里作者介绍了自己的读书方法：&lt;/p&gt;

&lt;p&gt;第一遍通读时，把有价值的页折起来，&lt;/p&gt;

&lt;p&gt;第二遍重读时只读折起来的部分，并且认为重要的划线或标记，&lt;/p&gt;

&lt;p&gt;第三遍再重读时只读划线和标记部分，认为精华的部分摘抄到笔记里。&lt;/p&gt;

&lt;p&gt;最后把摘抄的精华加上自己的理解把它们写成文章。&lt;/p&gt;

&lt;p&gt;作者自己发明了葱郁火锅笔记法，它有两部分组成：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;摘抄下重要语句&lt;/li&gt;
  &lt;li&gt;下方写上自己的感受&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;即重要的内容摘抄下来，下面写下自己的感想和评论。（摘抄和评论交替进行，趁着自己印象还鲜明写下自己的感想）&lt;/p&gt;

&lt;p&gt;作者认为，自己当下的想法才是最重要的，摘抄这一段文章的原因和自己当时的想法才是日后值得参考的贵重资料。&lt;/p&gt;

&lt;h1 id=&quot;如何重读与巩固&quot;&gt;如何重读与巩固？&lt;/h1&gt;

&lt;p&gt;读完一本书后的时间越长，在读书时产生的环境变化就会越模糊，你就越能冷静地看待书中的观点。&lt;/p&gt;

&lt;p&gt;因此不要在意一本书读完已经搁置了多久，再次重读时会有别样的风味。&lt;/p&gt;

&lt;p&gt;重读笔记的时间点：
睡觉前、洗完澡后、晚饭后、思绪进入死胡同时，可以是碎片时间也最好是安静的环境。&lt;/p&gt;

&lt;p&gt;重读习惯养成：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;量化重读频率，每周一次或者三天一次。&lt;/li&gt;
  &lt;li&gt;多种重读方式交替进行&lt;/li&gt;
  &lt;li&gt;重读后写复习总结并发布到博客上&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;多种重读方式：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;想要简单回顾时，重读读书笔记&lt;/li&gt;
  &lt;li&gt;想回忆起更多时，参考书中的重点段落&lt;/li&gt;
  &lt;li&gt;想从头开始看起时，重读原书&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;每一次的复习总结都是对自己知识的再回顾和再理解把这些复习总结放到博客上同样可以作为思想输出的一部分而且每次复习总结后都会让自己对这部分知识理解的更加透彻记忆也更加久远&quot;&gt;每一次的复习总结都是对自己知识的再回顾和再理解，把这些复习总结放到博客上同样可以作为思想输出的一部分，而且每次复习总结后都会让自己对这部分知识理解的更加透彻，记忆也更加久远。&lt;/h3&gt;

&lt;h1 id=&quot;吸收知识的重要手段主题阅读&quot;&gt;吸收知识的重要手段主题阅读&lt;/h1&gt;

&lt;h3 id=&quot;同一个主题的选出三本以上的书同时阅读或者依次阅读读完记录要点做笔记再把三本笔记合起来写一篇文章这篇文章就相当于是让三个专家围绕某个问题给自己提意见让他们进行讨论而我则思考并听取意见&quot;&gt;同一个主题的选出三本以上的书同时阅读或者依次阅读，读完记录要点，做笔记，再把三本笔记合起来写一篇文章，这篇文章就相当于是让三个专家围绕某个问题给自己提意见，让他们进行讨论而我则思考并听取意见。&lt;/h3&gt;

&lt;p&gt;经过整理和总结后，渐渐的得出一些自己的结论，这些结论是由三个专家共同讨论得出的，这样的结论比较客观且符合当下的情况。&lt;/p&gt;

&lt;h3 id=&quot;让读书百遍更轻松的方法就是阅读读书笔记通过读书笔记缩短复习时间提高记忆恢复速度以笔记内容为线索回忆整个读书过程达到快速恢复记忆的目的&quot;&gt;让读书百遍更轻松的方法就是阅读读书笔记，通过读书笔记缩短复习时间、提高记忆恢复速度，以笔记内容为线索回忆整个读书过程，达到快速恢复记忆的目的。&lt;/h3&gt;

&lt;h1 id=&quot;读书实用技巧&quot;&gt;读书实用技巧&lt;/h1&gt;

&lt;p&gt;作者列举了20条读书实用技巧，这里我筛选了一部分因为有些确实没什么用处，也是结合我实际阅读学习过程中的经验：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;读书过程中多用参考书，地图、图鉴、统计数据、百科全书、年表、词典等帮助自己加深理解书中内容。&lt;/li&gt;
  &lt;li&gt;随机阅读，偶尔阅读一些自己从来没有接触过领域的书籍，打破你的常规思维，有时候这种方式会给你一种豁然开朗的感觉。&lt;/li&gt;
  &lt;li&gt;由浅入深的吃透难懂的书，太难的书会让我们有强烈的挫败感，不如先从入门版、图解版、漫画版、精编版开始阅读，逐步熟悉、理解、渗透，最后再去阅读原著能收获更多的成就感和自信。&lt;/li&gt;
  &lt;li&gt;把书份分类堆放，未阅读的、阅读过的、已经做过笔记的、已经写好总结文章，并且分类摆放，这样会给自己一个方向，随着时间推移读完的和做完笔记的书堆放的会越来越多，会有更多的成就感，也激励自己阅读更多的书。&lt;/li&gt;
  &lt;li&gt;把经典名著放在枕头边，这些书比较难度，我们可以睡觉前看两页慢慢读，反复读几年也非常好，因为这些书是经得起考验的，读的次数阅读读的时间越久，我们从中吸取的知识就越多。&lt;/li&gt;
  &lt;li&gt;同时阅读几本书，为了减少自己在读书时的枯燥感，三本书的领域应该是不一样的，当我们觉得一本书读的很痛苦时就可以换另外一本读，从而增加愉悦感保持心灵舒畅。&lt;/li&gt;
  &lt;li&gt;在办公桌和家里的每个角落都放满书，给自己一个身边满是书的氛围，同时当我们想看书的时候就可以随时抓到一本，不给自己找更多没有书看的理由。&lt;/li&gt;
  &lt;li&gt;去掉外封面让读书变得轻松，有多书的外封面有自己的宣传设计，这些外封面在读书的时候很不舒服，不如去掉它们给自己一个轻松的书。&lt;/li&gt;
  &lt;li&gt;用边撕边读的方式阅读杂志，并不一定要从头到尾顺序读，读完的就撕下来扔进垃圾桶。&lt;/li&gt;
  &lt;li&gt;偶尔的音频、视频读书也是一种读书的一种方式，当读书累的时候可以换一下轻松的方式阅读，特别读不熟悉的领域时这种轻松的方式可能会让我们更加容易的跨过这道门槛。&lt;/li&gt;
  &lt;li&gt;买一支你喜欢的钢笔，读书笔记时一直用它，这会带动我们写文章的热情，会有总想用这支笔写点什么感觉，同时调节钢笔出水、吸墨、擦拭等也能激发我们的写作热情。&lt;/li&gt;
  &lt;li&gt;把自己信奉的名言贴在显眼的位置，让自己更有长远的目标和行动指南。&lt;/li&gt;
  &lt;li&gt;从摘抄开始写作，写文章时时常会遇到瓶颈，我们可以尝试先把喜欢的文章抄写下来，抄写的同时自己想写的东西会从脑中涌现出来。&lt;/li&gt;
  &lt;li&gt;写好的文章打印出来，这样更方便我们随手翻阅，也更容易帮助我们重组知识创建出新的点子。&lt;/li&gt;
  &lt;li&gt;记录读书的时间过程，在书的首页上，记下自己的名字、购买日期、阅读结束日期、标记结束日期、完成读书标记日期，不但让自己的读书过程更清晰，也让这本书完完全全归属了我们。&lt;/li&gt;
  &lt;li&gt;经常整理书籍摆放归类，经常整理自己的读书笔记，会让自己在读书习惯，以及复习巩固时有更好的提升。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484825&amp;amp;idx=1&amp;amp;sn=72548c4cb8aaf9961b3ea77cc0905ba6&amp;amp;chksm=fc22609ecb55e988c328a3a7b71c9614dd6e5d4b0df369ffd8d5275603ab7afd3ec9e01b036a&amp;amp;token=1401123908&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(三十四) 《C++ Primer》#6</title>
   <link href="http://www.luzexi.com/2021/07/11/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B034"/>
   <updated>2021-07-11T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/07/11/读书笔记34</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484819&amp;amp;idx=1&amp;amp;sn=229a9bb2eb6ca5fd7322c8b85bae6d8f&amp;amp;chksm=fc226094cb55e98229f537079c784bcf16f42579928fd034b46a69a953f6ddab28f1b4cf3c1c&amp;amp;token=1401123908&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h1 id=&quot;背景&quot;&gt;背景：&lt;/h1&gt;

&lt;p&gt;我为什么要重学C++？第一是巩固核心，软件编程有三大核心，语言、操作系统、框架设计，语言首当其冲，核心能力大都看不见摸不着只有你自己知道的东西。第二是将分散的知识串联起来，这样就能产生新的知识和新的创意。第三是当我们在巩固时创造出了新的知识，那么这些新的知识和旧的知识将同时变成智慧融合到身体中。&lt;/p&gt;

&lt;p&gt;本系列基于《C++ Primer》学习，由于重点放在“重学”，我略过了滚瓜烂熟的部分，挑出以前常忽略的部分，以及记忆没有那么深刻的部分，特别是那些重要的但没有上心的部分。&lt;/p&gt;

&lt;h1 id=&quot;正文&quot;&gt;正文：&lt;/h1&gt;

&lt;p&gt;本文是全书总结的最后部分，在总结的同时我又重读了一遍整本书，对各个知识点的理解又加深了许多，希望这些回顾对各位都有所帮助。&lt;/p&gt;

&lt;h3 id=&quot;模版编程&quot;&gt;模版编程&lt;/h3&gt;

&lt;p&gt;模版有两种，一种是类型模版参数，另一种是非类型模版参数。&lt;/p&gt;

&lt;p&gt;两种类型模版都是由编译器生成的代码，当两个不同的数据类型使用同一个模版时，会在模版实例化时生成的两份不同的代码。例如：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;template&amp;lt;typename T&amp;gt;
T compare(const T v1, const T v2)
{
    return v1 - v2;
}

count &amp;lt;&amp;lt; compare(1, 0) &amp;lt;&amp;lt; endl; // 编译器会生成 int compare(const int , const int)
count &amp;lt;&amp;lt; compare(1.3F, 2.3F) &amp;lt;&amp;lt;endl; // 编译器会生成 int compare(const float, const float)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;编译器发现有两种数据类型分别调用了模版函数compare，此时编译器会生成两个版本的compare，应用在Class模版也是同样的道理。&lt;/p&gt;

&lt;p&gt;另一种是非类型模版，意思是参数为指定常量，例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;template&amp;lt;unsigned N, unsigned M&amp;gt;
int compare(const char (&amp;amp;p1)[N], const char (&amp;amp;p2)[M])
{
    return strcmp(p1, p2);
}

compare(&quot;hi&quot;,&quot;mon&quot;); // 编译器会生成 int compare(const char (&amp;amp;p1)[3], const char (&amp;amp;p2)[4]);
compare(&quot;hello&quot;, &quot;world&quot;); // 编译器会生成 int compare(const char(&amp;amp;p1)[6], const char (&amp;amp;p2)[6]);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;非类型参数是一个常量，而且必须是常量或者常量表达式，编译器会根据不同的常量生成不同类型的代码，应用在Class模版上也是同样的道理。&lt;/p&gt;

&lt;p&gt;编译器生成模版代码，当且仅当编译器检测到模版被使用时才发生，即如果一个成员函数没有被使用，则它不会被编译器实例化。&lt;/p&gt;

&lt;p&gt;另外编译器生成模版代码会分三个阶段，第一个阶段是检查语法，第二个阶段是检查模版调用的参数匹配，第三个阶段才是模版实例化，因此链接时发生错误在模版编程时是常有的事。&lt;/p&gt;

&lt;p&gt;在新标准中C++可以通过显示实例化来明确指定某个类或函数实例化。例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;extern template declaration; // 显式实例化申明
template declaration; // 显式实例化定义
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当编译器遇到extern模版显式实例化声明时，表明程序会在其他地方有该程序的实例化。
和普通模版实例化不同的是，如果一个类定义了模版显式实例化，则该模版的所有成员包括内联成员函数都会被实例化。&lt;/p&gt;

&lt;p&gt;此外在调用中如果有需要，我们也可以显式指定模版参数类型。例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;template&amp;lt;tyepname T1, typename T2, typename T3&amp;gt;
T1 sum(T2, T3);

long long var1 = sum(11,22L); // 编译错误
long long var2 = sum&amp;lt;long long&amp;gt;(22, 33L); // 显式指定返回值为long long
long long var3 = sum&amp;lt;long long, int, float&amp;gt;(23, 34F); // 显式指定返回值和参数分别为 long long, int, float
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上述示例告诉我们，如果返回值使用了模版类型，则最好通过显式指定的方式来指定形参类型。&lt;/p&gt;

&lt;h3 id=&quot;理解右值引用stdmove和stdforward&quot;&gt;理解右值引用、std::move和std::forward&lt;/h3&gt;

&lt;p&gt;可以简单理解为，一个&amp;amp;为左值引用，两个&amp;amp;为右值引用，右值引用有些特殊，它转为转移数据而存在，即当右值引用赋值后，原值的内存数据将失效。&lt;/p&gt;

&lt;p&gt;通过右值引用可以做到数据转移的功能，例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;void f(int v1, int &amp;amp;v2)
{
    count &amp;lt;&amp;lt; v1 &amp;lt;&amp;lt; &quot; &quot; &amp;lt;&amp;lt; ++v2 &amp;lt;&amp;lt; endl;
}

template&amp;lt;typename F, typename T1, typename T2&amp;gt;
void flip1(F f, T1 t1, T2 t2)
{
    f(t2, t1);
}

template&amp;lt;typename F, typename T1, typename T2&amp;gt;
void flip2(F f, T1 &amp;amp;&amp;amp;t1, T2 &amp;amp;&amp;amp;t2)
{
    f(t2, t1);
}

int i = 0;
f(42, i); // i变量被改变
flip1(f, i, 42); // i变量没有被改变
flip2(f, i, 42); // i变量被改变
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述中filp1与flip2的差别是，filp2使用了右值引用，将实参转移到了形参上，进而在f中可以改变i本身的变量。&lt;/p&gt;

&lt;p&gt;模版可以接受右值引用参数，std::move的定义就是模版右值引用定义：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;template&amp;lt;typename T&amp;gt;
typename remove_reference&amp;lt;T&amp;gt;::type&amp;amp;&amp;amp; move(T&amp;amp;&amp;amp; t)
{
    return static_cast&amp;lt;typename remove_reference&amp;lt;T&amp;gt;::type&amp;amp;&amp;amp;&amp;gt;(t);
}

move函数参数T&amp;amp;&amp;amp;是一个指向模版类型的右值引用。通过引用折叠，参数可以与任何类型的实参匹配。
string s1(&quot;hi&quot;), s2;

s2 = std::move(string(&quot;bye&quot;)); // 实例化为 string&amp;amp;&amp;amp; move(string &amp;amp;&amp;amp;t)
s2 = std::move(s1); // 实例化为 string&amp;amp;&amp;amp; move(string &amp;amp;t)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;其实这两个模版实例化成了不同的函数，根本原因是右值引用可折叠，导致的结果是：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;如果一个函数参数是一个指向模版类型参数的右值引用（如，T&amp;amp;&amp;amp;）则它可以被绑定到一个左值。&lt;/li&gt;
  &lt;li&gt;如果实参是一个左值，则推断出的模版实参类型将是一个左值引用，且函数参数将被实例化为一个（普通）左值引用参数（T&amp;amp;）。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;标准库中std::forward可以帮助我们返回实参类型的右值引用，即std::forward&lt;T&gt;的返回类型是T&amp;amp;&amp;amp;。例如：&lt;/T&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;template&amp;lt;type F, typename T1, typename T2&amp;gt;
void flip(F f, T1 &amp;amp;&amp;amp;t1, T2 &amp;amp;&amp;amp;t2)
{
    f(std::forward&amp;lt;T2&amp;gt;(t2), std::forward&amp;lt;T1&amp;gt;(t1));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;模版重载函数与可变参数模版&quot;&gt;模版重载函数与可变参数模版&lt;/h3&gt;

&lt;p&gt;模版重载函数在匹配规则上有些小规则，当有多个重载模版对一个调用提供同样好的匹配时，应选择最特例化的版本。&lt;/p&gt;

&lt;p&gt;对于一个调用，如果一个非函数模版与一个函数模版提供同样好的匹配，则选择非模版版本。&lt;/p&gt;

&lt;p&gt;可变参数模版中，编译器会根据识别到的函数参数类型生成多个不同类型形参的函数。&lt;/p&gt;

&lt;p&gt;可变参数模版可以与sizeof…运算符一起使用，sizeof…运算符也是通过预编译生成的模版函数，返回的是类型数目或者参数数目。&lt;/p&gt;

&lt;p&gt;例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;template&amp;lt;typename T, typename... Args&amp;gt;
ostream &amp;amp;print(ostream &amp;amp;os, const T &amp;amp;t, const Args&amp;amp;... rest)
{
    os &amp;lt;&amp;lt; t &amp;lt;&amp;lt; &quot;,&quot;;
    return print(os, rest...); // 预编译会将函数展开成类似print(os, i, s, 42)
}

string debug_rep(char *p)
{
    return string(p);
}

template&amp;lt;typename... Args&amp;gt;
ostream &amp;amp;errorMsg(ostream &amp;amp;os, const Args&amp;amp;... rest)
{
    return print(os, debug_rep(rest)...); // 预编译会将函数展开成类似print(os, debug_rep(i), debug_rep(s), debug_rep(42));
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;编译器会在预编译时对可变参数展开，因此上述函数在调用展开后类似于 print(os, i, s, 42) 和 print(os, debug_rep(i), debug_rep(s), debug_rep(42))。&lt;/p&gt;

&lt;h3 id=&quot;模版特例化&quot;&gt;模版特例化&lt;/h3&gt;

&lt;p&gt;有时候我们不希望使用模版但又想跟模版的使用方式一样，此时就可以用一个特例化模版来独立定义模版中的一个或者多个参数指定为特定类型。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;template&amp;lt;typename T, typename U&amp;gt; int compare(const T&amp;amp;, const U&amp;amp;);  // 原模版

template&amp;lt;&amp;gt; int compare(const char* const &amp;amp;p1, const char* const &amp;amp;p2); // 全特例化

template&amp;lt;typename T&amp;gt; int compare(const T&amp;amp;, const char* const &amp;amp;p2); // 部分特例化

int compare(const int const &amp;amp;var1, const int const &amp;amp;var2);   //  普通函数

compare(3F,5F); // 调用源模版
compare(&quot;HI&quot;, &quot;HELLO&quot;); // 调用全特例化
compare(3F,&quot;HELLO&quot;); // 调用部分特例化
compare(3,5); // 调用普通函数
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当定义函数模版的特例化版本时，我们本质上接管了编译器的工作。即我们为原模版的一个特殊实例提供了定义。&lt;/p&gt;

&lt;p&gt;重要的是要弄清楚：一个特例化版本本质上是一个实例，而非函数名的一个重载版本，因此特例化不影响函数匹配。&lt;/p&gt;

&lt;p&gt;注意，当一个非模版函数提供与函数模版同样好的匹配时，编译器会选择非模版版本。&lt;/p&gt;

&lt;p&gt;另外特例化也可以应用到类定义上，类上的特例化也同样支持全特例化和部分特例化，例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;template&amp;lt;class T, class U&amp;gt; class testClass { // 原类
    public：
    T var1;
    U var2;
    void Func();
}

template&amp;lt;&amp;gt; class testClass&amp;lt;int, double&amp;gt; { // 全特例化
    public：
    int var1;
    double var2;
    void Func();
}

template&amp;lt;T&amp;gt; class testClass&amp;lt;T, double&amp;gt; { // 部分特例化
    public：
    T var1;
    double var2;
    void Func();
}

template&amp;lt;class T, class U&amp;gt; struct testClass { // 对函数成员特例化
    public：
    T var1;
    U var2;
    template&amp;lt;&amp;gt;
    void Func&amp;lt;int, double&amp;gt;(); // 成员函数特例化
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;类也可以全特例化和部分特例化，其中成员函数特例化时必须全特例化，不支持部分特例化。&lt;/p&gt;

&lt;h3 id=&quot;标准库中的特殊类型&quot;&gt;标准库中的特殊类型&lt;/h3&gt;

&lt;p&gt;tuple类似pair的模版，不过tuple可以有任意数量的成员，例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;tuple&amp;lt;T1, T2, T3...Tn&amp;gt; t; // 一个tuple变量可以有n个成员
T2 item = get&amp;lt;i&amp;gt;(t); // 用索引值获取成员
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;tuple的一个常见用途是从函数返回多值，当使用tuple作为多值返回值时，它相当于是一个可以方便临时构造的数据结构。&lt;/p&gt;

&lt;p&gt;bitset能使得二进制运算的使用更为容易，能处理超过整数大小的位集合。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;bitset&amp;lt;n&amp;gt; b; // b有n位，每一位均为0。
bool item = b[i]; // 访问b中pos处位置的值
b.set(27); // 第27个位置设置为1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;正则表达式regex类，这里不多说了，正则表达式网上说明太多。&lt;/p&gt;

&lt;p&gt;标准库中的rand随机数主要解决两类问题，第一是范围内随机，第二是非均匀分布。因此标准库提供了随机数引擎Engine，通过种子来锚定伪随机数。&lt;/p&gt;

&lt;h3 id=&quot;常用程序工具&quot;&gt;常用程序工具&lt;/h3&gt;

&lt;p&gt;异常处理流程通常是从一处抛出异常，不断沿着函数调用链寻找异常匹配，如果没有找到则调用标准库函数terminate终止程序执行。&lt;/p&gt;

&lt;p&gt;其中一条catch语句可以通过重新抛出操作将异常传递给另外一个catch语句，函数也可以通过声明noexcept或throw来承诺不会抛出异常。&lt;/p&gt;

&lt;p&gt;新标准中引入了内联命名空间，内联命名空间中的名字可以被外层命名空间直接使用。&lt;/p&gt;

&lt;p&gt;多重继承，指从多个直接基类中派生类的能力。多重继承的派生类继承了所有父类的属性。&lt;/p&gt;

&lt;p&gt;多重继承的派生类的内存模型其实就是类属性的叠加，不同父类之间通过内存叠加来实现多重继承。&lt;/p&gt;

&lt;p&gt;在构造时按继承顺序调用构造函数，析构时的调用顺序则刚好相反。&lt;/p&gt;

&lt;h3 id=&quot;运行时类型识别rttirun-time-type-identification&quot;&gt;运行时类型识别（RTTI，run-time type identification）&lt;/h3&gt;

&lt;p&gt;RTTI功能由两个运算符实现：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;typeid 运算符，用于返回表达式的类型&lt;/li&gt;
  &lt;li&gt;dynamic_cast 运算符，用于将指针或引用安全地转换为派生类指针或引用&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;我们知道不包含任何虚函数的类在内存模型中是没有虚表的，而虚表是运行时识别的重要关键。&lt;/p&gt;

&lt;p&gt;其实两个运算符都是针对虚函数实现的，其原理就是派生类中有虚表，虚表中有指向type_info的指针和虚函数指针。&lt;/p&gt;

&lt;p&gt;typeid通过虚表取得type_info返回给程序，dynamic_cast则通过比较目标类中的type_info和执行对象虚表中的type_info确定是否可以安全转换。&lt;/p&gt;

&lt;p&gt;有趣的一点是，typeid也可以用于静态类（即没有任何虚函数的类），此时获取的type_info则是通过编译器生成的，因为在编译阶段就能识别该对象类型。&lt;/p&gt;

&lt;h3 id=&quot;类成员指针&quot;&gt;类成员指针&lt;/h3&gt;

&lt;p&gt;与普通指针不同的是类成员指针指向的是成员而非对象。&lt;/p&gt;

&lt;p&gt;类成员指针分为数据成员指针和成员函数指针。&lt;/p&gt;

&lt;p&gt;数据成员指针指向的是数据成员，成员函数指针则指向成员函数，例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;class testClass
{
    public:
    string content;
    char * getChar();
}

const string testClass::*pdata = &amp;amp;testClass::content; // pdata为成员指针
const string testClass::*pfunc = &amp;amp;testClass::getChar; //pfunc为成员函数指针

testClass myObj; // 实例对象
testClass *pObj = &amp;amp;myObj; // 指针

string s = myObj.*pdata; // 用成员指针获取实例成员数据
string ss = myObj-&amp;gt;*pdata; //  用成员指针获取指针成员数据

char *c = (myObj.*pfunc)();
char *cc = (myObj-&amp;gt;*pfunc)();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上述例子中我们可以清楚的看到pdata，pfunc是testClass成员指针，通过声明testClass + 类型来指定成员指针，用成员指针可以调用或者取得成员数据。&lt;/p&gt;

&lt;p&gt;通过这种成员指针的方式，我们可以将类成员的函数放入一个函数表中以方便逻辑使用，例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;class testClass
{
    public:
    using Action = char* (testClass::*)();
    char* getChar1();
    char* getChar2();
    char* getChar3();
}

testClass::Action funcMenu[] = { &amp;amp;testClass::getChar1, &amp;amp;testClass::getChar2, &amp;amp;testClass::getChar3 };
testClass obj;
for(int i = 0; i&amp;lt;3 ; i++)
{
    char * c = (obj.*(funcMenu[i]))();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;类成员指针与普通函数指针不同之处在于成员指针并不是一个可调用对象，因此它不支持函数调用运算符。&lt;/p&gt;

&lt;p&gt;这时我们可以用funciton和bind将类成员函数指针封装成可调用对象，这样可以让成员函数指针运用的更加灵活。例如：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;function&amp;lt;bool (const string&amp;amp;)&amp;gt; fcn = &amp;amp;string::empty;
find_if(svec.begin(), svec.end(), fcn);
auto it = find_if(svec.begin(), svec.end(), bind(&amp;amp;string::empty, _1));
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;联合union&quot;&gt;联合union&lt;/h3&gt;

&lt;p&gt;一个union可以有多数据成员，其空间大小为最大的那个数据成员，因为在任意时刻只有一个数据成员有效，当我们给某个成员赋值后，其他成员就变成了未定义状态。&lt;/p&gt;

&lt;p&gt;union可以有自己的构造函数和析构函数，但不能有虚函数不能作为基类更不能派生。&lt;/p&gt;

&lt;p&gt;有时我们需要最终union中存储了什么类型的值，因此通常会自己定义个独立的类对象，通过这个对象来判别union当前类型。&lt;/p&gt;

&lt;h3 id=&quot;其他特性&quot;&gt;其他特性&lt;/h3&gt;

&lt;p&gt;位域，明确了变量的二进制位数，它必须是整数，当一个程序需要向其他程序或者硬件设备传递二进制数据时通常会使用位域，因为设备之间的编译环境不同。&lt;/p&gt;

&lt;p&gt;取地址运算符（&amp;amp;）不能作用于位域，因此任何指针都无法指向类的位域。&lt;/p&gt;

&lt;p&gt;它声明的方式为成名名字之后紧跟冒号和常量，例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;class testClass
{
    public:
    int mode: 2; // mode 占2位
    int var1: 4: // var1 占4位
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;volatile，会告诉编译器阻止这个对象优化。&lt;/p&gt;

&lt;p&gt;由于不同设备之间存在差异，因此在移植或不同环境时会有所改变，因此我们需要使用volatile阻止编译器在当前环境下做编译优化，例如：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;volatile int var; // 阻止优化变量
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;volatile用在如下的几个地方：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;中断服务程序中修改的供其它程序检测的变量需要加volatile；&lt;/li&gt;
  &lt;li&gt;多任务环境下各任务间共享的标志应该加volatile；&lt;/li&gt;
  &lt;li&gt;存储器映射的硬件寄存器通常也要加volatile说明，因为每次对它的读写都可能由不同意义；&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;链接指示extern “C”，意味着我们声明了另一个语言的编译器，从而使得我们在编译时让编译器遵守另一个语言的规则（其他兼容例如extern “Ada”，extern “FORTRAN”等）&lt;/p&gt;

&lt;p&gt;所有在链接指示里的内容都被指向该语言编写的链接，例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;extern &quot;C&quot;
{
    #include &amp;lt;string.h&amp;gt; // 操作c风格字符串的c函数
}

extern &quot;C&quot; void (*pf)(int); // 当pf被调用时，编译器认定pf为C函数
(*pf)(2);

extern &quot;C&quot; typedef void FC(int); // 定义一个C函数
void f2(FC *); // 将C函数指针作为C++函数的形参
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们可以通过宏来判断当前编译器是C++编译器还是C编译器，即__cplusplus，例如：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;#ifdef __cplusplus
extern &quot;C&quot;
#endif
int strcmp(const char*, const char*);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述代码通过预处理定义在代码中兼容C和C++。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484819&amp;amp;idx=1&amp;amp;sn=229a9bb2eb6ca5fd7322c8b85bae6d8f&amp;amp;chksm=fc226094cb55e98229f537079c784bcf16f42579928fd034b46a69a953f6ddab28f1b4cf3c1c&amp;amp;token=1401123908&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>给女儿写信(十四) 合作</title>
   <link href="http://www.luzexi.com/2021/07/11/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A114"/>
   <updated>2021-07-11T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/07/11/给女儿的信14</id>
   <content type="html">
&lt;hr /&gt;

&lt;p&gt;导读：喜欢跟孩子们一起，可惜一直在外地工作，因此我给孩子写点信并录成视频，跟孩子聊聊自己的故事以及最近的感受&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Hi Sharon Anne 爸爸好想你们哟。&lt;/p&gt;

&lt;p&gt;你们到阿婆家过暑假了，开心吗，爸爸视频看到你们很也开心哟。&lt;/p&gt;

&lt;p&gt;Sharon读书写字越来越棒了，Anne也越来越喜欢读书哟，你们都很棒哟，我们一起学习一起积累哟。&lt;/p&gt;

&lt;p&gt;爸爸最近看了一本书，书里写了关于我们人类历史的发展。爸爸想跟你们讲一下。&lt;/p&gt;

&lt;p&gt;书中说我们人类5万年都是智人，就是那种不会使用工具，只会嗷嗷叫的人。&lt;/p&gt;

&lt;p&gt;后来随着环境的变化慢慢演化成了我们现在的人类。&lt;/p&gt;

&lt;p&gt;人与动物到底有什么差别呢？&lt;/p&gt;

&lt;p&gt;差别就在于语言，我们人与人之间可以通过说话来交流，就像爸爸现在你们说话一样。&lt;/p&gt;

&lt;p&gt;这样就传达我想要传达的信息，你们也能听懂爸爸说了什么。&lt;/p&gt;

&lt;p&gt;通过这样信息的传达，我们人与人之间就有了合作，而且因为能交流所以这种合作比动物之间的合作会更加的好。&lt;/p&gt;

&lt;p&gt;所以我们人最大的优势就是合作，通过合作我们能办成很多大事情。&lt;/p&gt;

&lt;p&gt;古代人通过合作来捕杀大型的猎物，现代人通过合作来完成复杂而庞大的事情。&lt;/p&gt;

&lt;p&gt;由于我们一个人的能力非常有限，所以我们必须合作。&lt;/p&gt;

&lt;p&gt;通过合作我们能够让更多的人参与到事情中来，从而完成我们无法完成事情。&lt;/p&gt;

&lt;p&gt;所以合作特别重要。&lt;/p&gt;

&lt;p&gt;但合作并不是这么容易的哟，这需要我们有很好的沟通技巧，通过与别人沟通、交流，可以拉拢周围的人帮助我们完成事情。&lt;/p&gt;

&lt;p&gt;沟通技巧是可以训练也是需要训练的哟，通过训练我们可以提升自己的沟通技巧，这不仅让我们能够有合作的能力，还能让自己的心情更加愉快哟。&lt;/p&gt;

&lt;p&gt;好了，这次的信就写到这里，爸爸爱你们哟。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(三十一) 《装载、链接与库》#4 内核运行库</title>
   <link href="http://www.luzexi.com/2021/07/04/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B033"/>
   <updated>2021-07-04T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/07/04/读书笔记33</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484809&amp;amp;idx=1&amp;amp;sn=89091ecce47229ebf10e4855c0ccceca&amp;amp;chksm=fc22608ecb55e998cdff3952057e6d1c6f099463797d57458ec1f4d37a432d46dc3c5bfcfbb5&amp;amp;token=557108361&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;背景&quot;&gt;背景：&lt;/h2&gt;

&lt;p&gt;看此书的起源是我在了解Linux注入技术的时候翻阅到的，由于注入技术需要用到很多ELF格式的内容，很多网络上的技术文章都指向了同一本书。也刚好周围的同事有此书，便翻阅了一下，这一番翻阅打开了我对程序世界的又一扇大门。&lt;/p&gt;

&lt;p&gt;很快我就自己买了此书并阅读完成，整本书给我很大的震撼，让我对程序从编译到链接到装载有了更深刻的认识。&lt;/p&gt;

&lt;p&gt;我把我的整个学习过程以及对书本的理解，用自己的语言和自己画的图表达出来，让读者能够更容易接受到我所学的知识。&lt;/p&gt;

&lt;h2 id=&quot;目标&quot;&gt;目标：&lt;/h2&gt;

&lt;p&gt;了解编译过程&lt;/p&gt;

&lt;p&gt;了解动态库和静态库的装载细节&lt;/p&gt;

&lt;p&gt;了解可执行程序装载和执行过程&lt;/p&gt;

&lt;p&gt;了解可执行文件和动态库的数据格式&lt;/p&gt;

&lt;h2 id=&quot;疑问&quot;&gt;疑问：&lt;/h2&gt;

&lt;p&gt;c/c++编译器是如何将cpp编译为可执行文件的？&lt;/p&gt;

&lt;p&gt;多个c/c++文件是如何编译成一个可执行文件的？&lt;/p&gt;

&lt;p&gt;操作系统内存是如何初始化和管理的？&lt;/p&gt;

&lt;p&gt;动态库和静态库的链接和装载过程是怎样的？&lt;/p&gt;

&lt;p&gt;操作系统的用户态和内核态是如何运作的？&lt;/p&gt;

&lt;h2 id=&quot;正文&quot;&gt;正文：&lt;/h2&gt;

&lt;p&gt;前面我们介绍了Linux的动态库在装载时的动态链接过程，以及可执行文件的装载过程。这里简单介绍下Windows的动态库的装载过程。&lt;/p&gt;

&lt;p&gt;Windows下的动态链接与Linux下的相比有很多相似之处，但也有很多不同的地方。Windows下的动态库称为DLL（Dynamic-Link Library），它和EXE文件实际上是一个概念，即都是PE格式的文件，区别在于PE文件头的符号位标识位不同，而且DLL文件的扩展名不一定是.dll，也可能是.ocx或.CPL。&lt;/p&gt;

&lt;p&gt;与Linux不同的是Windows有符号导入表和导出表概念，即必须使用“__declspec(dllimport)”和“__declspec(dllexport)”来显式的声明符号的导入与导出。
Window编译器通过收集这些显式声明的导入符号，将它们放在一个导出表（Exprot Table）的符号集中，类似于Linux下的.dynsym段。
动态链接器根据需要导入的符号从导出表上查找到真实的函数地址，这期间导入表则起到了存储符号和记录地址的作用，类似于Linux下的GOT。&lt;/p&gt;

&lt;p&gt;其实DLL中的代码段并不是地址无关的，Windows加载DLL时采用了重定基地址（Rebasing）的方法重定位每个绝对地址引用。
由于DLL内部的地址都是基于基地址或相对于基地址的RVA，因此在重定位时只需要加上一个固定差值就能完成重定位工作。
由于是绝对地址重定位，所以重定基地址比ELF的延迟定位机制要快很多，因为它不需要通过类似的GOT机制，对外部数据和函数的访问不需要每次都计算GOT位置。&lt;/p&gt;

&lt;h3 id=&quot;内存堆与栈&quot;&gt;内存堆与栈&lt;/h3&gt;

&lt;p&gt;前面我们说了很多关于系统内存的，包括虚拟内存、页表、缺页、置换等，而这节主要对堆内存和栈内存进行讲解。&lt;/p&gt;

&lt;p&gt;当我们调用函数时，需要将当前函数的信息保存到栈上以便调用结束回到原来位置时所有数据都能恢复到原样。
我们把这些保存到栈上的数据称为栈帧（Stack Frame），这些数据主要包含：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;调用需要的参数，以及调用结束时函数的返回地址&lt;/li&gt;
  &lt;li&gt;临时变量：包括函数的非静态局部变量以及编译器自动生成的临时变量&lt;/li&gt;
  &lt;li&gt;上下文：当前函数调用的寄存器数据&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;注意，每种编译器都有自己的调用惯例，比如参数的传递顺序和方式、栈维护的方式、函数的签名规则、返回的寄存器等，在各个编译器之间可能有细微的差别。&lt;/p&gt;

&lt;p&gt;其中几乎所有的编译器调用惯例都将返回值存储在eax寄存器中，对于大于4字节的数据则存储在eax和edx中联合返回。&lt;/p&gt;

&lt;h3 id=&quot;堆内存&quot;&gt;堆内存&lt;/h3&gt;

&lt;p&gt;我们在写内存分配时通常使用内存分配接口malloc或new，这两个接口分配的内存通常都放在.heap堆内存区域中，而动态库文件在加载的时候，则通过mmap将动态库映射到.libraries文件映射区域中。&lt;/p&gt;

&lt;p&gt;注意，malloc分配的内存到.heap段，mmap分配内存到.libraries段，而当malloc请求的内存大于MMAP_THRESHOLD时，则会转而调用mmap，申请的内存则在.libraries区域，这个阈值可以通过mallopt调整。&lt;/p&gt;

&lt;p&gt;这里作者介绍了下堆内存管理的几种方式不是非常详细，简单介绍下（后面会写一个操作系统内存专题）：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;空闲链表法，把空闲的内存块加入到一个链表上，这个链表里全是空闲的内存块地址，当需要使用时就从这里找合适的。&lt;/li&gt;
  &lt;li&gt;分页分块法，把整个内存分成128K大小的XX页，然后再把每页分成32字节大小的内存块，用数组记录每个内存页和内存块的占用情况，这样在分配和释放时查找速度会比较快。&lt;/li&gt;
  &lt;li&gt;固定大小内存池，把整个分为固定大小的内存块，然后用空闲链表记录所有空闲的内存地址，每次分配都从空闲链表里推出一个内存使用。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;运行库&quot;&gt;运行库&lt;/h3&gt;

&lt;p&gt;作者专研的比较细，把进程启动到结束的整个过程都扒了出来，我们也跟着扒一下。&lt;/p&gt;

&lt;p&gt;程序从开始运行到结束的步骤如下：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;操作系统fork（创建）进程，把控制权交到程序入口，这个入口通常都是公共库的某个函数（libc或MSVC）&lt;/li&gt;
  &lt;li&gt;入口函数对运行库和程序运行环境进行初始化，包括堆、I/O、线程、全局变量的构造等等&lt;/li&gt;
  &lt;li&gt;入口函数在完成初始化之后，调用main函数，正式开始执行程序主体部分&lt;/li&gt;
  &lt;li&gt;main函数执行完毕，返回入口函数，入口函数进行清理工作，包括全局变量析构、堆栈销毁、关闭I/O等，然后进程调用系统调用结束进程。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;下面我们来说说在整个系统调用里各部分是如何运作的。&lt;/p&gt;

&lt;h3 id=&quot;文件句柄原理&quot;&gt;文件句柄原理&lt;/h3&gt;

&lt;p&gt;这里不得不提一下文件句柄的原理，因为我们常常使用文件句柄，却不知道文件句柄究竟是什么，以及是什么产生了文件句柄。&lt;/p&gt;

&lt;p&gt;文件句柄总是和内核的文件对象相关联，内核可以通过句柄来计算出内核里文件对象的地址。
在内核中，每个进程都有一个私有的“打开文件表”，这个表是一个指针数组，每一个元素都指向一个内核的打开文件对象，文件句柄就是数组的下标。
当用户打开一个文件时，内核就会在内部生成一个文件对象，并在这个表里找到一个空项，让这一项指向生成的打开文件对象，并返回这一项的下标作为句柄值。
由于这个“打开文件表”处于内核，并且用户无法访问到，因此用户即使拥有句柄值，也无法直接打开文件对象的地址，只能通过系统提供的函数来操作。
Windows的句柄稍有不同，虽然类似于文件表的下标但它并不是下标而是经过某种线性变换的结果。&lt;/p&gt;

&lt;p&gt;注意，文件表数组前三个元素填充的是stdin、stdout、stderr这三个内核对象。&lt;/p&gt;

&lt;h3 id=&quot;读取文件实现原理&quot;&gt;读取文件实现原理&lt;/h3&gt;

&lt;p&gt;我们知道标准库中，文件在写入时会有缓冲区，因此在写入完毕后可以手动调用刷新接口来将缓冲取的内容清空到文件。&lt;/p&gt;

&lt;p&gt;其实读取文件也是同样的道理，如果读取文件时总是对文件操作，那么每次都需要进入内核模式，完成后再回到用户模式。
很明显这样的读取方式消耗的比较大，因此读取文件也需要自己的缓冲，以提高文件读取效率。&lt;/p&gt;

&lt;p&gt;那么标准库是如何实现文件读取的呢？&lt;/p&gt;

&lt;p&gt;标准库中文件读取时的执行步骤：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;每个文件读取时都会有一个缓冲区，缓冲区存储了已经读取的文件数据和还未读取的文件数据，&lt;/li&gt;
  &lt;li&gt;当读取文件时，先判断缓冲区中是否有此数据，&lt;/li&gt;
  &lt;li&gt;如果缓冲区中有我们需要的数据，则直接取得后返回即可，&lt;/li&gt;
  &lt;li&gt;如果没有我们需要的数据或者数据不够，则读取文件并将其后的数据一并读取放入到缓冲区中后再返回&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;变长参数的实现方式&quot;&gt;变长参数的实现方式&lt;/h3&gt;

&lt;p&gt;这里作者说明了printf这种函数可变参数的实现方式：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;编译时将多个实参打包成byte数组传入函数&lt;/li&gt;
  &lt;li&gt;在函数中根据下标和大小从byte数组指针中提取形参数据&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;glibc的组成&quot;&gt;glibc的组成&lt;/h3&gt;

&lt;p&gt;glibc中的内容由两部分组成，一部分是头文件，比如stdio.h、stdlib.h等，它们位于/usr/include，另一部分则是库的二进制文件，/lib/libc.so，或者/usr/lib/libc.a。&lt;/p&gt;

&lt;p&gt;事实上glibc还有3个重要的运行库，&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;/usr/lib/crt1.o，包含了入口程序，负责调用__libc_start_main初始化libc，并调用main函数进入真正的程序主体。&lt;/li&gt;
  &lt;li&gt;/usr/lib/crti.o，包含了_init()部分的程序初始化指令， 所有的.init都会被收集起来在这里调用&lt;/li&gt;
  &lt;li&gt;/usr/lib/crtn.o，包含了_finit()部分的程序销毁指令，所有的.finit都会被收集起来在这里调用&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;注意，收集全局构造函数和析构函数的过程是，在链接时，将各个目标文件中.ctors段中的构造函数和析构函数合并为一个数组，在初始化和销毁时依次调用&lt;/p&gt;

&lt;p&gt;由于crti.o必须保证在用户目标文件和系统库之前，crtn.o必须在用户目标文件和系统库之后，因此链接器输入文件顺序必须是：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;ld crt1.o crti.o [user_objects] [system_libraries] crtn.o
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;注意，前面说的都是C语言，在C++中则提供了crtbeginT.o和crtend.o来配合glibc实现C++的全局构造和析构。&lt;/p&gt;

&lt;h3 id=&quot;线程局部存储tlsthread-local-storage&quot;&gt;线程局部存储(TLS，Thread Local Storage)&lt;/h3&gt;

&lt;p&gt;线程也可以拥有私有数据，这就是线程局部存储机制(TLS，Thread Local Storage)。
TLS的用法很简单，只需要在它定义的时候加上关键字__thread就可以，在MSVC里为__declspec(thread)。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;// Linux
__thread in number;

// MSVC
__declspec(thread) int number;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;一旦一个全局变量被定义为TLS类型，那么每个线程都会拥有这个变量的副本，任何线程对该变量的修改都不会影响其他线程中该变量的副本。&lt;/p&gt;

&lt;p&gt;在Windows中TLS的实现方式为：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;编译器把__declspec(thread)定义的线程私有变量放到.tls段中&lt;/li&gt;
  &lt;li&gt;当进程启动一个线程时，在进程堆中分配一块足够大小的空间&lt;/li&gt;
  &lt;li&gt;把.tls段中的内容复制到这块空间中&lt;/li&gt;
  &lt;li&gt;于是每个线程都有自己独立的一个.tls数据副本&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;注意，在.rdata表中有一个TLS表，它保存了所有的TLS变量的构造和析构函数地址，在线程启动和退出时都会对TLS变量进行构造和析构。除了使用添加关键字定义TLS变量外，还可以通过系统接口的方式，实时申请TLS类型变量。&lt;/p&gt;

&lt;h3 id=&quot;系统内核调用原理&quot;&gt;系统内核调用原理&lt;/h3&gt;

&lt;p&gt;为了安全操作系统将操作分为两种模式，一种用户模式，一种是内核模式，用户模式操作范围被限制在用户空间上，而内核模式的程序涉及的范围会更广一些，但也主要集中内核空间中。由于内核模式操作的权限更大，因此内核程序编写时需要更加谨慎并严格测试，通常我们都接触不到内核程序，除非自己编写或修改操作系统。&lt;/p&gt;

&lt;p&gt;当用户模式要调用内核模式中的程序，要先通过EAX寄存器设置调用号，再调用中断指令，内核通过中断向量表找到系统调用程序，系统调用程序根据系统调用表查找到系统函数，最后执行系统调用。
x86下中断指令为0x80，通过寄存器EAX指定系统调用的调用号，内核程序根据EAX中的值找到系统函数最后执行。&lt;/p&gt;

&lt;p&gt;举例创建进程时的系统调用过程：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;用户调用创建进程函数，&lt;/li&gt;
  &lt;li&gt;创建进程函数设置eax寄存器并调用中断指令进入内核态，&lt;/li&gt;
  &lt;li&gt;中断指令根据0x80和中断向量表找到系统调用程序，&lt;/li&gt;
  &lt;li&gt;系统调用程序根据eax寄存器和系统调用表找到需要调用的函数，&lt;/li&gt;
  &lt;li&gt;系统调用完毕返回用户态函数返回地址。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;当用户态和内核态切换时，首先SS要指向内核栈，ESP指向内核栈顶，这样才能让程序当前的栈从用户栈切换到内核栈。&lt;/p&gt;

&lt;p&gt;其实每个进程都有两个栈，用户栈和内核栈，在切换到内核栈时要保存当前上下文，因此要向内核栈中推入ESP、SS、EFLASHS、CS、EIP的值，以便切回用户态时再弹出恢复。
除了这几个特殊的寄存器，向内核栈中推入的还有EBX、ECX、EDX、ESI、EDI和EBP这6个寄存器，而这6个寄存器刚好可以被系统调用函数用于实参。&lt;/p&gt;

&lt;h3 id=&quot;实现自定义crtc-runtime&quot;&gt;实现自定义CRT（C Runtime）&lt;/h3&gt;

&lt;p&gt;作者根据前面的理论知识，实现了一个自己的CRT（C Runtime），在不使用标准库的情况下，支持自定义自己的函数入口、进程初始化与销毁、堆内存分配与释放、文件读取与写入、格式化输出。&lt;/p&gt;

&lt;p&gt;我们这里讲解下实现CRT的几个关键点。&lt;/p&gt;

&lt;h3 id=&quot;关键点一&quot;&gt;关键点一：&lt;/h3&gt;

&lt;p&gt;入口函数可以使用编译命令来指定，例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;gcc -nostartfiles -e myentry source.c -o source
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;-e：设置程序的入口函数&lt;/p&gt;

&lt;h3 id=&quot;关键点二&quot;&gt;关键点二：&lt;/h3&gt;

&lt;p&gt;进入main函数前要初始化堆内存，堆内存的管理分配与释放要自己来实现，比如实现一个FreeList空闲链表来存储销毁的堆内存以便下次使用。&lt;/p&gt;

&lt;p&gt;例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;//自定义堆内存块结构
typedef struct _heap_header
{
    unsigned size;
    struct _heap_header * next;
    struct _heap_header * prev;
} heap_header;

//空闲内存链表
static heap_header * freelist_head = NULL;

//自定义内存释放函数
void free(void * ptr)
{
    ....
}

//自定义堆内存申请函数
void * malloc(unsigned size)
{
    ....
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;关键点三&quot;&gt;关键点三：&lt;/h3&gt;

&lt;p&gt;堆内存的扩容可以通过brk系统调用设置进程的数据段边界，而sbrk可以移动进程的数据段边界，其实将数据段边界后移就相当于分配了一个定量的内存。&lt;/p&gt;

&lt;p&gt;由brk/sbrk分配的内存仅仅分配了虚拟空间，这些空间是不会提交的（即不分配物理内存页），当进程访问某个地址时才会与物理页关联。&lt;/p&gt;

&lt;h3 id=&quot;关键点四&quot;&gt;关键点四：&lt;/h3&gt;

&lt;p&gt;由于不使用标准库，因此文件系统也必须自己重写，重写文件读写底层时，我们可以用汇编写系统调用来实现。&lt;/p&gt;

&lt;p&gt;其中系统调用中，打开文件的系统调用号是5、读取文件的系统调用号是3、写入文件的系统调用号是4、关闭文件的系统调用号是6。
因此在调用int 0x80前，设置eax为各调用号就可以，同时用ebx、ecx、edx将参数传入系统调用。&lt;/p&gt;

&lt;p&gt;举例打开文件的系统调用：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;asm(&quot;movl $5, %%eax \n\t&quot; // 设置eax的调用号
    &quot;movl %1, %%ebx \n\t&quot; // 设置参数1
    &quot;movl %2, %%ecx \n\t&quot; // 设置参数2
    &quot;movl %3, %%edx \n\t&quot; // 设置参数3
    &quot;int $0x80 \n\t&quot; // 调用0x80系统中断
)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;前面我们说标准输入输出是文件表的前3个句柄，因此句柄0，1，2为标准输入、标准输出、标准错误的句柄。&lt;/p&gt;

&lt;h3 id=&quot;关键点五&quot;&gt;关键点五：&lt;/h3&gt;

&lt;p&gt;实现全局构造和析构时，从.ctors段中读取构造和析构函数数据，将构造和析构函数都合并到指针数组中，依次调用函数指针数组中的方法。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;typedef void (*ctor_func)(void);

ctor_func ctors_begin[1] __attribute__ ((section(&quot;.ctors&quot;))) = 
{
    (ctor_func) -1;
};

void run_ctors()
{
    const ctor_func * list = ctors_begin;
    while( (int)*++list != -1 )
    {
        (**list)();
    }
}
&lt;/code&gt;&lt;/pre&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(三十二) 《C++ Primer》#5</title>
   <link href="http://www.luzexi.com/2021/07/03/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B032"/>
   <updated>2021-07-03T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/07/03/读书笔记32</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484794&amp;amp;idx=1&amp;amp;sn=03be5eab5f7014e7d612ffee2a193cb9&amp;amp;chksm=fc22607dcb55e96bd6f1dd010815d3e382cbcacca18bb1a4c69ff96ca3c4644c3d2f6240f7ef&amp;amp;token=557108361&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;背景&quot;&gt;背景：&lt;/h2&gt;

&lt;p&gt;我为什么要重学C++？第一是巩固核心，软件编程有三大核心，语言、操作系统、框架设计，语言首当其冲，核心能力大都看不见摸不着只有你自己知道的东西。第二是将分散的知识串联起来，这样就能产生新的知识和新的创意。第三是当我们在巩固时创造出了新的知识，那么这些新的知识和旧的知识将同时变成智慧融合到身体中。&lt;/p&gt;

&lt;p&gt;本系列基于《C++ Primer》学习，由于重点放在“重学”，我略过了滚瓜烂熟的部分，挑出以前常忽略的部分，以及记忆没有那么深刻的部分，特别是那些重要的但没有上心的部分。&lt;/p&gt;

&lt;h2 id=&quot;正文&quot;&gt;正文：&lt;/h2&gt;

&lt;h3 id=&quot;关联容器&quot;&gt;关联容器&lt;/h3&gt;

&lt;p&gt;关联容器包括map、set以及相应的扩展容器，包括可容纳重复关键字的multimap和multiset容器，以及用unordered为前缀的无序容器。&lt;/p&gt;

&lt;p&gt;注意，对map使用下标操作时，如果关键字在容器中不存在，则会添加一个关键字在容器中。&lt;/p&gt;

&lt;p&gt;无序容器大都使用哈希方式来做查找工作，它在存储上组织为一个数组，我们可以比喻为桶管理，每个数组中的元素为桶，它保存0个或多个元素，用哈希函数将元素映射到桶中，当一个桶保存多个元素时，需要顺序搜索这些元素来查找正确的那个。因此无序容器的性能通常依赖于哈希函数的质量和数组的大小。&lt;/p&gt;

&lt;h3 id=&quot;智能指针&quot;&gt;智能指针&lt;/h3&gt;

&lt;p&gt;新的标准库中有两种智能指针，一种允许多个指针指向同一个对象shared_ptr，另一种则“独占”所指向的对象unique_ptr，标准库还提供了一个名为weak_ptr的弱引用方式，来指向shared_ptr所管理的对象。&lt;/p&gt;

&lt;p&gt;其实智能指针也是模版，它通过模版并重载=、+/-、==运算符来实现智能指针的逻辑。
智能指针的逻辑很简单，通过计数器来记录现在有多少个shared_ptr指向相同的对象，当计数为0时它就会自动释放对象。&lt;/p&gt;

&lt;p&gt;智能指针可以帮助我们管理内存对象的销毁时机，但智能指针不是万能的，如果你忘记销毁了智能指针，则同样会造成内存泄漏。
因此我们应该明白内存管理方式有很多种，用来防止内存泄漏的方法也不只智能指针这一种。&lt;/p&gt;

&lt;h3 id=&quot;动态内存管理&quot;&gt;动态内存管理&lt;/h3&gt;

&lt;p&gt;我们常常自己来管理内存，内存泄漏也是常用的事，分配了内存但没有释放，或者没有及时释放堆积的太多等问题都是比较普遍的。&lt;/p&gt;

&lt;p&gt;常用的new和delete运算符，在分配内存的同时，也负责调用构造函数和析构函数。&lt;/p&gt;

&lt;p&gt;如果我们想更好的管理内存，提高内存使用效率，让内存泄漏更容易分析，那么我们就得设计内存分配器来管理内存，例如固定大小内存分配器、环形内存分配器、双端内存分配器等等，这里不详细介绍。&lt;/p&gt;

&lt;p&gt;阻止对象拷贝&lt;/p&gt;

&lt;p&gt;用“=delete”定义删除函数来阻止拷贝和赋值：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;struct NoCopy
{
  NoCopy() = default; // 默认构造函数
  NoCopy(const NoCopy&amp;amp;) = delete; // 阻止拷贝
  NoCopy &amp;amp;operator = (const NoCopy&amp;amp;) = delete; // 阻止赋值
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;关键字“= delete”通知了编译器，告知它我们不希望定义这些成员。
注意，析构函数不能被删除，因此不能被定义删除函数。
另外父类和子类对删除函数具有一致性，即父类定义了删除函数时子类也同样具备。&lt;/p&gt;

&lt;p&gt;我们也常通过private来控制拷贝，即将构造函数和赋值运算符声明为private来阻止拷贝。&lt;/p&gt;

&lt;h3 id=&quot;对象移动&quot;&gt;对象移动&lt;/h3&gt;

&lt;p&gt;在内置对象中常发生对象拷贝的情况，如果想避免拷贝又想移动对象，则可以考虑右值引用。
所谓右值引用，就是必须绑定到右值的引用，我们可以通过&amp;amp;&amp;amp;（而不是&amp;amp;）来获得右值引用。
右值引用有一个重要性质，即只能绑定到一个将要销毁的对象。&lt;/p&gt;

&lt;p&gt;使用新标准库中move函数移动对象：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;int &amp;amp;&amp;amp;rr1 = 42; 
int &amp;amp;&amp;amp;rr3 = std::move(rr1);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;std::move函数除了获取rr1右值引用外，还负责销毁，因此std::move后我们将不能再使用它了。
由于一个移后源对象具有不确定的状态，对其调用std::move是危险的，所以当我们使用move时，必须绝对确认移动后源对象没有其他用户。&lt;/p&gt;

&lt;p&gt;为了让自定义类支持移动操作，我们可以为其定义移动构造函数和移动赋值运算符。例如：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;// 移动构造函数
testClass::testClass(testClass &amp;amp;&amp;amp;s) noexcept
{
  var1 = s.var1; // 移动var1
  var2 = s.var2; // 移动var2
  s.var1 = s.var2 = nullptr; // 移动后置空
}

// 移动赋值运算符
testClass &amp;amp; testClass::operator = (testClass &amp;amp;&amp;amp;s) noexcept
{
  var1 = s.var1; // 移动var1
  var2 = s.var2; // 移动var2
  s.var1 = s.var2 = nullptr; // 移动后置空
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;与构造函数不同的是，移动构造函数不分配任何新内存，它会接管给定对象中的内存。
在接管内存后，它将给定对象中的指针都置为 nullptr 以便在后续在销毁源对象时不会销毁这部分内存。
编译器也会帮助我们合成默认的移动构造函数和移动赋值运算符，当然必须是编译器发现移动操作被用到的时候，而且只有当一个类没有定义任何自己版本的拷贝控制成员且它的所有数据成员都能移动构造或移动赋值时，编译器才会为它合成默认的移动构造函数或移动赋值运算符。&lt;/p&gt;

&lt;h3 id=&quot;可调用对象&quot;&gt;可调用对象&lt;/h3&gt;

&lt;p&gt;可调用对象包括，函数、函数指针、lambda表达式、bind创建的对象、以及重载了的运算符。
从机器指令角度上来理解，函数实质是没有类型之分的，但实际调用时，仍然会需要对齐参数和返回值，因此区别在于参数数量、参数类型、返回值类型。
编译器在识别两个可调用对象是否一致时，其实并不关注它是函数指针或者其他，而关心它们的形参和返回值是否相同。&lt;/p&gt;

&lt;p&gt;这样说来，任意两个可调用对象可以共享同一种调用形式，这使得建立函数表成为了可能，前提是它们的参数和返回类型相同。&lt;/p&gt;

&lt;p&gt;标准库提供了这样一个模版封装，function&lt;T&gt;，让相同的可调用对象能够转换成同一类型指针，其中T为函数类型可以用lambda表达式。&lt;/T&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;function&amp;lt;int(int, int)&amp;gt; f1 = add; // 加法函数指针
function&amp;lt;int(int, int)&amp;gt; f2 = divide(); // 除法函数
function&amp;lt;int(int, int)&amp;gt; f3 = [](int i, int j){ return i*j;}; // lambda表达式

count &amp;lt;&amp;lt; f1(4,2) &amp;lt;&amp;lt; endl; // 打印6
count &amp;lt;&amp;lt; f2(4,2) &amp;lt;&amp;lt; endl; // 打印2
count &amp;lt;&amp;lt; f3(4,2) &amp;lt;&amp;lt; endl; // 打印8
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;虚函数&quot;&gt;虚函数&lt;/h3&gt;

&lt;p&gt;OOP的核心是多态，多态的核心就是虚函数，我们知道通过虚函数可以在运行时动态确定要调用的函数，派生类的虚函数会直接覆盖基类。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;class testClass1
{
   public:
       int var1;
       virtual int testFunc();
}

class testClass2
{
    public:
        virtual int testFunc();
}

testClass1 * point = new testClass2();

point-&amp;gt;testFunc(); // 调用派生类函数
point-&amp;gt;testClass1::testFunc(); // 调用父类函数
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;虚函数原理是在虚函数调用时根据虚表来的确定，虚表中存放了当前类的虚函数，每次调用虚函数都会按固定偏移在虚表中提取函数，所以派生类覆盖了父类后，在虚表中固定偏移的函数就成了派生类的函数了。&lt;/p&gt;

&lt;p&gt;既然派生类覆盖了父类的虚函数，派生类又怎么去调用父类的虚函数呢？其实是由于代码显式的指明了调用方向，所以在编译时编译器就已经知道要调用的哪个函数，也就不需要动态借助虚表来定位了，编译时直接静态绑定父类中函数便可。&lt;/p&gt;

&lt;p&gt;小知识，我们还可以通过final关键字，阻止虚函数派生类覆盖。&lt;/p&gt;

&lt;p&gt;虚函数在构造函数和析构函数中调用要特别小心，因为此时部分成员没有被初始化，例如在父类中调用虚函数，由于此时调用的是子类虚函数，而子类成员还未被初始化，调用虚函数有可能导致不可预测的错误，因此通常在构造和析构函数内执行虚函数时用显式的方式调用当前类的虚函数。&lt;/p&gt;

&lt;h3 id=&quot;改变类成员的访问规则&quot;&gt;改变类成员的访问规则&lt;/h3&gt;

&lt;p&gt;用using声明可以改变基类的成员访问级别&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;class Base
{
  private:
    void testFunc();
  public:
    void testFunc2();
}

class Derived : private Base
{
  public:
    using Base::testFunc;
  protected:
    using Base::testFunc2;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述例子中，基类中的testFunc和testFunc2在派生类中的访问级别都被改变了，testFunc从private改为了public，而testFunc2从public改为了protected。&lt;/p&gt;

&lt;p&gt;其实成员变量的访问规则只是编译器的规则，在进程的内存和指令中没有做任何限制，在汇编里你完全可以随意访问所谓的私有变量，限制你的只是编译器。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(三十一) 《装载、链接与库》#3 动态链接与装载</title>
   <link href="http://www.luzexi.com/2021/06/27/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B031"/>
   <updated>2021-06-27T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/06/27/读书笔记31</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484788&amp;amp;idx=1&amp;amp;sn=8e4f7f36e4ddf6dd2f7c086463c089de&amp;amp;chksm=fc226073cb55e965e5ab259f04293c48f718a2fd72858b11397852d8ba8c7fab656b0f46e248&amp;amp;token=283688006&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;背景&quot;&gt;背景：&lt;/h3&gt;

&lt;p&gt;看此书的起源是我在了解Linux注入技术的时候翻阅到的，由于注入技术需要用到很多ELF格式的内容，很多网络上的技术文章都指向了同一本书。也刚好周围的同事有此书，便翻阅了一下，这一番翻阅打开了我对程序世界的又一扇大门。&lt;/p&gt;

&lt;p&gt;很快我就自己买了此书并阅读完成，整本书给我很大的震撼，让我对程序从编译到链接到装载有了更深刻的认识。&lt;/p&gt;

&lt;p&gt;我把我的整个学习过程以及对书本的理解，用自己的语言和自己画的图表达出来，让读者能够更容易接受到我所学的知识。&lt;/p&gt;

&lt;h3 id=&quot;目标&quot;&gt;目标：&lt;/h3&gt;

&lt;p&gt;了解编译过程
了解动态库和静态库的装载细节
了解可执行程序装载和执行过程
了解可执行文件和动态库的数据格式&lt;/p&gt;

&lt;h3 id=&quot;疑问&quot;&gt;疑问：&lt;/h3&gt;

&lt;p&gt;c/c++编译器是如何将cpp编译为可执行文件的？&lt;/p&gt;

&lt;p&gt;多个c/c++文件是如何编译成一个可执行文件的？&lt;/p&gt;

&lt;p&gt;操作系统内存是如何初始化和管理的？&lt;/p&gt;

&lt;p&gt;动态库和静态库的链接和装载过程是怎样的？&lt;/p&gt;

&lt;p&gt;操作系统的用户态和内核态是如何运作的？&lt;/p&gt;

&lt;h3 id=&quot;正文&quot;&gt;正文：&lt;/h3&gt;

&lt;p&gt;前面我们说了ELF格式，以及ELF格式的静态链接过程，Windows也有自己的二进制文件格式，这里简单说一下Windows部分的格式。&lt;/p&gt;

&lt;p&gt;Windows的二进制文件格式称为PE（Protable executable），PE文件格式与ELF同根同源，都是COFF格式发展而来。
Windows平台使用Visual C++编译器产生的目标文件使用COFF文件，它们的结构很大程度上都是相同的。
“cl”是Visual C++的编译器，即“Compiler”的缩写，Visual C++还提供了用于查看目标文件和可执行文件的工具“dumpbin”。&lt;/p&gt;

&lt;h3 id=&quot;可执行文件装载&quot;&gt;可执行文件装载&lt;/h3&gt;

&lt;p&gt;在讲解执行文件装载前先简单介绍下虚拟空间，因为可执行文件装载需要虚拟空间支持，那么什么是虚拟空间呢？&lt;/p&gt;

&lt;h3 id=&quot;虚拟空间&quot;&gt;虚拟空间&lt;/h3&gt;

&lt;p&gt;虚拟空间很多人都认为是一个真实的空间，其实不然，它是我们想象出来的空间，其实并不存在。
而我们在理解的时候却需要承认这个空间的存在，即每个进程都拥有一个自己想象的虚拟空间，地址从0到0xFFFFFFFFF（32位设备）&lt;/p&gt;

&lt;p&gt;操作系统上的使用的内存空间都是虚拟地址空间，而非实际物理空间，它是由操作系统虚构出来的一个地址空间。&lt;/p&gt;

&lt;p&gt;这就像是操作系统给了每个进程一个世界那样，在这个世界里，进程可以自由的申请和释放内存，而不需要理会物理内存如何分配和释放。&lt;/p&gt;

&lt;p&gt;每个进程中的内存从虚拟地址都从0开始，到0xFFFFFFFF结束，其中有1G的空间专门为内核空间所用，用户空间也做了不同的分段。因此整个虚拟空间地址可以分为：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;.kernel 内核空间段
.stack 栈内存段
.libraries  动态库映射段（文件映射段）
.heap 堆内存段
.bass 未初始化的全局/静态变量段
.data 已初始化全局/静态变量段
.text 程序指令字节段
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;其中栈内存大小是固定的，只有1MB内存，栈会不断向下分配内存，回收时只需要调整栈顶指针，因此它的分配和回收效率很高，而堆内存则不断向上分配内存，效率相对较低，回收时也容易产生内存碎片。&lt;/p&gt;

&lt;p&gt;对于Windows操作系统来说，它的进程虚拟地址空间划分是操作系统占用2GB，用户空间剩下2GB。&lt;/p&gt;

&lt;p&gt;从硬件层面上来说原先的总线32位地址只能访问最多4GB物理内存。但自从扩展至36位地址线之后，Intel修改了页映射的方式，使得新的映射方式可以访问到更多的物理内存，这个扩展方式叫做PAE（Physical Address Extension）。原理就是多块物理内存随时交换映射为同一个虚拟内存地址，这样应用程序就不需要管超出部分的内存空间了。不过这只是在32位机时代的补救办法，现在逐渐淘汰了，但这个方法在其他领域里仍然在被使用。&lt;/p&gt;

&lt;p&gt;Linux和Windows操作系统都使用页映射方式管理虚拟内存和物理内存之间的关系&lt;/p&gt;

&lt;h3 id=&quot;虚拟内存与物理内存之间由一个页表作为映射连接它们之间的关系&quot;&gt;虚拟内存与物理内存之间由一个页表作为映射连接它们之间的关系&lt;/h3&gt;

&lt;p&gt;当我们访问一块虚拟空间时，操作系统发现没有这块地址没有被连接到真实的物理地址，此时才真正从真实物理内存中找到一块内存块用页表连接起来，这才真正分配成功并且可以使用。&lt;/p&gt;

&lt;p&gt;每次分配实际物理内存，也并不是分配一整块申请的虚拟内存空间，而是按页大小来分配。即，实际物理空间在使用到时才真正分配，而虚拟空间则已经先行分配。&lt;/p&gt;

&lt;p&gt;因此我们在虚拟内存上申请的连续内存，有可能在物理内存上是不连续的。当然，申请连续内存获得实际连续内存的概率要大很多。&lt;/p&gt;

&lt;p&gt;对虚拟空间来说，一块内存只是一个数据结构，没有实际的占用，也没有真正的空间之说，只是我们说起来容易理解一些。&lt;/p&gt;

&lt;p&gt;程序启动时虚拟内存中很多内存地址都没有被用到，因此也没有对应的物理页，只有当使用到该虚拟内存页时，发现有物理内存缺页的情况，才会发起物理内存申请和映射。&lt;/p&gt;

&lt;p&gt;除了虚拟内存，不一定有实际的物理内存外，实际的物理地址上的内存，也不一定在实际物理内存中。
操作系统又在实际物理内存上加了一个系统用于更好的利用实际物理内存空间，即Swap。&lt;/p&gt;

&lt;p&gt;当检测到某些物理内存上一次使用时间过长时，则会被Swap置换到硬盘空间，为实际物理内存腾出更多空间给其他进程使用。&lt;/p&gt;

&lt;p&gt;我们前文提到，在虚拟存储中，现代的硬件MMU提供了地址转换功能，帮助我们在虚拟地址和物理地址之间的转换。&lt;/p&gt;

&lt;h3 id=&quot;那么执行文件是怎么被装载进进程的呢&quot;&gt;那么执行文件是怎么被装载进进程的呢？&lt;/h3&gt;

&lt;p&gt;大致过程为，先创建一个进程，然后将可执行文件装载进进程，再开始运行程序。&lt;/p&gt;

&lt;p&gt;其中装载可执行文件时需要分三步：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;创建一个独立的虚拟地址空间&lt;/li&gt;
  &lt;li&gt;读取可执行文件头，并建立虚拟空间与可执行文件的映射关系&lt;/li&gt;
  &lt;li&gt;将CPU的指令寄存器设置成可执行文件的入口地址，开始运行&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;注意，创建一个虚拟空间实际上并不是创建空间，而是创建映射函数所需要的相应的数据结构。在i386的Linux下，创建虚拟地址空间实际上只是分配一个页目录就可以了。&lt;/p&gt;

&lt;p&gt;另外，可执行文件与虚拟空间建立映射关系时，不会有磁盘和内存的装载，当我们读取可执行文件内容时才发生“缺页”并从磁盘中读取文件内容，装载到虚拟内存再通过页表分配实际物理内存。
因此，由于可执行文件在装载时实际上是被映射的虚拟空间地址，所以可执行文件很多时候被称为映像文件（Image）。&lt;/p&gt;

&lt;p&gt;ELF可执行文件引入了Segment的概念，Segment段合并了多个Section，这样的好处是装载时能更高效且方便。
所有相同属性的Section都会归类到一个Segment并映射到同一个匿名虚拟内存区域中（VMA，Anonymous Virtual Memory Area）。&lt;/p&gt;

&lt;h3 id=&quot;linux内核装载elf过程&quot;&gt;Linux内核装载ELF过程&lt;/h3&gt;

&lt;p&gt;内核装载ELF的过程可以描述为如下步骤：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;调用fork()创建一个新的进程&lt;/li&gt;
  &lt;li&gt;新的进程调用execve()执行指定的ELF文件&lt;/li&gt;
  &lt;li&gt;进入execve()后，Linux内核就开始进行真正的装载工作&lt;/li&gt;
  &lt;li&gt;先调用sys_execve()进行一些参数检查与复制&lt;/li&gt;
  &lt;li&gt;再调用do_execve()，它会先查找被执行的文件，去读文件的前128个字节做文件检查，比如魔数的检查。&lt;/li&gt;
  &lt;li&gt;调用search_binary_handle()搜索和匹配合适的可执行文件装载处理过程，比如ELF可执行文件的装载处理过程为load_elf_binary()，以及装载可执行脚本程序的处理过程叫做load_script()。&lt;/li&gt;
  &lt;li&gt;load_elf_binary()执行完毕后由于把系统调用的返回地址改为了装载ELF程序的入口地址，&lt;/li&gt;
  &lt;li&gt;所以当sys_execve()从内核态返回到用户态时，EIP寄存器直接跳转到ELF程序的入口地址，&lt;/li&gt;
  &lt;li&gt;于是新的程序开始执行，ELF可执行文件装载完成。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;其中load_elf_binary()执行过程为：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;检查ELF可执行文件的有效性，比如魔数、ELF头数据中与Segment的数量&lt;/li&gt;
  &lt;li&gt;获取动态链接的“.interp”段，并设置动态链接器路径。&lt;/li&gt;
  &lt;li&gt;根据ELF可执行文件的头描述，对ELF文件进行映射，比如代码、数据、只读数据。&lt;/li&gt;
  &lt;li&gt;初始化ELF进程环境，比如进程启动时EDX寄存器的地址应该是DT_FINI的地址。&lt;/li&gt;
  &lt;li&gt;将系统调用的返回地址修改成ELF可执行文件的入口点，入口点对于静态链接来说就是ELF头中的e_entry所指的地址，对于动态链接的ELF可执行文件入口点就是动态链接器。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Windows PE的装载过程比ELF简单一些：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;读取文件第一页，里面包含了DOS头、PE文件头和段表&lt;/li&gt;
  &lt;li&gt;检查进程地址空间中目标地址是否可用。如果不可用则另外选一个装载地址，这里可执行文件装载不存在问题，主要针对DLL装载。&lt;/li&gt;
  &lt;li&gt;获取段表中提供的信息，将PE文件中所有的段一一映射到地址空间中相应的位置。&lt;/li&gt;
  &lt;li&gt;如果装载地址不是目标地址，则进行Rebasing重定位。&lt;/li&gt;
  &lt;li&gt;装载所有PE文件所需要的DLL文件。&lt;/li&gt;
  &lt;li&gt;对PE文件中的所有导入符号进行解析。&lt;/li&gt;
  &lt;li&gt;根据PE头中指定的参数，建立初始化栈和堆空间。&lt;/li&gt;
  &lt;li&gt;建立主线程并启动进程。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;动态链接&quot;&gt;动态链接&lt;/h3&gt;

&lt;p&gt;注意，静态链接和动态链接，与静态库和动态库是两个概念，前者说的是程序链接，后者说的是库的不同组装方式。&lt;/p&gt;

&lt;p&gt;静态链接由于必须在编译时就确定好库文件而且每个进程都需要载入一份内存，所以它的缺点是更多内存、更多磁盘空间、模块更新困难。&lt;/p&gt;

&lt;p&gt;我们可以想象一下，每个程序内部都增加一份1MB库程序的内存，那么100个进程就要浪费近100MB的内存，如果磁盘中有2000个这样的程序，就要浪费近2GB磁盘空间，而且在编译后还不能及时更新某个模块，弊端确实挺大的。&lt;/p&gt;

&lt;p&gt;动态链接就很好的解决了这个问题，它不仅可以使得不同进程之间共享一个库内存，还可以减少物理页的换入换出，也可以增加CPU缓存的命中率。
当我们升级程序库时，只要简单的覆盖原文件而无须将所有程序再重新链接一遍，当程序下次运行时新版本库文件就会自动装载到内存完成升级目标。&lt;/p&gt;

&lt;p&gt;注意，动态链接与静态链接在内存分布上也不同。静态链接后的库程序是可执行文件内不可分割的整体，动态链接后的库程序则有自己的独立内存模块。&lt;/p&gt;

&lt;h3 id=&quot;动态库的动态链接过程&quot;&gt;动态库的动态链接过程&lt;/h3&gt;

&lt;p&gt;其实在需要进行动态装载动态库的可执行文件中，已经装载好了一个ld-2.6.so动态库，它实际上就是Linux下的动态链接器。&lt;/p&gt;

&lt;p&gt;我们在开启一个进程时，在装载完执行文件后，进程首先会把控制权交给动态链接器，由它完成所有的动态链接工作，再把控制权交给进程开始执行。&lt;/p&gt;

&lt;p&gt;当我们在代码中写下动态库的函数时，在程序模块动态装载时，应该不需要因为装载地址的改变而改变。
所以实现动态链接的基本想法就是把指令中那些需要被修改的部分分离出来，放到数据那里去，这样指令部分就可以保持不变，而数据部分可以在每个进程中拥有一个副本。&lt;/p&gt;

&lt;p&gt;这种方案称为，地址无关代码（PIC，Position-independent Code）技术方案，通常我们在编译时会加上PIC这个标记，就是告诉编译器，我们这个库是地址无关的。&lt;/p&gt;

&lt;p&gt;如果一个库不是以地址无关（PIC）模式编译的，那么毫无疑问，它需要在装载时被重定位，即在启动执行时就需要装载库文件并且重定位所有与库文件有关的函数调用地址。&lt;/p&gt;

&lt;p&gt;如果一个库是以地址无关（PIC）模式编译的，那么就不会在装载时对整个库函数相关调用进行重定位，而是会用延迟绑定（PLT）的方式实时定位函数。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;static int a;
extern int b;
extern void ext();

void bar()
{
  a = 1; // 模块内数据访问
  b = 2; // 外部模块数据访问
}

void foo()
{
  bar(); // 模块内函数调用
  ext(); // 外部模块函数调用
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;以上图为例模块内和模块间的数据访问和函数调用方法，&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;模块内的函数调用使用相对地址调用&lt;/li&gt;
  &lt;li&gt;模块内的数据访问使用相对寻址方式&lt;/li&gt;
  &lt;li&gt;模块外的数据访问，由于无法知道外部数据的具体地址，所以需要借助全局偏移表（GOT）获取外部数据&lt;/li&gt;
  &lt;li&gt;模块外的函数调用，由于无法知道外部函数的具体地址，所以需要借助全局偏移表（GOT）获取外部函数地址后再调用&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;这样说来got就是关键中的关键那么got是怎么工作的呢&quot;&gt;这样说来GOT就是关键中的关键，那么GOT是怎么工作的呢？&lt;/h3&gt;

&lt;p&gt;大致的方案得从编译说起，我们知道在使用printf，scanf()，strlen()这样的公用库函数时都需要加载公共库libc.so，但公共库并没有被合并到可执行文件中，也就没有可依赖的地址规则。&lt;/p&gt;

&lt;p&gt;所以编译器在编译这些外部函数的时候，其实并不知道它们的调用地址是多少，无法填充真实地址。
但编译器会填充一个地址指向一段动态程序，当这个函数真正被调用时，先调用到动态程序，再由动态程序去寻找真正的调用地址，最后再调用真实地址的函数。&lt;/p&gt;

&lt;p&gt;这种方法就是延迟绑定（PLT），即进程启动加载外部so后并不立刻对任何调用到so函数的指令做重定位，而是当这些外部函数被调用到时才去寻找并且绑定函数与真实地址的关系。&lt;/p&gt;

&lt;p&gt;动态链接对全局和静态数据的访问要进行复杂的GOT定位，然后再间接寻址，对外部模块中的函数调用先定位GOT，然后再进行间接跳转。&lt;/p&gt;

&lt;h3 id=&quot;got延迟绑定plt运行原理&quot;&gt;GOT延迟绑定（PLT）运行原理&lt;/h3&gt;

&lt;p&gt;从上图中我们可以知道PLT有3个关键点：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;跳转到动态绑定程序&lt;/li&gt;
  &lt;li&gt;查找外部函数地址的程序&lt;/li&gt;
  &lt;li&gt;GOT表中数据的存储与读取&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;我们就从这三个关键点入手说明PLT的运行原理&lt;/p&gt;

&lt;p&gt;当我们在代码里编写外部模块函数，在编译时编译器为我们留下的其实是PLT程序的地址，这个地址就是专门为这个外部函数服务的动态绑定程序，在.plt段里，符号以@plt结尾。&lt;/p&gt;

&lt;p&gt;动态绑定程序中有3条指令：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;第一条就是跳转到GOT中去获取真实地址，如果有就直接跳转，没有就再跳转回来到第二条。&lt;/li&gt;
  &lt;li&gt;第二条和第三条指令是去查找函数真实地址，找到函数真实地址后把数据写入GOT中并跳转到真实地址。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;.plt段里动态绑定程序才是真实的查找函数地址的过程，其查找函数真实地址步骤为：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;将重定位表（.rel.plt）下标传入动态链接器_dl_runtime_resove()，&lt;/li&gt;
  &lt;li&gt;由它来完成符号解析和重定位工作，&lt;/li&gt;
  &lt;li&gt;再将函数的真实地址写入到GOT表中，最后跳转到真实地址。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ELF将GOT拆分成两个表，“.got”和“.got.plt”，其中“.got”用来保存全局变量引用地址，“.got.plt”用来保存函数引用地址。&lt;/p&gt;

&lt;p&gt;其中“.got.plt”的前三项有着特殊意义：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;第一项保存的是“.dynamic”段地址，该段描述了本模块动态链接相关的信息。&lt;/li&gt;
  &lt;li&gt;第二项保存的是本模块的ID。&lt;/li&gt;
  &lt;li&gt;第三项保存的是_dl_runtime_resolve()地址。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;三项中的第二项和第三项由动态链接器在装载模块时将它们填充。
第四项也稍有些特殊，通常会将跳转指令的通用程序放在第四项中以减少代码重复，因此“.got.plt”基本上前4项都是确定的内容。&lt;/p&gt;

&lt;h3 id=&quot;动态链接相关的数据结构&quot;&gt;动态链接相关的数据结构&lt;/h3&gt;

&lt;p&gt;在整个PLT绑定、查找和定位过程中用到了好几个数据段，这里介绍一下。&lt;/p&gt;

&lt;p&gt;.interp段里保存了一个字符串，这个字符串就是可执行文件件所需要的动态链接器的路径，Linux下动态链接器通常都在“/lib/ld-linux.so.2”，它通常是个软连接指向真正的库。&lt;/p&gt;

&lt;p&gt;.dynamic段是ELF动态链接最重要的结构，里面保存了动态链接器所需要的基本信息，比如依赖于哪些共享对象、动态链接符号表的位置、动态链接重定位表的位置、共享对象初始化代码的地址等。&lt;/p&gt;

&lt;p&gt;它有点像ELF文件头，只是ELF文件头中保存的是静态链接时需要的信息，比如符号表、重定位表等，.dynamic可以看成是动态链接下所需要的信息。&lt;/p&gt;

&lt;p&gt;.dynsym段是动态符号表，与.symtab符号表不同的是，.dynsym动态符号表只保存了与链接相关的符号，也就是外部接口符号表，对于那些模块内部的符号则不保存。&lt;/p&gt;

&lt;p&gt;为了在.dynsym中查找到对应的符号，会有一个动态符号字符串表.dynstr和符号哈希表.hash来辅助定位和查找符号字符串，加快了符号查找过程。&lt;/p&gt;

&lt;p&gt;.rel.dyn和.rel.plt段位动态链接时的重定位表，与我们之前在静态链接里介绍的.rel.text和.rel.data的用途类似，存储的是需要重定位的符号和全局数据。&lt;/p&gt;

&lt;p&gt;与.rel.text和.rel.data的区别在于，.rel.dyn实际上只是修正地址的引用，并不是实际数据地址，他修正的位置其实位于.got以及数据段中；同样的，.rel.plt修正的也只是函数地址的引用，而不是实际地址，其真正地址位于.got.plt中。&lt;/p&gt;

&lt;p&gt;了解了这几个段的用途，我们可以把调用动态库的延迟重定位的整个过程串连起来了：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;程序调用动态库中函数&lt;/li&gt;
  &lt;li&gt;跳转到PLT程序集中&lt;/li&gt;
  &lt;li&gt;从GOT中获取真实函数地址，有则直接跳转&lt;/li&gt;
  &lt;li&gt;没有则从.rela.plt中获取相关信息并调用_dl_runtime_resolve()查找真实函数地址&lt;/li&gt;
  &lt;li&gt;_dl_runtime_resolve()根据查找到的so中的.dynsym动态符号表找到真实函数地址&lt;/li&gt;
  &lt;li&gt;将真实地址填充到GOT中并跳转&lt;/li&gt;
  &lt;li&gt;库函数调用结束&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;动态链接器自己重定位&quot;&gt;动态链接器自己重定位&lt;/h3&gt;

&lt;p&gt;我们说动态链接器本身就是一个库文件（ld-linux.so），所以在进程把控制权交给动态链接器前必须先要将这个库装载到进程中，由于此时又没有程序能去重定位这个动态链接器的库，因此动态链接库必须自己来给自己做重定位，这个重定位过程称为“自举”。&lt;/p&gt;

&lt;p&gt;自举的过程可以简单描述为：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;通过自己的.dynamic段，找到它自己的重定位表和符号表&lt;/li&gt;
  &lt;li&gt;再根据重定位表和符号表，重定位所有符号表&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;自举期间不能调用函数，因为模块内函数调用必须采用GOT/PLT方式，但GOT/PLT没有被重定位，因此自举代码不可以使用任何全局变量或调用函数。&lt;/p&gt;

&lt;p&gt;动态链接器自举完成后，就可以通过.dynamic段获取所有需要装载的动态库依次装载、解析、重定位。
每个需要被装载的动态库，在装载完成后它的符号表都会被合并到全局符号表（Global Symbol Table）中，全局符号表包含了进程中所有动态链接需要的符号。&lt;/p&gt;

&lt;h3 id=&quot;运行时链接&quot;&gt;运行时链接&lt;/h3&gt;

&lt;p&gt;前面说的静态链接和动态链接都是在程序还未完全启动前做的事情，运行时链接则是在程序进行中加载动态库的一种方式。&lt;/p&gt;

&lt;p&gt;运行时链接的接口有4个，分别是：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;dlopen，装载指定动态库&lt;/li&gt;
  &lt;li&gt;dlsym，获取动态库中的某个符号的地址&lt;/li&gt;
  &lt;li&gt;dlerror，获取上次调用的错误信息&lt;/li&gt;
  &lt;li&gt;dlclose，关闭并卸载动态库&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;运行时链接与动态链接的一模一样（除了自举部分），区别一个在启动时装载，另一个则在程序运行时装载。&lt;/p&gt;

&lt;p&gt;注意，由于所有加载的动态库中的符号都会合并到全局符号表，因此在加载多个库时如果有重复的符号，则后加载的符号可以根据参数选择是否覆盖前面的符号表，默认是不覆盖的。&lt;/p&gt;

&lt;p&gt;这里作者做了一个试验，因为在Windows中有个rundll的小程序可以直接调用DLL中的函数并返回结果，所以作者想在Linux上也实现一个。
于是自己写代码读取动态库so中的函数并调用后打印返回结果，其原理就是利用这4个函数，先dlopen加载指定so，再根据函数名调用dlsym找到函数地址并且调用，最后调用dlerror卸载动态库。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484788&amp;amp;idx=1&amp;amp;sn=8e4f7f36e4ddf6dd2f7c086463c089de&amp;amp;chksm=fc226073cb55e965e5ab259f04293c48f718a2fd72858b11397852d8ba8c7fab656b0f46e248&amp;amp;token=283688006&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(三十) 《C++ Primer》#4</title>
   <link href="http://www.luzexi.com/2021/06/26/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B030"/>
   <updated>2021-06-26T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/06/26/读书笔记30</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484770&amp;amp;idx=1&amp;amp;sn=417618b18d6a0bf34b5fa720031f6312&amp;amp;chksm=fc226065cb55e973587b7a3e372b71acf1def7cb7b04ce3ba2d43a5e4871582f72459bcb962d&amp;amp;token=283688006&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;背景&quot;&gt;背景：&lt;/h3&gt;

&lt;p&gt;我为什么要重学C++？第一是巩固核心，软件编程有三大核心，语言、操作系统、框架设计，语言首当其冲，核心能力大都看不见摸不着只有你自己知道的东西。第二是将分散的知识串联起来，这样就能产生新的知识和新的创意。第三是当我们在巩固时创造出了新的知识，那么这些新的知识和旧的知识将同时变成智慧融合到身体中。&lt;/p&gt;

&lt;p&gt;本系列基于《C++ Primer》学习，由于重点放在“重学”，我略过了滚瓜烂熟的部分，挑出以前常忽略的部分，以及记忆没有那么深刻的部分，特别是那些重要的但没有上心的部分。&lt;/p&gt;

&lt;h3 id=&quot;正文&quot;&gt;正文&lt;/h3&gt;

&lt;h3 id=&quot;类相关定义&quot;&gt;类相关定义&lt;/h3&gt;

&lt;p&gt;注意，this指针是常量指针，因为我们不允许改变this指针中保存的地址。&lt;/p&gt;

&lt;p&gt;当我们定义类相关的非成员函数时，即如果函数在概念上属于类但不定义在类中，则它一般应与类声明在同一个头文件中。
这种方式下，当用户使用接口的任何部分都只需要引用一个文件，举例：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;//testClass.h

//类声明
class testClass {
  //test class
  public:
    int testVar;
}

//类相关的非成员函数
int readFrom_testClass(const testClass &amp;amp; item);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;默认合成的构造函数&lt;/p&gt;

&lt;p&gt;编译器创建的默认构造函数称为，合成的默认构造函数（synthesized default constructor），它的规则如下：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;如果存在类内的初始值，用它来初始化成员。&lt;/li&gt;
  &lt;li&gt;否则，默认初始化该成员。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;合成的默认构造函数只适合非常简单的类，其他某些类不能依赖于合成默认构造函数，原因有三：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;编译器只有在发现类不包含任何构造函数的情况下才替我们生成一个默认的构造函数。&lt;/li&gt;
  &lt;li&gt;类中含有内置类型或者复合类型成员的类应该在类的内部初始化这些成员，否则使用默认构造函数可能得到未定义值。&lt;/li&gt;
  &lt;li&gt;类中含有其他类型成员且成员类型没有默认构造函数，则默认构造函数无法初始化这个成员。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;当然最好是我们自己定义每个类的构造函数而不是让编译器使用默认构造函数，如果你不能使用类内初始化值，则所有构造函数都应该显式地初始化每个内置类型的成员。&lt;/p&gt;

&lt;h3 id=&quot;class和struct的区别&quot;&gt;Class和Struct的区别&lt;/h3&gt;

&lt;p&gt;与C#、Java不同，C++中Class和Struct几乎一样，只是关键字不同和使用习惯不同而已。
唯一的微小区别就是，使用Class的默认访问模式为private，而struct的默认访问模式是public。&lt;/p&gt;

&lt;h3 id=&quot;友元&quot;&gt;友元&lt;/h3&gt;

&lt;p&gt;友元允许其他类或函数访问自己的非公有成员，这个大家都知道就不多说了。
需要注意的是友元关系不存在传递性，也就是说子类的友元并不能理所当然的访问父类。
而且想要所有重载函数成为友元关系，必须为每个重载函数做一次友元声明。&lt;/p&gt;

&lt;p&gt;不过在编译时，友元的声明仅仅指定了访问的权限，它只是标记编译的语法规则好让编译器在语法检查时能知道友元标记的存在，实际上编译器并没有做任何内存上和寄存器上的修改。&lt;/p&gt;

&lt;h3 id=&quot;构造函数初始值列表&quot;&gt;构造函数初始值列表&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;class testClass
{
  public:
      testClass(int ii);
  private:
      int i;
      const int ci;
      int &amp;amp;ri;
}

testClass::testClass(intii):
    i(ii),ci(ii),ri(i)
{
  //do something
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们在类中常使用以上这种构造函数来初始化成员，其实这就相当于成员的初始化顺序，即先初始化i，然后ci，然后ri。&lt;/p&gt;

&lt;h3 id=&quot;隐式的类构造转换&quot;&gt;隐式的类构造转换&lt;/h3&gt;

&lt;p&gt;在对象构造时，也会出现隐式的构造情况，例如：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;class Test_data
{
   Test_data(const string str);
}

string test_str = &quot;test&quot;;
Vector&amp;lt;Test_data&amp;gt; item;
item.push(test_str);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;如果构造函数只接受一个实参，则实际上它同时也定义了转换为此类类型的隐式转换机制，我们把这种构造函数称为，转换构造函数（converting constructor）。&lt;/p&gt;

&lt;p&gt;上面例子中传入的string对象从而引发了Test_data的转换构造函数，构造出对象后再传入，其实这些隐式转换和构造都是在编译时离线计算的，根本不会放到运行时去发生。&lt;/p&gt;

&lt;p&gt;如果你不想让隐式构造发生，可以通过在构造函数前加 explicit 声明来阻止隐式转换，如下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;class Test_data
{
   explicit Test_data(const string str);
}

string test_str = &quot;test&quot;;
Vector&amp;lt;Test_data&amp;gt; item;
item.push_back(test_str); // 编译错误

item.push_back(Test_data(test_str)); // 编译正确
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;声明了explicit的构造函数，只能以直接初始化的形式使用，编译器将不会再进行隐式的自动转换这些构造函数。&lt;/p&gt;

&lt;h3 id=&quot;io库&quot;&gt;IO库&lt;/h3&gt;

&lt;p&gt;这里有些零散的知识需要注意下。&lt;/p&gt;

&lt;p&gt;IO库定义了很多标记，这些标记可以帮助我们访问和操作流，例如由于流可能处于错误状态，我们在操作之前先去检查它是否处于良好状态。
其中badbit标记相对用的比较多，它表示系统级错误，例如不可恢复的读写错误。通常情况下，一旦badbit被置位，流就无法再使用了。&lt;/p&gt;

&lt;p&gt;IO流有缓冲机制，这可以提升IO的性能，当刷新时才将数据写入到文件中。&lt;/p&gt;

&lt;p&gt;导致缓冲刷新的几种情况为：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;程序正常结束，main函数return操作。&lt;/li&gt;
  &lt;li&gt;缓冲区满时，需要刷新缓冲。&lt;/li&gt;
  &lt;li&gt;使用操作符endl来显式刷新缓冲区。&lt;/li&gt;
  &lt;li&gt;使用操作符unitbuf设置流的内部状态，清空缓冲区。&lt;/li&gt;
  &lt;li&gt;一个输出流被关联到另一个流时，输出流会被刷新。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;注意，如果程序崩溃了，输出流的缓冲区是不会被刷新的，这也是为什么崩溃时部分数据没有被写入文件的原因。&lt;/p&gt;

&lt;h3 id=&quot;顺序容器&quot;&gt;顺序容器&lt;/h3&gt;

&lt;p&gt;顺序容器指，vector，deque，list，array，string，它们都可以用迭代器访问元素。
如果我们不需要写访问元素，则应该使用cbegin和cend来获取常量迭代指针。&lt;/p&gt;

&lt;p&gt;顺序容器中有一个方法比较有趣assign（array除外），它可以用参数所指定的元素（拷贝形式）替换容器中的所有元素。
seq.assign(b,e); // 将seq替换为迭代器b到e范围内的元素
seq.assign(il); // 将seq替换为容器列表il中的元素
seq.assign(il); // 将seq替换为n个t值的元素&lt;/p&gt;

&lt;p&gt;注意，当我们用一个对象来初始化容器，或者将一个对象插入到容器中时，实际上放入到容器中的是对象的拷贝，而不是对象本身。就像我们将一个对象传递给非引用参数一样，容器中的元素与提供值的对象之间没有任何关联。&lt;/p&gt;

&lt;p&gt;另外我们也要注意向容器中添加元素或删除元素的操作，可能会使得指向容器的指针、引用、迭代器失效，进而导致运行时错误。&lt;/p&gt;

&lt;h3 id=&quot;泛型算法&quot;&gt;泛型算法&lt;/h3&gt;

&lt;p&gt;标准库并没有给每个容器添加大量功能，而是提供了一组算法，这些算法大多是通用算法，可以独立于任何特定的容器。&lt;/p&gt;

&lt;p&gt;归纳总结下来，泛型算法有4个特点：&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;大多是通用算法，可以独立于任何特定的容器&lt;/li&gt;
  &lt;li&gt;虽然独立于任何容器，但有些算法依赖于元素类型（例如累加算法依赖于带有加法运算的对象类型）。&lt;/li&gt;
  &lt;li&gt;不会直接操作容器，而是运行于迭代器之上，执行的是迭代器的操作。&lt;/li&gt;
  &lt;li&gt;算法可能改变容器中保存的值，或移动容器中的元素，但永远不会直接添加或者删除元素。&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;lambda函数&quot;&gt;lambda函数&lt;/h3&gt;

&lt;p&gt;当定义一个lambda函数时，编译器生成一个与lambda对应的新（未命名的）类类型。&lt;/p&gt;

&lt;p&gt;也就是说当向一个函数传递一个lambda函数时，编译器同时定义了一个新类型和该类型的一个对象，传递的参数就是此编译器生成的类类型的未命名对象，而不是传入的对象。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;void func1()
{
  int v1 = 41;
  auto f = [v1] {return v1;}; // 值捕获
  v1 = 0;
  auto j = f(); // j 为 41

  auto f2 = [&amp;amp;v1] {return v1;} // 引用捕获
  v1 = 2;
  auto j = f2(); // j 为 2
}


transform(v1.begin(), vi.end(), vi.begin(), [](int i) -&amp;gt; int { if (i &amp;lt; 0) return -i; else return i; }); // lambda函数指定了int返回值和int形参
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;注意，如上代码中，如果lambda形参为引用则需要我们注意实参的生命周期，因为在lambda执行时，如果该对象已经被销毁则可能引起运行时错误。&lt;/p&gt;

&lt;p&gt;除了使用lambda创建快速创建函数外，也可以使用标准库里bind函数来生成函数，例如：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;// 格式： auto newCallable = bind(callable, arg_list);

void func1(int a, int b)
{
  return a + b;
}

int aa = 1;
int bb = 2;
auto newFunc1 = bind(func1, aa, bb); // 生成一个新的可调用对象来适配算法
auto wc = find_if(words.begin(), words.end(), newFunc1); // bind生成了一个适配泛型算法的函数
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;bind可以看做是一个通用的函数适配器，它接受一个可调用对象，生成一个新的可调用对象来“适应”原对象的参数列表。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484770&amp;amp;idx=1&amp;amp;sn=417618b18d6a0bf34b5fa720031f6312&amp;amp;chksm=fc226065cb55e973587b7a3e372b71acf1def7cb7b04ce3ba2d43a5e4871582f72459bcb962d&amp;amp;token=283688006&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(二十九) 《装载、链接与库》#2 静态链接过程</title>
   <link href="http://www.luzexi.com/2021/06/20/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B029"/>
   <updated>2021-06-20T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/06/20/读书笔记29</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484763&amp;amp;idx=1&amp;amp;sn=f1b3d678c1309a66d44b64b7ff22a002&amp;amp;chksm=fc22605ccb55e94a53558e85fdbe196fc58612fe20c88f35f8c0f4098e240ef48bcd2da099b0&amp;amp;token=535520395&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;背景&quot;&gt;背景：&lt;/h3&gt;

&lt;p&gt;看此书的起源是我在了解Linux注入技术的时候翻阅到的，由于注入技术需要用到很多ELF格式的内容，很多网络上的技术文章都指向了同一本书。也刚好周围的同事有此书，便翻阅了一下，这一番翻阅打开了我对程序世界的又一扇大门。&lt;/p&gt;

&lt;p&gt;很快我就自己买了此书并阅读完成，整本书给我很大的震撼，让我对程序从编译到链接到装载有了更深刻的认识。&lt;/p&gt;

&lt;p&gt;我把我的整个学习过程以及对书本的理解，用自己的语言和自己画的图表达出来，让读者能够更容易接受到我所学的知识。&lt;/p&gt;

&lt;h3 id=&quot;目标&quot;&gt;目标：&lt;/h3&gt;

&lt;p&gt;了解编译过程&lt;/p&gt;

&lt;p&gt;了解动态库和静态库的装载细节&lt;/p&gt;

&lt;p&gt;了解可执行程序装载和执行过程&lt;/p&gt;

&lt;p&gt;了解可执行文件和动态库的数据格式&lt;/p&gt;

&lt;h3 id=&quot;疑问&quot;&gt;疑问：&lt;/h3&gt;

&lt;p&gt;c/c++编译器是如何将cpp编译为可执行文件的？&lt;/p&gt;

&lt;p&gt;多个c/c++文件是如何编译成一个可执行文件的？&lt;/p&gt;

&lt;p&gt;操作系统内存是如何初始化和管理的？&lt;/p&gt;

&lt;p&gt;动态库和静态库的链接和装载过程是怎样的？&lt;/p&gt;

&lt;p&gt;操作系统的用户态和内核态是如何运作的？&lt;/p&gt;

&lt;h3 id=&quot;正文&quot;&gt;正文：&lt;/h3&gt;

&lt;p&gt;前文《链接、装载与库-回顾基础》说了计算机设备的架构，硬盘的存储方式，内存虚拟空间的意义，多线程调度和安全的本质，以及代码编译的整体流程。&lt;/p&gt;

&lt;p&gt;我们平常用C++代码编译出来的可执行文件、静态库、动态库，看上去它们的不一样的东西，其实它们的数据格式是相同的。&lt;/p&gt;

&lt;p&gt;现代流行的操作系统分Windows和Linux两种，因此我们主要介绍这两种平台下的文件格式。在Windows下的可执行文件格式称为PE（Portable Executable），在Linux下则称为ELF（Executable Linkable Format），其实它们都是COFF（Common file format）格式的变种。&lt;/p&gt;

&lt;p&gt;不光是可执行文件，动态链接库（DLL，Dynamic Linking Library）和静态链接库（Static Linking Library）都是按照可执行文件格式存储的。&lt;/p&gt;

&lt;p&gt;静态链接库稍有不同，它把很多目标文件捆绑在一起形成一个文件，可以简单把它理解为一个包含很多目标文件的文件包。&lt;/p&gt;

&lt;p&gt;单个目标文件简单的结构介绍&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;int global_init_var = 84; // 已初始化全局变量
int global_uninit_var; // 未初始化全局变量

//全局函数
void func1( int i)
{
    printf(&quot;%d\n&quot;,i);
}

//入口函数
int main(void)
{
    static int static_var = 85; // 已初始化静态变量
    static int static_var2; // 未初始化静态变量

    int a = 1; // 已初始化局部变量
    int b; // 未初始化局部变量
    func1(static_var + static_var2 + a + b); // 调用全局函数

    return 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们先以目标文件为例，来举一个简单的文件ELF结构。目标文件是最常见的编译单位，它将指令代码、数据以Section的形式存储在文件中。&lt;/p&gt;

&lt;p&gt;上图中我们简单说明了程序、已初始化全局数据、未初始化全局数据在目标文件中的存放位置。
其中.data段保存的是已初始化了的全局静态变量和局部静态变量，这些数据是可写可读的，而只读数据则存放在.rodata中，例如const修饰的变量和字符串常量。
用.rodata单独保存只读数据能够更加好的保护数据，任何修改.rodata中的数据都会作为非法操作处理。&lt;/p&gt;

&lt;p&gt;类似的Section段有很多，我们列举一些重要的来说：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;.bss：包含程序运行时未初始化的数据（全局变量和静态变量）。当程序运行时，这些数据初始化为0。
.data和.data1，包含初始化的全局变量和静态变量。
.dynamic，包含了动态链接的信息，包括链接器地址、需要的动态库、段地址信息，类型为SHT_DYNAMIC。
.dynstr，包含了动态链接用的字符串，通常是和符号表中的符号关联的字符串，类型为SHT_STRTAB。
.dynsym，包含动态链接函数符号表和地址，没有地址的则为0，标志SHF_ALLOC，类型为SHT_DYNSYM。
.fini，正常结束时要执行的析构程序，类型为SHT_PROGBITS。
.got，全局偏移表(global offset table)，类型为SHT_PROGBITS。
.hash，包含符号hash表，用于快速查找函数名的。标志SHF_ALLOC，类型为SHT_HASH。
.init，程序运行或加载时初始化程序。类型为SHT_PROGBITS。
.interp，该节内容是一个字符串，指定了程序链接器的路径名。如果文件中有一个可加载的segment包含该节，属性就包含SHF_ALLOC，否则不包含。类型为SHT_PROGBITS。
.plt 过程链接表（Procedure Linkage Table），类型为SHT_PROGBITS。
.rodata和.rodata1， 包含只读数据，组成不可写的段。标志SHF_ALLOC，类型为SHT_PROGBITS。
.shstrtab，包含section的名字，真正的字符串存储在.shstrtab中，其他都是索引。类型为SHT_STRTAB。
.strtab，包含字符串，通常是符号表中符号对应的变量名字和函数名。类型为SHT_STRTAB。
.symtab，Symbol Table，符号表。包含了所有符号信息，包括变量、函数、定位、重定位符号定义和引用时需要的信息。符号表是一个数组，Index 0 第一个入口，它的含义是undefined symbol index， STN_UNDEF。
.rela.dyn，包含了除PLT以外的 RELA 类型的动态库重定向信息。
.rela.plt，包含了PLT中的 RELA 类型的动态库重定向信息
.rela.text 代码重定位表
.rela.data 数据重定位表
.line，调试时的行号表，即源代码行号与编译后指令的对应表
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;如果我们想自定义些Section，可以用__attribute__((section(“name”)))把相应的变量或函数放到“name”作为段名的段中，例如：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;__attribute__((section(&quot;GlobalTest&quot;))) int global = 42;

__attribute__((section(&quot;FuncTest&quot;))) void foo()
{
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;那么ELF结构在文件中是以怎样的形式存在的呢？&lt;/p&gt;

&lt;h3 id=&quot;下面是elf文件的大致布局&quot;&gt;下面是ELF文件的大致布局&lt;/h3&gt;

&lt;p&gt;（来源ELF描述文档 http://www.skyfree.org/linux/references/ELF_Format.pdf）&lt;/p&gt;

&lt;p&gt;左边是ELF的链接视图，可以理解为是目标代码文件的内容布局。右边是ELF的执行视图，可以理解为可执行文件的内容布局。
目标代码文件（编译过程中的.o文件）的内容是由Section组成的，而可执行文件（二进制可执行文件或库文件）的内容是由Segment段组成的。&lt;/p&gt;

&lt;p&gt;ELF header的定义可以在 /usr/include/elf.h 中找到。Elf32_Ehdr是32位 ELF header的结构体。Elf64_Ehdr是64位ELF header的结构体。
    （Linker and Libraries Guide：https://docs.oracle.com/cd/E19120-01/open.solaris/819-0690/chapter6-43405/index.html）&lt;/p&gt;

&lt;p&gt;其中e_ident的前4个字节是魔数，魔数是固定的，它用来确认文件的类型，操作系统在加载可执行文件时会确认魔数是否正确，如果不正确则说明文件不是ELF格式，则拒绝加载。&lt;/p&gt;

&lt;h3 id=&quot;elf的整体结构&quot;&gt;ELF的整体结构&lt;/h3&gt;

&lt;p&gt;现在我们知道了，ELF Header描述了Program header table 和 Section header table，Program header table 又描述了Segment摘要，Section header table 又描述了Section摘要。&lt;/p&gt;

&lt;h3 id=&quot;那么-program-header-table-和-section-header-table-究竟是怎么去描述segment和section摘要的呢&quot;&gt;那么 Program header table 和 Section header table 究竟是怎么去描述Segment和Section摘要的呢？&lt;/h3&gt;

&lt;p&gt;前面我们说ELF Header里已经有了Program header table 和 Section header table的位置的偏移量，有了它们每个table的大小，以及有多少个table，我们就能知道Program header table 和 Section header table的起始地址、每个结构体的位置、以及它们的范围。&lt;/p&gt;

&lt;p&gt;也就是说，每个Program header table 和 Section header table是一个由固定大小结构体组成的数组，于是可以根据ELF Header来确定它们的数据内容和范围。&lt;/p&gt;

&lt;p&gt;我们来看下它们的结构体，Program header结构：&lt;/p&gt;

&lt;p&gt;Section header结构：&lt;/p&gt;

&lt;p&gt;这里特别从重要的几个Section中挑出几个特别重要的字段来重点说明下。&lt;/p&gt;

&lt;h3 id=&quot;重定位表&quot;&gt;重定位表&lt;/h3&gt;

&lt;p&gt;任何需要重定位的Section都需要一个重定位表，例如&lt;/p&gt;

&lt;p&gt;.text需要.rel.text来辅助重定位
.data需要.rel.data来辅助重定位
.rela.dyn，用于动态重定位变量的表
.rela.plt，则用于动态重定位函数的表&lt;/p&gt;

&lt;p&gt;重定位分为两种&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;一种是静态编译时重定位，在编译时重定位&lt;/li&gt;
  &lt;li&gt;另一种是动态加载时重定位，在执行时重定位&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;重定位我们放后面详细介绍&quot;&gt;重定位我们放后面详细介绍&lt;/h3&gt;

&lt;h3 id=&quot;字符串表&quot;&gt;字符串表&lt;/h3&gt;

&lt;p&gt;字符串主要用于匹配函数和变量名，但不同用途的字符串也会分开存放。&lt;/p&gt;

&lt;p&gt;.shstrtab，包含section名字的字符串存储在.shstrtab中
.strtab，包含符号表中符号对应的变量名字和函数名的字符串
.symtab，Symbol Table符号表，包含了所有符号信息，包括变量、函数、定位、重定位符号定义和引用时需要的信息，这里所有的字符串都只有索引指向.strtab中的内容。
.dynstr，包含了动态链接用的字符串，通常是和符号表中的符号关联的字符串
.hash，包含符号hash表，用于快速查找函数名和变量名&lt;/p&gt;

&lt;p&gt;字符串表中包含了存储字符串的表、存储索引的表、以及hash表，它们的用途分别是：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;存储字符串的表目的是存储真实字符串数据&lt;/li&gt;
  &lt;li&gt;其他表有需要字符串的都用索引表示以节省内存&lt;/li&gt;
  &lt;li&gt;hash表则主要用于快速查找和比较字符串&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;符号表&quot;&gt;符号表&lt;/h3&gt;

&lt;p&gt;符号表主要用于记录函数名、变量名和地址，例如：&lt;/p&gt;

&lt;p&gt;.symtab，Symbol Table符号表，包含了所有符号信息，包括变量、函数、定位、重定位符号定义和引用时需要的信息
.dynsym，包含动态链接函数符号表和地址，没有地址的则为0&lt;/p&gt;

&lt;p&gt;这两者的区别是，symtab记录了所有符号，包括内部的和外部的函数和变量，而.dynsym则只包含了对外动态加载时需要提供的函数和变量，可以.dynsym是.symtab的阉割版。&lt;/p&gt;

&lt;p&gt;symtab的数据结构为&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;typedef struct{
  Elf32_Word st_name; // 符号名在符号表中的索引
  Elf32_Addr st_value; // 符号相对应的值，是地址或者其他值
  Elf32_Word st_size; // 符号大小，包含数据的符号，该值为占用大小
  unsigned char st_info; // 符号类型和绑定信息
  unsigned char st_other; // 无用，值为0
  Elf32_Half st_shndx; // 符号所在段的索引
} Elf32_Sym;
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;函数签名规则&quot;&gt;函数签名规则&lt;/h3&gt;

&lt;p&gt;前面说函数名就是符号名，会存储在符号表中，但C++中函数名常常会有相同的时候，因此编译器在编译时会对函数名进行处理，以区别不同范围内的函数。&lt;/p&gt;

&lt;p&gt;我们称这种改变符号名的机制为函数签名（Function Signature）。
签名后的函数名，包含了函数名、它的参数类型、它所在的类和命名空间以及其他信息。&lt;/p&gt;

&lt;p&gt;签名规则每种编译器都不一样，GCC编译器的规则为，所有符号以“_Z”开头，对于嵌套的名字后面紧跟“N”，然后是各个名称空间和类的名字，每个名字前是名字字符串长度，再以“E”结尾，“E”后面是参数列表，对于int类型来说就是字母“i”，float类型来说就是字母“f”，以此类推。&lt;/p&gt;

&lt;p&gt;来举几个例子：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;* int func(int)  签名后--&amp;gt; _Z4funci --&amp;gt; _Z开头，4个字符的函数名func，参数是i整型
* float func(float) 签名后--&amp;gt; _Z4funcf --&amp;gt; _Z开头，4个字符的函数名func，参数是f浮点型
* int C::func(int) 签名后--&amp;gt; _ZN1C4funcEi --&amp;gt; _Z开头，嵌套1个字符C，接4个字符的函数名func，E结尾，参数是i整型
* int C::C2::func(int) 签名后--&amp;gt; _ZN1C2C24funcEi --&amp;gt; _Z开头，嵌套1个字符C，再嵌套2个字符C2，接4个字符的函数名func，E结尾，参数是i整型
* int N::func(int) 签名后--&amp;gt; _ZN1N4funcEi --&amp;gt; _Z开头，嵌套1个字符N，接4个字符函数名func，E结尾，参数是i整型
* int N::C::func(int) 签名后--&amp;gt; _ZN1N1C4funcEi --&amp;gt; _Z开图，嵌套1个字符N，再嵌套1个字符C，接4个字符func，E结尾，参数是i整型
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;由于不同编译器的签名规则不同，所以当我们要使用C语言写的外部函数时，就需要告诉编译器使用C语言的命名规则&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;extern &quot;C&quot;{
  int func(int);
  int var;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;如果我们在C++中没有使用extern “C”申明会如何？
如果我们没有正确告知编译器外部引用函数的编译器规则，则在链接器链接时就会因为符号不匹配而无法链接，因此对于C++引用C函数来说，必须使用extern “C”来声明编译器规则。&lt;/p&gt;

&lt;h3 id=&quot;强符号弱符号强引用弱引用&quot;&gt;强符号弱符号，强引用弱引用&lt;/h3&gt;

&lt;p&gt;通常我们定义的变量和函数都是强符号和强引用，意思是当符号冲突时会直接覆盖原来的符号，而弱符号和弱引用则是当符号冲突时不覆盖原来的。
在编写通用库、共享库程序时，弱符号和弱引用，可以起到替换和被替换的作用。
例如库中定义的弱符号被用户定义的强符号覆盖，从而使得程序可以使用自定义版本的库函数；或者扩展某个功能模块时，弱引用被新模块接口覆盖，从而使得程序可以在正常使用的情况下扩展功能。
&lt;strong&gt;attribute&lt;/strong&gt;((week)) week_var = 2;
&lt;strong&gt;attribute&lt;/strong&gt;((weekref)) void func();&lt;/p&gt;

&lt;p&gt;Linux提供了几个工具来帮助我们了解ELF格式内容&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;readelf，可以帮助我们读取ELF文件中Section的内容&lt;/li&gt;
  &lt;li&gt;objdump，可以帮助我们翻译二进制指令和数据&lt;/li&gt;
  &lt;li&gt;c++filt，可以帮助我们解析被修饰过的名称&lt;/li&gt;
  &lt;li&gt;编译时通过加 -g 参数，可以让编译器在产生目标文件时加上调试信息&lt;/li&gt;
  &lt;li&gt;strip，可以通过这个命令把ELF中的调试信息都去除&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;静态链接&quot;&gt;静态链接&lt;/h3&gt;

&lt;p&gt;静态链接的过程其实就是把目标文件中的数据归档到一个文件中，其归档步骤有两步。&lt;/p&gt;

&lt;h3 id=&quot;第一步空间与地址分配&quot;&gt;第一步，空间与地址分配&lt;/h3&gt;

&lt;p&gt;扫描所有输入的目标文件，获取它们的各Section的数据，合并各段数据并建立映射关系。
同时把所有目标文件中的符号和符号定义都收集起来放到全局符号表中。&lt;/p&gt;

&lt;h3 id=&quot;第二步符号解析与重定位&quot;&gt;第二步，符号解析与重定位&lt;/h3&gt;

&lt;p&gt;根据第一步收集到的信息，包括Section和符号表信息，进行符号解析与重定位，调整代码中的地址。&lt;/p&gt;

&lt;h3 id=&quot;在重定位时由于各个符号的在section内的位置是相对固定的所以只要计算出section的偏移量就可以知道合并后的符号实际位置&quot;&gt;在重定位时，由于各个符号的在Section内的位置是相对固定的，所以只要计算出Section的偏移量就可以知道合并后的符号实际位置。&lt;/h3&gt;

&lt;h3 id=&quot;在第一步完成空间分配后就可以确定所有符号的虚拟地址了但这时目标文件内含有其他目标文件函数则需要另外通过符号表来重定位&quot;&gt;在第一步完成空间分配后，就可以确定所有符号的虚拟地址了，但这时目标文件内含有其他目标文件函数，则需要另外通过符号表来重定位。&lt;/h3&gt;

&lt;h3 id=&quot;由于在完成空间分配后全局符号表内的符号都确定了地址因此在重定位时可以使用全局符号表中的地址替换未定位地址&quot;&gt;由于在完成空间分配后，全局符号表内的符号都确定了地址，因此在重定位时可以使用全局符号表中的地址替换未定位地址。&lt;/h3&gt;

&lt;p&gt;每个目标文件都可能定义一些符号，也可能引用到定义在其他目标文件的符号。链接器在重定位的过程中，每个重定位的入口都是一个符号的引用，当需要对某个符号的引用进行重定位时，就要确定这个符号的目标地址。这时候链接器就会去查找由所有输入目标文件的符号表组成的全局符号表，找到相应的符号后进行重定位。&lt;/p&gt;

&lt;h3 id=&quot;注意在未确定外部符号地址时编译器把这部分地址暂时用0x00000000和0xfffffffc来代替在链接时再由链接器将计算好的地址填充进来&quot;&gt;注意：在未确定外部符号地址时，编译器把这部分地址暂时用“0x00000000”和“0xFFFFFFFC”来代替，在链接时再由链接器将计算好的地址填充进来。&lt;/h3&gt;

&lt;h3 id=&quot;重定位表-1&quot;&gt;重定位表&lt;/h3&gt;

&lt;p&gt;我们前面说过每个需要重定位的Section都有一个重定位表，例如“.text”有“.rel.text”，“.data”有“.rel.data”。&lt;/p&gt;

&lt;p&gt;每个重定位表里的数据为：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;typedef struct{
  Elf32_Addr r_offset; // 相对于当前Section的相对位置
  Elf32_Word r_info; // 低8位表示重定位入口类型，高24位表示重定位入口符号的索引
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;简单来说就是，重定位表数据中有一个需要重定位的位置和一个重定位的符号名字符串索引，有了这两个数据，链接器就能在链接时从全局符号表中找到合并后的真实虚拟地址。&lt;/p&gt;

&lt;p&gt;在符号地址修正时，也分绝对寻址修正和相对寻址修正，它们的区别是绝对寻址修正后的地址为该符号的实际虚拟地址，相对寻址修正后的地址为符号距离被修正位置的地址差，因此它们分别服务的是不同类型的指令。&lt;/p&gt;

&lt;h3 id=&quot;全局构造和析构表&quot;&gt;全局构造和析构表&lt;/h3&gt;

&lt;p&gt;.init段中保存的初始化代码。当一个程序开始运行时在main函数调用之前Glibc的初始化程序会执行这个段中的代码。
.fini段中保存的析构代码。当一个进程的main函数正常退出时Glibc会安排执行这个段中的代码。
这两个段有特别的目的，.init在main函数前执行，.fini则在main函数后执行，利用这两个特性C++的全局构造和析构函数就由此实现。&lt;/p&gt;

&lt;h3 id=&quot;将不同编译器的编译结果链接&quot;&gt;将不同编译器的编译结果链接&lt;/h3&gt;

&lt;p&gt;那么有没有可能将不同编译器的编译结果链接成一个可执行文件呢？其实可以的，但非常困难。&lt;/p&gt;

&lt;p&gt;目标文件必须满足以下条件：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;目标文件的数据格式兼容&lt;/li&gt;
  &lt;li&gt;符号签名规则兼容&lt;/li&gt;
  &lt;li&gt;变量的内存分布方式兼容&lt;/li&gt;
  &lt;li&gt;函数调用方式兼容&lt;/li&gt;
  &lt;li&gt;堆栈分布方式兼容&lt;/li&gt;
  &lt;li&gt;寄存器使用阅读兼容&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;我们把这种链接时的兼容性相关内容称为ABI（Application Binary Interface）。与API不同的是，ABI是指二进制层面的接口兼容性更加严格，而API则只是源代码级别上的的接口。
其实不同编译器链接成可执行文件的最大问题就是，各种硬件平台、编程语言、编译器、链接器和操作系统之间的ABI互不兼容。由于ABI的不兼容，导致各个目标文件之间无法互相链接。&lt;/p&gt;

&lt;h3 id=&quot;静态库与链接&quot;&gt;静态库与链接&lt;/h3&gt;

&lt;p&gt;其实静态库可以简单看成一组目标文件的集合，在制作静态库时只是将所有目标文件打包压缩后归档为一个文件而已。&lt;/p&gt;

&lt;p&gt;Linux下可以使用“ar”这个工具命令来归档目标文件，“ar”压缩程序将所有目标文件压缩到一起并对其进行编号和索引，以便于查找和检索，就形成了xxx.a静态库文件。Visual C++也提供了类似ar的工具，叫lib.exe，这个程序可以用来创建、提取、查看.lib文件中的内容。&lt;/p&gt;

&lt;p&gt;在静态库链接时，Linux下的链接器ld会自动寻找需要的静态库，并从中解压出来目标文件，再最终将这些目标文件链接在一起成为可执行文件。&lt;/p&gt;

&lt;p&gt;从这个规则上看来，我们在编写静态库的时候，最好是每个函数独立存放在一个目标文件中，因此这样可以尽量减少空间浪费，因为没有被用到的目标文件就不会链接到最终的输出文件中。&lt;/p&gt;

&lt;h3 id=&quot;中间件bfd库&quot;&gt;中间件BFD库&lt;/h3&gt;

&lt;p&gt;我们说各种操作系统平台都遵循类似ELF格式的文件格式，虽然是相似的ELF格式但在不同软硬件平台都有着不同的变种。
种种差异导致编译器和链接器很难处理不同平台之间的目标文件，因此最好有一种统一的接口来处理不同格式的文件。
BFD库（Binary File Dexcriptor library）就是为了这个目的存在的，它的目标就是用一种统一接口来处理不同的目标文件格式。&lt;/p&gt;

&lt;p&gt;现在GCC编译器、链接器ld、调试器gdb、binutils等工具都是通过BFD库来处理文件，而不直接操作目标文件。
这样做的好处是将编译器和链接器与具体的目标文件格式隔离开来，一旦我们需要支持一种新的目标文件格式，只需要在BFD库里添加一种格式就可以了，而不需要再去修改编译器和链接器。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484763&amp;amp;idx=1&amp;amp;sn=f1b3d678c1309a66d44b64b7ff22a002&amp;amp;chksm=fc22605ccb55e94a53558e85fdbe196fc58612fe20c88f35f8c0f4098e240ef48bcd2da099b0&amp;amp;token=535520395&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>读书笔记(二十八) 《C++ Primer》#3</title>
   <link href="http://www.luzexi.com/2021/06/19/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B028"/>
   <updated>2021-06-19T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/06/19/读书笔记28</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484764&amp;amp;idx=1&amp;amp;sn=edc0beab7722d092571c8f88a9a38bd6&amp;amp;chksm=fc22605bcb55e94d95e3445b7449c42f87450a2fcc8dd16d63f44a242b17e63b6f5051e91dcd&amp;amp;token=535520395&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;背景&quot;&gt;背景：&lt;/h3&gt;

&lt;p&gt;我为什么要重学C++？第一是巩固核心，软件编程有三大核心，语言、操作系统、框架设计，语言首当其冲，核心能力大都看不见摸不着只有你自己知道的东西。第二是将分散的知识串联起来，这样就能产生新的知识和新的创意。第三是当我们在巩固时创造出了新的知识，那么这些新的知识和旧的知识将同时变成智慧融合到身体中。&lt;/p&gt;

&lt;p&gt;本系列基于《C++ Primer》学习，由于重点放在“重学”，我略过了滚瓜烂熟的部分，挑出以前常忽略的部分，以及记忆没有那么深刻的部分，特别是那些重要的但没有上心的部分。&lt;/p&gt;

&lt;h3 id=&quot;开始&quot;&gt;开始&lt;/h3&gt;

&lt;h3 id=&quot;左值和右值&quot;&gt;左值和右值&lt;/h3&gt;

&lt;p&gt;这两个名词是从C语言继承过来的，原本是为了帮助记忆即：左值可以位于赋值语句的左侧，右值则不能。
在C++语言中则没那么简单了，可以简单归纳为：
当一个对象被用作右值的时候，用的是对象的值（内容）；当对象被用作左值的时候，用的是对象的身份（在内存中的位置）。&lt;/p&gt;

&lt;p&gt;不同运算符对运算对象的要求不同，有的需要左值运算对象、有的则需要右值运算对象。
因此有个重要原则：在需要右值的地方可以用左值来代替，但不能把右值当左值（也就是位置）使用。当一个左值被当成右值使用时，实际使用的是它的内容（就是值）。&lt;/p&gt;

&lt;p&gt;举几个例子：
	赋值运算符，是左值运算，因为结果是值（内容）
	取地址符，是右值运算，因为结果的是指针或地址
	解引用运算符、下标运算符，是左值运算，因为运算结果是值（内容）
	递增运算，是左值运算，因为运算结果是值（内容）&lt;/p&gt;

&lt;h3 id=&quot;关于后置&quot;&gt;关于后置++&lt;/h3&gt;

&lt;p&gt;这里有个小提示，后置++版本会多浪费一个寄存器，但是编译器会帮我们优化掉，不过对于迭代器来说则不同，编译器通常不会优化迭代器的后置++，因此我们在使用迭代器后置++时要注意一下代码的优化。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;cout&amp;lt;&amp;lt; *iter++ &amp;lt;&amp;lt; endl;
==&amp;gt; 优化
count&amp;lt;&amp;lt; *iter &amp;lt;&amp;lt; endl;
++iter;
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;sizeof运算符&quot;&gt;sizeof运算符&lt;/h3&gt;

&lt;p&gt;sizeof有离线计算和实时运算之分，通常情况下有些值在离线时就能知道的类型的大小，这时编译器都会在编译时就将其替换成固定值。&lt;/p&gt;

&lt;p&gt;举例说明sizeof运算符的结果取决于其作用类型：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;对char类型表达式执行sizeof运算，结果为1。编译器离线计算&lt;/li&gt;
  &lt;li&gt;对引用类型执行sizeof，结果为被引用对象所占空间大小。编译器离线计算&lt;/li&gt;
  &lt;li&gt;对指针执行sizeof运算，结果为指针本身空间大小。编译器离线计算&lt;/li&gt;
  &lt;li&gt;对解引用指针执行sizeof，结果为指针指向的对象的空间大小。编译器离线计算&lt;/li&gt;
  &lt;li&gt;对数组执行sizeof运算，结果是整个数组所占空间大小。编译器离线计算&lt;/li&gt;
  &lt;li&gt;对string对象或vector对象执行sizeof运算，结果为该类型固定部分的大小。实时计算空间大小&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;强制类型转换&quot;&gt;强制类型转换&lt;/h3&gt;

&lt;p&gt;强制类型转换的根本目的就是告诉编译器，该变量的类型改变了，符合后面的逻辑运算，好让在编译器在编译时能够不误解逻辑运算，所以编译器并没有对内存内容做任何的改变，它只是为了迎合编译器。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;//旧式的强制类型转换
type (expr); // 函数形式的强制类型转换
(type) expr; // c语言风格的强制类型转换

//新式的强制类型转换
static_cast&amp;lt;type&amp;gt;(expression); // 静态转换
dynamic_cast&amp;lt;type&amp;gt;(expression); // 运行时转换
const_cast&amp;lt;type&amp;gt;(expression); // 常量转换
reinterpret_cast&amp;lt;type&amp;gt;(expression); // 低阶类型转换
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;强制转换分为static_cast静态转换dynamic_cast运行时转换const_cast常量转换reinterpret_cast低阶类型转换4种&quot;&gt;强制转换分为，static_cast（静态转换），dynamic_cast（运行时转换），const_cast（常量转换），reinterpret_cast（低阶类型转换）4种&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;static_cast静态转换，直接将对象转换成指定类型对象。&lt;/li&gt;
  &lt;li&gt;dynamic_cast运行时转换，通常用于继承对象，运行时转换会先通过RTTI的方式判断是否能进行父子转换，如果可以再进行执行转换。&lt;/li&gt;
  &lt;li&gt;const_cast常量转换，只能改变运算对象的底层const无法改变具体类型，可以将const变量转换为非const变量，这样编译器就不再阻止我们对该对象进行写操作了。&lt;/li&gt;
  &lt;li&gt;reinterpret_cast低阶类型转换，通常为运算对象的位模式提供低层次上的解释。例如将int转换为char*等低阶模式。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;注意使用reinterpret_cast比较危险的是使用reinterpret_cast强制转换后编译器不会怀疑所转换的类型如果类型错误则会因调用混乱而崩溃而编译器在编译时不会发出任何警告或错误因此reinterpret_cast通常适合比较底层转换工作&quot;&gt;注意：使用reinterpret_cast比较危险的是，使用reinterpret_cast强制转换后编译器不会怀疑所转换的类型，如果类型错误则会因调用混乱而崩溃，而编译器在编译时不会发出任何警告或错误，因此reinterpret_cast通常适合比较底层转换工作。&lt;/h3&gt;

&lt;h3 id=&quot;异常处理&quot;&gt;异常处理&lt;/h3&gt;

&lt;p&gt;当异常被抛出时，首先在该函数中寻找匹配的catch子句，如果没有找到则终止该函数并在调用该函数的函数中继续寻找，以此类推沿着程序的执行路径逐层回退，直到找到适当类型的catch子句为止。
如果最终还是没能找到任何匹配的catch子句，程序会转到名为terminate的标准库函数中。该函数的行为与系统有关，一般情况下，执行该函数会导致程序非正常退出。
如果当程序没有使用try catch语句且发生了异常，系统会直接调用terminate函数并终止当前程序的执行。&lt;/p&gt;

&lt;h3 id=&quot;分离式编译&quot;&gt;分离式编译&lt;/h3&gt;

&lt;p&gt;分离式编译的意思就是，多个源文件在编译中可以只顾自己编译，最后再统一链接到执行文件或库文件中。&lt;/p&gt;

&lt;p&gt;在分离式编译中，如果我们修改了其中一个源文件，那么只需要重新编译那个改动了的文件，再重新链接就可以。
现代大多数编译器提供了分离式编译每个文件的机制，这一过程通常会产生一个后缀名是.obj（windows）或.o(UNIX)的目标文件，再用链接器将所有目标文件链接成可执行文件或库文件。&lt;/p&gt;

&lt;h3 id=&quot;参数传递&quot;&gt;参数传递&lt;/h3&gt;

&lt;p&gt;解释下，实参就是要传递的数据变量，形参就是传递后函数中的参数变量。&lt;/p&gt;

&lt;p&gt;参数传递分引用传递和值传递&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;引用传递，通过传递引用的方式避免了对象拷贝。&lt;/li&gt;
  &lt;li&gt;值传递，则都是通过拷贝对象传递数据的。其中指针形参也是值传递，只不过传递时拷贝的是指针数据而非具体对象。&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;函数设置参数时，我们要注意尽量使用const常量引用或指针作为形参，因为这样既避免了对象拷贝，又可以限制对象的修改。&lt;/p&gt;

&lt;h3 id=&quot;可变形参&quot;&gt;可变形参&lt;/h3&gt;

&lt;p&gt;使用initializer_list类型的形参可以做到传递可变数量的实参。和Vector不一样的是，initializer_list中的元素永远都是常量值。&lt;/p&gt;

&lt;p&gt;举个例子：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;void test_func(initializer_list&amp;lt;string&amp;gt; li)
{
  for(auto beg = li.begin(); beg != li.end() ; ++beg)
      cout&amp;lt;&amp;lt; *beg &amp;lt;&amp;lt; &quot; &quot;;
}

test_func(&quot;1&quot;,&quot;3&quot;,&quot;4&quot;);
test_func(&quot;2&quot;,&quot;3&quot;);
test_func(&quot;2&quot;,&quot;4&quot;,&quot;6&quot;,&quot;8&quot;,&quot;10&quot;);
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;值返回和引用返回&quot;&gt;值返回和引用返回&lt;/h3&gt;

&lt;p&gt;与值传递和引用传一样，在函数返回时，我们也需要注意拷贝操作。&lt;/p&gt;

&lt;p&gt;例如字符串拼接后的返回值就会造成对象拷贝的消耗：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;string test_func(string &amp;amp;a, string &amp;amp;b)
{
   return a + b;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;为了避免拷贝我们会使用引用返回或指针，但要注意，使用引用返回或指针时如果返回的是局部对象则会引起内存使用错误。&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;string &amp;amp;test_func()
{
   string test = &quot;test&quot;;
   return test; // 错误的返回了局部变量，导致内存使用错误
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;函数重载&quot;&gt;函数重载&lt;/h3&gt;

&lt;p&gt;函数重载的特点是函数名字相同参数列表不同且在同一个作用域。&lt;/p&gt;

&lt;p&gt;在重载函数调用时，可能与我们想象的不同，其实编译器在离线时就区分了不同重载函数的调用地址。
当调用重载函数时，编译器会根据传递的实参类型去比较每个重载函数的参数直到匹配成功或失败。
我们知道虽然重载函数名字相同，但参数列表不同，这导致函数签名时的实际名字是不同，也就相当于调用的是不同名字的函数。&lt;/p&gt;

&lt;p&gt;举例说明重载函数的函数签名后命名不同：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;int func(int); //  函数签名后--&amp;gt; _Z4funci 
int func(float); // 函数签名后--&amp;gt; _Z4funcf

func(3); // -&amp;gt; 匹配 _Z4funci
func(3.5F); // -&amp;gt; 匹配 _Z4funcf
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;重载函数的匹配过程为：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;收集所有重载函数&lt;/li&gt;
  &lt;li&gt;比较实参与所有重载函数的形参，寻找最佳匹配函数&lt;/li&gt;
  &lt;li&gt;匹配规则为精准匹配和转换匹配两种，精准匹配要求实参与形参高度一致，转换则可以通过const转换、类型提升、算术类型转换来实现匹配。&lt;/li&gt;
  &lt;li&gt;匹配成功后替换为匹配函数的函数签名&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;至于函数签名的规则，其实每种编译器都不同，不过也是大同小异的。例如上述我们举例代码中的GCC编译器的规则为：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;所有符号以“_Z”开头，&lt;/li&gt;
  &lt;li&gt;对于嵌套的名字后面紧跟“N”，&lt;/li&gt;
  &lt;li&gt;然后紧跟各个名称空间和类的名字，每个名字前是名字字符串长度，&lt;/li&gt;
  &lt;li&gt;再以“E”结尾，“E”后面是所有参数，&lt;/li&gt;
  &lt;li&gt;参数以简写代替，例如对于int类型来说就是字母“i”，float类型来说就是字母“f”，以此类推。&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;内联函数&quot;&gt;内联函数&lt;/h3&gt;

&lt;p&gt;我们知道函数在调用时会先保存当前寄存器、拷贝实参值、并在返回时拷贝恢复，这些都是函数调用的消耗。&lt;/p&gt;

&lt;p&gt;内联函数则可以避免函数调用的消耗，其原理是就地展开函数，这样能省去了函数调用前和后的一系列操作。
我们可以通过内联关键字inline告诉编译器该函数需要在编译器时展开，但这也只是向编译器发出请求而已，如果编译器认为该内敛展开后性价比不高可以选择忽略这个请求。&lt;/p&gt;

&lt;p&gt;常量表达函数constexpr，我们可以通过constrexpr定义常量表达式的函数，与普通内联函数不同的是，编译器会把constexpr函数的调用替换成实际结果，简单来说就是离线计算好结果并替换调用函数。&lt;/p&gt;

&lt;p&gt;对于某个给定的内联函数或者constexpr函数来说，它的多个定义必须完全一致，基于这个原因，内联函数和constexpr函数通常定义在头文件中。&lt;/p&gt;

&lt;h3 id=&quot;调试辅助指令&quot;&gt;调试辅助指令&lt;/h3&gt;

&lt;p&gt;assert是常见的调试工具，它其实是一个预处理宏，通过判断表达式来终止程序执行。&lt;/p&gt;

&lt;p&gt;assert的行为依赖于一个名为NDEBUG的预处理变量。如果定义了NDEBUG，则assert什么也不做。
默认状态下没有定义NDEBUG，我们可以使用#define来定义NDEBUG从而关闭调试状态，或者使用编译器的预处理指令
cc -D NDEBUG main.c&lt;/p&gt;

&lt;p&gt;除了assert，C++编译器还定义了多个帮助我们调试的变量，例如：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;func&lt;/strong&gt;，存放的是当前函数名字&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;FILE&lt;/strong&gt;，存放的是当前文件名&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;LINE&lt;/strong&gt;，存放的是当前代码行号&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;TIME&lt;/strong&gt;，存放的是当前文件编译时间&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;DATA&lt;/strong&gt;，存放的是当前文件编译日期&lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;函数指针&quot;&gt;函数指针&lt;/h3&gt;

&lt;p&gt;函数指针指向的是函数地址，它的类型由它的返回值类型、形参类型共同决定，编译器会检查函数类型与函数指针类型之间的一致性。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;bool (*point_func)(const string &amp;amp;, const string &amp;amp;);
// point_func前面有个*，因此point_func是指针，这里括号不能少。
// point_func右侧是形参列表，表示point_func指向的是，两个const string引用为参数、并且返回值是bool的函数类型。

bool lenCompare(const string &amp;amp;, const string &amp;amp;);

point_func = lenCompare; //指向lenCompare
point_func = &amp;amp;lenCompare; //等价于赋值
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在指向不同函数类型的指针间不存在转换规则，也就是说，不同类型函数之间无法转换，即我们无法将一个类型函数转换到另一个类型函数指针。&lt;/p&gt;

&lt;p&gt;在将函数指针用于形参、返回值、重载函数时，都必须遵循函数类型与函数指针一致性的原则，编译器不会自动将函数类型转换成我们想要的类型，因为转换是不允许的。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484764&amp;amp;idx=1&amp;amp;sn=edc0beab7722d092571c8f88a9a38bd6&amp;amp;chksm=fc22605bcb55e94d95e3445b7449c42f87450a2fcc8dd16d63f44a242b17e63b6f5051e91dcd&amp;amp;token=535520395&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>给女儿写信(十三) 要赢的是自己</title>
   <link href="http://www.luzexi.com/2021/06/16/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A113"/>
   <updated>2021-06-16T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/06/16/给女儿的信13</id>
   <content type="html">
&lt;hr /&gt;

&lt;p&gt;导读：喜欢跟孩子们一起，可惜一直在外地工作，因此我给孩子写点信并录成视频，跟孩子聊聊自己的故事以及最近的感受&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Sharon，Anne，爸爸回到深圳了，很想你们哟。&lt;/p&gt;

&lt;p&gt;这里的天气和杭州一样暖，只是这里没有太多的雨。爸爸又回到了公司开始工作了。&lt;/p&gt;

&lt;p&gt;想起在家里和你们一起的这几天，爸爸很开心哟，爸爸喜欢和你们一起玩、一起学习。&lt;/p&gt;

&lt;p&gt;还记得Sharon跟爸爸一起玩拼图游戏的时候吗，Sharon老想着要和爸爸比谁快。&lt;/p&gt;

&lt;p&gt;爸爸跟你说，不要总想着和别人比，要和自己比。&lt;/p&gt;

&lt;p&gt;Sharon疑惑的问爸爸，什么是跟自己比。&lt;/p&gt;

&lt;p&gt;爸爸跟你说，爸爸以前也总是和别人比，但是和别人比总会让爸爸很紧张发挥不出真正的实力，因为爸爸总想赢，太着急了太在意了赢得也不轻松，输了就会很失落。&lt;/p&gt;

&lt;p&gt;所以当爸爸想着和别人比的时候，情绪波动很大，特别遇到比自己厉害很多的人时心情就更低落了。&lt;/p&gt;

&lt;p&gt;爸爸后来不和别人比了，因为这样太不好了，转而和自己比。&lt;/p&gt;

&lt;p&gt;怎么比呢，例如，去年我能看多少本书，今年我就比去年多看几本书。&lt;/p&gt;

&lt;p&gt;去年我能认识多少字，今年我就比去年的我多认识一些。&lt;/p&gt;

&lt;p&gt;每天坚持积累一些些，今天的我比昨天的我进步一些些，每天都在跟昨天的自己比，跟过去的自己比，我每次都能战胜过去的自己，我就像是不断战胜自己的勇士那样，充满了斗志。&lt;/p&gt;

&lt;p&gt;即使这样爸爸有时候也还是会陷入到和别人比较的陷进中去，但爸爸慢慢习惯和自己比赛后，挣脱出这种困境就越来越容易了。&lt;/p&gt;

&lt;p&gt;由于每天积累看书，帮助爸爸建立起了丰富的知识，让爸爸在工作和生活当中更加轻松自如哟。&lt;/p&gt;

&lt;p&gt;Sharon、Anne要和爸爸一起加油哟。爱你们。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(二十七) 《装载、链接与库》#1 回顾基础</title>
   <link href="http://www.luzexi.com/2021/06/14/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B027"/>
   <updated>2021-06-14T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/06/14/读书笔记27</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484732&amp;amp;idx=1&amp;amp;sn=e53f438b8815df3840208d7caa36f4f7&amp;amp;chksm=fc22603bcb55e92debcabe242e42139c805b56addbc5993c1f521e967806f5bb76d62b0bb612&amp;amp;token=890029854&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;背景&quot;&gt;背景：&lt;/h2&gt;

&lt;p&gt;看此书的起源是我在了解Linux注入技术的时候翻阅到的，由于注入技术需要用到很多ELF格式的内容，很多网络上的技术文章都指向了同一本书。也刚好周围的同事有此书，便翻阅了一下，这一番翻阅打开了我对程序世界的又一扇大门。&lt;/p&gt;

&lt;p&gt;很快我就自己买了此书并阅读完成，整本书给我很大的震撼，让我对程序从编译到链接到装载有了更深刻的认识。&lt;/p&gt;

&lt;p&gt;为了能更好的巩固知识，我把我的整个学习过程以及对书本的理解，用自己的语言和自己画的图表达出来，让读者能够很好的接受到我所学的知识。&lt;/p&gt;

&lt;h2 id=&quot;目标&quot;&gt;目标：&lt;/h2&gt;

&lt;p&gt;了解编译过程&lt;/p&gt;

&lt;p&gt;了解动态库和静态库的装载细节&lt;/p&gt;

&lt;p&gt;了解可执行程序装载和执行过程&lt;/p&gt;

&lt;p&gt;了解可执行文件和动态库的数据格式&lt;/p&gt;

&lt;h2 id=&quot;疑问&quot;&gt;疑问：&lt;/h2&gt;

&lt;p&gt;c/c++编译器是如何将cpp编译为可执行文件的？&lt;/p&gt;

&lt;p&gt;多个c/c++文件是如何编译成一个可执行文件的？&lt;/p&gt;

&lt;p&gt;操作系统内存是如何初始化和管理的？&lt;/p&gt;

&lt;p&gt;动态库和静态库的链接和装载过程是怎样的？&lt;/p&gt;

&lt;p&gt;操作系统的用户态和内核态是如何运作的？&lt;/p&gt;

&lt;h2 id=&quot;正文&quot;&gt;正文：&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484732&amp;amp;idx=1&amp;amp;sn=e53f438b8815df3840208d7caa36f4f7&amp;amp;chksm=fc22603bcb55e92debcabe242e42139c805b56addbc5993c1f521e967806f5bb76d62b0bb612&amp;amp;token=890029854&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(二十六) 《C++ Primer》#2</title>
   <link href="http://www.luzexi.com/2021/06/12/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B026"/>
   <updated>2021-06-12T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/06/12/读书笔记26</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484732&amp;amp;idx=3&amp;amp;sn=13204e3fd9171849653497a8c788c649&amp;amp;chksm=fc22603bcb55e92dfe641a5066c21000b70a3a03a61d2c691324b23a20ae3fffeef40a70facd&amp;amp;token=890029854&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;背景&quot;&gt;背景：&lt;/h2&gt;

&lt;p&gt;我为什么要重学C++？第一是巩固核心，软件编程有三大核心，语言、操作系统、框架设计，语言首当其冲，核心能力大都看不见摸不着只有你自己知道的东西。第二是将分散的知识串联起来，这样就能产生新的知识和新的创意。第三是当我们在巩固时创造出了新的知识，那么这些新的知识和旧的知识将同时变成智慧融合到身体中。&lt;/p&gt;

&lt;p&gt;本系列基于《C++ Primer》学习，由于重点放在“重学”，我略过了滚瓜烂熟的部分挑出以前常忽略的部分，以及记忆没有那么深刻的部分，特别是那些重要的但没有上心的部分。&lt;/p&gt;

&lt;h2 id=&quot;开始&quot;&gt;开始&lt;/h2&gt;

&lt;h2 id=&quot;1const限定&quot;&gt;1.const限定&lt;/h2&gt;

&lt;p&gt;关键字const对变量的类型加以限定，因此它的值在初始化后不能被改变。&lt;/p&gt;

&lt;p&gt;const默认情况下只在文件内生效，当需要支持多个文件共享时，则使用extern关键字，不管定义和声明都添加extern关键字，并且只需要定义一次。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c--&quot; data-lang=&quot;c++&quot;&gt;&lt;span class=&quot;c1&quot;&gt;//a.cpp&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test_constVar&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fcn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;//b.cpp&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test_constVar&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;通过添加extern共享const变量。&lt;/p&gt;

&lt;h2 id=&quot;const引用常量引用&quot;&gt;const引用（常量引用）&lt;/h2&gt;

&lt;p&gt;与普通引用不同的是，常量引用不能被用作它修改它所绑定的对象。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c--&quot; data-lang=&quot;c++&quot;&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ci&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ci&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;r1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//错误，常量引用不能修改绑定对象&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;r2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ci&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//错误，普通引用无法赋值常量&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;严格来说，并不存在常量引用。因为引用不是一个对象，所以我们没法让引用本身恒定不变。
事实上，由于c++语言并不允许随意改变引用所绑定的对象，所以从这层意义上理解所有的引用又都算是常量。
引用的对象是常量还是非常量可以决定其所能参与的操作，却无论如何都不会影响到引用和对象的绑定关系。
简单来说，由于绑定对象无法改变，所以要求引用对象也不能改变。&lt;/p&gt;

&lt;p&gt;但const引用可以绑定一个非const对象，其意义为，绑定后const引用无法改变，只能由所绑定的对象来决定内容。&lt;/p&gt;

&lt;h2 id=&quot;const指针常量指针&quot;&gt;const指针（常量指针）&lt;/h2&gt;

&lt;p&gt;const指针与const引用有同样的规则。
const指针在初始化后无法改变，const指针不能改变所指对象的值，但可以指向非常量变量。
当指向非常量时，const指针所指向的对象的值，只能由对象本身来决定。&lt;/p&gt;

&lt;p&gt;C++11中，用constexpr来表示常量表达式，声明为constexpr的变量是一个常量，编译器会验证变量的值是否为常量表达式并离线计算该值。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c--&quot; data-lang=&quot;c++&quot;&gt;&lt;span class=&quot;k&quot;&gt;constexpr&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//离线计算&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;constexpr&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;limit&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//离线计算&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;constexpr&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sz&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// size()必须是constexpr函数&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;2类型别名&quot;&gt;2.类型别名&lt;/h2&gt;

&lt;p&gt;类型别名是某种类型的同义词。&lt;/p&gt;

&lt;p&gt;使用类型别名是为了让复杂的类型名字变得简单明了、易于理解和使用。&lt;/p&gt;

&lt;p&gt;C++11新标准中，使用using来定义类型的别名&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c--&quot; data-lang=&quot;c++&quot;&gt;&lt;span class=&quot;k&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;double&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wages&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//wages是double的同义词&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;wages&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//base是double的同义词，并且p是double*的同义词&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pstring&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//pstring 是char*的同义词&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;It&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Item_test&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// It 是Item_test的同义词&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;//定义&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;wages&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//定义double 变量&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;It&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//定义Item_test 变量&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;pstring&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;cstr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//定义一个char *&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;3其他&quot;&gt;3.其他&lt;/h2&gt;

&lt;p&gt;auto和decltype都是用来让编译器在离线下自动识别类型的关键字。
其中decltype着重识别结果类型与表达式的关系。&lt;/p&gt;

&lt;p&gt;预处理器能确保头文件多次包含仍能安全工作，它在编译之前被执行。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c--&quot; data-lang=&quot;c++&quot;&gt; &lt;span class=&quot;cp&quot;&gt;#include、#define、#ifdef、#ifndef都是预处理关键字。&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;第3章&quot;&gt;第3章&lt;/h2&gt;

&lt;h2 id=&quot;1string字符串类&quot;&gt;1.string字符串类&lt;/h2&gt;

&lt;p&gt;前面说using可以用于类型别名，using也可以用于声明命名空间。
using namespace::name
不过头文件中不应包含using声明，因为这会导致头文件在被拷贝时多次重复声明using。&lt;/p&gt;

&lt;p&gt;string是标准库中的类对象。
string在使用中最容易发生的问题就是拷贝。
特别是等号(=、+)引起的合并和拷贝需要注意。
string在比较时(==、!=、&amp;lt;、&amp;lt;=、&amp;gt;、&amp;gt;=)有比较算法但每个字符都会比较。&lt;/p&gt;

&lt;p&gt;严格来说string对象不属于容器类型，但string支持很多与容器类似的操作，比如下标、迭代器等。&lt;/p&gt;

&lt;h2 id=&quot;2vector动态数组&quot;&gt;2.vector动态数组&lt;/h2&gt;

&lt;p&gt;vector是标准中的动态数组模版库。
模版本身不是类或函数，可以把模版看成编译器生成的类或函数。编译器根据模版创建类或函数的过程称为实例化。因此使用模版时，我们要指出编译器应把类或函数实例化成何种类型。
因此vector是模版而非类型，由vector生成的类才叫类型。&lt;/p&gt;

&lt;p&gt;vector容器本身就是对象，因此也能通过拷贝初始化。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c--&quot; data-lang=&quot;c++&quot;&gt;&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 初始化3个元素，1、2、3&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 初始化10个元素，都是-1&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 用拷贝来初始化，将v1中的数据拷贝到v3，完成后v3拥有独立的数据&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 提前预备20个值，每个值都是0&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;由于vector是动态数组，因此如果能在初始化时提前告知vector的话，vector就不用扩容了，运行时性能会更好。&lt;/p&gt;

&lt;h2 id=&quot;3迭代器&quot;&gt;3.迭代器&lt;/h2&gt;

&lt;p&gt;迭代器有三种不同含意，一是迭代器概念本身，二是容器定义的迭代器类型，三是指某个迭代器对象。&lt;/p&gt;

&lt;p&gt;有时她跟指针很像，但又完全不一样。&lt;/p&gt;

&lt;p&gt;其实迭代器是个由模版封装过的类，根据不同的类型的容器，编译器生成功能相似但命名不同的类。&lt;/p&gt;

&lt;p&gt;迭代器又重写了==、&amp;gt;、&amp;lt;、!=、&amp;lt;=、&amp;gt;=、+=、-=运算符，使得我们在编写代码时更加方便。&lt;/p&gt;

&lt;p&gt;共识：当使用迭代器时如果对容器做了增删操作，则会使得迭代器失效，甚至报错和崩溃。&lt;/p&gt;

&lt;p&gt;迭代器对象是实时生成的，当我们获取迭代器对象时，容器会实时生成一个迭代器对象，我们再通过操作这个迭代器对象达成我们的目的。&lt;/p&gt;

&lt;h2 id=&quot;4数组&quot;&gt;4.数组&lt;/h2&gt;

&lt;p&gt;一些复杂的数组声明难以理解，因此如果能从数组的名字开始按照由内向外的顺序阅读会更加容易些。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c--&quot; data-lang=&quot;c++&quot;&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 普通数组含有10个整数&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ptrs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// ptrs是含有10个整数指针的数组&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Parray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Parray指向一个含有10个整数的数组&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arrRef&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// arrRef引用一个含有10个整数的数组&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arry&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ptrs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//arry是数组的引用，该数组含有10个整数指针&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;数组真正使用的时候编译器会把它转成指针。
因此指向数组的指针可以使用+、-、==、!=运算符号来操作数组。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c--&quot; data-lang=&quot;c++&quot;&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// e指向arr元素的下一位置&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;arr&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;cout&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;endl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;至于多维数组，严格来说C++中没有多维数组，通常所说的多维数组其实是数组的数组，也就是用数组类型组成的数组。&lt;/p&gt;

&lt;p&gt;其中要注意的是在多维数组遍历时，其遍历顺序应该按照数组的整块内存来遍历，否则命中效率比较低。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484732&amp;amp;idx=3&amp;amp;sn=13204e3fd9171849653497a8c788c649&amp;amp;chksm=fc22603bcb55e92dfe641a5066c21000b70a3a03a61d2c691324b23a20ae3fffeef40a70facd&amp;amp;token=890029854&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(二十五) 《C++ Primer》#1</title>
   <link href="http://www.luzexi.com/2021/06/07/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B025"/>
   <updated>2021-06-07T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/06/07/读书笔记25</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484732&amp;amp;idx=2&amp;amp;sn=cbf9e354ddf1157c9913d7e4fc3ad41f&amp;amp;chksm=fc22603bcb55e92dd8e5fdd6210f007c072f1208ecf85a081f0273f26e54ff95ad9d219d2094&amp;amp;token=890029854&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;背景&quot;&gt;背景：&lt;/h2&gt;

&lt;p&gt;我为什么要重学C++？第一是巩固核心，软件编程有三大核心，语言、操作系统、框架设计，语言首当其冲，核心能力大都看不见摸不着只有你自己知道的东西。第二是将分散的知识串联起来，这样就能产生新的知识和新的创意，将这些知识变成智慧融合到身体中。&lt;/p&gt;

&lt;p&gt;本系列基于《C++ Primer》学习，重学的时候，我略过了滚瓜烂熟的部分，挑出以前忽略的，以及记忆没有那么深刻的，特别是那些重要的但以前没有上心的部分。&lt;/p&gt;

&lt;h2 id=&quot;开始&quot;&gt;开始&lt;/h2&gt;

&lt;p&gt;使用命令行界面来学习c++会更好一些，这种学习方式可以让你将精力集中在c++语言本身上，一旦掌握了语言，使用和学习IDE编辑器通常也更容易些。&lt;/p&gt;

&lt;p&gt;.h文件作为头文件只是程序员的习惯，头文件后缀并没有特定的规范，通常为了方便查看都以.h为后缀。&lt;/p&gt;

&lt;p&gt;编译器一般不关心文件名的形式，但有的IDE对此有特定要求。&lt;/p&gt;

&lt;h2 id=&quot;c基础&quot;&gt;c++基础&lt;/h2&gt;

&lt;p&gt;c++是一种静态数据类型语言，它的类型检查发生在编译时。因此，编译器必须知道程序中每一个变量对应的数据类型。&lt;/p&gt;

&lt;p&gt;c++提供了一组内置数据类型、相应的运算符、以及为数不多的程序流控制语句，这些元素共同构成了c++语言的基本形态。&lt;/p&gt;

&lt;p&gt;仅就c++的基本形态来说，它是一种简单的编程语言。&lt;/p&gt;

&lt;p&gt;但其强大的能力显示于它对程序员自定义数据结构的支持，程序员可以通过自主定义新的数据结构来使语言满足他们各自的需求。&lt;/p&gt;

&lt;p&gt;省略掉一些简单变量类型的内容，将一些特别需要注意，以及大家常忽略又比较重要的内容提取出来。&lt;/p&gt;

&lt;p&gt;类型转换&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;char在一些机器上是有符号的，另一些机器上则是无符号的。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;当我们赋值给一个无符号类型的变量一个超出它范围的值时，结果是总数取模后的余数。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;当我们赋值给带符号类型一个超出他范围的值时，结果是未定义（不可预测）。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;切勿混用有符号和无符号的类型，如果表达式里既有带符号又有无符号类型，带符号的数会自动转换成无符号，如果是个负数则会出现异常结果。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;无法预知的行为源于编译器无须检查的错误。在某些情况或某些编译器下，含有无法预知行为的程序也能正确执行。&lt;/p&gt;

&lt;p&gt;不可移植通常都是程序依赖了环境，或操作系统环境或编译器环境，程序应该尽量避免依赖于实现环境的行为。例如我们把 int 的尺寸看成一个固定不变的值，这样的程序就称为不可移植。&lt;/p&gt;

&lt;h2 id=&quot;变量&quot;&gt;变量&lt;/h2&gt;

&lt;p&gt;何为对象？对象就是指一块能存储数据并具有某种类型的内存空间。&lt;/p&gt;

&lt;p&gt;初始化是一个复杂的过程，很多程序员对于初始化的认识，停留在=等号赋值的认知上。事实上它完全不是这样的。&lt;/p&gt;

&lt;p&gt;它有构造、拷贝、赋值三种途径，且有默认构造、未初始化、定义与声明三个容易出错的地方。&lt;/p&gt;

&lt;p&gt;初始化并不一定被默认执行，这是因为并对象不一定有默认构造函数，且有些编译器构造的默认构造函数并不一定会去初始化变量，这导致一个未被初始化的变量在拷贝和访问时就会引发错误，因此我们最好自己主动的去初始化每个变量。&lt;/p&gt;

&lt;p&gt;c++支持分离式编译，允许将程序分为若干个文件，每个文件可被独立编译。&lt;/p&gt;

&lt;p&gt;因此我们可以把变量的定义和声明区分开来，即在某个文件中定义，在另一些文件中声明后再使用。&lt;/p&gt;

&lt;p&gt;变量能且只能定义一次，但可以被多次声明，在声明时不能重复初始化：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c--&quot; data-lang=&quot;c++&quot;&gt;&lt;span class=&quot;c1&quot;&gt;// a.cpp&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;global_index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//定义&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// b.cpp&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;global_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//声明&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;global_index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// c.cpp&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;global_index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;//声明&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;global_index&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;err&quot;&gt;如上代码中，&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cpp&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;中定义了&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;global_index&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;并初始化为&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;，在&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cpp&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;和&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;c&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cpp&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;中使用。&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;复合类型&quot;&gt;复合类型&lt;/h2&gt;

&lt;p&gt;复合类型有两种，即引用和指针。&lt;/p&gt;

&lt;p&gt;引用特点：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	引用本身不是对象，而它必须绑定一个对象。

	引用在定义时必须被初始化，它不能为空。

	引用在初始化后，与它的值绑定在一起，并且无法再重新绑定到另外的对象上。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;指针特点：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	指针本身就有自己的对象概念，存放的是对象的地址，可为null也可以为具体的指针值。

	指针无须在定义时初始化（未初始化有风险），可以为空，也可以指向某个对象，也可能是无效的野指针。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;引用和指针通过，取地址符（&amp;amp;），解引用（*）来操作，我们来举个例子：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c--&quot; data-lang=&quot;c++&quot;&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dval&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 初始化&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tval&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;dval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 绑定dval&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dval&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 取地址，赋值给指针&lt;/span&gt;

&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// 此时dval 为3，tval也为3，*pd也为3&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;特殊指针：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	1.c++11中引入了nullptr的空指针类型，以前用的NULL空指针，其实质是一个宏预处理，NULL定义在cstdlib中值为0。
	
	2.void*也是特殊指针，可用于存放任意对象地址。但不能操作void*指针所指的对象，因为我们并不知道这个对象到底是什么类型，它仅仅是内存空间。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484732&amp;amp;idx=2&amp;amp;sn=cbf9e354ddf1157c9913d7e4fc3ad41f&amp;amp;chksm=fc22603bcb55e92dd8e5fdd6210f007c072f1208ecf85a081f0273f26e54ff95ad9d219d2094&amp;amp;token=890029854&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(二十四) 《如何高效阅读 - 中》</title>
   <link href="http://www.luzexi.com/2021/05/29/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B024"/>
   <updated>2021-05-29T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/05/29/读书笔记24</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484709&amp;amp;idx=1&amp;amp;sn=4c609d7afb6e41db78948f7797e31de6&amp;amp;chksm=fc226022cb55e934935d6df6b188978331f28a6854abd70eec313fc71d225a709bdcbd291af0&amp;amp;token=679775473&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;背景&quot;&gt;背景：&lt;/h2&gt;

&lt;p&gt;看了很多书，也总结过很多书，对如何看书以及如何总结书本仍然迷茫。
所以自己找了3本书，《如何阅读》、《如何阅读一本书》、《如何高效阅读一本书》，专门针对这三本书做了仔细的总结和分析。
我把文章分为上、中、下三篇，本篇为中篇，讲的是《如何阅读一本书》这本书。&lt;/p&gt;

&lt;p&gt;书中有几个方法和框架，无论你是完全照做，或只是掌握个形式，你都得了解要如何跟着规则走，这是第一步。&lt;/p&gt;

&lt;p&gt;只有了解规则怎么走，熟悉了规则，才会能逐渐掌握。就像我们在学习新知识的时候那样，可能一开始只是模仿，只是形式上的掌握，没有触及技巧的灵魂。随着我们熟练度的深入，规则越来越熟悉，技巧越来越熟练，也就慢慢地将技巧的灵魂融入到我们的身体中甚至生活习惯中。&lt;/p&gt;

&lt;p&gt;下面我们就来看看书中是怎么论述阅读规则和技巧的。&lt;/p&gt;

&lt;h2 id=&quot;简要总结&quot;&gt;简要总结：&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;阅读有四个层次，基础阅读、检视阅读、分析阅读、主题阅读。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;主动阅读就是带着目的阅读，在阅读前提出问题，带着问题阅读，并且在多次重复阅读中回答问题。最后阅读完成用自己的语言写大纲和评价。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;主题阅读需要阅读很多同一主题的书，然后再做分析和讨论。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;针对不同类型的读物，我们需要用不同类型的方法。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;珍惜一本好书的方法，就是读很多遍，甚至是一辈子都在读。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;主动阅读对我们各方面的帮助都会很大。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;正文&quot;&gt;正文：&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484709&amp;amp;idx=1&amp;amp;sn=4c609d7afb6e41db78948f7797e31de6&amp;amp;chksm=fc226022cb55e934935d6df6b188978331f28a6854abd70eec313fc71d225a709bdcbd291af0&amp;amp;token=679775473&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(二十三) 《如何高效阅读 - 上》</title>
   <link href="http://www.luzexi.com/2021/05/23/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B023"/>
   <updated>2021-05-23T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/05/23/读书笔记23</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484653&amp;amp;idx=1&amp;amp;sn=f16e52ea17560947ace74b8a7ecfaefb&amp;amp;chksm=fc2261eacb55e8fc2d96d43bcf8dc1978cfa67e678d5003b793d33a7adb7bdf7b4859e28c740&amp;amp;token=2074622695&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;背景&quot;&gt;背景：&lt;/h2&gt;

&lt;p&gt;看了很多书，也总结了很多书，但对读书这个词语越来越迷茫。&lt;/p&gt;

&lt;p&gt;迷茫的是，不知道自己的阅读习惯是否真的好，自己的阅读效率是否真的高，我对自己的阅读能力不自信。&lt;/p&gt;

&lt;p&gt;因此买了三本经典阅读技巧书，一本是《如何阅读一本书》、另一个本是《如何阅读》，再加上去年读过的《如何有效阅读一本书》，&lt;/p&gt;

&lt;p&gt;三本书加起来，对阅读的技巧和效率做一个巩固和总结。因此我把此系列总结文章分成三部分，上、中、下，本文是上篇。&lt;/p&gt;

&lt;p&gt;本文总结的是《如何阅读》这本书，它主要讲的主题是，如何提高我们的阅读速度。&lt;/p&gt;

&lt;p&gt;阅读速度是否是阅读的关键呢，我认为是的，但也认为不是。关键在于你怎么理解和运用。我们来看看作者是怎么认为的。&lt;/p&gt;

&lt;p&gt;文中并没有详细的提到带着目的去阅读的具体方法，但全文都在提倡带着目的去阅读，我认为这是个重要方法，这个重要的方法我打算在另一本书《如何阅读一本书》的总结里做分析。&lt;/p&gt;

&lt;h2 id=&quot;简要总结&quot;&gt;简要总结：&lt;/h2&gt;

&lt;pre&gt;&lt;code&gt;不一定什么书都要细读，针对不同的书要使用不同的阅读方式。

提高阅读速度的技巧有很多种，针对不同的内容使用不同阅读速度。

阅读的环境和习惯是提高效率的一个重要关键点。

理解力是阅读效率的一个重要指标，它要求我们和作者不断对话，彼此交流达到沟通理解的地步。

阅读笔记是增加理解力，加强注意力，提高回顾效率的一个有效方式。
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;正文&quot;&gt;正文：&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484653&amp;amp;idx=1&amp;amp;sn=f16e52ea17560947ace74b8a7ecfaefb&amp;amp;chksm=fc2261eacb55e8fc2d96d43bcf8dc1978cfa67e678d5003b793d33a7adb7bdf7b4859e28c740&amp;amp;token=2074622695&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(六十五) 人生的框架</title>
   <link href="http://www.luzexi.com/2021/05/14/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A865"/>
   <updated>2021-05-14T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/05/14/思路探讨65</id>
   <content type="html">&lt;p&gt;目标管理从2月份到现在已经实践了有一段时间了，它给我带来了很多震撼的效果，也开阔了我不少视野。&lt;/p&gt;

&lt;p&gt;我慢慢发现行为习惯和思维习惯的改变就像脱胎换骨一样，是极难的，也是极其痛苦的过程。&lt;/p&gt;

&lt;p&gt;当然这也意味着，目标管理的这3个月折磨的我很痛苦，不过这些都是值得的，都是改变行为习惯和思维习惯所必须经历。人就是这样的，改变的太多太快，身体就会承受很大的痛苦，心思上和肉体上都会有。&lt;/p&gt;

&lt;h3 id=&quot;最大的体会是目标给我提供了动力和方向&quot;&gt;最大的体会是，目标给我提供了动力和方向。&lt;/h3&gt;

&lt;p&gt;这给了我很多提示，很多旧思想和旧习惯在我脑海中浮现。&lt;/p&gt;

&lt;p&gt;以前经常会去寻找安全感，去为获得安全感而努力，比如想着有一天能安心躺着赚钱，或者存够钱去农村养老，或者找到一份安逸稳定的饭碗，还有我们总是想早点完成手头上的工作来获得安全感。&lt;/p&gt;

&lt;p&gt;这其实就是人性的一面，为获得安全感而活，所以很多新闻标题都以恐吓人的方式来命名，以吸引人来关注。&lt;/p&gt;

&lt;h3 id=&quot;这些年的经历告诉我不要试图去追求安全感周遭的环境从来都不会有绝对的安全如果有那肯定是陷进你安全的越久就会陷的越深越无法自拔&quot;&gt;这些年的经历告诉我，不要试图去追求安全感，周遭的环境从来都不会有绝对的安全，如果有，那肯定是陷进，你安全的越久，就会陷的越深越无法自拔。&lt;/h3&gt;

&lt;h3 id=&quot;只有不断的挑战不断努力不断拼搏才能让自己有永恒的动力才会有不灭的优势&quot;&gt;只有不断的挑战，不断努力，不断拼搏，才能让自己有永恒的动力，才会有不灭的优势。&lt;/h3&gt;

&lt;p&gt;也体会到，人越老，年龄越大就越对周围的事物失去兴趣，保持热情变得越来越困难。&lt;/p&gt;

&lt;p&gt;年轻时对周围的事物都充满了新鲜感，社会生活了10几年后，这层新鲜感就不在了，留下的都是枯燥和乏味。有些人沉沦，有些人走偏，这也都是在预料之中的事。&lt;/p&gt;

&lt;p&gt;保持热情，就要保持好奇。而保持好奇心最好的方式，就要有目标，这些都是一环扣一环的，有目标，可量化，就能得到反馈，有反馈就有令人兴奋的动力。&lt;/p&gt;

&lt;p&gt;这些我都写在《目标管理》的一篇文章中，这些年我建立了自己的框架，框架中包含了做人做事的心态，家庭，价值观，目标，行为习惯和思维习惯等方方面面，我希望我的人生是平衡的，这样走下去才会更加的稳。&lt;/p&gt;

&lt;p&gt;什么是更加稳的人生呢？&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;1.更少的情绪波动（外部波动可能很大，内部波动则更小）

2.要事优先，做更重要的事情

3.朝着正确的人生方向不偏离

4.帮助周围的人一起往更好的方向奔去
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;同时这个框架让我所做的事情变得可积累，甚至是把原本不可积累事情的变成了可积累。这样，在这个框架中，我积累的越多，框架就越强大，我也会活的更好些。&lt;/p&gt;

&lt;h3 id=&quot;当然框架不是一蹴而就的可能需要建设10年20年甚至一辈子所以我一直在完善这个框架不断去调整它加入更好的元素去掉错误的理解调整偏离的方向&quot;&gt;当然，框架不是一蹴而就的，可能需要建设10年20年甚至一辈子，所以我一直在完善这个框架，不断去调整它，加入更好的元素，去掉错误的理解，调整偏离的方向。&lt;/h3&gt;

&lt;p&gt;框架也同时成为了我人生中的可以一直感兴趣的事情，因为我在不断研究它完善它，我相信这个框架会给我带来越来越好的人生。也希望各位能共同努力，往更好的人生方向奔去。&lt;/p&gt;

&lt;p&gt;一点点小总结，希望能给大家带去一些参考价值。&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>给女儿写信(十二) 关于积分制</title>
   <link href="http://www.luzexi.com/2021/05/11/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A112"/>
   <updated>2021-05-11T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/05/11/给女儿的信12</id>
   <content type="html">
&lt;hr /&gt;

&lt;p&gt;导读：喜欢跟孩子们一起，可惜一直在外地工作，因此我给孩子写点信并录成视频，跟孩子聊聊自己的故事以及最近的感受&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;hi Sharon Anne，爸爸几天不见好想你们。爸爸又回到深圳工作了，爸爸跟你们说过，爸爸不只是来工作的，更是来学习的。&lt;/p&gt;

&lt;p&gt;爸爸每天都很努力的去学习，去思考，爸爸想多学一点。&lt;/p&gt;

&lt;p&gt;但是，你知道，每个人都会有累的时候，都会有这么段时间想偷懒的时候。&lt;/p&gt;

&lt;p&gt;爸爸不想偷懒，但人就是会有是有懒的时候，懒的早起，懒的看书，懒的运动，懒的写文章。&lt;/p&gt;

&lt;p&gt;这不是爸爸想要的，爸爸想克服它，为了能抵抗人的懒惰，爸爸想了很多办法，也尝试了很多办法。&lt;/p&gt;

&lt;p&gt;幸运的是，爸爸终于找到一个很好的办法来抵抗懒惰，那就是给自己设定目标，并且奖励小星星。&lt;/p&gt;

&lt;p&gt;爸爸给自己设置了很多奖励小星星的任务，&lt;/p&gt;

&lt;p&gt;比如看完一本书，就给自己10颗小星星，写一篇文章就给自己8颗小星星，每天早起锻炼就给自己1颗小星星。&lt;/p&gt;

&lt;p&gt;爸爸又给自己设定了奖励，比如用30颗小星星换一次回家休息的机会，用50颗小星星换一次去外面野餐的机会，80颗小星星换一个游戏。&lt;/p&gt;

&lt;p&gt;爸爸把这些小星星积累起来，到了足够多的时候，就把它们用在自己喜欢的事情上。&lt;/p&gt;

&lt;p&gt;于是为了能够凑足小星星，获得自己想要的奖励，爸爸又充满的动力了，每天都努力读书，写文章，锻炼，攒小星星。&lt;/p&gt;

&lt;p&gt;哈哈，是不是很好玩，爸爸也跟你们一起攒小星星呢，攒满了小星星，一起去玩哦。&lt;/p&gt;

&lt;p&gt;加油Sharon，加油Anne，爸爸喜欢你们哦，要照顾好妈妈呦，笔芯。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(二十二) 《金字塔原理》下</title>
   <link href="http://www.luzexi.com/2021/05/09/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B022"/>
   <updated>2021-05-09T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/05/09/读书笔记22</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484626&amp;amp;idx=1&amp;amp;sn=cdbe54838b9b0207441663acb49bec6f&amp;amp;chksm=fc2261d5cb55e8c3185818b45f69941cffe1cfe7466b901b907c08d16576a04a3eb11890b6e0&amp;amp;token=1892444225&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;背景&quot;&gt;背景：&lt;/h2&gt;

&lt;p&gt;上一篇，我们总结了《金字塔原理》上半部分，这一篇继续了解思考、表达的逻辑。&lt;/p&gt;

&lt;p&gt;本书我们主要学习的是框架，以前都是没有章法的去思考、去表达、去分析，看了这本书后就知道思考、表达、分析、和解决问题的框架。
当然理论和实践差距比较大，我们应该把这些理论落地到实践中去。&lt;/p&gt;

&lt;p&gt;建立起自己在现实中的框架，这样我们在学习和工作中会更稳一些，倘若我们发现自己的框架有问题，
我们则需要停下来先修理自己的框架，这样就能逐渐形成自己的体系，从而让你更有章法、更有节奏、更有效率的去解决问题。&lt;/p&gt;

&lt;h2 id=&quot;概述&quot;&gt;概述：&lt;/h2&gt;

&lt;p&gt;回顾一下前面的知识，前面我们说了，我们在表达时的逻辑，表达时要以金字塔的形式叙述，这样能让听者和读者更容易接受。&lt;/p&gt;

&lt;p&gt;序言的故事结构，就很好的契合了这种表达目的，由于序言常以故事结构作为表达形式，而人更容易接受以故事形式叙述的事情，所以它更能让人接受和吸收。&lt;/p&gt;

&lt;p&gt;序言有4个要素组成，即背景、冲突、疑问、答案，遵照这4个要素，我们在表达时就会更容易些。&lt;/p&gt;

&lt;p&gt;表达逻辑之后，也讲到思考的逻辑。我们在应用逻辑顺序时，会用到逻辑的时间顺序、结构顺序、程度顺序去分析和归纳问题。&lt;/p&gt;

&lt;p&gt;当然，采用各种逻辑思考最终目的是要产生新的思想，那么如何提高产生新的思想的概率呢，我们可以通过分析和归纳的方法。&lt;/p&gt;

&lt;p&gt;即，列出值得思考的思想、找出思想之间的共同点、提出新的思想。&lt;/p&gt;

&lt;p&gt;简单回顾到这里，现在继续下篇的总结，内容包括：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;解决问题的逻辑
    &lt;ul&gt;
      &lt;li&gt;界定问题的框架&lt;/li&gt;
      &lt;li&gt;结构化分析问题&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;演示的逻辑
    &lt;ul&gt;
      &lt;li&gt;书面上的金字塔&lt;/li&gt;
      &lt;li&gt;PPT上的金字塔&lt;/li&gt;
      &lt;li&gt;字里行间的金字塔&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;正文&quot;&gt;正文：&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484626&amp;amp;idx=1&amp;amp;sn=cdbe54838b9b0207441663acb49bec6f&amp;amp;chksm=fc2261d5cb55e8c3185818b45f69941cffe1cfe7466b901b907c08d16576a04a3eb11890b6e0&amp;amp;token=1892444225&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(六十四) 习惯养成-阅读技巧-要事优先</title>
   <link href="http://www.luzexi.com/2021/05/03/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A864"/>
   <updated>2021-05-03T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/05/03/思路探讨64</id>
   <content type="html">&lt;p&gt;看到了越来越多人的努力和奋进，因此不要以为自己是最努力的那个。&lt;/p&gt;

&lt;p&gt;当然努力只是开始，为了能有一个好的结果，我们不只是努力就够了，还要有目标和策略。&lt;/p&gt;

&lt;p&gt;人总是对自己当前的能力高估，对自己的未来低估。这是自然反应，反人性的。&lt;/p&gt;

&lt;p&gt;为了能做反人性的事做的更好，我们需要形成好习惯，这样才能在反人性的事情上，做起来更容易些。&lt;/p&gt;

&lt;p&gt;最近开始整理读书技巧，以前看过《如何有效阅读一本书》改进了下自己在阅读时的习惯和技巧，现在正在看《如何阅读一本书》，&lt;/p&gt;

&lt;p&gt;前一本是日本人写的，后一本是美国人写的，两本书的风格不同，但都挺有用的，&lt;/p&gt;

&lt;p&gt;我把两本书结合起来，巩固自己的阅读技巧，这对自己巩固学习基础建设很重要，磨刀不误砍柴工。&lt;/p&gt;

&lt;p&gt;最后聊下事情的重要性，我发现生活中90%的事情都是紧急但不重要的，而10%是重要的事情却常常被我们以各种理由搁置。&lt;/p&gt;

&lt;p&gt;因此我们常常做的都是无用功，做这些无用功的理由通常是比较荒唐的，但当事人以及周围的人听起来却是理所当然。&lt;/p&gt;

&lt;p&gt;这些，养成好习惯、做重要的事情、巩固基础技巧、历史和人性，我将继续总结，继续说出我的理解，继续分享。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(六十三) 投资-工作-生活</title>
   <link href="http://www.luzexi.com/2021/04/27/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A863"/>
   <updated>2021-04-27T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/04/27/思路探讨63</id>
   <content type="html">&lt;p&gt;假如你的股票投资，已经有100万，并且已经盈利了50%，你在这场投资游戏中还会慌吗？&lt;/p&gt;

&lt;p&gt;如果投资注定是一场漫长的游戏，那么你该如何正确的面对一时的亏损，面对投资标的的选择？&lt;/p&gt;

&lt;p&gt;投资本身就反应了我们的价值观，这种价值观最后都会反应到，投资回报中，从长期来看就是这样的。&lt;/p&gt;

&lt;p&gt;当然这中间，波动会很大，你会意外的发现买了后就涨或跌，如果你需要安全感，就不要做投资，投资本身就是打破安全感的事情。&lt;/p&gt;

&lt;p&gt;投资也好，工作也好，生活也好，其实都是一个道理。&lt;/p&gt;

&lt;p&gt;最终你会发现，其实事情可以很简单，做好人做好事，多帮助他人，这样我们保持下去，生活就会对我们好。&lt;/p&gt;

&lt;p&gt;但因为时间过于漫长了，效果也很难在短时间见到，所以很多人都想走捷径，最终走了偏门。&lt;/p&gt;

&lt;p&gt;其实最艰难的道路就是捷径，那就是做好自己，帮助他人，一步一个脚印脚踏实地向前走。&lt;/p&gt;

&lt;p&gt;再看看自己的平衡轮，将自己的轮子平衡一下，这样就可以走的更稳一些，同时稳的同时也更快了。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>给女儿写信(十一) 时间很重要</title>
   <link href="http://www.luzexi.com/2021/04/18/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A111"/>
   <updated>2021-04-18T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/04/18/给女儿的信11</id>
   <content type="html">
&lt;hr /&gt;

&lt;p&gt;导读：喜欢跟孩子们一起，可惜一直在外地工作，因此我给孩子写点信并录成视频，跟孩子聊聊自己的故事以及最近的感受&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Hi Sharon，Anne，爸爸好想你们。没过几天没看到你们就很想你们。&lt;/p&gt;

&lt;p&gt;Sharon生病了还吐了，爸爸心疼你，要保护好自己的身体哦。Anne也是，在姐姐生病时要照顾好姐姐和妈妈，帮助妈妈一起照顾好姐姐，大家一起对抗病魔哈。&lt;/p&gt;

&lt;p&gt;最近爸爸在抓紧时间学习，爸爸发现时间对人很重要。&lt;/p&gt;

&lt;p&gt;假如我们的时间用不完的话，我们就能做很多我们想做但又没时间做的事情，比如锻炼健身、看书、写文章、去旅行看看世界等。&lt;/p&gt;

&lt;p&gt;如果爸爸有用不完的时间的话，爸爸就能一边跟你们在一起玩，又一边能学习和工作了。&lt;/p&gt;

&lt;p&gt;可是时间是有限的，爸爸1天就24个小时，除去睡觉吃饭休息，满打满算也就只有12个小时的时间。&lt;/p&gt;

&lt;p&gt;为了赚钱爸爸又要把12个小时里的8个小时分给公司，帮公司干活，剩下的4个小时，爸爸要学习新的知识，还要和妈妈聊天，要去奶奶家看奶奶，还要和你们玩，时间实在不够用。&lt;/p&gt;

&lt;p&gt;所以爸爸很珍惜时间，每天精打细算的花时间，好想让每一分钟的时间都用在刀口上。&lt;/p&gt;

&lt;p&gt;为了能把所有花时间做过的事情都保存下来，爸爸把每件事都记录了下来，这样就可以保存爸爸做过的每一件事。&lt;/p&gt;

&lt;p&gt;就好像时间停留在了那个点上一样，所有做过的事情都停留在了每个时间点上。以后再翻开这些记录的文章去回忆时，一下子就能记起来当时发生的环境和心情了，是不是很好：）&lt;/p&gt;

&lt;p&gt;为了精打细算的花时间，爸爸给自己定了计划，爸爸每年都给自己定一个大的计划，然后把大的计划拆分成很多很多份，直到爸爸知道每天应该干什么，当爸爸知道每天都该做什么的时候，做每件事的时候都会觉得非常有意义。&lt;/p&gt;

&lt;p&gt;爸爸相信只要每天不断学习，不断进步，这些计划和目标都会慢慢变成现实的。给爸爸加油喔~&lt;/p&gt;

&lt;p&gt;喜欢你们Sharon和Anne，我们一起加油，我们一家人都很优秀，继续加油。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(六十二) 知识的深度与广度</title>
   <link href="http://www.luzexi.com/2021/04/18/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A862"/>
   <updated>2021-04-18T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/04/18/思路探讨62</id>
   <content type="html">&lt;p&gt;知识的深度和广度都是非常重要的，没有一定说，只做某个方向一定能成，或者说只要涉猎足够广一定能成事。&lt;/p&gt;

&lt;p&gt;很多有知识深度的人，特别是某个专业领域特别强的人，会去鄙视那些专业程度不深但知道很多个领域知识的人。
例如学者就常看不上商人，学者有足够深的专业，而商人则有足够广的领域涉猎。
其实这种思维是非常错误的，也是狭隘的，其本身的做法也是不够明智的。&lt;/p&gt;

&lt;p&gt;实际上深度和广度是兼并的，知识的深度支撑了广度，广度又回馈了深度，这样深度和广度相辅相成的才是比较好的模式，这也是人们口中常说的“一专多精”的意义所在。&lt;/p&gt;

&lt;p&gt;就像你懂的事情越多，越能对事物提出不同的观点那样。不同领域间的知识常以惊人的方式互相取长补短。毕竟，创造的核心是将从未有过交集的东西融合在一起。&lt;/p&gt;

&lt;p&gt;如果你接受了人需要博识的观点后，学会了微精通，那么你就能从多种微精通实践中获得了思维流畅性，自然而然就从封闭转换到了开放。&lt;/p&gt;

&lt;p&gt;这是种普遍的生活观，开放地拥抱生活中的各种奇迹机遇，你就会变得乐于学习了，因为你知道了获取专业知识的微精通的奥秘，不再有被其他领域拒之门外的挫败感。那时你就会变得信心十足、无所畏惧，这是任何境遇下都需要的优秀品质。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(二十一) 《金字塔原理》上</title>
   <link href="http://www.luzexi.com/2021/04/11/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B021"/>
   <updated>2021-04-11T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/04/11/读书笔记21</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484575&amp;amp;idx=1&amp;amp;sn=2d34a8dbcfb9c1210fa3e0ae5b0c13fb&amp;amp;chksm=fc226198cb55e88e892b31fa217da8e11e8eb6bfbcbd74805e610b706f69c4e776cb7da9b40f&amp;amp;token=673219380&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;背景&quot;&gt;背景：&lt;/h3&gt;

&lt;p&gt;很多同学在大学里就已经学习过《金字塔原理》，这本书主要讲了如何借鉴几个经典的框架来解决和表达问题。&lt;/p&gt;

&lt;p&gt;看了这本书后，发现书中说的东西非常好，于是我在现实生活和工作中也落地实践了许多，基于这些理论和实践经验，我想对这本书中所描述的知识做一次全面的回顾和总结。&lt;/p&gt;

&lt;h3 id=&quot;疑问&quot;&gt;疑问：&lt;/h3&gt;

&lt;p&gt;为了能够更好的理解这本书，我同时给自己提了很多疑问，这让我在看书的同时有了目标性。
我也想通过疑问-解答的方式来总结这本书的内容，因为这种方式更能让人接受。&lt;/p&gt;

&lt;p&gt;为什么说是金字塔？（让人感觉很势利）
为什么要用框架去解决问题？会不会让做事变的很死板？
有哪些比较经典的框架？
它们具体是怎么去做表达和解决问题的？&lt;/p&gt;

&lt;h3 id=&quot;概述&quot;&gt;概述：&lt;/h3&gt;

&lt;p&gt;金字塔原理是一种突出重点、逻辑清晰、层次分明、简单易懂的思考方式和沟通方式。&lt;/p&gt;

&lt;p&gt;它的基本结构是：结论先行，以上统下，归类分组，逻辑递进。先重要后次要，先总结后具体，先框架后细节，先结论后原因，先结果后过程，先论点后论据。&lt;/p&gt;

&lt;p&gt;它能够帮助我们挖掘受众的意图、需求点、利益点、关注点、兴趣点。
也能让我们在沟通时达到，观点鲜明、重点突出、思路清晰、层次分明、简单易懂，让对方有兴趣、能理解、记得住。
其具体做法是，自上而下的表达，自上而下的思考，纵向总结概括，横向归类分组，序言讲故事，标题提炼思想精华。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s?__biz=MzU1ODY1ODY2NA==&amp;amp;mid=2247484575&amp;amp;idx=1&amp;amp;sn=2d34a8dbcfb9c1210fa3e0ae5b0c13fb&amp;amp;chksm=fc226198cb55e88e892b31fa217da8e11e8eb6bfbcbd74805e610b706f69c4e776cb7da9b40f&amp;amp;token=673219380&amp;amp;lang=zh_CN#rd&quot;&gt;已发布在微信公众号上，点击跳转&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(六十一) 《原则》摘录</title>
   <link href="http://www.luzexi.com/2021/04/03/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A861"/>
   <updated>2021-04-03T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/04/03/思路探讨61</id>
   <content type="html">&lt;p&gt;最近看了《原则》一书的前半部分，讲述的是桥水创始人瑞.达利欧的个人经历。&lt;/p&gt;

&lt;p&gt;这本书2019年6月份就买了，一直没空看，但我知道它是一本好书，直到2021年过年时才从箱底里翻出来看。（我时常翻箱底，因为我知道很多东西被我遗忘在箱底了，我时常要去翻它们，这会让我有很多惊喜）&lt;/p&gt;

&lt;p&gt;本来这周想用自己的小脑袋思考弄清楚点事情，可惜在电脑前发呆了1个小时也没写出一个字，个人思考的力量毕竟有限，所以还是看书吧，站在巨人的肩膀上，让巨人带你领略风景。&lt;/p&gt;

&lt;p&gt;最近现在每次看书都会画线并记录自己的感受，就讲讲我记录的笔记吧。&lt;/p&gt;

&lt;p&gt;原文：我一直记不住没有内在逻辑的东西（如电话号码），而且我不喜欢听从别人的指示。同时，我的好奇心很强，喜欢自己把事情弄清楚，不过当时我并不像现在这么清楚地知道这一点。&lt;/p&gt;

&lt;h3 id=&quot;笔记我也一样记不住没有逻辑的东西喜欢自主的想法不喜欢被动&quot;&gt;笔记：我也一样，记不住没有逻辑的东西，喜欢自主的想法，不喜欢被动。&lt;/h3&gt;

&lt;p&gt;原文：我一直不明白，除了能得到母亲的认可之外，上学上得好还能给给我什么。&lt;/p&gt;

&lt;h3 id=&quot;笔记这让我更加慎重的考虑我的孩子的成长和教育如何才能让她们更有自主的学习动力&quot;&gt;笔记：这让我更加慎重的考虑我的孩子的成长和教育，如何才能让她们更有自主的学习动力。&lt;/h3&gt;

&lt;p&gt;原文：与失败比起来，我对乏味和平庸的恐惧要严重得多。对我而言，很好的事要比糟糕的事好，糟糕的事要比平庸的事好，因为糟糕的事至少给生活增加了滋味。&lt;/p&gt;

&lt;h3 id=&quot;笔记我不想要乏味和平庸即使失败也比平庸和乏味来的好失败还意味着拼搏过平庸和乏味基本上都是由于躺在舒适区不想动的缘故&quot;&gt;笔记：我不想要乏味和平庸，即使失败也比平庸和乏味来的好。失败还意味着拼搏过，平庸和乏味基本上都是由于躺在舒适区不想动的缘故。&lt;/h3&gt;

&lt;p&gt;原文：经历这些让我认识到，尽管几乎所有人都会估计未来和当下大同小异，但未来通常和当下大相径庭。&lt;/p&gt;

&lt;h3 id=&quot;笔记未来和当下总是出乎人们的意料相差比较大&quot;&gt;笔记：未来和当下总是出乎人们的意料，相差比较大。&lt;/h3&gt;

&lt;p&gt;原文：这个现实传递给我的信息时：你最好弄明白其他时间，其他地点，其他人身上发生的事，因为如果你不这么做，你就不知道这些事情会不会发生在你身上，而且一旦发生在你身上，你将不知道如何应对。&lt;/p&gt;

&lt;h3 id=&quot;笔记弄明白历史能更清晰的看清当下自己身上正在发生的事情&quot;&gt;笔记：弄明白历史能更清晰的看清当下自己身上正在发生的事情。&lt;/h3&gt;

&lt;p&gt;原文：哈佛教学：没有教师在黑板前告诉我们什么东西要记住，也没有考试测试我们有没有记住。相反，教师给我们的都是真实的案例研究，让我们进行阅读与分析。然后我们分成小组自由讨论，加入我们处在和案例中的人相同的情况下，我们会怎么做。这正是我喜欢的教学方式。&lt;/p&gt;

&lt;h3 id=&quot;笔记开放式教育自主阅读案例分析讨论实践&quot;&gt;笔记：开放式教育，自主阅读，案例分析，讨论，实践。&lt;/h3&gt;

&lt;p&gt;原文：你必须既有防御心又有进攻心。如果没有进攻心，你就赚不到钱，而如果没有防御心，你的钱就保不住。&lt;/p&gt;

&lt;h3 id=&quot;笔记进攻和防御最好兼备&quot;&gt;笔记：进攻和防御最好兼备&lt;/h3&gt;

&lt;p&gt;原文：把赚钱作为你的目标是没有意义的，因为金钱并没有固有的价值，金钱的价值来自它能买到的东西，但金钱并不能买到一切。更聪明的做法是，先确定你真正想要什么，你真正的目标是什么，然后想想你为了得到这些目标需要做什么。&lt;/p&gt;

&lt;h3 id=&quot;笔记真正想要什么制定目标目标不是钱而是自己的想法用自己的视野去实现并拓展自己的视野&quot;&gt;笔记：真正想要什么，制定目标，目标不是钱，而是自己的想法，用自己的视野去实现，并拓展自己的视野。&lt;/h3&gt;

&lt;p&gt;原文：在考虑有意义的人际关系与金钱相比的重要性时，很明显，人际关系更重要，因为一种有意义的人际关系时无价的，我用再多的金钱也买不到比这更有价值的东西。有意义的工作和有意义人际关系都是我的主要目标，我做的一切事情都是为了实现这两个目标。赚钱只是其附带结果。&lt;/p&gt;

&lt;h3 id=&quot;笔记有意义的工作和有意义的人际关系是核心围绕着这个两个有意义人生才真正有意义有意义的工作指自己想做的工作有意义人人际关系指与周围同事家人朋友建立良好的关系&quot;&gt;笔记：有意义的工作和有意义的人际关系，是核心。围绕着这个两个有意义，人生才真正有意义。有意义的工作指自己想做的工作，有意义人人际关系，指与周围同事、家人、朋友建立良好的关系。&lt;/h3&gt;

&lt;p&gt;…&lt;/p&gt;

&lt;h3 id=&quot;还有很多摘录的只是百分之一这本书不错摘录部分内容希望能给大家带来一点启示&quot;&gt;还有很多，摘录的只是百分之一，这本书不错，摘录部分内容希望能给大家带来一点启示。&lt;/h3&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(二十) 《目标管理》</title>
   <link href="http://www.luzexi.com/2021/03/28/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B020"/>
   <updated>2021-03-28T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/03/28/读书笔记20</id>
   <content type="html">&lt;h3 id=&quot;背景&quot;&gt;背景：&lt;/h3&gt;

&lt;p&gt;机缘巧合从印象笔记中看到目标管理这个课程，也正好契合我最近的理念。
我发现学习，工作，生活如果没有目标就很难做的好，同样的人生如果没有目标，就很难保持动力和正确的方向。&lt;/p&gt;

&lt;p&gt;虽然我一直提倡时间管理，精力管理，但这一切都是为了什么呢？
在漫无目的的行动中，常常使我迷茫，因为太枯燥太无聊了，我时常问自己，“这样坚持下去真的有效吗？”
于是目标管理映入我的眼帘，以前很排斥的东西，现在变成了我的宝贝。&lt;/p&gt;

&lt;p&gt;当然事情并没有这么顺利，我发现它需要一个很强大的习惯来支撑它的运作，不过我认定这是个好东西
我开始将目标管理付诸于实际生活中，它是一个漫长的练习过程，我希望在学习和实践中不断领悟其精髓，并分享给大家。&lt;/p&gt;

&lt;h3 id=&quot;疑问&quot;&gt;疑问：&lt;/h3&gt;

&lt;p&gt;我在这次好奇般的学习过程中到底学到了什么呢？我给自己提出了几个疑问，并做了总结和自我解答。&lt;/p&gt;

&lt;h3 id=&quot;我们为什么害怕目标&quot;&gt;我们为什么害怕目标？&lt;/h3&gt;
&lt;h3 id=&quot;为什么要目标管理&quot;&gt;为什么要目标管理？&lt;/h3&gt;
&lt;h3 id=&quot;目标管理的精髓是什么&quot;&gt;目标管理的精髓是什么？&lt;/h3&gt;
&lt;h3 id=&quot;它有哪些策略可以用分别是什么&quot;&gt;它有哪些策略可以用？分别是什么？&lt;/h3&gt;

&lt;h3 id=&quot;内容&quot;&gt;内容：&lt;/h3&gt;

&lt;p&gt;我们对目标管理其实有很深的偏见。
因为无论是工作还是学校学习，都会有上级和老师给我们的目标，每次这种目标都会给我们很大的压力。
甚至很多时候因为目标太大，压力太重，压垮了我们。
所以我们大部分人都很害怕‘目标’这两个字，很少有人能真正主动去接受它、拆解它、分析它。&lt;/p&gt;

&lt;p&gt;我也是一样，只是在学习过程中，我渐渐了解了如何应对，慢慢的熟知了它，并且喜欢上了它。
这里我想所讲的目标管理，都是我们自己主动发出的目标，而不是别人给予我们的目标。
因为这两者有很大的不同，一个是你自己想要的，另一个则是别人强加给你的。至于怎么把别人强加给你的目标变成自己的目标则是另外一回事了。&lt;/p&gt;

&lt;p&gt;目标管理有一个成功方程式，即，&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;目标达成 = 目标 * 策略 * （动力 - 阻力）
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;方程式中每个词语都是关键，目标、策略、动力、阻力。下面我们就来 一 一 分解他们。&lt;/p&gt;

&lt;h3 id=&quot;首先目标制定非常关键它是所有一切的前提好的目标让我们动力满满而差的目标让我们沮丧&quot;&gt;首先，目标制定非常关键，它是所有一切的前提，好的目标让我们动力满满，而差的目标让我们沮丧。&lt;/h3&gt;

&lt;p&gt;定目标的时候，我们要考虑自己的力量，不能超过自己能力范围太多，这样会让我们受到很大的挫败感而不敢前进，也不能在自己的能力范围之内，会让我们觉得很无聊。&lt;/p&gt;

&lt;p&gt;目标制定的时候要有延展性，就是在原来可达到的目标之上延展30-50%左右，这样目标会更加合理些。如果原来完全没接触过的，则要把目标定的低一些，以免挫败感太强烈。&lt;/p&gt;

&lt;h3 id=&quot;其次目标要符合我们的价值观&quot;&gt;其次，目标要符合我们的价值观。&lt;/h3&gt;

&lt;p&gt;价值观反应了我们的个性，如果不符合我们的价值观，我们在目标的执行过程中也很难进行下去。&lt;/p&gt;

&lt;p&gt;其根本原因通常是跟我们的价值观相反的东西，我们很难坚持。&lt;/p&gt;

&lt;p&gt;没有绝对好的价值观，价值观通常不是我们想要的东西，而是反应我们性格的东西，你是什么样的人，就会有什么样的价值观。&lt;/p&gt;

&lt;h3 id=&quot;接着目标定制要有符合smart原则即要具体的可衡量的可达到的和其他目标有关联的&quot;&gt;接着，目标定制要有符合SMART原则，即：要具体的，可衡量的，可达到的，和其他目标有关联的。&lt;/h3&gt;

&lt;h3 id=&quot;最后长远目标要拆分成很多小而简单的目标比如1年的目标要拆分到月目标再拆分到周目标再细分到每天所要做的事情上来&quot;&gt;最后，长远目标要拆分成很多小而简单的目标，比如1年的目标要拆分到月目标，再拆分到周目标，再细分到每天所要做的事情上来。&lt;/h3&gt;

&lt;p&gt;也只有这样才能让自己感到安全，把事情落地到每天的任务上才能让我们有实实在在的安全感，而不会只是远远得望着这个1整年的大目标而感到害怕。&lt;/p&gt;

&lt;p&gt;当然我们在制定目标的时候，最好不要只制定单方面的目标，因为单一的目标会让自己很枯燥，也无法平衡人生的这辆车。
我们的目标应该比较多元化，这些目标加起来应该要平衡我们人生的各个方向，包括，精神上的、思维上的、情绪、身体、婚姻、亲情、人际、专业、娱乐、财务。
只有这样才能让我们能感到，是整辆车在不断前进，而不单单是单个轮子在走，整辆车则停滞不前。&lt;/p&gt;

&lt;h3 id=&quot;下面是一些在目标执行过程中的策略前半部分是我在实践过程中的一些体会后半部分是目标管理的学习有助于我们更好的坚持下去并最终完成目标&quot;&gt;下面是一些在目标执行过程中的策略，前半部分是我在实践过程中的一些体会，后半部分是目标管理的学习，有助于我们更好的坚持下去，并最终完成目标。&lt;/h3&gt;
&lt;h3 id=&quot;这是一个过程首先我们要享受过程在过程中学习和进步其次我们要不断调整自己努力让自己达成目标获得成就&quot;&gt;这是一个过程，首先我们要享受过程，在过程中学习和进步，其次我们要不断调整自己，努力让自己达成目标获得成就。&lt;/h3&gt;
&lt;h3 id=&quot;这不仅能让我们达成目标获得成就更重要的是在未来的日子中我们能活的更加自信和洒脱也许无法让你财务自由但肯定能让你精神自由&quot;&gt;这不仅能让我们达成目标获得成就，更重要的是在未来的日子中，我们能活的更加自信和洒脱，也许无法让你财务自由，但肯定能让你精神自由。&lt;/h3&gt;

&lt;h3 id=&quot;策略1&quot;&gt;策略1&lt;/h3&gt;

&lt;p&gt;观察自己&lt;/p&gt;

&lt;p&gt;记录生活中的每一样事情，这样可以方便我们统计自己的行为。
由于我们很难看清自己，自己的日常生活中觉醒过来是很难的，所以将自己生活中的每一件事记录下来，再隔1周去看这些数据，会有惊人的发现。&lt;/p&gt;

&lt;p&gt;发现了自己的问题，并寻求改善，再记录，在分析，再改善，形成一种良性循环。&lt;/p&gt;

&lt;p&gt;记录的方式有两种&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;第一种是，对某些自己在意的事做数据记录，并统计，最后通过数据分析，看到自己的问题，再寻找解决办法
第二种是，把事情的前因后果，和自己的所思所想，记录下来，通过复盘的形式寻找调整方案
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;策略2&quot;&gt;策略2&lt;/h3&gt;

&lt;h3 id=&quot;强化习惯&quot;&gt;强化习惯&lt;/h3&gt;

&lt;h3 id=&quot;在做了某件事之后如果得到一个你很喜欢的结果那么这件事做多了就会被强化&quot;&gt;在做了某件事之后，如果得到一个你很喜欢的结果，那么这件事做多了，就会被强化。&lt;/h3&gt;
&lt;p&gt;其结果可以是物品、赞扬、活动等，因此我们这里最应该研究的是如何奖励自己。&lt;/p&gt;

&lt;p&gt;研究奖励时应该注意，做完某件事后，会很愉快，这个愉快到底是从哪里来才是关键。&lt;/p&gt;

&lt;p&gt;下面些奖励的例子&lt;/p&gt;

&lt;p&gt;娱乐：看电影，爬山，游乐场，旅行，玩游戏
感情：和家人玩，和朋友玩，和闺蜜玩
物品：买电子产品，买家具电器，买衣服鞋子
社交：得到赞扬，受人关注
食物：美食，巧克力&lt;/p&gt;

&lt;h3 id=&quot;策略3&quot;&gt;策略3&lt;/h3&gt;

&lt;p&gt;列出阻力，并且列出应对方案&lt;/p&gt;

&lt;h3 id=&quot;列出所有可能阻碍自己计划进行的障碍并想出应对方案&quot;&gt;列出所有可能阻碍自己计划进行的障碍，并想出应对方案&lt;/h3&gt;

&lt;h3 id=&quot;当预料到的阻碍来临时用自己设计好的应对方案去应对&quot;&gt;当预料到的阻碍来临时，用自己设计好的应对方案去应对。&lt;/h3&gt;
&lt;h3 id=&quot;如果不行则再更换其他方案直到能应对为止&quot;&gt;如果不行，则再更换其他方案，直到能应对为止。&lt;/h3&gt;

&lt;p&gt;当自己实行应对方案时，有没有效果都要记录下来。
这能够不断记得自己的目标，也能看到自己的进步。&lt;/p&gt;

&lt;h3 id=&quot;策略4&quot;&gt;策略4&lt;/h3&gt;

&lt;p&gt;记录反馈数据&lt;/p&gt;

&lt;p&gt;前面说，我们的目标必须是可衡量的，也就是说，每一步必须都是可用数据表现出来了。&lt;/p&gt;
&lt;h3 id=&quot;在每次小目标完成时都要把数据记录下来这样可以及时帮助我们调整计划&quot;&gt;在每次小目标完成时都要把数据记录下来，这样可以及时帮助我们调整计划。&lt;/h3&gt;
&lt;h3 id=&quot;比如能看到我们最近的目标进度情绪变化等等从而调整新的计划和应对方案&quot;&gt;比如，能看到我们最近的目标进度，情绪变化等等，从而调整新的计划和应对方案。&lt;/h3&gt;

&lt;h3 id=&quot;策略5&quot;&gt;策略5&lt;/h3&gt;

&lt;p&gt;和自己签约&lt;/p&gt;

&lt;p&gt;这是一个增强动力的方法。&lt;/p&gt;

&lt;h3 id=&quot;把目标想象成一个项目甲方乙方都是自己&quot;&gt;把目标想象成一个项目，甲方乙方都是自己，&lt;/h3&gt;
&lt;h3 id=&quot;作为甲方我要监督项目的进展把控项目的质量在项目完成后给予报酬&quot;&gt;作为甲方，我要监督项目的进展，把控项目的质量，在项目完成后给予报酬。&lt;/h3&gt;
&lt;h3 id=&quot;作为乙方你要遵守合约的约定调动资源合理安排进度排除障碍保证高质量的完成项目&quot;&gt;作为乙方，你要遵守合约的约定，调动资源，合理安排进度，排除障碍，保证高质量的完成项目。&lt;/h3&gt;

&lt;p&gt;用这种很正式的书面约定，签字画押，将自己与目标绑定。
一个人对目标承诺度越高，他完成目标的可能性就越大。&lt;/p&gt;

&lt;p&gt;这样就能让自己非常重视目标，也就有了动力。&lt;/p&gt;

&lt;h3 id=&quot;1合约能让自己明确这个目标对自己有多重要&quot;&gt;1.合约能让自己明确这个目标对自己有多重要。&lt;/h3&gt;
&lt;h3 id=&quot;2合约会加强你对目标成功的期待它会让你看到目标实现后的样子就好像前方的苹果树你能清晰的看到它&quot;&gt;2.合约会加强你对目标成功的期待。它会让你看到目标实现后的样子，就好像前方的苹果树，你能清晰的看到它。&lt;/h3&gt;
&lt;h3 id=&quot;3合约是一种自我约束尽一切努力去完成约定不能随便毁约这就是契约精神&quot;&gt;3.合约是一种自我约束，尽一切努力去完成约定，不能随便毁约，这就是契约精神。&lt;/h3&gt;

&lt;p&gt;合约内容&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;1.把SMART目标写在合约里。
2.写下这个目标对你的重要性，1-5分。
3.把目标拆分成小目标也写在合约里，每个小目标的内容，阶段性的目标，周目标，这样就能减少自己对目标的恐惧。
4.把计划写在合约里，受到阻碍时的应对方案。
5.在合约里写明奖励机制，对小目标、阶段性目标也要有奖励。
6.写下你曾经经历过的依次成功经历，写出自己的哪些特质和能力让自己成功。
7.写下自己对合约的承诺，签字画押，并贴在墙上，同时把合约给重要的人看，从而获得他们的支持，亲人和朋友对你的支持是非常重要的。
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;策略-6&quot;&gt;策略 6&lt;/h3&gt;

&lt;h3 id=&quot;自我关怀&quot;&gt;自我关怀&lt;/h3&gt;

&lt;p&gt;大多数人总是在批评自己，而不是在关怀自己
自我批评是我们每个人都很熟悉和擅长的一件事。&lt;/p&gt;

&lt;p&gt;常常，当我们没有达成预想的目标时，或者经历失败的时候，我们总是会对自己说，“我就是不如别人”，“我真的很失败”，“我怎么就是做不好呢”，
然后你可能会对自己说，“你应该再努力点”，“你不能总是这样懒惰”，“你再不努力就没有机会了”，
我们以为只有不断鞭策自己、不断找出问题并改正，才能够进步，才能够达成目标。&lt;/p&gt;

&lt;h3 id=&quot;这其实是我们自身启动了防御系统通过自我批评的防御系统来改变自己通过改变自己让自己变得更加安全&quot;&gt;这其实是我们自身启动了防御系统，通过自我批评的防御系统来改变自己，通过改变自己，让自己变得更加安全。&lt;/h3&gt;
&lt;h3 id=&quot;这个防御系统有很多负面作用它会让我们出现三种常见表现第一战斗猛烈的批评自己第二僵住不停反思自己为什么会这样以后该怎么做第三逃跑逃避所有可能的失败一个人呆在安全的地方&quot;&gt;这个防御系统有很多负面作用，它会让我们出现三种常见表现，第一，战斗，猛烈的批评自己，第二，僵住，不停反思自己为什么会这样，以后该怎么做，第三，逃跑，逃避所有可能的失败，一个人呆在安全的地方。&lt;/h3&gt;
&lt;h3 id=&quot;它对我们实现目标有相反的作用会导致我们降低自信增加焦虑感甚至导致抑郁这些会让我们放弃目标的概率变的更大&quot;&gt;它对我们实现目标有相反的作用，会导致我们降低自信，增加焦虑感，甚至导致抑郁，这些会让我们放弃目标的概率变的更大。&lt;/h3&gt;

&lt;p&gt;自我关怀则不是，它可以让我们更好的坚持目标。
它最重要的作用是，让我们感到放松和安全，我们就像怀抱里获得温暖的孩子那样，当我们获得了力量，就不会轻易放弃我们的目标了。&lt;/p&gt;

&lt;h3 id=&quot;第一个方法写自我关怀日记&quot;&gt;第一个方法，写自我关怀日记&lt;/h3&gt;

&lt;p&gt;日记分为三部分：此时此刻，连接，爱。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;1.不带任何评价的写出此时此刻的感受，
2.要意识到世界上每个人都在经历挫折和痛苦，
3.把自己当做好朋友用关怀的语气给自己写些温暖的话语。
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;第二个方法身体触摸&quot;&gt;第二个方法，身体触摸&lt;/h3&gt;

&lt;p&gt;原理是，当母亲轻抚孩子的头，轻拍孩子的肩膀，拥抱孩子的时候，孩子都会感到安全和轻松。
同样的，我们对自己做类似的触摸时，也会感到被关爱，这样身体和精神都会放松下来。
找一个能让你感到安全、感到被支持的身体姿势，用这个姿势来安慰自己。&lt;/p&gt;

&lt;p&gt;例如：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;把一只手放在心上
双手交叠在心上
一只手放在心上，一只手放在肚子上
两只手交叠放在肚子上
一只手或两只手放在脸颊上
交叉胳膊，给自己一个温柔的拥抱
把手放在大腿上，轻轻地拍
一只手轻拍另一只收
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;第三个方法对自己说善意的话语&quot;&gt;第三个方法，对自己说善意的话语&lt;/h3&gt;

&lt;p&gt;在心里，或者对着镜子里的自己说，让自己感到安全、温暖、被支持的话语。
这个方法可以降低我们内在批评的背景声音，从而减少内在冲突，让我们在实现目标的路上更加平静和自信。&lt;/p&gt;

&lt;p&gt;善意的话语有点像是祝福自己的话语，“愿我平安、愿我平静、愿我勇敢”
找善意的话语，聚焦一个问题：我需要什么？然后从内心深处的需要找适合自己的话语。&lt;/p&gt;

&lt;h3 id=&quot;找到内心需要而不是想要需要是脖子以下的需要包括被认可被欣赏被爱被看见被听到被保护被珍惜被尊重&quot;&gt;找到内心需要，而不是想要，需要是脖子以下的需要，包括，被认可、被欣赏、被爱、被看见、被听到、被保护、被珍惜、被尊重。&lt;/h3&gt;
&lt;h3 id=&quot;去发现和找到适合自己的当自己情绪低落的时候希望别人对你说什么哪些话能够安慰到你然你平静下来或振作起来&quot;&gt;去发现和找到适合自己的，当自己情绪低落的时候，希望别人对你说什么？哪些话能够安慰到你，然你平静下来或振作起来？&lt;/h3&gt;
&lt;h3 id=&quot;把这些话记录下来作为自己的善意话语库供自己随时使用&quot;&gt;把这些话记录下来，作为自己的“善意话语库”，供自己随时使用。&lt;/h3&gt;

&lt;p&gt;这三个方法要勤加练习，自我关怀让阻碍我们实现目标的最大敌人（我们自己），变成帮助我们实现目标的最好盟友。&lt;/p&gt;

&lt;h3 id=&quot;策略7&quot;&gt;策略7&lt;/h3&gt;

&lt;h3 id=&quot;建立自我奖励机制&quot;&gt;建立自我奖励机制&lt;/h3&gt;

&lt;p&gt;1.列出自我奖励清单
    头脑风暴出所有让你感到高兴、满足、愉快的事物和活动。
        想象奖励：达成目标后的美好场景，让自己产生愉悦的感觉
        口头奖励：当做了符合目标的行为后，对自己口头的表扬
2.行为和奖励配对
    即，做了什么，就会获得什么奖励，这样就能强化我们的行为，让自己更加喜欢去做这件事。&lt;/p&gt;

&lt;h3 id=&quot;经常让自己去想象获得奖励时的场景这不仅会对你现在的行为有好处而且会提醒和强化你的长期目标是什么让你更聚焦于自己的目标&quot;&gt;经常让自己去想象获得奖励时的场景，这不仅会对你现在的行为有好处，而且会提醒和强化你的长期目标是什么，让你更聚焦于自己的目标。&lt;/h3&gt;

&lt;p&gt;口头奖励加强的自我关怀，替代了破坏性强的自我批评，例如达成小目标后对自己说“你能这样做很棒”，替代了破坏性强的自我批评“这没什么大不了的，别人都比你做的好”&lt;/p&gt;

&lt;p&gt;我们经常赞扬和鼓励自己，就会更愿意去做自己期待的事情。&lt;/p&gt;

&lt;p&gt;用奖励强化行为，完成什么事后，获得什么奖励，这种强化机制，让自己更有动力去执行这件事。&lt;/p&gt;

&lt;p&gt;奖励强化行为可以用个方法更有效，即代币机制：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;1.给期待的行为制定相应的分值，
2.积累到一定分值可以获得奖励，
3.给每个奖励都标上需要的分值，
4.用代币机制去获得这个奖励
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;可以用这种方式为自己建立良好的习惯。执行了什么符合目标的事情，获得多少分值，累积了多少分值，想要获得什么奖励。
关键在于记录，记录自己做的事情，并记录分数。&lt;/p&gt;

&lt;h3 id=&quot;自我奖励改善了我和自己的关系我们犒劳了自己奖赏了自己关注自己的需要并满足它们这些都是滋养自己的事情&quot;&gt;自我奖励改善了我和自己的关系，我们犒劳了自己，奖赏了自己，关注自己的需要并满足它们，这些都是滋养自己的事情。&lt;/h3&gt;
&lt;p&gt;让自己（主动让自己，调整自己）进入一个良性的循环中去。&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;专项对策计划遇阻的对策&quot;&gt;专项对策：计划遇阻的对策&lt;/h3&gt;

&lt;p&gt;很多人认为事情的发展是一条直线，其实不是，它是曲折的，不可预测的。&lt;/p&gt;

&lt;h3 id=&quot;大部分人害怕失败当他们看到失败的可能性时为了避免彻底失败干脆主动放弃逃避失败&quot;&gt;大部分人害怕失败，当他们看到失败的可能性时，为了避免彻底失败，干脆主动放弃，逃避失败。&lt;/h3&gt;

&lt;h3 id=&quot;大部分人对自己太苛刻用很多必须和应该来包装自己的计划一旦计划中断就会自我攻击你就是不如别人你就是个三分钟热度的人你一辈子也就这样了&quot;&gt;大部分人对自己太苛刻，用很多‘必须’和‘应该’来包装自己的计划，一旦计划中断，就会自我攻击，‘你就是不如别人’、‘你就是个三分钟热度的人’、‘你一辈子也就这样了’&lt;/h3&gt;

&lt;p&gt;很多人只关注结果，而不享受过程。当不符合预期的情况出现时，就会因为害怕而夸大可能出现的结果。比如三天不运动就会说，自己的坚持运动的计划破产了。悲观预期削弱了他们前进的动力，无法继续走下去。&lt;/p&gt;

&lt;p&gt;用ABCDE法，即，&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;A记录客观事实
B记录发生后自己的看法
C记录行为导致的结果
D重新审视自己的看法
E计划在新看法下激发新行动
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;因为这事会发生很多次，所以我认为除了这个方法外，我们可以再改进方法，即，记录，统计，总结，制定应对方法。
把所有阻碍都记录下来，然后统计原因，总结原因，制定当这些原因发生的时候，我们应该用什么样的方法或技巧去应对。&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;专项对策计划失败时的对策&quot;&gt;专项对策：计划失败时的对策&lt;/h3&gt;

&lt;p&gt;《终生成长》提到的成长型思维是关键&lt;/p&gt;

&lt;p&gt;固定型思维，人是天生的，聪明和笨是不无法改变的，人的基因决定了一切，命运是无法改变的，他人的成功对我造成了威胁。&lt;/p&gt;

&lt;h3 id=&quot;成长型思维人的智力是可以提高的这会让人产生学习的欲望人是可以通过努力学习和拼搏改变命运的遇到困难时迎接挑战面对挫折坚持不懈努力练习才能熟能生巧从他人的成功中学到新的知识获得新的灵感&quot;&gt;成长型思维，人的智力是可以提高的（这会让人产生学习的欲望），人是可以通过努力学习和拼搏改变命运的，遇到困难时迎接挑战面对挫折坚持不懈，努力练习才能熟能生巧，从他人的成功中学到新的知识获得新的灵感。&lt;/h3&gt;

&lt;p&gt;关键是去塑造成长型思维，有哪些方法可以帮助我们塑造成长型思维呢？&lt;/p&gt;

&lt;h3 id=&quot;1当出现失败时观察自己的固定型思维在说些什么把它们写下来它们可能是放弃吧你的能力还做不了这么难的事情或者不管你怎么努力你永远都不可能比别人优秀&quot;&gt;1.当出现失败时，观察自己的固定型思维在说些什么。把它们写下来，它们可能是“放弃吧，你的能力还做不了这么难的事情”或者“不管你怎么努力，你永远都不可能比别人优秀”&lt;/h3&gt;

&lt;h3 id=&quot;2和自己的固定型思维对话例如欢迎你存在你的存在使得我有对照可以让我变的更好让我们来看看能从这次失败中学到什么我可以做些什么可以让我做得更好些&quot;&gt;2.和自己的固定型思维对话，例如“欢迎你存在，你的存在使得我有对照，可以让我变的更好。让我们来看看能从这次失败中学到什么？我可以做些什么可以让我做得更好些”&lt;/h3&gt;

&lt;h3 id=&quot;3列出具体的调整计划按照调整计划去行动这步很重要是思维到行为落地的过程只有真正行动起来才算有效调整也只有真正按调整计划行动起来成长型思维才算真正建立起来&quot;&gt;3.列出具体的调整计划，按照调整计划去行动。这步很重要，是思维到行为落地的过程，只有真正行动起来才算有效调整。也只有真正按调整计划行动起来，成长型思维才算真正建立起来。&lt;/h3&gt;

&lt;h3 id=&quot;塑造成长型思维的关键是让自己的关注点从我是不是足够好转变为关注我还能学到什么东西不断从失败中学习成长让失败成为我们实现目标的加速器&quot;&gt;塑造成长型思维的关键是让自己的关注点，从“我是不是足够好”转变为关注“我还能学到什么东西”，不断从失败中学习成长，让失败成为我们实现目标的加速器。&lt;/h3&gt;

&lt;p&gt;（可以学习下人复盘加速成长）&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;专项对策意志力&quot;&gt;专项对策：意志力&lt;/h3&gt;

&lt;p&gt;意志力包括三种不同级别大小的力量&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;1.我不要
2.我要做
3.我想要
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我不要，说的是我为了自身考虑，不要做什么，比如我不要吸烟、我不要玩游戏、我不要吃甜食等。
我不要这种力量虽然能够促进自我，但力量太薄弱，人很难用这种力量与自己对抗。&lt;/p&gt;

&lt;p&gt;我要做，说的是我在事情面前认为自己应该做的，比如我要努力干活，我要努力学习，我要坚持运动。
我要做这种力量，虽然比我不要更强大一些，但仍然不受我们控制，不是我们要做就能做到的，时常会遇到我们要做什么但最终还是没有去做或者做不下去的情况。&lt;/p&gt;

&lt;p&gt;我想要，说的是当自己知道真正的目标的时候，就更有可能拒绝眼前的诱惑，并且会主动去选择那些困难的任务，所以它是前面两种力量的基础，也是根基最深的力量。
我想要这种力量，带给你无穷的想象力，无穷的动力，让你能想出各种办法，专心专注的去实现自己的目标。&lt;/p&gt;

&lt;p&gt;意志力就是去驾驭这三种力量而存在的，如果驾驭的好，它就能帮我们实现目标。
意志力也是可以通过练习提高的。&lt;/p&gt;

&lt;h3 id=&quot;提高意志力方案&quot;&gt;提高意志力方案&lt;/h3&gt;

&lt;p&gt;1.运动提高意志力储备&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;运动就是提高意志力储备的最好的方法之一。
每天运动一下，可以是10分钟-60分钟之间，不断加强锻炼强度，让自己不断去提高意志力储备。
这里提醒大家一句，低强度而持续的锻炼，比高强度但短暂的锻炼有更明显的提高意志力的效果。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;2.有计划的练习，‘我不要’，‘我要做’，‘我想要’的三种能力&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;1.列出‘我不要’的事情，和，‘我要做’的事情，用任务的方式去做自己给自己规定的事情。
    比如，我不要喝咖啡，我不要吸烟，我不要吃甜食。我要每天看书30分钟，我要每天早起，我要每月强制存款3000元，我要每周写篇文章。
2.有意识的每天练习‘我不要’和‘我要做’能锻炼自己的意志力，为自己的执行计划打下坚定的基础。
    增强这两种力量，对我们实现目标的可能性也就越大。

3.练习‘我想要’的力量时，有两个方法，
    一是，每天花两分钟在脑海中描绘目标实现后的景象，始终记住自己的目标。
    二是，面对诱惑时不马上行动，而是先等10分钟，10分钟里告诉自己真正想要的是什么，让前额皮层开始工作。

 通过每天记住自己的目标，诱惑前思考10分钟，来锻炼“我想要”的力量，这样做的同时也会增强“我不要”和“我要做”的力量。
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;给自己未来写信方案&quot;&gt;给自己未来写信方案&lt;/h3&gt;

&lt;p&gt;我们会把未来的自己想象成完全不同的人，这是一种错误的想象。&lt;/p&gt;

&lt;h3 id=&quot;这会让我们对待未来的自己就像对待陌生人一样当我们考虑未来的自己的时候你就好像在想一个陌生人一样&quot;&gt;这会让我们对待未来的自己就像对待陌生人一样。当我们考虑未来的自己的时候，你就好像在想一个陌生人一样。&lt;/h3&gt;

&lt;p&gt;这样的话，你就有很大可能对未来的自己说，“去你的，你会怎么样我一点都不关心”，这会导致我们不关心自己的未来，而被当下的诱惑所屈服。&lt;/p&gt;

&lt;h3 id=&quot;这也是为什么很多人不为未来做准备的原因因为和陌生人的幸福比起来我们更关心自己的幸福&quot;&gt;这也是为什么很多人不为未来做准备的原因，因为和陌生人的幸福比起来，我们更关心自己的幸福。&lt;/h3&gt;

&lt;h3 id=&quot;比如不为年老的自己存款不为了未来更优秀的自己而学习不为了未来的身体健康而锻炼不为了更好的节奏而早起等等等&quot;&gt;比如，不为年老的自己存款，不为了未来更优秀的自己而学习，不为了未来的身体健康而锻炼，不为了更好的节奏而早起等等等。&lt;/h3&gt;

&lt;p&gt;有一种测量方式，叫做‘未来自我的连续性’，这个方法可以帮助我们认识到未来的自己在本质上和现在的自己是一样的。&lt;/p&gt;

&lt;h3 id=&quot;研究表明那些未来自我连续性比较高的人也就是现在的我和未来的我圆圈重叠得比较多的人为未来计划和实施的事情也越多表象上来看他们的存款更多储备的知识更丰富也更加健康未来也更加美好&quot;&gt;研究表明，那些“未来自我连续性”比较高的人，也就是“现在的我”和“未来的我”圆圈重叠得比较多的人，为未来计划和实施的事情也越多，表象上来看他们的存款更多，储备的知识更丰富，也更加健康，未来也更加美好。&lt;/h3&gt;

&lt;p&gt;当你的两个圈重叠越多时，你就越有可能为未来的目标而努力，你的目标也就越容易实现。&lt;/p&gt;

&lt;p&gt;我们可以每个月给未来的自己写封信，这有助于我们挡住眼前即时满足的诱惑，而选择更有利于长期目标的行动。&lt;/p&gt;

&lt;p&gt;信中可以写：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;想象下自己未来在做什么？
他会如何看待现在做出的选择？
向未来的自己描述下自己将会怎么做，这是他希望的吗？
我对未来有什么希望？
我觉得自己会变成什么样？
想象未来的自己回头看现在的自己，他会为现在的自己做了什么而表示感激？
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;每个月写一封，这样的信，给未来的自己，让自己和未来的自己越来越亲密，重叠度越来越高，也就越来越喜欢做对自己未来有帮助的事情了。&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;专项对策时间不够&quot;&gt;专项对策：时间不够&lt;/h3&gt;

&lt;p&gt;假如你是个‘时间富翁’，有取之不尽的时间，你每天都有很多时间去做你想要做的事情，运动、学习、工作、副业，实现一个个目标，你变得健康、自信、富有。&lt;/p&gt;

&lt;p&gt;因此关键是时间，和使用时间的效率。
时间和效率，两者不同，一个是注重使用情况，另一个注重使用的点。&lt;/p&gt;

&lt;h3 id=&quot;我们知道二八原则20的重要的事情要花费80的精力是比较高效的而反过来如果我们把80的精力花在80不重要的事情上效率就会很差&quot;&gt;我们知道二八原则，20%的重要的事情，要花费80%的精力是比较高效的，而反过来如果我们把80%的精力花在80%不重要的事情上，效率就会很差。&lt;/h3&gt;

&lt;p&gt;首先把无效时间转变为有效时间。
如何转变呢？&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;1.关键在于记录时间

2.记录后，分析你的时间，每天无效时间和有效时间分别是多少

3.把无效时间转变为以有效时间。
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;其次重注效率&quot;&gt;其次重注效率。&lt;/h3&gt;

&lt;p&gt;给那些重要但不紧急的目标加上优先级。
我们很多时候都是在工作，处理的都是紧急但不重要的事情，导致对我们重要而不紧急的事情被无限期拖延。&lt;/p&gt;

&lt;h3 id=&quot;方法1每天列个清单只做5件事如果出现了更重要的事情则在5件事中删除一件&quot;&gt;方法1，每天列个清单，只做5件事，如果出现了更重要的事情，则在5件事中删除一件。&lt;/h3&gt;
&lt;p&gt;这样可以确保我们把时间花在这些重要的事情上。&lt;/p&gt;

&lt;h3 id=&quot;方法2把紧急但可以缓一缓做的事情放在一个列表里然后在某段时间批量处理&quot;&gt;方法2，把紧急但可以缓一缓做的事情放在一个列表里，然后在某段时间批量处理。&lt;/h3&gt;
&lt;p&gt;比如还信用卡，回某人短息，处理邮件，安排会议时间等，放在任务列表里，等到自己手头上的事情结束时，集合起来去完成它们。&lt;/p&gt;

&lt;h3 id=&quot;最后是时间盒子法&quot;&gt;最后是时间盒子法&lt;/h3&gt;

&lt;p&gt;安排固定一段时间，专门做某事，就叫做时间盒子。&lt;/p&gt;
&lt;h3 id=&quot;比如早上8点到8点半30分钟写文章9点到9点半健身房运动晚上10点10点半中午12点到12点半30分钟看书梳理一天的生活&quot;&gt;比如早上8点到8点半：30分钟写文章，9点到9点半：健身房运动，晚上10点10点半：中午12点到12点半，30分钟看书：梳理一天的生活。&lt;/h3&gt;
&lt;h3 id=&quot;把时间固定下来成为一个时间盒子这样能确保我们每天都能朝着目标行动&quot;&gt;把时间固定下来，成为一个时间盒子。这样能确保我们每天都能朝着目标行动。&lt;/h3&gt;

&lt;p&gt;时间盒子要注意两点：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;1.时间要固定，形成固定节奏后不能随意改动。
2.时间盒子内，神圣不可侵犯，关掉手机关掉电视，全心全意做好这件事。
&lt;/code&gt;&lt;/pre&gt;
</content>
 </entry>
 
 <entry>
   <title>思路探讨(六十) 杂谈</title>
   <link href="http://www.luzexi.com/2021/03/22/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A860"/>
   <updated>2021-03-22T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/03/22/思路探讨60</id>
   <content type="html">&lt;p&gt;最近看了《基业长青》，这本书着实不错，它讲了组织发展的核心，分析了曾经那些高瞻远瞩的公司是如何屹立不倒并越走越旺的根本原因。&lt;/p&gt;

&lt;p&gt;作者为了不让自己陷入自己设计好的偏见陷阱中，拿了各种与自己观念相反的案例来分析，包括那些曾经高瞻远瞩公司后来倒下去的案例，以及并没有与自己观点相符但活的很好的公司。&lt;/p&gt;

&lt;p&gt;全书给我很多启发，其精神世界的力量贯穿始终，后面打算专门写一篇关于此书的文章来全面介绍此书的精髓之处。&lt;/p&gt;

&lt;p&gt;看完后让我沉思许久，发现我的技术经验是我唯一的财富，它不能转变成为可以传承的东西，或者说可以几代人不断经营的东西，这让我重新审视了我的职业和我正在做的事情。&lt;/p&gt;

&lt;p&gt;从更大的视角来说，我现在做的事情是短视的。&lt;/p&gt;

&lt;p&gt;我正在做一件，只考虑了我自己的事情。&lt;/p&gt;

&lt;p&gt;我喜欢编程技术，用它来赚钱养家糊口，这其实就是短视的一种，因为我并没有建立几代人可以共同努力的东西，只顾自己喜欢和擅长的东西。&lt;/p&gt;

&lt;p&gt;我的经验和技术会随着我的死亡而消失，期间也无法与任何人共同经营，而我只能自己拼搏，然后独自乐呵或悲伤。&lt;/p&gt;

&lt;p&gt;经验的好处是让我在处理事情的时候有了依据，但同时也限制了我的想象力。&lt;/p&gt;

&lt;p&gt;想到这里，我确实短视了很多年了，只想到自己没有太多的顾及别人。&lt;/p&gt;

&lt;p&gt;不过庆幸的是这么多年下来，我比较注重精神世界，没有被世俗吞没。来深圳后，让我想明白一件事，大多数事情跟我想象的完全不一样，我只要做好我认为对的事情就可以了，直到自己认识到有问题及时改进，这样不断完善自己。&lt;/p&gt;

&lt;p&gt;人与人的区别是精神世界，俗话说，广厦千间，夜眠仅需六尺，家财万贯，日食不过三餐。&lt;/p&gt;

&lt;p&gt;房再多，睡觉时也只需要4平方面的床，钱再多，一天只不过吃三餐。&lt;/p&gt;

&lt;p&gt;说到这里，让我想起，以前看过的《亮剑》中的军魂，它就是精神世界的代表，它应该代表了军队中精神世界的比较高的境界。&lt;/p&gt;

&lt;p&gt;狭路相逢勇者胜，逢敌先亮剑而不是想着先退缩，这是真实战斗中比较好的精神状态。&lt;/p&gt;

&lt;p&gt;精神世界是人类最高层级的东西，就因为人有精神世界，才与机器人完全区别开来。
所以我在自律的同时，一直告诫自己，要多观察自己，要多问自己为什么，要多看看别人和自己的区别是什么，避免死板。&lt;/p&gt;

&lt;p&gt;如果我想要更多的确定性，就得付出更多的努力，去经营的世界，经营的理念，步步完善，日日积累。&lt;/p&gt;

&lt;p&gt;有才却耐不住性子，忍受不了枯燥，和平庸者没两样，甚至会更差。更何况我自认为，我是个《笨小孩》，这首歌也在最近的歌单里放了很多遍，勉励自己要稳扎稳打。&lt;/p&gt;

&lt;p&gt;所以不要放纵，它会成为习惯，要时常忍耐，才能守住自己的成果，因为它很容易就灰飞烟灭。回头看去年最大的成就是，其实是成功戒烟。&lt;/p&gt;

&lt;p&gt;最后再提醒自己两句：&lt;/p&gt;

&lt;h3 id=&quot;1你是什么样的人你就会有什么样的朋友和家人&quot;&gt;1.你是什么样的人，你就会有什么样的朋友和家人。&lt;/h3&gt;

&lt;h3 id=&quot;2你怎么对待别人别人就会怎么对待你&quot;&gt;2.你怎么对待别人，别人就会怎么对待你。&lt;/h3&gt;
</content>
 </entry>
 
 <entry>
   <title>给女儿写信(十) 关于自我驱动</title>
   <link href="http://www.luzexi.com/2021/03/18/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A110"/>
   <updated>2021-03-18T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/03/18/给女儿的信10</id>
   <content type="html">
&lt;hr /&gt;

&lt;p&gt;导读：喜欢跟孩子们一起，可惜一直在外地工作，因此我给孩子写点信并录成视频，跟孩子聊聊自己的故事以及最近的感受&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Hi Sharon Anne，爸爸又想你们了。&lt;/p&gt;

&lt;p&gt;爸爸再给你们讲讲最近爸爸给自己定目标的事情。&lt;/p&gt;

&lt;p&gt;爸爸为什么要给自己定目标呢？&lt;/p&gt;

&lt;p&gt;因为人总是会有懒的时候，总是会有不用脑子去做事情的时候，爸爸想更好的鞭策自己。&lt;/p&gt;

&lt;p&gt;于是自己给自己定目标，有了目标，爸爸就要想各种办法去慢慢的实现目标，爸爸在慢慢实现目标的过程中，就会不断得动脑筋。&lt;/p&gt;

&lt;p&gt;这样爸爸不断的动脑筋，既实现了目标，自己获得了成就感，开心了，脑袋也越来越聪明了，你说这是不是一举三得。&lt;/p&gt;

&lt;p&gt;爸爸知道无论大人小孩，都会有懒的动的时候。&lt;/p&gt;

&lt;p&gt;我们家每个人都很自律，每天都会坚持打卡，每天都会学习、看书，我们家每个人都很棒喔。&lt;/p&gt;

&lt;p&gt;但打卡、学习、看书，并不一定代表着动脑子哦，有时会进入一种浑浑噩噩的状态，手在写、嘴巴在动，但脑子却不在动。&lt;/p&gt;

&lt;p&gt;这很常见，爸爸时常会这样不动脑子的时候。这样很浪费时间，看似自己在学习，其实是在浪费时间。&lt;/p&gt;

&lt;p&gt;于是爸爸问自己，那该怎么办呢，爸爸想到了，用定目标的方式来驱动自己，驱动自己去做的更好。&lt;/p&gt;

&lt;p&gt;有了目标，爸爸就会想各种办法去实现目标，这样既动了脑子，又完成了目标，自己就会很开心。&lt;/p&gt;

&lt;p&gt;你们也可以试试喔，很有趣很好玩的，多练习几次，一旦上手了就会感觉很棒喔。&lt;/p&gt;

&lt;p&gt;Sharon和Anne也要加油喔，妈妈和小姨妈也是呢，我们全家都一起加油好不好！加油~&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(五十九) 避免自动驾驶</title>
   <link href="http://www.luzexi.com/2021/03/15/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A859"/>
   <updated>2021-03-15T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/03/15/思路探讨59</id>
   <content type="html">&lt;p&gt;这周继续目标管理，执行过程中细节比较多，每个脚步每个转折我都记录下来了，待积累一段时间再来为大家呈现我的总结和分享。&lt;/p&gt;

&lt;h3 id=&quot;我对目标管理的赞不绝口目标管理是我见过的最强的自我驱动方式也是我见过的最好的自我潜力挖掘方式&quot;&gt;我对目标管理的赞不绝口：目标管理是我见过的，最强的，自我驱动方式，也是我见过的最好的自我潜力挖掘方式。&lt;/h3&gt;

&lt;p&gt;一直以来我都在为自己找自我驱动的方式，也一直认为自我驱动是人这一生很重要的东西。&lt;/p&gt;

&lt;p&gt;活了这么久，我受够了日复一日枯燥乏味的生活，希望能给自己一些改变，寻找自我驱动就是我寻求突破和改变的途径。&lt;/p&gt;

&lt;p&gt;其实我们在日常工作生活中，非常容易行尸走肉，无论做的是什么高级的’活‘，都是如此。&lt;/p&gt;

&lt;p&gt;无论什么事情，做多了，我们就会发现很无聊，如果我们没有从中发现新的亮点，新的兴趣点的话，就会陷入疲惫，陷入枯燥乏味，陷入苦闷的状态。&lt;/p&gt;

&lt;h3 id=&quot;我写公众号本身就是为了避免行尸走肉所以我很喜欢写公众号它能让我思考反思通过写公众号我找出了自身很多问题这样就能更好的观察自己了解自己&quot;&gt;我写公众号本身就是为了避免行尸走肉，所以我很喜欢写公众号，它能让我思考，反思。通过写公众号我找出了自身很多问题，这样就能更好的观察自己、了解自己。&lt;/h3&gt;

&lt;p&gt;我知道，无论做什么都是会陷入一种自动驾驶的状态，当进入自动驾驶状态时，我们会很自然的忽视周围发生的问题。&lt;/p&gt;

&lt;p&gt;看书、健身、写作、学习、做题、工作、甚至社交也同样不例外。&lt;/p&gt;

&lt;p&gt;特别是 ，我一直提倡的自律，时间管理，精力管理，目标管理，其实自律这东西最容易行尸走肉，每天日复一日的做同样的事情，如果你没有找到做这些事情的动力、看到自律的未来、没有期望其达到的目标，那么做的再多，坚持得再久，可能效果也会是微乎其微的。&lt;/p&gt;

&lt;h3 id=&quot;所以关键不是你做了多少而是你是否主动去思考分析调查思考后有没去尝试去实践&quot;&gt;所以关键不是你做了多少，而是你是否主动去思考、分析、调查，思考后有没去尝试、去实践。&lt;/h3&gt;

&lt;h3 id=&quot;主动思考驱动了自我完善觉醒了自我意识&quot;&gt;主动思考驱动了自我完善，觉醒了自我意识。&lt;/h3&gt;

&lt;p&gt;但其实实际中并没那么容易，主动思考很难，人都有惰性，不是随时都能做到的，那么怎么让自己保持主动思考呢，目标管理就可以。&lt;/p&gt;

&lt;p&gt;当然它是有前提条件的，前提条件是：自律、进取。这也是我核心价值观中的前两个。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(五十八) 学习目标管理的第二周</title>
   <link href="http://www.luzexi.com/2021/03/08/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A858"/>
   <updated>2021-03-08T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/03/08/思路探讨58</id>
   <content type="html">&lt;p&gt;说到上周，给自己定的目标比较满，自己比较焦虑，当时写下思路探讨57后，发现最后竟然在自己的奋力拼搏之下在周日的最后一刻完成了。&lt;/p&gt;

&lt;p&gt;虽然完成了，喜悦感和成就感，仍然很少，因为太满太重，让自己很焦虑，做事很急躁，即使完成了也觉得太辛苦了，不想再来第二次。&lt;/p&gt;

&lt;p&gt;这周又继续犯了同样的毛病，，给自己定的目标仍然太高，但心态稳了，由于这周紧急事情比较多，大部分时间都花费在了工作上，留给自己的时间很少。但我仍然挤出了不少时间来完成目标，可惜最后仍然还有一项完全没时间去做。&lt;/p&gt;

&lt;p&gt;我原谅了我自己，我跟我自己说，事情一直在变，没有按计划走很正常，以后我要考虑的更周到一些，于是默默的将这项目标移到了下周。&lt;/p&gt;

&lt;h3 id=&quot;没有按计划走很正常生活和工作中失败也很正常我一生都充斥着失败我曾一度认为自己会成为那个游走在社会边缘的人成为一个寄生虫最后还不是挺过来了&quot;&gt;没有按计划走很正常，生活和工作中失败也很正常，我一生都充斥着失败，我曾一度认为自己会成为那个游走在社会边缘的人成为一个寄生虫，最后还不是挺过来了。&lt;/h3&gt;

&lt;h3 id=&quot;失败是人生旅途中的很关键的要素它激发我的反思敲醒了我的斗志让我从迷糊和沉睡中醒来进而让我从困惑中走出来所以没有必要太在意反而要感谢它它抹去了那层虚伪的窗户纸让我们知道我们自己现在的真实状况&quot;&gt;失败是人生旅途中的很关键的要素，它激发我的反思，敲醒了我的斗志，让我从迷糊和沉睡中醒来，进而让我从困惑中走出来。所以没有必要太在意，反而要感谢它，它抹去了那层虚伪的窗户纸，让我们知道我们自己现在的真实状况。&lt;/h3&gt;

&lt;h3 id=&quot;当你遇到失败时你应该这么想所有杀不死你的终将使你强大只要秉持这个理念你就能从失败走向成功从黑暗走向光明&quot;&gt;当你遇到失败时，你应该这么想，所有杀不死你的终将使你强大。只要秉持这个理念，你就能从失败走向成功，从黑暗走向光明。&lt;/h3&gt;

&lt;p&gt;其实这周我完成了很多事情，目标清单上的内容，我很有把握努力一下能完成它们，虽然最后还是有一项没完成，但我没有沮丧，也没有焦虑，从这个现象上看，我进步了，我对目标的本身，以及对制定目标的意义有了更深层次的理解，同时我认为我对制定目标的尺度在越来越靠谱了。&lt;/p&gt;

&lt;h3 id=&quot;前面一直都在说我给自己制定周目标但其实周目标的大方向要用月目标来配合月目标要用年目标来配合这样才能真正发现自己每周做的事情都是有意义的而1年目标要用5年目标来指引方向5年目标要用10年目标来指引方向10年目标要用生命的目标来指引方向&quot;&gt;前面一直都在说我给自己制定周目标，但其实周目标的大方向要用月目标来配合，月目标要用年目标来配合，这样才能真正发现自己每周做的事情都是有意义的，而1年目标要用5年目标来指引方向，5年目标要用10年目标来指引方向，10年目标要用生命的目标来指引方向。&lt;/h3&gt;

&lt;h3 id=&quot;只有这样一环扣一环拆分到每周的目标才会更加清晰信心也能得到更加多的支撑知道自己要什么该怎么做&quot;&gt;只有这样一环扣一环，拆分到每周的目标才会更加清晰，信心也能得到更加多的支撑，知道自己要什么，该怎么做。&lt;/h3&gt;

&lt;h3 id=&quot;当你知道自己要什么该怎么做时具体执行时如果能多坚持几周每周都能完成自己拆分后的目标我猜你很快就能体会到什么叫信心爆棚动力十足思路广阔&quot;&gt;当你知道自己要什么，该怎么做时，具体执行时如果能多坚持几周，每周都能完成自己拆分后的目标，我猜你很快就能体会到什么叫信心爆棚、动力十足、思路广阔。&lt;/h3&gt;

&lt;p&gt;很多人害怕目标，一看到目标就吓坏了。其实目标没有这么可怕，就像我今天跟媳妇提出要买别墅这个目标时那样，媳妇连想都没想过，分析都没分析过就吓坏了，直接反击到“不可能”。&lt;/p&gt;

&lt;p&gt;其实这个目标有很多条道路通向它，比如选择3、4、5线城市的别墅，别墅本身就没有那么多聚集属性，本身就是要远离城市的喧嚣，是一个要让自己更加独立和自律的地方，假如我们没有那么多现金可以抵押现有的房子拿现金付首付，也可以向亲戚朋友借点钱，或者把房子卖了去3、4线，或者把现有的房子拆分成两半，一半付别墅首付，一半付更小套的首付，装修自己来，自己一天天蚂蚁搬砖自己DIY。如果仅仅是为了住别墅，其实方法有很多种，条条道路通罗马，把所有选项整理一下，找出适合自己的，目标就变的没那么可怕了。&lt;/p&gt;

&lt;p&gt;哈哈哈哈我脑洞挺大的，说实话，这个脑洞还真的可行。&lt;/p&gt;

&lt;p&gt;以前我时常问自己，我究竟要的是什么，
后来改成了，我想成为什么样的人，
现在又改成了，我想让后人如何评价我，
每一层思想的蜕变都有不一样的风景。
每每遇到问题，都要以这个为前提去思考。&lt;/p&gt;

&lt;p&gt;希望我的学习分享，能给大家一些启示。&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>思路探讨(五十七) 目标管理学习的第一周</title>
   <link href="http://www.luzexi.com/2021/02/28/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A857"/>
   <updated>2021-02-28T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/02/28/思路探讨57</id>
   <content type="html">&lt;p&gt;目标管理这个词已经听过很多次了，以前一直对它有点排斥，以为是陈词滥调，因此也一直没有去研究。&lt;/p&gt;

&lt;p&gt;但就在最近发现自己对目标管理这个东西越来越感兴趣了，越来越对目标这个事感兴趣，于是找了网上的学习资料学习了一下‘目标管理’，并在我生活中实践一下。&lt;/p&gt;

&lt;p&gt;其实这也符合我近些年一直练习的精力管理和时间管理，也让我回忆起，去年看过的一本书《精英们的清晨日课》，里面的一些话让我感同身受，后续再回顾总结一下这本书。&lt;/p&gt;

&lt;p&gt;这里简单介绍下目标在制定时必须遵守的原则。&lt;/p&gt;

&lt;p&gt;首先目标必须与我们的核心价值观一致，核心价值观就是我们自己认为我这一生想成为什么样的人，自律、智慧、真诚、可靠、进取、友爱、合作、魅力、独立、冒险、安全等等。&lt;/p&gt;

&lt;p&gt;其次我们的目标必须遵循3个准则：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	1. 目标必须遵循 SMART原则，即具体的、可测量的、可达到的、和其他目标相关联的、有时间限制的。
	2. 目标必须在人生的各领域平衡，包括，事业、家庭、健康、关系、财务、精神生活等人生各领域，一个不能少。
	3. 目标必须是有挑战性的，并且不能是够不到的，也不能是轻松就能办到的，必须我们使劲用力跳一下能够到的目标。
		有挑战性目标，会给你带来突破限制的想象力和创造力，让你看到自己都没发现的潜力。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;等等等….其实里面有很多内容和原理，待我日后一一跟大家详述。&lt;/p&gt;

&lt;p&gt;很不幸目标管理学习第一周，由于自己制定的目标太大，导致挫败感很强烈。&lt;/p&gt;

&lt;p&gt;在此我对这次失败的目标管理总结一下。&lt;/p&gt;

&lt;h3 id=&quot;第一这周我给自己设定了一个周目标可以我设定了很多的目标把自己的目标排的很满&quot;&gt;第一，这周我给自己设定了一个周目标，可以我设定了很多的目标，把自己的目标排的很满。&lt;/h3&gt;

&lt;p&gt;这周前3天动力很足但后几天就嫣了，因为前3天太用力了差点累垮了自己，虽然我做了很多事情，完成了比较重的任务，但没有感觉到成就感，相反挫败感却油然而生。&lt;/p&gt;

&lt;p&gt;这是因为自己给自己制定的目标太多，即使我花了大力气也没能完成，自己却发现用力过猛有点不适应。当我发现无法完成所有任务时，情绪就会低落，精神状态就会萎靡，接下来就越来越糟糕。&lt;/p&gt;

&lt;h3 id=&quot;第二我在制定目标时并没有思考它是否符合我的核心价值观而是一味的觉得自己想达成这些目标&quot;&gt;第二，我在制定目标时并没有思考，它是否符合我的核心价值观，而是一味的觉得自己想达成这些目标。&lt;/h3&gt;

&lt;p&gt;没有核心价值观的支撑，目标在执行时，显得很脆弱，一旦遇到不顺心的事，就没有底层原动力去支撑继续前进。&lt;/p&gt;

&lt;p&gt;目标必须符合我的核心价值观，我的核心价值观是，自律，智慧，团队合作，进取。当然核心价值观随着年龄的增大会有所不同，我也不断在审视自己的核心价值观。&lt;/p&gt;

&lt;h3 id=&quot;第三我的目标并没有平衡我的生活只是一味的在某个方向上尝试突破&quot;&gt;第三，我的目标并没有平衡我的生活，只是一味的在某个方向上尝试突破。&lt;/h3&gt;

&lt;p&gt;在制定目标前，我并没有审视自己在人生各领域的进展，而一味的选择自己认为现在重要的方向去突破。&lt;/p&gt;

&lt;p&gt;这让我的工作和生活，在原来的基础上更加偏离轨道，向着一个更极端的方向驶去。也同时导致了我对世界和周围事物的理解更加偏执。&lt;/p&gt;

&lt;h3 id=&quot;总的来说我并没有仔细认真的对待目标管理的每个细节而是粗糙的将它理解为定目标执行目标完成目标的简单过程导致目标执行时很痛苦最终也难以完成的结果&quot;&gt;总的来说，我并没有仔细认真的对待目标管理的每个细节，而是粗糙的将它理解为，定目标，执行目标，完成目标的简单过程，导致目标执行时很痛苦，最终也难以完成的结果。&lt;/h3&gt;

&lt;p&gt;没关系，这只是第一周的学习，失败也是常有的，失败给我警示，总结复盘带给我进步。后面我会，调整目标，改善行动策略，设定奖励，写计划，拆分目标等等，待我下回一一跟大家分享我学习的过程。&lt;/p&gt;

&lt;p&gt;也为了更加完善我的目标管理练习，接下去我也会加入目标执行的统计日志，记录每次执行目标时的原因、行动、结果，用数据统计和数据分析的方式来改善我执行目标的方式。&lt;/p&gt;

&lt;h3 id=&quot;仍然在学习中希望我的分享能给大家一些启示&quot;&gt;仍然在学习中，希望我的分享能给大家一些启示。&lt;/h3&gt;
</content>
 </entry>
 
 <entry>
   <title>给女儿写信(九) 关于定目标</title>
   <link href="http://www.luzexi.com/2021/02/27/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A19"/>
   <updated>2021-02-27T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/02/27/给女儿的信9</id>
   <content type="html">
&lt;hr /&gt;

&lt;p&gt;导读：喜欢跟孩子们一起，可惜一直在外地工作，因此我给孩子写点信并录成视频，跟孩子聊聊自己的故事以及最近的感受&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Sharon，Anne，爸爸好想你们。还没过1周时间，爸爸就忍不住要想你们了，爸爸开始数剩下还有多少日子才能见到你们了。&lt;/p&gt;

&lt;p&gt;爸爸给你们讲讲最近爸爸的工作和生活。&lt;/p&gt;

&lt;p&gt;在新的一年里，爸爸想给自己在新的一年里有一个明确目标，这样爸爸学习和工作就会有方向。&lt;/p&gt;

&lt;p&gt;于是爸爸给自己定了几个目标，希望自己在新的一年里能完成它们。&lt;/p&gt;

&lt;p&gt;它们有，早起锻炼，看完某些爸爸想看但又一直没时间看的书，学习某个爸爸工作上的技能，总结爸爸学过的知识，等等等。&lt;/p&gt;

&lt;p&gt;在定目标的时候，爸爸看了本书，书中教我们如何定目标。&lt;/p&gt;

&lt;p&gt;原来定目标也有方法，你说搞笑不搞笑。&lt;/p&gt;

&lt;p&gt;爸爸还真的学到了不少东西，比如，它说，&lt;/p&gt;

&lt;p&gt;这个目标必须是具体的，不能含糊不清，比如锻炼就不行，必须是锻炼多少分钟，才是具体的。
并且这个目标必须是有期限的，不能无限期延迟，比如以后在学习就不行，必须是这周每天学习半小时，才是有期限的。
最后这个目标必须是自己经过努力后能够完成的，不能完不成，而且必须是经过自己努力后才能完成的目标，比如爸爸每天只能学习半小时，但爸爸努力下每天可以学习1个小时，这才是有挑战的目标。&lt;/p&gt;

&lt;p&gt;但是你知道么，爸爸在第一个星期，给自己定了目标后，发现自己定下的目标太多了，完不成。&lt;/p&gt;

&lt;p&gt;哈哈哈，爸爸很失落，心情很难受。&lt;/p&gt;

&lt;p&gt;第一个星期，定目标失败了，没关系，我们还有第二个星期，第三个星期，爸爸可以再定目标，调整目标难度，让自己能够努力一把就能达成的那种，但定目标的时候也要小心，不能定的太低，太低了，难度太小，自己都觉得没意思了。&lt;/p&gt;

&lt;p&gt;你说搞笑不搞笑。&lt;/p&gt;

&lt;p&gt;爸爸一直在努力，Sharon，Anne，小姨妈，妈妈，大家都要一起加油哦。加油~&lt;/p&gt;

&lt;p&gt;喜欢你们，笔心。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(十九) 《十分钟冥想》</title>
   <link href="http://www.luzexi.com/2021/02/21/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B019"/>
   <updated>2021-02-21T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/02/21/读书笔记19</id>
   <content type="html">&lt;h2 id=&quot;背景&quot;&gt;背景：&lt;/h2&gt;

&lt;p&gt;买此书《十分钟冥想》时，是在一个比较特殊的时期，自己的精神状态比较混乱，恰好在某本书中看到推荐此书的评语。
抱着好奇心和神秘感读了此书，刚读前几章就深深体会到此书的精妙。&lt;/p&gt;

&lt;p&gt;于是在后面的几个月中，我逐步践行了书中所描述的冥想技巧，着实很受益，让自己在这个繁杂的社会中获得了一片小小的宁静。&lt;/p&gt;

&lt;h2 id=&quot;疑问&quot;&gt;疑问：&lt;/h2&gt;

&lt;p&gt;整本书大约有25万字，读完并践行后，我把整个过程分为四个部分：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	1. 第一部分，告诉你什么是冥想？为什么要冥想？
	2. 第二部分，告诉你如何去冥想？
	3. 第三部分，告诉你如何将冥想融入生活？
	4. 第四部分，我的冥想体验记录，增进你对冥想的了解
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;结论&quot;&gt;结论：&lt;/h2&gt;

&lt;p&gt;书对我的帮助很大，可以认为这是一本能教会你如何排除外界干扰的书，如果你肯坚定的去练习冥想的话，一切事情都会越来越专注。&lt;/p&gt;

&lt;p&gt;我也从冥想中学习到了如何保持专注，以及如何在平常事物中发现新的趣味（书中称觉醒），同时自己也日复一日的不断练习，希望自己能有一天在杂乱或危机的环境中仍然能保持定力，也希望自己能在发现事物的更多面。&lt;/p&gt;

&lt;p&gt;不只是学习，冥想从此成为了我排除干扰，练习专注的一个方法，我把它当作我日常生活中的一部分，借助它来缓解我的紧张、焦虑和急躁的性格，同时也借助它来帮助我练习心流体验（《心流》一书中所描绘的专注）。
关于心流体验，我将在另一个篇文章中写关于我看了《心流》这本书后，在自己工作、学习、生活中是如何去实践，书中所描绘的心流，如何去整理内心秩序，让自己更加专注于当下的事物，挖掘更多当下事物的乐趣。&lt;/p&gt;

&lt;h2 id=&quot;内容&quot;&gt;内容：&lt;/h2&gt;

&lt;h2 id=&quot;点击查看内容&quot;&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/x3_Xq7-lUh8D2BCf0i9C1Q&quot;&gt;点击查看内容&lt;/a&gt;&lt;/h2&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(五十六) 自信的支撑点</title>
   <link href="http://www.luzexi.com/2021/02/17/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A856"/>
   <updated>2021-02-17T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/02/17/思路探讨56</id>
   <content type="html">&lt;p&gt;这篇聊聊自信的支撑点。&lt;/p&gt;

&lt;p&gt;事情的缘由是，最近看到很多人都会因为xxx而感到自豪。&lt;/p&gt;

&lt;p&gt;这个xxx指某件事，或某个能力，或某个项目，或某个企业。&lt;/p&gt;

&lt;p&gt;于是想到了自己是否会因为某个东西而感到自豪，想了想，我可能会因为自己看了很多书而感到自豪，也可能会因为自己坚持健身很多年而感到自豪，会因为自己在腾讯感到自豪，会因为自己写了很多文章感到自豪。&lt;/p&gt;

&lt;p&gt;每个人都有自信的支撑点，有些人会因为自己有钱而感到自信，有些人则会因为自己有学识而感到自信，有些人则是强壮的身体，有些人则是美貌，有些人则是说话表达技巧。&lt;/p&gt;

&lt;p&gt;除了自身的因素外，他人也会成为支撑自己信心的点，比如有亲人中的某位有权有势的人会支撑我们的自信，也有可能是工作在世界500强的这个企业支撑我们的自信，也有可能是自己的老师或朋友中有才或者有名支撑我们的自信。&lt;/p&gt;

&lt;p&gt;无论如何，自己身上的自信支撑点，比依赖他人的支撑点更加牢固。对自己有信心比对别人有信心更重要。因为他人因素是不由我们控制，我们所依赖的某亲戚、某公司、某朋友、某个政策，发生变化时，我们就会失去依赖或支撑。&lt;/p&gt;

&lt;p&gt;而自身因素上，钱、设备工具、荣誉名声、美貌，又是自身因素中的外在因素，它会随着时间的流逝而不断变化，虽然有些可控性，但不足以完全掌控。&lt;/p&gt;

&lt;p&gt;而自身内在因素，学识、身体、思维方式和习惯，则可以被我们完全掌控，并且随着日积月累，越来越多，越来越强。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;当然我们不能只关注内在因素，外在因素也是需要我们关注和维护的内容，只是没有内在因素那么重要，但也不可忽视。&lt;/p&gt;

&lt;p&gt;正好我半年前看过一本书《自信的力量》，他讲的很不错。&lt;/p&gt;

&lt;p&gt;他说，一个人的自信，首先是由自己与周围环境的和谐程度决定的，再由自己与周围朋友合作默契程度决定，其次和自己的高深的学识、强壮的身体息息相关，最后才和金钱、公司、职位挂钩。&lt;/p&gt;

&lt;p&gt;想来确实如此，自信心本就是他人给予的，它与周围的人和事息息相关，由自身素质从内而外散发出来，最后由其他外在因素景上添花。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>改造jekyll 为iframe框架的博客内容</title>
   <link href="http://www.luzexi.com/2021/02/10/%E6%94%B9%E9%80%A0jekyll%E4%B8%BAiframe%E6%A1%86%E6%9E%B6%E7%9A%84%E5%8D%9A%E5%AE%A2%E5%86%85%E5%AE%B9"/>
   <updated>2021-02-10T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/02/10/改造jekyll为iframe框架的博客内容</id>
   <content type="html">
&lt;div class=&quot;static_post&quot;&gt;
&lt;iframe src=&quot;/static-page/post-2021-02-10/改造jekyll 为iframe框架的博客内容.html&quot; width=&quot;100%&quot; height=&quot;100%&quot;&gt; &lt;/iframe&gt;
&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(五十五) 第一性原理</title>
   <link href="http://www.luzexi.com/2021/02/09/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A855"/>
   <updated>2021-02-09T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/02/09/思路探讨55</id>
   <content type="html">&lt;p&gt;最近越来越经常写文章但很少发出来（基本都是每周一篇总结 + 每日一篇自我反省的文章），没能分享到博客的原因第一是写的文章有些是属于公司保密级的，不能发布在博客上，甚至有几篇文章完全不能脱敏。第二是自己写的向内看系列的内容，真的越来越向内看越来越私密，由于太私密触及内心的兴奋与失落，起伏过大所以也不好拿出来跟大家分享。&lt;/p&gt;

&lt;p&gt;​在公众号中，我秉持着开放的态度与自己对话，也常与朋友和同事们聊聊自己的想法。
希望能从不同的角度来看待事物，也希望能从各个方向上来改善自己做人做事的态度。&lt;/p&gt;

&lt;p&gt;由于年底事情特别多，也特别繁杂，因此只能将有限的精力用在照顾自己的工作和学习进度。在此跟大家说声抱歉。
2021年会继续跟大家分享我的所思所想，以及公开自己的所学所为。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;前面听过第一性原理的概念，但没有了解的很清楚，在放假回老家最后一天突然兴致勃勃的看了，张善友教授讲的第一性原理的视频。&lt;/p&gt;

&lt;p&gt;感觉讲的很不错，也契合我一直以来秉持的理念，在听了他所讲的内容后，对这块理念更加深刻。&lt;/p&gt;

&lt;p&gt;什么是第一性原理？
第一性原理，事物背后的不变的理念(原理)，根据这个原理可以推出所有的其他东西出来。这个理念就叫第一性原理。&lt;/p&gt;

&lt;p&gt;第一性原理是事物的本质、本性，是事物背后支配性的理念，而不是现象层面流变的物质。一旦在逻辑上导通一个共同的抽象概念，与此相关的所有具象问题，立即全部化解。&lt;/p&gt;

&lt;p&gt;随着我们年龄增大，接触的事情越来越复杂，处理这件复杂的事物需要花费的精力也通常越来越复杂。
第一性原理告诉我们，越高深的事物，底层理念越简约，其原理通常具有简一性，即简约和单纯。
找到第一性原理，就像是找到了支配事物运动的发动机。&lt;/p&gt;

&lt;p&gt;计算机程序执行原理、操作系统内核原理，对于每个编程的人来说都是第一性原理。为技术人拓展更扎实的编程思路提供了底层支撑。
抽象能力是软件架构设计的第一性原理。为技术人拓展更简洁实用的架构能力提供了底层支撑。
尊严与诚信是人在社会生存是第一性原理。为社会人拓展生活和工作中的方方面面提供了支撑。
能量守恒是宇宙的第一性原理。为物理、化学的研究工作提供了支撑。等等等&lt;/p&gt;

&lt;p&gt;人们常说，道理说的很好，但听过就忘了。显然大多数人都没有将人生的底层原理铭刻在心中，时刻忘记遵守事物的本质规律，也时常不得不在所谓的现实面前妥协。&lt;/p&gt;

&lt;p&gt;你找到生活中的真正的第一性原理了吗？
当第一性原理（事物的本质）和现象冲突的时候，你选择了什么？&lt;/p&gt;

&lt;p&gt;每个你的选择都决定了你今后的道路，你是坚持本质还是妥协现象，N个选择积累后，最终成为了今天的你。&lt;/p&gt;

&lt;p&gt;当然我们也需要注意下，第一性原理不是绝对真理，虽然事物的本质和原理适用于每个人，但对于每个人来说都需要有不同方式去执行。​&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>向内看自己(十二) 精力训练</title>
   <link href="http://www.luzexi.com/2021/01/24/%E5%90%91%E5%86%85%E7%9C%8B%E8%87%AA%E5%B7%B112"/>
   <updated>2021-01-24T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/01/24/向内看自己12</id>
   <content type="html">&lt;p&gt;健身一整年的体会与变化，精力训练的一整年。&lt;/p&gt;

&lt;p&gt;体会 + 对策 + 图 + 视频&lt;/p&gt;

&lt;h2 id=&quot;点击查看内容&quot;&gt;&lt;a href=&quot;https://mp.weixin.qq.com/s/PkEWsEOxpRmdIm8AdV2OPA&quot;&gt;点击查看内容&lt;/a&gt;&lt;/h2&gt;

</content>
 </entry>
 
 <entry>
   <title>向内看自己(十一) 底层能力和核心</title>
   <link href="http://www.luzexi.com/2021/01/23/%E5%90%91%E5%86%85%E7%9C%8B%E8%87%AA%E5%B7%B111"/>
   <updated>2021-01-23T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/01/23/向内看自己11</id>
   <content type="html">&lt;p&gt;最近一直在学习技术，由于几篇文章是跟项目有关的所以得脱敏后才能发。由于技术聊的太多，感觉自己脑袋闭塞了，这篇我想聊聊别的，可能有点乱。&lt;/p&gt;

&lt;p&gt;最近有点苦闷。&lt;/p&gt;

&lt;p&gt;又到了，阶段性怀疑人生的时刻。&lt;/p&gt;

&lt;p&gt;工作上的、生活上的、学习上的都有一些。&lt;/p&gt;

&lt;p&gt;最近接触了些自己以前没有接触过的东西，包括，冥想，专注练习，分析和整理的方法，这些可以说是以前没有在意的东西。&lt;/p&gt;

&lt;p&gt;在我看了《十分钟冥想》、《心流》、《金字塔原理》这三本书后，发现书中的这些方法在我们生活中特别重要。&lt;/p&gt;

&lt;p&gt;于是我开始学习冥想、学习如何保持专注、学习整理和分析知识和经验的方法，但收效甚微。&lt;/p&gt;

&lt;p&gt;由于开始注重自己的心境、自己的专注力、自己的分析方法，发现自己在这方面能力值好低，瓶颈好大，思路狭窄。&lt;/p&gt;

&lt;p&gt;以前并没有这么觉得，因为我没有注重这些技巧的运用，都是猛打猛冲的在向前进发。&lt;/p&gt;

&lt;p&gt;看了大师们的解说，再自己去试行，才发现好难。&lt;/p&gt;

&lt;p&gt;所以挫败感很强烈。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;做这些目的是想把自己的底层能力巩固一下，没想到的是原来自己的底层能力这么差。&lt;/p&gt;

&lt;p&gt;为什么要去打造底层能力这个问题，也是我从健身中感悟到。&lt;/p&gt;

&lt;p&gt;在一开始健身的时候，会无脑的去跑步、去跳操，一段时间后接触了固定器械健身，以及固定肌肉部位的健身，觉得这样的效率会更高，于是开始了固定肌肉和器械的健身方式，过了一段时间，但这导致了我的核心没有被训练到，肌肉是上来了，但核心没有变强，腿部、腹部、身体的整个平衡能力，耐力，都没有变强。&lt;/p&gt;

&lt;p&gt;只是改善了外观，增加了肌肉，降低了体脂，但核心体魄上完全没有变化，甚至变的更弱了。&lt;/p&gt;

&lt;p&gt;没有抓住核心导致我虽然外观改善了，但内在却没有改善。&lt;/p&gt;

&lt;p&gt;这也是我为什么要执著于打造底层能力的原因。&lt;/p&gt;

&lt;p&gt;万事万物都要是一个道理，万事万物都要是一个道理，要抓住核心。&lt;/p&gt;

&lt;p&gt;写程序、做游戏的道理也是一样，做游戏要先做核心战斗，有了核心战斗，周边的功能开发才有意义。
写程序也是一个道理，理解底层原理，熟悉底层机制，去实践底层逻辑，核心的组件做出来，围绕核心做业务才会更稳，否则就像豆腐渣工程很容易倒塌，底层不牢固。（其实每个模块有核心，先做核心，再做周边是比较好的顺序）&lt;/p&gt;

&lt;p&gt;延展到生活中也是如此，核心我认为除了工作外的生活中核心是家庭，我们应该把重心放在家庭上，而家庭中，成员之间的的默契程度是核心，因此我们要团结友爱，互帮互助，分工合作来努力让这个核心更加稳固。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;这个世界证明了未来总是不可预测的，历史总是固定并有理有据的。&lt;/p&gt;

&lt;p&gt;我们常去预测未来，认为根据历史经验就能知道未来，认为至少知道个大概。&lt;/p&gt;

&lt;p&gt;不要去预测未来，我们大部分人都不肯接受不确定性，有些人希望能控制未来，那更是不可能。&lt;/p&gt;

&lt;p&gt;我们能做只能是尝试去稳定未来的发展方向，至于未来到底会发生什么，我们完全控制不了。&lt;/p&gt;

&lt;p&gt;我们能控制的只有自己，做好自己该做的事情，不断完善自己，完善底层能力，再在自己能力范围内尽可能照顾好周围的人。&lt;/p&gt;

&lt;p&gt;至于那些我们不能控制的未来，永远都会让我们焦虑，并借此扰乱我们的心智。&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Perfetto内存工具分析</title>
   <link href="http://www.luzexi.com/2021/01/20/Perfetto%E5%86%85%E5%AD%98%E5%B7%A5%E5%85%B7%E5%88%86%E6%9E%90"/>
   <updated>2021-01-20T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/01/20/Perfetto内存工具分析</id>
   <content type="html">&lt;p&gt;用Markdown在贴图方面不是很方便，于是用印象笔记写了篇文章，尝试一下新的方式。&lt;/p&gt;

&lt;p&gt;我越来越觉得图对理解的重要性，所以想把更多文字转为以图文结合的方式表达，这样会更加清晰一些，也更易于理解。&lt;/p&gt;

&lt;p&gt;后面会更多的尝试用这样方式来写文章，即，用印象笔记写完后，导出html放在博客上。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;/static-page/Perfetto/Index/Perfetto内存工具分析.html&quot;&gt;点击查看文章内容&lt;/a&gt;&lt;/p&gt;

&lt;div class=&quot;static_post&quot;&gt;
&lt;iframe src=&quot;/static-page/Perfetto/Index/Perfetto内存工具分析.html&quot; width=&quot;100%&quot; height=&quot;100%&quot;&gt; &lt;/iframe&gt;
&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>给女儿写信(八) 关于生命与运动</title>
   <link href="http://www.luzexi.com/2021/01/14/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A18"/>
   <updated>2021-01-14T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2021/01/14/给女儿的信8</id>
   <content type="html">
&lt;hr /&gt;

&lt;p&gt;导读：喜欢跟孩子们一起，可惜一直在外地工作，因此我给孩子写点信并录成视频，跟孩子聊聊自己的故事以及最近的感受&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Sharon、Anne，几天不见爸爸又想你们了。&lt;/p&gt;

&lt;p&gt;Anne身体好些了么，妈妈跟我说你能吃能玩了，爸爸好开心。生病好难受，所以我们每天都要坚持运动哦。&lt;/p&gt;

&lt;p&gt;提高身体的免疫力很重要哎，病毒到我们身体里时就会有免疫细胞出来杀死它们。&lt;/p&gt;

&lt;p&gt;爸爸和妈妈每天都抽空去健身房运动，或者在家里做运动。&lt;/p&gt;

&lt;p&gt;Sharon是不是也生病了，病毒传染起来很快，也看不见摸不着，我们要当心哦。&lt;/p&gt;

&lt;p&gt;生病会很难受，要听妈妈的话，按时吃药，有身体哪里不舒服及时跟妈妈说，妈妈能想到办法治好你的病的。&lt;/p&gt;

&lt;p&gt;我们一起加强运动哈，爸爸和妈妈都喜欢跑步、游泳、做操、练哑铃，我们全家人都一起每天运动好吗。&lt;/p&gt;

&lt;p&gt;身体棒棒的才有精力和活力，每天满满的活力多好，学习也会很专注。加油Sharon，加油Anne。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(十八) 《C++性能优化指南》三</title>
   <link href="http://www.luzexi.com/2020/12/27/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B018"/>
   <updated>2020-12-27T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/12/27/读书笔记18</id>
   <content type="html">&lt;p&gt;此篇主要介绍热点代码、I/O、以及并行部分的优化，我们会从原理出发来，再根据原理讲优化，这样即学习了原理，又知道了优化的来龙去脉。&lt;/p&gt;

&lt;h2 id=&quot;热点代码&quot;&gt;热点代码&lt;/h2&gt;

&lt;p&gt;这里作者带我们聊一聊关于代码细节的优化，虽然语句的细节优化并不能带来非常明显的提升，但是也是非常有必要的优化步骤，尤其在那些追求极致高性能或精小的组件中，代码细节的优化决定了组件与组件之间的差异。&lt;/p&gt;

&lt;p&gt;语句细节的优化，其实质是对CPU指令的优化，可以认为是从执行指令流中移除指令的过程。下面先来阐述一下细节优化的原理。&lt;/p&gt;

&lt;h3 id=&quot;语句的细节优化其实质是执行指令数量的优化指令跳转次数的优化向栈中保存临时寄存器次数的优化以及内存分配次数的优化&quot;&gt;语句的细节优化，其实质是执行指令数量的优化，指令跳转次数的优化，向栈中保存临时寄存器次数的优化，以及内存分配次数的优化。&lt;/h3&gt;

&lt;p&gt;执行指令数量减少了可以减少CPU在执行程序时的耗时我们很好理解，指令跳转则是因为指令也是被放在内存中的数据，因此它也会被高速缓存cache，长距离跳转会让高速缓存失效，静态函数调用和非成员函数调用通常都是长距离指令跳转的典型案例。&lt;/p&gt;

&lt;p&gt;函数调用开销不可忽视，即使一个空函数，在调用时也会有性能开销（编译器可能会帮我们优化掉空函数），有时为了极致的优化，我们应该最大限度的减少调用函数的频率，特别是频率最高的top3。&lt;/p&gt;

&lt;p&gt;因为在函数被调用时会保存当前函数的数据，包括参数、局部变量、当前指令地址、临时寄存器和标记寄存器等，每次调用一个函数会做如下处理：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	1.当调用函数时，先保存当前函数的临时变量、参数、临时寄存器、标记寄存器。
	
	2.将这些每个要保存的数据都复制到栈中。
	
	3.当前执行的地址复制到栈中。

	4.将指令指针寄存器IP指向要执行的函数体的第一句

	5.执行函数体中的指令

	6.将函数调用结果保存到寄存器

	7.从栈中推出要返回的地址，并复制给指令寄存器IP

	8.推出栈中的临时寄存器、参数、局部变量、标记寄存器都重新还原回去

	9.继续执行剩下的指令直到遇到下一个函数。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;如果遇到成员函数是虚函数的，还得先从虚表中偏移并取出函数地址再调用，这里又多了2次计算，即先取出虚表地址、再根据虚表地址偏移获得真正的函数地址、最后再才能跳转过去。如果是多重继承、或者是多重继承的继承类中的虚函数成员，则需要再加一次地址偏移计算。&lt;/p&gt;

&lt;p&gt;inline内联是减少函数调用的最佳方式，内联函数并不像一般函数那样会保存数据并且跳转指令，因为编译器会就地展开内联函数中的指令，因此没有推栈入栈保存数据到栈和跳转指令到函数再返回的步骤，取而代之的是就地直接执行指令。&lt;/p&gt;

&lt;p&gt;这样看来减少函数调用(或让函数内联)的同时也减少了入栈、出栈、复制数据的指令数量，也减少了指令跳转的丢失高速缓存的概率。&lt;/p&gt;

&lt;p&gt;不必要的内存分配也是在代码细节中常犯的错误，尤其指向堆内存分配，当函数中需要某个容器或者类实例时，常会临时向堆内存申请一次以用来计算。&lt;/p&gt;

&lt;p&gt;我们来看看以上这说的7个细节的具体例子：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;
for(int i = 0 ; i&amp;lt;strlen(str) ; i++)
	...

//改为

for(int i = 0, n = strlen(str) ; i&amp;lt;n ; i++)
	...

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;1.将重复计算提到前面，但这里不一定有优化，因为编译器可能会识别这类循环并将实时计算移出去，不过不能保证编译器一定会这么干，所以我们最好做人为的优化，保证不重复计算。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;
void function()
{
	list = new list();
	for(int i = 0 ; i&amp;lt;n ; i++)
	{
		list.Add(xx);
	}

	return;
}

//改为

public static list = new list(); //改为全局变量

void function()
{
	list.Clear();
	for(int i = 0 ; i&amp;lt;n ; i++)
	{
		list.Add(xx);
	}

	return;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;2.函数中临时的堆内存分配，改为全局的共用内存，只要分配一次，每次使用前先清理就能节省开销。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;
int k = 0;
for(int i = 0 ; i&amp;lt;100 ; i++)
{
	int j = sin(100) + cos(50);
	k = j*i;
}

//改为

int k = 0;
int j = sin(100) + cos(5);
for(int i = 0 ; i&amp;lt;100 ; i++)
{
	k = j*i;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;3.移除循环中不变的计算，减少不必要的指令，可能会被编译器优化掉。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;for(int i = 0 ; i&amp;lt;10 ; i++)
{
	int b = Add(3,5);
	...
}

//改为

int b = 0
for(int i = 0 ; i&amp;lt;10 ; i++)
{
	b = 3 + 5;
	...
}

//或者将Add函数内联

inline int Add(a,b)
{
	...
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;4.用内联或者手动内联的方式，减少循环中的函数调用开销。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;
string str = &quot;a&quot;;

str = &quot;&amp;lt;p&amp;gt;&quot; + str + &quot;&amp;lt;/p&amp;gt;&quot;;

//改为

str = string.format(&quot;&amp;lt;p&amp;gt;{0}&amp;lt;/p&amp;gt;&quot;,str);

//或者

str = StringCacheMgr.instance.Format(&quot;&amp;lt;p&amp;gt;{0}&amp;lt;/p&amp;gt;&quot;,str);

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;5.减少字符串内存分配次数，将原来要分配两次的字符串，改为只要分配1次。或者使用自制的字符串内存管理方式管理分配和操作字符串(前面的内存优化那一节我们讲过这种方式)&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;
for(int i = 0 ; i&amp;lt;1000 ; i++)
{
	test();
}

//改为

test();

void test()
{
	for(int i = 0 ; i&amp;lt;1000 ;i++)
	{
		...
	}
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;6.将1000次调用函数的开销改为1次，节省函数调用的开销。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;
if(x == a)
{
	...
}
else if(xx == a)
{
	...
}
else if(xxx == a)
{
	...
}
else ...

//改为

switch(a)
{
	case x:
		break;
	case xx:
		break;
	case xxx:
		break;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;7.通常switch都会被编译器优化为索引的方式去跳转，因此用switch比if效率高的多，也不用我们自己对数据排序。&lt;/p&gt;

&lt;p&gt;最后说下异常try catch的开销，在早期C++的try catch机制会在栈帧上包含一个异常上下文，这些上下文会随着异常抛出或者作用范围结束而销毁，增加性能开销。现在则不同了，上下文开销都没有了，只是会在开始try时多几个指令更改中断程序的指向，退出时再改回来，但是即使这样，我们也不能有太多try catch，因为try catch太多仍然会多出很多额外的指令消耗。&lt;/p&gt;

&lt;h3 id=&quot;我们做个小结我们说语句的细节优化实质是减少执行指令数量减少指令跳转次数减少函数调用以及减少内存分配次数我们用代码细节来解释有哪些细节是可以遵从我们的原理来优化的这些细节的优化在性能要求比较高的组件上会比较有用好的代码细节是业务逻辑优化的前提业务逻辑上当我们更多运用的是调整实现方式调整数据结构的方式调整业务逻辑策略的方式时这些细节的优化则成了底层的支柱&quot;&gt;我们做个小结，我们说语句的细节优化，实质是减少执行指令数量，减少指令跳转次数，减少函数调用，以及减少内存分配次数。我们用代码细节来解释有哪些细节是可以遵从我们的原理来优化的，这些细节的优化，在性能要求比较高的组件上会比较有用，好的代码细节是业务逻辑优化的前提。业务逻辑上，当我们更多运用的是调整实现方式，调整数据结构的方式，调整业务逻辑策略的方式时，这些细节的优化则成了底层的支柱。&lt;/h3&gt;

&lt;h2 id=&quot;io-文件操作&quot;&gt;I/O 文件操作&lt;/h2&gt;

&lt;h3 id=&quot;io操作的优化空间相对比较小是因为它主要的工作大部分由操作系统完成因此我们先介绍下操作系统中io的读写原理再从原理出发优化io操作效率包括降低读写次数减少读取时间和优化体验俩个方面&quot;&gt;I/O操作的优化空间相对比较小，是因为它主要的工作大部分由操作系统完成。因此我们先介绍下操作系统中I/O的读写原理，再从原理出发优化I/O操作效率，包括降低读写次数减少读取时间和优化体验俩个方面。&lt;/h3&gt;

&lt;p&gt;操作系统中为了分割操作内容让调用更安全，分为‘用户态’和‘内核态’，当用户态需要内核态工作时我们称为‘系统调用’，我们平常写的程序只要不涉及系统调用的都是用户态完成的，一旦涉及到需要操作系统工作的部分，就要先切到内核态完成工作，结束后再切回用户态继续执行后面的程序，这个切换的过程是比较费时费力的，I/O调用就是其中一种需要切换内核态的系统调用。&lt;/p&gt;

&lt;p&gt;操作系统可以支持多种底层不同的文件系统（比如NTFS, FAT, ext3, ext4），为了给内核和用户进程提供统一的文件系统视图，Linux在用户进程和底层文件系统之间加入了一个抽象层，即虚拟文件系统(Virtual File System, VFS)，进程所有的文件操作都通过VFS，由VFS来适配各种底层不同的文件系统，完成实际的文件操作。&lt;/p&gt;

&lt;h3 id=&quot;这里我们来了解下虚拟文件系统构成和主要模块&quot;&gt;这里我们来了解下虚拟文件系统构成和主要模块：&lt;/h3&gt;

&lt;h3 id=&quot;超级块super_block&quot;&gt;超级块（super_block）&lt;/h3&gt;

&lt;p&gt;用于保存一个文件系统的所有元数据，相当于这个文件系统的信息库，为其他的模块提供信息。&lt;/p&gt;

&lt;p&gt;因此一个超级块可代表一个文件系统，文件系统的任意元数据修改都要通过超级块修改，超级块对象是常驻内存并被缓存起来的。&lt;/p&gt;

&lt;h3 id=&quot;目录项模块&quot;&gt;目录项模块&lt;/h3&gt;

&lt;p&gt;是管理路径的目录项，比如一个路径 /home/foo/hello.txt，那么目录项有home, foo, hello.txt三个。&lt;/p&gt;

&lt;p&gt;每个目录项的块，存储的是这个目录下的所有的文件的inode号和文件名等信息。其内部是树形结构，操作系统检索一个文件是从根目录开始，按层次解析路径中的所有目录，直到定位到具体文件。&lt;/p&gt;

&lt;h3 id=&quot;inode模块&quot;&gt;inode模块&lt;/h3&gt;

&lt;p&gt;管理的是一个具体的文件，是文件的唯一标识，一个文件对应一个inode。&lt;/p&gt;

&lt;p&gt;通过inode可以方便的找到文件在磁盘扇区的位置，同时inode模块可链接到address_space模块，方便查找自身文件数据是否已经被缓存在内存中。&lt;/p&gt;

&lt;h3 id=&quot;打开文件列表模块&quot;&gt;打开文件列表模块&lt;/h3&gt;

&lt;p&gt;包含所有内核已经打开的文件，已经打开的文件对象由open系统调用在内核中创建，也叫文件句柄。&lt;/p&gt;

&lt;p&gt;打开文件列表模块中包含一个列表，列表表项是一个结构体struct file，结构体中的信息存储了打开的一个文件的各种状态参数。&lt;/p&gt;

&lt;h3 id=&quot;file_operations模块&quot;&gt;file_operations模块&lt;/h3&gt;

&lt;p&gt;模块中维护一个数据结构，是一系列函数指针的集合，其中包含所有可以使用的系统调用函数，例如open、read、write、mmap等。&lt;/p&gt;

&lt;p&gt;每个打开文件（打开文件列表模块的一个表项）都可以连接到file_operations模块，从而对任何已打开的文件，通过系统调用函数，实现各种操作。&lt;/p&gt;

&lt;h3 id=&quot;address_space模块&quot;&gt;address_space模块&lt;/h3&gt;

&lt;p&gt;记录了文件在页缓存中已经缓存了的物理页信息，是页缓存和外部设备中文件系统的桥梁。如果将文件系统可以理解成数据源，内存中的页缓存是已经读取的内容，那么address_space可以说是内存系统和文件系统的中间层。&lt;/p&gt;

&lt;h3 id=&quot;所有文件信息保存在超级块中通过目录项模块找到文件所在位置所有被打开的文件放在文件列表模块中file_operations模块负责操作文件每个文件都有唯一标识inode磁盘数据与内存缓存通过address_space联接与映射&quot;&gt;所有文件信息保存在超级块中，通过目录项模块找到文件所在位置，所有被打开的文件放在文件列表模块中，file_operations模块负责操作文件，每个文件都有唯一标识inode，磁盘数据与内存缓存通过address_space联接与映射。&lt;/h3&gt;

&lt;h3 id=&quot;下面我们来看看读写入文件时的基本流程&quot;&gt;下面我们来看看读写入文件时的基本流程：&lt;/h3&gt;

&lt;h3 id=&quot;读文件&quot;&gt;读文件&lt;/h3&gt;

&lt;p&gt;1、进程调用库函数向内核发起读文件请求；&lt;/p&gt;

&lt;p&gt;2、内核通过检查进程的文件描述符定位到虚拟文件系统的已打开文件列表表项；&lt;/p&gt;

&lt;p&gt;3、调用该文件可用的系统调用函数read()&lt;/p&gt;

&lt;p&gt;read()函数通过文件表项链接到目录项模块，根据传入的文件路径，在目录项模块中检索，找到该文件的inode；&lt;/p&gt;

&lt;p&gt;4、在inode中，通过文件内容偏移量计算出要读取的页；&lt;/p&gt;

&lt;p&gt;5、通过inode找到文件对应的address_space；&lt;/p&gt;

&lt;p&gt;6、在address_space中访问该文件的页缓存树，查找对应的页缓存结点：&lt;/p&gt;

&lt;p&gt;（1）如果页缓存命中，那么直接返回文件内容；&lt;/p&gt;

&lt;p&gt;（2）如果页缓存缺失，那么产生一个页缺失异常，创建一个页缓存页，同时通过inode找到文件该页的磁盘地址，读取相应的页填充该缓存页；重新进行第6步查找页缓存；&lt;/p&gt;

&lt;p&gt;7、文件内容读取成功。&lt;/p&gt;

&lt;h3 id=&quot;写文件&quot;&gt;写文件&lt;/h3&gt;

&lt;p&gt;前5步和读文件一致，在address_space中查询对应页的页缓存是否存在：&lt;/p&gt;

&lt;p&gt;6、如果页缓存命中，直接把文件内容修改更新在页缓存的页中。写文件就结束了。这时候文件修改位于页缓存，并没有写回到磁盘文件中去。&lt;/p&gt;

&lt;p&gt;7、如果页缓存缺失，那么产生一个页缺失异常，创建一个页缓存页，同时通过inode找到文件该页的磁盘地址，读取相应的页填充该缓存页。此时缓存页命中，进行第6步。&lt;/p&gt;

&lt;p&gt;8、一个页缓存中的页如果被修改，那么会被标记成脏页。脏页需要写回到磁盘中的文件块。有两种方式可以把脏页写回磁盘：&lt;/p&gt;

&lt;p&gt;（1）手动调用sync()或者fsync()系统调用把脏页写回&lt;/p&gt;

&lt;p&gt;（2）pdflush进程会定时把脏页写回到磁盘&lt;/p&gt;

&lt;p&gt;同时注意，脏页不能被置换出内存，如果脏页正在被写回，那么会被设置写回标记，这时候该页就被上锁，其他写请求被阻塞直到锁释放。&lt;/p&gt;

&lt;h3 id=&quot;页缓存实际上就是一个基数树结构它将一个文件的内容组织起来存放在struct-page结构中文件越大树形结构越庞大每一页都记录着文件内容的页信息和缓存信息&quot;&gt;页缓存实际上就是一个基数树结构，它将一个文件的内容组织起来存放在struct page结构中，文件越大树形结构越庞大，每一页都记录着文件内容的页信息和缓存信息。&lt;/h3&gt;

&lt;p&gt;另外内核使用task_struct来表示单个进程的描述符，其中包含维护一个进程的所有信息。task_struct结构体中维护了一个 files的指针（和“已打开文件列表”上的表项是不同的指针）来指向结构体files_struct，files_struct中包含文件描述符表和打开的文件对象信息，这使得系统能够：&lt;/p&gt;

&lt;p&gt;1、多个进程可以同时指向一个打开文件对象（文件列表表项）。&lt;/p&gt;

&lt;p&gt;2、一个进程可以多次打开一个文件，生成不同的文件描述符，每个文件描述符指向不同的文件列表表项。但是由于是同一个文件，inode唯一，所以这些文件列表表项都指向同一个inode。&lt;/p&gt;

&lt;h3 id=&quot;我们知道了文件的读写原理再来看看我们在平时编写文件操作时的优化思路&quot;&gt;我们知道了文件的读写原理再来看看我们在平时编写文件操作时的优化思路。&lt;/h3&gt;

&lt;p&gt;1.减少读写次数，减少读写时间。&lt;/p&gt;

&lt;p&gt;由于每次读取文件内容都会从用户态转到内核态，完成后再切回来，这种切换的消耗是比较重的，因此我们应该尽量减少读写次数。&lt;/p&gt;

&lt;p&gt;在读取一个文件时，尽量将需要的内容一次性读取完毕，甚至可以预先读取未来的内容，以避免多次读取。在写文件时也是同样，尽量一次性写入硬盘，避免多次写入。例如下面代码：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;
while(getline(file,line))
{
	doSomeThing(line);
}

//改为

lines = getlines(file);

for(int i = 0 ,n = len(lines) ; i&amp;lt; n ; ++i)
{
	doSomeThing(lines[i]);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;一次性读取所有行再对每行做处理&quot;&gt;一次性读取所有行，再对每行做处理。&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;
void write_lines(std::ostream&amp;amp; file std::string const&amp;amp; lines[])
{
	for(int i = 0,n = len(lines) ; i&amp;lt;n ; ++i)
	{
		file &amp;lt;&amp;lt; lines[i]
		file-&amp;gt;flush();
	}
}

//改为

void write_lines(std::ostream&amp;amp; file std::string const&amp;amp; lines[])
{
	for(int i = 0,n = len(lines) ; i&amp;lt;n ; ++i)
	{
		file &amp;lt;&amp;lt; lines[i];
	}
	file-&amp;gt;flush();
}

//或用内存池的方式改为

void write_lines(std::ostream&amp;amp; file std::string const&amp;amp; lines[])
{
	int str_size = 0;
	for(int i = 0,n = len(lines) ; i&amp;lt;n ; ++i)
	{
		str_size += len(lines[i] + 1)*sizeof(char);
	}
	
	byte[] data = MemoryPool.instance.AllocMemory(str_size);

	CombineStringData(lines, data); //将lines中的数据都拷贝到data中

	file-&amp;gt;write(data);
	file-&amp;gt;flush();

	MemoryPool.instance.Free(data);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;每次写入文件时系统并不会立即写入文件而是存放在页缓存如果我们每次刷新则会每次都同步到硬盘写入硬盘速度比较内存慢很多会消耗比较长时间所以要减少刷新调用次数&quot;&gt;每次写入文件时系统并不会立即写入文件，而是存放在页缓存，如果我们每次刷新，则会每次都同步到硬盘，写入硬盘速度比较内存慢很多，会消耗比较长时间，所以要减少刷新调用次数。&lt;/h3&gt;

&lt;h3 id=&quot;只是减少刷新次数但依然减少不了内核态切换的次数于是我们用内存池的方式减少内存分配的时间将内存分配的耗时降低将原本要调用很多次系统调用的次数降低为了一次从而减少了内核态切换的次数&quot;&gt;只是减少刷新次数，但依然减少不了内核态切换的次数。于是我们用内存池的方式减少内存分配的时间，将内存分配的耗时降低，将原本要调用很多次系统调用的次数降低为了一次，从而减少了内核态切换的次数。&lt;/h3&gt;

&lt;p&gt;2.优化体验&lt;/p&gt;

&lt;p&gt;用阻塞读取的方式，由于线程要等待磁盘设备的工作，对于整个程序的效率来说是比较低的，因为硬盘设备读写的速度比较慢，主线程要等待硬盘设备工作完毕后才进行后面的工作。因此为了能让程序的整体效率提升，我们可以用异步读取的方式来优化整体的程序时间，即在读取或写入文件的同时，其他程序工作同步进行。&lt;/p&gt;

&lt;p&gt;最常见的是游戏的开机画面，加载画面，切换场景画面，甚至有些游戏中边加载边进行的部分，都是可以通过并行来优化体验的。&lt;/p&gt;

&lt;h3 id=&quot;文件读写并发通常都是开启线程后的读写操作与阻塞内容一样只是更多的利用可利用的cpu时间而不让线程空闲等待硬盘原理是我们尽力能让计算机中的所有设备资源都满负荷运转并很好的协作而不是相互牵制&quot;&gt;文件读写并发通常都是开启线程后的读写操作，与阻塞内容一样，只是更多的利用可利用的CPU时间而不让线程空闲等待硬盘，原理是我们尽力能让计算机中的所有设备资源都满负荷运转并很好的协作，而不是相互牵制。&lt;/h3&gt;

&lt;p&gt;下面我们就来讲讲并发的优化内容。&lt;/p&gt;

&lt;h2 id=&quot;并发&quot;&gt;并发&lt;/h2&gt;

&lt;p&gt;并发的方案有很多特别是在业务层上有很多技巧，这里只是选取与语言相关部分。我们将从原理出发讲一讲，线程同步中的技巧、原子性、以及无锁容器的原理。&lt;/p&gt;

&lt;p&gt;由于设备资源并不总是运行，因为我们的程序并没有使用到这些资源，或者说有时没有同一时间同时让它们一起运转，这使得资源的闲置造成了浪费，如果能在当某个程序在执行指令时，另一个设备在满负荷运作，这样就相当于提高了运行效率。&lt;/p&gt;

&lt;h3 id=&quot;并发的挑战是找到足够多的独立任务来充分地使用所有可用的计算机资源让资源都能满负荷的执行提高整体运行效率其中cpu资源是最稀缺也是使用最频繁的资源如果能让多核中所有cpu都满负荷工作不考虑降频问题程序的效率就能提高很多即使不满负荷也能提其他cpu分担不少工作让降频的概率减少到最低&quot;&gt;并发的挑战是找到足够多的独立任务来充分地使用所有可用的计算机资源，让资源都能满负荷的执行，提高整体运行效率。其中CPU资源是最稀缺，也是使用最频繁的资源，如果能让多核中所有CPU都满负荷工作(不考虑降频问题)程序的效率就能提高很多，即使不满负荷，也能提其他CPU分担不少工作，让降频的概率减少到最低。&lt;/h3&gt;

&lt;p&gt;首先我们来梳理下操作系统中的进程、线程、时间片的概念。&lt;/p&gt;

&lt;p&gt;线程是实际工作的单元，进程只是一个容器用来管理线程。严格来说Linux内核其实不区分进程和线程，内核把执行单元叫做任务(Task)。操作系统实际上调度的是进程，进程通过fork()来创建同样的另一个进程。每个进程有一个PID，同一组进程中最先启动的那个还有一个TGID，严格来说前者应该叫线程ID，后者应该叫进程ID，其实它们都是Linux的Task。&lt;/p&gt;

&lt;p&gt;多线程能同时做好几件事情以提高效率，但实际问题是，CPU的数量（核心数）是有限的，而且并不多。如果你的CPU有8个核，并且整个操作系统中有8个线程的话，不考虑中断等因素，每个线程理论上能一直执行下去。然而多于8个线程以后，操作系统就必须进行调度，也就是分配时间片。具体的分配方案，或者说调度算法有很多种。如果一个进程创建了很多线程的话，最多也只有8个能够处于执行的状态(这里说的是物理线程，有别于逻辑线程)，其余的线程必须等待调度。线程被调度的时候需要进行上下文切换，这个操作是一种额外的开销。当线程数量过多的时候，上下文切换产生的额外开销会对系统的效率造成负面影响。&lt;/p&gt;

&lt;p&gt;线程的调度算法和进程一样通常有优先级之分，优先级高的线程可以比优先级低的线程多抢占些CPU时间片。甚至不同的线程可以通过系统调用将线程绑定在某个CPU核上。因此我们也可以通过将线程绑某个cpu核的方式来强制执行线程调度，从而优化并行开销。&lt;/p&gt;

&lt;h3 id=&quot;线程同步&quot;&gt;线程同步&lt;/h3&gt;

&lt;p&gt;通常我们在多个线程交叉执行时最关心的是同步问题。解决这个问题我们可以用，减少锁的占用时间、减少锁的颗粒度、无锁容器三个方式。前两者更好理解些，也用的比较多，最后一个需要阐明下原理。&lt;/p&gt;

&lt;p&gt;通常我们使用锁和互斥量来解决线程间的同步问题，但这会带来潜在问题，就是由于锁的原因导致线程间的等待时间变长，实际执行的效率可能并没有因此而增加。&lt;/p&gt;

&lt;p&gt;因此锁的范围必须被压缩到最小，例如：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;
void doFunction(item)
{
	lock(obj)
	{
		doSomeThing1();

		list.push(item); //must lock

		doSomeThing2();
	}
}

//改为

void doFunction(item)
{
	doSomeThing1();

	lock(obj)
	{
		list.push(item); //must lock
	}

	doSomeThing2();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;尽可能的缩小锁的范围减少锁等待时间&quot;&gt;尽可能的缩小锁的范围，减少锁等待时间。&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;
void doFunction1()
{
	lock(obj)
	{
		...
	}
}

void doFunction2()
{
	lock(obj)
	{
		...
	}
}

//改为

void doFunction1()
{
	lock(obj1)
	{
		...
	}
}

void doFunction2()
{
	lock(obj2)
	{
		...
	}
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;减少锁的颗粒度让各自的锁只负责自己一小部分的内容这里也涉及到细粒度锁算法它通常基于轻量级原子性原语由于并不是基于系统提供的同步原语所以性能开销很小但在高并发的情况下细粒度锁算法就会成为程序的瓶颈&quot;&gt;减少锁的颗粒度，让各自的锁只负责自己一小部分的内容。这里也涉及到细粒度锁(算法)，它通常基于轻量级原子性原语，由于并不是基于系统提供的同步原语所以性能开销很小，但在高并发的情况下，细粒度锁(算法)就会成为程序的瓶颈。&lt;/h3&gt;

&lt;p&gt;由于指定锁某些局部的计算范围或者函数，锁的时间太长并不划算，所以我们通常在两个线程间的协作上使用消息队列(或者其他容器)让线程更大程度的并行，但这依然需要对容器加锁，以使得操作不冲突。&lt;/p&gt;

&lt;p&gt;为了能让容器冲突时间更小，当只有两个线程操作容器时，我们通常会采用些技巧。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;例如双队列，设置两个队列，其中一个用于处理消息，另一个用于接收消息，当处理消息时，两个队列的头指针交换一下再处理，这样就能让接收的队列继续接收，处理的队列独自处理，这种方式的缩短了锁的占用时间，锁的时间缩短到了变量交换时间。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;以及循环队列的方式，用头尾循环的方式做队列，一个线程只对尾部操作，另一个线程只对头部操作，发送数据的线程向尾部推入数据，接收数据的线程从头部取出数据，循环操作队列，只有当队列满时才需要停一停等待数据
处理，其他时候，两者永远都不需要相互等待。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;无锁容器&quot;&gt;无锁容器&lt;/h3&gt;

&lt;p&gt;无锁容器的复杂度有点高，它适用于高并发场景，这里不便深入，其原理是原子操作。虽然我们不深入无锁容器的具体写法，但我们用问答的方式来解释下原子操作。&lt;/p&gt;

&lt;h3 id=&quot;什么是原子性&quot;&gt;什么是原子性？&lt;/h3&gt;

&lt;p&gt;如果一个更新操作不会计算到一半的时候被另外一个线程看到，就叫原子性。&lt;/p&gt;

&lt;p&gt;原子操作可认为是一个不可分的操作；要么发生，要么没发生，我们看不到任何执行的中间过程，不存在部分结果(partial effects)。可以想象的到，原子操作要保证要么全部发生，要么全部没发生，这样原子操作绝对不是一个廉价的消耗低的指令，相反，原子操作是一个较为昂贵的指令。&lt;/p&gt;

&lt;h3 id=&quot;非原子操作为什么会更新到一半被另一个线程看到&quot;&gt;非原子操作，为什么会更新到一半被另一个线程看到？&lt;/h3&gt;

&lt;p&gt;即使一个简单的整型变量的赋值操作，也有可能更新到一半被另一个线程看到，这是为什么呢？原因就是高速缓存中的旧数据。&lt;/p&gt;

&lt;p&gt;由于每个cpu除了共享一个内存设备外还有各自的高速缓存，一个cpu更新了内存中的内容后，其实并没有通知其他cpu中的缓存去掉该内容，这导致其他cpu中的高速缓存存储的仍然是旧的数据。当这些CPU读取这个变量时会从缓存中取得旧数据，直到缓存中的这个数据被丢弃或更新。&lt;/p&gt;

&lt;h3 id=&quot;原子操作做了什么使得更新不被其他线程看到&quot;&gt;原子操作，做了什么使得更新不被其他线程看到？&lt;/h3&gt;

&lt;p&gt;CPU的高速缓存间有一个MESI协议(cache一致性协议，4个关键词 Modifed Exclusive Shared Invalid 拼凑起来的缩写)，通过这个消息协议，CPU可以查看其他CPU高速缓存中的数据状态，就像不同设备间通信那样。&lt;/p&gt;

&lt;p&gt;当执行原子操作 store 即写入数据时，先查看当前cpu高速缓存中有没有数据，如果没有，则通知其他cpu中的高速缓存该数据切为无效状态，等待所有cpu都将该数据切为无效状态后，此cpu才开始发起写入内存和高速缓存的操作，并标记该值为修改状态。如果有，则更新高速缓存中的值，并通知其他cpu中的高速缓存该值已经不合法，最后此cpu并没有将该值写入内存，而是在高速缓存中标记该值被修改，以便下次再利用，或者等到丢弃时再写入内存。&lt;/p&gt;

&lt;p&gt;当执行原子操作 load 即读取数据时，先查看当前cpu高速缓存中有没有该数据，如果没有(或者是无效的)，则从先从查看其他cpu中查看该数据，如果有则获取，没有则从内存中获取。如果当前cpu高速缓存中有该数据，则直接使用该数据(必须不是无效的)。&lt;/p&gt;

&lt;h3 id=&quot;其他并行优化&quot;&gt;其他并行优化&lt;/h3&gt;

&lt;p&gt;1.分割资源，减少线程间的争夺。&lt;/p&gt;

&lt;p&gt;分割或者复制一块内存出来，让某个线程专门使用，这样就不会与其他线程冲突，计算结束时再考虑合并的事。&lt;/p&gt;

&lt;p&gt;此方法在Unity引擎的 Job System中有使用到，即给Job System一块独立的内存来处理自己的事物，与其他线程不冲突。&lt;/p&gt;

&lt;p&gt;2.散列容器，减少锁的范围，和前面介绍的细粒度锁(算法）稍微有点不一样，这种容器是散列的，冲突更少但使用范围很小。&lt;/p&gt;

&lt;p&gt;3.SIMD指令虽然不是并行，但由于它可以同时处理4个数据的运算，也算是勉强算并行处理了。&lt;/p&gt;

&lt;p&gt;参考资料：&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.cnblogs.com/huxiao-tee/p/4657851.html&quot;&gt;《从内核文件系统看文件读写过程》&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.zhihu.com/question/64723752&quot;&gt;《Linux系统中 进程 、线程 、时间片的关系》&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.jiqizhixin.com/articles/2019-01-22-12&quot;&gt;《说说无锁(Lock-Free)编程那些事》&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>给女儿写信(七)  圣诞节</title>
   <link href="http://www.luzexi.com/2020/12/25/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A17"/>
   <updated>2020-12-25T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/12/25/给女儿的信7</id>
   <content type="html">&lt;p&gt;Hi 陆秀恩&lt;/p&gt;

&lt;p&gt;圣诞快乐，今年的圣诞爸爸在很远的深圳，爸爸好想跟你一起过圣诞，爸爸一直很想念你，也一直很关注你。&lt;/p&gt;

&lt;p&gt;我知道你与同学相处的越来越融洽了，在学校里的表现很积极很开朗，大家都喜欢跟你交朋友，因为你懂得关心朋友。&lt;/p&gt;

&lt;p&gt;你的画画和跳舞越来越棒了，对自己喜欢的事很专注。我跟秀恩一样也很专注呢。我还听妈妈说你越来越自律了，这是个不错的开始喔。&lt;/p&gt;

&lt;p&gt;爸爸一直在学习如何学习，想把学习的技巧再多研究一下，让自己更懂得如何高效学习，以前爸爸学习总是很低效，注意力很难集中，浪费了很多时间，现在我想改变一下自己。&lt;/p&gt;

&lt;p&gt;这是一条漫长的路，爸爸每天都在坚持，坚持每天看点书，每天运动一下，每天学习一下，这样一天天积累下来，1年就会有大的不同，3、5年就有翻天覆地的变化。我想成为一个知识渊博的人，可能成不了科学家，但可以成为一个有价值的人，一个自律、智慧、有爱的人。&lt;/p&gt;

&lt;p&gt;爸爸很想念你，祝你圣诞快乐，加油陆秀恩 ：）&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Hi 陆安妮&lt;/p&gt;

&lt;p&gt;圣诞快乐，爸爸很想念你。&lt;/p&gt;

&lt;p&gt;我常听妈妈说你也很想念爸爸，爸爸心里暖暖的很高兴。&lt;/p&gt;

&lt;p&gt;在幼儿园里的日子还好么，和朋友们相处的还融洽么，爸爸很想过来每天都陪着你，但爸爸有点忙，需要在深圳工作几年。&lt;/p&gt;

&lt;p&gt;爸爸知道最近你跟姐姐一起开始学习写字和数学，爸爸知道你很努力，跟爸爸一起加油喔。你会慢慢的找到自己学习的节奏的，安妮。你很棒哎，要坚持住喔。爸爸也在深圳努力呢，每天都有学习和健身，爸爸跟你一起做身体壮壮的、学习棒棒的好孩子，比心。&lt;/p&gt;

&lt;p&gt;爸爸拿着你送给我的画，每天都放在床头看一下，一看到这幅画爸爸就想到你，下次记得再送爸爸一副画哦。最近去台上表演时心里紧张吗，爸爸看了你跳舞的视频，很不错哎，我们再接再厉，一起加油。&lt;/p&gt;

&lt;p&gt;爸爸、妈妈、小姨妈、安妮、秀恩都一起加油，加~油~！&lt;/p&gt;

&lt;p&gt;圣诞快乐安妮，想念你的爸爸 ：）&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(十七) 《C++性能优化指南》二</title>
   <link href="http://www.luzexi.com/2020/12/13/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B017"/>
   <updated>2020-12-13T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/12/13/读书笔记17</id>
   <content type="html">&lt;h3 id=&quot;字符串问题&quot;&gt;字符串问题&lt;/h3&gt;

&lt;p&gt;这里作者讲的有点啰嗦，没有围绕着重点去讲，所以这里我吸收下他的知识点并总结下自己的知识点和经验。&lt;/p&gt;

&lt;p&gt;把字符串问题单独拎出来说是因为字符串问题比较大，也比较隐性，常常容易引起性能问题。字符串在概念上很简单，它就是一个字符数组，但是想要实现高效的字符串却非常不容易。&lt;/p&gt;

&lt;p&gt;首先字符串内存是动态分配的，其次是字符串常常被用来当成一个值来使用，这导致字符串操作常常带来大量不必要的内存分配和内存复制。&lt;/p&gt;

&lt;p&gt;字符串问题的重点在内存分配问题和字符串查找。字符串操作带来的内存频繁的分配和浪费导致程序性能效率大大降低，操作包括，字符串拼接、字符串拆分、字符串大小写切换等，每次操作字符串都会新分配一个字符串内存或者字符串内存集，而查找字符串，即使使用算法通常也会逐字比较，效率比价差，特别是在一个集合中查找某个字符串对应的值时，需要完整的比较两者字符串是否完全一致。&lt;/p&gt;

&lt;p&gt;改善内存分配效率，我们可以使用预分配和内存池机制。字符串内存预分配可以有2种，一种预先分配很多个长度不同的字符串缓存起来，使用时给出去，例如分别预分配1-50长度的字符串各100个，当逻辑需要时再给出去，用完了再收回来。另一种是先分配多个长一点的字符串，例如分配100个100长度的字符串，让字符串能够在拼接时不用再分配内存，由于本身字符串长度足够长，所以可以直接在当前字符数组中改写内容。&lt;/p&gt;

&lt;p&gt;除了预分配，我们也可以将已经分配的字符串加入到字符串缓冲池中去，来管理这些预先分配好的字符串，需要时给予，不用时回收，不够时再分配一批，这样就能重复利用已经分配的字符串内存，将分配内存的工作集中起来消耗在某个点上。&lt;/p&gt;

&lt;p&gt;我们常会去比较和查找字符串，查找字符串中的字符串有很多算法，但比较两个字符串是否相同如果仍然使用查找算法去做就是一种浪费，那么我们应该怎样去做两个字符串的比较呢，最好用哈希的方式，把字符串计算出一个哈希值，用这个哈希值来比较是否相等就快很多了。例如我们常常在业务逻辑中判断两个字符串是否相等，每次比较都会浪费掉很多计算量，用哈希的方式就会快很多，因为比较的是两个整数，在每次更新字符串时只需要重新计算一次哈希值就可以，由于通常字符串更新的并不频繁，所以计算哈希的消耗远比每次遍历两个字符串所带来的消耗要小的多。但哈希计算不一定能够获得一个唯一值，所以它只能被用来判断是否不同，即当哈希值不同时，两个字符串一定不同，而两个哈希值相同时有可能不同，此时再比较两者是否真的相同。虽然无法直接比较相同的字符串，但仍然大大减少了不同的两个字符串的比较的计算。&lt;/p&gt;

&lt;h3 id=&quot;算法&quot;&gt;算法&lt;/h3&gt;

&lt;p&gt;算法是性能优化中的精髓，多数平庸的优化方法对性能改善都是线性的，但算法不同，如果说一个高效的算法替换了一个低效的算法性能可能呈现指数级的增长。&lt;/p&gt;

&lt;p&gt;算法时间开销一般有O(1)，O(log2(N))，O(N)，O(Nlog2(N))，O(N^2)，O(N^3)，O(2^N)，从高效到低效的排列，大多数算法如果能到log2(N)已经是非常优秀了，再进一步到O(1)则通常要付出巨大的内存代价。&lt;/p&gt;

&lt;p&gt;作者对二分查找、哈希、散列查找算法特别中意，于是讲了散列算法是怎样的和关键点是什么。我们在用在用查找算法时log2(N)的算法已经是非常不错的查找效率了，所以二分查找是效率比较高的，但它仍然是建立在有序队列的基础上，需要先排序再查找，排序比查找更能耗时，而且后续的元素加入需要插入到有序队列中。而散列算法则不同，排序和查找会更快，前提是数据特性能够散列，或者说散列的冲突不会那么高，于是他告诉我们哈希算法对于散列的重要性。&lt;/p&gt;

&lt;p&gt;这里作者讲的查找和排序内容比较浅，我融入了一些自己的经验。&lt;/p&gt;

&lt;p&gt;算法通常是根据数据特效来定制的，所以对数据独有的特性分析是重点。就我的理解，算法并一定要局限于当前的结构，可以重新创建一个全新的结构方式，这样在用算法去优化程序的时候思路会更开阔。例如我们在全地图寻找某个人的时候并不一定要在查找函数上优化算法，而可以重新制造一个方格结构体把世界分割成不同的方块，找人时只要收集四周方块内的人就可以更快的找到。&lt;/p&gt;

&lt;p&gt;算法也可以是局部的，因为数据多少和数据的特性可以由不同算法处理，所以我们在处理一大堆数据时可以拆分成不同的数据集进行处理，这些数据集的大小和特性也有所不同，针对性的处理会得到更有效的算法效率，例如快速排序算法，可以由不同的算法组成，由于它的中间值决定了快排速度，所以中间值我们可以用三个值取中间数的算法来找到更加稳定的中间值，当数段被分割成多个区间并且单个区间小于等于8个数时，插入排序其实比快排会更加快一些，因此我们在快排中间当数据量小于8时选择用插入排序算法。&lt;/p&gt;

&lt;p&gt;作者列举了一些常用的算法优化思路和套路。&lt;/p&gt;

&lt;p&gt;套路有：预计算、延迟计算、批量处理、缓存、特化、提高处理量、提示、优化执行路径、散列法、双重检查。&lt;/p&gt;

&lt;h3 id=&quot;预计算&quot;&gt;预计算&lt;/h3&gt;

&lt;p&gt;提前计算好一些可以离线完成计算量存储在文件中，这样可以省去了实时计算的开销。&lt;/p&gt;

&lt;h3 id=&quot;延迟计算&quot;&gt;延迟计算&lt;/h3&gt;

&lt;p&gt;先让所有中间过程改变完成后再对最后的结果计算，这样就省去了每次都需要计算全路径的消耗，例如引擎中通常有节点相互的挂载，每次赋值节点position时都会去计算重新计算子节点的位置，这时就要思考如何让计算延后，每次子节点的真实位置只要在帧结束时计算一遍就可以了，不用每次改变position时都去全路径计算一遍所有子节点。&lt;/p&gt;

&lt;h3 id=&quot;批量处理&quot;&gt;批量处理&lt;/h3&gt;

&lt;p&gt;某些具有相同性质的数据不要一个个处理，因为一起同一类型的数据处理起来会有更优的方法，例如堆排序如果一个个插入的元素的话性能开销是O(Nlog2(N))，而一次性构建一个堆的话只需要O(N)。&lt;/p&gt;

&lt;h3 id=&quot;缓存&quot;&gt;缓存&lt;/h3&gt;

&lt;p&gt;不要每次都计算，然后拿着结果去比较，计算完后缓存起来，一直用缓存的值，直到需要更改时再计算一遍。&lt;/p&gt;

&lt;h3 id=&quot;特化&quot;&gt;特化&lt;/h3&gt;

&lt;p&gt;一堆数据处理时它们都会消耗一些计算量，如果某个数据是特别的，不需要计算或者计算量可以很小，则另外开辟一个通道给它，让它少消耗一些。&lt;/p&gt;

&lt;h3 id=&quot;提高处理量&quot;&gt;提高处理量&lt;/h3&gt;

&lt;p&gt;一次处理多个数据而不是一个个处理，例如写日志不要每次都写，每隔一段时间写一次，类似这样的操作，先把准备的数据集中起来，集中起来的数据可能会有更多相似的特性可以用来优化。&lt;/p&gt;

&lt;h3 id=&quot;提示&quot;&gt;提示&lt;/h3&gt;

&lt;p&gt;当处理数据的时候，给予一个提示，这样我们就能知道该如何更好更快的处理，例如在插入一个数据到队列里去时，告诉插入函数，这个数是个比较大的值或者是一个比较小的值，这样我们在做插入时就有了更多优化提示。&lt;/p&gt;

&lt;h3 id=&quot;优化执行路径&quot;&gt;优化执行路径&lt;/h3&gt;

&lt;p&gt;程序语句里有很多个if…else，如果95%的语句都进某个if，那么最好把它提前到语句前，这样就不用执行其他的if里的计算了。&lt;/p&gt;

&lt;h3 id=&quot;散列法&quot;&gt;散列法&lt;/h3&gt;

&lt;p&gt;哈希值比较法，数据结构和字符串在比较时比较费时，用哈希值比较则比较方便，为它们计算一个哈希值，当两个哈希值不同时，它们一定不同，如果哈希值相同则再比较是否真的相同。&lt;/p&gt;

&lt;h3 id=&quot;双重检查&quot;&gt;双重检查&lt;/h3&gt;

&lt;p&gt;数据其实有很多个特征可供我们使用来优化我们的算法，比如长度，如果两个数组长度不一样，那么它们两个的内容肯定是不一样的。类似这样的特征还有数据结构的某个字段或者某几个字段可以决定算法的计算路径，我们只要判断这几个字段就能排除很多计算量。&lt;/p&gt;

&lt;h3 id=&quot;套路只是方法论&quot;&gt;套路只是方法论&lt;/h3&gt;

&lt;p&gt;很多时候技巧谁都知道，实际运用时却能难灵活自如，如果你不常用它们，它们就不回成为你思考的一部分。&lt;/p&gt;

&lt;h3 id=&quot;容器&quot;&gt;容器&lt;/h3&gt;

&lt;p&gt;作者对容器类数据结构性能做了一些介绍，从本质和测试标准两个方面做了讲解。&lt;/p&gt;

&lt;p&gt;不管C++标准库和Boost中的容纳器，还是其他语言的标准容器，它们都是非常通用且性价比不错的容器。但是如果你想让容器的性能发挥到最佳状态，就得自己去改造它。为什么要改造呢？因为首先标准容器内存的分配方式对具体的业务逻辑并不友好，我们可以把跟业务强相关的内存分配方式和内存池的技巧用在容器上以提高内存分配效率，其次容器中数据结构的插入、删除、查找的算法跟业务匹配上并不是最佳的，所以我们要根据我们的实际情况来改造这些算法以提高性能效率。&lt;/p&gt;

&lt;p&gt;容器包括，序列容器和关联容器，序列容器有string，vector，deque，list，forward_list，大都是以数组或链表形式存在的容器，而关联容器则以map和set为代表，用来建立key和value之间联系的容器，包括map、multimap、set、multiset等。&lt;/p&gt;

&lt;p&gt;我们在构造完自己的容器后，需要跟标准容器进行比较，只有这样才能知道我们改造的与业务强相关的容器是否比标准容器更加高效。&lt;/p&gt;

&lt;p&gt;容器性能测试标准就是为了判断容器的在各方面的性能是否更优秀，测试内容需要包含向一个没有内容的容器中以及向一个有数万条记录的容器中插入，删除，查找十万个不同的元素所需要消耗的纳秒数，比较拥有相同功能的容器在执行同一功能的操作时所消耗的时间。我们在测试时用到的数据也会遇到问题，十万个元素也并不能代表数据的典型性，所以很多时候我们需要离线Random随机1000组不同的数据以覆盖所有测试范围。&lt;/p&gt;

&lt;p&gt;由于我们自己不可能对所有容器进行重构，所以第三方容器库也是我们需要关注的地方，包括Boost、EASTL等都是我们需要参考的对象。&lt;/p&gt;

&lt;h2 id=&quot;内存分配&quot;&gt;内存分配&lt;/h2&gt;

&lt;p&gt;提升内存分配效率是提升程序性能非常有效的手段，其实质就是减少内存分配次数，减少内存分配次数就意味着减少内存分配带来的消耗，因此关键的关键还是如何减少内存分配次数，注意，是次数，不是大小，也不是释放。&lt;/p&gt;

&lt;p&gt;讲内存分配之前，我们先来了解下内存分配接口以及内存分配的底层原理。&lt;/p&gt;

&lt;p&gt;C++中内存管理函数有new，delete，malloc，free，其中new，delete运算符可以被类重载为 new()，new&lt;a href=&quot;&quot;&gt;&lt;/a&gt;，delete()，delete&lt;a href=&quot;&quot;&gt;&lt;/a&gt;，他们与class强相关，而malloc()和free()则是经典的C函数库中的内存管理函数，它们分配和释放的是无类型内存块，当无类型内存块被创建出来后，可以被强制指定为是某个变量、数据结构或者类实例。&lt;/p&gt;

&lt;p&gt;从概念上来说，分配内存的函数会从内存中寻找一块可以使用的内存来满足请求，但事实上并没有这么简单。作者没有详细介绍内存分配和释放的底层原理，但我觉得这部分的底层原理是值得挖掘和说明的，作为一个频繁与内存打交道的程序员来说，知道内存是如何分配和释放的是非常有必要的。&lt;/p&gt;

&lt;p&gt;我们的程序被加载到内存后分为几个内存段，包括指令段，数据段，bss段，栈段，堆段，指令段放具体指令，数据段放常量数据，bss段放静态和全局变量，栈段放调用栈、临时寄存器和临时变量，以上几个段都是固定的，不会有扩容一说，只有堆段是可扩容的。new和malloc分配的内存就在这个堆段中。&lt;/p&gt;

&lt;p&gt;堆段会事先分配一段内存，当malloc请求分配的内存时，如果剩余空的内存足够，则分配返回一段足够大小的内存，如果不足则再申请内存。&lt;/p&gt;

&lt;p&gt;堆段在向系统申请内存时类似于提高内存块大小，分配新内存这个操作需要系统将用户态切换到内核态再切回来，因此性能损耗是比较大的。其实malloc分配了内存，也并不代表实际物理内存中申请了某块内存，因为进程所面对的虚拟内存地址空间，只有按页映射到物理内存地址，才能真正使用。而且分配了新的内存，物理内存上实际没有此内存空间，只有当我们memset实际使用它时系统发现了物理内存缺页情况时才真正分配实际物理内存空间。&lt;/p&gt;

&lt;p&gt;从这个角度看，内存分配在操作系统底层上会稍显复杂。那么申请内存在系统底层到底是如何操作的呢？我们来深究一下。&lt;/p&gt;

&lt;p&gt;在Linux中进程由进程控制块(PCB)描述，用一个task_struct 数据结构表示，这个数据结构记录了所有进程信息，包括进程状态、进程调度信息、标示符、进程通信相关信息、进程连接信息、时间和定时器、文件系统信息、虚拟内存信息等. 和malloc密切相关的就是虚拟内存信息，定义为struct mm_struct描述了进程的地址空间。&lt;/p&gt;

&lt;p&gt;mm_struct结构对整个用户空间（进程空间）的描述如下:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;///include/linux/sched.h 

struct mm_struct {
  struct vm_area_struct * mmap;  /* 指向虚拟区间（VMA）链表 */
  rb_root_t mm_rb;         /*指向red_black树*/
  struct vm_area_struct * mmap_cache;     /* 指向最近找到的虚拟区间*/
  pgd_t * pgd;             /*指向进程的页目录*/
  atomic_t mm_users;                   /* 用户空间中的有多少用户*/                                     
  atomic_t mm_count;               /* 对&quot;struct mm_struct&quot;有多少引用*/                                     
  int map_count;                        /* 虚拟区间的个数*/
  struct rw_semaphore mmap_sem;
  spinlock_t page_table_lock;        /* 保护任务页表和 mm-&amp;gt;rss */       
  struct list_head mmlist;            /*所有活动（active）mm的链表 */
  unsigned long start_code, end_code, start_data, end_data; /* 代码段、数据段 起始地址和结束地址 */
  unsigned long start_brk, brk, start_stack; /* 栈区 的起始地址，堆区 起始地址和结束地址 */
  unsigned long arg_start, arg_end, env_start, env_end; /*命令行参数 和 环境变量的 起始地址和结束地址*/
  unsigned long rss, total_vm, locked_vm;
  unsigned long def_flags;
  unsigned long cpu_vm_mask;
  unsigned long swap_address;

  unsigned dumpable:1;
  /* Architecture-specific MM context */
  mm_context_t context;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;其中start_brk和brk分别是堆的起始和终止地址，我们使用malloc动态分配的内存就在这之间。系统分配堆空间时，进程通过malloc()库函数在堆上进行空间动态分配，堆空间如果不够用，malloc就进行系统调用增大brk的值。malloc只知道start_brk 和brk之间连续可用的内存空间它可用任意分配，如果不够用了就向系统申请增大brk。&lt;/p&gt;

&lt;p&gt;我们看到实际堆内存在虚拟空间中是可以不断向上扩张的，虽然实际物理内存中不是这样，但至少在虚拟空间中我们可以认为堆内存是一段连续的内存空间地址。&lt;/p&gt;

&lt;p&gt;由于我们分配的内存空间都在虚拟空间当中，我们看到的都是虚拟的地址，实际物理内存分配并不是我们想象的那样连续，也有可能在分配时由于物理内存不足我们拿到的空间是从硬盘空间上的一段数据交换到内存上来的。所以很多时候即使我们分配了连续的空间，在物理内存上也并不是连续的，只能说连续的概率比较大而已。&lt;/p&gt;

&lt;p&gt;其实还有很多分配内存在操作系统层面的原理，这里暂时不深究下去。&lt;/p&gt;

&lt;p&gt;作者指出提高内存分配效率的方法有两种，一种是减少不必要的内存复制的情况，另一种是用固定大小内存分配器解决减少内存分配次数。&lt;/p&gt;

&lt;p&gt;其中内存复制现象常会存在于对象初始化、赋值运算、函数参数、函数返回、插入一个元素到标准容器中，这几种情况是我们需要特别注意的，常常会由于失误编码而导致内存复制的情况，特别是针对一些常用的结构体和非指针类型的实例传递。&lt;/p&gt;

&lt;p&gt;固定大小内存管理器，意思是分配的内存块大小是固定的，这块内存可以是某个类或数据结构相匹配的一个固定大小，也可以是按某固定大小的内存块，这块内存能容纳某个范围内的一个类或数据结构的实例，这个内存管理器专门管理这个类或数据结构的所有内存的，或者专门管理某个固定大小内存块的管理类，这样在分配某一类大小实例时可以专门使用这样的内存管理器(有冗余不可避免)。&lt;/p&gt;

&lt;p&gt;在固定大小内存管理器中，会预加载一段内存以便给足够数量的类和数据结构使用，并且在回收时存储在管理器中以便重复利用，这样既减少了内存分配次数，也减少了内存碎片。&lt;/p&gt;

&lt;p&gt;这样一来，内存块的管理，可以分为，专门为某个类设计的内存管理类，和专门为某个大小范围内设计的内存管理类，我们可以称它们为通用的内存块管理类。&lt;/p&gt;

&lt;p&gt;在实际编程中，我们在写一个固定大小的内存分配管理器时，如果某个类使用数量比较固定和分配释放率比较频繁的话，可以专门为这个类做一个分配器是性价比比较高的。我们通常也会写一个比较通用的内存管理器，用大小不同的内存块来进行区分，例如我们可以为64byte，128byte，256byte，512byte，1k，2k，这几档大小分别预分配几十个内存块存储在通用内存分配管理器中，当程序请求内存时，将请求大小四舍五入后变为2的下一个幂，这样就能获得一个最快适配的内存管理器，当然在使用完毕释放时也同样只是回到这个内存管理器中存储起来以便重复利用，就像内存池和对象池那样。&lt;/p&gt;

&lt;h2 id=&quot;热点代码&quot;&gt;热点代码&lt;/h2&gt;

&lt;p&gt;这周依然没有写完，下周继续最后一部分的总结。&lt;/p&gt;

&lt;p&gt;参考资料：&lt;/p&gt;

&lt;p&gt;《malloc和free的实现原理解析》https://jacktang816.github.io/post/mallocandfree/&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(十六) 《C++性能优化指南》一</title>
   <link href="http://www.luzexi.com/2020/12/06/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B016"/>
   <updated>2020-12-06T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/12/06/读书笔记16</id>
   <content type="html">&lt;p&gt;这本书给我的感受是，有技巧有细节也有许多不足，作者介绍了大部分程序上性能优化的方案和思路，也从原理上讲了性能问题的根本原理，但没能做得通俗易懂、深入浅出，书本有几处地方在故弄玄虚以及凑字数，也许是我的功力不足没能理解，这部分无法理解的杂乱无章的内容，可以留到以后再慢慢回顾。&lt;/p&gt;

&lt;p&gt;整本书其实并不是针对C++写的，而是面向程序执行而效率写的，其他语言也一样通用。我结合我的经历和经验写下我的理解，以及我从书中学到的知识，或许过几年回头看会是另一番情景，但现在我需要把它们拎出来总结一下。&lt;/p&gt;

&lt;p&gt;作者是一个有30多年编程经历的人，对编程依然保持的狂热兴趣，注意，他从未去过微软、谷歌等知名公司，这本书就是在这样的一个前提下写下的。&lt;/p&gt;

&lt;p&gt;作者认为我们在优化程序的时候，很多时候都是靠‘猜测’而不是实际的去测试，这是一个比较大的问题。实际上我们并不知道某段程序是否有性能问题，因为有可能编译器已经将它优化了，或者某段程序在我们优化后是否真的有性能提升，因为很多时候我们只是肉眼去代码或是用脑袋去猜，不知道性能问题出在哪里就花费很多时间去优化这是不行的。而且我们也不能因为优化程序性能而破坏了程序的稳定性，如果从中制造出Bug导致产品崩溃那是非常得不偿失的。&lt;/p&gt;

&lt;p&gt;另外他提出了一个比较重要的理念，即大部分性能问题在代码层面上的分布都是‘90/10规则’，也就是说，90%的性能问题出在10%的代码上。因此为了我们在做性能优化时提高效率，应该重点去找出这影响90%性能问题的那10%的代码，它们就是性能问题的重点。不过这10%的代码并不是集中在某处，而是分散在各个模块中，需要我们去找出来，因此按照我的经验和他的理念来理解性能优化的90/10规则，是说我们需要改动的代码远比整体代码要少的多，而我们必须精准的找出这部分少数代码并优化它。&lt;/p&gt;

&lt;p&gt;原书内容比较繁杂，我又重新归类，我把它归类为，计算机执行原理、性能测试、字符串问题、算法、内存分配、热点代码、IO、并发，这八个方面。下面就让我们来讲讲我从书本中学到的对性能优化的理解。&lt;/p&gt;

&lt;h2 id=&quot;计算机执行原理&quot;&gt;计算机执行原理&lt;/h2&gt;

&lt;p&gt;代码从被编译到成为可执行文件也就是机器码，这个过程就是一个从本文字符串翻译成机器码的过程，当我们执行它们的时候，它可机器码被放入了内存，内存中也有分块，包括数据段、栈段、指令段。&lt;/p&gt;

&lt;p&gt;CPU在执行指令时是从内存中将指令送入CPU的，而执行指令的速度通常比读取内存的快很多，因此读取指令也成为了瓶颈的一种。CPU在读取指令时也并不会一行一行的读取，因为这样效率太差，取而代之的是它会把一大块内容读取到高速缓存从而加快执行速度，指令会顺序执行直到结束或有跳转。&lt;/p&gt;

&lt;p&gt;而内存芯片也有自己的工作原理，它相当于另一个CPU，它只有在顺序访问时才能在一个周期内完成，而访问一个非连续的位置则会花费更多周期。&lt;/p&gt;

&lt;p&gt;这里就涉及到了内存在访问时的形式，每次访问内存都是以某个大小为单位，例如x86机器，每次访问内存时都是以4个字节为单位访问，一个int整数为4个字节需要一次访问，但如果这个int整数内存没有对齐，那么可能就需要访问两次才能获得这个值，因为构成这个内存的物理结构可能是垮了两个物理内存字。&lt;/p&gt;

&lt;p&gt;不过请注意，现代编译器都会默认对对象和数据结构做内存对齐操作，除非我们告诉编译器某个数据结构不做内存对齐。对齐时编译器也会优化内存结构让高速缓存命中率提高，关于class的内存布局我们在《深度探索C++对象模型》总结中有详细的讲解。&lt;/p&gt;

&lt;p&gt;作者没有细说关于不对齐时内存访问的来龙去脉，不过我们来举个例子：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;[-][-][x][x][x][x][-][-]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;例如上面这个非对齐的内存空间，‘x’表示某个int变量占用的4个字节，‘-’表示其他，当CPU读取这个int整数时，其实是先读取&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;[-][-][x][x]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;再读取&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[x][x][-][-]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;拼接完成后为&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[x][x][x][x]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;最后交给寄存器。实际上访问非内存对齐并没有我们想象的那么简单，一个内存实际上有很多个内存芯片共同组成。为了提高访问的带宽，通常会将存储地址分开放在不同的芯片上，例如上面位置0，1，2，3，这4个byte分别存放在芯片1，芯片2，芯片3，芯片4中，当需要它们时，可以一次性全部读取，即如下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;偏移量/芯片存储空间
   x1 x2 x3 x4
0 [-][x][x][x]
1 [x][-][-][-]
2 [-][-][-][-]
3 [-][-][-][-]
4 [-][-][-][-]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;图中x为要读取的数据，每一列为一个芯片负责的空间，每一行为一个偏移量。这就是说，内存实际上并不是完全以连续byte形式组织的，而是以偏移(offset)量给出具体地址。当我们读取[0][1][2][3]这4个byte数据时可以一次性读取，但如果从1开始读取1，2，3，4时就要多一次偏移(offset)的操作，即先让4个芯片读取偏移量为0时的数据，再让它们读取偏移量为1的数据。&lt;/p&gt;

&lt;p&gt;为了改善内存速度，高速缓存被大量运用，即我们说的L1、L2、L3、L4四级高速缓存，它们每一层的速度大约是下一层的10倍左右。当执行单位需要的内容不在高速缓存中时，需要从内存中加载数据到高速缓存中，并同时将一部分内容舍弃以换取足够的空间，通常会选择放弃的数据都是最近被使用频率比较低的数据。在读取一个不在高速缓存中的数据时通常会将临近的数据也被缓存起来，从访问概率上来说做了加速了数据访问，即概率上来说临近的数据访问概率比较高的特点。&lt;/p&gt;

&lt;p&gt;这意味着频繁被访问的数据和频繁被访问的周围附近的数据都会因为高速缓存而加速。不仅仅是变量数据，机器指令也是数据的一种，超远的if和goto跳转以及远地址function函数调用同样会让高速缓存失效，其原理是执行指令地址从一处跳到另一处导致执行指令不连续。&lt;/p&gt;

&lt;p&gt;内存的访问也会有不够的时候，这时虚拟内存带来了很大的便利，但也给性能带来了很大影响。在虚拟内存机制中，当内存不够时需要借用磁盘空间来扩充，这让内存制造出拥有充足物理空间的假象，将使用频率小的内存数据作为文件存放在磁盘上，当使用时再读区进内存同时更换部分内存到硬盘上，我们常称它们为内存swap操作。由于swap操作很费时，因此检测swap次数也常被纳入性能监控中。&lt;/p&gt;

&lt;p&gt;通常我们一个操作系统中有多个程序需要同时内存访问，而内存总线就只有一个，内存芯片必须一个个去完成CPU分配给它的任务，有时甚至是经常不连续的内存访问，由此看来，内存的读写负担是相当重的。如果未来有更多的处理器内核增加，而内存接口和读取速度没有增加的话，那么其实这些内核对性能的改善效果也是趋于递减的，因为虽然有多个CPU来处理指令，但内存并没有被加速。&lt;/p&gt;

&lt;p&gt;接着我们来看看线程和进程如何影响执行效率，由于操作系统会执行一个线程很短时间然后将上下文切换到其他线程或进程。导致在切换时会浪费掉很多时间，操作系统需要暂停当前的线程并且保存处理器中的寄存器到内存，然后为即将被执行的线程加载之前保存过的寄存器。如果新的线程的数据不在高速缓存中，那么还需要从内存中加载数据到高速缓存，因此上下文切换的代价比我们想象的高。&lt;/p&gt;

&lt;h3 id=&quot;这里做个小结以上讲的都是些计算机执行原理我们需要明白的原理才能真正明白优化背后的逻辑首先内存效率没有我们想象那么快非对齐内存会多一次开销编译器通常都有做内存对齐指令远距离跳转和内存远距离访问都会让高速缓存命中丢失频繁访问的数据和附近的数据会比较快虚拟内存扩充了内存但swap时性能开销很大多个进程和线程争夺内存使用权是性能瓶颈之一太多线程和进程上下文切换代价较大会导致执行效率降低&quot;&gt;这里做个小结，以上讲的都是些计算机执行原理，我们需要明白的原理才能真正明白优化背后的逻辑。首先内存效率没有我们想象那么快，非对齐内存会多一次开销(编译器通常都有做内存对齐)，指令远距离跳转和内存远距离访问都会让高速缓存命中丢失，频繁访问的数据和附近的数据会比较快，虚拟内存扩充了内存但swap时性能开销很大，多个进程和线程争夺内存使用权是性能瓶颈之一，太多线程和进程上下文切换代价较大，会导致执行效率降低。&lt;/h3&gt;

&lt;h2 id=&quot;性能测试&quot;&gt;性能测试&lt;/h2&gt;

&lt;p&gt;性能测试对于性能优化来说是关键的关键，就像作者提到的那样，我们不能靠猜来判断哪些代码需要优化，或者代码执行效率提升了多少不能由某个人说了算。&lt;/p&gt;

&lt;p&gt;那些具有最让人折服的优化技巧的开发人员都会系统地完成如下步骤：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;
1.测试出哪些地方是可优化的，做出预测并记录预测。

2.保留优化的代码记录

3.用测试工具进行测量优化前后的数据对比

4.保留实验结果并做详细的笔记。

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;以上列的四项步骤是性能优化过程中必须不断实践的技能，现实中多数开发人员都想当然的去优化代码，而不是按照上面的方式有条不紊的进行优化，这是优化过程中最糟糕的一点，即不知道自己该从哪里开始优化，优化结束时不知道是否真的优化了优化了多少，有可能更加糟糕，过了段时间甚至记不起来优化了什么。&lt;/p&gt;

&lt;p&gt;除了实际的去测量和记录，我们在性能测试时要注意哪些关键点呢？作者给出了自己的经验。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;1.测量程序的启动时间，执行时间，退出时间。

通常人们总是忘了启动和退出时间，这导致部分测量范围不够或者测量不准确。

2.测量的数据和环境必须是可重复的。

只有两个数据和环境是可重复的，才有可能让两次测量在同一个标准中进行。

3.测量必须有一个标准和一致的范围。

如果前后两次测量的环境、测量内容、测试的持续时间不一致，则测量出来的数据是无效的，这种情况下的任何优化数据都是可笑的。

4.测量数据通常都是波动的，没有不波动的测量数据。

因此我们需要通过反复测量多次给出平均值的方式来确定最后的数据。

5.其他进程会影响测量结果。

关闭其他会导致影响的进程，或者提高测量进程为最高级别。

6.测量工具很重要

测量工具包括类似Stopwatch方式的打点，抓取堆栈调用时间，内存分配，内存快照等方式。

7.分析代码和测量运行时间是帮助找出可优化代码的两种有效途径。

只分析代码是行不通的，只测量运行时间也不可行。要分析代码与测量相互迭代，分析后测量，测量后分析，以此方式不断找出可优化的代码。

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;开发人员需要向同事和领导展示他们在性能优化中取得的进展，我们需要精准的测量和详细的记录，如果凭直觉进行优化，也不发表结果，或者发表了结果也会遭到质疑，这是因为他们分不清你到底是在用高度专业的直觉进行优化还是只是在碰运气。&lt;/p&gt;

&lt;h2 id=&quot;字符串问题&quot;&gt;字符串问题&lt;/h2&gt;

&lt;p&gt;这把字符串问题单独拎出来说是因为字符串问题比较大，也比较隐性，常常容易引起性能问题。字符串在概念上很简单，它就是一个字符数组，但是想要实现高效的字符串却非常不容易。&lt;/p&gt;

&lt;h3 id=&quot;这周太忙暂时写到这里下周继续&quot;&gt;这周太忙，暂时写到这里，下周继续…&lt;/h3&gt;

</content>
 </entry>
 
 <entry>
   <title>给女儿写信(六)  平衡工作和自己的事</title>
   <link href="http://www.luzexi.com/2020/12/02/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A16"/>
   <updated>2020-12-02T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/12/02/给女儿的信6</id>
   <content type="html">
&lt;hr /&gt;

&lt;p&gt;导读：喜欢跟孩子们一起，可惜一直在外地工作，因此我给孩子写点信并录成视频，跟孩子聊聊自己的故事以及最近的感受&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Hi Sharon and Anne，爸爸好想你们，爸爸喜欢你们。爸爸还在深圳呢，要再过大概1个月才能回来呢。&lt;/p&gt;

&lt;p&gt;爸爸最近在给自己安排任务，以前爸爸常常等公司给我安排任务，这样会有个问题，爸爸总是会忙于公司的事，没有空去做自己想做的事情了。爸爸觉得这样不行，爸爸必须做些自己想做的事情，但又不能将公司的事情落下，于是爸爸就想到自己给自己安排任务，每天晚上都写下爸爸明天要做的事情，把公司的事和自己的事都兼顾起来，每天都做完他们，当然爸爸在指定任务的时候都是指定一天里能做完的，做不完的后面分成一天天的小任务去做。就像老师给你们安排作业一样，这些都是别人要你们做的，有些被动，做被动的事情的时候其实没有灵魂，不如反过来，每天晚上写下明天自己要做的事情，第二天的时候去做自己写下的列表上的事情，这样会更快乐哦。&lt;/p&gt;

&lt;p&gt;加油Sharon，加油Anne！&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>向内看自己(十) 反思自己的行为带给人的感觉</title>
   <link href="http://www.luzexi.com/2020/11/21/%E5%90%91%E5%86%85%E7%9C%8B%E8%87%AA%E5%B7%B110"/>
   <updated>2020-11-21T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/11/21/向内看自己10</id>
   <content type="html">&lt;p&gt;最近发现自己的行为和话语给别人的感觉很不好，这源于我自身的性格问题。&lt;/p&gt;

&lt;p&gt;我有很多性格问题，导致生活中、工作中在与人交往过程中就会遇到障碍或瓶颈。&lt;/p&gt;

&lt;p&gt;我很想弄清楚我到底出了什么问题，但一直没弄明白，于是我想好好剖析一下自己。&lt;/p&gt;

&lt;p&gt;也因为最近在学习冥想，这个看起来有点玄乎的词语，是我最近研究的内容，我会专门写一篇文章来详细说明我对它的理解与感受。&lt;/p&gt;

&lt;p&gt;我发现冥想最大的益处就是让我忽略杂念专注于当前所做的事，在专注过程中不断发现当前事物中以前没有被我发现的细节，这些细节很多时候都是关键中的关键。&lt;/p&gt;

&lt;p&gt;当然最重要的也是最大的问题是我自己本身，于是我边练习冥想技巧边来发现自己的问题。&lt;/p&gt;

&lt;p&gt;自身问题一，我常常会去要求别人却没有用心体会对方的感受，慢慢会引起别人的反感。&lt;/p&gt;

&lt;p&gt;自身问题二，我常常会以炫耀的口吻讲述自己的经历和想法，让对方感觉不愉快。&lt;/p&gt;

&lt;p&gt;自身问题三，我常常会想妥协而装做一副顺从的样子，让别人有了更多负担，也让事情进展有些尴尬。&lt;/p&gt;

&lt;p&gt;自身问题四，我常常心急如焚想立刻搞定某事而选择比较激进的做法，让人会觉得我不靠谱，或者不尊重他们。&lt;/p&gt;

&lt;p&gt;自身问题五，与人交流成为我了的负担，我宁愿自己辛苦点去搞定也不愿意或者说恐惧与人打交道。&lt;/p&gt;

&lt;p&gt;即使我知道这些问题的存在，我仍然不知道该如何去改进，因为我没有找到解决问题的方向。&lt;/p&gt;

&lt;p&gt;就像上学时一直喜欢玩耍没有好好学习一样，我知道好好学习的重要性，但就是坐不下来安静学习。&lt;/p&gt;

&lt;p&gt;我觉得是因为我没有get到某个点。这和我现在所面临的问题有点相似，明知道有问题，明知道该如何如何去做，但就是做不到。&lt;/p&gt;

&lt;p&gt;很多时候我们都是撞的很痛了才下定决心去改，但这时通常已经很迟了，我不想每次都等到我痛到骨子里了才去改。&lt;/p&gt;

&lt;p&gt;于是我问自己，我需要get什么点？&lt;/p&gt;

&lt;h4 id=&quot;我回忆了下长大成人后喜欢上了自律和学习的过程我为什么会有转变我是怎么转变过来的&quot;&gt;我回忆了下，长大成人后喜欢上了自律和学习的过程，我为什么会有转变，我是怎么转变过来的。&lt;/h4&gt;

&lt;h4 id=&quot;我依稀记得长大成人后逐渐喜欢上学习的起点是因为我第一次获得学习上的成就感开始的可能这个成就感是非常微不足道的一点点但足以敲开了我对此感兴趣的大门随着我不断深入研究慢慢的我被一次次的成就感所激励着不断前进许多正向的反馈在我的生活中不断冒出中间也有些许负向的反馈但由于相对正向反馈来说量少我还能克服它们&quot;&gt;我依稀记得，长大成人后，逐渐喜欢上学习的起点，是因为我第一次获得学习上的成就感开始的，可能这个成就感是非常微不足道的一点点，但足以敲开了我对此感兴趣的大门。随着我不断深入研究，慢慢的我被一次次的成就感所激励着不断前进，许多正向的反馈在我的生活中不断冒出，中间也有些许负向的反馈，但由于相对正向反馈来说量少我还能克服它们。&lt;/h4&gt;

&lt;h4 id=&quot;这个过程中最重要的是第一次正向反馈当我感觉到自己的努力有了回报我就更感兴趣去深入研究它们如果在开始的时候一次次又一次的正向反馈不断袭来我就能感觉到我自己找对了方法就像找到了金钥匙那样不断向这个方向前进直到遇到更大的瓶颈当遇到更大的瓶颈时我则需要更换思路和方法如果没有找对思路和方法则会继续原地踏步常常会因为原地踏步时间太久而感到沮丧和气馁因此而懈怠并且开始退步&quot;&gt;这个过程中最重要的是第一次正向反馈，当我感觉到自己的努力有了回报，我就更感兴趣去深入研究它们。如果在开始的时候，一次次又一次的正向反馈不断袭来，我就能感觉到我自己找对了方法，就像找到了金钥匙那样，不断向这个方向前进，直到遇到更大的瓶颈。当遇到更大的瓶颈时，我则需要更换思路和方法，如果没有找对思路和方法，则会继续原地踏步，常常会因为原地踏步时间太久而感到沮丧和气馁，因此而懈怠并且开始退步。&lt;/h4&gt;

&lt;h4 id=&quot;想到这里我想着能不能根据这个途径先去找些改善的行动项做起来从而获得正向反馈再根据这个正向反馈来一步步改善我的行为&quot;&gt;想到这里，我想着能不能根据这个途径，先去找些改善的行动项做起来，从而获得正向反馈，再根据这个正向反馈来一步步改善我的行为。&lt;/h4&gt;

&lt;h4 id=&quot;这当然是很难的起步的时候更是艰难特别是第一次的正向反馈可能需要一段比较长时间的积累我需要保持耐心维持改善行为好一阵&quot;&gt;这当然是很难的，起步的时候更是艰难，特别是第一次的正向反馈可能需要一段比较长时间的积累，我需要保持耐心维持改善行为好一阵。&lt;/h4&gt;

&lt;h4 id=&quot;路漫漫其修远兮吾将上下而求索&quot;&gt;路漫漫其修远兮，吾将上下而求索。&lt;/h4&gt;

&lt;h3 id=&quot;希望大家一起加油同我一样来改善自己&quot;&gt;希望大家一起加油同我一样来改善自己​&lt;/h3&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(十四) 《深度探索C++对象模型》</title>
   <link href="http://www.luzexi.com/2020/11/20/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B014"/>
   <updated>2020-11-20T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/11/20/读书笔记14</id>
   <content type="html">&lt;p&gt;看此书的起因是自己想更加深刻的了解底层，最底层的莫不过于汇编了，但只有汇编还不够，因为它需要上层工具语言编译器的支持，编译器怎么去编译程序的，决定了汇编的执行方向，理论上说汇编只是执行编译器编译后的程序内容，因此我认为编译器是关键所在。&lt;/p&gt;

&lt;p&gt;我们大多数人自认为自己已经把面向对象语言已经烂熟于心了，其实台面下的机制，如编译器合成的默认构造函数、对象内存布局等都不是很了解。我也是一样，我对编译器一块知识一直不是清晰，所以想通过这本书来了解我们平时编程时的底层的工作原理是什么。&lt;/p&gt;

&lt;p&gt;作者说第一句话就打动了我，让我认真看完了全书，他说“我的经验告诉我，如果一个程序员了解底层实现模型，他就能够写出效率较高的代码，自信心也比较高。一个人不应该用猜的方式，或者等待某位大师的宣判，才确定何时提供一个copy constructor 而何时不需要。这类问题的解答应该来自于我们自身对对象模型的了解。“&lt;/p&gt;

&lt;p&gt;不管我们是在使用C#、Java、C++，它们都是面相对象的编程语言，因此底层的原理都会有些相似性，特别是内存布局上。我也是抱着这种用C++内存布局去理解其他语言的心态去学习和研究这部分内容的。&lt;/p&gt;

&lt;h2 id=&quot;对象的内存布局&quot;&gt;对象的内存布局&lt;/h2&gt;

&lt;p&gt;一个普通的class，有成员变量、成员函数，静态变量，静态函数，关于它们我们可以话一张图，拿一个简单的Point来举例：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	class Point

	成员变量 float x

	成员函数 int PointCount()

	静态变量 static int sMaxCount

	静态函数 static int GetMaxCount()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;假如我们实例化一个Point，即 Point * pt = new Point，Point的内存中为：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	float x
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;没错，内存中只有一个浮点数变量的空间大小，那么其他的包括成员函数、静态变量、静态函数都不在Point实例内存中，它们在哪呢？&lt;/p&gt;

&lt;h4 id=&quot;它们被编译器编写在了代码段和数据段中可以被所有point实例共享的内存使用&quot;&gt;它们被编译器编写在了代码段和数据段中，可以被所有Point实例共享的内存使用。&lt;/h4&gt;

&lt;p&gt;我们知道一个可执行程序的内存分布，分为数据段、代码段、栈段，这三个基本都是静态不会被改变的空间段，其他还有中断表、堆内存空间等。&lt;/p&gt;

&lt;p&gt;class的成员函数、静态函数无论是否public还是private修饰，都会被放入代码段中，静态变量则被放入数据段中无论是否public还是private(放入数据段中的内容，每次取值都会做远距离寻址，相对近距离寻址会费一些，因为隔断了内存连续操作)，而成员变量例如Point中的x，无论它是public还是private都会被放入动态内存分配的内存块中。&lt;/p&gt;

&lt;p&gt;实际上计算机内存中、以及机器码中没有public和private之分，我们可以任意的取得任何内存中的内容没有限制，限制我们程序访问的，只是语言和编译器的语法检查器这两者为我们提供的语言方法和规则检查。&lt;/p&gt;

&lt;h4 id=&quot;当class有继承和多态后则有所不同&quot;&gt;当class有继承和多态后则有所不同&lt;/h4&gt;

&lt;p&gt;当class有了继承后，通常都会有多态出现，即虚函数。让我们来举个例子说明：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;class Point
{
public:
	float x;
	virtual int PointCount();
}

class Point2D : public Point
{
public:
	float y;
	virtual int PointCount();
	virtual void PrintPoint();
}

Point pt = new Point2D();
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;加上继承和多态后，内存布局就有所变化，由于基类和子类都有虚函数，子类重载了函数后，我们在内存中就需要有一张表来存放所有虚函数，以便正确调用。&lt;/p&gt;

&lt;p&gt;如上Point和Point2D的关系，可以将一个Point2D实例内存看作如下结构：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	- virtual table 虚表地址 --------&amp;gt;   Point2D的虚表空间
										- PointCount()地址
										- PrintPoint()地址
	- x
	- y
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;和普通class不同的是，当我们有了多态后，我们的一个实例内存中除了数据外，还多了一个变量指向这个类的虚表空间，这个虚表空间已经被编译器编写在了数据段中，是一个静态的并且专门为Point2D制定的空间，这个空间中存放着指向类中所有虚函数地址，空间不会随着多个实例化而增多因为它只有一个，但每个Point2D实例的虚表指针指向着它（Point实例也是类似的内存布局）。&lt;/p&gt;

&lt;p&gt;（这里不得不讲一下struct和class的区别，其实它在不同语言中的语义和用途不太一样，在C++中可能大部分struct都是用来兼容c或当纯数据的内存结构的，很多class有的功能struct也同样能实现，但在其他语言则不同，比如在C#中的用途就是期望struct能更多的做些内存连续优化，因为它是复制类型数据结构，每次赋值和传递参数都会复制一份内存，除非使用引用关键字。）&lt;/p&gt;

&lt;p&gt;此时如果是Point实例则是&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	- virtual table 虚表地址 --------&amp;gt;   Point的虚表空间
										- PointCount()地址
	- x
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;与Point2D相比，少了一个y变量，以及虚表空间中少了一个函数地址。&lt;/p&gt;

&lt;p&gt;(编译器编译中很多时候都会将许多我们看来是动态的访问变为静态，例如子类的强制转换(其实内存上没有做任何操作，只是通知编译器后面的操作是基于某个类开始的，使得编译器在编译时偏移地址较前面有所不同)、前面Point中的静态函数（程序在执行时就会直接跳到代码段的GetMaxCount这个函数的地址上而不会通过实例内存去找）、Templete会在编译期就将代码和指令生成完毕，以及一些编译器对代码的优化会直接将某个公式计算好以数字的形式呈现在机器码里，还有很多排错机制，其实编译器帮我们干了很多本该我们需要检查和手动指定的工作。)&lt;/p&gt;

&lt;p&gt;多重继承的内存布局更为复杂些，会带有好几个虚表地址在内存中，效率也更差，因为每一层的虚表都会间接性降低把处理搬到寄存器执行的优化，我们也不鼓励多重继承的写法，因此这里不做详细讲解。&lt;/p&gt;

&lt;h4 id=&quot;实例化后的类大小真的只有变量大小的总和加一个虚标指针吗还有内存对齐的规则&quot;&gt;实例化后的类大小真的只有变量大小的总和，加一个虚标指针吗？还有内存对齐的规则。&lt;/h4&gt;

&lt;p&gt;在32位计算机上，由于寄存器是32位、总线有32条、一次取内容为32位，因此每次取值时都会取得一个4字节的内存内容，编译器也会遵循32位字节对齐的方式去编译。&lt;/p&gt;

&lt;p&gt;例如：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;class A
{
	int a; //4字节
	bool b; //1字节
	float c; //4字节
	double d; //8字节
	char e; //1字节
}
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;	[a 4字节]
	[b 1字节][填充3字节]
	[c 4字节]
	[d 8字节]
	[e 1字节][填充3字节]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;32位计算机中，此对象的内存空间为 4 +（1+3） + 4 + 8 + (1+3) = 24个字节。&lt;/p&gt;

&lt;p&gt;而在64位计算机中，以64位内存对齐的规则时：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	[a 4字节][b 1字节][填充3字节]
	[c 4字节][填充4字节]
	[d 8字节]
	[e 1字节][填充7字节]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;此对象实例的内存占用空间为 (4+1+3) + (4+4) + 8 + (1+7) = 32字节，在取a、b变量时可以一次取得，节省一次内存调度。&lt;/p&gt;

&lt;h4 id=&quot;对象在内存上对齐的越紧凑能节省的内存调度次数就会越少程序运行的性能也会因此提高&quot;&gt;对象在内存上对齐的越紧凑，能节省的内存调度次数就会越少，程序运行的性能也会因此提高。&lt;/h4&gt;

&lt;h2 id=&quot;inline-内联&quot;&gt;inline 内联&lt;/h2&gt;

&lt;p&gt;除了对齐内存，inline也能让编译器优化函数，让函数执行更快。那么它是怎么优化的呢？&lt;/p&gt;

&lt;p&gt;一般而言，处理一个inline函数有两个阶段：&lt;/p&gt;

&lt;p&gt;1.分析函数，以决定函数是否具备inline能力。&lt;/p&gt;

&lt;p&gt;如果函数因其复杂度，或因其建构问题，被判断不可成为inline，它会被转为一个static函数，并在“被编译模块”内产生对应的函数定义。&lt;/p&gt;

&lt;p&gt;2.真正的inline函数在调用时展开操作，省去函数调用导致的推栈和入栈寄存器的操作，也一并优化了函数中的计算内容(更少的内存存取次数和更快更少的计算次数)。&lt;/p&gt;

&lt;p&gt;这也导致我们通常给予inline后并不清楚编译器是否真正将其视为inline去优化，只有我们进入汇编中才能看到是否真的实现了inline。&lt;/p&gt;

&lt;h3 id=&quot;inline具体会优化哪些方面呢我们来举几个例子&quot;&gt;inline具体会优化哪些方面呢？我们来举几个例子&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;minval = min(val1, val2); //1
minval = min(1024, 2048); //2
minval = min(foo(), bar() +1); //3
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;如果min是一个inline内联函数：&lt;/p&gt;

&lt;p&gt;第一行会被改为 minval = val1 &amp;lt; val2 ? val1 : val2; 省去了函数调用。&lt;/p&gt;

&lt;p&gt;第二行会被改为 minval = 1024; 编译器直接离线计算好结果用常数代替函数调用。&lt;/p&gt;

&lt;p&gt;第三行会被改为&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;int t1 = foo();
int t2 = bar() + 1;
minval = t1 &amp;lt; t2 ? t1 : t2;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;增加了临时性的变量，从而替代内联的函数调用。inline函数中的局部变量加上inline自己增加的局部变量，在展开后可能会导致大量临时性的变量产生。&lt;/p&gt;

&lt;p&gt;inline函数对于封装提供了一种必要的支持，可以有效存取装于class中的nonpublic数据。它同时也是C程序中大量使用#define宏处理的一个安全代替品，但如果inline函数被调用太多次的话，会产生大量的扩展代码，使得程序集本身的大小暴涨。&lt;/p&gt;

&lt;h2 id=&quot;template-模板&quot;&gt;Template 模板&lt;/h2&gt;

&lt;p&gt;C++ 中的Template 模板，在许多语言里也称为泛型。自从1991年加入到cfront 3.0之后深深改变了C++编程习惯。它被使用在编译期做些评估和生成代码的工作，也因而带来了重大的效率提升，同时也成为了程序员一个噩梦以及最挫败的主题。&lt;/p&gt;

&lt;h4 id=&quot;那么当我们声明了一个-template-class或者-template-function时究竟会发生什么呢&quot;&gt;那么当我们声明了一个 template class、或者 template function时究竟会发生什么呢？&lt;/h4&gt;

&lt;p&gt;其实什么都不会发生，如果我们不使用它的话，编译器就会忽略它什么都不干。有也只有当我们使用定义的template做事时，编译器才开始工作，我说的使用是指在我们在代码中使用了前面声明的 template class 或者 template function，而不光是定义。&lt;/p&gt;

&lt;p&gt;如果我们使用了一个指针，指向特定的实例，像这样：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;template&amp;lt;class T&amp;gt;
class Point
{
public:
	void Print();
private:
	T x,y,z;
}

Point&amp;lt;float&amp;gt; * ptr = 0;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;编译器依然什么都不回做，因为一个指向class object的指针本身并不是一个class object，编译器不需要知道与该class有关的任何成员数据或对象布局数据，它只是一个指针，至于指向什么并不重要。&lt;/p&gt;

&lt;p&gt;然而如果带template的class被实例化时，则编译器才真正开始为template产生代码，例如我们定义了一个实体而非指针：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;Point&amp;lt;float&amp;gt; origin;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;此时origin会被实例化，编译器也检查到了这种情况，则会启动template代码生成器为Point&lt;float&gt;生成一个类，如下面代码生成格式：&lt;/float&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;class Point_float
{
private:
	float x,y,z;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;然而在编译器生成template代码类时，也并不会将所有template定义的类中的代码都生成出来，编译器只会在代码中生成被使用的函数或者可能使用的函数。例如上述中的Point_float就没有生成Print函数，因为我们并没有使用它。&lt;/p&gt;

&lt;h4 id=&quot;这样做的主要原因是为空间和时间上的效率考虑如果我们的template-class中有100个函数使用中的类型有10个我们只使用了其中5个函数那么原本会生成是100--10--1000个函数现在编译器只会生成-5--10--50-个函数其余的950个函数代码讲会被忽略这大大节省了空间提升了编译效率&quot;&gt;这样做的主要原因是，为空间和时间上的效率考虑，如果我们的template class中有100个函数，使用中的类型有10个，我们只使用了其中5个函数，那么原本会生成是100 * 10 = 1000个函数，现在编译器只会生成 5 * 10 = 50 个函数，其余的950个函数代码讲会被忽略。这大大节省了空间提升了编译效率。&lt;/h4&gt;

&lt;h4 id=&quot;那么编译器是如何生成template代码的呢&quot;&gt;那么编译器是如何生成template代码的呢？&lt;/h4&gt;

&lt;p&gt;首先要发现template使用情况，在.h和.cpp文件中寻找template使用情况，如果有使用则继续生成，否则忽略。&lt;/p&gt;

&lt;p&gt;其次编译器尝试模拟链接操作，检查看看哪一个函数真正需要，将真正需要的生成的函数提取出来，位它们生成具体的函数代码。&lt;/p&gt;

&lt;p&gt;最后编译器要阻止template function在多个.o文件中被生成出来，它会从链接器提供的支持中获取信息，只留下一份代码，其余的都将忽略。&lt;/p&gt;

&lt;h2 id=&quot;rtti-执行期类型识别&quot;&gt;RTTI 执行期类型识别&lt;/h2&gt;

&lt;p&gt;执行期类型识别(Runtime Type Identification)最初是由于支持异常处理(Exception Handling)而产生的，可以说它是异常处理的副产品，后来被大量的使用在执行期代码中。&lt;/p&gt;

&lt;p&gt;C++被吹毛求疵的一点就是，它缺乏一个保证安全的向下转换操作，只有在类型真的可以被转换的情况下，你才能够执行转换。想要实现安全的转换，则需要额外的类信息支持，于是就有了类信息(type information)来作为RTTI的保障。&lt;/p&gt;

&lt;p&gt;type_info是C++标准所定义的类型描述器的class名称，该class中放置着待索求的类型信息。虚表空间中的第一个空格就是指向type_info信息的地址。&lt;/p&gt;

&lt;p&gt;在现代C++编译器中，如果我们想要有RTTI功能，则class必须有虚函数或者基类有虚函数，即&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;
class Point
{
	float x;
	virtual int PointCount();
};

class Point2D: public Point {...};

&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;- Point2D 和 Point实例中的内存布局情况
- virtual table 虚表地址 --------&amp;gt;   Point2D的虚表空间
									- type_info ptr 类信息地址
									- PointCount()地址
									- PrintPoint()地址
- x
- y
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;当类中不存在虚函数时，typeid是编译时期的事情，也就是静态类型，当类中存在虚函数时，typeid是运行时期的事情，也就是动态类型，关于这一点，我们在实际编程中，经常会出错，一定要谨记。&lt;/p&gt;

&lt;p&gt;Point2D和Point的​虚表头，都是type_info的信息地址，它们的type信息地址分别指向两个不同的type_info静态数据内存地址。这样看来，每次我们使用指针取得class object类型描述器时，其实就是通过虚表指针去取得类信息地址，即如下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;((type_info)(pt-&amp;gt;vptr[0]))-&amp;gt;name(); //从虚表指针中的第一个槽位中取得类信息
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;type_info object在C++标准下的定义为：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;class type_info
{
public:
	virtual ~type_info();
	bool operator==(const type_info&amp;amp;) const;
	bool operator!=(const type_info&amp;amp;) const;
	bool before(const type_info&amp;amp;) consnt;
	const char* name() const;
	const char* raw_name() const;//返回类名称的编码字符串
private:
    void *_m_data;
    char _m_d_name[1];
	type_info(const type_info&amp;amp;);
	type_info&amp;amp; operator=(const type_info&amp;amp;);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;有了类信息，我们在做向下转换时，就可以用根据类信息来判断是否可以转换，即如下代码：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;Point2D pt2d = dynamic_cast&amp;lt;Point2D&amp;gt;(pt);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;相当于：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c++&quot;&gt;Point2D pt2d = NULL;
type_info type_pt2d = typeid(Point2D);
type_info type_pt = typeid(Point);
if(type_pt2d == type_pt || type_pt.before(&amp;amp;type_pt2d))
{
	pt2d = (Point2D)pt;
}
return pt2d;
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;总结class内存分布由成员变量虚表指针组成布局大小会根据计算机的内存对齐方式不同而不同inline内联函数并不一样会真的内联跟编译器根据函数的复杂度判断是否可以内联的如果帧的内联了其优化会节省函数调用开始和计算次数开销template模板会根据template-class使用情况生成代码生成步骤是先判断是否有被使用-生成代码-去除重复想要有rtti执行期类型识别则必须有额外的信息空间支持即在c中我们必须有虚继承或基类有虚继承才能获得rtti的功能甚至我们可以自己写一个执行期类型识别功能来替代标准c有了执行期类型识别功能我们在向下转换时才更加安全即dynamic_cast调用时先判断是否类型一致或者是父类的情况再转换否则为null&quot;&gt;总结，class内存分布由成员变量+虚表指针组成，布局大小会根据计算机的内存对齐方式不同而不同。inline内联函数并不一样会真的内联，跟编译器根据函数的复杂度判断是否可以内联的，如果帧的内联了其优化会节省函数调用开始和计算次数开销。Template模板会根据template class使用情况生成代码，生成步骤是先判断是否有被使用-&amp;gt;生成代码-&amp;gt;去除重复。想要有RTTI执行期类型识别，则必须有额外的信息空间支持，即在C++中我们必须有虚继承或基类有虚继承才能获得RTTI的功能，甚至我们可以自己写一个执行期类型识别功能来替代标准C++，有了执行期类型识别功能，我们在向下转换时才更加安全，即dynamic_cast调用时先判断是否类型一致或者是父类的情况，再转换否则为NULL。&lt;/h4&gt;

</content>
 </entry>
 
 <entry>
   <title>安卓性能分析工具Simpleperf详解与应用</title>
   <link href="http://www.luzexi.com/2020/11/13/%E5%AE%89%E5%8D%93%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E5%B7%A5%E5%85%B7Simpleperf%E8%AF%A6%E8%A7%A3"/>
   <updated>2020-11-13T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/11/13/安卓性能优化工具Simpleperf详解</id>
   <content type="html">&lt;h3 id=&quot;本文关注三个问题simpleperf的工作原始里是什么simpleperf该如何使用它如何在unity项目上使用&quot;&gt;本文关注三个问题：Simpleperf的工作原始里是什么？Simpleperf该如何使用？它如何在Unity项目上使用？&lt;/h3&gt;

&lt;p&gt;关注Simpleperf的缘由是，虽然一直使用Xcode Intruments 、 Unity profiler 、Perfdog、以及一些自制的工具（自定义打点+内存快照）来做性能分析，但一直缺少专门针对安卓设备的的函数耗时分析。偶尔间搜到Simpleperf这么好的分析工具，于是开始研究 Simpleperf，最后把它用到项目中去并且建立起日常性能监控流水线。&lt;/p&gt;

&lt;h4 id=&quot;simpleperf是android开源项目aosp的一部分-是一个-cpu-性能剖析工具可以剖析-android-客户端-java-和-c-代码是-android-ndk-工具的一部分其包含两部分simpleperf可执行文件命令行和python脚本&quot;&gt;Simpleperf是Android开源项目（AOSP）的一部分， 是一个 CPU 性能剖析工具，可以剖析 Android 客户端 Java 和 C++ 代码，是 Android NDK 工具的一部分。其包含两部分：Simpleperf可执行文件（命令行）和python脚本。&lt;/h4&gt;

&lt;p&gt;python脚本整合了Simpleperf可执行文件（命令行）和adb的功能，让Simpleperf使用起来更加方便快捷。&lt;/p&gt;

&lt;p&gt;Simpleperf中可执行文件：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/simpleperf/image7.png&quot; alt=&quot;1&quot; /&gt;
&lt;img src=&quot;/assets/simpleperf/image21.png&quot; alt=&quot;2&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Simpleperf中整合了命令行的Python脚本：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/simpleperf/image23.png&quot; alt=&quot;3&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;simpleper可执行文件支持安卓50及以上的系统但python脚本只支持安卓70及以上的系统&quot;&gt;Simpleper可执行文件支持安卓5.0及以上的系统，但python脚本只支持安卓7.0及以上的系统。&lt;/h4&gt;

&lt;p&gt;这里的Python脚本不仅封装了命令行可执行文件的操作，同时提供了生成测试数据报告，线程消耗图，火焰调用图等功能。由于数据报告生成方面，Simpleperf命令行本身只支持文本数据报告的生成，因此要生成可视化的数据报告，还得依靠python。&lt;/p&gt;

&lt;h4 id=&quot;所以我把数据分析和数据报告生成给拆分开来直接用可执行文件去执行性能分析的任务让simpleperf可以支持更低的安卓系统性能分析完毕后再用python脚本对所得的数据文件来生成可视化的数据报告以及文本数据报告&quot;&gt;所以我把数据分析和数据报告生成给拆分开来，直接用可执行文件去执行性能分析的任务，让Simpleperf可以支持更低的安卓系统。性能分析完毕后，再用python脚本对所得的数据文件来生成可视化的数据报告，以及文本数据报告。&lt;/h4&gt;

&lt;h4 id=&quot;其中数据报告也分成两部分一部分是文本数据报告在生成文本数据报告后对文本数据报告中的数据再加工和筛选筛选出关键的信息上传到性能数据平台作为每日的性能日常报告另一部分是火焰图和消耗图的数据报告用于查看更细致的函数消耗分析&quot;&gt;其中，数据报告也分成两部分，一部分是文本数据报告，在生成文本数据报告后，对文本数据报告中的数据再加工和筛选，筛选出关键的信息上传到性能数据平台作为每日的性能日常报告，另一部分是火焰图和消耗图的数据报告，用于查看更细致的函数消耗分析。&lt;/h4&gt;

&lt;p&gt;我用一个demo做实验，demo的程序就是每帧运行1000w次浮点数和随机数计算，作为实验性能分析数据：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/simpleperf/image3.png&quot; alt=&quot;4&quot; /&gt;&lt;/p&gt;

&lt;p&gt;以下展示的是性能报告生成后的火焰图和文本数据报告：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/simpleperf/image19.png&quot; alt=&quot;5&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/simpleperf/image12.png&quot; alt=&quot;6&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/simpleperf/image9.png&quot; alt=&quot;7&quot; /&gt;&lt;/p&gt;

&lt;p&gt;下面就来详细介绍一下，如何使用Simpleperf来做性能分析。&lt;/p&gt;

&lt;h3 id=&quot;功能概要&quot;&gt;功能概要&lt;/h3&gt;

&lt;p&gt;Simpleperf主要功能分为事件摘要（stat），记录样本(record)和生成数据报告(report)三个功能。stat功能给出了在一个时间段内被分析的进程中发生了多少事件的摘要。record功能必须在Android系统中运行，当Simpleperf运行分析时会不断将数据写入到性能数据文件，所以它可以随时停止，随时拷贝分析数据文件。分析完毕后我们可以需要将输出数据文件拷贝到PC上，再使用report功能解析成数据报告。&lt;/p&gt;

&lt;h3 id=&quot;前提条件&quot;&gt;前提条件&lt;/h3&gt;

&lt;p&gt;Simpleperf需要有权限去做性能采样，所以想要使用Simpleperf做性能分析，需要满足4个条件中的一个就够了。&lt;/p&gt;

&lt;p&gt;1.debug版本，即在manifest中设置了android::debuggable=”true”，并且允许JNI 测试，并且C/C++没有被编译器优化过。也就是Unity构建的Development Build版本。&lt;/p&gt;

&lt;p&gt;2.release版本，如果安卓10以上则需要manifest中加入&lt;profileable android:shell=&quot;true&quot;&gt;&lt;/profileable&gt;就可以。&lt;/p&gt;

&lt;p&gt;3.release版本，如果是安卓8以上则在manifest中加入&amp;lt;application android::debuggable=”true” …&amp;gt;后，把wrap.sh 放入 lib/arch 文件夹中。 wrap.sh 会在没有debug标志给ART的情况下跑app。&lt;/p&gt;

&lt;p&gt;4.release版本，你的手机是root过的，有root权限Simpleperf就会畅通无阻。&lt;/p&gt;

&lt;h3 id=&quot;底层原理&quot;&gt;底层原理&lt;/h3&gt;

&lt;p&gt;现代CPU具有一个硬件组件，称为性能监控单元(PMU)。PMU具有一些硬件计数器，计数一些诸如经历了多少次CPU周期，执行了多少条指令，或发生了多少次缓存未命中等事件。&lt;/p&gt;

&lt;p&gt;Linux内核将这些硬件计数器包装到硬件perf事件 (hardware perf events)中。此外，Linux内核还提供了独立于硬件的软件事件和跟踪点事件。Linux内核通过 perf_event_open 系统调用将这些都暴露给了用户空间，这正是simpleperf所使用的机制。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/simpleperf/image25.png&quot; alt=&quot;8&quot; /&gt;&lt;/p&gt;

&lt;p&gt;linux系统在各个层都封装了一套性能接口。图中perf字样的接口主要位于CPU层，系统调用层，系统库层，调度层，内存层中。我们通过simpleperf list可以查看受支持的事件类型，有硬件事件类型和软件事件类型之分。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/simpleperf/image6.png&quot; alt=&quot;9&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/simpleperf/image1.png&quot; alt=&quot;10&quot; /&gt;&lt;/p&gt;

&lt;p&gt;硬件事件借助现代CPU中的PMU（性能监控单元）部件实现采样，比如cpu-cycle，cache-miss等硬件级数据，kernel会开启PMU的计数器去采集对应进程的数据。&lt;/p&gt;

&lt;h5 id=&quot;pmu是cpu中的部件专门用于性能监控cpu在运行时可以收集关于处理器和内存的各种统计信息对于处理器来说这些统计信息中的事件非常有用这样我们可以利用它们来调试或者剖析代码&quot;&gt;PMU是CPU中的部件，专门用于性能监控，CPU在运行时可以收集关于处理器和内存的各种统计信息。对于处理器来说这些统计信息中的事件非常有用，这样我们可以利用它们来调试或者剖析代码。&lt;/h5&gt;

&lt;h5 id=&quot;软件事件则是系统内核kernel层自行实现统计操作系统相关性能事件在各个功能模块中如内存对齐断层事件线程context-switch上下文切换事件cpu时钟事件cpu迁移事件页断层事件等等&quot;&gt;软件事件则是系统内核kernel层自行实现，统计操作系统相关性能事件在各个功能模块中，如内存对齐断层事件、线程context-switch上下文切换事件、cpu时钟事件、cpu迁移事件、页断层事件等等。&lt;/h5&gt;

&lt;p&gt;事实上SimplePerf就是通过linux系统的perf_event_open接口来获得这些perf数据。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/simpleperf/image17.png&quot; alt=&quot;11&quot; /&gt;&lt;/p&gt;

&lt;p&gt;linux内核perf_event_open接口 https://www.man7.org/linux/man-pages/man2/perf_event_open.2.html&lt;/p&gt;

&lt;h3 id=&quot;那么stat和record是如何工作的呢&quot;&gt;那么stat和record是如何工作的呢？&lt;/h3&gt;

&lt;h4 id=&quot;stat命令给出时&quot;&gt;Stat命令给出时&lt;/h4&gt;

&lt;p&gt;1.simpleperf调用linux系统接口启用perf分析。&lt;/p&gt;

&lt;p&gt;2.设置监控事件和周期。&lt;/p&gt;

&lt;p&gt;3.Linux 内核在调度到被分析进程时启用计数器。&lt;/p&gt;

&lt;p&gt;4.simpleperf从内核读取计数器，并报告计数器摘要。&lt;/p&gt;

&lt;p&gt;cmd_stat.cpp 471行 StatCommand::Run&lt;/p&gt;

&lt;p&gt;https://android.googlesource.com/platform/system/extras/+/master/simpleperf/cmd_stat.cpp&lt;/p&gt;

&lt;h4 id=&quot;record-命令给出时&quot;&gt;Record 命令给出时&lt;/h4&gt;

&lt;p&gt;1.Simpleperf通过对linux内核接口调用加入监控，锁定监控进程对象。&lt;/p&gt;

&lt;p&gt;2.调用linux系统接口启用perf分析。&lt;/p&gt;

&lt;p&gt;3.Simpleperf在simpleperf 和 linux 内核之间创建共享映射缓冲区。&lt;/p&gt;

&lt;p&gt;4.设置需要监控的事件和采样频率，告诉linux 内核将样本数据转储到映射缓冲区。&lt;/p&gt;

&lt;p&gt;5.开启线程不断监控共享映射缓冲区，从映射缓冲区读取数据并写入到数据文件。&lt;/p&gt;

&lt;p&gt;cmd_record.cpp 455行 RecordCommand::PrepareRecording&lt;/p&gt;

&lt;p&gt;https://android.googlesource.com/platform/system/extras/+/master/simpleperf/cmd_record.cpp&lt;/p&gt;

&lt;h3 id=&quot;这里有2个重要的节点我们深入了解下这两个重点能映射出simpleperf是如何在linux系统工作的&quot;&gt;这里有2个重要的节点我们深入了解下，这两个重点能映射出Simpleperf是如何在Linux系统工作的？&lt;/h3&gt;

&lt;h3 id=&quot;第一个它是如何与linux操作系统通信的&quot;&gt;第一个，它是如何与Linux操作系统通信的？&lt;/h3&gt;

&lt;p&gt;通常进程之间通信都是通过共享内存进行，Simpleperf通过创建映射缓冲区来与系统共享一块内存。那么它是怎么创建和映射的呢？&lt;/p&gt;

&lt;p&gt;我们来看看源码中的关键函数，CreateMappedBuffer创建映射缓冲区，其中mmap为Linux系统接口，调用它使得进程之间通过映射同一块内存(或文件)实现共享内存。
Simpleperf调用mmap创建指定的共享内存大小，设置读写权限，设置共享标记，以及设置通道句柄。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/simpleperf/image13.png&quot; alt=&quot;12&quot; /&gt;&lt;/p&gt;

&lt;p&gt;有了共享内存，接着Simpleperf需要告诉与系统把性能数据放入共享缓冲中去。&lt;/p&gt;

&lt;p&gt;其中ioctl为Linux系统接口，是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理，就是对设备的一些特性进行控制，例如串口的 传输波特率、马达的转速等等。&lt;/p&gt;

&lt;p&gt;Simpleperf调用ioctl告诉Linux系统将性能数据写入通道缓存。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/simpleperf/image2.png&quot; alt=&quot;13&quot; /&gt;&lt;/p&gt;

&lt;p&gt;ioctl调用格式为：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;int ioctl(int fd, ind cmd, …)；
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们可以通过ioctl的命令码(cmd)告诉Linux驱动程序我们想做什么，至于怎么解释这些命令和怎么实现这些命令，这都是驱动程序要做的事情。
现在我们使用cmd命令码PERF_EVENT_IOC_SET_OUTPUT，告诉系统让内核把性能相关事件数据放入指定缓冲中。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/simpleperf/image20.png&quot; alt=&quot;14&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;第二个它是如何得到linux操作系统的性能信息的&quot;&gt;第二个，它是如何得到Linux操作系统的性能信息的？&lt;/h3&gt;

&lt;p&gt;Simpleperf通过创建一个线程来监控数据并记录数据，它就是RecordReadThread。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/simpleperf/image11.png&quot; alt=&quot;14&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这个线程也会不断的接收用户的命令输入，来暂停和恢复数据记录。&lt;/p&gt;

&lt;p&gt;创建线程后，接着实例化需要监控的事件，并开始记录和监控。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/simpleperf/image10.png&quot; alt=&quot;15&quot; /&gt;&lt;/p&gt;

&lt;p&gt;此线程不断循环监控共享内存的状况，同时记录性能数据：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/simpleperf/image24.png&quot; alt=&quot;16&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;第一个问题中我们说simpleperf通过linux系统接口mmap申请共享内存再通过linux系统接口ioctl告诉内核把性能监控的数据放在共享内存中由于linux系统在各个层都封装了一套perf性能接口于是在接下来的程序运行中linux内核将这些perf事件-perf-events数据放入simpleperf设置的共享内存中而simpleperf又开启了一个线程来不断监控是否有数据加入并将它们放入自己缓存中去最后再通知主线程适当的时候将这些数据写入本地文件&quot;&gt;第一个问题中我们说，Simpleperf通过Linux系统接口mmap申请共享内存，再通过Linux系统接口ioctl告诉内核把性能监控的数据放在共享内存中。由于Linux系统在各个层都封装了一套perf性能接口，于是在接下来的程序运行中，Linux内核将这些perf事件 (perf events)数据放入Simpleperf设置的共享内存中。而Simpleperf又开启了一个线程来不断监控是否有数据加入，并将它们放入自己缓存中去，最后再通知主线程适当的时候将这些数据写入本地文件。&lt;/h4&gt;

&lt;h2 id=&quot;执行性能分析&quot;&gt;执行性能分析&lt;/h2&gt;

&lt;h3 id=&quot;record-命令行说明&quot;&gt;record 命令行说明&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/assets/simpleperf/image16.png&quot; alt=&quot;17&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;性能分析命令执行步骤&quot;&gt;性能分析命令执行步骤&lt;/h3&gt;

&lt;p&gt;现在我们用Simpleperf命令行的形式来对安卓系统进行C/C++的性能分析：&lt;/p&gt;

&lt;p&gt;1.将simpleperf可执行文件放入安卓系统&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;adb push .\ndk21\simpleperf\bin\android\arm64\simpleperf /data/local/tmp
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;2.将可执行文件设置为可执行文件&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;adb shell chmod a+x /data/local/tmp/simpleperf
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;3.开始执行分析，这里设定一个99秒的持续时间。也可以设定为99999秒，然后用杀进程的方式来终止分析。因为我们前面说过，simpleperf是逐步写入数据文件的，数据文件随时保持完整性。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;adb shell /data/local/tmp/simpleperf record -o /data/local/tmp/perf.data -g --app com.xxx.xxx --duration 99 -f 800
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;4.停止分析&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;adb shell pkill -l 2 simpleperf
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;5.将分析数据文件拷贝出来&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;adb pull /data/local/tmp/perf.data .\report_data\report\perf.data
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;生成数据报告步骤&quot;&gt;生成数据报告步骤&lt;/h3&gt;

&lt;p&gt;有了Simpleperf生成的性能数据后，我们就可以对这份性能数据文件进行分析并生成数据报告。&lt;/p&gt;

&lt;h4 id=&quot;生成报告前我们首先需要准备符号表&quot;&gt;生成报告前，我们首先需要准备符号表。&lt;/h4&gt;

&lt;h4 id=&quot;为什么要准备符号表呢因为这样就可以通过分析数据中调用地址映射到具体的函数名这些都由simpleperf帮我们完成只是我们需要为此准备这个带符号表的so&quot;&gt;为什么要准备符号表呢，因为这样就可以通过，分析数据中调用地址映射到具体的函数名。这些都由Simpleperf帮我们完成，只是我们需要为此准备这个带符号表的so。&lt;/h4&gt;

&lt;p&gt;Unity引擎带符号表so的放在Unity的安装目录下&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/simpleperf/image8.png&quot; alt=&quot;1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;打完apk包后带符号的il2cpp的so在项目的Temp目录下面：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/simpleperf/image5.png&quot; alt=&quot;1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;有了这些准备接下我们来开始讲生成数据报告的操作步骤：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/simpleperf/image14.png&quot; alt=&quot;1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;1.准备符号表&lt;/p&gt;

&lt;p&gt;把包地址打印出来，因为真正的包地址可能会在你知道的包名后面加些字符串&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;adb shell pm path com.xxxx.xxx &amp;gt; tmp_appinfo.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;把符号表拷贝过去到./binary_cache/data/app/com.xxx.xxxx/lib/arm(或者arm64)/里去&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;python:
cp_src1 = 'libil2cpp.so.debug'
cp_target1 = './binary_cache/data/app/com.xxx.xxxx/lib/arm/libil2cpp.so'
if os.path.exists(cp_src1):
    shutil.copy(cp_src1, cp_target1)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这里只拷贝了il2cpp的，至少还需要拷贝带符号的libunity.so。符号表是simpleperf用来查找调用函数名的。&lt;/p&gt;

&lt;h4 id=&quot;simpleperf是怎么查到函数名的呢它是通过调用地址与符号表中的函数名对应关系来获得所调用的函数名的我们可以用simpleperf解析后数据打开文本数据获得调用地址来查看到底是怎么回事&quot;&gt;Simpleperf是怎么查到函数名的呢，它是通过调用地址与符号表中的函数名对应关系来获得所调用的函数名的。我们可以用Simpleperf解析后数据，打开文本数据获得调用地址，来查看到底是怎么回事。&lt;/h4&gt;

&lt;p&gt;举个例子，我们查看数据报告中的88.31%的耗时占比函数调用库指令地址为83cb7c：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/simpleperf/image18.png&quot; alt=&quot;1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在汇编工具IDA中加载libil2cpp带符号的so文件，查看到83cb7c地址恰好是在Random调用地址上：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/simpleperf/image4.png&quot; alt=&quot;1&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;也就是说有8831的消耗在randomrange这个函数上simpleperf就是通过指令调用地址来获取符号表中的函数名的通过调用地址和符号表上的函数名对齐后得到最终的函数名&quot;&gt;也就是说，有88.31%的消耗在Random.Range这个函数上。Simpleperf就是通过指令调用地址来获取符号表中的函数名的，通过调用地址和符号表上的函数名对齐后得到最终的函数名。&lt;/h4&gt;

&lt;p&gt;2.生成普通数据报告&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;python .\ndk21\simpleperf\report.py -i .\perf.data -o .\report.txt -n --full-callgraph --symfs .\binary_cache
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;3.生成调用者耗时分布数据报告&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-shs&quot;&gt;python .\ndk21\simpleperf\report.py -i .\perf.data -o .\report.caller.txt -g caller --full-callgraph --symfs .\binary_cache
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;4.生成被调用者耗时分布数据报告&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;python .\ndk21\simpleperf\report.py -i .\perf.data -o .\report.callee.txt -g callee --full-callgraph --symfs .\binary_cache
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;最后生成的文本数据如下图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/simpleperf/image9.png&quot; alt=&quot;1&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;生成火焰图和消耗图&quot;&gt;生成火焰图和消耗图&lt;/h3&gt;

&lt;p&gt;1.用simpleperf提供的report_html.py生成可视图&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;python .\ndk21\simpleperf\report_html.py -i .\perf.data -o .\report.html --binary_filter .\binary_cache
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;2.拷贝可视图网页需要用到的js文件&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;copy  &quot;.\ndk21\simpleperf\report_html.js&quot; &quot;.\report_html.js&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&quot;/assets/simpleperf/image12.png&quot; alt=&quot;1&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;日常性能监控流程建设&quot;&gt;日常性能监控流程建设：&lt;/h3&gt;

&lt;p&gt;那么我们是否可以将Simpleperf运用到日常性能监控中去呢？&lt;/p&gt;

&lt;p&gt;经过Simpleperf的性能分析后，我们得到了性能数据文本数据，我们可以对这个文本类型的数据文件再解析，然后将我们需要的重要的函数性能数据加入到后台数据库中，并在每日自动化性能测试报告中体现出来。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/simpleperf/image22.png&quot; alt=&quot;1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们来看看上图中，生成出来的性能数据格式。根据性能数据格式，百分比、采样次数、线程名、库文件名、调用函数名的格式，我们可以将数据解析到内存中，并用http的方式上传到web后台。web后台对数据加工后，可以用趋势图的形式在后台页面上展现出来。例如下面的样例图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/simpleperf/image15.png&quot; alt=&quot;1&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;参考文献&quot;&gt;参考文献：&lt;/h3&gt;

&lt;p&gt;Simpleperf官方地址：&lt;/p&gt;

&lt;p&gt;https://android.googlesource.com/platform/system/extras/+/master/simpleperf/doc/README.md#executable-commands-reference&lt;/p&gt;

&lt;p&gt;Simpleperf源码地址：&lt;/p&gt;

&lt;p&gt;https://android.googlesource.com/platform/system/extras/+/master/simpleperf/&lt;/p&gt;

&lt;p&gt;《SimplePerf 安卓客户端性能剖析及自动化性能测试》&lt;/p&gt;

&lt;p&gt;http://km.oa.com/articles/show/466087?kmref=search&amp;amp;from_page=1&amp;amp;no=1&lt;/p&gt;

&lt;p&gt;《Simpleperf介绍》&lt;/p&gt;

&lt;p&gt;https://blog.csdn.net/tq08g2z/article/details/77311712&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>向内看自己(九) 慌乱的内心世界</title>
   <link href="http://www.luzexi.com/2020/11/04/%E5%90%91%E5%86%85%E7%9C%8B%E8%87%AA%E5%B7%B19"/>
   <updated>2020-11-04T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/11/04/向内看自己9</id>
   <content type="html">&lt;p&gt;来深圳快1年了，我以为我会适应的很快，但恰恰相反。我感觉自己像一个惊弓之鸟，被很多事情震惊到了，包括，今年的疫情、远离亲人、陌生的城市、周围很多牛人、项目重要而又紧迫、孩子们慢慢长大了、感觉自己肩负的责任又重了一些。&lt;/p&gt;

&lt;p&gt;好几次我都迫不及待的坐飞机回家看看老婆孩子，多休息几天跟她们多呆几天是几天，感觉自己在逃避。&lt;/p&gt;

&lt;p&gt;这让我想起了幼年时自己被送去武术学校的那段回忆。父母想给我一些锻炼的机会，他们打听到10公里外有个武术学校可以在暑假时间里收一些学生锻炼一下意志力，于是将我送去了那里。那段时间我很害怕，不知道为何，感觉自己被丢到了一个无人关心的地方，虽然后来我慢慢适应了，但两星期后还是忍不住逃了回来。&lt;/p&gt;

&lt;p&gt;这段回忆好像我现在的样子，恐慌不安，犹如惊弓之鸟，童年的阴影困住了我。所以有时我很苦恼，像是被自己所设的陷阱困住了。&lt;/p&gt;

&lt;p&gt;​长大后我才知道每个人都有这样那样大大小小的童年阴影，起先我一直责怪父母给我了一个不好的童年，自从自己当了父母，特别是看了《自卑与超越》之后，才明朗了些，才知道这些童年阴影无法避免，作为父母在这个社会生存，很多时候也是生不由己，没有人是容易的。&lt;/p&gt;

&lt;p&gt;现在轮到自己了，我成为了别人的父母。我已经在社会上独立了，但仍然被这个社会中的小事所很震惊，我想对于这种恐慌我的父母在我这个岁数时也有过同样的经历。&lt;/p&gt;

&lt;p&gt;想到这一层时，我对自己孩子的教育和关心有更加深刻的理解，我不想犯父母的错误，同时很多错误也无法避免。我该如何去补救和行动，我想我应该更努力的去关心她们，感受她们的心灵，并引导她们往好的方向发展。虽然我在很远的地方不能陪伴她们，但我想我也可以做些力所能及的事情作为补救。&lt;/p&gt;

&lt;p&gt;说到自己恐慌，大多数时候是因为压力导致的，压力导致焦虑，焦虑又破坏了我原本的行动准则，没有了行动准则就变得紊乱没有方向。&lt;/p&gt;

&lt;p&gt;其实都是自己的问题，是我忘了初心。&lt;/p&gt;

&lt;p&gt;​于是我深深的问了自己一下，我的初心是什么？第一反应给我的答案是，我的初心是学习，学习是我最大的愿望，我很享受学习的时间，包括学习如何学习，学习具体的知识和技能，以及学习如何运用所学到的。&lt;/p&gt;

&lt;p&gt;我希望自己不忘初心，不断学习，学习知识，学习心态，学习与他人交流，学习如何认识自己等等等等，这是我最终想要的。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>给女儿写信(五)  放平心态</title>
   <link href="http://www.luzexi.com/2020/11/02/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A15"/>
   <updated>2020-11-02T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/11/02/给女儿的信5</id>
   <content type="html">
&lt;hr /&gt;

&lt;p&gt;导读：喜欢跟孩子们一起，可惜一直在外地工作，因此我给孩子写点信并录成视频，跟孩子聊聊自己的故事以及最近的感受&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Hi Sharon Anne，又有1个月没见到你们，爸爸好想你们。&lt;/p&gt;

&lt;p&gt;爸爸最近工作很忙，也碰到了一些问题，想跟你们说说。爸爸的这家公司里有很多比爸爸厉害很多的人，每天他们都跟爸爸一起做事情。&lt;/p&gt;

&lt;p&gt;他们很厉害，懂的比爸爸多，爸爸有段时间很难受，因为爸爸觉得比不过他们，所以显得很垂头丧气的。&lt;/p&gt;

&lt;p&gt;一段时间后，慢慢地爸爸想明白了。其实你看，这个世界上比我厉害的人很多，我能遇上他们，并跟他们一起工作是爸爸的荣幸，所以爸爸要乘这个机会多向他们学习一下。&lt;/p&gt;

&lt;p&gt;现在爸爸常常跟他们交流，学习他们身上的优点，并且平时的时候自己也在努力的看书，为了巩固知识，也会写一些文章做一下总结。&lt;/p&gt;

&lt;p&gt;虽然现在爸爸还是没他们厉害，但爸爸相信我会慢慢赶上来的。&lt;/p&gt;

&lt;p&gt;Sharon，Anne，你们也会遇到很多比你们厉害的人，这个时候不要慌张哦，因为这是很正常的，要向爸爸这样，多向他们学习，并且自己也要努力看书学习哦，这样我们就能慢慢赶上他们。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(五十四) 系统性的学习</title>
   <link href="http://www.luzexi.com/2020/11/01/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A854"/>
   <updated>2020-11-01T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/11/01/思路探讨54</id>
   <content type="html">&lt;p&gt;前面很多文章对人生哲理的认知比较多，但对系统性的知识则却没有几篇。这也是最近几年犯的错误之一，忙于吸收一些零散的知识，却没有形成体系。&lt;/p&gt;

&lt;h4 id=&quot;把看别人写的文章当作吸收知识的重要来源是我最近几年所犯的重要错误之一&quot;&gt;把看别人写的文章当作吸收知识的重要来源，是我最近几年所犯的重要错误之一。&lt;/h4&gt;

&lt;p&gt;我们写的文章大部分都是写给自己的总结（即使记者在撰写实时新闻时也会夹带有自己的观点和情绪），在写文章的同时总结了我们学到的知识，这些知识无论从书本上还是实践中的来，最后总结完毕后的文章第一个看的人是我们自己，所以从某种意义上来说我们写的文章都是我们写给自己看的，这使得我们可以让自己的知识在脑袋中有了一次整理和巩固的机会，然后这篇文章才顺带的分享给大家，让大家能够知道我们在这个学习过程中经历了什么，有什么样的体会，知识的重点是什么，这对于写文章的人帮助很大。&lt;/p&gt;

&lt;p&gt;对于看文章的人，其吸收的知识其实微乎其微。这句话我一开始也不是很明白，为什么我看别人写的精彩的技术文章就没什么用呢？为什么我看别人写的文章的时候，你怎么知道我吸收的知识就微乎其微了呢？后来我才慢慢发现，这些大部分文章无论是新闻还是技术相当于一种资讯，就像我们每天在xx新闻里看的文章那样，写的都是天下发生的事，可能你觉得新奇会去看一下，但其具体的知识无法进入到你的脑袋中。&lt;/p&gt;

&lt;p&gt;为什么呢？因为你没有系统性的去学习过它，你只是片面的从别人的口中知道了这么一件事。比如某个专家在文章中提到，xx编程语言效率低，你可能就记住了，但其实他讲了很多关于为什么这个语言是低效的内容，你却很难记住，因为这不是你的经历，你无法在这么短的文章中体会到他的所有成功和失败的经历。但当有人问你的时候，你也这么告诉他，就像专家那样。可是你一直没有去研究为什么这种编程语言效率低，没有去系统性的学习编程语言是怎么设计的，是怎样编译成机器码的，在执行时是怎么运行的。所以你的脑袋里只有这么一句话，xx编程语言是低效的。&lt;/p&gt;

&lt;h4 id=&quot;如果你没有从上到下从底层到设计系统性的去学习知识我们可能会迷失在这个充满知识的海洋里&quot;&gt;如果你没有从上到下，从底层到设计，系统性的去学习知识，我们可能会迷失在这个充满知识的海洋里。&lt;/h4&gt;

&lt;p&gt;系统性学习和零散的看些文章的区别，本质上围绕的依然是高效。我们关注的是，“如何才能高效的学习？”和“如何学习才高效？”。答案可能很长，包括且不局限于，练习的技巧、精力管理、目标管理、阅读技巧、心态的调整、教导他人时的方法、自我总结的技巧、以及与他人交流方式等等等。有很多我们需要我们学习的东西，只是这么多东西如果我们抓不住重点，就会迷路。&lt;/p&gt;

&lt;p&gt;就如同我最近在健身中学到的那样，如果我们没有目标的去锻炼肌肉，则通常都是东锻炼一下西锻炼一下，很难形成有效的可见的成果，大部分人都在锻炼一阵后由于看不到成效放弃了健身。我也这样没有目标的锻炼了很久，直到最近我才开始反思，我在想“怎样才能让我锻炼的成果放大”。于是我开始系统性的学习人体的肌肉组成结构，肌肉增长的原理，塑形减脂的原理，慢慢的我去健身时也更加注重肌肉的局部锻炼，把有限的时间花费在几个重要的肌肉锻炼上，饮食上也更加注重搭配和克制，肌肉的增长效果显著。自己看到了成果，就会更加努力学习，向着目标前进。&lt;/p&gt;

&lt;h4 id=&quot;最后总结下零散的知识用处小效率差误导多我们需要系统性的学习某个领域的知识从底层开始因为系统性的知识能带领我们看透事物的本质和底层逻辑这让我们进入一个新的世界一个能抓住重点从而更加高效的世界尽可能自己写文章而不是期盼依靠别人的文章来获得知识&quot;&gt;最后总结下，零散的知识用处小效率差误导多，我们需要系统性的学习某个领域的知识（从底层开始），因为系统性的知识能带领我们看透事物的本质和底层逻辑，这让我们进入一个新的世界，一个能抓住重点从而更加高效的世界。尽可能自己写文章，而不是期盼依靠别人的文章来获得知识。&lt;/h4&gt;

</content>
 </entry>
 
 <entry>
   <title>向内看自己(八) 团队合作、冥想、精力</title>
   <link href="http://www.luzexi.com/2020/10/25/%E5%90%91%E5%86%85%E7%9C%8B%E8%87%AA%E5%B7%B18"/>
   <updated>2020-10-25T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/10/25/向内看自己8</id>
   <content type="html">&lt;p&gt;向内看自己，看自己内心的所思所想，找出问题并逐步纠正它。&lt;/p&gt;

&lt;p&gt;很佩服公司里很多同事，他们拥有很好的合作能力，知道如何更加有效的合作，让集体的力量放大很多倍。&lt;/p&gt;

&lt;p&gt;我知道自己有些许性格上的缺陷，这会使得我在平时的工作和生活中与他人合作起来会有障碍，从而导致了团队磨合期较长。这让我万分痛苦，因为年纪越大越知道团队的力量有多重要。&lt;/p&gt;

&lt;p&gt;团队凝聚的力量可以让大事情大难题分解成不同的小事情和小问题，每个小问题都由某成员来处理，综合起来就像，单车道变成了八车道那样并行处理和推进，让整个进度变得更快，让原本看似大的难题被团队良好的合作和分工，分解成了小问题并迅速消化掉。&lt;/p&gt;

&lt;p&gt;每个人擅长的领域不一样，将这些不同领域的人才聚集起来并且形成有效的合作才能迸发出巨大的创造力。团队中有些人擅长分解难题，有些人擅长某个领域的难题，有些人擅长在不同难题领域之间的衔接，难题被分解成各种小领域并在领域之间有人衔接，这是团队分工合作达成大目标的关键。&lt;/p&gt;

&lt;p&gt;​如果人没有合作意识，就会使得团体的效率大大降低，大事办不成，小事争执不断，一盘散沙。&lt;/p&gt;

&lt;h4 id=&quot;团队这个概念无处不在除了公司里的团队外我们还有家庭班级球队兴趣班等等这些团队都需要我们认真对待培养起自己良好的合作观念如何配合队友如何有效分工如何衔接队友与队友之间的成果以及如何培养团队感情一辈子都要学习如何有效合作&quot;&gt;团队这个概念无处不在，除了公司里的团队外，我们还有，家庭、班级、球队、兴趣班等等。这些团队都需要我们认真对待，培养起自己良好的合作观念，如何配合队友，如何有效分工，如何衔接队友与队友之间的成果，以及如何培养团队感情。一辈子都要学习如何有效合作。&lt;/h4&gt;

&lt;hr /&gt;

&lt;p&gt;前段时间压力很大，所以一直睡不着觉，精力也随之变差，工作效率也随之下降，形成了一个恶性循环。失眠一直是我的一个大问题，我相信也是所有在大城市拼搏的人的问题，为了解决这个难题，大家都尝试过很多种方法，我也是。我最近在看《十分钟冥想》（后面看完后会写个简版的笔记），这本书里面讲的些内容让我豁然开朗。&lt;/p&gt;

&lt;p&gt;他说我们睡不着的人大多数都是在想，今天发生的事，以前发生的事，明天即将发生的事，或者今天的困难、明天的困难，总之我们在睡觉时一直在想这想那，注意力不在睡觉上。这是睡不着的主要原因，想太多，脑袋和身体一直处于紧绷状态。到了后半夜当发现我们发现想的太多了，很累的时候想让自己睡觉，就拼命的去抗拒它，试图让脑袋不要去想。但大脑由不得我们，你越抗拒，就越被这种抗拒而不得的情绪折磨。于是在你脑袋中又多了一种运作程序，即抗拒。&lt;/p&gt;

&lt;h4 id=&quot;解决方法是不要抗拒而选择旁观你不要试图去控制它们而是成为一个旁观者就像你在公交站等车时看着路边来来往往的车你不会有兴趣去挡住它们你只是注视着它们来了走了又来了又走了当你成为这样一个傍观者时你会发现这些来来往往的想法很无聊于是慢慢的你的脑袋开始放空最后宁静如水&quot;&gt;解决方法是，不要抗拒而选择旁观。你不要试图去控制它们，而是成为一个旁观者，就像你在公交站等车时看着路边来来往往的车，你不会有兴趣去挡住它们，你只是注视着它们来了走了，又来了又走了。当你成为这样一个傍观者时，你会发现这些来来往往的想法很无聊，于是慢慢的你的脑袋开始放空，最后宁静如水。&lt;/h4&gt;

&lt;p&gt;这就是冥想的过程，做一个自己脑袋的旁观者，让心宁静如水，清澈见底。&lt;/p&gt;

&lt;p&gt;这个过程里有一些技巧，我会在后面的文章中详细解剖一下，我们如何掌握这些技巧，让自己可以随时保持宁静，排除噪音，去除烦恼。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;除了冥想，平时里由于事情太多太杂，特别是在有了孩子后，生活的复杂度上升了一个台阶。生活的复杂度压垮了我很多次，脑袋用不过来。于是我从老婆大人那里学了一个技巧，把自己脑袋里想的都记下来，为脑袋腾出更多空间干别的事。把事都记下来，那样就不会占用你的脑袋了，脑袋里的东西太多，需要有地方放，要么完结掉，要么存起来，我选择存起来，放在印象笔记里，等空了在翻开来想想怎么解决。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;前面有篇文章介绍《精力管理》的读书笔记，自己也在不断的践行中，最近又发现了很多问题。&lt;/p&gt;

&lt;p&gt;生活和工作中总有那么段时期是困难的时期，困难时期最需要我们用精力去支撑。但我发现，越是困难时期越容易对精力的节约和储备放松警惕，于是每次在困难时期都会胡乱的使用精力，去所谓的放松一下，比如玩游戏，ktv，抽烟喝酒等，其实这些反而消耗了精力，增加了负担。&lt;/p&gt;

&lt;p&gt;我也是一样，有段时间总会掉入这个陷阱，在最需要精力去面对困难时会选择去逃避一下，把精力浪费在“轻松一下”上，使得第二天的没有体力，进而使得困难更加难以对付，逐步形成恶性循环，越放松越没有精力去应对困难，事情就变得越来越糟糕。&lt;/p&gt;

&lt;p&gt;最近我试着学习这个克制的技巧，让自己在最困难的时候告诉自己要克制要保存体力，在关键时刻不能浪费精力，锻炼一下并早早睡觉把精力补充的满满的用到第二天去面对困难。&lt;/p&gt;

&lt;p&gt;自从用了这个方法，我的情绪变的好多了，事情也变得更加顺利了，困难也慢慢迎刃而解。每次遇到紧张、压力、焦虑的心情时要保存自己的精力，并设法补充下自己的精力，今天解决不了的事情，就补充下精力到第二天去解决，让自己能有更多保持专注的时间和保持专注的精力去面对困难，&lt;/p&gt;

&lt;h4 id=&quot;坚守理智锻炼并补充精力以应对困难而不是随意的浪费让困难更难以对付&quot;&gt;坚守理智，锻炼并补充精力以应对困难，而不是随意的浪费，让困难更难以对付。&lt;/h4&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(十二) 《汇编语言》</title>
   <link href="http://www.luzexi.com/2020/10/18/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B012"/>
   <updated>2020-10-18T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/10/18/读书笔记12</id>
   <content type="html">&lt;p&gt;两个月前看完王爽著的《汇编语言》经过一段时间的休整，今天再次翻开来又有另一番滋味，趁着最近对底层原理这股热情，对书本的内容进行一些整理。&lt;/p&gt;

&lt;h4 id=&quot;我的初衷是希望更多的理解程序集在计算机中执行的原理我中意深度探索c对象模型里作者说的一句话他说我的经验告诉我如果一个程序员了解底层实现模型他就能够写出效率较高的代码自信心也比较高一个人不应该用猜的方式或是等待某大师的宣判才确定何时提供一个copy-constructor-而何时不需要这类问题的解答应该来自于我们自身对对象模型的了解&quot;&gt;我的初衷是希望更多的理解程序集在计算机中执行的原理，我中意《深度探索C++对象模型》里作者说的一句话，他说“我的经验告诉我，如果一个程序员了解底层实现模型，他就能够写出效率较高的代码，自信心也比较高。一个人不应该用猜的方式，或是等待某大师的宣判，才确定”何时提供一个copy constructor 而何时不需要“，这类问题的解答应该来自于我们自身对对象模型的了解”。&lt;/h4&gt;

&lt;p&gt;我们很多时候由于不了解底层的执行原来，导致我们在写代码的时候基本都靠猜，我们自认为的程序在计算机中的执行方式是这样的或是那样的。以前我猜过很长一段时间，但现在我不想猜了，我想了解所有关于计算机底层的执行原理，包括计算机设备的体系结构，计算机机器码的执行原理，面向对象模型的布局和执行原理。&lt;/p&gt;

&lt;h4 id=&quot;以下是我对书本内容的记录和理解&quot;&gt;以下是我对书本内容的记录和理解&lt;/h4&gt;

&lt;p&gt;我把这本书的理解分为，基础知识、总线、寄存器、寻址、跳转、中断，6个部分。书中写了很多案例，总53万字，我把这些知识精简一下，并说出我自己的理解。&lt;/p&gt;

&lt;h3 id=&quot;基础知识&quot;&gt;基础知识&lt;/h3&gt;

&lt;p&gt;我们知道机器指令是一列0和1构成二进制数字，为什么是0和1呢，因为它可以在设备中用高低电平表示。高低电平就是一个晶体管的两个状态，即有电流和没电流。&lt;/p&gt;

&lt;p&gt;一列010101二进制数字如果用数字电路来表示的话，就是一排晶体管。这排晶体管假如要做逻辑判断或计算的话，就需要涉及到逻辑门（即电流的走向逻辑），我们常接触到的逻辑门有，与（and）、非（not）、或（or）这三种逻辑门，除了这三种还有，与非门、或非门、反相器、异或门，通过这六种基础的逻辑门的组合，我们可以制造出很多很多复杂的逻辑门电路，比如加法数字电路、乘法数字电路等，计算器就是由众多逻辑门组合而成的，输入一排电流后得到一排电流的结果。&lt;/p&gt;

&lt;p&gt;由于0101形式的机器码，太难记忆，所以人们发明了汇编，它其实就是机器码的助记符，即某个10101的指令用一个英文符号来表示。因此汇编指令与机器指令的差别在于表示方式上，汇编是机器指令便于记忆的一种书写格式。我们程序员用汇编写出程序后，用汇编编译器将其翻译成机器码，再交由计算机执行。反过来也是一样，翻译完成的机器码（即一个可执行文件）也可以反过来翻译成汇编语言符号。&lt;/p&gt;

&lt;p&gt;我们常用的cpu处理器的发展过程，从8080到奔腾4的过程是，8080、8086/8088、80186、80286、80386、80486、奔腾、奔腾2、奔腾3、奔腾4.&lt;/p&gt;

&lt;p&gt;其中8086/8088开始被使用在了微机上，即我们现在的个人电脑。80386开始在微机上可以做多任务操作，一台个人电脑中的操作系统可以同时处理多项任务。虽然80286也具备了对多任务系统的支持，但它对8086/8088的兼容性支持比较差，所以过度非常困难，于是就有了80386，它既有多任务系统的功能也兼容了8086/8088。&lt;/p&gt;

&lt;h3 id=&quot;总线&quot;&gt;总线&lt;/h3&gt;

&lt;p&gt;CPU是计算机的核心，要让CPU工作就必须向它提供指令和数据，指令和数据存放在内存，因此内存的作用仅次于CPU。而磁盘则不同与内存，磁盘上的数据或程序集如果不读到内存中就无法被CPU使用，所以对CPU来说，磁盘其实是外部设备。计算机设备中除了CPU芯片外，其他设备中也有属于自己的芯片，比如内存芯片、磁盘芯片、键盘芯片、显卡芯片、网络芯片等等，这些芯片都有自己的寄存器，CPU可以通过这些寄存器与其他芯片进行交流。&lt;/p&gt;

&lt;p&gt;设备与CPU之间的交流都是数据交流（也就是不同高低平的电流），CPU在读写数据时要指明，它要对哪一个器件进行操作，进行哪种操作，是从中读出数据还是写入数据。&lt;/p&gt;

&lt;h4 id=&quot;那么cpu是通过什么将数据传到其他设备的芯片中去的呢在计算机中有专门连接cpu和其他芯片的导线电线或电流线我们称它为总线总线又分为3类地址总线控制总线数据总线&quot;&gt;那么CPU是通过什么将数据传到其他设备的芯片中去的呢？在计算机中有专门连接CPU和其他芯片的导线（电线或电流线），我们称它为总线。总线又分为3类，地址总线、控制总线、数据总线。&lt;/h4&gt;

&lt;p&gt;这3根总线的分类职责不同，地址总线用于指定内存单元地址，控制总线用于传输指令，数据总线用于传输数据。&lt;/p&gt;

&lt;p&gt;我们来举个例子，CPU要读取内存中3号单元的数据，CPU先通过地址线将地址信息3发给内存芯片，再通过控制总线发出读取内存的指令，指令中选中存储芯片，并通知它将要从中读取数据，最后内存芯片将3号单元中的数据通过数据线送入CPU。反过来，写的步骤也是类似，先通过地址总线将地址信息3发给内存芯片，再通过控制总线向内存芯片发出写入命令，最后通过数据总线将数据传入内心芯片。&lt;/p&gt;

&lt;h4 id=&quot;我们现在知道了地址总线能传输多少个二进制信息cpu就可以对多少个单元地址进行寻址数据总线也是一样有多少根决定了一次可传二进制数据的大小控制总线是个总称是一些不同控制线的集合有多少根控制总线就意味着cpu提供了对外部器件的多少种控制&quot;&gt;我们现在知道了，地址总线能传输多少个二进制信息，CPU就可以对多少个单元地址进行寻址。数据总线也是一样，有多少根决定了一次可传二进制数据的大小。控制总线是个总称，是一些不同控制线的集合，有多少根控制总线就意味着CPU提供了对外部器件的多少种控制。&lt;/h4&gt;

&lt;p&gt;现在我们一台机子上有很多存储器，包括随机存储器（RAM）、只读存储器（ROM）、显存RAM、网卡ROM等，这些存储器在物理上是独立的器件，它们都与CPU总线相连。&lt;/p&gt;

&lt;p&gt;CPU在地址传输的时候是怎么去识别到底传给哪个存储器的呢？CPU在操控它们的时候，把它们总的当作一个整体的内存空间来看待，相当于若干个存储器排列组成了一个逻辑存储器，每个存储器的存储地址与前一个是紧密连接的，我们称这整个存储空间为，内存地址空间。&lt;/p&gt;

&lt;p&gt;举个例子，假如所有存储器地址加起来为从64KB的空间，即总共0～FFFF的地址，那么主随机存储器的地址空间可能为32KB即 0～7FFF，显存地址空间可能为8KB即 8000～9FFF，其他各个ROM的地址空间为24KB 即 A000～FFFF。如果CPU向内存地址为1000的地址发送数据，就是向主随机存储器发送数据，如果向9000地址写入数据，则为显存空间。&lt;/p&gt;

&lt;p&gt;这里所说的都是外部总线，是CPU连接外部器件的导线集合。其实CPU内部也是有总线把各个部件连接起来的，一个CPU由运算器、控制器、寄存器等器件构成，这些器件就是靠内部总线相连，它们之间相互进行数据传输。&lt;/p&gt;

&lt;h3 id=&quot;寄存器&quot;&gt;寄存器&lt;/h3&gt;

&lt;p&gt;现代的CPU寄存器由很多种，我们拿最常用的一些来讲，即8086的14个寄存器，AX、BX、CX、DX、SI、DI、SP、BP、IP、CS、SS、DS、ES、PSW。&lt;/p&gt;

&lt;p&gt;通用寄存器有4个，AX、BX、CX、DX，它们可以存放一般性数据，可以看作是平时程序中的变量。它们中又有高低位寄存器来指向它们的高位和低位，即AH和AL为AX的高8位和低8位，BH和BL为BX的高8位和低8位，CH和CL为CX的高8位和低8位，DH和DL为DX的高8位和低8位。&lt;/p&gt;

&lt;p&gt;8086中可以一次性处理8位和16位数据，即字节（byte）和字（word）。即我们可以向AX中传数据也可以向AH和AL上传数据。&lt;/p&gt;

&lt;p&gt;我们来举个例子：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	mov ax,18 将18送入AX寄存器

	mov ah,78 将78送入AH寄存器

	add ax,8 将AX中的值加上8后覆盖AX

	现在ax中的值为86
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;在汇编代码中寄存器的用法有习惯性用法的讲究比如ax寄存器就是存放临时数据的bx则存放偏移地址数据cx存放的是循环计数数据dx则通常用来存放要访问的数据段地址而sidi与bx的作用一样存放的是内存偏移地址只是它们不能拆分为高低位&quot;&gt;在汇编代码中寄存器的用法有习惯性用法的讲究，比如AX寄存器就是存放临时数据的，BX则存放偏移地址数据，CX存放的是循环计数数据，DX则通常用来存放要访问的数据段地址。而SI、DI与BX的作用一样，存放的是内存偏移地址，只是它们不能拆分为高低位。&lt;/h4&gt;

&lt;p&gt;除了通用寄存器外，还有段寄存器，标志寄存器，也是汇编中重要的寄存器。&lt;/p&gt;

&lt;p&gt;段寄存器用于存放，段内存的起始地址和偏移地址。它们包括，CS、IP、SS、SP、BP、DS。&lt;/p&gt;

&lt;h4 id=&quot;其中cs和ip就是代码段寄存器cs指向代码段在内存中的起点ip则指向当前执行指令在内存起点中的偏移位置ip指向的地址就是程序要执行的指令位置&quot;&gt;其中CS和IP就是代码段寄存器，CS指向代码段在内存中的起点，IP则指向当前执行指令在内存起点中的偏移位置，IP指向的地址就是程序要执行的指令位置。&lt;/h4&gt;

&lt;h4 id=&quot;sssp和bp是内存栈的段寄存器其中ss指向栈内存段起点地址sp则指向当前栈顶偏移地址bp有点特殊它和sp联合使用作为sp校准使用因为sp要指向栈顶不能乱动所以sp把地址传给bp由bp来做栈内的寻址&quot;&gt;SS、SP和BP是内存栈的段寄存器，其中SS指向栈内存段起点地址，SP则指向当前栈顶偏移地址，BP有点特殊，它和SP联合使用作为SP校准使用，因为SP要指向栈顶不能乱动，所以SP把地址传给BP，由BP来做栈内的寻址。&lt;/h4&gt;

&lt;p&gt;对于栈操作，计算机有push和pop指令可以使用，即我们先定义号SS和SP好地址后用push和pop操作：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	mov ax, 123H ;给一个地址

	mov ss, ax ;段寄存器必须用寄存器操作数值

	mov sp, 100H ;设置栈顶，就是设置栈总大小

	push bp ;保存bp指针

	mov bp, sp ;把栈顶置针给bp，这时候如果函数有参数，则[bp + 2*4]就是第一个参数位置，[bp + 3*4]就是第二个参数位置，以此类推。

	mov ax, 22 ;把22给ax

	push ax ;ax中的值推入栈

	pop bx ;把栈中的值推出给bx

	现在bx中数据为22
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;最后是dsds则是静态数据段的寄存器指向静态数据段的起始地址所有在程序中的常量静态变量都被放在静态数据段中有ds数据段地址指向&quot;&gt;最后是DS，DS则是静态数据段的寄存器，指向静态数据段的起始地址，所有在程序中的常量、静态变量都被放在静态数据段中，有DS数据段地址指向。&lt;/h4&gt;

&lt;p&gt;有了指令内存地址，栈内存地址，静态数据内存地址，我们就可以根据数据执行指令，控制栈顶保存函数临时变量。&lt;/p&gt;

&lt;p&gt;标志寄存器则对各种标志位存储，它包括ZF（零标志）、OF（溢出标志）、DF（方向标志）、CF（进位标志）、PF（奇偶标志）、SF（符号标志）、TF（中断标志）、IF（屏蔽标志）等。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	ZF 结果是否为0

	OF 加法是否溢出

	DF si、di的增减方向

	CF 进行无符号运算时记录最高有效位的进位值。

	PF 结果是否为偶数

	SF 结果是否为负

	TF 是否有中断程序

	IF 是否屏蔽中断
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这些标志位都是被动被计算机赋值的，只有当我们结束某个操作指令后才会需要去查看它们。&lt;/p&gt;

&lt;h3 id=&quot;寻址&quot;&gt;寻址&lt;/h3&gt;

&lt;h4 id=&quot;绝大部分机器指令都是进行数据处理的指令处理大致可分为3类读取写入运算在机器指令这一层来讲并不关心数据的值是多少而关心指令执行前一刻它将要处理的数据所在的位置指令在执行前所要处理的数据可以在3个地方分别是cpu内部内存端口&quot;&gt;绝大部分机器指令都是进行数据处理的指令，处理大致可分为3类：读取、写入、运算。在机器指令这一层来讲，并不关心数据的值是多少，而关心指令执行前一刻，它将要处理的数据所在的位置。指令在执行前，所要处理的数据可以在3个地方，分别是：CPU内部、内存、端口。&lt;/h4&gt;

&lt;p&gt;我们知道CPU通过内存来获得数据，在访问内存时要给出内存的地址，通过地址总线送入存储器芯片必须是一个内存单元的物理地址，于是CPU可访问的内存大小就受到地址总线的限制。&lt;/p&gt;

&lt;p&gt;8086CPU是16位结构的CPU，也就是说8086内部职能一次性处理和传输总长16位的信息，如果将地址从内部简单发出，那么它只能送出16位地址，即只能访问0～FF之间的地址，表现出的寻址能力时64K。&lt;/p&gt;

&lt;h4 id=&quot;这样的寻址能力太少于是cpu采用了一种内部用两个地址合成一个地址的方法来增加寻址能力即段地址偏移地址--物理地址这样就一下子增加了1倍的寻址能力如果地址总线够宽的话可惜8086只有20根总线因此最大寻址能力只能提高到1mb&quot;&gt;这样的寻址能力太少，于是CPU采用了一种内部用两个地址合成一个地址的方法来增加寻址能力，即“段地址+偏移地址 = 物理地址”，这样就一下子增加了1倍的寻址能力，如果地址总线够宽的话。可惜8086只有20根总线，因此最大寻址能力只能提高到1MB。&lt;/h4&gt;

&lt;p&gt;我们现代计算机中也运用了同样的寻址方式，但是现代计算机中地址总线只有32条和40条，即32位CPU处理器原本只能处理4G内存，64位CPU处理器原本只能处理1T内存（而且还要受到其他地址总线的限制，比如内部总线）。&lt;/p&gt;

&lt;p&gt;段地址是重要的标记位置，是一段内存的起点，因此在程序中有专门的寄存器来存储它们，就是我们前面说的，CS、IP、SS、SP、BP、DS、ES。&lt;/p&gt;

&lt;p&gt;寻址方式分，直接寻址、间接寻址、基址变址寻址，即如下：&lt;/p&gt;

&lt;h4 id=&quot;例子1数据段寻址&quot;&gt;例子1，数据段寻址：&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;	mov bx, 1000H ;将1000数据送入bx

	mov ds, bx ;将bx里的数据送入ds

	mov al, [0] ;将ds段地址加上0得到的1000H:0，在该内存地址单元中的数据送入al寄存器

	mov [0], bx ;将bx的数据送入1000H:0内存地址单元中
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;例子2寄存器寻址&quot;&gt;例子2，寄存器寻址：&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;	mov bx, 1000H ;将1000H送入bx

	mov ax, [bx] ;根据bx中的值去内存中的单元地址中的值，传送给ax

	mov ax, [bx + 123] ;根据bx+123得到的值去内存中的单元地址中取值，传送给ax

	mov ax, [bx + si] ;根据bx + si的到的值去内存中取值，传送给ax

	mov ax, [bx + si + 123] ;根据bx + si + 123得到的值去内存中的单元地址中取值，传送给ax
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;例子3段地址寻址&quot;&gt;例子3，段地址寻址：&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;	mov bx, 1000H ;将1000H送入bx

	mov cs, bx ;将bx的值送入cs代码段寄存器

	mov ax, 0 ;将0送入ax

	add ax, cs:[bx] ;用段地址+偏移地址的方式获得cs+bx地址，并从该地址上取得数值送入ax
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;例子4不同类型的数据放入不同的段&quot;&gt;例子4，不同类型的数据放入不同的段：&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;	assume cs:code, ds:data, ss:stack

	data segment ; 定义data内容

		dw 0123h, 0456h, 0789h, 0abch, 0defh, 0fedh ;dw为字内容即2个byte

	data ends

	stack segment ; 定义stack内容

		dw 0,0,0,0,0,0

	stack ends

	code segment ; 定义code内容

		mov ax, statck ; 将statck地址送入ax

		mov ss, ax ; 将ax内容送入ss栈段寄存器

		mov sp, 20h ; 将栈顶定义为20h大小

		mov ax, data ; 将data定义为

		mov ds, ax ; 将ax数据传入ds数据段寄存器

		mov bx, 0 ; 将0传入bx

	code ends

	end start
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;跳转&quot;&gt;跳转&lt;/h3&gt;

&lt;p&gt;计算机执行机器指令时是顺序的并且根据代码段指针在没有跳转的情况下依次执行下去，这个指针就是IP寄存器。因此跳转就是将原本IP寄存器指向的地址改为我们指定的地址，这样计算机根据IP指针指向的机器指令可以随意操控了。&lt;/p&gt;

&lt;p&gt;最常用的跳转就是loop。计算机在执行loop指令时每次都会在先将cx中的值减1，再判断cx是否为0，如果不为零则跳转到指定地址：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	mov cx, 11 ; 将11传送给cx

	mov ax, 1 ; 将1传送给ax

	s: add ax, ax ; 将ax加上ax的值传入ax

	   loop s  ; 先做cx=cx-1操作，再判断cx是否为零，如果不是零则跳转到 s位置，如果是零则继续下一条
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;除了修改IP来实现跳转外，我们也可以通过就该CS代码段地址来实现跳转。&lt;/p&gt;

&lt;h4 id=&quot;只修改ip实现的跳转我们称为段内跳转也称短跳转因为跳转范围在一个寄存器大小范围内而同时修改cs和ip实现的跳转我们称为段间跳转也称为长跳转&quot;&gt;只修改IP实现的跳转，我们称为段内跳转，也称短跳转，因为跳转范围在一个寄存器大小范围内。而同时修改CS和IP实现的跳转，我们称为段间跳转，也称为长跳转。&lt;/h4&gt;

&lt;p&gt;常用的跳转指令有：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	无条件跳转 jmp

	有条件跳转 je(相等就跳转)，jcxz（判断cx为0就跳转）

	循环跳转   loop

	调用跳转 call，ret（使用栈中数据修改IP内容实现近跳转），retf（用栈中数据修改CS和IP实现远跳转）

	中断跳转   int
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;前面有些一般形式的跳转我们都会理解的比较快，比如jmp，je，jcxz，和loop，都是通过寻址地址跳转，或先做某个判断再跳转。和我们平时写的代码中的，if…else，goto，for循环，switch有很多相似之处，因此理解起来相对比较容易，最多也只是多了一个远近的跳转，也好理解，近跳转只修改IP，远跳转修改了CS和IP。&lt;/p&gt;

&lt;h4 id=&quot;callretretf和我们平时编写的函数有紧密的联系&quot;&gt;call、ret、retf和我们平时编写的函数有紧密的联系。&lt;/h4&gt;

&lt;p&gt;当call被调用时，它先将当前IP或CS和IP推入到栈中，再进行转移。执行call时相当于：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	push IP

	jmp 某寄存器
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;而ret和retf在执行时，会先从栈中推出一个数据传送给IP，或推出2个数据传送给CS与IP。&lt;/p&gt;

&lt;p&gt;ret相当于：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	pop IP
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;retf相当于&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	pop IP

	pop CS
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;这两个指令很像我们代码中的函数调用function-call-和-return时的作用我们来看看这两个指令配合起来是如何运作的&quot;&gt;这两个指令很像我们代码中的函数调用function call 和 return时的作用。我们来看看这两个指令配合起来是如何运作的：&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;	mov ax, 1 ;将1传送给ax

	mov cx, 3 ;将3传送给cx

	call s ;先push IP 并且 jump s，指令跳到s段地址上

	mov bx, ax ;将ax的值传送给bx

	mov ax, 4c00h ;将4c00h传送给ax

	int 21h ;中断调用，结束

	s: add ax, ax ;将ax值相加传送给ax

	   loop s ;先cx减去1传送给cx，再判断cx是否为0，如果不为0条转到s，如果为0则继续下一个

	   ret ;pop IP，从栈中取的前面call时推入的地址给IP，实现跳转到 call s 那句指令下

	最后结束前，bx为8，loop了跳转了3次，ax从1自增到了8，call跳转到了s做了循环，做完循环ret又回到了call s下。
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;有了function和ret的概念我们就可以传递更多参数到栈中在function里去取出栈中的值进行计算在汇编里会变得如何呢我们来看看&quot;&gt;有了function和ret的概念，我们就可以传递更多参数到栈中，在function里去取出栈中的值进行计算，在汇编里会变得如何呢，我们来看看：&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;	mov ax 1000 ;将1000传入ax

	mov ds ax ;通过ax设置数据内存单元

	mov [101] 10 ;将10传送到数据内存101地址单元上

	mov [102] 20 ;将20传送到数据内存102地址单元上

	push [101] ;将101数据内存地址单元上的数据推入栈中

	push [102] ;将102数据内存地址单元上的数据推入栈中

	mov ax, 1 ;将1传入ax

	call s ;先push IP，再将IP设置为 s段地址上

	mov bx, ax ;将ax中的数值传送给bx

	mov ax 4c00h ;将4c00h传送给ax

	int 21h ;中断调用，结束

	s: pop bx ;从栈中推出数据给bx，其实就是IP值

	   pop dx ;从栈中推出数据给dx，其实就是[102]的值

	   add ax dx ;将ax和dx相加传送给ax

	   pop dx ;从栈中推出数据给dx，其实就是[101]的值

	   add ax dx ;将ax和dx相加传送给ax

	   jmp bx ;直接跳转到call s的位置
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述程序利用了栈传递数据，在调用 call s之前，在栈中推入了内存地址101和102的值，到s中执行计算时分别推出了IP和两个数据，做完加法后跳转到call s的原位置下，继续执行直到结束。&lt;/p&gt;

&lt;h2 id=&quot;中断&quot;&gt;中断&lt;/h2&gt;

&lt;p&gt;中断分内部中断和外部中断，中断的意思是CPU不再接着向下执行，而是转去处理特殊的信息。&lt;/p&gt;

&lt;p&gt;CPU具备一种能力，可以在执行完当前正在执行的指令后检测到从CPU外部发过来的或者内部产生的一种特殊信息，并立即处理接收到的信息，这就是中断信息。&lt;/p&gt;

&lt;p&gt;内部中断比如CPU在除法时溢出就会触发内部的中断程序，外部中断比如收到键盘的输入数据时会触发中断程序。&lt;/p&gt;

&lt;p&gt;用来处理中断信息的程序被称中断处理程序，一般来说，需要对不同的中断信息编写不同的处理程序。&lt;/p&gt;

&lt;p&gt;每个中断程序都有一个中断码，通过中断码我们可以找到中断程序。在计算机内存中有一块内存是专门存放中断码和对应的中断程序地址的，我们称为中断向量表：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	0号 -&amp;gt; 中断程序入口地址

	1号 -&amp;gt; 中断程序入口地址

	...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;当程序引发中断时过程如下，我们假设发生了除法溢出错误，产生了0号中断信息：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	1.除法溢出，产生中断信息，取得中断码

	2.标志寄存器的值入栈（因为在中断过程中要改变标志寄存器值）

	3.设置标志寄存器TF和IF的值为0

	4.CS和IP入栈

	5.根据中断码从中断向量表中取出中断处理程序的入口地址
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;中断程序编写的方法和子程序相似：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	1.保存用到的寄存器

	2.处理中断

	3.恢复用到的寄存器

	4.调用iret指令返回
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;iret指令功能可描述为：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	pop IP ;推出地址给指令指针IP

	pop CS ;推出地址给指令段地址给CS

	popf ;推出标志寄存器
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;除了系统自带的中断程序外我们也可以自己安装中断程序例如我们常用的try-cache-和-throw-就是利用安装中断程序来做抓异常的功能的&quot;&gt;除了系统自带的中断程序外，我们也可以自己安装中断程序，例如我们常用的Try cache 和 throw 就是利用安装中断程序来做抓异常的功能的。&lt;/h4&gt;

&lt;p&gt;我们来看看，安装中断程序时如何做的：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	mov ax, cs ;将cs地址传入ax

	mov ds, ax ;将ax地址传入ds，为后面拷贝做准备

	mov si, offset ss ;设置ds:si指向地址

	mov ax, 0 ;将0传入ax

	mov es, ax ;将ax传入es

	mov di, 200h ;设置es:di指向目的地址

	mov cx, offset sqrend-offset ss ;设置ss中断程序长度

	cld ;设置传输方向为正

	rep movsb ; 拷贝ds:si到es:di

	mov ax, 0 ; 设置ax为0

	mov es, ax ; 将ax的0传入es

	mov word ptr es:[7ch*4], 200h ; 修改中断编码7ch的中断程序入口地址

	mov word ptr es:[7ch*4 + 2], 0 ; 修改中断编码7ch的中断程序入口地址

	mov ax, 4c00h ;设置ax

	int 21h ; 结束中断

	ss: mul ax ;中断程序，ax乘以ax传入ax

	    iret ;恢复中断
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们将ss这段程序放入了中断向量表7ch中去，当我们执行 int 7ch 时就执行了ss段程序，虽然这个程序执行完毕后就没有在内存了，但ss这段指令已经被拷贝到了内存中。&lt;/p&gt;

&lt;h4 id=&quot;我们来看看bios和dos在启动时时如何安装它们的中断程序的&quot;&gt;我们来看看BIOS和DOS在启动时时如何安装它们的中断程序的。&lt;/h4&gt;

&lt;p&gt;1.开机后，CPU一加电，初始化(CS)=0FFFFh，(IP)=0，自动从FFFF:0单元开始执行程序。FFFF:0出油一条跳转指令，CPU执行该指令后转去执行BIOS中的硬件系统检测和初始化程序。&lt;/p&gt;

&lt;p&gt;2.初始化程序将建立BIOS所支持的中断向量表，将BIOS提供的中断程序的入口地址登记在中断向量中。&lt;/p&gt;

&lt;p&gt;3.硬件系统检测和初始化完成后，调用 int 19h 进行操作系统的引导。从此将计算机交由操作系统控制，DOS开始初始化程序。&lt;/p&gt;

&lt;p&gt;4.DOS启动后，除完成其他工作外，还将它所提供的中断程序装入内存，并添加到相应的中断向量表中。&lt;/p&gt;

&lt;h4 id=&quot;外部中断&quot;&gt;外部中断&lt;/h4&gt;

&lt;p&gt;前面说CPU总线连接了很多芯片，这些芯片都有一组可以由CPU读写的寄存器，虽然这些寄存器都在不同的芯片中，但它们都与总线相连，CPU可以通过控制总线对它们进行读写。&lt;/p&gt;

&lt;p&gt;从CPU的角度看，将这些寄存器都当作端口，对它们进行统一编址，从而建立了一个统一的端口地址空间。每个端口在地址空间中都有一个地址。&lt;/p&gt;

&lt;p&gt;在访问端口时，CPU通过端口地址来定位端口，端口地址范围为0～65535。&lt;/p&gt;

&lt;p&gt;访问端口与访问内存略有不同，访问内存使用mov ax, ds:[8]，将内存地址数据段中的8单元地址上的数据传送到ax，而端口则是 in al, 60h，从60h号端口读入1个字节的数据送入al，out 20h, al 将al上的数据传入到20h端口。&lt;/p&gt;

&lt;p&gt;CPU有了与外部芯片通信的机制，就可以接受外部中断信息。比如，外设的输入到达时，相关芯片就会向CPU发出相应的中断信息。CPU在执行完当前指令后，检测到发过来的中断信息，引发了中断过程，跳转到中断程序处理外设的输入数据。&lt;/p&gt;

&lt;h4 id=&quot;我们来看看计算机时如何处理键盘输入中断的&quot;&gt;我们来看看计算机时如何处理键盘输入中断的：&lt;/h4&gt;

&lt;p&gt;1.键盘上每个键都相当于一个开关，键盘中有一个芯片对键盘上的每个键的开关状态进行扫描。&lt;/p&gt;

&lt;p&gt;2.按下一个键时，开关接通，该芯片就产生了一个扫描码，扫描码被送入主板上的接口芯片(usb接口)的寄存器中，该寄存器的端口地址通常为60h。&lt;/p&gt;

&lt;p&gt;3.松开按下的键时，也会产生一个扫描码，也同样被送入主板上的接口芯片(usb接口)的寄存器中。&lt;/p&gt;

&lt;p&gt;4.当键盘的输入到达60h端口时，接口芯片就会向CPU发送中断码。CPU检测到该中断信息后，如果IF=1则认为是不可屏蔽中断，接着响应中断引发中断过程，转去执行int 9的中断程序。&lt;/p&gt;

&lt;p&gt;5.BIOS提供了int 9的中断程序，读出60h端口中的扫描码，并送入到内存的键盘缓存中，如果是Ctrl之类的控制键则写入内存中存储状态字节单元，接收完成后向接口芯片发出应答信息。&lt;/p&gt;

&lt;p&gt;6.CPU继续根据内存中键盘缓存中的数据执行相关程序，响应键盘的输入。&lt;/p&gt;

&lt;h4 id=&quot;除了键盘外磁盘也是外部设备的一种也同样需要通过端口和中断的形式处理相关程序&quot;&gt;除了键盘外，磁盘也是外部设备的一种，也同样需要通过端口和中断的形式处理相关程序。&lt;/h4&gt;

&lt;p&gt;BIOS和操作系统都提供了访问磁盘的中断程序。当需要向磁盘访问数据时，会先在寄存器和内存上设置访问的地址和大小，再发起中断，中断程序会根据设置在寄存器和内存上的信息从磁盘上传输到内存，传输完毕后结束中断。&lt;/p&gt;

&lt;p&gt;下面这个写入数据到3.5英寸软盘为例：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	mov ax, 0

	mov es, ax

	mov bx, 200h ; ex:bx指向写入磁盘的数据

	mov al, 1 ; 读取的扇区数

	mov ch, 0 ; 磁道号

	mov cl, 1 ; 扇区号

	mov dl, 0 ; 驱动器号

	mov dh, 0 ; 磁头号

	mov ah, 3 ; 表示写入

	int 13h ; 执行磁盘写入中断
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;CPU首先需要告诉磁盘设备，写入还是读取，接着要告诉磁盘写入的内容，以及写入的地址，中断程序在执行完毕写入操作后，结束中断并返回原地址。&lt;/p&gt;

&lt;h4 id=&quot;总结汇编其实就是机器码的助记符它有很多个寄存器每个寄存器都有不同的用途内存分为几个段不同的内存段有不同的用途通常包括有静态数据段代码段栈空间段我们通过指令可以在内存中寻址也可以在不同的段之间跳转cpu和设备之间用总线连接在一起总线又分为地址总线数据总线控制总线每个设备又都有自己的芯片它们与cpu的通信方式就是通过自己的寄存器cpu统一称它们为端口并为它们做了统一的编号除内存外设备与cpu之间的通信通常使用中断来进行中断程序执行完毕后会跳转到原位置继续执行下面的程序指令&quot;&gt;总结，汇编其实就是机器码的助记符，它有很多个寄存器，每个寄存器都有不同的用途，内存分为几个段，不同的内存段有不同的用途，通常包括有静态数据段、代码段、栈空间段，我们通过指令可以在内存中寻址，也可以在不同的段之间跳转。CPU和设备之间用总线连接在一起，总线又分为地址总线，数据总线，控制总线，每个设备又都有自己的芯片，它们与CPU的通信方式就是通过自己的寄存器，CPU统一称它们为端口并为它们做了统一的编号。除内存外，设备与CPU之间的通信通常使用中断来进行，中断程序执行完毕后会跳转到原位置继续执行下面的程序指令。&lt;/h4&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(十一) 《精力管理》</title>
   <link href="http://www.luzexi.com/2020/10/11/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B011"/>
   <updated>2020-10-11T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/10/11/读书笔记11</id>
   <content type="html">&lt;p&gt;前面有一段时间，对时间管理着迷，想着能不能通过自我管理的方式去实现更高效率的学习与工作。&lt;/p&gt;

&lt;p&gt;2019年初，大概有几个月的时间，我用时间限制的方式去履行自己的计划。这段时间非常痛苦，后来我接触了《精力管理》，2019年10月时看完了这本书，发现精力管理比时间管理要好的多，原因是精力管理并不是泯灭人性的，它顺从人性，以精力为基础来告诉我，怎样才能保持活力，扩充精力，打破了时间上的局限。&lt;/p&gt;

&lt;p&gt;去执行时间管理的这段时间，其实没有白跑，给了很多启示和感悟，我也一直在不断调整自己的作息、生活、工作方式。我接触精力管理后虽然认同，但仍然没有完全放弃时间管理，因为我一直认为时间是比较重要的因素，从那时到现在我尝试了，每日任务、时间块记录、时间限制、打卡表的方式去管理我每日的精力。虽然嘴上说着精力管理，但其实对时间管理的相当严格。&lt;/p&gt;

&lt;p&gt;在坚持对时间把控的这段时间，其实出了很多问题，我过分关注时间，过分关注自律，导致我的效率虽然提升了些许，但没有像想象的那么好，情绪上也有过反抗和厌弃。我隐隐的知道对精力管理的理解犯了很多错误，似乎我的行动和计划有点偏离了轨道。“肯定是我理解错了”，我这样告诉我自己。&lt;/p&gt;

&lt;p&gt;我想要的是更高效率的学习、工作和生活，但为什么看似把所有时间利用的很紧凑，也主动去调节自己的情绪和身体的状态，但效率很难提上来，于是我再次阅读了这本书，这次我想把它的弄的明白些。我秉持着不完全相信别人的话的原则去审视这些内容，再次看完后我仍然认为这本书上所讲的值得我们深度参考。&lt;/p&gt;

&lt;p&gt;我现在是怎么想的呢？我认为人应该从各个方面去扩充自己的精力，并且不是每件事都要竭尽全力去完成的，找出最重要的、最关键的、性价比最高的事情，把精力投入进去，现在的我又加入了一个参考事项，即自己最感兴趣的事，当我发现我对某件事特别感兴趣事（感兴趣的程度非常强烈才算），我会投入大量精力去做去学习，此时的学习效率是最高的，因为我对这件事很好奇很喜欢也很开心去做。短期的最重要和长期的最重要又是不同的，积跬步至千里很必要，给自己一个长期的目标和计划去执行，每天都做一些自己认为对自己长期计划有用的事（比如看书、锻炼、写作），这些事最好能在早上完成，对于一天的开始，我们就能信心满满的去面对，每一天的好的开始是一天成功的一半。&lt;/p&gt;

&lt;h3 id=&quot;下面是我对全书的记录和理解&quot;&gt;下面是我对全书的记录和理解&lt;/h3&gt;

&lt;p&gt;全书着重强调的一点是：精力，而非时间，是高效表现的基础。&lt;/p&gt;

&lt;p&gt;它说我们做每件事都需要精力，不管是与同事互动、作出每个决定、陪伴家人都需要精力。有技巧的精力管理是高度表现、健康和幸福的基础。&lt;/p&gt;

&lt;p&gt;事实确实是这样，很多时候我们仅仅希望竭尽所能去完成工作或为生活做出尽可能多的努力，但事实上我们会为眼前的现状作出临时的决策来透支自己，短期这会让现状运作良好，而负面效应却往往在长期的过程中慢慢浮现。面对工作和生活的无尽索取，我们会变得暴躁、易怒，注意力也难以集中，于是结束漫长的一天，我们疲惫的回到家，发现家庭也不再是欢乐和力量的源泉，它成为了超负荷运转的一项负担。&lt;/p&gt;

&lt;p&gt;我们常常会通过减少睡眠、点外卖填饱肚子、用咖啡提神、用酒精和安眠药放松身心等，短期透支自己的方式来确保自己能够完成短期目标。&lt;/p&gt;

&lt;p&gt;短期完成目标确实很重要，但我们每天都很忙，我们总是为一个接一个的短期目标不断的透支自己，停不下来，这是问题的根本。&lt;/p&gt;

&lt;h3 id=&quot;我们人的精力有四个来源体能精力情感精力思维精力意志精力&quot;&gt;我们人的精力有四个来源，体能精力、情感精力、思维精力、意志精力。&lt;/h3&gt;

&lt;p&gt;通过对这4个来源的调整和训练，我们可以掌握精力使用和拓展技巧。&lt;/p&gt;

&lt;h4 id=&quot;体能精力&quot;&gt;体能精力&lt;/h4&gt;

&lt;p&gt;从生理学的角度看，精力来源于氧气和血糖的化学反应。从实际生活来看，精力储备取决于我们的呼吸模式、进食的内容和时间、睡眠的长短和质量、白天间隙恢复的程度以及健康程度。&lt;/p&gt;

&lt;p&gt;因此建立起体能消耗和恢复的节奏性平衡，能够确保精力储备保持在相对稳定的水平。而走出舒适区，然后等待恢复，则是拓展体力的方法，适用于体力无法满足要求的情况。&lt;/p&gt;

&lt;p&gt;为了能让体能精力有用良好的表现，我们需要对自己生活中的体能消耗、恢复、拓展做些调整。&lt;/p&gt;

&lt;p&gt;第一就是调整饮食方式。由于任何一顿丰富的食物都不足以维持4-8小时高效表现，因此我们最好一天内吃5-6餐低热量高营养的食物，少吃多餐并且是低热量高营养，少一些碳水化物和油腻食物，多一些高蛋白食物。进食也需要讲究一下，其一不要让自己感到饥饿才去进食，此时精力早已耗尽。其二不要去追求饱腹感，吃后会感到撑胀，这样会额外消耗一部分我们的精力，补充精力不充分。其三要多喝水，水是调节身体内部运转的有力催化剂。&lt;/p&gt;

&lt;p&gt;第二，调整生理周期和睡眠。充足的睡眠是活力的保障，思维能力，即反应时间、专注力、记忆力、逻辑分析和辩证能力，会随着睡眠不足而衰退。我们人每晚需要7-8小时的睡眠才能在第二天运转良好。除了充足的睡眠，小息也是一种精力恢复的手段，睡眠研究员、心理学家发现每4个小时小睡20-30分钟仍可以保持超过24小时的惊人高效和敏锐。&lt;/p&gt;

&lt;p&gt;第三，增加体能训练。体力丧失是机体衰老和能力下降的显著特征，随着年龄的增长，新陈代谢速度减慢，过了40岁如果没有日常的力量训练，人们将平均每年失去0.5磅的肌肉重量。力量训练可以全面增强精力、加速新陈代谢、强健心脏。&lt;/p&gt;

&lt;h4 id=&quot;精力消耗和再生都是活跃的生理过程从经验上看体能情感思维意志精力的单线化消耗都不利于最优表现甚至会随着时间出现潜在的危害因此我们需要注意在消耗精力的同时加入更多的补充饮食小息锻炼以恢复和拓展精力&quot;&gt;精力消耗和再生都是活跃的生理过程，从经验上看，体能、情感、思维、意志精力的单线化消耗都不利于最优表现，甚至会随着时间出现潜在的危害。因此我们需要注意在消耗精力的同时加入更多的，补充饮食、小息、锻炼，以恢复和拓展精力。&lt;/h4&gt;

&lt;h4 id=&quot;情感精力&quot;&gt;情感精力&lt;/h4&gt;

&lt;p&gt;情感精力可以将威胁转化为挑战，为了发挥出最佳水平，我们必须调动积极愉悦的感情：喜悦的、爱挑战的、爱冒险的，这样才能保持正面积极的精力。在实际应用中，自信、自控、社交技巧及共情能够牵动积极情感的肌肉。&lt;/p&gt;

&lt;p&gt;若要调动情感肌肉塑造最佳表现，也同样要创造定期使用和间歇恢复的平衡，以及拓展情感肌肉。说实在的，这比起体力层面要困难的多。&lt;/p&gt;

&lt;p&gt;压力和负面情绪是消耗情感肌肉的主要途径，那么如何恢复呢？答案是获得正面情感。正面情感可以更有效地支配个人表现，它对团体也有积极的影响。&lt;/p&gt;

&lt;p&gt;第一就是兴趣活动。由于我们人的兴趣各异，这些兴趣可以激发出我们的正面情绪，即享受、满足和安全感。有些人可能喜欢唱歌、园艺、跳舞、亲热、练瑜伽、读书、体育活动、听音乐会，或仅仅静坐自省。&lt;/p&gt;

&lt;h4 id=&quot;这其中的关键是要表达出你对这些兴趣活动的重视并将投入在它们上面的时间视为神圣不可侵犯这些活动对你的吸引力越大程度越丰富生动性越强对情感再生的深度和质量也越好不仅快乐本身便是奖赏从更实际的角度看快乐也是维持最佳表现的重要因素&quot;&gt;这其中的关键是要表达出你对这些兴趣活动的重视，并将投入在它们上面的时间视为神圣不可侵犯。这些活动对你的吸引力越大，程度越丰富，生动性越强，对情感再生的深度和质量也越好。不仅快乐本身便是奖赏，从更实际的角度看，快乐也是维持最佳表现的重要因素。&lt;/h4&gt;

&lt;p&gt;第二，让人际关系促进精力再生。一段健康的友谊可以带来积极的情感精力，例如在工作环境中交一位好友，与好友有丰富的互动，包括付出与回报、倾诉与倾听、珍视他人和被人同等珍视。不仅如此，在家中与妻子的亲密互动，与孩子们的亲密互动，在学校中与同学们以及亲密好友的交头接耳，都可以认为是从人际关系角度促进精力再生的方式。这给我们带来了情感上的滋润、放松、享受。&lt;/p&gt;

&lt;p&gt;第三，积极扩充情感容量。我们可以通过一些仪式习惯来达成，比如定期与同事有深入的交流，定期与家人有一段亲密的互动，定期去做些公益活动，定期去上语言和心理学课程等。&lt;/p&gt;

&lt;h4 id=&quot;情感容量的最高境界是接纳不同情感的能力我们很容易重视某些情绪技巧而忽略了另外一些比如我们可能过于看重强硬而藐视温和也可能恰恰相反其实两种品质对情感肌肉同等重要还有很多相互对立的品质例如自控与积极坦率与感伤慷慨与节俭开放与审慎激情与淡漠耐心与急迫谨慎与鲁莽自信与谦逊等&quot;&gt;情感容量的最高境界是接纳不同情感的能力，我们很容易重视某些情绪技巧而忽略了另外一些。比如我们可能过于看重强硬而藐视温和，也可能恰恰相反。其实两种品质对情感肌肉同等重要。还有很多相互对立的品质，例如自控与积极，坦率与感伤，慷慨与节俭，开放与审慎，激情与淡漠，耐心与急迫，谨慎与鲁莽，自信与谦逊等。&lt;/h4&gt;

&lt;h4 id=&quot;只有接受那些看似相反的品质不逼自己在其间二选一才有可能获得最深刻最丰富的情感能力&quot;&gt;只有接受那些看似相反的品质，不逼自己在其间二选一，才有可能获得最深刻最丰富的情感能力。&lt;/h4&gt;

&lt;h3 id=&quot;思维精力&quot;&gt;思维精力&lt;/h3&gt;

&lt;p&gt;思维精力可以让我们保持专注。为了发挥出最好的水平，我们必须保持专注，在整体方向和局部目标之间灵活游走，一方面我们要看清事物的本质，另一方面我们还要朝着目标成果积极努力。而缺乏思维精力会让我们注意力涣散，过于悲观，思维固化，眼光狭窄。&lt;/p&gt;

&lt;p&gt;体能、情感、思维，三方面的精力是相辅相成的，身体层面睡眠少或亚健康导致的疲倦，会使得注意力难以集中；感情层面的焦虑、挫败、愤怒的情绪也会干扰注意力，损害乐观心态，尤其是面临高强度压力的情况时。&lt;/p&gt;

&lt;p&gt;那么怎么来优化思维精力呢？&lt;/p&gt;

&lt;p&gt;第一，在放松中思考。世界上绝大多数工作环境都充斥着一条或明显或隐晦的错误信息，长时间连续工作是高产出的最佳途径，按时休息不会受到奖励，白天抽时间活动不会得到赞扬等等。这些都错误的认知，只有在放松中思考（或者说间歇性休整）才能真正迸发出创造力。&lt;/p&gt;

&lt;p&gt;《如何像达芬奇一样思考》一书中提出了颇为深意的问题“什么场合你会获得最佳灵感？”，经过多年的收集，最普遍的答案是：沐浴的时候、躺床上的时候、在自然中散步的时候、听音乐的时候、慢跑的时候、冥想的时候、做梦和海边度假的时候等等。&lt;/p&gt;

&lt;p&gt;达芬奇也曾说过“最伟大的天才，有时工作越少成果越出色。时不时离开工作放松一下是个非常好的习惯，当你回到工作时，做出的判断会更加准确。”。&lt;/p&gt;

&lt;h4 id=&quot;精神外科专家罗杰用科学的方式解释了这种现象大脑左半球有语言神经有条理按次序地工作通过逻辑推演得出结论右半球更擅长视觉化和空间概念有更强的全局观能将事物的部分与整体联系起来间隙地让右大脑主持大局可以让我们从占据我们多数工作时间的左脑理性分析模式中脱离出来得到有效恢复&quot;&gt;精神外科专家罗杰用科学的方式解释了这种现象，大脑左半球有语言神经，有条理、按次序地工作，通过逻辑推演得出结论，右半球更擅长视觉化和空间概念，有更强的全局观，能将事物的部分与整体联系起来。间隙地让右大脑主持大局，可以让我们从占据我们多数工作时间的左脑理性分析模式中脱离出来，得到有效恢复。&lt;/h4&gt;

&lt;p&gt;因此为了能够有更多的创造力，我们应该间隙的调动左右半脑交替思考。&lt;/p&gt;

&lt;p&gt;第二，重塑大脑。我们要重点强调的是，积极使用大脑能够提升其运转能力，反之使用不足则会萎缩。不管你处于什么年龄段，都可以持续优化大脑，大脑会因为使用而日益敏锐，越磨越锋利。&lt;/p&gt;

&lt;h4 id=&quot;年轻人的思维具有很强的可塑性学习一门新语言之类的复杂过程都轻而易举随着年龄的增长思维肌肉的调动越来越少学习新语言或新技能就变得更加困难带来了加倍的挫折感为了避免不适感有时是避免丢脸人们很容易放弃结果导致本可避免的大脑退化进一步加重&quot;&gt;年轻人的思维具有很强的可塑性，学习一门新语言之类的复杂过程都轻而易举。随着年龄的增长，思维肌肉的调动越来越少，学习新语言或新技能就变得更加困难，带来了加倍的挫折感。为了避免不适感（有时是避免丢脸），人们很容易放弃，结果导致本可避免的大脑退化进一步加重。&lt;/h4&gt;

&lt;h4 id=&quot;其实每当人们学习新的事物时都会建立起大脑细胞新的联结即使你已经出现老化的征兆甚至部分大脑细胞受损你仍然可以依靠这些新的联结完就部分脑细胞学习新的体育活动可以帮助我们打造新的肌肉学习新的电脑技能新的课程新的语言和单词能够帮助我们锻炼大脑塑造思维肌肉&quot;&gt;其实每当人们学习新的事物时，都会建立起大脑细胞新的联结。即使你已经出现老化的征兆，甚至部分大脑细胞受损，你仍然可以依靠这些新的联结完就部分脑细胞。学习新的体育活动可以帮助我们打造新的肌肉，学习新的电脑技能，新的课程，新的语言和单词，能够帮助我们锻炼大脑，塑造思维肌肉。&lt;/h4&gt;

&lt;h3 id=&quot;意志精力&quot;&gt;意志精力&lt;/h3&gt;

&lt;p&gt;这里需要解释一下，书中的意志精力并不是我们理解的“意志力”或“毅力”。他想表达的是，精神层面的动力，即热情、愿景、意义。&lt;/p&gt;

&lt;p&gt;本质上说，意志精力是一股掌管所有维度行为的独特力量，它是动机最丰富的源泉，通向最深层的价值取向和超越个人利益的意图。&lt;/p&gt;

&lt;p&gt;意志精力能让我们活出人生的意义：我们究竟为了什么而活下去，为了什么而工作，知晓生命的意义，方能忍耐一切。一个人如果有自己的人生目标，他的勇气和信念，即使面对艰难困苦时，会展现出更加积极的一面，这就时意志精力饱满的体现。&lt;/p&gt;

&lt;p&gt;但是人们往往在悲剧发生后才会意识到意志精力的重要性。著名演员克里斯托佛1995年骑马时摔下来不幸瘫痪，绝望的情绪笼罩着他，甚至有过自杀的念头。但他找到了自己的意志精力的来源，想要继续陪伴家人、继续为这个世界贡献力量。这种强烈的意愿帮助他振作起来，在体能严重不足、极易产生脆弱绝望情绪的情况下，最终依靠希望、乐观恢复了专注和清晰的思维。&lt;/p&gt;

&lt;p&gt;实际上意志精力的更新来源于价值观和使命感的启发和带领。&lt;/p&gt;

&lt;p&gt;价值观和使命感也不是那么容易找到，其实我们过分关注了自身的利益，这导致我们的思维受到局限，使得我们往往被自身的生存和不安全感打败，陷入狭隘。&lt;/p&gt;

&lt;p&gt;人们往往认为自身需求迫在眉睫，注意力一旦转移，便会发生原始的生存恐惧。如果我把精力用在别人身上，谁来关照我？但具有讽刺意味的是，自私自利反而会削弱精力，妨碍表现。我们越是被自己的恐惧和担忧掌控，越难调动精力做出正确的举措。&lt;/p&gt;

&lt;h4 id=&quot;将个人利益置后起初会让人倍感不安但实际上这样做回报颇丰你通过帮助他人提升个人价值同时也丰富人生意义因此我们应该更多的想着怎么去帮助他人会更好不是盲目的帮要从本质上帮助他们在未来摆脱困境而不是暂时的摆脱在帮助他人的同时我们能学到和得到比个人利益更大的东西比如人生的意义社会的本质以及群体的利益&quot;&gt;将个人利益置后起初会让人倍感不安，但实际上这样做回报颇丰，你通过帮助他人提升个人价值，同时也丰富人生意义。因此我们应该更多的想着怎么去帮助他人会更好（不是盲目的帮，要从本质上帮助他们在未来摆脱困境，而不是暂时的摆脱），在帮助他人的同时，我们能学到和得到比个人利益更大的东西，比如人生的意义，社会的本质，以及群体的利益。&lt;/h4&gt;

&lt;h6 id=&quot;因此意志精力在深层的价值取向所带来的生活方式是生活的主心骨而且能帮助我们更好地应对各种挑战&quot;&gt;因此意志精力在深层的价值取向所带来的生活方式是生活的主心骨，而且能帮助我们更好地应对各种挑战。&lt;/h6&gt;
</content>
 </entry>
 
 <entry>
   <title>思路探讨(五十二) 无题</title>
   <link href="http://www.luzexi.com/2020/09/20/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A852"/>
   <updated>2020-09-20T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/09/20/思路探讨52</id>
   <content type="html">&lt;p&gt;很多时候我们应该付出行动去实践，去打破现有的笼子，而不是光靠脑子想，因为大部分时候脑子想出来的很实际情况是完全不一样的。&lt;/p&gt;

&lt;p&gt;积极的行动和开朗的笑容可以打破现实中的困局，是不可多得的良药，虽然这不是万能的，但我们不应该因为害怕失败而退缩，害怕爱而选择不爱，害怕比赛而不参加，害怕与人比较而避而远之。不要害怕，没什么东西是一定及肯定的，我们要努力去接受不确定性，去选择勇敢的面对失败。&lt;/p&gt;

&lt;p&gt;世界上也没有完美的东西，你可能有些知识但没有钱，你可能有点钱但没有好看的皮囊。接受这个世界的不完美，没有一个人是容易的，不要太在意，但我们可以持续的去努力。如果你太在意很可能扰乱了你的心智，会掉入更大的深渊，不要掉进那个坑。&lt;/p&gt;

&lt;p&gt;《做时间的朋友》里有一句话说到点子上了。“如果你行为上散发着快乐，心理上就不可能存放的了忧郁。有时候即使是装着的，装着装着，也就真的回到了快乐点线上。强迫自己回到快乐，在现实中是可行的。”这就是快速从悲伤中走出来的最好办法。&lt;/p&gt;

&lt;p&gt;失败不可怕，实际上它占据了我们生活的大部分时间，无论是谁都是一样的。我们更应该关注失败后的调整，不管是心态调整还是对这事上的策略的调整，谁能更快的调整过来，谁能更好的从失败中反思到真正的重点，那么谁就会能更快的走出失败的阴影。不过走出失败并不一定代表走向成功，也许只是试错的更快了，但这也让我们更加接近最终的成功，每个人都不一样，试错的长度和速度也不同。&lt;/p&gt;

&lt;p&gt;不要看到别人失败就幸灾乐祸，“专注于吸取教训，而不是幸灾乐祸”，这件事本身都不容易做到，是反人类的行为。人类毕竟是动物，很多时候都不太理智，我们应该时刻想着如何控制自己的脑袋做些更加有意义的事情。&lt;/p&gt;

&lt;p&gt;大部分上面所说的包括积极的行动、开朗的笑容、勇于面对、专注于吸取教训，对大脑来说都是相当困难的，它们都是逆大脑正常运作方向的，这使得它们会损耗大脑许多能量，因此很多时候我们做着做着就做不下去了。唯一的办法就是让它变成一种习惯，在大脑中建立起一个链路，由于习惯就是大脑中的一条链路，没有链路时做起来很生疏也很难受，只要链路建立起来，在执行时脑力就能畅通，这使得我们损耗的能量就会减少许多，就不会那么痛苦了。这根链路就是习惯性积极行动、习惯性乐观开朗、习惯性面对困难、习惯性吸取教训。&lt;/p&gt;

&lt;p&gt;实际上轻松的学习是无效的，就像人家说什么你就信什么那样，完全是错误的，只有经过自己的仔细思考和分析，才能将知识真正的消耗到自己脑袋里。&lt;/p&gt;

&lt;p&gt;《做时间的朋友》说，与其关注成功者，不如反其道而行之，努力从失败者身上汲取经验。不要说模仿成功者，就算观察成功者也很困难，成功者很多，但是，我们身边真正的成功者却很少。成功背后的东西很难看清楚，所谓成功的真实性也很难判断，成功者们又会有意无意地美化和包装他们的经验，而这一切都在干扰我们的判断。不过，观察失败者却相对容易得多，因为失败者的失败往往是明显的、确定的、失败的真正原因也往往很容易查实。并且我们身边失败者的数量显然要多于成功者的数量。如此，我们也就有了更多的观察机会。&lt;/p&gt;

&lt;p&gt;很多时候，不仅归纳经验需要很长时间，通过演绎论证归纳出来的经验可能需要更长的时间，所以一定要保持足够的耐心。要知道有些阶段是无法跨越，很多人不懂等待的必要性，最终的结局是，同样等了，因为心急如焚但不得不等，最终等来的结果却是另外一个，反正不是通过耐心等待应该获得的那个。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(五十三) 生活的原理</title>
   <link href="http://www.luzexi.com/2020/09/06/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A853"/>
   <updated>2020-09-06T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/09/06/思路探讨53</id>
   <content type="html">&lt;p&gt;近几年一直追寻各种原理，觉得人这一生对原理的理解特别重要，人能理解多少原理并且能够融会贯通是能够好好活在这个世界上比较关键的事。这与看清本质有些许相似，但比它更广泛，也更务实些。&lt;/p&gt;

&lt;h3 id=&quot;1例如工作我一直追寻它的底层原理&quot;&gt;1.例如工作，我一直追寻它的底层原理。&lt;/h3&gt;

&lt;p&gt;即，我为什么要工作，人类工作的原理是什么。其实资本论已经告诉我们了，工作就是出卖脑力和体力给资本家。我们都需要钱，怎么赚钱最容易呢，就是给资本家打工。&lt;/p&gt;

&lt;p&gt;深推一步，我们打工仔就可以认为是一个资源，资本家需要资源帮助它实现梦想，我们就是这些资源。资源供大于求则价格会比较便宜，供不应求则会比较贵。因此打工的工资多与少，取决于你的稀缺度，以及需求量。因此我们会看到，需求大但人才稀缺(通常都是门槛高：知识深度门槛、思维多样化门槛、包容力门槛、交际圈门槛等)的职位工资通常比较高。这是供需平衡决定的。&lt;/p&gt;

&lt;p&gt;我们除了出卖体力和脑力来换钱外，不要忘了工作也让我们得到了锻炼、学到了新的知识，前提是你真的得到了锻炼和学习到了新的知识，而不是天天瞎混或只做些重复的劳动。&lt;/p&gt;

&lt;h3 id=&quot;为了能让自己更稀缺我们应该走不寻常的路只有不寻常才会让我们成为稀缺的人这在整个社会也是同样的原理生活中我们要做到稀缺做别人做不到的做别人不敢做的以及做别人想不到的这也是为什么我常常鼓励自己试着反相操作的本质我同你们一样不想平庸&quot;&gt;为了能让自己更稀缺，我们应该走不寻常的路，只有不寻常才会让我们成为稀缺的人。这在整个社会也是同样的原理，生活中，我们要做到稀缺。做别人做不到的，做别人不敢做的，以及做别人想不到的。这也是为什么我常常鼓励自己试着反相操作的本质，我同你们一样，不想平庸。&lt;/h3&gt;

&lt;h3 id=&quot;2我也进一步学习和思考了做事的原理&quot;&gt;2.我也进一步学习和思考了，做事的原理。&lt;/h3&gt;

&lt;p&gt;无论做什么事，我们平时包括自己的工作、学习、家庭、朋友、事业，会有非常多的事需要我们去做。我们应该如何处理？做事的原理是什么？&lt;/p&gt;

&lt;p&gt;我在32岁左右才终于明白了事情是做不完的这个道理，生活会不断的冒出很多事情让我们来做，因此很重要的一点是事情的顺序，做事情的顺序决定了我们效率。&lt;/p&gt;

&lt;p&gt;效率有多重要呢，如果你每天的效率比别人高1%，那么365天后你拉开了与别人34.5倍的效率差距，3、5年下来你能将别人与自己拉开几个层级，让你变得“稀缺”。&lt;/p&gt;

&lt;p&gt;“要事优先”就是最高效的做事方式。什么事情是最重要的，要先做。先想“什么事情是最重要的”，再做或把重要的精力放在最重要的事情上。&lt;/p&gt;

&lt;p&gt;看上去简单的手法执行起来却相当困难。为什么呢？&lt;/p&gt;

&lt;h3 id=&quot;首先是我们很难分清楚什么是重要的事情不知你是否能体会到我们大部分时候认为的重要的事情其实并不重要这也是导致我们努力白费的主要原因什么事情最重要如果我们没有弄明白这一点我们做的都是无用功&quot;&gt;首先是我们很难分清楚，“什么是重要的事情”。不知你是否能体会到，我们大部分时候，认为的重要的事情其实并不重要，这也是导致我们努力白费的主要原因。“什么事情最重要”如果我们没有弄明白这一点，我们做的都是无用功。&lt;/h3&gt;

&lt;h3 id=&quot;其次通常摆在我们眼前的有近期最重要的事情近期最紧急的事情长期最重要的事情以及近期次重要的事情近期次紧急的事情长期次重要的事情&quot;&gt;其次通常摆在我们眼前的有，近期最重要的事情，近期最紧急的事情，长期最重要的事情，以及近期次重要的事情，近期次紧急的事情，长期次重要的事情。&lt;/h3&gt;

&lt;p&gt;很多人常会一直处于处理紧急的事情当中极度焦虑，另一些人则徘徊在近期最紧急与近期最重要的事情之间无法自拔。而很少有人能从近期紧急的事情中抽离出来，做近期最重要的事情，同时能兼顾长期最重要的事情。这种人很稀缺。&lt;/p&gt;

&lt;h3 id=&quot;3快感的原理&quot;&gt;3.快感的原理&lt;/h3&gt;

&lt;p&gt;我一直在探索大脑的运作方式，前面探索了几年大脑与习惯之间的联系，发现习惯是大脑中的连接体，它可以通过每天一点点的练习来建立连接，从而建立好的习惯。&lt;/p&gt;

&lt;p&gt;人类快感的原理，也是大脑的运作方式之一。我们生活中有很多追求快感的场景，例如，美食、性爱、刺激、冒险、视觉盛宴等等都是快感之一。&lt;/p&gt;

&lt;p&gt;这是因为人类大脑中分泌的多巴胺。多巴胺，让大脑产生快感，所以称它为快乐激素。除了产生快乐，多巴胺还可以帮助大脑更好的指挥肌肉和提高人体协调性，让人类可以做更多精细化的运动，同时帮助大脑记忆和学习更多东西，因此我们需要更多的多巴胺来让大脑高效运作。&lt;/p&gt;

&lt;p&gt;从某种程度上说，大脑对快感的追求推动了人类文明的进步。我们对美食的追求，对冒险的挑战，对未知的探索，对工具的制作都是快感直接或间接推动的结果。&lt;/p&gt;

&lt;p&gt;运动、聊天、打游戏、看电影、刷抖音、喝酒、抽烟都会分泌多巴胺。但大脑更倾向与更简单的方式获得快感，吸烟、喝酒、刷抖音就是最简单获得快感的方式，我们称它们为垃圾快感。&lt;/p&gt;

&lt;h3 id=&quot;垃圾快感的特点是容易获得与垃圾食品一样快速有效只要我们拿起手机就能刷抖音享受视觉盛宴美食塞进嘴巴里就能获得快感点上只烟就能飘飘欲仙喝杯酒就能醉生梦死大脑左右了我们明知道垃圾快感会对我们产生危害它却欺骗我们说由于不会马上产生危害那就先获得快感等危害真的来了再说于是这些人在大脑的自我欺骗中吞云吐雾纵情狂欢人的健康和智慧在经年累月后逐渐瓦解&quot;&gt;垃圾快感的特点是容易获得，与垃圾食品一样快速有效，只要我们拿起手机就能刷抖音享受视觉盛宴，美食塞进嘴巴里就能获得快感，点上只烟就能飘飘欲仙，喝杯酒就能醉生梦死，大脑左右了我们，明知道垃圾快感会对我们产生危害，它却欺骗我们说，由于不会马上产生危害，那就“先获得快感，等危害真的来了再说”。于是这些人在大脑的自我欺骗中吞云吐雾、纵情狂欢，人的健康和智慧在经年累月后逐渐瓦解。&lt;/h3&gt;

&lt;p&gt;无法抵制诱惑，是我们诸多痛苦的来源之一。于是相对的，另一种快感并不这么容易获得，它也会分泌多巴胺，而且分泌的还不少，只是没有这么容易。&lt;/p&gt;

&lt;h3 id=&quot;规律的运动积极或激烈的讨论达成新的目标完成某件事充足的睡眠饱满的精力都会分泌多巴胺带来快感&quot;&gt;规律的运动、积极或激烈的讨论、达成新的目标、完成某件事、充足的睡眠饱满的精力，都会分泌多巴胺带来快感。&lt;/h3&gt;

&lt;h3 id=&quot;快感会推动我们继续去寻找更多的快感这是大脑享受后的反应如果我们能自律的放弃垃圾快感转而追求对长期更有益的快感来推动我们前进的话我们的成就能力智慧就会被快感带领着走向巅峰&quot;&gt;快感会推动我们继续去寻找更多的快感，这是大脑享受后的反应。如果我们能自律的放弃垃圾快感，转而追求对长期更有益的快感来推动我们前进的话，我们的成就、能力、智慧就会被快感带领着走向巅峰。&lt;/h3&gt;

&lt;h3 id=&quot;那么怎样才能控制自己的快感让它在更有益的方向上释放呢靠的就是自律和延迟满足为了让多巴胺每天都分泌多一些我们每天都要有充足的睡眠规律的运动制定一些小目标积极的分享和交流为了不让自己及时行乐我们推崇延迟满足让自己在积累后从成就中得到更大的快乐就这样慢慢地我们的成就会越来越大智慧会越来越多多巴胺也不断在分泌不断推动着我们继续向前形成了一个良性的循环&quot;&gt;那么怎样才能控制自己的快感，让它在更有益的方向上释放呢？靠的就是自律和延迟满足，为了让多巴胺每天都分泌多一些，我们每天都要有充足的睡眠、规律的运动、制定一些小目标、积极的分享和交流。为了不让自己及时行乐，我们推崇延迟满足，让自己在积累后从成就中得到更大的快乐。就这样慢慢地我们的成就会越来越大，智慧会越来越多，多巴胺也不断在分泌，不断推动着我们继续向前，形成了一个良性的循环。&lt;/h3&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(五十一) 回顾自律</title>
   <link href="http://www.luzexi.com/2020/08/31/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A851"/>
   <updated>2020-08-31T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/08/31/思路探讨51</id>
   <content type="html">&lt;p&gt;趁着最近休息，回顾一下最近大半年自己做的事情。&lt;/p&gt;

&lt;p&gt;自律仍然是我的主旋律，我用每日打卡的方式，让自己不忘记每天需要做的事。我为自己列了一个表，这个表上每天都有自己要做的事情，虽然量不是很大，但足以提醒自己每天要完成的任务，毕竟我还有工作要做，而且最近半年的工作压力也特别的大。&lt;/p&gt;

&lt;p&gt;来看看打卡的记录：&lt;/p&gt;

&lt;p&gt;[办公室打卡图]&lt;/p&gt;

&lt;p&gt;[宿舍打卡图]&lt;/p&gt;

&lt;p&gt;我把打卡分成办公室打卡和宿舍打卡，分别贴在公司的座位上和宿舍的墙上，让自己无论在公司还是在宿舍都能清楚的知道自己除了工作还有什么事情需要做。&lt;/p&gt;

&lt;h3 id=&quot;打卡的目的是坚持和培养习惯这是我自从看过微习惯后从书中领悟到的东西即我要的是一个长期的东西这个东西能不断地积累并带给我越来越多的好处可能它一开始几年会慢一些效果很不明显但随着年份的积累它会形成一条像护城河那样的壁垒让我成为人群中的佼佼者逐渐与他人拉开差距强壮的身体良好的习惯和优秀的思维方式就是我所追寻的那个能带给我无尽好处的长期的东西它无法靠一时的冲刺来拉近距离&quot;&gt;打卡的目的是坚持和培养习惯，这是我自从看过《微习惯》后，从书中领悟到的东西。即，我要的是一个长期的东西，这个东西能不断地积累并带给我越来越多的好处，可能它一开始几年会慢一些，效果很不明显，但随着年份的积累，它会形成一条像护城河那样的壁垒，让我成为人群中的佼佼者，逐渐与他人拉开差距。强壮的身体、良好的习惯和优秀的思维方式，就是我所追寻的那个能带给我无尽好处的长期的东西，它无法靠一时的冲刺来拉近距离。&lt;/h3&gt;

&lt;p&gt;[滴答清单]&lt;/p&gt;

&lt;p&gt;虽然我最近3年一直坚持着这个理念，但最初那一年并没有想的很明白。那时我不明白的还有很多，包括我要成为什么样的人，我究竟想要什么，我活着的意义是什么，等等。即使在现在也依旧有些模糊，但这并不妨碍我继续前进。&lt;/p&gt;

&lt;p&gt;庆幸的是我开始逐渐有了自己独特的见解，与众人观察到的有所区别，是完全属于自己的理念和体系。&lt;/p&gt;

&lt;h3 id=&quot;这对我来说是比较关键的一个突破这使得我可以经营自己的理念即使它不怎么样我也可以通过不断改善来完善自己的体系从而让它变得更加契合这个世界并带着我走在世界的更前头&quot;&gt;这对我来说是比较关键的一个突破，这使得我可以经营自己的理念，即使它不怎么样我也可以通过不断改善来完善自己的体系，从而让它变得更加契合这个世界并带着我走在世界的更前头。&lt;/h3&gt;

&lt;p&gt;[时间块]&lt;/p&gt;

&lt;p&gt;所有的这些都跟时间有关，我控制着时间，也被时间控制着，这是个相互作用的过程。我并没有使用时间管理，因为这不符合人性，但我也不排斥使用时间管理，它有其长处，我们不应该带有偏见的眼光看待它。这些记录都只是我希望能更好的控制我自己去达到自己想要达到的旅途中的一些过程和记录​。&lt;/p&gt;

&lt;p&gt;我知道很多厉害的家伙已经习惯把时间按掐的很准确而没有一丝心理反抗，而我还不行，我只能把每天的时间分成很多个15分钟，每个15分钟我都会记录下这15分钟里做了什么。这样我每天结束时就能知道今天做了什么，或者今天的效率如何。第二天我可以知道前一天或前一段时间的做事的效率和自律执行的情况，从而不断调整自己的状态。&lt;/p&gt;

&lt;h3 id=&quot;这些对时间的理解我都是跟跟时间做朋友学的这本书给了我很多启发包括下面这段话我刚又从我的笔记中翻出来看了一遍觉得自己应该更多的向这方面靠拢去更多的帮助别人鼓励别人我们在帮助和鼓励别人的同时自己的能力视野得到了进一步的提升&quot;&gt;这些对时间的理解我都是跟《跟时间做朋友》学的，这本书给了我很多启发。包括下面这段话，我刚又从我的笔记中翻出来看了一遍，觉得自己应该更多的向这方面靠拢。去更多的帮助别人、鼓励别人，我们在帮助和鼓励别人的同时，自己的能力、视野得到了进一步的提升。&lt;/h3&gt;

&lt;p&gt;“当我们不停地鼓励所有人的时候，最大的受益者其实是我们自己，因为最终我们会发现，自己开始进入一种他人无法想象的状态，成为一个不需要他人鼓励的人。这一点很重要，原因就在于他们是必须获得别人的鼓励才敢于行动的人。可以，我们却能成为另一种人，不需要别人鼓励就能不断前进的人，这是一种境界。”&lt;/p&gt;

&lt;h3 id=&quot;我仍然在很多事情上做的不够特别是在长期重要的事情和短期重要的事情中我时常搞不清楚到底谁比较重要这使得我常疲于应付手头上紧急的事情而忽略了长期重要的事情过后我又常常自责没有好好守护自己的原则这也是我时常为自己无法看清这个世界而痛苦的原因&quot;&gt;我仍然在很多事情上做的不够，特别是在长期重要的事情和短期重要的事情中，我时常搞不清楚到底谁比较重要，这使得我常疲于应付手头上紧急的事情而忽略了长期重要的事情，过后我又常常自责，没有好好守护自己的原则。这也是我时常为自己无法看清这个世界而痛苦的原因。&lt;/h3&gt;

&lt;h3 id=&quot;这也激励着我继续前进失败是很平常的成功只是偶然我希望我们能够坚持积累当质变到来时我们就会有更大的成就感&quot;&gt;这也激励着我继续前进，失败是很平常的，成功只是偶然。我希望我们能够坚持积累，当质变到来时，我们就会有更大的成就感。&lt;/h3&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(五十) 去同质化</title>
   <link href="http://www.luzexi.com/2020/08/28/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A850"/>
   <updated>2020-08-28T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/08/28/思路探讨50</id>
   <content type="html">&lt;p&gt;由于我们从小到大的经历了不同，所以我们每个人的个性都不一样，但在对待工作和学习时却总是趋于同质化。&lt;/p&gt;

&lt;p&gt;年轻时我们都喜欢让自己特别些，喜欢穿些稀奇古怪的搭配，从不拘泥于传统，嘴里总是挂着“我可不是普通人”、“好酷啊”。&lt;/p&gt;

&lt;p&gt;成年后，我们被社会打磨的没有了棱角，特别是在对待工作和学习这两个问题上，大家都特别的小心，以至于小心得过了头，没有了自己的主见，鲜少的深入思考。&lt;/p&gt;

&lt;p&gt;我们这样小心翼翼的的原因可以有很多，这样的借口我自己都能找出很多，例如有房贷压力、有家庭需要养活、经济萧条工作不好找等等。这些都是我们自己束缚自己的借口，自己给自己设了一个笼子。其根源是我们人不想改变，对改变有恐惧感，所以想在舒适区里多待会。&lt;/p&gt;

&lt;p&gt;如果你想成为高人，想让自己有所成，就得直面这些困难，主动出击，而不是躲躲藏藏被动受敌。&lt;/p&gt;

&lt;p&gt;说的简单点，就是让自己变得特别些。当你变得特别时，你才有可能突破重围，至少你能被大家记住，而不像其他人那样一转身就被人忘得一干二净。&lt;/p&gt;

&lt;p&gt;问题是我们被同质化太久，已经忘记了什么是特别。&lt;/p&gt;

&lt;h3 id=&quot;同质化使得我们发现不了我所处的位置&quot;&gt;同质化使得我们发现不了我所处的位置。&lt;/h3&gt;

&lt;p&gt;例如我是一个程序员，作为以写程序为生的程序员，无论是普通的还是资深的还是专家，大部分都只是整个工作流水线上的一个节点，开发着功能和组件。如果大家都是流水线上的节点，那么谁来制造流水线呢？&lt;/p&gt;

&lt;p&gt;让自己特别些，去发现和创造新的流水线，让自己成为流水线的创造者，而不是维护者。&lt;/p&gt;

&lt;p&gt;大部分程序员都用一门语言搞定工作，一个IDE，一个操作系统，一种类型项目，一个引擎从头用到尾。我们不如做的特别些，多学几门语言，多用几个IDE，多做几种类型的项目，多学习几种类型的引擎和操作系统。&lt;/p&gt;

&lt;p&gt;做得特别些，这能让我们的视野更加开阔，也很容易能让别人记住我们的才华。&lt;/p&gt;

&lt;h3 id=&quot;同质化使得我们一直沿着某个方向行走而学不会拐弯&quot;&gt;同质化使得我们一直沿着某个方向行走而学不会拐弯。&lt;/h3&gt;

&lt;p&gt;这就像脑筋急转弯里的问题，“老王没有头发却为什么每天都去理发店？”，题目暗示了‘老王’去理发，因为我们的脑袋被同质化了，通常去理发店都是理发，谁能想到老王其实是理发师呢。&lt;/p&gt;

&lt;p&gt;工作中和生活中，我们常常陷入别人给我们的暗示，例如队友告诉你出Bug的原因，上级告诉你工作的方向，老师告诉你学习要努力，妈妈告诉你和她不可能结婚等等。&lt;/p&gt;

&lt;p&gt;根据我的经验，别人给出的方向通常使得我们走更多的弯路。不如做的特别些，甚至不妨往反相走一走，不按队友给出的套路查Bug，上级给出的方向当作次要方向自己再想一个更重要的方向去突破，不死记硬背劳逸结合多维度开发兴趣，来个“曲线救国”让不可能成为可能，逆水行舟可能是很难，但也有可能是最近的捷径，不要被他人的提示所局限。&lt;/p&gt;

&lt;h3 id=&quot;同质化使我们远离危险却离危机更近&quot;&gt;同质化使我们远离危险却离危机更近&lt;/h3&gt;

&lt;p&gt;环境会被我们习惯，无论是工作环境还是生活坏境，都渐渐趋向于平稳和熟悉，舒适区是真的舒服，它看起来让我们远离危险，实际上却离危机越来越近。当真正危机来临时，没有与危险相处习惯的我们会很受伤。&lt;/p&gt;

&lt;p&gt;不如做的特别些，当大家都在安逸时，我们多走一步，多折腾一把，很可能就是另一番天地。时刻保持警惕，烫水不可怕，烫伤了我们可以缩回来治疗，最怕的是温水煮青蛙，当明白过来时可能我们已无力回天。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>给女儿写信(四) 爸爸在输出</title>
   <link href="http://www.luzexi.com/2020/08/24/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A14"/>
   <updated>2020-08-24T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/08/24/给女儿的信4</id>
   <content type="html">
&lt;hr /&gt;

&lt;p&gt;导读：喜欢跟孩子们一起，可惜一直在外地工作，因此我给孩子写点信并录成视频，跟孩子聊聊自己的故事以及最近的感受&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Hi Sharon and Anne, 爸爸周四回来了，爸爸有5天的假期来陪你们，爸爸想你们也很爱你们。&lt;/p&gt;

&lt;p&gt;最近爸爸在学习上知道了一件事，想跟你们说一说。爸爸最近几年学了很多东西，在脑子里装满了知识，但有时学过了的知识总是忘记，特别是要用到的时候总是记不起来。有些知识爸爸在书里看到过的，或者从别人嘴里听到过的，甚至自己还背过，总是不能在该用到的时候记起来。&lt;/p&gt;

&lt;p&gt;爸爸琢磨了很久，为什么自己学过的知识，总是忘记呢。后来爸爸发现了，原来自己从书里看过的，从别人嘴里听过的知识，是无法在脑袋里留下深刻的印象的，因为我们只是看过或者听过。于是爸爸琢磨着，怎么才能让这些学过的知识和听过的知识，有更深刻的印象。&lt;/p&gt;

&lt;p&gt;爸爸找到了一个很好的方法，那就是讲给别人听。当爸爸把自己知道的知识和事情讲给别人听的时候，这些知识和事情就深深的留在了爸爸的脑袋里，爸爸讲的次数越多，知识在脑袋里留下的印象越深刻。这个方法让爸爸的学习的效率倍增。&lt;/p&gt;

&lt;p&gt;于是爸爸每次遇到别人聊起某个知识时，爸爸就会滔滔不绝的跟他讲爸爸所有知道的事情。有时候爸爸讲的并不好，于是爸爸就回去查资料，看书，把不知道的部分再补充一下，下次再遇到他或者其他人聊起这个知识时，爸爸就再跟他们讲一次。&lt;/p&gt;

&lt;p&gt;可能跟别人讲自己知道的知识机会很难得，所以爸爸每次都要好好把握好好讲。&lt;/p&gt;

&lt;p&gt;除了教别人知识外，爸爸还把自己知道的知识写下来，发到网上去，这样就是在教大家学知识了，不仅让大家学到了知识，自己也对知识有了很好的梳理和巩固。&lt;/p&gt;

&lt;p&gt;Sharon Anne 如果你们想让学习更高效，记住的东西更多，记得把它们讲出来，说给别人听哦，多多教别人怎么做怎么写怎么说，这样我们自己会有很大的进步哦。&lt;/p&gt;

&lt;p&gt;爸爸周四就回来看你们，爱你们哦！&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(四十九) 对思维笼子的反思</title>
   <link href="http://www.luzexi.com/2020/08/17/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A849"/>
   <updated>2020-08-17T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/08/17/思路探讨49</id>
   <content type="html">&lt;p&gt;前面有篇文章我们说到我们都生活在笼子里，只是每个人属于自己的笼子大小不同，有些人觉得笼子里挺舒服的，就会在里面呆很久，另一些人则认为自己应该去更大更好的笼子，于是更多的折腾自己寻找出路，当然出路也并不一定通向更大更好的笼子，我们也有走歧路的时候。&lt;/p&gt;

&lt;p&gt;笼子没有极限，永远有更大更好的笼子在等着你去寻找打开它的钥匙，我们也应该量力而为。打开它的钥匙也有很多种，其中一种就是自律，自律不仅体现在对时间和精力的管理上，还体现在健康和心态的管理上。&lt;/p&gt;

&lt;p&gt;另一种是思维方式，不同高度的思维方式所带来的效果会截然不同。我拿德州扑克来做个比喻，德州扑克的规则很简单，就是个比大小的赌博游戏，每个人都有两张只有自己看见的牌，再与桌上的明牌合起来看谁比较大，它有5轮下注的机会，每轮每个人都有机会下更大的注或跟注。&lt;/p&gt;

&lt;p&gt;当你是个初级玩家的时候，你脑袋里想的都是我有什么牌，牌越大下注也会越大，敢跟敢拼。&lt;/p&gt;

&lt;p&gt;然后你进阶了，到了第二阶，你脑袋里想的都是对方有什么牌，因为只要知道对方有什么牌，你在押注和弃牌时会更加准确且有信心，胜算自然就会更大。&lt;/p&gt;

&lt;p&gt;接着你又进阶了，到了第三阶，你脑袋里想的是对方认为你有什么牌，因为牌在还没翻开来前都是相对的，对方的恐惧与骄傲是你取胜的重要因素。&lt;/p&gt;

&lt;p&gt;继续进阶，到了第四阶，你脑袋里想的是“对方认为你的眼中他是什么牌”，这时更高级的斗智斗勇开始了，对方想要利用你的恐惧和骄傲来反制你，而你则要利用他的小聪明来反制他，究竟谁能最终反制，谁就是那个能站在更高层级下看问题的人。&lt;/p&gt;

&lt;p&gt;最后一个阶段，不猜测也猜测，明与暗并存，虚虚实实混淆不清，想要战胜对方的欲望小了，被别人看穿的可能性也少了许多。&lt;/p&gt;

&lt;p&gt;高层级的思维方式有着截然不同的境界。不过这种竞争思维还是有些狭隘的，不是非常恰当，我们可以看些更高大上的思维方式转换，比如我们工作上的工作能力。&lt;/p&gt;

&lt;p&gt;什么是工作能力，大部分时候我们还是给自己设了一个限，说，我的技术就是我工作的能力。这是第一阶段的能力，我关心的是我自己，我脑袋里想的都是自己，包括，我有什么，我知道什么，我能做什么，我能得到什么。&lt;/p&gt;

&lt;p&gt;个人的创造力和创新是非常有限的，很多时候我们无法突破自己时，通常是因为没有更多的互动和分享，教和讲是最高的学习效率，第二阶段，我们与周围的人有更多的互动，创意与点子则源源不断的从我们的脑子和嘴巴里蹦出来。这时你知道讲出来分享出来才会有更多的思考和思路。&lt;/p&gt;

&lt;p&gt;前两个阶段毕竟还是‘我’的一个人能力，第三阶段，我们该打破‘我’的观念，这时你充分认识到了个人能力是有限的，无论我多牛逼一天还是24小时，放弃自己对想要全部自己做的念头，让更多人帮助你来完成任务是这个阶段我们的主心骨。&lt;/p&gt;

&lt;p&gt;第四阶段，你充分意识到合作的潜力，你主动放弃了自己的研究欲望和成果，转而主动把这个机会让给别人，同时也要求自己主动打破‘我与他人的沟通壁垒’，说服更多人来帮助你完成任务。这时你的工作基本上都是项目管理和HR上的事，你不但要寻找和把控方向以让所有人都知道方向在哪里，以免做无用功，还要寻找和说服人们来一起帮你完成任务。&lt;/p&gt;

&lt;p&gt;第五阶段，你已经打破了你与他人，甚至是陌生人之间的沟通壁垒，你能聊会侃，有着开放的心态，有渊博的知识和丰富的经验，能集思广益，对人对事有包容力，但毕竟依然是你自己的事，他人与他人之间的关系与协作效率我们依然无法改变。怎么让人与人之间的协作更融洽更高效，是我们这个阶段要做的事。这时我们要更多的放弃自己所谓的能力，你的能力反而激化了矛盾，我们要从台前走向幕后，用潜移默化的方式影响他人，让人与人之间的合作更加​融洽。​&lt;/p&gt;

&lt;h4 id=&quot;没有人能够给我们设限给我们设限的只有我们自己每个阶段都要主动放弃些什么才能得到更多大部分人都由于自身的原因无法放弃这种掌握在手的安全感这种安全感可以是某种技术也可以是金钱或者是某种引以为傲的东西或者是某种认知&quot;&gt;没有人能够给我们设限，给我们设限的只有我们自己，每个阶段都要主动放弃些什么，才能得到更多。大部分人都由于自身的原因，无法放弃这种掌握在手的‘安全感’，这种安全感可以是某种技术，也可以是金钱，或者是某种引以为傲的东西，或者是某种认知。&lt;/h4&gt;

&lt;h4 id=&quot;对我来说也是我正竭力消除自己愚昧和肤浅的偏见直面自己的懦弱拥抱脆弱放手一些所谓的安全感做一些比自身安全感更重要的事情帮助他人进步团结人们一起来更好的完成任务&quot;&gt;对我来说也是，我正竭力消除自己愚昧和肤浅的偏见，直面自己的懦弱拥抱脆弱，放手一些所谓的安全感，做一些比自身安全感更重要的事情，帮助他人进步，团结人们一起来更好的完成任务。&lt;/h4&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(十) 《林徽因》</title>
   <link href="http://www.luzexi.com/2020/08/02/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B010"/>
   <updated>2020-08-02T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/08/02/读书笔记10</id>
   <content type="html">&lt;p&gt;3周前读完的这本书，一直忙于工作没能抽出时间来做一下记录，今天来回忆下林徽因的一生。&lt;/p&gt;

&lt;p&gt;林徽因出生在浙江杭州，母亲没什么文化且素质不高，在那个年代女人没有资格上学接受正规的教育，这在整个世界其他文明地区也有这样一段经历，女人没有权利接受正规的教育，无知的认为‘女子无才便是德’，其根本原因是主导社会的那些愚蠢的男人们认为女人是比她们低一级别的动物，不应该站在同一起跑线上，所以通常在那个愚蠢的时代男人们并没有给予女人足够的尊重。&lt;/p&gt;

&lt;p&gt;林徽因就出生在那个即将变迁的时代，幸好她的父亲是一个非常开明、视野开阔且有文化的人，但纵使有这样的父亲，林徽因在当时的生存也受到社会巨大的束缚。她不得不让自己更加努力刻苦、’懂事‘，以优秀的表现来维持包括父亲、爷爷以及其他家人对她的支持。&lt;/p&gt;

&lt;p&gt;林徽因的祖上世代为官，到爷爷林孝恂这代家道中落，林孝恂虽然是晚清官吏，头顶乌纱帽，但骨子里却是一身书卷气。他见多识广，知识渊博，再加上敞开怀抱接受西方政法思想，他的境界早已超越士官同僚。林孝恂的妻子游氏也并非小脚女人，她喜好书法，平素阅读典籍为乐，文化素养极高。&lt;/p&gt;

&lt;p&gt;林徽因的父亲林长民被父亲林孝恂寄予厚望，林长民两度远赴东洋留学，得中外文化双重滋养，再加上热衷于广交政界名流，年纪轻轻已经有了改良中国社会的宏伟抱负。&lt;/p&gt;

&lt;p&gt;无论父亲有多开明，在当时的时代受到时代的制约，父亲的思想仍然是受到侵蚀的。她的父亲林长民，一共娶了3个妻子，第一个因病去世，第二个就是林徽因的母亲，由于没有文化素养脾气不好与林长民不合，所以林长民又娶了一个二房并且生下了3个儿女。&lt;/p&gt;

&lt;p&gt;林徽因就是那个与父亲不合的母亲的女儿。我想就是这个原因，林徽因很小就知道了该如何获得父亲和爷爷的欢心，她知道获得周围人的喜欢才能在这个大家庭中立足。小孩子不会想太多，有好的反馈她就会去争取，她跟着爷爷学读书写字，获得了爷爷奶奶的疼爱，也就获得了大家庭的喜欢。母亲不会教她这些，母亲也没有好好教育过女人，因为爷爷和奶奶不放心把林徽因交给没有文化的母亲去教育，所以爷爷奶奶一直把林徽因带在身边教她读书写字，林徽因从小十分要强，也是因为母亲在家庭中没有地位，她只能靠自己去争取。&lt;/p&gt;

&lt;p&gt;林徽因深得爷爷奶奶和父亲的宠爱，八岁进了上海爱国小学读书，这所学校由蔡元培创办师资力量雄厚是当时百所名校之一。10岁时爷爷和奶奶相继离世，家里剩下自己的母亲和二房两个大人，父亲又常年在外，母亲从来不干活也不参与家庭事务，像是生活在另外一个世界。11岁那年政局动荡，一家人去了天津英租界住下，父亲在北平任职，父亲常年不在家，二房生孩子后身体不好，母亲又指靠不上，11岁的林徽因不得不用瘦小的身体扛起了家庭里的事务打点。&lt;/p&gt;

&lt;p&gt;那段时间的特殊境遇，让她很快成熟起来。从家里的吃喝用度，到照顾二房和弟妹，以及抚慰脾气不好的母亲，11岁的林徽因安排的井然有序，处理得妥贴稳当，并常写信告知父亲家里的事情。林徽因如此尽心尽力，一方面因为懂事，心系大局，对家庭有责任感；另一方面则是想证明自己的能力，希望得到父亲的肯定。&lt;/p&gt;

&lt;p&gt;如此乖巧懂事，聪明伶俐，美丽大方的女孩谁不喜欢，14岁那年，梁启超的带着17岁的儿子梁思成去了老朋友林长民家，梁思成一见林徽因清纯可爱，楚楚动人，如天女下凡，从此以后梁思成的目光再也没有离开过林徽因。&lt;/p&gt;

&lt;p&gt;之后中国政治时局更加动荡，林长民政坛失落，借着赴欧洲考察之机，带着16岁林徽因去了欧洲看看外面的世界。欧洲之行是林徽因一生的转折点，不仅影响了她的人生观、世界观、最重要的是奠定了她对学习建筑艺术坚定的信心。林徽因非常好学，她非常珍惜这次机会在欧洲期间每天都看书学习，并且在游历期间记录下自己的心得体验。在一个机缘巧合，她与伦敦房东的闲聊时，听到了很多关于建筑艺术的故事与知识，因为这位房东正是一位伦敦有名的建筑师。&lt;/p&gt;

&lt;p&gt;也是在那年林徽因遇上了才华横溢、知识丰富、语言幽默的才子徐志摩。徐志摩从此也变成了林徽因的忠实粉丝。林徽因16岁是情窦初开的年纪，遇上如此有才又帅气的男人当然心花怒放，他们在伦敦度过了一段美好的回忆，但那时徐志摩已经是有妇之夫，虽然妻子是家里安排的徐志摩并不喜欢，借出国留学知名回避妻子，但毕竟是有妇之夫。当林徽因知道此事后，理智告诉她，她正破坏另外一个女人的家庭，深思熟虑之下婉拒了徐志摩的爱意。徐志摩当然不甘心放弃，在林徽因回国后做出了离婚的决定，但这也并没有让林徽因回心转意，她知道徐志摩情感丰富，他爱上了幻想中完美的自己，而且无论怎样她仍然无意中破坏了别人的家庭，回国后她始终对徐志摩避而不见，直到她与梁思成订婚并打算去美国留学。&lt;/p&gt;

&lt;p&gt;梁思成是林徽因遇到的最爱她的男人，他有渊博的知识、沉稳的心智，他不仅包容她、爱护她，并且时常鼓励她、给予她信心。梁思成与林徽因，他们互相依赖相互成就，性格也正好互补，梁思成内向沉稳，林徽因则外向开朗。林徽因因为急躁的性格时常向梁思成发脾气，而梁思成则默默包容她并鼓励她支持她从困境中走出来。&lt;/p&gt;

&lt;p&gt;梁思成与林徽因1924年去了美国康奈尔大学留学，一呆就是4年，他们在美国刻苦学习，彼此照顾，获得优异的成绩，备受教授们和同学们的喜欢。期间林徽因的父亲，在战乱中丧生，林徽因痛哭流涕想要立马回国，梁思成的父亲梁启超为了不影响她的学业写信尽力安抚她，并为林徽因安排妥当了父亲的后事。1928年回国后他们在东北大学建筑系任教，林徽因负责讲授美术与建筑设计，梁思成负责讲授建筑学概论和建筑设计原理以及具有独特风格的西洋建筑史。回国后1年1929年梁启超病逝，林徽因和梁思成悲痛万分。&lt;/p&gt;

&lt;p&gt;1929年林徽因生了个宝贝女儿取名为梁再冰，为了纪念敬爱的父亲梁启超“饮冰室的辉煌”。林徽因生完孩子后由于过度劳累，病倒了，从此身体一直不见好转，疼痛和病魔一直缠绕着她。不久林徽因由于身体原因辞去了学校的职务回到故乡静养了一段时间。&lt;/p&gt;

&lt;p&gt;1931年林徽因和梁思成回到了北平，他们参加了朱启钤创办的中国营造学社，致力于中国传统建筑的研究与保护。他们在北总布胡同三号定居下来，从此这里就成了当时有名的“太太的客厅”，每逢周末这里都传出朗朗的笑声，许多才子才女，博学多才的文人雅士都在这里聚集彼此交流包括徐志摩。&lt;/p&gt;

&lt;p&gt;1931年11月徐志摩飞机失事，林徽因痛心万分潸然泪下。&lt;/p&gt;

&lt;p&gt;1932年林徽因生下二胎取名“从诫”，意在希望他步宋代李诫后尘，在建筑方面有所成就，最后儿子读了北大的历史系，热衷于关注环保问题，被国家总局授予“环境使者”称号，毕生致力于文物和环境保护。&lt;/p&gt;

&lt;p&gt;“太太的客厅”是由作家冰心在《大公报》上发表的一篇小说，很多人认为这篇小说是在讽刺林徽因，可见当时林徽因的太太的客厅有多有名，许多著名学者都以能在太太的客厅聊天交流为荣。&lt;/p&gt;

&lt;p&gt;就因为这个太太的客厅，出现一个一直没有离开林徽因的人，中国哲学的开山鼻祖金岳霖，是他在清华大学创办了国内第一个哲学系。&lt;/p&gt;

&lt;p&gt;金岳霖一直好奇徐志摩爱的死去活来的女神是什么样的女人，于是有天被徐志摩邀请去参加太太的客厅，从此以后金岳霖就成了太太客厅的座上宾，不但如此，比林徽因大10岁的金岳霖还不可救药的爱上了林徽因，终生未娶，一直跟随着梁思成与林徽因，做他们的邻居。&lt;/p&gt;

&lt;p&gt;这段情很耐人寻味，金岳霖痴情，梁思成包容，林徽因理智。就促成了后面的故事，金岳霖一直跟随他们到各地做他们的邻居，梁思成则依然跟金岳霖是好朋友，林徽因则理智应对金岳霖的爱意。最后金岳霖痴到什么程度呢？林徽因去世那年，他当着学生的面痛哭失声。在林徽因去世后，金岳霖还设宴叫来老朋友给林徽因过生日。&lt;/p&gt;

&lt;p&gt;1937年开始，国内战乱爆发，日本借卢沟桥事变发动战争。7月北平沦陷，梁思成与林徽因开始了一段长达8年的流亡生活，出发前他们把在营造学社考察和拍摄的建筑物底片和写的资料整理并放在了天津英租界的外国银行地下保险库中，想着等战乱平息后再来取，可谁也没想到外国银行的地下保险库也没能保住他们7年的汗水。&lt;/p&gt;

&lt;p&gt;流亡中，他们去了长沙，长沙又被攻陷，他们又去了昆明，昆明被轰炸，他们又去了十五公里外的龙头村，他们在那里造了自己的房子。房子造好后也只持续了半年，日军不断攻陷城市，昆明也不再安全。1940年他们又再次投入到流亡大军中，最终抵达了长江边上的一个偏远小镇，李庄。在这里他们生活了5年，这里没有水没有电，几乎没有医疗设备，林徽因在这里病的很严重，即使这样她也没有放弃看书和查阅资料。直到1945年12月，在林徽因外国好友的安排下离开李庄赴重庆治病。&lt;/p&gt;

&lt;p&gt;1944年梁思成和林徽因历尽辛苦完成了《图像中国建筑史》，这本书是用英文写的，梁思成把他带到美国等待时机再出版，可没想到的是，这本书一直到1984年才出版，其中的故事也是非常曲折。&lt;/p&gt;

&lt;p&gt;1945年日本宣布无条件投降后，林徽因在昆明修养了几个月，身体稍微好转后于1946年7月与梁思成返回北平。梁思成在清华大学担任建筑系主任，成立了建筑研究所，担任研究所所长。林徽因不计报酬为梁思成分担工作，在梁思成考察美国时，林徽因来处理建筑系各种杂事，年轻的教师会经常向她请教。&lt;/p&gt;

&lt;p&gt;1949年北平解放，林徽因被正式聘为清华大学建筑系一级教授，主讲《中国建筑史》。7月林徽因开始主导设计新中国国徽。10月梁思成和林徽因开始对人民英雄纪念碑进行设计，经过反复推敲，她抱病完成了图案设计。&lt;/p&gt;

&lt;p&gt;1950年6月23日，全国政协一届二次大会上，以林徽因为主设计的中华人民共和国国徽，得到了全体代表的一致通过。&lt;/p&gt;

&lt;p&gt;1951年林徽因拖着病体挽救了濒临停业的景泰蓝传统工艺。&lt;/p&gt;

&lt;p&gt;1953年林徽因挽救了四朝五都仅存的完整牌楼不被拆除。10月被选为建筑学会理事，并担任《建筑学报》编委，同时受邀参加第二届全国文代会。&lt;/p&gt;

&lt;p&gt;1954年林徽因当选为北京市人民代表大会代表。&lt;/p&gt;

&lt;p&gt;1955年，林徽因病情再次恶化，4月1日，中国第一代著名女建筑师、国民第一才女林徽因永远闭上了眼睛。临别时没有留下遗言，面对这个美丽而又遗憾的世界，她已经倾尽全力。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>给女儿写信(三)  爸爸在学习</title>
   <link href="http://www.luzexi.com/2020/07/26/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A13"/>
   <updated>2020-07-26T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/07/26/给女儿的信3</id>
   <content type="html">
&lt;hr /&gt;

&lt;p&gt;导读：喜欢跟孩子们一起，可惜一直在外地工作，因此我给孩子写点信并录成视频，跟孩子聊聊自己的故事以及最近的感受&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Hello Sharon and Anne，爸爸好想你们，爸爸也好想妈妈。爸爸已经3个月没有回家来看你们了，爸爸好想回来但爸爸还要工作和学习，可能要到十月一日的国庆节时才能回来，但到那时也不一定哦，你们可要做好心理准备哈，抱歉Sharon and Anne 爸爸有点忙。&lt;/p&gt;

&lt;p&gt;爸爸在忙什么呢？爸爸在忙工作，爸爸在一家公司里工作，每天公司都会交给爸爸一些任务去完成，为了奖励爸爸完成的工作，公司会每个月发钱爸爸，这样爸爸就能把钱给妈妈，妈妈把钱存起来慢慢的钱就变多了，我们买好吃的，买好看的衣服，去旅行，去郊游就能花钱啦。&lt;/p&gt;

&lt;p&gt;除了忙工作，爸爸还在忙着学习和看书。大家都在不断学习新的知识，爸爸也不能落下啊。爸爸每天都要些书补充下知识，每天也要写一些对学习到的知识的总结，让看过的书和学习到的知识，再在脑袋里再复习一遍，这样才能更好的掌握学过的东西。如果只是学习和看书，没有去整理和巩固的话，就白白的浪费了前面学习的时间，因为我们很快就忘记了学过的东西，特别是眼睛看过一遍，耳朵听过一遍的东西，很快就忘记了，所以爸爸时常要写一些对前面学过的知识的总结。&lt;/p&gt;

&lt;p&gt;最近爸爸接到一项比较重要的任务，爸爸忙的不可开交，有时会到凌晨2、3点，这样对身体不太好，所以爸爸一直在调整自己的，让自己恢复到正常的状态，平时多运动，早睡早起身体好，这样我们的学习效率也会增加。&lt;/p&gt;

&lt;p&gt;Sharon、Anne，爸爸很爱你们，爸爸也很爱妈妈！&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》 前言</title>
   <link href="http://www.luzexi.com/2020/07/26/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-%E5%89%8D%E8%A8%80"/>
   <updated>2020-07-26T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/07/26/Unity3D高级编程之进阶主程-前言</id>
   <content type="html">&lt;h2 id=&quot;前言&quot;&gt;前言&lt;/h2&gt;

&lt;h3 id=&quot;为什么要写这本书&quot;&gt;为什么要写这本书&lt;/h3&gt;

&lt;p&gt;本书该从很多年前说起，毕业前由于自视甚高错过了好几次绝佳的学习机会，那时是个名副其实的“幼稚男”，清醒过来时周围的人已经走了很远的路了，那夜猛然醒悟开始告诉自己不能再幼稚了。非常幸运的是我赶上了Unity3D引擎大潮，版本从2.6开始一直学习到现在，它提供给了我更低的门槛让我跨过前人需要数年甚至数十年才能跨过的技术难关，于是我一路拼命学习一直走到了现在，虽然对游戏业界来说仍然是个小菜但比起以前已经好了非常多了。&lt;/p&gt;

&lt;p&gt;从2013年开始我就开始担任Unity3D主程一职，虽然当时只是个菜鸡主程，但当时的我可并不这么认为，我认为自己的技术已经强大到可以驾驭整个客户端，并且能扎实完成整个游戏项目从开发到上线，年轻人敢闯敢拼的劲头在那时的我身上体现的淋漓尽致。那是我第一次当主程，却敢承诺老板说自己能在3个月内开发出对方想要的游戏，那时自己吹牛可以不打草稿，也从不想后果。最后游戏项目3个月又3个月，开发了半年终于出来了。不管怎样，事情从这里开始一发不可收拾，手机游戏从2013年开始火遍整个国家和全球，我也被浪潮推到了各公司里担任主程，去过日企被派到日本去交接畅销项目，也为动视暴雪Activision做过几个项目，还到过历史悠久的盛大游戏做过主程。就这样跌跌撞撞担任了7年的主程，2020年去了一家新公司，这里的技术让我大开眼界，也打开了我对技术更高层次的理解，我如饥似渴的学习着，事情远没有结束我仍然不断的拼搏和学习，学习的不只是技术还有生活。&lt;/p&gt;

&lt;p&gt;回头看自己，我比较幸运，幸运的搭上了Unity3D的便车，幸运的搭上了手机游戏的便车，幸运的搭上了公司发展的便车，但不可缺少的仍然是我如饥似渴的学习热情与拼命三郎般的努力，也只有热情与努力才能长久的让自己在浪潮中牢牢抓住机会以便在退潮时不被打回原形。说到努力，我的努力比起先贤实在微不足道，古今中外的先贤们远到，孔孟与管仲，唐太宗与成吉思汗，乔布斯与林肯等，近到游戏业界，UWA张鑫，第一代大佬云风(吴云洋)， IT投资大佬吴军等，都是长期努力拼搏的典范，前辈们付出的汗水可以汇成江河，我的汗水只是能浸湿自己而已，因此我希望能够学习先贤十年如一日的自律，并维持饱满的精力不断向前进。&lt;/p&gt;

&lt;p&gt;在2016年开始就想着能不能写一本关于自己是如何理解Unity3D和游戏开发的书，一直未能启动，直到2018年初才开始着手写些草稿。行动是最好的解决动力和自信的良药，在开始写后不久就一发不可收拾，一篇又一篇，一章又一章的写下去，由于文字功底不足，在开始阶段自己都不知道自己写的是什么，前后连句都读不通，却有许多网友看了给我赞赏和鼓励，实在感激不尽，感动不已。同时也发现了自己的知识面的诸多不足和错误理解，于是拼命看各类技术书籍，查阅资料以补充自己浅薄的知识。&lt;/p&gt;

&lt;p&gt;可是事情并没有这么顺利，自己看过的书和查阅的资料并没有马上理解和融会贯通，于是文章上依然很多错误和无知的言辞，前后连句依然并不通顺。但给我最大的安慰是，至少在我的坚持下，书有了比较明确的框架，对技术面的理解也到了一个新的高度。为了能让自己的知识能匹配我所写的书，在写书期间我不停的学习，不只是阅读各类技术书，还加入了更多人文类书籍，看完后实践并总结，同时会不时的在博客上写些想法和故事以补充自己在文字上的缺陷。渐渐我的博客上有了很多非技术类的文章，这些文章都是我给自己整理的对人和物的理解，以及一些对非技术性书本内容的回顾，我把它们归类总结成我的学习之路，包括对做人做事的思路探讨、改善自己缺陷的自省和看书后对书本加深记忆的总结。许多文类的书籍与文章让我对人和事物也有了更高层次的理解，让我惊喜的是这些非技术性知识反补了我对技术的理解，带我到达了更高的层次，我开始发现生活和工作是可以融会贯通的，于是经过逐日的自我完善走上了终生学习、自律生活的道路。&lt;/p&gt;

&lt;p&gt;随着在项目中不断实践自己的技术，自己对不足的知识面的补充以及勤奋的锻炼文字功底后，我对全书进行了3次重构，为的就是让书中内容能更好的表达技术原理，更全面和准确的技术讲解要点。全书分成：架构、C#技术要点、数据表与程序、UI用户界面、3D模型与动画、网络通信、AI人工智能、地图与寻路、渲染管线与图形学，覆盖了Unity3D游戏项目的技术要点，确保读者能看到整个Unity3D游戏项目的技术全貌，对各项技术与知识点有更深层次的理解。书中不仅关注项目中比较大的解决方案，还讲解了具体的技术细节，让读者不仅有更宏观的视野去审视自己的项目，还能在书中找到最接地气的技术细节以便大家能更深入理解技术原理。虽然才疏学浅，但凭着自己看过这么多书做过这么多项目写过这么多文章，本书实在可以成为大家在技术修行路上一本不可多得的参考书。&lt;/p&gt;

&lt;p&gt;​感谢各位能看完前言，本人最爱看序，序是最能体现作者心境的地方，它凝结了作者智慧的结晶，作者可以在这里畅所欲言讲自己的经历体会。&lt;/p&gt;

&lt;h3 id=&quot;读者对象&quot;&gt;读者对象&lt;/h3&gt;

&lt;p&gt;这里我们根据行业用户团体划分出一些使用者：&lt;/p&gt;

&lt;p&gt;● Unity3D程序员和爱好者&lt;/p&gt;

&lt;p&gt;● 游戏开发者&lt;/p&gt;

&lt;p&gt;● 各公司的资深游戏开发者以及游戏前端主程&lt;/p&gt;

&lt;p&gt;● 虚拟现实项目开发者&lt;/p&gt;

&lt;p&gt;● 技术美术和技术美术爱好者&lt;/p&gt;

&lt;p&gt;● 致力于图形图像、引擎架构的程序员&lt;/p&gt;

&lt;p&gt;● 开设相关课程的院校&lt;/p&gt;

&lt;h3 id=&quot;如何阅读本书&quot;&gt;如何阅读本书&lt;/h3&gt;

&lt;p&gt;​本书将知识点分为十个章节，每个章节都有自己独立的知识领域，读者可以按照章节顺序阅读本书，也可以根据自己的喜好挑选自己感兴趣的章节学习知识领域。如果你是一名经验丰富的资深程序员，能够理解游戏编程的相关基础知识，那么你可以直接阅读你感兴趣的章节。如果你是一名初学者，尽量从第一章开始学习。
​
第一章，软件架构。讲了架构的意义、架构的原理以及如何架构。
​
第二章，C#技术要点。对基础基础做了详细的讲解。
​
第三章，数据表与程序。针对客户端中表格数据与程序的协作与应用做了详细的讲解。
​
第四章，UI用户界面。针对UI用户界面的工作原理与优化手段做了详细的讲解。
​
第五章，3D模型与动画。针对3D模型的原理，动画的原理以及两者的优化做了详细的讲解。
​
第六章，网络通信。针对网络层部分业务与底层原理做了详细的讲解。
​
第七章，AI。针对各类型AI做了详细的讲解。
​
第八章，地图与寻路。针对场景构建与优化、地图构建以及寻路算法优化做了详细的讲解。
​
第九章，渲染管线与图形学。针对图形数学、图形学常用算法、渲染管线做了详细的讲解。
​
第十章，渲染原理与知识。针对客户端各类渲染技术的渲染原理做了详细的讲解。&lt;/p&gt;

&lt;p&gt;附录A为参考文献。&lt;/p&gt;

&lt;h3 id=&quot;勘误和支持&quot;&gt;勘误和支持&lt;/h3&gt;

&lt;p&gt;由于作者的水平有限，写书的时间也很紧张，书中难免会出现一些错误或者不准确的地方，不妥之处在所难免，恳请读者批评指正。由于资金和时间有限无法专门弄一个网站来反馈书本中的问题，于是在Github上开了个仓库做问题集记录，&lt;a href=&quot;https://github.com/luzexi/Unity3dToBeLeader/issues&quot;&gt;https://github.com/luzexi/Unity3dToBeLeader/issues&lt;/a&gt; ，如果发现书本中的错误，可以将书中的错误发布在这个网页地址的问题列表，我将会及时的提供反馈。书本的初稿内容发布在我的个人博客上&lt;a href=&quot;http://www.luzexi.com&quot;&gt;http://www.luzexi.com&lt;/a&gt;，由于精力有限只能更新到初稿，后面再更新书本版本的时候会重新再发布一次新书稿，我希望能在这块内容上不断升级和精进。如果你有更多的宝贵意见，也欢迎你发送邮件至我的邮箱 jesse_luzexi@163.com ，很期待能够听到你们的真挚反馈。&lt;/p&gt;

&lt;h3 id=&quot;致谢&quot;&gt;致谢&lt;/h3&gt;

&lt;p&gt;最后要感谢同事同僚以及网友的支持和点赞，《Unity3D网络游戏实战》作者罗培羽给予的帮助，机械工业出版社杨绣国老师细致缜密的审阅。特别要感谢我的妻子余胜男，为了支持我能安心写书安心工作，负担了家里的所有工作，辛苦万分，还不忘开导我，当我的心理医生为我排忧，这本书实在应该作为礼物献给她，以及我的两个女儿陆秀恩(Sharon)与陆安妮(Anne)，她们实在是世界上最好的女儿，知道该如何说出自己的诉求，并懂得遵守规则的重要性，时常还能做出曲线‘救国’的行动和思维，她们每次都要鼓励我，对我说“爸爸加油！”，很感激孩子们能和我一起努力，走在这条学习之路上又多了两个知己，谢谢陆秀恩感谢陆安妮。&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>向内看自己(七) 自信是如何被建立起来的</title>
   <link href="http://www.luzexi.com/2020/07/19/%E5%90%91%E5%86%85%E7%9C%8B%E8%87%AA%E5%B7%B17"/>
   <updated>2020-07-19T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/07/19/向内看自己7</id>
   <content type="html">&lt;p&gt;我在自我探索的过程中一直未完全明白人的的自信是如何建立起来的，我不明白为何有些人学识不高却自信满满，而另一些人知识渊博却处处自卑，还有很多类似不明白的问题，比如有钱和没钱是否支撑了人自我的信心，漂不漂亮，强壮不强壮，是否真的决定了我们的自信程度。&lt;/p&gt;

&lt;h4 id=&quot;带着这些疑问我读了自信的力量这本书让我豁然开朗原来以前我认为的自信是比较局限的原来是我给自己画了一个圈从此再也没能走出这个圈&quot;&gt;带着这些疑问我读了《自信的力量》，这本书让我豁然开朗，原来以前我认为的自信是比较局限的，原来是我给自己画了一个圈，从此再也没能走出这个圈。&lt;/h4&gt;

&lt;p&gt;全书每章内容都让人有种豁然开朗的感觉，其中有一章讲的是“行动在建立自信过程中起到的作用”非常精华。&lt;/p&gt;

&lt;p&gt;开篇讲一个年轻男子正在经历的一场春宵的场景，这是他的第一次，面前的女子让他心醉神迷，这一刻他期待了很久，想象过不知多少次，现在这一刻终于来临了。然而他并没有任何性爱经验。他该如何建立自信来完成这一出人生的首场表演呢？&lt;/p&gt;

&lt;p&gt;作者说自信来自具体的行动，以及与他人的互动，而不是自我内心的独角戏。这个年轻人的自信来自真实的爱抚和亲吻，从小心翼翼地温柔抚摸，到享受鱼水之欢的整个过程，信心被我们在一点点的实际行动中积累起来。不止如此，他的自信也源自与对方的亲密互动，不只是享受欢鱼时的互动，更是生活中彼此之间建立起来的亲密联结。&lt;/p&gt;

&lt;p&gt;倘若他刻意扮演经验丰富的情场老手，反而会束手束脚，无法在亲密接触中找到支点。倘若他坦诚是自己的第一次，就有可能被她引导，自信便会来自对方，从对她的信任转化为对自己的信心。&lt;/p&gt;

&lt;h4 id=&quot;很多人过分在意自我的表现让事情变得更糟人们通常会沉浸于内心的独角戏认为我们单方面努力就够了以为自己才是成功的关键&quot;&gt;很多人过分在意自我的表现让事情变得更糟，人们通常会沉浸于内心的独角戏，认为我们单方面努力就够了，以为自己才是成功的关键。&lt;/h4&gt;

&lt;h4 id=&quot;这种无知制约了我们的发挥也是这种无知让我们的自信局限于简单的自我就像是我们给自己画了一个圈很长时间都没能走出这个圈&quot;&gt;这种无知制约了我们的发挥，也是这种无知让我们的自信局限于简单的“自我”，就像是我们给自己画了一个圈，很长时间都没能走出这个圈。&lt;/h4&gt;

&lt;h4 id=&quot;其实自信是自我与世界相互作用的产物我们与世界互动的越多越能更好更快的建立自信通过与人互动我们能感受到来自他人的支持会发现事情其实比想象中简单得多甚至会发现自己其实非常幸运信心所覆盖的不仅仅是自我还是他人与我世界与我之间的互动&quot;&gt;其实自信是自我与世界相互作用的产物，我们与世界互动的越多，越能更好更快的建立自信。通过与人互动，我们能感受到来自他人的支持，会发现事情其实比想象中简单得多，甚至会发现自己其实非常幸运。信心所覆盖的不仅仅是自我，还是他人与我、世界与我之间的互动。&lt;/h4&gt;

&lt;p&gt;那么怎样才能让我与他人、与世界有更多的互动，从而建立自信呢？&lt;/p&gt;

&lt;h4 id=&quot;作者说行动就是最好的催化剂&quot;&gt;作者说，行动，就是最好的催化剂。&lt;/h4&gt;

&lt;p&gt;很多时候我们因为缺乏自信而感到手足无措，明知自己必须去做，却迈不开步子，连自信都没有，又怎样能够勇敢地投身于行动呢？这似乎是一把无情的双向锁。&lt;/p&gt;

&lt;p&gt;然而事实是，只要我们动手去做，就能卸下心头的重担，从此便可以一往无前。&lt;/p&gt;

&lt;h4 id=&quot;作者举了个例子在课堂上老师时常会提出一些高难度的论题有些孩子敢于尝试即使答得不好也会慢慢自信起来在其他同学眼里他们是敢于尝试的人是能够放得开的人仅此一点就足以让他们引以为荣通过这样的尝试这些善于行动的孩子们会发现原来自己可以提出如此新颖的想法原来自己还有从未发觉的潜能即使没有达到要求也收获了满满的成就感在此基础上不断完善精益求精&quot;&gt;作者举了个例子，在课堂上，老师时常会提出一些高难度的论题，有些孩子敢于尝试，即使答得不好，也会慢慢自信起来，在其他同学眼里，他们是敢于尝试的人，是能够放得开的人，仅此一点就足以让他们引以为荣。通过这样的尝试，这些善于行动的孩子们会发现，原来自己可以提出如此新颖的想法，原来自己还有从未发觉的潜能，即使没有达到要求，也收获了满满的成就感，在此基础上不断完善精益求精。&lt;/h4&gt;

&lt;p&gt;其实我们都有一种误区，认为只有彻底清除了不确定性，我们才能自信的去行动，只有知道答案才能发言回答，其实不确定性是永远无法彻底清除的，如果我们执着于铲除所有不确定性，那么行动将永远会被搁置和拖延，我们也永远无法建立牢固的自信。&lt;/p&gt;

&lt;h4 id=&quot;不知你是否有这样的体会在鼓起勇气强迫自己去做某事时在战胜胆怯心理同倾慕的人说活时在放手一搏上台公开演讲时我们能感受到仅仅放手去做这个事实就能唤醒身体里的本能唤醒内心深处那股对建立自信至关重要的原始战斗力&quot;&gt;不知你是否有这样的体会，在鼓起勇气强迫自己去做某事时，在战胜胆怯心理同倾慕的人说活时，在放手一搏上台公开演讲时，我们能感受到：仅仅放手去做这个事实就能唤醒身体里的本能，唤醒内心深处那股对建立自信至关重要的原始战斗力。&lt;/h4&gt;

&lt;p&gt;其实我们的现状并非与生俱来，而是后天逐步积累造成的，信心也是一样。如果对一件事没有信心？没关系。我们可以在日常的行动中逐步熟悉事物，在与世界互动，与他人互动的过程中，建立对事物的信心。&lt;/p&gt;

&lt;p&gt;除了行动本身，我们通常比较关注行动的结果。&lt;/p&gt;

&lt;h4 id=&quot;对于行动的结果作者认为人不可能主宰一切我们可以决定某些事但很多事情都不以人的意志为转移&quot;&gt;​对于行动的结果，作者认为，人不可能主宰一切。我们可以决定某些事，但很多事情都不以人的意志为转移。&lt;/h4&gt;

&lt;h4 id=&quot;我们应该对有些因素不在掌控范围内随时可能出现意外的情况做好心理准备即使我们精心谋划好每一个环节制定事无巨细的规划都会因为具体行动而产生新的情况和新的变量&quot;&gt;我们应该对有些因素不在掌控范围内，随时可能出现意外的情况做好心理准备。即使我们精心谋划好每一个环节，制定事无巨细的规划，都会因为具体行动而产生新的情况和新的变量。&lt;/h4&gt;

&lt;p&gt;我们来看看优秀的企业家是如何面对这些不确定性的。&lt;/p&gt;

&lt;h4 id=&quot;他们在出手之前深思熟虑一旦付诸行动便对自己的行为造成的直接和间接的结果给予充分信心即使如此他们也明白事情的结果并不完全取决于自己而是由许许多多的不确定因素影响造成的面对这种不确定的必然性他们毫不退缩反而张开双臂坦然面对他们深知机会是在不确定中由人的行为创造出来的因此他们特别喜欢和擅长利用事物的不确定性主动开启一段可能拥有的美好前景&quot;&gt;他们在出手之前深思熟虑，一旦付诸行动，便对自己的行为造成的直接和间接的结果给予充分信心。即使如此，他们也明白，事情的结果并不完全取决于自己，而是由许许多多的不确定因素影响造成的。面对这种不确定的必然性，他们毫不退缩，反而张开双臂坦然面对。他们深知，机会是在不确定中，由人的行为创造出来的。因此他们特别喜欢和擅长利用事物的不确定性主动开启一段可能拥有的美好前景。&lt;/h4&gt;

&lt;p&gt;现在我们知道自信的来源与建立的过程了。不要只顾自己对自己有信心，你的行动所创造的一切都可能成为成为你在世界上立足的支点。不要太在意自己，行动能让你遇到很多人，改变很多事，他们可能给你灵感，让你看到希望。&lt;/p&gt;

&lt;h4 id=&quot;当我们因为缺乏自信而饱受煎熬或者给自己过大的压力时一定是因为我们对事务的认识出现了偏差你可能误以为一切都取决于我自身可能缺少与他人以及世界互动这些都会导致你的思想被束缚被局限在一个小范围内挣扎&quot;&gt;当我们因为缺乏自信而饱受煎熬，或者给自己过大的压力时，一定是因为我们对事务的认识出现了偏差。你可能误以为一切都取决于我自身，可能缺少与他人以及世界互动，这些都会导致你的思想被束缚，被局限在一个小范围内挣扎。&lt;/h4&gt;

&lt;h4 id=&quot;张开双臂抛开我们的偏见拥抱这个世界与世界互动与他人互动行动起来在平日中逐步建立自信&quot;&gt;张开双臂，抛开我们的偏见，拥抱这个世界，与世界互动，与他人互动，行动起来，在平日中逐步建立自信。&lt;/h4&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(四十八) 从价格到人性</title>
   <link href="http://www.luzexi.com/2020/07/12/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A848"/>
   <updated>2020-07-12T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/07/12/思路探讨48</id>
   <content type="html">&lt;p&gt;在投机的这条路上我有过很多误解，我很长一段时间并不认为自己在投机，我并不认为自己是在投机取巧，我认为自己是正义的化身，做的都是正能量的投资。其实我并不知道我的想法是种偏见，因为我完全不认为自己是错的，甚至没想过这种想法是不是有错的可能性。连错的可能性能都没曾想过，可想而知我有多么相信我所做的是对的。&lt;/p&gt;

&lt;p&gt;随着时间的冲刷，我很自然的被按在地上摩擦摩擦，也有曾激烈的反抗过，反抗也不是反转开始而是继续认死理，直到自己没有脾气，任由自己被社会摩擦摩擦。当彻底没有脾气时才慢慢回想自己是否真的错了，反思自己的想法是否是一种偏见，片面且幼稚的见解，这也需要一个过程，并不是一下明白或领悟的事情。&lt;/p&gt;

&lt;h6 id=&quot;让一个人相信一件事很难让一个人不相信一件事更难-&quot;&gt;让一个人相信一件事很难，让一个人不相信一件事更难。^&lt;em&gt;^ ^&lt;/em&gt;^&lt;/h6&gt;

&lt;p&gt;啊，太阳。这就是人啊。脑回路永远都是难以快速转出来的，永远都是撞了南墙才回头的。&lt;/p&gt;

&lt;p&gt;于是我开始寻找一个答案，在投机中什么才不是偏见。我为这事做了很多努力，并且持续了很长时间，我看了很多经济相关的书，并规定自己每天做一些K线的模拟练习。&lt;/p&gt;

&lt;h6 id=&quot;我发现最难的就是没有偏见人很容易就陷入到一个自己或别人圈出来的逻辑或信念一旦相信就会像是被黑布蒙上了眼睛无视其他方向的视角和周围环境的变化但又不能不信因为你必须有一套理论体系来支撑你的行为方向要自己没有偏见经验经历知识心智一样不能少最好每样都强大即使这样也不能保证没有偏见还得不断反思不但反思错误的也要反思正确的我又给自己画了一个圈&quot;&gt;我发现最难的就是没有偏见，人很容易就陷入到一个自己或别人圈出来的逻辑或信念，一旦相信就会像是被黑布蒙上了眼睛，无视其他方向的视角和周围环境的变化，但又不能不信，因为你必须有一套理论体系来支撑你的行为方向。要自己没有偏见，经验、经历、知识、心智一样不能少，最好每样都强大，即使这样也不能保证没有偏见，还得不断反思，不但反思错误的，也要反思正确的。(我又给自己画了一个圈)&lt;/h6&gt;

&lt;p&gt;股票的价格没有规律可循，很多人都有自己的一套价格理论，比如我曾有平台的概念，认为价格会以平台的形式梯度上涨或梯度下跌，但价格经常打破平台稍微一点点又回来，于是我又认为这种平台概念是对的，只是没有绝对的边界。当一个理论被证明错误时，我又给自己设立了一个理论。&lt;/p&gt;

&lt;p&gt;随着时间的推移，我的理论越来越高级，我不再关心价格平台、K线指标，而是用情绪理论代替之。比如我用情绪理论去解释价格的原因，我认为情绪是主导价格的重要因素，我们应该感受到股价的情绪，在上升那一刻即时的跟上，不管股价在哪里，前期是否涨的比较多，只要情绪对了就有机会。&lt;/p&gt;

&lt;p&gt;我还认为情绪只是短线理论，长线的就应该不在意股价的波动，而是关注行业趋势、经济趋势、管理层的人员的能力。我还特意去寻找情绪的答案，比如我怎样才能正确感受到价格的情绪。我认为价格是情绪的延续，需要找到情绪的同理心，用心去感受情绪的发展方向，这样就不会因为试图去反抗情绪而遭到暴击。&lt;/p&gt;

&lt;h6 id=&quot;类似的理论有过很多种每次都被反证我的理论开始从表象深入到内在我发现表象只是结果内在才是逻辑的驱动力人性永不变这个社会是由几十亿人构成由人性建立起来的一个社会自然的运作规律也跟人性有关我看了些关于人性的书包括人性的弱点态度人格心理学以及富兰克林自传乔布斯传林徽因传陆小曼传等等些许人物传记从人身上体会人性是最好的途径&quot;&gt;类似的理论有过很多种，每次都被反证。我的理论开始从表象深入到内在，我发现表象只是结果，内在才是逻辑的驱动力。人性永不变，这个社会是由几十亿人构成，由人性建立起来的一个社会，自然的运作规律也跟人性有关。我看了些关于人性的书包括《人性的弱点》、《态度》、《人格心理学》以及《富兰克林自传》、《乔布斯传》、《林徽因传》、《陆小曼传》等等些许人物传记，从人身上体会人性是最好的途径。&lt;/h6&gt;

&lt;p&gt;最近我又有了一套新的理论，不妨来看看我的所思所想，比起以前的理论这次更宏观些许。&lt;/p&gt;

&lt;p&gt;我发现，中国的体制完全独立与美国，是一个自主探索的体制。我认为这是对的，不要去刻意的模仿别人是正确的。这就像是我们自身和我们的家庭，我们应该积极经营自己和家庭，让自己和家庭茁长成长，而不是看到别人有什么，就想着自己也该这样做。我很清晰的记得一句英文是这样写的：As long as you follow other people and as long as you’re being a copy cat you will never ever be the best copy cat in the world. But you will be the best you can be. 一直模仿他人就等于永远跟在别人的屁股后面，这样就永远突破不了自己。美国也是一样，它从建国开始就是独立探索出来的体制，其他国家都是模仿，而且基本都模仿的很不到位，只有美国自己玩的最溜。情况同中国一样，只有自己探索经营出来的路才是最好最适合自己的，也是最符合这个国家的，模仿者都只会偏离轨道。&lt;/p&gt;

&lt;p&gt;最后我发现人性是不变的，大家都为赚钱而赚钱，逻辑很多时候只是个借口，至少对价格来说是个借口，当我们不相信这个借口时，它什么用都没有，但当越来越多的人相信这个借口时，这个底层逻辑就开始有用了。这就像我们自身不断得去拼搏努力，这些拼搏和努力本身就是为了拼出一个底层逻辑，这个底层逻辑支撑着越来越多人汇聚过来形成更大的底层逻辑，慢慢的相信的人越来越多，形成的趋势也越来越强，当大部分人都认可时就完全爆发了。&lt;/p&gt;

&lt;h6 id=&quot;我认为价格上涨的逻辑也是如此上涨的逻辑是大众信不信这个逻辑如果非常相信就会涨很快如果不相信就会平着熬着等待新的拐角不过即使相信也在不断随着价格的上涨而不断起伏不定价格太高就会有人开始怀疑本身的逻辑低下来又有人开始觉得逻辑是对的应该坚持于是那些相信上涨的逻辑的人数成了关键下跌也是同样并不是一定说有下跌逻辑才下跌要有足够多的人相信下跌逻辑才会下跌否则大家都在犹豫中熬着等待新的拐点出现直到某一边的人数开始增多并且逐渐大过另一边形成了跟随相信的趋势一旦相信某逻辑的人数超过不相信那么就开始涨随着时间的推移这个逻辑人数不断变化到一定数量时某一边的忠实粉丝越来越多就开始所谓的趋势上涨无脑上涨疯狂上涨就是没有逻辑就只是相信的人数足够多&quot;&gt;我认为价格上涨的逻辑也是如此。上涨的逻辑是大众信不信这个逻辑，如果非常相信就会涨很快，如果不相信就会平着熬着等待新的拐角，不过即使相信也在不断随着价格的上涨而不断起伏不定，价格太高就会有人开始怀疑本身的逻辑，低下来又有人开始觉得逻辑是对的应该坚持。于是那些相信上涨的逻辑的人数成了关键。下跌也是同样，并不是一定说有下跌逻辑才下跌，要有足够多的人相信下跌逻辑才会下跌，否则大家都在犹豫中，熬着等待新的拐点出现，直到某一边的人数开始增多并且逐渐大过另一边形成了跟随相信的趋势。一旦相信某逻辑的人数超过不相信，那么就开始涨，随着时间的推移，这个逻辑人数不断变化，到一定数量时，某一边的忠实粉丝越来越多，就开始所谓的趋势上涨，无脑上涨，疯狂上涨，就是没有逻辑，就只是相信的人数足够多。&lt;/h6&gt;

&lt;p&gt;我有种感觉，我们可能过分关注投资股票，这消耗了我们大部分关注力，进而导致重要的事情没有得到足够的关注，从而导致无法进阶，最终导致我们人生遇到瓶颈。有问那还有啥好的投资呢？我认为应该投资自己和家庭成员。把注意力转移到人身上，知识、能力、境界、健康、视野，这些上来，相对于这些，股票的涨跌实在不值得一提。家庭成员之间的关系值得我们投资更多的精力去融合、团结、去相爱。&lt;/p&gt;

&lt;p&gt;各位共勉&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>向内看自己(六)</title>
   <link href="http://www.luzexi.com/2020/07/05/%E5%90%91%E5%86%85%E7%9C%8B%E8%87%AA%E5%B7%B16"/>
   <updated>2020-07-05T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/07/05/向内看自己6</id>
   <content type="html">&lt;p&gt;按事情的重要程度去分配精力所有人都知道，但做比知道容易，最近就被这个问题伤到。为什么我们知道了什么事情最重要，要先做，却总是反着来呢？我总结下。&lt;/p&gt;

&lt;h6 id=&quot;1逃避心态&quot;&gt;1.逃避心态&lt;/h6&gt;

&lt;p&gt;最重要的事情往往都是最艰难的，谁都不愿意主动去受这个苦，这导致随着时间的推移困难越来越大。&lt;/p&gt;

&lt;p&gt;解决这个问题得靠自己，这通常是习惯造成的，如果你习惯直面痛苦而不是逃避，每次都能迅速下定决心去面对和解决困难，那么这个问题就会迎刃而解，在你的这种习惯驱动下问题总会被一次次及时的化解。&lt;/p&gt;

&lt;p&gt;面对重要而令人痛苦的事情时，你总是能迅速提起精神，迎难而上直到事情有了新的进展有了更多的乐观消息，反之事情总是会被拖入越来越糟糕的地步。&lt;/p&gt;

&lt;p&gt;有困难最好立刻去解决，有没有真正解决是另一回事，不要顾虑太多，行动起来是前提，能提起勇气去解决是很好的开始。&lt;/p&gt;

&lt;h6 id=&quot;2环境一直在变化会有新的当下更紧急更重要的事情加入&quot;&gt;2.环境一直在变化，会有新的当下更紧急更重要的事情加入。&lt;/h6&gt;

&lt;p&gt;人很难适应变化，特别是年纪越大越难适应，当环境改变时，人的想法通常要在老地方呆很久，很多时候都是不撞南墙不回头。&lt;/p&gt;

&lt;p&gt;当有新的更重要更紧急的事情加入到我们的生活中来时，我们通常都是依然还坚持着我们自己脑海中原来的想法，比如我最近就一直以把书完成为最重要目标，但是工作中忽然插入了一个更紧急更重要的任务，可我仍然没能引起有效的重视，直到2周后发现事情有些不对劲，才换了想法做了紧急的补救工作。&lt;/p&gt;

&lt;p&gt;解决这个问题得打开我们的思路，要把‘不确定性’和‘环境时时在变化’更深刻的植入到自己的脑中。我们应该时刻记着，我们现在能有效执行自己的计划并按照自己的想法来安排时间和精力是得益于环境的稳定，而环境的这种稳定是非常脆弱的，我们应该时刻提高警觉，时刻观察和感受环境是否在变化，一旦有变化就该采取行动，即使是轻微的变化也要第一时间想想，自己是否做出了应对环境变化的行为，如果没有立刻着手开始应对。&lt;/p&gt;

&lt;p&gt;通常环境变化都会伴随着一些特殊事件的发生，比如公司的某个规则变了，政府的某个政策变了，某次会议发生了些奇怪的事，自己去了另一个陌生的地方，进入了一个新的环境(公司或学校或民族或国家)，领导忽然强调了些以前没有强调的事情，家庭里发生了些以前没有发生的事，国家或全球突发了什么事件等等，我们都应该引起警觉，最好能开始计划自己根据变化做出点什么改变，一开始改变不用很大，因为一开始改变太大会影响自己和周围的人和事反而会产生反应过大而陷入困境，应该一点点来逐步改变，这样就可以随时随地适应环境，因为环境改变也是一点点来的，通常一下子改变很大的环境通常都是个很深的坑。反之，警觉心不够，则很容易陷入被动，被动的去适应和主动的去改变是两种完全不同的状态。&lt;/p&gt;

&lt;p&gt;这些都是我的方法论，各位共勉。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>给女儿写信(二)  妈妈的脾气</title>
   <link href="http://www.luzexi.com/2020/07/01/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A12"/>
   <updated>2020-07-01T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/07/01/给女儿的信2</id>
   <content type="html">
&lt;hr /&gt;

&lt;p&gt;导读：喜欢跟孩子们一起，可惜一直在外地工作，因此我给孩子写点信并录成视频，跟孩子聊聊自己的故事以及最近的感受&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Sharon、Anne爸爸好想你们，爸爸最近忙着学习。&lt;/p&gt;

&lt;p&gt;爸爸想，我这么老远的跑过来工作，看不到我最爱的Sharon和Anne，当然要努力一点了，否则还不如不过来呢。&lt;/p&gt;

&lt;p&gt;所以爸爸把生活计划的很满，每天都要学各种东西，运动也不能落下。&lt;/p&gt;

&lt;p&gt;夏天到了呢，你们那边天气热不热，该去游泳啦，每年都要去游泳一下哦，如果能学会游泳并经常坚持游泳的话，会更漂亮哦。&lt;/p&gt;

&lt;p&gt;你希望自己长高些长得漂亮些吗，那就多运动哦，特别是游泳，它是所有运动里对身体最好的一种。&lt;/p&gt;

&lt;p&gt;妈妈最近还有发脾气吗，要告诉妈妈不要发脾气哦，发脾气不好，要学会控制自己的情绪，控制自己不要发脾气。因为能控制的发脾气的人会非常厉害，他们会变得越来越聪明，处理事情的时候很冷静理智。&lt;/p&gt;

&lt;p&gt;当我们冷静理智的时候所做的决定才是相对比较好的。要记得学会控制自己的情绪哦，也要把这个告诉妈妈，让她学会控制情绪。&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>读书笔记(九) 《精英们的清晨日课》</title>
   <link href="http://www.luzexi.com/2020/07/01/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B09"/>
   <updated>2020-07-01T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/07/01/读书笔记9</id>
   <content type="html">&lt;p&gt;《一日之计》精英们的清晨日课&lt;/p&gt;

&lt;p&gt;作者建议我们事先列出每日的清单，并将最重要的事情放在首位，每天早晨起来先按照清单中的内容完成任务。&lt;/p&gt;

&lt;p&gt;作者采访了许多名人，其中《财富》杂志的高级编辑杰夫.克尔，强调自己在每天早上完成任务清单上最重要的事情，不过他也承认尽管他十分努力，也没办法做到每天都做到。&lt;/p&gt;

&lt;p&gt;《困难即通途》的作者，瑞安.霍利迪说，她认为当你变得愈加忙碌时，例如有了更多新机遇，你的任务清单便要经受考验，还有当在旅行时是否依然能保持这种习惯，这是关键的点。她很自豪说自己因为养成了清单行事的习惯，所以能很好的做到。&lt;/p&gt;

&lt;p&gt;她喜欢在清晨中写作，她说她在清晨的写作，让她带着新鲜感和清醒的头脑走进新的一天。&lt;/p&gt;

&lt;p&gt;《财富》杂志高级主编，杰夫.克尔文说，自己通常会在6点到6点半起床，并会在起床后不到60秒里喝下3杯水，这样可以有效唤醒自己的身体和大脑。然后开始做清单上的任务。&lt;/p&gt;

&lt;p&gt;他说晚上睡觉前会阅读一些与工作无关的东西，这样有助于更轻松地入眠。&lt;/p&gt;

&lt;p&gt;最后他说自己是任务清单的忠实信徒，每天早上都会按照列出的任务清单，做最重要的任务，然后再在剩余的一天时间里，陆续做剩余的清单中的任务。&lt;/p&gt;

&lt;p&gt;《精英们的清晨日课》&lt;/p&gt;

&lt;p&gt;p66&lt;/p&gt;

&lt;p&gt;艺术家艾丽.卢娜说不论这一天是要写作、设计还是要画画，坚持晨间例程才是通向成功的大门。&lt;/p&gt;

&lt;p&gt;作者提出让一天变得高效的几个要点：&lt;/p&gt;

&lt;p&gt;每天列出待办事项。
建议每天工作结束时将第二天的工作待办事项列在纸上，这样第二天坐下来工作时就可以立即看到这份清单。&lt;/p&gt;

&lt;p&gt;这能使你减少决策的疲劳，不用去想今天我工作要干什么，待办清单将你从对清晨重要事项的干扰中解脱出来。这份待办清单最好能反应你一天具有挑战性的工作，也不要超负荷让自己陷入做不完单位状态。&lt;/p&gt;

&lt;p&gt;优先去做最重要的工作
早晨是最宝贵的时间，你应该做你认为最重要的事情。计算机科学副教授在《深度工作》中说：在无干扰的状态下专注地进行职业活动，使个人认知能力达到极限。这种努力能创造新价值、提升技能，而且难以复制。而肤浅工作是对认知要求不高的事务性任务，往往在受到干扰的情况下开展。此类工作通常不会为世界创造太多新价值而且容易复制。&lt;/p&gt;

&lt;p&gt;就因为无人打扰的时间宝贵，我们就更应该在早晨处理自己觉得最重要的事情，而下午则用来处理琐事。&lt;/p&gt;

&lt;p&gt;不要在清晨陷入琐碎的工作
作者举例清晨看邮件，说一旦清晨去查看邮件回复邮件，我们就会陷入一种被动模式，我们会按照其他人而非自己的日程安排来行事。这样会很糟糕，在清晨我们应该掌握主动权，不要让自己变得被动。&lt;/p&gt;

&lt;p&gt;杜绝早上开会
作者说会议通常会浪费我们很多时间，特别是早上的宝贵时间。因此尽量不要将会议放在早上，尽量将他们放到下午，让早上留出一大块自己的时间，这样才能让我们保持专注。&lt;/p&gt;

&lt;p&gt;将大目标分解成小目标
没人能一下子完成一个大项目，一口吃不了大象，我们应该将大目标分解成小目标，这样我们才能控制好工作的进展节奏，目标被分解为更小的部分，小块的工作往往更容易上手和完成。&lt;/p&gt;

&lt;p&gt;找到适合自己的方式
我们应该找到最适合自己的方式，而不是适合其他任何人的方式。不应该只是去一味的模仿，一定要放宽视野，全身心尝试新的方法，适合自己的方法。如果早晨不能成为你最有效的时间，也可以选择其他时间，或者为了应付工作不至于被开除，可以每隔1小时查看一下工作，如果没有特别紧急的事情就继续自己的专注。&lt;/p&gt;

&lt;p&gt;我们大多数人都能意识到一天中某个时间段的效率最高，我们应该找到那个时间段，把对自己来说最重要的事情放在那里。关键是明确那些高效的时间段在一天的哪里，并相应的调整自己的日程。&lt;/p&gt;

&lt;p&gt;请留意你最高效的那些时段。&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>读书笔记(八) 《自信的力量》</title>
   <link href="http://www.luzexi.com/2020/06/30/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B08"/>
   <updated>2020-06-30T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/06/30/读书笔记8</id>
   <content type="html">&lt;p&gt;《自信的力量》&lt;/p&gt;

&lt;p&gt;作者说自信来自于人与人之间的相互关系。成年人之所以有信心和能力完成各项计划其原因归根结底在于幼年时通过“早期互动”找到内在安全感。&lt;/p&gt;

&lt;p&gt;在野外生存的狼孩子就缺乏对人类的互动联系，在幼年时他们没有得到同类的保护和保障，没人与他们互动交流。他们不依赖他人，也就不存在这份依恋所培养出来的内在安全感，于是便不可能对人类有起码的信任。这份信任让世界看起来不那么有敌意。&lt;/p&gt;

&lt;p&gt;精神学家说，如果一个两岁的男孩有勇气对上门拜访的陌生人微笑问好、看着对方和对方说话或身体接触，那便说明这个孩子获得了足以应对新事物的内在安全感。他所依恋的人给了他足够的信心，让他有勇气远离他们去接近陌生人。&lt;/p&gt;

&lt;p&gt;举例麦当娜，她幼年时的遭遇使她一直没有信心突破自己甚至一度怀疑自己，直到遇到弗林老师，他对她倍加呵护并且夸奖她很美有天赋，还有一种令人如痴如狂的魅力。这使麦当娜改变了一生。&lt;/p&gt;

&lt;p&gt;还有网球运动员雅尼克.诺阿，11岁时遇见了世界排名第四的阿瑟.阿什，阿瑟对她发球大加赞赏并且把球拍送给他，最后还在机场给他签名时写上“在温网等你”，这句话始终陪伴着他，激励着他，为他指引方向，让他坚持信念，最后获得法网冠军。&lt;/p&gt;

&lt;p&gt;我们在童年时期与身边的人缔结踏实而温暖的纽带，在之后的成长过程中，人际交往对于自信构建的人作用也十分重要，这会巩固幼年时通过亲子关系建立的信心。&lt;/p&gt;

&lt;h3 id=&quot;自信是如何被建立起来的&quot;&gt;自信是如何被建立起来的&lt;/h3&gt;

&lt;p&gt;我在自我探索的过程中一直未完全明白人的的自信是如何建立起来的，我不明白为何有些人学识不高却自信满满，而另一些人知识渊博却处处自卑，还有很多类似不明白的问题，比如有钱和没钱是否支撑了人自我的信心，漂不漂亮，强壮不强壮，是否真的决定了我们的自信程度。&lt;/p&gt;

&lt;h4 id=&quot;带着这些疑问我读了自信的力量这本书让我豁然开朗原来以前我认为的自信是比较局限的原来是我给自己画了一个圈从此再也没能走出这个圈&quot;&gt;带着这些疑问我读了《自信的力量》，这本书让我豁然开朗，原来以前我认为的自信是比较局限的，原来是我给自己画了一个圈，从此再也没能走出这个圈。&lt;/h4&gt;

&lt;p&gt;全书每章内容都让人有种豁然开朗的感觉，其中有一章讲的是“行动在建立自信过程中起到的作用”非常精华。&lt;/p&gt;

&lt;p&gt;开篇讲一个年轻男子正在经历的一场春宵的场景，这是他的第一次，面前的女子让他心醉神迷，这一刻他期待了很久，想象过不知多少次，现在这一刻终于来临了。然而他并没有任何性爱经验。他该如何建立自信来完成这一出人生的首场表演呢？&lt;/p&gt;

&lt;p&gt;作者说自信来自具体的行动，以及与他人的互动，而不是自我内心的独角戏。这个年轻人的自信来自真实的爱抚和亲吻，从小心翼翼地温柔抚摸，到享受鱼水之欢的整个过程，信心被我们在一点点的实际行动中积累起来。不止如此，他的自信也源自与对方的亲密互动，不只是享受欢鱼时的互动，更是生活中彼此之间建立起来的亲密联结。&lt;/p&gt;

&lt;p&gt;倘若他刻意扮演经验丰富的情场老手，反而会束手束脚，无法在亲密接触中找到支点。倘若他坦诚是自己的第一次，就有可能被她引导，自信便会来自对方，从对她的信任转化为对自己的信心。&lt;/p&gt;

&lt;h4 id=&quot;很多人过分在意自我的表现让事情变得更糟人们通常会沉浸于内心的独角戏认为我们单方面努力就够了以为自己才是成功的关键&quot;&gt;很多人过分在意自我的表现让事情变得更糟，人们通常会沉浸于内心的独角戏，认为我们单方面努力就够了，以为自己才是成功的关键。&lt;/h4&gt;

&lt;h4 id=&quot;这种无知制约了我们的发挥也是这种无知让我们的自信局限于简单的自我就像是我们给自己画了一个圈很长时间都没能走出这个圈&quot;&gt;这种无知制约了我们的发挥，也是这种无知让我们的自信局限于简单的“自我”，就像是我们给自己画了一个圈，很长时间都没能走出这个圈。&lt;/h4&gt;

&lt;h4 id=&quot;其实自信是自我与世界相互作用的产物我们与世界互动的越多越能更好更快的建立自信通过与人互动我们能感受到来自他人的支持会发现事情其实比想象中简单得多甚至会发现自己其实非常幸运信心所覆盖的不仅仅是自我还是他人与我世界与我之间的互动&quot;&gt;其实自信是自我与世界相互作用的产物，我们与世界互动的越多，越能更好更快的建立自信。通过与人互动，我们能感受到来自他人的支持，会发现事情其实比想象中简单得多，甚至会发现自己其实非常幸运。信心所覆盖的不仅仅是自我，还是他人与我、世界与我之间的互动。&lt;/h4&gt;

&lt;p&gt;那么怎样才能让我与他人、与世界有更多的互动，从而建立自信呢？&lt;/p&gt;

&lt;h4 id=&quot;作者说行动就是最好的催化剂&quot;&gt;作者说，行动，就是最好的催化剂。&lt;/h4&gt;

&lt;p&gt;很多时候我们因为缺乏自信而感到手足无措，明知自己必须去做，却迈不开步子，连自信都没有，又怎样能够勇敢地投身于行动呢？这似乎是一把无情的双向锁。&lt;/p&gt;

&lt;p&gt;然而事实是，只要我们动手去做，就能卸下心头的重担，从此便可以一往无前。&lt;/p&gt;

&lt;h4 id=&quot;作者举了个例子在课堂上老师时常会提出一些高难度的论题有些孩子敢于尝试即使答得不好也会慢慢自信起来在其他同学眼里他们是敢于尝试的人是能够放得开的人仅此一点就足以让他们引以为荣通过这样的尝试这些善于行动的孩子们会发现原来自己可以提出如此新颖的想法原来自己还有从未发觉的潜能即使没有达到要求也收获了满满的成就感在此基础上不断完善精益求精&quot;&gt;作者举了个例子，在课堂上，老师时常会提出一些高难度的论题，有些孩子敢于尝试，即使答得不好，也会慢慢自信起来，在其他同学眼里，他们是敢于尝试的人，是能够放得开的人，仅此一点就足以让他们引以为荣。通过这样的尝试，这些善于行动的孩子们会发现，原来自己可以提出如此新颖的想法，原来自己还有从未发觉的潜能，即使没有达到要求，也收获了满满的成就感，在此基础上不断完善精益求精。&lt;/h4&gt;

&lt;p&gt;其实我们都有一种误区，认为只有彻底清除了不确定性，我们才能自信的去行动，只有知道答案才能发言回答，其实不确定性是永远无法彻底清除的，如果我们执着于铲除所有不确定性，那么行动将永远会被搁置和拖延，我们也永远无法建立牢固的自信。&lt;/p&gt;

&lt;h4 id=&quot;不知你是否有这样的体会在鼓起勇气强迫自己去做某事时在战胜胆怯心理同倾慕的人说活时在放手一搏上台公开演讲时我们能感受到仅仅放手去做这个事实就能唤醒身体里的本能唤醒内心深处那股对建立自信至关重要的原始战斗力&quot;&gt;不知你是否有这样的体会，在鼓起勇气强迫自己去做某事时，在战胜胆怯心理同倾慕的人说活时，在放手一搏上台公开演讲时，我们能感受到：仅仅放手去做这个事实就能唤醒身体里的本能，唤醒内心深处那股对建立自信至关重要的原始战斗力。&lt;/h4&gt;

&lt;p&gt;其实我们的现状并非与生俱来，而是后天逐步积累造成的，信心也是一样。如果对一件事没有信心？没关系。我们可以在日常的行动中逐步熟悉事物，在与世界互动，与他人互动的过程中，建立对事物的信心。&lt;/p&gt;

&lt;p&gt;除了行动本身，我们通常比较关注行动的结果。&lt;/p&gt;

&lt;h4 id=&quot;对于行动的结果作者认为人不可能主宰一切我们可以决定某些事但很多事情都不以人的意志为转移&quot;&gt;​对于行动的结果，作者认为，人不可能主宰一切。我们可以决定某些事，但很多事情都不以人的意志为转移。&lt;/h4&gt;

&lt;h4 id=&quot;我们应该对有些因素不在掌控范围内随时可能出现意外的情况做好心理准备即使我们精心谋划好每一个环节制定事无巨细的规划都会因为具体行动而产生新的情况和新的变量&quot;&gt;我们应该对有些因素不在掌控范围内，随时可能出现意外的情况做好心理准备。即使我们精心谋划好每一个环节，制定事无巨细的规划，都会因为具体行动而产生新的情况和新的变量。&lt;/h4&gt;

&lt;p&gt;我们来看看优秀的企业家是如何面对这些不确定性的。&lt;/p&gt;

&lt;h4 id=&quot;他们在出手之前深思熟虑一旦付诸行动便对自己的行为造成的直接和间接的结果给予充分信心即使如此他们也明白事情的结果并不完全取决于自己而是由许许多多的不确定因素影响造成的面对这种不确定的必然性他们毫不退缩反而张开双臂坦然面对他们深知机会是在不确定中由人的行为创造出来的因此他们特别喜欢和擅长利用事物的不确定性主动开启一段可能拥有的美好前景&quot;&gt;他们在出手之前深思熟虑，一旦付诸行动，便对自己的行为造成的直接和间接的结果给予充分信心。即使如此，他们也明白，事情的结果并不完全取决于自己，而是由许许多多的不确定因素影响造成的。面对这种不确定的必然性，他们毫不退缩，反而张开双臂坦然面对。他们深知，机会是在不确定中，由人的行为创造出来的。因此他们特别喜欢和擅长利用事物的不确定性主动开启一段可能拥有的美好前景。&lt;/h4&gt;

&lt;p&gt;现在我们知道自信的来源与建立的过程了。不要只顾自己对自己有信心，你的行动所创造的一切都可能成为成为你在世界上立足的支点。不要太在意自己，行动能让你遇到很多人，改变很多事，他们可能给你灵感，让你看到希望。&lt;/p&gt;

&lt;h4 id=&quot;当我们因为缺乏自信而饱受煎熬或者给自己过大的压力时一定是因为我们对事务的认识出现了偏差你可能误以为一切都取决于我自身可能缺少与他人以及世界互动这些都会导致你的思想被束缚被局限在一个小范围内挣扎&quot;&gt;当我们因为缺乏自信而饱受煎熬，或者给自己过大的压力时，一定是因为我们对事务的认识出现了偏差。你可能误以为一切都取决于我自身，可能缺少与他人以及世界互动，这些都会导致你的思想被束缚，被局限在一个小范围内挣扎。&lt;/h4&gt;

&lt;h4 id=&quot;张开双臂抛开我们的偏见拥抱这个世界与世界互动与他人互动行动起来在平日中逐步建立自信&quot;&gt;张开双臂，抛开我们的偏见，拥抱这个世界，与世界互动，与他人互动，行动起来，在平日中逐步建立自信。&lt;/h4&gt;

</content>
 </entry>
 
 <entry>
   <title>向内看自己(五)</title>
   <link href="http://www.luzexi.com/2020/06/29/%E5%90%91%E5%86%85%E7%9C%8B%E8%87%AA%E5%B7%B15"/>
   <updated>2020-06-29T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/06/29/向内看自己5</id>
   <content type="html">&lt;p&gt;我们很容易让生活进入自动驾驶的状态，没有动力，没有活力，麻木不仁的像僵尸一样，不加思考的向前走。大多数情况下都是因为年纪越大越难以改变自己的想法，于是随波逐流都是稀松平常的事。每周都要停下来反思下自己，让自己尽量不要去做无用功，虽然常常做了很长时间的无用功才发现该停一下，每周一反思是个挺好的打破现有习惯和现有常规的事情。&lt;/p&gt;

&lt;p&gt;曾一度认为焦虑是执行力强的代表其实不是，也曾一度认为焦虑越多越能迫使自己推进事情，但事实是完全相反的，这事在心中存在了很久，误区其实挺大的。&lt;/p&gt;

&lt;p&gt;后来想想街上的快递员和外卖员，无时无刻不在奔跑着逼自己赶向下一个目标，何等焦虑，那时我才明白焦虑吞噬了他们的耐心和理智，长久来看这让他们变得更糟糕。焦虑虽说不是低级的，但也不是什么好东西，适度焦虑确实能让人在短时间内保持警惕，但人常常控制不住它，使焦虑反噬了我们的心智。&lt;/p&gt;

&lt;p&gt;我们四周随便看一眼就能看到很多人焦虑的样子，在大城市里，这恐怕是很多人的常态。&lt;/p&gt;

&lt;p&gt;我比较倡导反人性操作，在大城市里挡住焦虑就个很好的反人性操作，类似于出淤泥而不染，当大家都焦虑时我们就要镇定，不要急着去奔跑，想想自己的能力范围，想想自己的兴趣爱好，想想自己能达到的最接近的目标，想想10年后想成为什么样的人。这样近的从能力范围开始做起，脚踏实地，远的想想10年后的事从长计议，积跬步至千里。&lt;/p&gt;

&lt;p&gt;拿我自己来说，我开始用更长远的目标来驱动我的努力。这个目标是以可积累，以及可变成成就的目标。我开始做任何事时主动去寻找一个目标，一个可以被人啧啧称赞的目标，并且这个目标是可以永久保留在这个世上的。比如写一本书将自己积累的知识变成可见的东西，健身让身体看起来很强壮，看书让自己随着岁月积累越来越多的知识，学习英语让自己未来融入到全球化热潮中去，定投让自己能跟随着岁月抚平世界带来的波动。&lt;/p&gt;

&lt;p&gt;当我找到这个目标时，我就有非常的大的信心不断走下去，去完成这个目标，因为我知道这个目标是可积累的，是不会消失的，并且是众人所喜欢的。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>向内看自己(四)</title>
   <link href="http://www.luzexi.com/2020/06/22/%E5%90%91%E5%86%85%E7%9C%8B%E8%87%AA%E5%B7%B14"/>
   <updated>2020-06-22T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/06/22/向内看自己4</id>
   <content type="html">&lt;p&gt;最近确实把自己累到了，我干了什么呢？每天要写至少3千字的文章以完成出版书籍的工作，还要精读至少1小时的书为了补充知识，公司有大量的工作等着我去做，甚至有好几个Dead line要过，除了这些我还得每天健身2次每次20分钟，还要每天背单词以及读一篇英语文章，朗读至少5分钟中文书本，我还给自己定了每天打扫房间的任务，衣服得自己手洗的任务，主动与别人交流的任务。啊，我太累了。为什么要这么累呢，我尝试将努力学习的奖励转移到睡眠上。“累了就睡觉”，这是我试图给自己一个暗示，让自己形成一个习惯，一累就想着睡觉，而不是其他娱乐。&lt;/p&gt;

&lt;p&gt;我们平时总是将放松寄托在娱乐上，比如打个游戏，看个电影，去旅游，当然这些都必要的。只是我对自己严苛了点，我认为身体的休息和放松完全可以用睡眠来代替，只是精神上的放松无法用睡眠代替。&lt;/p&gt;

&lt;p&gt;最近临近出版社的交稿日，我的焦虑情绪更加严重，这导致了我没能很好的控制自己，一屁股坐下开始做事就控制不住自己的时间。比如我一开始写文章就忘记时间，一开始写代码就忘记时间，一开始看书就忘记时间，总之常常让前一件事情的过量影响后面的计划与安排，这使得我每天都手忙脚乱，并且忙到深夜才睡觉，我感觉累到手都有点在颤抖的感觉，第二天又继续恶性循环。&lt;/p&gt;

&lt;p&gt;归根结底是自己没有把握好尺度，没有很好的控制自己，对控制自己的重要性没有认识到位。每次做一件事总是连续不停的做很久，以至于打乱了原来的计划。&lt;/p&gt;

&lt;p&gt;这种疲惫不堪的生活也不是没有收获，至少我明白了如果没有节制，如果没能很好的控制自己斩钉截铁的暂停当前过量的工作，我们是很难维持长久的高效率的。如果没能维持长久的高效率，我们很容易走走停停，甚至停停就不走了。&lt;/p&gt;

&lt;p&gt;究其原因必是自己没有管理好自己的身体，没能有节制的去做事，没能选择最重要的事情先去完成，当体力耗尽时，仍然有一大堆重要的事情没做，只能透支自己去完成，这直接导致第二天恶性循环。&lt;/p&gt;

&lt;p&gt;于是我今年最大的目标是，每天能12点前睡，早上7点起床。这表象背后是每天完成所有任务的前提下去完成这个目标，就像我在运作自己的生活那样，让身体更好更高效的运转起来。&lt;/p&gt;

&lt;p&gt;说到精神上的放松，我也尝试将它转化为更有价值的方式，比如我正在尝试将精神上的放松，从纯粹的娱乐转为“与他人的交流”。我给自己定了一个目标说，每天主动与别人交流一下，并且保持融洽最好能气氛愉悦。&lt;/p&gt;

&lt;p&gt;我认为与人交流能产生很多精神上的共鸣，这能很大程度上放松人的精神状态，轻松愉悦的交谈让人赏心悦目。同时我在很多伟人的书中看到，人与人之间的交流能给自己建立起强大的自信的理论，我对此坚信不疑。我认为人的自信很大程度上是与他人关联的，与他人的关系融洽，与他人在交谈时有轻松愉悦的氛围，是建立起我们自信的基础。&lt;/p&gt;

&lt;p&gt;我在互动交流这项技能上比较弱，很多时候甚至有点恐惧，所以我想不断的挑战自己，主动并且融洽的与人交流、合作。这可能是我今后生活的基础，将是重新建立起我自信的源泉。&lt;/p&gt;

&lt;h6 id=&quot;感谢各位听我唠叨&quot;&gt;感谢各位听我唠叨。&lt;/h6&gt;

</content>
 </entry>
 
 <entry>
   <title>给女儿写信(一)  初次</title>
   <link href="http://www.luzexi.com/2020/06/20/%E7%BB%99%E5%A5%B3%E5%84%BF%E7%9A%84%E4%BF%A11"/>
   <updated>2020-06-20T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/06/20/给女儿的信1</id>
   <content type="html">
&lt;hr /&gt;

&lt;p&gt;导读：喜欢跟孩子们一起，可惜一直在外地工作，因此我给孩子写点信并录成视频，跟孩子聊聊自己的故事以及最近的感受&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Sharon，Anne，爸爸好想你们。&lt;/p&gt;

&lt;p&gt;爸爸知道你们两个都很棒，每天都在努力学习，而且学到了好多好多知识。&lt;/p&gt;

&lt;p&gt;Anne的英语越来越棒了，Sharon的数学也学会了加法和减法，好棒啊，爸爸好开心。&lt;/p&gt;

&lt;p&gt;现在学校开学了，Sharon还适应吗，爸爸很关心你。&lt;/p&gt;

&lt;p&gt;Anne过几个月也要去上城堡幼儿园了呢，Anne爸爸喜欢你，要帮爸爸照顾姐姐和妈妈哦。&lt;/p&gt;

&lt;p&gt;爸爸也很努力在深圳学习，现在爸爸很努力，每天都让自己很早就起床了&lt;/p&gt;

&lt;p&gt;然后去公司里写会字，爸爸正在写一本书，爸爸花了好长时间写这本书，爸爸想把这本书出版。&lt;/p&gt;

&lt;p&gt;然后爸爸会看会书，每天爸爸都要看会书，每天都要打看书的卡，这样每天都能补充些知识，看书能让自己更聪明些知道更多的东西。&lt;/p&gt;

&lt;p&gt;看完书爸爸就开始上班了，爸爸的工作就是写程序，爸爸每天都要写很多程序。&lt;/p&gt;

&lt;p&gt;下班回来后爸爸想着，是不是要健身啦，每天运动健身也要打卡。强壮有活力的身体是我们首要任务哦，身体要健康才会更好哦。&lt;/p&gt;

&lt;p&gt;爸爸一直在努力，妈妈也在努力，要照顾好妈妈哦宝贝们，爸爸爱你们。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>向内看自己(三)</title>
   <link href="http://www.luzexi.com/2020/06/14/%E5%90%91%E5%86%85%E7%9C%8B%E8%87%AA%E5%B7%B13"/>
   <updated>2020-06-14T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/06/14/向内看自己3</id>
   <content type="html">&lt;p&gt;最近忙于工作和写书，对于改善自己的事落下了很多。我记下自己的所思所想的目的是改善自己而不是炫耀和比较，人在进阶时是比价痛苦的所以我记录下来，能供我过几年回头看看进阶之路的苦与乐，这能帮助我在未来的进阶道路上应对更多苦中作乐的情况。&lt;/p&gt;

&lt;p&gt;进阶难主要还是因为我们本身是顽固的，主要是我本身比较顽固，思想开化比较难。随着年龄的增长，我越来越有自己的思想，也形成了独立的性格，独立思考也带给了我一个人的王国，顽固不化也是从独立开始的。年纪越大，我可能越顽固，因为我的思想越来越形成体系，拥有了自己的整个独立的思想体系时，我可能再也无法转变我的所思所想了。趁现在还不老，多做些反向操作，让自己多见识下更多的思路与事物，也是个不错的选择。&lt;/p&gt;

&lt;p&gt;我很顽固，虽然我同时也认为自己很开放。每次当我意识到自己无法突破瓶颈时，我都会想”肯定是我错了，有没可能反向操作一下，以求得更开阔的眼界和思路“。当然我的大部分反向操作都是失败的，陷入了让自己更糟糕的境遇，虽然如此，但大部分失败也都成了最宝贵的财富，为更准确的找到思路提供了宝贵的经验。&lt;/p&gt;

&lt;p&gt;随着自律生活的进程不断推进，我开始习以为常自律的生活，并且能够有能力随着周围环境的变化不断有条不紊的调整，从而使得我既能自律的生活又能适应周围环境，说这些是因为我今年的变化比较大，从上海独自一人来了深圳，以前大部分时候都是身边有亲人，至少有熟悉的朋友。&lt;/p&gt;

&lt;p&gt;同时我也越来越发现身边的自律者越来越多，不是因为我来了腾讯使得周围自律者变多了，而是因为我彻彻底底成为了自律者，这使得物以类聚人以群分的效应得到了体现。喜欢与我接触的以及我喜欢接触的对象发生了变化，我潜意识中会去选择与一些跟自己志趣相投的人交流，这使得我周围聚集了一群自律者。&lt;/p&gt;

&lt;p&gt;当然这些都是表象，就像人总是会拿些东西来增长自己的自信一样，表象的诱惑力比较强大，成就、权利、财富、职位、身材、脸蛋、甚至游戏中的虚拟币都可以成为表象。随着我越来越了解自己，越来越深的挖掘自己的内心，人性的弱点也慢慢被我了解。表象也正在逐渐被我抛弃，个人的那些小小的成就都是微不足道的，我认为只有当我不在意这些表象时，我才能拥有控制表象的力量。&lt;/p&gt;

&lt;p&gt;我最担心的还是自己太过努力的折腾导致反噬，从而挫伤自己的信心。所以我一直很警惕，一旦过了那个我超负荷的界限我就让自己慢下来，但通常我也很难制止自己，所以大部分时候都是直到伤到自己了才停下来休息几天，去外面走走换个心情，整顿一下。&lt;/p&gt;

&lt;h6 id=&quot;向内看说自己真实的心声解剖自己&quot;&gt;向内看，说自己真实的心声，解剖自己&lt;/h6&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(四十六) 向内看自己2</title>
   <link href="http://www.luzexi.com/2020/05/25/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A846"/>
   <updated>2020-05-25T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/05/25/思路探讨46</id>
   <content type="html">&lt;p&gt;向内看自己发现自己的问题只是第一步，找到问题的根源是第二步，改变自我纠正问题是最难的第三步。每一步都不容易，大部人包括我都会将自身问题停留在某一步不愿意继续，这种拿自己开刀给自己做手术的事特别的难，就从这点来看我们也应该理解对方所表现出来的情绪背后的苦，很多时候不是我们不愿意去改善自己，而是这种行为真的很痛苦。&lt;/p&gt;

&lt;p&gt;最近为了突破自己做了几件自己不太喜欢做的事，一个是比较严格的要求自己把每天的时间填满了学习，另一个是主动去做一些交流，特别是与陌生人的交流。第一个让我尝到了严格时间管理的利与弊，时间管理是一个反人性的东西，我们人本身是感性的情绪的，自己想到什么并且愿意去行动就去做，因此我们有时‘浪费’的时间里会休息和调整，严格要求自己每天必须做完一些任务就会让自己每天倍感压力，情绪也会很刻板，特别是在我加入了时间管理机制，每次做事都给自己定一个时间，以15分钟为单位去完成一些任务，每隔15分钟就提醒下自己应该进入下一个15分钟，这种方式让我喘不过气来，每天都感觉自己很累很困，脑袋似乎一直在抗议我的这种新的习惯，所以最近很痛苦每天都感觉自己很疲惫。&lt;/p&gt;

&lt;p&gt;另一个是最近连续参加了几个腾讯的课程，接触了很多自己圈子外的同事。这些不只是课程的学习，我更希望增加与人之间交流。于是发现自己的很多问题，特别是交流上的，大部分问题集中在我对别人的兴趣比较少，这导致在平时随意聊天时脑袋中蹦出来的话题比较少，由于自己生活知识面不宽也导致了无法接住别人抛出的聊天内容，动不动就断了话题的弦，因此我最近很是懊恼。我也渐渐意识到，自信来源于人与人之间的互动，人与人之间的互动又绝非聊天这么简单，需要我们有足够宽的知识面可以聊，有时聊深了最好还要有深度，于是我也决定要多看些小说与历史书来丰富自己的生活和娱乐上的知识面。除了知识面更重要的还是我是否能对别人产生兴趣，如果我没有对对方产生兴趣就只会是很拘谨很生硬的聊天，这导致我与对方不会产生任何共鸣，这种交流大都没有多大的意义。&lt;/p&gt;

&lt;p&gt;最近虽然痛苦但毕竟有了新的尝试，每次走出舒适圈后都会很痛苦，回来后又恢复了精力，再出发继续拓展舒适圈，来去重复折腾几次自己的舒适圈又会大一圈，我也是抱着这种乐观的态度在不断的向自我发起挑战。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(七) 《人性的弱点》</title>
   <link href="http://www.luzexi.com/2020/05/24/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B07"/>
   <updated>2020-05-24T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/05/24/读书笔记7</id>
   <content type="html">&lt;h4 id=&quot;人性的弱点卡耐基&quot;&gt;《人性的弱点》卡耐基&lt;/h4&gt;

&lt;h4 id=&quot;1不要去批评责怪或抱怨&quot;&gt;1，不要去批评、责怪或抱怨&lt;/h4&gt;

&lt;p&gt;批评不但不会改变事实，反而会招致愤恨。
尽量去了解别人，尽量舍身处地去想“他们”为什么要这样做，这比批评和责怪有益、也有趣得多。&lt;/p&gt;

&lt;p&gt;作者举例1931年发生在美国的“双枪杀手”克洛雷，他是个凶恶的歹徒开枪杀人无数，最后判处死刑时他反而说“这是我自卫的结果”，他根本不觉得自己有什么错。每个人都有自己的视角，不同的视角理解同一样事物有不同的想法。&lt;/p&gt;

&lt;p&gt;作者又举例林肯总统的例子来说明，与人相处的艺术是多么美好。&lt;/p&gt;

&lt;p&gt;林肯年轻时喜欢在公开报纸上抨击别人，其中有一次抨击的那个人很愤怒，一定要跟林肯真刀真枪决斗个你死我活。决斗那天幸好被人阻止了，这对林肯的心灵非常震撼，从此懂得了如何与人相处的艺术。&lt;/p&gt;

&lt;p&gt;另外又举例了林肯当总统后，有一次南北战争把对方击溃了，这是一个很好的结束战争的机会，林肯要求米蒂将军追击彻底击溃敌军，但将军完全违背了林肯的指令，没有追击反而让对方逃走了。林肯本想写信斥责对方，信都写好了，最后没有发出去。作者猜测林肯肯定设身处地的想了将军的环境、遭遇、性格才做出不去责怪的选择。&lt;/p&gt;

&lt;p&gt;作者很赞赏《父亲备忘录》这篇文章。文中父亲对孩子忏悔，说自己常常对孩子发脾气、常常责怪他这责怪他那，还不停的吼叫，孩子却在睡觉前偷偷跑过来匆匆的害羞的吻了父亲一下道晚安。父亲突然感觉害怕，害怕自己养成的挑错、呵斥的习惯。他深夜跪在孩子床边深感愧疚。&lt;/p&gt;

&lt;p&gt;明早起他要做一个真正的父亲，要和孩子结为好朋友，与孩子一起痛苦，与孩子一同欢乐。&lt;/p&gt;

&lt;p&gt;不要批评、责怪或抱怨他人&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;2真诚的赞赏他人&quot;&gt;2，真诚的赞赏他人&lt;/h4&gt;

&lt;p&gt;天底下只有一种方法可以促使他人去做事，就是给他想要的东西。那么人想要什么呢？大多数人需要的东西包括：&lt;/p&gt;

&lt;p&gt;健康的身体，食物，睡眠，金钱以及金钱可以买来的东西，未来生活的保障，性满足，儿女的幸福，被人重视的感觉。&lt;/p&gt;

&lt;p&gt;懂得满足人类这种渴望的人可以将他人安抚和掌握。&lt;/p&gt;

&lt;p&gt;其中被人重视的感觉最难满足，也正是对这种感觉的渴望，促使一位未受教育、极度贫困的杂货店店员，去从大木桶底下找出那本花了他5角钱买的法律书来阅读，他就是林肯。&lt;/p&gt;

&lt;p&gt;夏布是美国钢铁公司第一任总裁，他的秘诀是善于处理人事和管理人事。他说他天生具有引发人们热情的能力。促使人将自身能力发展到极限的最好办法，就是赞赏和鼓励。任何来自长辈或上司的批评，都容易丧失一个人的志气。他说自己从不批评他人，他喜欢真诚、慷慨地赞美他人。&lt;/p&gt;

&lt;p&gt;我们不要老是想着自己的成就、需要，而应尽量去发现别人的优点，然后不是逢迎，而是发出真诚地赞赏他们。&lt;/p&gt;

&lt;p&gt;真诚地而不是虚有其表的赞赏他人，“真诚、慷慨地赞美”会让你的语言在别人的记忆中珍藏多年许久不忘。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-1&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;3给别人想要的&quot;&gt;3，给别人想要的&lt;/h4&gt;

&lt;p&gt;成功的社交在于你能否捕捉到对方视角，以及如何兼顾你和对方不同视角的能力。&lt;/p&gt;

&lt;p&gt;我们应该舍身处地为他人着想，了解别人需要什么。只有这样才能成就别人的同时成就自己。&lt;/p&gt;

&lt;p&gt;打个比方，我去钓鱼，我个人很喜欢吃海鲜，但鱼不喜欢吃海鲜，他喜欢吃虫子，所以当我钓鱼时我需要先想到鱼要吃什么，而不是关注我爱吃什么。&lt;/p&gt;

&lt;p&gt;这个方法要特别注意，观察别人，提出他们需要的，告诉他们怎么去获得他们需要的，然后与自己的视角相兼容。&lt;/p&gt;

&lt;p&gt;人所有的行为都是发自我们自己的基本欲望，在商场，在学校，在公司，在政府都是一样。安德鲁.卡耐基从一个贫穷少年变为亿万富翁就是因为他很早就懂得影响他人的唯一方法就是处处为人设想，他知道他们需要什么，虽然他只上了4年学但却深谙此道。&lt;/p&gt;

&lt;p&gt;史坦.诺瓦克有个例子，他儿子不喜欢去上学，若瓦克本能反应是警告、恐吓孩子要乖乖上学，但他知道这个方法不管用，也不会让孩子喜欢上上学，于是他和太太列出了儿子喜欢做的事，如画画、唱歌、结交新朋友等。他们两在厨房画画，并画的很兴高采烈，儿子也来凑热闹想加入，若瓦克说“啊，不可以，你的画画技能还不够，不能加入，幼儿园里能学到如何画的好的技巧。第二天儿子早早等在楼下等不急要去上学了。这是威胁和争论所不能达到的效果。&lt;/p&gt;

&lt;p&gt;很多时候我们并没有看到对方的需求，纵使我们说服对方，使他相信自己的观点错了，但是自尊心也必然使他不愿意做出太大的让步。&lt;/p&gt;

&lt;p&gt;对于推销人员也是一样，顾客总喜欢主动购买而不是被动，因此我们需要找到他们的需求并与自己联系在一起。但仍然有许多销售人员，终其一生不知道怎么从顾客的角度去看事情。&lt;/p&gt;

&lt;p&gt;这是个充满竞争、充满经验机遇与风险的世界，所以少数表现得不自私、愿意帮助别人的人，便能得到极大的益处，因为很少有人能处处为人着想。&lt;/p&gt;

&lt;p&gt;我们要想到他人的需求，给别人想要的，处处为他人着想。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-2&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;4做自己保持自我&quot;&gt;4，做自己，保持自我&lt;/h4&gt;

&lt;p&gt;我们在求职面试时通常会犯的最大错误就是不能保持自我，我们常常不能坦诚地回答问题，只想说出我们认为对方想得到的答案。可是这一点用也没有，因为没有人愿意听这种不真实、虚伪的东西。&lt;/p&gt;

&lt;p&gt;大部分都没有深刻认识到，我们每个人在这个世界上是独一无二的。以前没有像你一样的人，以后也不会有。&lt;/p&gt;

&lt;p&gt;作者自己曾想当演员，于是报名戏剧学院，并且自作聪明的开始研究几位名演员，并尝试把他们的优点都集合起来变成自己。最终花了好多年模仿别人后才发现他学不来任何人，他只能成为自己。&lt;/p&gt;

&lt;p&gt;后来他又在写书时犯了同样的错误。他借来很多书，花了一年的时间吸收他们，把他们的想法变成自己的文章，这文章枯燥乏味，最后作者把他们统统丢进垃圾桶，从头再来。&lt;/p&gt;

&lt;p&gt;卓别林开始排片时导演要他模仿当时的著名明星，结果他一事无成，直到他开始成为他自己才开始渐渐成功。&lt;/p&gt;

&lt;p&gt;所有的艺术归根结底都是一种自我的体现，我们只能以自己的方式唱歌，以自己的方式画画。我们的经验、环境、遗传造就了我们自己。&lt;/p&gt;

&lt;p&gt;不管好坏，我们只有好好经营自己的小花园，才能在生命的管弦乐中演凑好自己的那份曲子。&lt;/p&gt;

&lt;p&gt;爱默生在《自我信赖》中说，人们总有一天会明白，嫉妒是无用的，模仿他人也无异于自杀。无论好坏，我们都只有帮助自己，种自己的田，才能收获自己的人生。&lt;/p&gt;

&lt;p&gt;不用去嫉妒他人，即使我们做不了大路，可以做小路，做不了太阳可以做星星，成败不在于大小，只在于我们是否竭尽自己所能做自己。&lt;/p&gt;

&lt;p&gt;切勿模仿别人，发现自己，做自己，保持自己的本色。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-3&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;5改变不好的工作习惯&quot;&gt;5，改变不好的工作习惯&lt;/h4&gt;

&lt;p&gt;不好的工作习惯有哪些&lt;/p&gt;

&lt;h4 id=&quot;1办公桌上乱七八糟当你的办公桌上乱七八糟堆满了待复信件报告和备忘录时就会导致你慌乱紧张忧虑烦恼这不仅会让你感到紧张劳累而且容易引发很多疾病&quot;&gt;1.办公桌上乱七八糟。当你的办公桌上乱七八糟、堆满了待复信件、报告和备忘录时，就会导致你慌乱、紧张、忧虑、烦恼。这不仅会让你感到紧张劳累，而且容易引发很多疾病。&lt;/h4&gt;

&lt;p&gt;连续不断的待办事件真的必须处理完毕吗？先整理好桌面，让它看起来干净整洁，先让自己有个好心情，再去处理他们。&lt;/p&gt;

&lt;h4 id=&quot;2做事不分轻重缓急提高效率的重要方法是先做最重要的事情每天早起列张今天要做的事把一天要做的事情中最重要的事情先做完这样你的效率就会维持在高水平如果你能做到早起的话效果会加倍&quot;&gt;2.做事不分轻重缓急。提高效率的重要方法是先做最重要的事情。每天早起列张今天要做的事，把一天要做的事情中最重要的事情先做完，这样你的效率就会维持在高水平，如果你能做到早起的话，效果会加倍。&lt;/h4&gt;

&lt;p&gt;长期经验告诉我们，没人能永远按照事情的轻重程度去做。但如果能按部就班地做事，总比想到什么做什么的要好得多。&lt;/p&gt;

&lt;h4 id=&quot;3学不会组织能力不会授权他人没有督导&quot;&gt;3.学不会组织能力，不会授权他人，没有督导。&lt;/h4&gt;

&lt;p&gt;许多人常因为不懂得授权他人，因而提早进入失败的坟墓。这些人事必躬亲，结果被那些烦琐细节淹没，难怪他们常常感到匆忙、忧烦、急躁和紧张。&lt;/p&gt;

&lt;p&gt;大企业高管经常因为不懂得组织、授权和督导，导致五六十岁死于心脏疾病。&lt;/p&gt;

&lt;p&gt;快乐并非取决于你是什么人，或拥有什么，而是取决于你的思想。每天早晨先想想你应该感恩的事，你未来大半由你今天的思想决定。所以让你心中注满希望、自信、真爱与成功的想法很重要。&lt;/p&gt;

&lt;p&gt;养成良好的工作习惯。&lt;/p&gt;

&lt;p&gt;把你的桌面清理干净。&lt;/p&gt;

&lt;p&gt;按照事情的重要程度去做。&lt;/p&gt;

&lt;p&gt;学会去组织，去授权，去督导。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-4&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;6学会解除疲劳&quot;&gt;6，学会解除疲劳&lt;/h4&gt;

&lt;p&gt;有没想过，我们的脑力劳动也会让人感到疲劳，这种说法其实是错误的，至少不那么准确。研究表明，大脑是全然不会累的，那么人为什么会经常感到疲劳呢？&lt;/p&gt;

&lt;p&gt;其实是厌烦、不满、觉得自己无用、匆忙、焦虑、忧烦等这些情绪因素才会消耗掉我们脑力工作者的精力。是我们的情绪在体内制造了紧张而使得我们觉得疲惫。&lt;/p&gt;

&lt;p&gt;确切的说，忧虑、紧张、心乱导致我们经常紧绷着身体去工作，是紧绷的肌肉消耗掉了经理人，使我们感到疲惫。&lt;/p&gt;

&lt;p&gt;几乎全世界人都相信，当我们认真、努力、聚精会神时，总是要皱着眉头，绷紧肩膀，让肌肉做出努力的动作，其实这些跟大脑工作一点关系都没有，只是我们很难控制。&lt;/p&gt;

&lt;p&gt;我们要学习在认真时放松自己，这样就能最大限度减少精力消耗，让自己不再那么容易疲惫。&lt;/p&gt;

&lt;p&gt;放松从肌肉开始。第一是眼睛，眼睛消耗的能量为全身神经消耗能量的四分之一。闭上你的眼睛，静静休息，尝试让自己的眼睛全部松弛下来，一旦你的眼部肌肉放松，你就能忘掉一切烦恼。&lt;/p&gt;

&lt;p&gt;其次是脸部上的肌肉，也做同样的步骤，可以尝试口里不断说，“放松～放松，松弛下来…”。放松就是释放所有紧张和努力，让自己像个旧袜子一样松弛。&lt;/p&gt;

&lt;p&gt;让自己在舒适的环境下工作也能减少紧张的状态。我们应该想尽办法去让自己紧张的情绪缓和下来，目的是减少精力的消耗，让自己不那么容易疲惫。&lt;/p&gt;

&lt;p&gt;找到放松紧张情绪的方法，消除疲劳&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-5&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;7枯燥乏味使你疲劳&quot;&gt;7，枯燥乏味使你疲劳&lt;/h4&gt;

&lt;p&gt;爱丽丝一天的工作完回到家显得精疲力尽，但听到有朋友邀请去跳舞，一下子活力十足，跳到凌晨三点才回家。为什么有前后两种不同的表现，她是真的疲倦不堪吗？她只是对工作感到厌倦，抑或对生命感到乏味。&lt;/p&gt;

&lt;p&gt;我们又何尝不是这样，其实情绪上的态度比生理上的操劳更易使人产生疲倦。&lt;/p&gt;

&lt;p&gt;巴马科博士让学生做一系列枯燥无味的实验，结果学生们感到不耐烦想瞌睡，还抱怨头疼、眼睛疲惫、坐立不安，甚至有人胃不舒服。于是他又让学生们做了些有趣的实验，他们一下子焕发活力，精神抖擞。&lt;/p&gt;

&lt;p&gt;当人们感到厌倦乏味时身体的血压和氧消耗会显著降低，而当工作较为有趣和富有吸引力时，代谢现象就会加速。&lt;/p&gt;

&lt;p&gt;其实厌倦是你唯一降低工作能力的原因。我们的疲劳往往不是因为工作而起，而是由于枯燥、忧烦和不满等所造成。&lt;/p&gt;

&lt;p&gt;有个女速记员，每天都要整理和处理资料，这些工作枯燥乏味，她想了个办法让他们变的有趣。她每天跟自己比赛，处理资料的速度每天都记录下来，争取每次记下来的处理速度比上次更快。结果呢？她可能没升迁，也没得到称赞，也没感谢，也没加薪，但她用这种方式让她对工作不至于产生厌倦而疲劳，也对她产生了鼓舞作用。&lt;/p&gt;

&lt;p&gt;尽力使一件原本枯燥无味的工作变得有趣，而自身也会充满活力，这能让我们在一段时刻里得到快乐和享受。&lt;/p&gt;

&lt;p&gt;如何让自己的工作变得饶有兴趣呢？这可能是我们要尽力想办法去做的事。&lt;/p&gt;

&lt;p&gt;以前有个美国销售员卡腾本在法国谋生，他要跑很多家陌生人家去推销产品，这是项艰难而乏味的工作，但他决意要使得工作变得有趣。后来他做到了，他告诉我们：&lt;/p&gt;

&lt;p&gt;何不假装自己是个演员，而有一个人正在看你的表演？你所做的就是在舞台上表演一样有趣。他努力把工作当作有趣的探险，这使得他原本讨厌的工作变得饶有兴致。&lt;/p&gt;

&lt;p&gt;时时刻刻提醒自己，对工作保持兴趣，这不但可以免除忧虑，还可以帮助你得到升迁加薪的机会。纵使不能，它依然能降低你的疲劳，并帮助你享受快乐的时光。&lt;/p&gt;

&lt;p&gt;努力让枯燥的工作变得饶有兴趣。这不但能让我们消除大部分的疲倦，也能让我们享受快乐的时光。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-6&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;8思想的重要性&quot;&gt;8，思想的重要性&lt;/h4&gt;

&lt;p&gt;诺曼.文森特.皮尔说，你所认为的并非真正的你；反倒是你怎么想，你求是什么样的人。&lt;/p&gt;

&lt;p&gt;罗威尔.托马斯是拍纪录片的好手，他拍的影片在全世界都轰动，但在几年后宣告破产时只能靠借钱吃顿好的。但他任然在牛津街头上抬头挺胸，拥有着积极的心态和勇气。他拒绝被打倒，他认为挫折是人生的一部分，要想达到成功的巅峰就得经历这种磨练。&lt;/p&gt;

&lt;p&gt;心理学家也对人们在催眠时的生理功能做了测试。测试结果发现，当我们在催眠时被告知自己拥有强壮的身体后平均释放的力量要多出50%。这说明当我们心中充满极有力的思想时，每人平均都提升了50%的体力。&lt;/p&gt;

&lt;p&gt;基督科学派创始人艾迪曾遭遇各种不信，最后自己躺在病床上等死时翻了耶稣的话，这让她内心产生了一股力量，支撑她走出病痛恢复健康。从此开始她教导人们如何通过改变思想来治愈身体。作者说自己年纪越大越知道思想的巨大力量。我们可以通过改变想法来克服忧虑、恐惧甚至疾病，从而改变人生。&lt;/p&gt;

&lt;p&gt;只要我们有思想的力量，外在的环境对我们去的影响也会变得有限。一个送去断头台的军人可以坐在自己的棺木上还在欣赏着远处的美景。科考队在南极遇到暴风雪没有粮食时可以继续欢唱直到去世。失明的弥尔顿在300年前就知道，心灵是它自己的殿堂，它可以成为地狱中的天堂也可以成为天堂中的地狱。&lt;/p&gt;

&lt;p&gt;那么我们怎么来改变自己的思想呢？
顶尖心理学家威廉.詹姆士说，行动与感觉是并行的，意志控制了行动，行动也能间接控制意志。&lt;/p&gt;

&lt;p&gt;我们虽然不能一下决心就改变了自己的思想，但我们确实可以通过改变行动来改变思想。当我们行动时，我们的思想也会随着改变，特别是在感觉上。&lt;/p&gt;

&lt;p&gt;试着在你脸上堆起大大的开心的笑脸，放松肩膀，深吸一口气，再唱歌，或吹口哨。很快你就能明白什么是“行动引起感觉的变化”。如果你的行为散发的是快乐，就不可能在心理上保持忧郁。&lt;/p&gt;

&lt;p&gt;让我们先行动起来，改变自己的行为方式，从而改变思想，改变生活，改变命运。&lt;/p&gt;

&lt;p&gt;詹姆斯.艾伦在《思想的力量》中有这么段话：
人如果改变了对事与人的看法，事与人就会对他发生改变。如果一个人的想法有巨大的变化，他会惊讶的发现生活中的状况也同样有急速的变化。我们所有人都是思想的产物，人提升了自己的思想才能提升自己的实力去完成更高级的事情。&lt;/p&gt;

&lt;p&gt;如果想控制你的思想，克服自己的恐惧，控制自己的心智与精神。我们要先从控制自己的行为开始，从行为中控制思想改变思想。&lt;/p&gt;

&lt;p&gt;让我们为自己的快乐奋斗。想的开心、做得开心，你就真的会觉得开心。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-7&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;9不要曾恨敌人&quot;&gt;9，不要曾恨敌人&lt;/h4&gt;

&lt;p&gt;当我们对敌人曾恨时就是在伤害自己。它会控制我们的睡眠、胃口、健康、以及情绪。这对我们来说太不划算了。&lt;/p&gt;

&lt;p&gt;耶稣说“要你的敌人”，其实他是在替你治病，这样你就会从糟糕的身体状况中恢复过来，拥有好的心情。他也是在帮你美容，因为很多人因为仇恨愤怒而布满皱纹甚至变形。&lt;/p&gt;

&lt;p&gt;即使我们没办法爱我们的敌人，起码也应该多爱自己一点。&lt;/p&gt;

&lt;p&gt;罗纳就用爱敌人的方式赢得了工作。他在找工作时写了很多信求职，有位雇主说他字写的烂而且文字也差，他很愤怒想写回信骂对方，但他后来一想或许这可以提醒自己改进书写技巧，于是写了封信感谢对方，没想到对方回信请他见面并录用了他。罗纳找到了，以柔和驱退愤怒的方法。&lt;/p&gt;

&lt;p&gt;没有任何能侮辱或困扰我们，除非我们自己允许。如果你能爱敌人，那么棍棒虽然可以打断我们的骨头，但言语休想动我们分毫。&lt;/p&gt;

&lt;p&gt;1918年有位黑人教师兼传教士琼斯曾在对黑人演说时因自己说了“战斗”“武器”这几个词语而被判策动叛乱执行死刑。在脚手架上，他仍然没有恨打他骂他的人，娓娓道来他的理想，改善南方落后地区，教育他贫困的同胞兄弟，教育失学的孩子等。最后改动了所有人将他放了下来，事后他说“我没空争吵，也没时间反悔，没有人能强迫我恨他们”，他不为自己求情，只为自己的使命求情。&lt;/p&gt;

&lt;p&gt;那么我们怎么做到不将敌人怀恨在心呢？
从长远来看，每个人都会为自己的错误付出代价。能将此常怀于心的人。就能不对人发怒、愤懑、诽谤、攻击或怨恨。&lt;/p&gt;

&lt;p&gt;林肯就是很好的榜样，他委任相当高的职位给曾侮辱过他的人，包括迈克兰、使瓦德、史丹顿、蔡斯等。&lt;/p&gt;

&lt;p&gt;他认为，如果你我像我们的敌人一样承袭了同样的生理、心理以及情绪特征，如果我们的人生也完全一样，我们可能会做出跟他们完全一样的事。与其诅咒报复我们的敌人，何不给他们谅解、同情、援助、宽容以及为他们祈祷。&lt;/p&gt;

&lt;p&gt;不要仇恨我们的敌人，那样只会伤害我们自己。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-8&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;10不指望别人感激你&quot;&gt;10，不指望别人感激你&lt;/h4&gt;

&lt;p&gt;有个老板给员工发了10000美元奖金却没有一个人感谢他，他很愤怒，为此他近一整年为这事愤愤不平。圣人说过，一个愤怒的人，浑身都是毒。&lt;/p&gt;

&lt;p&gt;也许书他本身就是个挑剔又不知感谢的人，以致别人不敢也不想去感谢他。&lt;/p&gt;

&lt;p&gt;感恩其实是件极有教养的产物，很少有人拥有。如果你指望别人感恩，那么你犯了大错。&lt;/p&gt;

&lt;p&gt;有个妇人一直抱怨自己孤独，没有一个亲戚愿意接近她，去看望她的人都要被她喋喋不休很久。最后没有人再去看望她了，她只能装病发作，医生也束手无策，说她的问题是情绪问题。&lt;/p&gt;

&lt;p&gt;有很多人像她那样渴望被爱，但世上真正能得到爱的唯一方式就是不索求，而且还要不求回报的付出。&lt;/p&gt;

&lt;p&gt;作者说自己的父母就很享受付出的快乐。他们本来就穷，还不断的向孤儿院寄钱，他们享受了帮助这些无助小孩的喜悦，并不寄希望于任何回报。施与而不求回报的快乐是他们所能得到的最大快乐。&lt;/p&gt;

&lt;p&gt;除了不求回报得付出，我们也要以身作则的教育孩子们要懂得感恩。作者举例自己的姨母，她把自己的母亲接过去照料，同时也照料她婆婆，她还有6个孩子，对她来说做这些都是正常的她也愿意。年老后她的子女都希望她住他们家去，子女们都对她钟爱极了。&lt;/p&gt;

&lt;p&gt;要想有感恩的子女，只有自己先成为感恩的人。我们所言所行都会成为孩子的榜样，这些对言行我们来说是小事，孩子们却听进去了。如果我们习惯感恩，孩子们无意中也学会了赞美感恩的习惯。&lt;/p&gt;

&lt;p&gt;不要指望别人感恩，付出是一种享受施与的快乐。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-9&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;11知足常乐&quot;&gt;11，知足常乐&lt;/h4&gt;

&lt;p&gt;作者有个朋友叫罗德，是个开杂货店的，但亏了很多钱也欠了一屁股债，他说自己像只斗败的公鸡，失去了斗志和信心。有一天他看到马路对面过来一个没腿的人，坐在木板上下面做了4个轮子，两手用木头支撑划动着向前。正当他费力抬起身下木板时，目光与我相遇，他向我灿烂的笑着说“早安先生！今天的天气真好不是吗？”。这一幕把他震惊了，一个失去双腿的人还能开心快乐、充满自信，我为什么还那样沮丧。想到此他顿时就精神多了，从此鼓起勇气向前迈进。&lt;/p&gt;

&lt;p&gt;飞行家肯贝克曾在太平洋漂流21天，他说这给他最大的教训就是，我们如果已经有足够的饮水与食物，就不该再有任何抱怨。他把烦恼缩小到了最小。&lt;/p&gt;

&lt;p&gt;其实大多数我们担心的事情都是没有意义的，它只会浪费掉我们更多精力。这也阻碍了我们前进的脚步。&lt;/p&gt;

&lt;p&gt;那么怎样才能消除这些担忧和烦恼呢？&lt;/p&gt;

&lt;p&gt;作者的同学讲述了自己的故事：
他说那时自己工作排的很紧，忙得不可开交，后来终于病了，医生说要躺在床上一整年。他哭了很久担心着担心那，最后终于慢慢平静下来开始努力建立另一套价值观。他每天早晨醒来开始强迫自己回顾自己拥有的一切，我的身体没有疼痛，我有个可爱的女儿，我的眼睛依然能看到世界，耳朵依然能听到悦耳的音乐，有看书的闲暇，也有一些好朋友。他慢慢养成了这种盘点自己所拥有的福分的习惯，担忧和烦恼也渐渐离他远去。&lt;/p&gt;

&lt;p&gt;为了清除掉我们的担忧和烦恼，我们应该每天告知自己一下自己已经拥有的多少福分，养成这种习惯，当自己面对自己缺少的东西时更加平静。&lt;/p&gt;

&lt;p&gt;知足常乐。每天盘点下我们所拥有的，这些已经够我们快乐的活下去了。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-10&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;12将不利因素转化为成功因子&quot;&gt;12，将不利因素转化为成功因子&lt;/h4&gt;

&lt;p&gt;当人们发现命运给了他不幸时很多人都只会自怨自艾的说“我完了，我的命怎么这么差。”于是陷入与世界作对的自怜之中。&lt;/p&gt;

&lt;p&gt;芝加哥大学校长采取的方法则反其道而行之。他会说“我可以从这次不幸中学到什么？怎么样才能改善我目前的处境？怎么才能从我现有的情况下发展起来？”&lt;/p&gt;

&lt;p&gt;作者访问了一个佛罗里达州的农民，他将不幸转化为了成功的因子。那时他买了一块农地，发现这地比较贫瘠无法种植，只有灌木丛和响尾蛇，他很沮丧。后来他开始在农地上养响尾蛇，并将响尾蛇做成蛇肉罐头出口，蛇皮卖给鞋厂做鞋，最后给整个村子带来了致富的道路。&lt;/p&gt;

&lt;p&gt;人生中用我们已获得去投资那谁都会。真正关键的是如何从不幸中找到出路。&lt;/p&gt;

&lt;p&gt;作者还认识一个失去双腿的人，他最后反转了不幸成就了自己。他叫本.佛森，在24岁那年因为车祸被宣判一辈子要在轮椅上度过。他当时很愤怒，也很怨恨命运的作弄。但他慢慢地发现抗拒对自己毫无帮助，这只会使自己变得尖酸刻薄。&lt;/p&gt;

&lt;p&gt;过了震惊和愤怒的阶段，他开始阅读，他14年里读了1400本书，这些书拓展了他的领域，他的人生比以前想象的还要丰富。他也开始真正欣赏音乐，不过真正重要的是他开始有了自己思考的时间。他终于体会到以前努力追求的很多事其实没有真正的价值。&lt;/p&gt;

&lt;p&gt;由于阅读，他开始对政治感兴趣，他研究公共问题。他坐在轮椅上发表演说，他开始了解人们，而人们也开始认识他。最后他坐在轮椅上当上了佐治亚州的州务卿。&lt;/p&gt;

&lt;p&gt;当我们面对不幸时我们要相信，我们的缺陷、不幸、弱点，也是提供给我们另一种机会和力量的来源。&lt;/p&gt;

&lt;p&gt;进化论的达尔文就曾坦承他自己受到过弱点的刺激，众人认为他无能所以才刺激他辛勤努力完成进化论的工作。&lt;/p&gt;

&lt;p&gt;看不见的弥尔顿，耳聋的贝多芬，耳聋目盲的海伦凯勒，是不幸激发了他们的潜力，他们也在不幸中找到了通向成功的因子。&lt;/p&gt;

&lt;p&gt;人生最重要的不只是运用你所拥有的，这谁都会，真正重要的是如何从困难中找到出路，在不幸中找到幸运。&lt;/p&gt;

&lt;p&gt;当命运交给你一个酸柠檬时，你得想办法把它做成甜的柠檬汁。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-11&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;13批评他人是一种自我满足&quot;&gt;13，批评他人是一种自我满足&lt;/h4&gt;

&lt;p&gt;英国威尔斯亲王小时候曾就读一所海军学校，有一次他被人打了，虽然他没抱怨但军官还是查出了打他的人，最后问他们为什么打人，他们说因为长大了可以吹嘘自己曾经打过英国国王。&lt;/p&gt;

&lt;p&gt;我们总是情不自禁的会因为想要引起重视而去批评别人。如果你被批评，那是因为批评你会给他带来一种重要感。这也侧面说明，你是有成就的重要的。很多人凭借职责比自己更有成就的人来获得满足感。&lt;/p&gt;

&lt;p&gt;前任耶鲁大学校长蒂莫西.德怀特也不例外，他以诋毁美国总统候选人为乐，他诋毁杰佛逊总统是个行为不端，道德沦丧的人，但杰佛逊最后撰写的独立宣言被赞誉为民主先驱。&lt;/p&gt;

&lt;p&gt;皮尔里上将瘾到北极探险而闻名于世，华盛顿的将军们指控他以科学研究为名骗取经费，到处在北极游荡，最后麦肯来总统亲自下令才得以平息。&lt;/p&gt;

&lt;p&gt;我们在批评他人的时候常常是因为需要想从他人身上得到重要感。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-12&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;14不要让别人的批评中伤到自己&quot;&gt;14，不要让别人的批评中伤到自己&lt;/h4&gt;

&lt;p&gt;作者曾因为一个记者写了一篇报道攻击自己而生气，于是他打电话给那个报社要求他对这篇报道做出解释，并且坚决要他为他的错误受到惩罚。后来想起这件事，作者感到很惭愧，他知道一半的读者可能根本没看到这篇文章，另一半看到的也是抱着随意的心情去看它。即使这一半中有一半的读者仔细看过也会在几周内将它忘得一干二净。&lt;/p&gt;

&lt;p&gt;其实没人真正关心别人的事，因为人们通常只想到自己。人们关切自己轻微的头疼，只怕比关切你我的报道要多的多。&lt;/p&gt;

&lt;p&gt;作者也曾向罗斯福总统的夫人请教如何处理恶意的非难，因为她曾受尽这类批评，可以算是拥有最多的朋友以及最多的敌人的白宫女夫人。她说她少女时期非常害羞也恐惧别人的批评，有一天去请教了罗斯福的姐姐，姐姐告诉他：“只要你相信自己做的是对的，就不要在意别人怎么说”，“做你认为正确的事，因为你反正都会受到批评。你会因为做了某些事被骂，也会因为什么都不做而被骂，结果都是一样的。”&lt;/p&gt;

&lt;p&gt;美国国际公司总裁马修.布鲁斯也曾为这事烦恼，一开始他不断去安抚反对他的人，最后发现为了避免别人对他个人的批评，他视图安抚的人越多，同时得罪的人也越多。&lt;/p&gt;

&lt;p&gt;不要去在意恶意中伤你的人，如果你身居领导地位，就注定了要被批评。&lt;/p&gt;

&lt;p&gt;林肯总统可是被很多人中伤过的，他有句名言“只要我不对任何攻击做出反应，这件事就只有到此为止。我尽力而为，我将继续如此直到生命结束”&lt;/p&gt;

&lt;p&gt;不要让别人的批评中伤到自己，只要认为自己做的是对的，就做好自己的事。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-13&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;16学会自我批评&quot;&gt;16，学会自我批评&lt;/h4&gt;

&lt;p&gt;作者曾让秘书记下自己做的傻事，但很多事情任然蠢到没脸让秘书去记，只好自己偷偷写在笔记本里。他说自己曾把自己的麻烦怪罪到别人头上，随着年龄渐增才发现应该怪的人只有自己。&lt;/p&gt;

&lt;p&gt;作者曾请教美国商业信托银行董事长豪威尔，他说自己每周末都给自己一天时间打开笔记本回顾下这周所有面谈、讨论、会议的过程，审视下自己做错了什么，有什么是正确的，我还能干什么来改进我的工作表现，我能从这次经验中吸取什么教训。每周这种检查都让自己不开心，因为做了很多自己不敢相信的鲁莽的事。年事渐长后情况会好一些，他也一直保持着这种自我分析的习惯，它的帮助很大。&lt;/p&gt;

&lt;p&gt;豪威尔这种做法是向富兰克林学来的，富兰克林做的更极致，每晚都反省一次。他发现13项严重的错误，其中3项最严重的是，浪费时间、关心琐事、与人争论。富兰克林每周都努力改进一些一个坏习惯，他与自己的缺点奋战了整整两年，后面的日子坏习惯就越来越少了。&lt;/p&gt;

&lt;p&gt;林肯也是，有一次林肯签署了一次调兵命令，史丹顿拒绝执行并托人告诉林肯这个命令愚不可及，林肯平静的回答“如果史丹顿骂我愚蠢，我多半是真的笨，我会亲自去跟他谈一下”。林肯到了史丹顿那里，史丹顿指出了他这项命令的错误，林肯就此收回了命令，林肯很有接受批评的雅量，只要他相信对方是真诚的，有意帮忙的。&lt;/p&gt;

&lt;p&gt;“敌人对我们的看法比我们自己的观点可能更接近事实。”这句话常常是正确的，可是被人批评的时候，如果我们不提醒自己，我们还是会不假思索地采取防卫姿态。&lt;/p&gt;

&lt;p&gt;人总是讨厌被批评，喜欢被赞赏，因为我们并非逻辑动物，而是情绪动物。&lt;/p&gt;

&lt;p&gt;听到别人谈论我们的缺点时，想办法不要急着辩护。让我们放聪明点也更谦虚点，我们可以气度恢宏地说“如果让他知道我其他的缺点，只怕他还要批评得更厉害”。&lt;/p&gt;

&lt;p&gt;高露洁的总裁曾在商家处推销产品时主要要求对方指出问题所在，每次他推销失败，都会返回去商家告诉他自己不是回来推销的，希望能得到你的意见与指正“请告诉我刚才有哪些地方做的不对的，你的经验比我丰富，事业又成功，请给我一点指正，直说无妨，不必保留。”&lt;/p&gt;

&lt;p&gt;查找自己的问题，并竭尽全力去改善，大度的接受批评，并从中受益。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-14&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;17真诚地去关心他人&quot;&gt;17，真诚地去关心他人&lt;/h4&gt;

&lt;p&gt;很多人终其一生都在向别人搔首弄姿，企图引起别人的注意，让别人来关心自己。这是枉费力气的，因为人们根本不会来注意你，他们只会注意自己。有人在500通电话中统计过，人们用的最多的字眼就是‘我’。
如果我们只想引起别人的注意，一心只想在别人心里留下印象，我们就不可能交到许多真心、诚恳的朋友。&lt;/p&gt;

&lt;p&gt;心理学家阿德勒在《自卑与超越》中写了这么一段话发人深醒：但凡不关心别人的人，必定会在有生之年遭受重大困难，并且大大伤害到其他人。也就是这种人，导致了人类的种种错误。&lt;/p&gt;

&lt;p&gt;霍华.社斯顿是个魔术大师，虽然别人也有他那样的技巧，但许多魔术师在面对观众的时候，总是会想“看啊，那里坐着的是群蠢货，一堆土包子。看我必能把他们唬的目瞪口呆。”社斯顿绝不这么想，他每次上台都会对自己说“我很感谢这些人来看我。是这些人使我的生活如此愉快，我要尽量把绝活使出来让大家欣赏。”这就是他的成功秘诀。&lt;/p&gt;

&lt;p&gt;罗斯福总统就是这样一个真诚关心他人的人，有次仆人问他什么是鹌鹑时，他详细的跟他解释，还在某日打电话给对方让他看窗外有只鹌鹑停在窗口外面。所有白宫里的工作人员他都会跟他们非常亲切的交流，对每个人都打招呼并且寒暄几句。这些人都非常怀念罗斯福在白宫的日子，那是他们仅有的感到快乐的日子。&lt;/p&gt;

&lt;p&gt;对看似平凡的人给予同样的关怀，使得一位业务员挽回了一个客户。他叫爱华徳.赛克斯，他每次去一家药品杂货店都会跟柜员职员寒暄几句，然后才去见店主。有次店主告诉他不卖他的产品了，他落荒而逃，当他平复心情后回到店里，照常跟店员聊了几句再去见店主，此时店主却高兴的欢迎他回来并且比平时订的货还多，当他奇怪时，店主告诉他，有个卖饮料的男孩告诉他，他是所有推销员中少数几个会同他打招呼的人之一，他告诉店主如果有什么值得信赖的人的话，那就是赛克斯了，从此店主成了赛克斯最好的客户。&lt;/p&gt;

&lt;p&gt;对人关心是推销员必须具备的特质。我从个人的经验中也发现，只有你真正关心他人，才能赢得他人的注意、帮忙和合作。&lt;/p&gt;

&lt;p&gt;不仅如此，如果我们想结交朋友，就要先为别人做些事情，那些需要真正花时间、精力、体贴、奉献才能做到的事。
还有，如果我们想结交朋友，在向人致敬的时候一定要显得热诚而有精神。最好用愉悦悦耳的声音说“哈喽”。很多公司训练接线员的回答电话的声音就是这样的，要显得关心、热忱。以后我们说话和打电话时也要记住这个要诀。&lt;/p&gt;

&lt;p&gt;一位生活在底层的人也曾告诉作者这样的故事。那时他还在靠着社会福利救济金过活，有天他受伤趟在医院里，没人来看他，他觉得十分孤独、绝望和恐惧，于是躲在被子里哭，有个护士过来帮他抹去眼泪，跟他聊天，邀请他一起在医院吃饭，直到开始上班才离去，并且下班后又回来看他，陪他聊天，最后直到他睡下了才离开。他永远不会忘记那个特殊的日子，那时他有挫折、恐惧、孤独，还有来自一位陌生人的温情和关怀。&lt;/p&gt;

&lt;p&gt;真诚地去关心他人，这样我们才能真正的交到朋友，得到别人的喜欢。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-15&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;18如何使人喜欢你-给予真诚的微笑&quot;&gt;18，如何使人喜欢你-给予真诚的微笑&lt;/h4&gt;

&lt;p&gt;每个男人都知道一点关于女人的微笑，一个女人脸上所表露的神色，比身上所穿的衣服重要得多。&lt;/p&gt;

&lt;p&gt;斯瓦伯说他的微笑值百万，他的成功差不多都要归功于他那倾心的微笑。那么我们只是张嘴笑就可以了吗？不是的，微笑必须是真实的、热心的，有内心发出来的微笑，那种能在市场上得到好价格的微笑。&lt;/p&gt;

&lt;p&gt;纽约的百货公司人事部主人告诉作者，他只雇佣有一个可爱微笑的职员，也绝不雇佣一个面孔冰冷的博士。一家大橡胶公司董事也说，就他的观察，一个人无论做什么事，如果不高兴去做，是很少成功的。&lt;/p&gt;

&lt;p&gt;纽约交易所交易员丹哈德说了他的故事，他已经结婚18年了，对妻子很少微笑，他是百老汇街上脾气最坏的人。因为作者要他演讲一个关于微笑的经验故事，于是他开始尝试一周，每天早上向妻子面带微笑的说“亲爱的早安”，就这样坚持了两个月，没想到这两个月所得到的快乐比过去1年中所有加起来还要多。于是我在办公室也开始这么做，对公寓中对所有遇见的工作人员都说一声“早”并报以微笑，在地铁站小店里买东西时对里面的伙计也微笑，站在交易所里对所有工作人员都以微笑相待。不久就发现人人都反过来对他微笑，微笑每天都带给他许多财富。最后他已经摒弃批评了，嘴里只有欣赏和赞赏。他说他已经不讲我要什么了，而是看别人需要什么。整个人都变了，变成了一个更快乐更充实，拥有更多朋友的人。&lt;/p&gt;

&lt;p&gt;棒球手白德阁，现在是一个美国最成功的保险商人，他告诉作者，自己多年前研究得出，会微笑的人永远受欢迎。所以他每次去别人办公室前都停留片刻，想想他应该感谢的许多事情，这能引发他真实的微笑，然后再进入办公室。
他相信这种简单的技术与他销售保险获得特殊的成功有很大的关系。&lt;/p&gt;

&lt;p&gt;赫巴德写过这样一段话：对朋友微笑，每次握手集中精神，不要怕被误会，不要费一分钟想你的仇敌。全神贯注于你喜欢做的事情上，一段日子后你就会发现你不知不觉抓住了你梦寐以求的机会。保持一个正确的心态，勇敢、诚实、欢悦的态度。
所有的事都是由欲望而生的，凡真心祈求的都会应验，因为我们心中关注的是什么，我们就会变成什么。&lt;/p&gt;

&lt;p&gt;给予别人真诚的微笑，使人喜欢你&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-16&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;19记住别人的名字&quot;&gt;19，记住别人的名字&lt;/h4&gt;

&lt;p&gt;吉姆是个孤儿，10岁就去砖厂搬砖，他从未接受过教育。但凭着他乐观积极的性格和讨人喜欢的本领慢慢摸爬滚打，最后参政当上了美国邮政总监，他的秘诀就是“记住别人的名字”。
吉姆在早年就发现普通人对自己的名字最感兴趣。记住他人的姓名并很轻松的叫出来，你便是对他有了巧妙而很有效的恭维。&lt;/p&gt;

&lt;p&gt;钢铁大王卡耐基10岁的时候就发现人们对自己的名字惊人的重视，有一次他的兔子没有食物可以喂了，于是他告诉邻近的孩子们，如果他们愿意出去采集充足的蒲公英与金花菜来喂他们，他就可以用自己的名字来命名这只兔子，这一计划功效神奇。
后来他把这套运用到商业上，有一次他们与普尔门精英的公司竞争，双方比拼的不可开交。有一天卡耐基刚好在旅馆遇到普尔门，就与他讨论互相合作而非竞争的事，最后普尔门问“他们合作的新公司起什么名字”，卡耐基立刻回答“当然是普尔门卧车公司”，普尔门听后脸上发光，随后这桩实业界的奇迹就这样促成了。卡耐基这种记忆与尊敬朋友的名字的策略是他成为商界领袖的秘诀。&lt;/p&gt;

&lt;p&gt;罗斯福也同样非常重视别人的名字，有一次张伯伦为他定制了一辆汽车，并与机械师一同将此车送交白宫，罗斯福在众人面前大赞张伯伦送来的汽车，在他们离开时，罗斯福总统特意寻找那位机械师与他握手，并呼出他的名字，只因为他们在交谈时听过一次他的名字。罗斯福向他感谢他来华盛顿，并制作出了这么精美的汽车，他的致谢绝非草率，确是一种真诚。
罗斯福知道一种简单、明显、并且重要的获得好感的办法，就是记住他人的名字，并呼出来使他感觉重要。&lt;/p&gt;

&lt;p&gt;那么用何种方法才能记住别人的名字呢？
拿破仑三世说自己与别人交谈时会仔细听对方的名字，如果没听清楚，就会说“对不起，我没有听清姓名”，如果是一个不常见的名字，则他会继续追问“请告诉我你的名字是如何拼写的”。在谈话中，他费心地将姓名反复记忆数次，并在脑海中奖姓名与这人的面孔、神色以及其他外观联系起来。如果这人很重要，那更要费点事去记忆，他会把名字写在纸上，并注意观察他们，牢记在心后将纸撕掉，这样印象深刻使得名字记在心中。&lt;/p&gt;

&lt;p&gt;记住别人的名字，它是语言中最甜蜜最重要的声音&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-17&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;20静静听他人讲话&quot;&gt;20，静静听他人讲话&lt;/h4&gt;

&lt;p&gt;作者有一次在一个聚会上与一个朋友聊天，她说想请作者告诉她到过的所有名胜及见过的奇景。当他们坐下后，朋友说丈夫最近去了趟非洲，作者表示很好奇且非常感兴趣，于是这个朋友滔滔不绝说了45分钟直到谈话结束。
作者说其实她并不要听的我的旅行，她所需要的不过是一个专注的听她讲故事的人，这能使她很愉快。&lt;/p&gt;

&lt;p&gt;作者也在一次宴会上遇到一个植物学家，并且静静的在椅子上听他讲述关于大麻、室内花园、以及马铃薯的惊人事实。最后结束时，那位植物学家向主人极力夸奖作者是一个“富有激励性的、有趣的谈话专家”，但其实作者什么都没做，只是静静的听他讲他的故事，并且真的对他讲的故事感兴趣。
静静的听是我们对任何人的一种最好的恭维，让对方感受到自己被受到了足够的尊重。&lt;/p&gt;

&lt;p&gt;玛顿说了自己经历的一个故事，有个顾客的衣服褪色了，他回去找到店员，店员跟他争论不休，这使得他更加恼怒，此时有为经理过来调解，他静静听了他的述说，并且站在顾客的角度与店员辩论，最后他承认自己并不真正知道原因究竟是什么，希望顾客再使用1周，如果还不满意保证帮他换一套满意的衣服。最后顾客满意的走出了商店，使用一周后衣服并没有毛病，并对这家商店恢复了信任。
那些挑剔的，情绪激烈的人，常常在一个忍耐、同情的静听者面前软化降服，因为他们感觉到了自己被尊重和重视。&lt;/p&gt;

&lt;p&gt;另一个打电话投诉电话公司的老者以某种理由拒绝支付费用，并且不断投诉公司，一位富有调解技巧的调解员去拜访他4次，每次都静静的听他发牢骚，并对他表示同情，前几次每次都有3个小时的牢骚，最后1次他成为了老先生的朋友，并且老先生改变了态度，变得友善且支付了账单。
这位老先生认为自己在为公义而战，保障公众权利，不受无情的剥削，但实际上他需要的是得到尊重和重视。他一开始以不停的抱怨和投诉来求得这种重视和尊重，后来他从调解员那里得到重视和尊重后，他不切实际的冤屈就消失了。调解员的忍耐和静听降服了老先生。&lt;/p&gt;

&lt;p&gt;不只是普通人，名人也有这样的问题。林肯总统在最黑暗的内战时邀请老朋友来白宫聊聊黑奴宣言的事，老朋友到白宫后，林肯一直滔滔不绝的说着自己对这些事情的看法和想法，最后竟然都没有征求老朋友的意见就送走了老朋友。
林肯总统也与我们普通人一样，在困难中，在感情受到伤害时，需要一位友善的、富有同情的静听者，使得他可以发泄苦闷。&lt;/p&gt;

&lt;p&gt;要使人对你感兴趣，那就先使人感兴趣。问别人喜欢回答的问题，鼓励他人谈论他们自己以及他们所取得的成就。
不要忘记，与你谈话的人，他们对自己的需要，自己的问题，比对你及你的问题要感兴趣100倍。&lt;/p&gt;

&lt;p&gt;静静听别人讲话，鼓励对方谈论他们自己&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-18&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;21主动迎合别人的兴趣&quot;&gt;21.主动迎合别人的兴趣&lt;/h4&gt;

&lt;p&gt;罗斯福总统每次接待来宾都说罗斯福见识广博，是因为它每次接待访客前一晚都会阅读访客的资料来了解他们的兴趣与爱好，以便找到令人感兴趣的话题。&lt;/p&gt;

&lt;p&gt;作者小时候在家遇见一位律师，他与父母交谈完后与自己聊天，当时自己谈了很多自己感兴趣的关于船的事并向母亲称赞他为人很好，而母亲告诉他，其实他对船一无所知，只是见我对船感兴趣，所以谈论它能让我喜欢并感到愉悦，同时也使他自己被人们所欢迎。这件事震惊了我。&lt;/p&gt;

&lt;p&gt;查理夫曾为童子军去欧洲露营筹集款项，有一次要请一个大公司的经理资助，听说他曾开过一张100万美元的支票最后被退回来的故事，于是当他访问这位经理时，告诉他自己听说过100万美元支票的事，并表示很羡慕他，请他告诉自己其中精彩的经过。这位经理兴致勃勃的聊了很久他所做的事，最后才回神过来问查理夫“我顺便问你，你要见我有什么事？”当查理夫告诉他关于资助童子军的事后，对方爽快的答应了，并且还资助了5位童子军去欧洲露营，还在巴黎接待了他们。
如果不是查理夫找出他感兴趣的事，恐怕他很难接近这位经理。&lt;/p&gt;

&lt;p&gt;杜佛诺先生想把接下一家旅店的面包生意，他跟了这家旅店经理前前后后4年也没什么进展，最后发现这位经理对旅馆招待员协会很感兴趣，而且是这个协会的会长，于是有一次与他碰面时跟他聊了关于招待员协会的事，他们聊的很开心。这次谈话后旅馆的一位负责人打电话告诉杜佛诺，要他带着货样和价目表去签合同。
若不是杜佛诺找到这位经理感兴趣的事，恐怕还得紧追不舍好久。&lt;/p&gt;

&lt;p&gt;我们应该主动寻找和谈论别人感兴趣的话题&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-19&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;22让别人感到被重视&quot;&gt;22.让别人感到被重视&lt;/h4&gt;

&lt;p&gt;人们总是喜欢自我表现，夸大吹嘘自己，一旦事情成功，他们首先表现就是自己有多大的功劳，作出了多大贡献。其实这样不就是向他人表明，你们并不重要，这无形中伤害了别人，最终也伤害了自己。&lt;/p&gt;

&lt;p&gt;作者有一次去邮局寄包裹，营业员对工作感到厌烦，作者想让他喜欢自己，于是想说些好话，但又想不出能值得称赞他的事，这确实是个难题，特别是面对一个陌生的人。当他称重作者的物件时，作者热切的称赞他的头发很浓密、乌黑靓丽。营业员露出了微笑并告诉作者“它已经不像以前那么好看了”，然后他十分高兴的与作者聊了一会。
事后有人问作者“你想从那人身上得到什么？”。其实作者就是想从他身上得到快乐，作者得到了助人的快乐，并把这种快乐传递给别人，这种感觉和记忆会永远保存在我们的记忆中。&lt;/p&gt;

&lt;p&gt;如果我们能时时让别人感到重要，生活中大概不会惹来什么麻烦，而且可以得到许多友谊和永恒的快乐。
哲学家约翰.杜威说过“人类本质里最深远的驱动力就是，希望自己具有重要性”
心理学家威廉.詹姆士也说过类似的话“人类本质中最殷切的需求就是，渴望得到他人的肯定”&lt;/p&gt;

&lt;p&gt;加州有位老师叫罗兰，上完课后与班里一位安静、害羞、缺乏自信心的男孩克里斯搭话，他问他是否喜欢上这堂课，男孩很紧张害羞，情绪波动很大，极力忍住后说“你是说，我表现得不够好吗，罗兰先生”，罗兰回答到“啊不，克里斯，你表现的很好”，下课后走出教室时，克里斯用肯定有力的语气对罗兰说“谢谢你，罗兰先生”。这使得罗兰对我们内心深处的自尊印象深刻，于是他每堂课都在教室前方挂上一个标语“你是重要的”，来提醒自己，每个我所面对的学生都同等重要。&lt;/p&gt;

&lt;p&gt;差不多每个人都认为自己在某些地方比你优秀，所以要打动他们内心的最好方法，就是巧妙的表现你衷心的认为他们很重要。
带给对方快乐的同时，自己也感受到了快乐。当对方感觉自己被重视时，我们也得到了尊重。&lt;/p&gt;

&lt;p&gt;真诚的表现，让对方感到自己被重视&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-20&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;23不要争论不休&quot;&gt;23，不要争论不休&lt;/h4&gt;

&lt;p&gt;作者有一次出席一个名人的宴会，在宴会上这个名人说了一句引字《圣经》的话，作者知道它其实是引自莎士比亚，于是为了彰显自己的自重感和优越感，未经请求就去纠正了他，但他坚持阵地与作者争辩。争辩许久，作者无奈只好请求旁边博学的朋友帮忙，朋友用脚踢了作者，并说作者是错的，那位名人是对的。事后作者问朋友为何如此说，朋友说“为什么要证明他是错的呢，那能使他喜欢你吗，应该永远避免正面的冲突”。
永远避免正面的冲突，作者牢牢的记下了这句话。&lt;/p&gt;

&lt;p&gt;作者说自己年轻时就是一个执拗的辩论者，喜欢与别人辩论，参加了许多辩论大赛，甚至计划写一本辩论的书。于是他研究了很多辩论，并注意了它们的影响，最后发现一个结论：天下之忧一种方法能使辩论的利益最大化，那就是避免辩论。
我们10次辩论后有9次，结果是每个争论的人都比以前更坚信他们是绝对正确的。&lt;/p&gt;

&lt;p&gt;我们在辩论时，我们也许是对的，绝对是对的。但在并不能改变对方的思想，就像我们错了一样。我们绝不可能用口头争辩来改变任何人的思想，无论他的智力高低。&lt;/p&gt;

&lt;p&gt;以前有一位税务顾问叫巴森士，他与一位税收稽查员争论税务问题。这位稽查员冷淡、傲慢、固执，巴士先生与他争辩毫无办法。于是他换了种方式，他绝对避免辩论，他先称赞他所说的知识曾在书本中看到过，夸奖他经验丰富，然后与他探讨民间一些巧妙舞弊的方式，最后还聊到他孩子的故事，在临走前，稽查员告诉他愿意再考虑下巴士先生的问题，过几天给予答复。3天后他到巴士先生办公室告诉他已经按照他的意思办理税目。
稽查员表现的正是一种最普通的人性特点，他需要一种自重感。巴士先生越与他争论，他就越想扩大自己的权利，得到他的自重感。但一旦承认他的重要，辩论便停止了，因为他的自尊心得到了满足，他立即变成了一个同情和友善的人。&lt;/p&gt;

&lt;p&gt;我们常常要记得，生活中，要使得我们的顾客、情人、丈夫、妻子在偶然发生的细小讨论上故意让他们胜过我们。这样才能使得人们信服。&lt;/p&gt;

&lt;p&gt;永远避免与人正面冲突，不要争论不休。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-21&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;24尊重他人的看法&quot;&gt;24，尊重他人的看法&lt;/h4&gt;

&lt;p&gt;当我们指出对方的错误时，对方会因此同意你的观点吗？绝对不会。因为我们先伤害了他们的智力、判断力、荣誉和自尊，这只会造成对方的反击，而不会改变他人的观点。
千万不要宣称“我要证明给你看”&lt;/p&gt;

&lt;p&gt;诗人波普说，“你在教人的时候，要好像若无其事一样。事情要不知不觉地提出来，好像事情是被他遗忘的一样”
科学家伽利略也说过，“你不能教人什么，你只能帮助他们去发现”
切斯特菲尔德爵士告诉儿子，“要比别人聪明，但不要让他们知道”&lt;/p&gt;

&lt;p&gt;指出别人的错误是要付出代价的。如果一定要指出，也要有技巧。“啊，慢着，我有另一个想法，不知对不对。假如我错了希望你纠正我，让我们共同看看这件事”。
如果你这样说，天下绝对没有人反对你。&lt;/p&gt;

&lt;p&gt;心理学家卡尔.罗杰斯在他的书中写过，“能了解别人的想法，你会获益很大。也许你会觉得奇怪，真有必要去了解别人吗？我想是的。每当有人表达自己的看法时，我们通常第一反应是，你错了，你好蠢，这毫无道理，我们很少去了解对方真正的意图”&lt;/p&gt;

&lt;p&gt;作者有一次买了一个很贵的窗帘，结账时自己也吓一跳。有一天一位朋友来看他，当他得知窗帘的价格时态度很夸张“什么？别吓人了，你肯定被骗了。”于是作者只能提出便宜没好货的道理来搪塞。而第二天另一个朋友来看他，见到窗帘赞不绝口，说自己也要买一个。此时作者的反应与前一天截然不同，“啊，老实说，我买贵了，真后悔没先问好价钱”。
假如别人的态度温和一些，或显得有技巧一些，我们就有可能会向他们认错，甚至自认为自己心胸宽大。假如对方让你难堪，则情况会截然相反。因为当你过于直率的指出别人的错误时，你已经剥夺了别人的自尊，再好的意见都不会被人接受。&lt;/p&gt;

&lt;p&gt;有次总统问罗伯特.李将军对麾下的一名将军的看法，李将军对齐称赞有加，另一位军官诧异的说“难道您不知道那人无时无刻不在对你攻击和诽谤吗？”，李将军回答到“我知道，不过总统问的是我对他的看法，并不是他对我的看法”。
我们应该用别人的原则去判断他们，而非用我自己的原则。&lt;/p&gt;

&lt;p&gt;别与顾客、配偶、敌人发生冲突，别指责他们的错误，别惹他们动怒，如果非得与人发生对立，也得运用一点技巧。
对别人的意见表示尊重，千万别说“你错了”。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-22&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;25先承认自己的错误&quot;&gt;25，先承认自己的错误&lt;/h4&gt;

&lt;p&gt;作者喜欢狗常常在一个人烟少的公园遛狗，但不喜欢把狗拴着，有一次一个警察看到他命令他为狗带上口罩并且拴住它，以显示他的权威，作者谦逊的应允遵守了他的命令。后来作者实在不喜欢为宠物狗套上口罩和栓绳，于是就在公园里与宠物狗玩，但又遇上了这位警察，于是作者主动承认错误，告诉警察自己不应该放任小狗在这里乱跑，这位警察则温柔的说，这里没什么人，这么小的狗稍微跑一下没事。
警察也有人情味，当作者开始自责时，警察唯一能滋长自尊的办法就是采取宽大的态度，以显示自己的慈悲了。通常如果你将别人正想批评你的事情在他有机会说出来前说出来，他就会采取宽厚、原谅的态度，以减轻你的错误。&lt;/p&gt;

&lt;p&gt;美国南北战争时，李将军的部队战败了，李将军悲痛至极，他向南方政府总统提出辞呈，要求另外一位“年富力强的人”。李将军内心高贵，并没有责备别人，其实他可以找出数十个理由，师长不胜任，马队太迟到达没有协同步兵进攻等等。&lt;/p&gt;

&lt;p&gt;当我们对的时候，我们要温和地、巧妙地区得到人们对我们的同意。当我们错的时候，我们要当即真诚地承认我们的错误，通常情况下这比为自己辩护更有趣。&lt;/p&gt;

&lt;p&gt;用争夺的方法，你永远得不到满足，但用让步的方法，你可得到比你所期望的更多。&lt;/p&gt;

&lt;p&gt;如果我们错了，先主动承认自己的错误&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-23&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;26温和友善地对待他人&quot;&gt;26，温和友善地对待他人&lt;/h4&gt;

&lt;p&gt;美国1915年左右都处于大罢工状态，洛克菲勒作为公司的代表要与工人们进行谈判，但他并没有去激怒他们，而是以友善的方式平息了众怒，还为自己赢得了不少赞赏。他在一次与工友们演说时说道“这几周我拜访过好多工友，与他们的家人见面，现在虽然我们谈不上好友，但与你们也算是朋友，基于这份友谊，我很高兴有这个机会和大家讨论我们的共同利益。我承蒙你们的好意，得以坐在这里，我虽然并非股东或劳工，但我深感与你们关系密切。”
这是一番很出色的演讲，这可能是化敌为友的一种最佳的艺术表现形式之一。&lt;/p&gt;

&lt;p&gt;林肯在100多年前说，假如人心不平，对你印象恶劣，你就是用尽所有基督理论也很难使他们信服于你。想想那些好责备的父母、专横跋扈的上司、唠叨不休的妻子。我们都应该认识到一点：人的思想不易改变。你不能强求他们同意于你，但你完全有可能引导他们，只要你温和友善。&lt;/p&gt;

&lt;p&gt;怀特公司大罢工时，总裁罗波.布莱克没有采取对抗的做法，而是登报称赞罢工者用和平的方法解决问题，并在公司内为他们买了很多棒球、保龄球，并且租下一个保龄球场提供他们去放松。布莱克先生富有人情味的举动，得到的是富有人情味的反应。工人们自觉的打扫公司，清扫工厂附近的地面，最终这场罢工在一星期内就和解了。
没有恐吓、没有高压手段、没有强迫说明的企图。他用最温和、平静、友善的方式处理时，仍然能不失权威性，而这正是他成功的最大助力。&lt;/p&gt;

&lt;p&gt;士特劳博住的房子要到期了，他想要求房东减少点房租，这是项艰巨的任务。于是他写了封信温和的告诉房东自己正在尝试向房东请求减少房租的事，房东一接到信后就来找他并在门口聊了许久，他完全没有提房租的事，只是在真诚的慷慨的赞美他，恭维他很会管理这个房子。房东告诉他一些自己的烦恼，一些房客写信抱怨房租和邻居的事，像士特劳博这样的房客让他很省心。最后房东自己降低了房租，并在离开时还询问士特劳博是否需要装修下。
这就是友善、同情、赞赏所产生的力量。&lt;/p&gt;

&lt;p&gt;温和、友善和赞赏的态度更能教人改变心意，这是咆哮和猛烈攻击所难以奏效的。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-24&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;27先说些对方同意的话题&quot;&gt;27，先说些对方同意的话题&lt;/h4&gt;

&lt;p&gt;当你与别人交谈时，不要先讨论你们都不同意的事，要先讨论你们都同意的事，因为这样你们就是有了合作与共同之处，你们的相异之处在于方法而不是目的。
尽量不要让对方说‘不’，当你说了一个‘不’字后，你那本性的自尊就会迫使你继续坚持下去。&lt;/p&gt;

&lt;p&gt;懂得说话技巧的人，就会在一开始找出许多“是”的答复，这可以引导对方进入肯定的方向。
“是”的反应其实是一种很简单的技术，却为大多数人所忽略。很多人以为在一开始便提出相反的意见，这样不正好可以显示出自己的重要而有主见吗？但事实并非如此。&lt;/p&gt;

&lt;p&gt;有位年轻人来银行开户，但态度恶劣，不想填写资料单，于是艾博森用自己的技巧与他交流。他先同意了这位先生的观点，填写资料并非必要，然后问他如果有意外发生，你是否愿意银行打电话通知你，或者转交给自己亲信的人，他说是的，然后继续问是否愿意将亲人的名字写下来告知银行，他说是的。年轻人的态度开始缓和下来，知道这些材料并非仅仅服务于银行，而是为他个人利益，最后他不仅填写了资料还在艾博森的建议下开设了信托账户。
这一切都开始于同意，先同意对方的观点，并渐渐让对方同意自己的看法。&lt;/p&gt;

&lt;p&gt;爱迪对弓箭很敢兴趣，他哥哥建议他去租弓箭，于是他去常常光顾的店里询问，但店员说他们不对外租借弓箭，于是他又去了另一家店，那家店的店员跟他解释道，很多年前店里做过租借弓箭的生意那时租金要25美元，还问我是否是个喜欢节约的人，我说是的，他继续说，他们正好有一套弓箭在售，包括所有的小装备，总价才30美元，如果你再多付5块钱就能得到全部器材而不只是租借，后来爱迪当然买下了那套器材，并且还买了额外的东西，从此他就成了他们家的常客。
店员温和的解释并找到了爱迪的共同点，使得他赢得了顾客的芳心。&lt;/p&gt;

&lt;p&gt;苏格拉底是人类历史上伟大的哲学家之一，他改变了人类的思考方式。他的秘诀是什么，是指出别人的错误吗？当然不是。他找出并问些对方同意的话题，然后渐渐引导对方进入设定的方向。
下次你要告诉别人的犯错时，问些温和的话题，找出对方同意的话题，一些能引发对方做出‘是’的反应的问题，这样就能以柔克刚。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-25&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;28给对方说话的机会&quot;&gt;28，给对方说话的机会&lt;/h4&gt;

&lt;p&gt;很多人为了让别人同意自己的观点往往采用了错误的策略：说太多。其实不如让对方畅所欲言，因为每个人对自己的事和自己有关的问题一定比我们知道的多，所以不如问他一些问题，引导他讲述自己的事情和想法，这样我们才能从中得到更多的信息也能更有把握好方向引导对方。&lt;/p&gt;

&lt;p&gt;一位R先生由于嗓子哑得厉害去参加投标却得到了一次意外的经历，有好几家公司去检验样品，那天他去参加对方公司高级职员议会时无法说话，只能写字告诉众位自己嗓子疼的厉害无法说话，此时对方公司经理站出来说自己可以替R先生说话，于是这位经理不断的描述R先生带去产品，并且品论它的优点与缺点，大家纷纷议论，在此期间R先生只是轻微的点头和少量的手势。最终R先生惊喜的得到了那笔合同，价值160万美元，这是他得到的最大的订单。
这个例子稍微离奇了一些，但也同样能说明让对方说话的重要性，这能使得对方充分参与进我们的世界中。&lt;/p&gt;

&lt;p&gt;一家电器公司的业务员范勃也有类似的经历，他在农村推广农舍电光设备时，打听到这里的农舍们有反感情绪。于是他亲自去敲开了一家农舍的门，一个妇人一看见是公司代表就叩上了门，于是范勃告诉她自己是来了解她对公司的看法的，妇人又一次探出头，范勃称赞妇人养的鸡很好，是否愿意卖一些鸡蛋给她，这又一次撩动了她的心，门又被打开了一些，范勃环顾四周发现农场中有一个牛奶棚，就继续说，自己猜测妇人养的鸡赚的钱一定比丈夫多，此时妇人高兴极了，开始请他去参观她的鸡舍，并详细说了自己的养鸡经验和自己制造的一些小设备。范勃也介绍了几种食料和温度，并向妇人征求了意见，他们很高兴的交换了经验。过了会，妇人开始说自己几位邻居在他们的鸡舍里安装了电光设备说是效果非常好，她开始征求范勃的意见，问是否应该采取这种办法。两星期后妇人购买了范勃的电光设备并使得鸡舍产量提高了不少，这是个双方都满意的结果。&lt;/p&gt;

&lt;p&gt;事实上即使我们的朋友，也宁愿对我们谈论他们自己的成就，而不喜欢听我们吹嘘自己的成就。
哲学家罗西法说，如果你要树敌，就胜过你的朋友。如果你要得到朋友，那就让你的朋友胜过你。
当我们的朋友胜过我们时，他们获得了一种自重感。但当我们胜过他们时，他们会产生一种自卑感，并引起猜忌与妒忌。&lt;/p&gt;

&lt;p&gt;不要时时向他人夸大自己的成就，我们要谦逊，这样永远能使人喜欢。
我们应当谦逊，因为你我都没有什么了不起的，你我都要逝去，并很快被人遗忘，生命过于短促，不要总是谈论我们小小的成就，这使人厌烦。相反，我们应该鼓励对方说话。&lt;/p&gt;

&lt;h4 id=&quot;29引导而不是强迫别人做某事&quot;&gt;29.引导而不是强迫别人做某事&lt;/h4&gt;

&lt;p&gt;一家汽车公司业务经理阿道夫与员工做了一次交易，他让员工真诚的提出自己对公司的期望，他也提出了自己对员工的期望，他们做了一场交易，他用满足员工愿望的方式来与员工努力工作交换。他并没有强迫员工去做努力工作，而是以引导的方式。&lt;/p&gt;

&lt;p&gt;没有人喜欢接受被推销，或者被人强迫去做一件事。我们都喜欢按照自己的意愿购买物体，按照自己的意思行动，我们喜欢别人征询我们的需求、意见和愿望。&lt;/p&gt;

&lt;p&gt;韦森先生从事服装设计，3年来他每次去拜访一位著名设计师都被他拒绝购买他的产品。后来他改变了方式，他带着几张美完成的草图去拜访，他说“我想请你帮点小忙，这几张尚未完成的草图，可否请您帮忙给出意见，以更加符合你们的需要”。设计师让那个他3天后来找他，3天后韦森听了设计师的意见，并按照他的意见做了改动，最后他自主的买下了这个作品。因为雇主成了设计参与者，他当然用心且喜欢。&lt;/p&gt;

&lt;p&gt;发生在一个医生L身上也有如此的情况。他工作在一家大医院里需要添加几套X光设备，很多厂商都来介绍自己的产品，有一家公司很聪明，写信给他告诉他，公司设计了一套X光设备，设备并非完美，还需要他来指导一下，提携意见，以便他们改进，他们可以随时派车过来接送。L医生每晚都很忙，但还是取消了一个约会，腾出时间去看了看那套设备，最后发现自己越研究这套设备越喜欢这套机器。
这就是自主的力量，引导对方用他们的行为去实现自己的目标。&lt;/p&gt;

&lt;p&gt;一个加拿大钓鱼营地的做法很聪明，他们给想去钓鱼的潜在客人们寄去一封信，信里附有很多姓名和电话，他们都是曾经去过那里的人，告诉客人们可以打电话询问这些人便可以知道他们提供哪些服务是否好玩。我很惊奇发现一个朋友的名字于是就打过去问了一下，最后定下了他们的营地。&lt;/p&gt;

&lt;p&gt;中国老子也说过，圣人若想领导人民，必须谦卑服务；若想引导人民，必须跟随其后。圣人虽在上，而人民不觉其压力；虽在前，而人民不觉得有什么伤害。&lt;/p&gt;

&lt;p&gt;聪明人引导别人，强迫是最差的下下策&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-26&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;30从他人角度考虑问题&quot;&gt;30，从他人角度考虑问题&lt;/h4&gt;

&lt;p&gt;对方或许真的错了，那也不要指责他，这是愚人的做法。应该去了解他，也只有聪明的人才会这样做。
我们应该假设，自己处在他当时的困难中，我们将有何感受，会有何反应。这样做可以省去你许多时间和烦恼，也可以增加许多处理人际关系的技巧。&lt;/p&gt;

&lt;p&gt;从前作者家周围有个公园里面有很多橡树，但几个孩子总是去树下玩火，作者上前警告他们，用威严的声调命令他们将火扑灭。而且，如果他们拒绝，作者就恐吓他们要将他们交给警察。
作者只是在发泄情绪，而没有考虑孩子们的观点。结果，孩子们怀着反感的情绪遵从了，但他离开以后，他们又重新生火了，并恨不得烧尽公园。
多年后作者换了方法，他上前去温柔的告诉孩子们，公园中生火是极其危险的，我知道你们不是故意的，但别的孩子们不会这样小心，他们过来见你们生火了，他们也会学着生火，回家时也不扑灭，以致于蔓延烧毁整棵树。我不干涉你们的快乐，我喜欢看到你们感到如此快乐。&lt;/p&gt;

&lt;p&gt;这种说法产生的效果区别很大，孩子们产生了一种同你合作的欲望，没有怨恨，没有反感。他们没有被强制服从命令，保全了面子。&lt;/p&gt;

&lt;p&gt;我们应该永远按照对方的观点去考虑问题，由他人的立场去看事，这将成为影响你终身事业的关键因素。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-27&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;31同情对方&quot;&gt;31，同情对方&lt;/h4&gt;

&lt;p&gt;我们每天所遇到的人，之所以他们会烦躁、固执、缺乏理智，大部分是因为他们的出生所在的家庭以及父母的原因，其他与他们本身没有很大的过错。
你不是一条响尾蛇，唯一原因是因为你的父母不是响尾蛇，你不与牛接吻，不以蛇为神，唯一原因是，因为你没有生在印度家庭中。因此我们之所以成为现在这样的人，我们并没什么可以居功自傲的，大部分都需要归功于我们出生的家庭和父母。&lt;/p&gt;

&lt;p&gt;对待出现在你面前表现出烦躁、固执、缺乏理智的人，要对他们表示怜悯与同情，三分之四的人都是为了同情而活。给他们同情，他们就会即可喜爱你。&lt;/p&gt;

&lt;p&gt;作者有一次在广播上播音，说到了某个不讨人喜欢的人住在某地方，遭到了很多人的炮轰，其中有一个女士写信来言语非常恶劣。作者想给自己一个挑战，要将她的仇视变成友善，对自己说，如果我是她大概也要同她所感觉的一样。于是有一天，作者打电话给这位女士，说自己是卡耐基，前段时间在广播上犯了一个愚笨的错，并为此道歉，也同时感谢女士费心写信过来批评。女士听到道歉，也表示自己对写信批评的事惭愧，这舒缓了她的心情。他们聊了一会，最后女士怀着高兴的心情结束了对话。&lt;/p&gt;

&lt;p&gt;因为道歉，并同情她的观点，我得到了她的道歉，以及对作者的同情。作者得到了控制自己脾气的收获，以友善报答侮辱的收获，并从中得到了无限的乐趣。&lt;/p&gt;

&lt;p&gt;一位美国经纪人伍勒与艺术家打了22年的交道，他说自己与那些性情无常的艺术家交往时，得到的最大的经验是，对他们可笑而古怪的脾气表现出更多的同情。
他做了3年的亚彬的音乐会经纪人，他用一句话改过他的为人，“他各方面都是糟糕的很”。每次演唱前，亚彬都要三番五次的找各种借口要取消演唱会，伍勒没有反对，只是表示对他的同情，并且支持他取消演唱会，告诉他，这只会损失一点小钱，只是对他的名声损失比较大而已。发过3、4次的脾气后亚彬终于被哄着走上了舞台完成了演唱会。&lt;/p&gt;

&lt;p&gt;《教育心理》中说，人类普遍地追求同情，儿童迫切地显示他的伤害；或甚至故意割伤或打伤自己，以获得大量的同情。
出于同样的理由，成人也会显示他们的伤害，叙述他们的意外、疾病，特别是动手术开刀的详情。为真实的或想象的不幸而感到‘自怜’，实际上，这是人类的一种共同的习惯。&lt;/p&gt;

&lt;p&gt;同情对方所表现出来的意念和欲望，反感和厌恶只会让事情变得更糟。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-28&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;32激发别人高尚的品质&quot;&gt;32，激发别人高尚的品质&lt;/h4&gt;

&lt;p&gt;作者有一次去拜访大盗杰西的故乡，杰西儿子的太太给我们将了杰西的故事，他是如何抢劫火车和银行，然后把钱送给附近的农夫以偿还他们的贷款。
杰西已经成为犯罪们的榜样，他们都认为自己是劫富济贫的理想主义者。&lt;/p&gt;

&lt;p&gt;其实我们所遇到的每个人都很尊重自己，都认为自己是一个善良而不自私的人。
我们每个人的内心都把自己理想化，都喜欢为自己行为的动机赋予一种良好的解释。
因此我们要想改变他人，就应该诉诸一种高尚的动机。&lt;/p&gt;

&lt;p&gt;法瑞先生有一个挑剔的租客，他在合同到期前就单方面通知法瑞先生要搬出去。法瑞先生本可以痛骂他一顿，与他争辩要求他偿付赔偿或迫使他继续租下去直到期满。但他并没有这样做，他换了种方式，他向这位房客说，听说您要搬家，但我还是不敢相信您真的会这么做。我多年与人相处的经验使我多少看人比较准，我一直认为您是一个高尚的人，不会轻易做出什么损人不利己的举动。我相信您的为人处世是守信誉的，您可以再考虑几天，如果还是坚持要搬，我尊重您的决定。事情到了第二个月，房客亲自去见了法瑞先生并付了房租，他说自己和太太商量过认为至少也要住到期满。&lt;/p&gt;

&lt;p&gt;有家汽车服务公司的6位顾客对维修后的费用表示不满，他们认为清单上收费不合理并拒绝付款。公司的信用部经理亲自催收了几次也没有成效，他每次去都表明自己公司没有错，你们必须付清费用，这导致顾客很愤怒。于是总经理打算亲自出面来解决问题，他先向顾客们道歉，说之前公司的员工处理不当，自己来拜访他们主要是因为他们是最了解事情经过的人，他先静静听了他们的述说，然后说，听您的叙述，我深深感到您是一个正直且有耐心的人，这份账单您有权更正他们，于是这位顾客修改了账单，并削减了不少他认为不合理的部分然后付了钱，但其余5位都尽可能多付了些不让公司吃亏。事情的微妙之处是，后来2年内，这6个顾客又向公司购买了6台车。&lt;/p&gt;

&lt;p&gt;每个人都认为自己是高尚的，我们更应该用这种方式去激发别人高尚的品质，而不是去贬低和激怒他们。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-29&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;33用更有趣的方式表现你的意图&quot;&gt;33，用更有趣的方式表现你的意图&lt;/h4&gt;

&lt;p&gt;《费城晚报》曾收到一种恶意谣言攻击，说他们的报纸广告太多读者已经不喜欢。《费城晚报》将自己报纸上刊载的有趣的文章取出来，出版成一册书价值2美元，但它以2美分的价格出售好让公众们知道自己曾刊登大量有趣的读物。
《费城晚报》并不争辩，而是用这种有趣的方式来告诉人们，我们的报刊是受读者们欢迎的。&lt;/p&gt;

&lt;p&gt;纽约大学的鲍登和伯西分析了许多售货面洽的案例并写了一本书，后来他们把它拍成了一个电影，以真实表演的形式向数百家大公司的营业部职员展示如何去洽谈业务。
这种戏剧性的方式更能被大众所接受。&lt;/p&gt;

&lt;p&gt;这是一个充满戏剧性的时代，仅靠一点点语言的叙述是不够的。真理需要我们使之更生动，更有趣，更加戏剧化，你必须恰当运用表演的艺术来吸引人们了解你的意图。&lt;/p&gt;

&lt;p&gt;有个润肤膏品牌的业务员在向一家大公司介绍产品时跟这家公司的职员争辩，最后他赢得了辩论但却什么合同都没得到。第二次，他再次去了他们的办公室，并带了32个冷膏的样品，上面标注了商业调查结果，他为每个样品瓶子讲述了一个生动有趣的故事。最后原本只是10分钟的恰当变为了20分钟，40分钟，快到1小时的时候他们成交了。
这次这位业务员采用戏剧化的形式呈现自己的产品，这让听者更能感受到其意图。&lt;/p&gt;

&lt;p&gt;如果你想表达你的意图，不要争辩，用一种有趣的方式去表现，这更能吸引人。&lt;/p&gt;

&lt;h4 id=&quot;34不断面临挑战&quot;&gt;34.不断面临挑战&lt;/h4&gt;

&lt;p&gt;一家工厂效率低下，老板问经理为什么，经理回答说，自己用尽了各种办法，哄诱，强迫，威胁手段都用过就是不奏效。于是老板去工厂问了最近的一个人今天做了多少个单位，那人回答6个，于是老板在地板上写上了一个大大的“6”字就走了，夜班的人过来后好奇的问了这个数字，并在结束后擦掉了这个数字并写上了大大的“7”字。第二天上班的工人们看到这个数字，就说，他们要给夜班点颜色看看，于是他们留下了一个大大神气的“10”字。工厂的情形逐渐好转起来。&lt;/p&gt;

&lt;p&gt;要做成事的办法，是激起斗志，不是勾心斗角而是相互取胜的欲望。
取胜的欲望！挑战激发人产生一种向上的精神。&lt;/p&gt;

&lt;p&gt;没有挑战，罗斯福也不会当上总统，他刚从古巴回来时被推举为纽约州候选人，但反对者抗议他不是合法居民，罗斯福恐慌了要退出。普拉德激将他说，“难道圣巨恩山的英雄是一个弱者吗？”，这激起了罗斯福的斗志，随后的事情就已成历史了。&lt;/p&gt;

&lt;p&gt;纽约州有个臭名昭著的监狱，没有狱长，传出许多丑恶的黑幕。史密斯州长召来劳斯，告诉他那里需要一个有经验的人。劳斯窘了，他知道那里很危险，史密斯看出他的犹豫，微笑着告诉他，“青年人，我不怪你害怕，那不是一个太平的地方，但那里确实需要一个大人物去治理”。
他提出了一个挑战，劳斯喜欢用大人物的工作理念去挑战这个任务。最后他成为了哪里任职最久最著名的狱长，还著写了《在猩猩的两年里》并大卖几十万册。&lt;/p&gt;

&lt;p&gt;任何成功者都喜欢一种竞技，一种表现自己的机会，那是证明自身价值，争强斗胜的机会。&lt;/p&gt;

&lt;p&gt;如果你有使一个富有上进心、充满血气的人同意你的意见，那你就应该记住，向他提出一个挑战，一个他渴望的挑战。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-30&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;如何更好的说服他人&quot;&gt;如何更好的说服他人&lt;/h4&gt;

&lt;h4 id=&quot;35从称赞与真诚的欣赏开始&quot;&gt;35，从称赞与真诚的欣赏开始&lt;/h4&gt;

&lt;p&gt;这是一种心理技巧，当我们听到他人对自己的优点加以称赞以后，再去听一些不愉快的话，自然觉得好受一些。这就像理发师在修面之前，图上一层肥皂一样，有些缓冲作用。&lt;/p&gt;

&lt;p&gt;曾有一位共和党人写了一篇演讲稿，他自己觉得非常得意，于是大声朗读给麦金利听。麦金利听了觉得不太适合，但又不想伤害这位作者的感情，于是就对他说，“我的朋友，这的确是一篇极好的演讲稿，一篇伟大的演讲稿。这篇演讲稿在许多场合我可以这样说，但在这种特殊的场合是否合适？也许在你的立场来看，这是非常合理与慎重的，但我必须从共和党立场方向来考虑它的影响。现在按照我的指示方向修改这篇演讲稿”。那人回去照做了，后来成为了一名有影响力的演讲员。&lt;/p&gt;

&lt;p&gt;林肯在内战最黑暗的时期给前线的将军写了一份信，他先称赞这位将军，但又指出他了最严重的错误，林肯没有称他们为严重的错误，而是委婉的更富外交手段的指出他们。他在信中说，“我相信你是一位智勇双全的将军，那是我所喜欢的。你有志气，你有自信，那是一种有价值、不可少的性格。但你在阻扰一位战功显赫的同僚官员上犯了大错。政府要尽力帮助你，同其他所有将军的帮助一样。但我深怕你灌输军队以批评以及不信任，我要尽力帮助你消灭这种精神。现在要小心，不要匆忙，要尽力不懈地努力前进，使得我们胜利。”
信中隐含着一种非常严肃的谴责，但字面上却依然委婉诚恳，娓娓动听。在最黑暗的时刻，看不到一丝希望的曙光时，还能用最动听的语言来说服别人，这就是林肯的过人之处。&lt;/p&gt;

&lt;p&gt;这种处世哲学在日常生活和工作中也极其有用。&lt;/p&gt;

&lt;p&gt;在费城一幢办公大厦的建筑工程，差不多完工时，负责建筑物外部装饰材料的供应商突然声称他们不能按期完成供货。电话，争执，辩论，都没有用。高伍先生被派去督促他们。他到了这位经理的办公室就说道，自己在查电话簿时找到地址，电话簿中你的名字真是独一无二。经理听了很有兴趣的查阅电话簿，说自己的家庭是差不多200年前由荷兰迁到纽约的。接着他又谈论了自己家庭和祖先数分钟。高伍先生恭维他有这么大的一家厂，并称这里是自己见过最好的最清洁的铜器工厂。经理自豪的说，自己费了一生功夫经营这项事业，并愿意带他去参观一下工厂。参观期间，高伍先生又对他的构造系统赞美不觉。经理坚持要请高伍先生吃午餐，而高伍先生一直未提他来访的目的。午餐后，经理说，自己知道你来的目的，你可以带着我的应许回去，即使别的订货不得不延迟，也会优先制造好送过去。
处世的技巧就是这么奇妙，高伍先生甚至没有请求，就得到了所要的东西。&lt;/p&gt;

&lt;p&gt;如果你想更好的说服别人，先从赞赏和真诚的欣赏开始。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-31&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;如何更好的说服他人-1&quot;&gt;如何更好的说服他人&lt;/h4&gt;

&lt;h4 id=&quot;36委婉地指出他人的错误&quot;&gt;36，委婉地指出他人的错误&lt;/h4&gt;

&lt;p&gt;一家钢铁厂里几个工人在室内抽烟，头上挂着“请勿吸烟”的牌子。老板夏先生看到这一情形，并没有指着牌子指责他们，而是给每人递了一支烟说，老兄，如果你们能到外面抽我会很感谢你们。
夏先生不但没说什么，反而给了他们每个人一个小礼物，你能不尊重这样的老板吗？&lt;/p&gt;

&lt;p&gt;有时候我们先称赞别人然后转折后指出别人错误，这让人怀疑前面的赞美，如果能很委婉的指出错误，会让人更舒服。&lt;/p&gt;

&lt;p&gt;有人想让孩子加强数学学习，于是就说了，杰克，你这次成绩进步了我们很高兴，但是你如果能多加强一下代数，那就更好了。
但是的转折让人不舒服，如果能这样说就会好些，杰克，你这次成绩进步了我们很高兴，如果你在数学方面继续努力下去，下次一定会更其他科目一样好。&lt;/p&gt;

&lt;p&gt;间接提出别人的错失，要比直接说出口来得温柔，且不会引起别人的强烈反感。&lt;/p&gt;

&lt;p&gt;贾克卜太太就是这么做的，他请的加盖房子的建筑工人每天都把东西弄的乱糟糟，于是有一天她在他们离开后清理的下，第二天她对领工说，昨天自己很满意你们把院子清理的干干净净，从此以后工人们每天完工后都能收拾下院子。&lt;/p&gt;

&lt;p&gt;一位上士要让下属们去剪头发，他没有直接吼叫和恫吓，而是在集会上告诉他们，自己今天要去理发，军队对头发长度有规定，虽然自己头发比你们还短，如果你们也有需要可以安排时间去理发室。结果士兵们都去照了镜子，并且遵照规定理了发。&lt;/p&gt;

&lt;p&gt;委婉地指出别人的错误，能让事情进展的更加顺利。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-32&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;如何更好的说服他人-2&quot;&gt;如何更好的说服他人&lt;/h4&gt;

&lt;h4 id=&quot;37用谦卑的态度指出别人的错误&quot;&gt;37，用谦卑的态度指出别人的错误&lt;/h4&gt;

&lt;p&gt;作者的侄女来担任他的秘书，有次作者想指责她，但他想到，自己比她大2倍的年龄，做事经验更是多出好几倍，怎么可以要求她能有自己一样的看法、判断和主动自发的精神呢。想到自己19岁时的德行，像蠢驴一样犯下的错误，侄女实在比作者19岁时要好得多。
作者对侄女说，你犯了一个错误，这个错误我以前也常常如此。判断力并非与生俱来，我年轻时还比不上你呢。我实在没有资格批评你，但依据我的经验，假如你这么这么做的话，会不会好些。&lt;/p&gt;

&lt;p&gt;听别人数说我们的错误很难，但假如对方态度谦卑，我们就比较容易接受。&lt;/p&gt;

&lt;p&gt;有位工程师发现秘书常把信件拼错字，虽然他常常指正秘书，但她依然我行我素。于是他换了种方式，这次当他发现错别字时，他告诉他，这个字看起来似乎不像，这也是我常常拼错的许多字之一，我随时携带拼写本，现在对它十分注意，因为别人常会以此来批评自己并显得自己不够专业。&lt;/p&gt;

&lt;p&gt;克莱伦斯也用这种方式劝告了孩子抽烟的问题，他没有劝孩子不愁，或警告抽烟的危险，只是指出自己如何上烟瘾，然后受到如何的影响。孩子想了一阵决定在高中不抽烟了，好几年过去了孩子再也没抽过烟，而自己也在家人的帮助下戒掉了烟。&lt;/p&gt;

&lt;p&gt;在指责别人前，我们应该先想想自己的错误。&lt;/p&gt;

&lt;h4 id=&quot;38没有人喜欢受人指使&quot;&gt;38.没有人喜欢受人指使&lt;/h4&gt;

&lt;p&gt;作者听说美国实业家，商人，律师，外交官欧文.杨从来不指使别人，他常常会说，“你可以这样考虑”，“你觉得这样如何”，“也许这样写会好些”。
这种办法容易让一个人更正错误，并且还能保持个人的尊严，给他一种自重感，这样他就会与你保持合作，而不是背板。&lt;/p&gt;

&lt;p&gt;无礼的命令只会导致长久的冤仇，即使这个命令可以更正他人明显的错误。&lt;/p&gt;

&lt;p&gt;学校的一位老师看到有个学生的车子停在道路中央，就冲进教室大吼，是谁的车子挡住了道路，马上把车子移开，否则我叫人把车拖走。
虽然这个学生犯了错，但从那天起，所有的学生都对那位老师心存不满。如果这位老师能好好问，“谁的车子挡住了道路，可不可以去挪一下，方便大家进出”，相信这个学生会很乐意这么做。&lt;/p&gt;

&lt;p&gt;一家制造精密机器零件的工厂接到一批大订单，但工厂进度早有安排，短期赶出这批货比较难。老板麦当没有催促工人赶工，他只是召集工人们，把详细的事情说明了一番，便开始提问。“我们有没有办法可以处理这批货物？”，“有没有人想出其他办法可以赶出这批订单”，员工们纷纷提出意见，并坚持接下订单。&lt;/p&gt;

&lt;p&gt;作为一个有效的领导者，如果你想说服别人，你应该以建议和提问的方式来代替命令，因为没有人喜欢被人指使，也没有理所当然的可以指使任何人。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-33&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;如何更好的说服他人-3&quot;&gt;如何更好的说服他人&lt;/h4&gt;

&lt;h4 id=&quot;39尽力保全他人的面子&quot;&gt;39，尽力保全他人的面子&lt;/h4&gt;

&lt;p&gt;通用电气曾为了将一个天才工程师调任到其他岗位费劲了心思，它首先给这位工程师一个新的看起来不错的头衔，然后派了另一个人来顶替他的位置。&lt;/p&gt;

&lt;p&gt;保全他人的面子！这是个何等重要的问题。我们常喜欢摆架子、我行我素、挑剔、恫吓、在众人面前指责孩子或雇员，却很少能多考虑几分钟，对他讲几句关心的话，或为他设身处地的想一下。&lt;/p&gt;

&lt;p&gt;葛伦杰在会计师行业，他的行业具有季节性，热潮过后常会因为没活而解雇员工。但他从不对他人轻言解雇，他每次都会说，你的工作做得很好，上次我们要你去XXX，那工作很麻烦，而你处理得很好，一点也没有出差错，我们要你知道，公司十分引你为荣，也相信你的能力，愿意永远支持你，希望你别忘记了这些。
结果被遣散的人觉得好过多了，他们知道假如我们有工作的话，还会继续留他们做的，或等到我们又需要他们的时候，他们还是很乐意再回来的。&lt;/p&gt;

&lt;p&gt;克拉克讲述了一段反面的故事，有一次开会，副总裁提出一个尖锐的问题，并将矛头指向生产部总督，生产部总督不愿出丑避而不答，副总裁更恼火，直骂生产总督是个骗子。
再好的工作关系都会因这样的火爆场面而毁坏。&lt;/p&gt;

&lt;p&gt;马桑也谈了她遇到的情况，但有些不同的是，当她的工作出错等待老板大发雷霆时，老板却出乎意料的先感谢了她的勤奋工作，并表示新计划难免会有错，他相信新的工作一定会更好，会对公司有很大帮助。&lt;/p&gt;

&lt;p&gt;纵使别人错了，而我们是对的，如果没有为别人保留面子，就会毁了一个人。&lt;/p&gt;

&lt;p&gt;如果你想说服一个人，先尽力保全他的面子。&lt;/p&gt;

&lt;h4 id=&quot;40激励他人获得成功&quot;&gt;40，激励他人获得成功&lt;/h4&gt;

&lt;p&gt;作者留意训狗师在狗显出轻微的进步时，他轻轻地拍它并给它肉吃，当作一件大事似的。
他又观察到，当我们要改变一个人的时候，为何不用同样的办法，以肉代鞭，用称赞代替指责，即使是最微小的进步，我们也要称赞、激励他人继续进步。&lt;/p&gt;

&lt;p&gt;50年前一位10岁的孩子希望成为一名歌唱家，他的第一位老师给了他一个重大的打击，说他不能唱，完全没有一副好嗓子，但他的母亲拥抱孩子并告诉他，她知道他能唱，她已经看出他的这一进步了。那位农家母亲的称赞和鼓励改变了孩子的一生。他就是意大利歌唱家卡鲁沙。&lt;/p&gt;

&lt;p&gt;多年前伦敦有位青年希望成为一位作家，但很不幸，他的父亲因为债务而被捕。接下来的日子这位青年饱尝饥饿的痛苦，他找了一份很卑微的工作，但每天坚持写作，终于有一天有家报社接受了他的文章，虽然他没有得到任何报酬，但有位编者称赞了他，他非常兴奋，以至在街上活蹦乱跳，泪流满面。由第一篇文章被刊出所得到的称赞和承认改变了他一生。他就是文学家狄更斯《双城记》的作者。&lt;/p&gt;

&lt;p&gt;我们人只利用了自己身心资源的一小部分，还有很多远未被利用起来的，称赞别人，激励他们就可以让他们认识到他们可能拥有的神奇能力。&lt;/p&gt;

&lt;p&gt;称赞他人的每个进步，即使十分微小，也要当作大事一样。我们要“诚于嘉许、宽于称道”，真心诚意的表扬他人，经常性的称赞他人。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-34&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;如何更好的说服他人-4&quot;&gt;如何更好的说服他人&lt;/h4&gt;

&lt;h4 id=&quot;41学会给人戴高帽&quot;&gt;41，学会给人“戴高帽”&lt;/h4&gt;

&lt;p&gt;琴德夫人雇佣了一个女仆，她的前主人说她不好，但在女仆上班时琴德夫人说她看出来你是一个穿着整洁爱干净的人，你一定会与我们相处得很好的，那位女仆为了顾全名声不让夫人失望干得很勤快。
如果你要在某方面改进一个人，就要做得好像那种特点已经是他显著的特性之一。最好假定，并公开地说，对方有你要他发展的美德。给他一个好名誉去实现，他便会尽力去做而不愿看到你失望。&lt;/p&gt;

&lt;p&gt;法国的吕士纳将军想影响在法国的美国士兵，他引用他们将军的话说，美国兵是他接触过的最清洁、最合乎理想的人。
吕士纳知道，即使这不是真实的，也将激励那些士兵努力达到这个标准。&lt;/p&gt;

&lt;p&gt;猩猩监狱的狱长说，如果你必须应付一个盗贼，只有一个可能可以制服他，那就是待他好像他是一个体面的君子，他将因为有人信任他而引以为豪。&lt;/p&gt;

&lt;p&gt;如果你想影响一个人，给他一个美名，他会努力保全这个名声。&lt;/p&gt;

&lt;h4 id=&quot;42鼓励更容易使人改正错误&quot;&gt;42.鼓励更容易使人改正错误&lt;/h4&gt;

&lt;p&gt;作者一位朋友请了老师学跳舞，第一位老师说他跳的全都不对，必须忘掉一切重新开始，于是他很灰心不久就不练了。第二位老师则相反，她不断称赞这位朋友做对的事，并鼓励他说“你天生就有韵律感，你将成为跳舞专家”，这位老师的鼓励使得这位朋友不断前进和进步。&lt;/p&gt;

&lt;p&gt;如果你告诉自己的孩子，丈夫，妻子，或他人，他在某件事上真是愚笨，他对某事没有天赋，或者他做的都错了，那么你就差不多消除了他要作出改进的各种动力。
如果我们用相反的办法，宽容他人，鼓励他人，使事情更容易行动起来，使对方知道你相信他有能力去做，他就会为了取胜而刻苦练习。&lt;/p&gt;

&lt;p&gt;鼓励他人更容易使人改正错误。&lt;/p&gt;

&lt;h4 id=&quot;43学会授权他人&quot;&gt;43.学会授权他人&lt;/h4&gt;

&lt;p&gt;一群孩子常在琴德夫人的草坪上践踏，她为此烦恼。有一天她试着给孩子群中带头的那个人一个授权，授予他一个头衔，她叫他“侦探先生”，让他管理草坪，找出入侵草坪的人。谁知这事就这样解决了，她的“侦探”为了恫吓践踏草坪的孩子甚至拿了烧红的铁棒在她们面前挥舞。&lt;/p&gt;

&lt;p&gt;拿破仑创立荣誉队时，给士兵们颁发了1500枚荣誉勋章，并给18名将军以“法国大将”的头衔，称他的部队为“大军”。这让这些人能遵纪守法，为了这个荣誉捍卫国土和自己的使命。&lt;/p&gt;

&lt;p&gt;学会授权他人，这会使对方乐于做你所建议的事。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-35&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;让你的家庭生活幸福快乐&quot;&gt;让你的家庭生活幸福快乐&lt;/h4&gt;

&lt;h4 id=&quot;44切勿喋喋不休&quot;&gt;44，切勿喋喋不休&lt;/h4&gt;

&lt;p&gt;拿破仑娶了很漂亮的女人，他们拥有健康、财富、势力、名誉、美貌、爱情与信仰，他们有一切拥有幸福的条件，但他们却生活的非常糟糕，因为这位王后无法做一点：停止喋喋不休。
她抱怨、哭泣、喋喋不休，甚至恫吓，并强制进入拿破仑的书房，向他发作、谩骂。在喋喋不休的毒氛中，皇位与美貌都不能保持爱情的存在。&lt;/p&gt;

&lt;p&gt;喋喋不休是最致命的，它像毒蛇的毒汁一样，永远侵蚀着人们的生命。&lt;/p&gt;

&lt;p&gt;托尔斯泰的人生是一个悲剧，而悲剧的原因是他的婚姻。他的妻子喜欢奢侈，但他追求朴素。她的妻子因为托尔斯泰放弃书籍出版权赚不到钱而向他谩骂、责怪、发狂到地上打滚、甚至声称要自杀。结婚48年后托尔斯泰实在不能忍受与她见面，从他妻子那里逃了出去最后死在大街上。
当托尔斯泰的妻子去世以前，她终于对她的女儿们承认：“你们父亲的死，是因为我的缘故”。这也许是托尔斯泰夫人因唠叨抱怨所付出的代价。&lt;/p&gt;

&lt;p&gt;如果你想家庭生活幸福快乐，首先切勿抱怨和喋喋不休。&lt;/p&gt;

&lt;h4 id=&quot;45不要试图改造对方&quot;&gt;45.不要试图改造对方&lt;/h4&gt;

&lt;p&gt;英国伟大的政治家迪斯瑞利在35岁时娶了比他大15岁的寡妇，他所选择的有钱寡妇既不年轻，也不貌美，更不聪明。但她却是一个懂得与男人相处的天才艺术家。&lt;/p&gt;

&lt;p&gt;她没有用她的智力与迪斯瑞利对抗，而是用她的温柔与他对话。当丈夫回到家时，与他轻松闲谈，这使他愉快并让他心神安宁，沐浴在妻子敬爱的温存中。妻子是他的伴侣，也是他的亲信，更是他的顾问。最重要的是，无论迪斯瑞利做什么，他的妻子都相信他。
迪斯瑞利也同样对待他的妻子，妻子在公众场合表现的并不好的时候，他从不责备，反而安慰鼓励她，如果有人讥笑她，他即可猛烈忠诚的维护她。&lt;/p&gt;

&lt;p&gt;正如詹姆士所的，与人交往，第一件应学的事情就是不要干涉他们自己快乐的特殊方法。&lt;/p&gt;

&lt;p&gt;不要试图改造你的配偶。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-36&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;让你的家庭生活幸福快乐-1&quot;&gt;让你的家庭生活幸福快乐&lt;/h4&gt;

&lt;h4 id=&quot;46不要批评对方&quot;&gt;46，不要批评对方&lt;/h4&gt;

&lt;p&gt;格莱斯是英国首相，他在公众面前是一个可畏的形象，而在家中从未批评过家人。当他早晨下来用餐时看见家人还在睡觉，他会用一种温柔的方式唤醒他们。&lt;/p&gt;

&lt;p&gt;凯瑟琳也是一样，他曾统治世界上最大的帝国，但如果厨师将肉烤焦，她什么也不会说，而是微笑的将肉吃下去。&lt;/p&gt;

&lt;p&gt;狄克斯研究婚姻不幸的专家，他发现击碎婚姻的大多数都是批评，无用的，令人心碎的批评。&lt;/p&gt;

&lt;p&gt;如果你想拥有家庭生活的快乐，不要批评你的丈夫或妻子。&lt;/p&gt;

&lt;h4 id=&quot;47真诚的欣赏对方&quot;&gt;47.真诚的欣赏对方&lt;/h4&gt;

&lt;p&gt;可能你不知道欣赏对方有多重要，男子在寻求伴侣时从不像在找高级职员那样寻找能力强过自己的人，而是在寻找一个对自己具有诱惑力的，愿意奉承自己的使自己感到舒服的人。&lt;/p&gt;

&lt;p&gt;如果一位女办公室主任应邀去吃饭，她总是提及自己的学识自己的成就，甚至坚持自付餐费，那最后的结果只能是再也没人邀请她吃饭了。
相反，如果一位未进过大学的打字员应邀吃饭，她能温柔的注视着男伴，仰慕的说‘再给我讲讲有关你的事’，最后的结果可能是，男伴会告诉别人，她是我遇到过的最会说话的人。&lt;/p&gt;

&lt;p&gt;男性也应如此对待女性，特别是对女性的服饰和装饰，应当给予真诚的赞美。&lt;/p&gt;

&lt;p&gt;作者祖母95岁去世前不久给她看30多年前的照片，她唯一问的一句话是，那时我穿着什么衣服。对于男人来讲，他们也许想不起5年前穿什么衣服，但女人不同。法国上等社会的男子都要训练对女人的衣帽表示赞美，而且每晚都要训练很多次。&lt;/p&gt;

&lt;p&gt;迪斯瑞利英国伟大的政治家就不羞于让世界知道他对他的“小妇人沾光多少”。他说，我沾光于我夫人多于世上其他任何人。她帮助我勇敢前进，她为我很好的管理钱财，并为我生了5个孩子，为我建造了一个美丽的家庭，我所拥有的一切都是她的功劳。&lt;/p&gt;

&lt;p&gt;巴克斯得为丈夫放弃了舞台，专心在家带孩子，虽然她失去了舞台的快乐，但他从丈夫那里得到了更多的快乐。她丈夫说，妻子为自己和家庭放弃了很多，我应该尽量的称赞她鼓励她，这样她就能从失去的快乐中挽回一些快乐。
丈夫的赞美和欣赏使得妻子重拾了生活的信心。&lt;/p&gt;

&lt;p&gt;如果你想保持家庭生活快乐，请给予对方真诚的赞美和欣赏。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-37&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;让你的家庭生活幸福快乐-2&quot;&gt;让你的家庭生活幸福快乐&lt;/h4&gt;

&lt;h4 id=&quot;48注重生活中的小事&quot;&gt;48，注重生活中的小事&lt;/h4&gt;

&lt;p&gt;琐碎的事情是多数婚姻不幸的根源。其实只要做一些简单的事就能顺利的避免走向不幸。比如每天早上向妻子或丈夫说早安，出门上班时向对方说再会，就能避免许多离婚。&lt;/p&gt;

&lt;p&gt;人是很奇怪的动物，我们都非常注重生活中的小事，一朵花，一个小礼物，一句甜言蜜语，这比给妻子或丈夫买个车子或房子还重要。如果许多纪念日你记不住，切不可忘记，对方的生活和结婚纪念日。&lt;/p&gt;

&lt;p&gt;勃朗宁与夫人的生活就很可歌可叹，他从未忙得忘记对夫人用小小的恭维及注意来保护爱情的活力。终究婚姻是一串琐事，忽略这个事实，将造成家庭生活的灾难。&lt;/p&gt;

&lt;p&gt;注重那些看似小的事情，那才是人类感受的关键。&lt;/p&gt;

&lt;h4 id=&quot;49家庭生活也应该有礼&quot;&gt;49，家庭生活也应该有礼&lt;/h4&gt;

&lt;p&gt;丹姆罗希是位有名演说家，他与夫人的生活很快乐，问她夫人的秘诀是什么时，她说，“她认为结婚后的礼貌是最重要的。我们对丈夫或妻子应该像对刚见面的人一样有礼。”&lt;/p&gt;

&lt;p&gt;无礼是侵浊爱情的活水。大部分时候我们对陌生人和朋友比对自家人要更加客气有礼。我们不会阻止陌生人讲自己听过的故事，也不会未经朋友的允许而拆朋友的信，在请求向陌生人和朋友帮助时都会先彬彬有礼的请求一番。但我们对家人则是另一番态度，总是因为他们的小错而侮辱他们，对我们说出刻薄、侮辱、伤感情的话的人，都是我们自家的人。&lt;/p&gt;

&lt;p&gt;我们应该尽早的发现这个问题，并改进它。
我们应该像经营自己事业或职业那样去经营家庭，使家庭成为一个发达的机构，动力十足并团结一致。
对待妻子和丈夫时，更多的使用礼仪外交手段来对待他们，更多用些温柔的手段，而不是高压或强硬手段。&lt;/p&gt;

&lt;p&gt;其实很多男人都知道应该先让妻子快乐然后就可以使她快乐的做事并不需要任何报酬。他知道如果给她几句简单真诚的恭维和赞美，说她管家是如何的好，她就会很乐意去帮助男人们做事，她也会为他节省每一分钱，因为男人们赞美了她对她彬彬有礼。&lt;/p&gt;

&lt;p&gt;相反有部分男人却完全不知道如何对待妻子，他们情愿与她争吵，情愿浪费他的钱为她买新衣服、新车子、新珠宝，也不愿意为一点小事去赞美去恭维，最后浪费了钱还是没能得到家庭的快乐。&lt;/p&gt;

&lt;p&gt;给予你的妻子或丈夫更多礼貌&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-38&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;让你的家庭生活幸福快乐-3&quot;&gt;让你的家庭生活幸福快乐&lt;/h4&gt;

&lt;h3 id=&quot;50如何与女性相处&quot;&gt;50，如何与女性相处&lt;/h3&gt;

&lt;p&gt;一般人看来单身汉似乎都是横冲直撞、无所顾虑的，而结了婚的男子则十分谨慎、庸俗。其实单身汉比结了婚的男子更严肃、认真，在金钱上斤斤计较，也更懂得为自己打算，因此他们通常不会冒险去结婚登记处，相信许多未婚女子也是如此。&lt;/p&gt;

&lt;p&gt;我们应该向结了婚的男人致敬，他们既然有勇气结婚，相信也一定会愿意用各种技巧来增进自己的婚姻幸福。&lt;/p&gt;

&lt;p&gt;康奈尔大学文理学学院院长解释说，婚姻是否美满，要看双方的心理是否成熟。也就是说，他们是否了解自己、了解与对方的关系，并且愿意彼此分担责任，以增进对方的快乐。维持家庭关系是凭借内在价值的满足，例如感情、友谊、价值观等，而且不能用强求的方式取得。&lt;/p&gt;

&lt;p&gt;以下是与妻子相处的7个建议&lt;/p&gt;

&lt;h4 id=&quot;1感谢她称赞她&quot;&gt;1.感谢她、称赞她&lt;/h4&gt;

&lt;p&gt;千万别短缺了太太的配粮，这样她就会心甘情愿为你卖命。在你失去工作、头发或腰围的时候，她会很乐意与你同甘共苦，甚至不会怨天尤人地天天穿着那件仅有的外套，只要你能时时不忘赞美她。&lt;/p&gt;

&lt;p&gt;通常男士比较容易知道自己的定位，因为他们的工作表现很快就能从上级反馈，或者他们做成了一笔生意，很快就能晋升、加薪或得到表扬。
但女士们则不同，她们待在家里穷忙，却一点也不知道自己的成绩如何，除非她生命中的另一半告诉她、肯定她。因此男士们的感谢和赞美是她唯一的奖励。
要想赢得女人的心，并让她愿意永远不辞辛劳地取悦自己，最好、最有用、最不会失败的方法，便是时时全心全意地感谢她、赞美她。&lt;/p&gt;

&lt;p&gt;一位纽约专栏作家罗伯特娶了一位美丽聪慧的妻子珍妮，但珍妮却认为罗伯特是世界上最好的丈夫。因为罗伯特总是不断得赞美妻子，每当他有什么新的书出版时，总不会忘记在首页上写上“献给珍妮”，这些题字比起支票上的数字有用的多。&lt;/p&gt;

&lt;h4 id=&quot;2要慷慨关心她&quot;&gt;2.要慷慨、关心她&lt;/h4&gt;

&lt;p&gt;许多男士认为慷慨就是指大方的付清所有账单，其实女士真正需要的慷慨并不是这些，她们需要的是男士的关注与关心，例如可以对她说，要不请你妈妈来我们家住一段时间，或者在宴会上时时表达你对她的关心，这才是真正的慷慨。&lt;/p&gt;

&lt;p&gt;作者记得有一次参加宴会，男主人是个相当出名的杰出人物，对每个人都极为殷勤有礼，唯独对自己太太例外。他的太太在陌生人群当中显得很不自在，而她的丈夫则如鱼得水，在人群当中显得十分得意。
分一点关注给自己的太太并不会影响他的公共关系，反而有助他的形象，更可增进夫妻之间的关系。后来听说他们的婚姻果然恶化。&lt;/p&gt;

&lt;h4 id=&quot;3不要过于不修边幅&quot;&gt;3.不要过于不修边幅&lt;/h4&gt;

&lt;p&gt;一般人认为注重打扮或保持吸引力是女人家的事，但男士也同样需要注意自己的形象，特别是在妻子面前。男士们上班的时候是西装笔挺，但在家里的德性却像尚未整理过的床铺一样，不忍直视。
这种邋遢的男子大概从没想过，太太们也是希望自己的另一半清洁整齐，她更喜欢你能洗脸刮胡子，至少在她面前晃来晃去的时候，能对得起她的眼睛。
一名真正的男子当然不靠外貌，但外表却是他人见到你时对你的第一印象。我们应该在家里也同样时时保持整齐，至少干净整洁。&lt;/p&gt;

&lt;h4 id=&quot;人性的弱点-39&quot;&gt;《人性的弱点》&lt;/h4&gt;

&lt;h4 id=&quot;让你的家庭生活幸福快乐-4&quot;&gt;让你的家庭生活幸福快乐&lt;/h4&gt;

&lt;h3 id=&quot;51如何与男性相处&quot;&gt;51，如何与男性相处&lt;/h3&gt;

&lt;p&gt;二次世界大战后有人对军人做了一个调查，“你希望从婚姻中获得什么”，这些穿制服、肌肉发达的小伙子们不假思索的列出了答案，“舒适”。
这与许多化妆品或香水广告中的暗示大不相同。男人心中的“舒适”到底指的是什么，是母亲般的温柔，还是玛丽莲梦露般的性感。&lt;/p&gt;

&lt;p&gt;作者归结出几个重点。&lt;/p&gt;

&lt;h4 id=&quot;1要脾气好善解人意&quot;&gt;1.要脾气好、善解人意&lt;/h4&gt;

&lt;p&gt;男人通常最注意对方是否具有好脾气，他们宁愿在欢愉的气氛中吃泡面，也不愿意和一个唠叨、烦躁、牢骚满腹的女性一起吃牛排。
一位单身汉坦言，如果一位活泼、好脾气、明朗却不忠实的女人和一位贞洁的悍妇，他毫不迟疑的选择前者。&lt;/p&gt;

&lt;p&gt;几年前，作者雇佣了一位女打字员，但她的能力太差，常常拼错字，速度慢，记录又不准确，但是她具有一种欢愉的气质，能忍受各种怒气、抱怨和批评，她如同阳光般照亮整个房间，她的丈夫每次望着她时脸上都发出霓虹灯般的光彩。&lt;/p&gt;

&lt;h4 id=&quot;2当个好伴侣&quot;&gt;2.当个好伴侣&lt;/h4&gt;

&lt;p&gt;什么是好伴侣。作者解释到，琳和杰克就是好伴侣，琳不曾实际参加杰克的各种活动，但她总是在一旁为他加油，并且帮助他做些他无法分心去做的事。
结婚16年的梅娜太太发现自己的婚姻缺少了点什么，她终于发现缺少了与丈夫一份如朋友般的情谊，于是她试着去了解丈夫的兴趣。现在她对曲棍球赛很敢兴趣，与丈夫常一起看曲棍球，除了曲棍球，他继续了解丈夫的其他兴趣，并与丈夫一起分享乐趣。&lt;/p&gt;

&lt;h4 id=&quot;3当名好听众&quot;&gt;3.当名好听众&lt;/h4&gt;

&lt;p&gt;许多女性在这方面做得不好，是因为不了解听的艺术。很多女性唠叨不休，男人根本找不到机会可让自己也发表一些长篇大论。&lt;/p&gt;

&lt;p&gt;聆听的品质十分重要，可以鼓舞讲话的人表达出完整的意思。聆听并不是指必须保持沉默，你大可以从旁加进几句鼓舞的话。
想要当好听众，首先要注意听讲，眼睛不要四处张望，或显出烦躁不安的样子。&lt;/p&gt;

&lt;p&gt;一个好的听众除了要专心之外，还要懂得合作。
聪明的男人分得出什么人真的在听他讲话，或什么样的女性只是敷衍了事讨他欢心。
在他讲话的时候偶尔插几句文化，或提供些不同的看法，可以刺激或鼓励他继续讲下去，最好在他讲得告一段落时再插进去，并简明扼要以便尽快把发言权还给他。&lt;/p&gt;

&lt;p&gt;只要勤加练习，我们完全可以熟悉听讲的规则。
懂得技巧的观众通常也会变成优秀的谈话者，因为听与讲不可分，一方的技巧通常会加强另一方面的表现。
它还可以帮助我们迈向成熟，因为这正是我们能不断学习的最佳途径。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(四十七) 当前经济环境下的投资方向</title>
   <link href="http://www.luzexi.com/2020/05/18/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A847"/>
   <updated>2020-05-18T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/05/18/思路探讨47</id>
   <content type="html">&lt;p&gt;这个时间点让我们来冷静的分析下当前经济的大环境与投资的方向。在新型冠状病毒导致的全球经济衰退，各国央行纷纷大放水的背景下，货币贬值是不可避免的。现在人们的生活受到了限制，情绪受到了打击，收入也同时缩减了很多，即使各国开仓放粮印钞票也并不会使得人们的口袋鼓起来，钱并不会进入大众人民的口袋中，为了让企业扛过这个危难的时刻国家大量印货币并把它们借给企业以抵御债务造成的危机，当然这种方式只会增加债务，延缓破产的时间，虽然让融不到资的实业经济得以喘息，但经济并不会因此而恢复。&lt;/p&gt;

&lt;h4 id=&quot;说到国家经济我们不防来聊下经济好与坏是怎么来的经济好坏的根源还是人们的花钱和赚钱的速度如果人们愿意花更多的钱比如去旅游去购物去吃喝玩乐也可以去拿投资实物投资金融投资等同时也能很容易赚钱以及相对容易的赚到更多的钱工资实业债券投资股市投资实物投资这个时候钱在这个国家流动的速度就很快赚钱容易花钱也多这使得人们心情愉悦对很多事都不会斤斤计较人们的活动范围交流途径交易方式都会比较多而广这是一个国家或一个地区经济好的表现反之如果人们不愿意花钱赚的钱也更少了钱在这个国家的流动速度就会变慢人们心情糟糕对很多小事都斤斤计较这导致人们的活动范围缩小交流方式开始变得粗暴交易方式变少接着也进一步导致人们更不愿意花钱企业也就更难赚到钱了或许会让工资收入也更难提高甚至减少于是这个地区的经济就会表现的很差&quot;&gt;说到国家经济，我们不防来聊下，经济好与坏是怎么来的。经济好坏的根源还是人们的花钱和赚钱的速度，如果人们愿意花更多的钱(比如去旅游去购物去吃喝玩乐，也可以去拿投资，实物投资、金融投资等)，同时也能很容易赚钱以及相对容易的赚到更多的钱(工资、实业、债券投资、股市投资、实物投资)，这个时候钱在这个国家流动的速度就很快，赚钱容易花钱也多，这使得人们心情愉悦，对很多事都不会斤斤计较，人们的活动范围、交流途径、交易方式都会比较多而广，这是一个国家或一个地区经济好的表现。反之，如果人们不愿意花钱，赚的钱也更少了，钱在这个国家的流动速度就会变慢，人们心情糟糕，对很多小事都斤斤计较，这导致人们的活动范围缩小，交流方式开始变得粗暴，交易方式变少，接着也进一步导致人们更不愿意花钱，企业也就更难赚到钱了，或许会让工资收入也更难提高甚至减少，于是这个地区的经济就会表现的很差。&lt;/h4&gt;

&lt;h4 id=&quot;央行为了救经济会大量印钞这使得货币贬值很多人奇怪为什么央行印的钱不给人民去花让经济得以恢复事情可能并没有这么简单经济是要遵循市场规律的货币是国家的信用体系的支撑如果让大量货币进入市场就会造成货币信用体系被稀释轻的会导致货币贬值重的就不感想了货币放水会让物价膨胀而工资收入却难以改变因此央行是不会干这蠢事的印的钱也不会放入人们的口袋的如果要满足人们的口袋鼓起来要印太多钞票货币贬值量会非常大物价膨胀会很厉害央行不能它只能有计划的印钱并要把这些钱用到刀刃上哪里是刀刃呢刀刃就是向企业提供更多的融资和贷款支撑它们不陷入债务危机而破产同时也启动许多大型项目并有计划的花费这些钱财来让企业有活干有钱赚从而让一部分企业和工人有更多的工作和目标去执行也有些稳定的收入进而有更多的花销来盘活经济&quot;&gt;央行为了救经济会大量印钞，这使得货币贬值。很多人奇怪为什么央行印的钱不给人民去花让经济得以恢复。事情可能并没有这么简单，经济是要遵循市场规律的，货币是国家的信用体系的支撑，如果让大量货币进入市场就会造成货币信用体系被稀释，轻的会导致货币贬值，重的就不感想了，货币放水会让物价膨胀，而工资收入却难以改变。因此央行是不会干这蠢事的，印的钱也不会放入人们的口袋的，如果要满足人们的口袋鼓起来要印太多钞票，货币贬值量会非常大，物价膨胀会很厉害，央行不能，它只能有计划的印钱，并要把这些钱用到刀刃上。哪里是刀刃呢？刀刃就是向企业提供更多的融资和贷款，支撑它们不陷入债务危机而破产，同时也启动许多大型项目并有计划的花费这些钱财来让企业有活干有钱赚，从而让一部分企业和工人有更多的工作和目标去执行，也有些稳定的收入进而有更多的花销，来盘活经济。&lt;/h4&gt;

&lt;p&gt;不过央行的救援能力终究是有限的，通常人们的口袋不会比以前有更多的钱，人们的也越来越惧怕风险从而存钱的欲望也在逐渐增强，这进一步导致货币的流动速度减弱，人们为了节省开支更少的花钱。用专业术语来说，人们的风险偏好减弱，更倾向于稳定而对未来的展望会悲观一些至少对未来更加谨慎。&lt;/p&gt;

&lt;p&gt;那么真的如我们所想象的那样，我们所有人的口袋里的钱会越来越少吗？可能不是，富裕和平穷是相对的，非洲一个村子里的首富也就只有10几万美元的财富，他就能命令其他更穷的人去替他工作，这点钱在富裕地区更本算不了什么。所以确切的来说，虽然大家的资产都在减值，但我们穷人的资产减值会更快，富人的则更慢，因此贫富差距也逐渐拉开。&lt;/p&gt;

&lt;p&gt;这要从经济环境旺盛的情况下说起了，在旺盛的经济环境下，大家都容易赚钱，富人和穷人拥有同等抬升财富的速度，甚至穷人由于更加激进些使得抬升的速度会更快一些，但当经济状况差的情况来临时，富人财富的缩水速度则更慢一些，因为它们拥有更多的资产和更多元化的投资，除了资产和投资，他们依然能够保持稳定的现金流来支撑生活、企业、工作、投资，当然也不是所有富人都能做到这么平稳，而穷人则不是，穷人的资产少、现金流脆弱，抗风险能力差，对于那些在鼎盛时太过激进的穷人来说财富的缩水速度可能会更快，也会有因为债务危机导致一夜破产明天就一无所有的情况发生。不过太过激进的投资方式本身就有问题，这对于无论富人还是穷人来说都是同样的结果，只是富人失去了一部分还有另一部分，而穷人则可能是全部。&lt;/p&gt;

&lt;h4 id=&quot;我们把话题拉回到现实现在央行开始货币放水大量印钞货币贬值导致商品涨价各类商品价格可能会膨胀重量级实体商品房地产是充当起冲的黄金由于得到全球国家的共识拥有抗通胀能力它的涨价也是大概率事件现在可能是买房的好时刻因为房贷利率本来就低贷款后的钱又会因为央行放水而稀释因此如果你有稳定的现金流现在买房会很划算有央行印钞帮你稀释债务还因商品涨价帮你抬升房子价格很多人也没钱付首付还贷款包括我那么买黄金也是大概率升值的方向虽然赚的少了点它不像房子有个比较高的门槛它的门槛非常低谁都可以买多少也没有限制但比起房子黄金它没有租金的利率黄金是零利率产品也没有央行帮你稀释贷款它只有抗通胀这一个功能&quot;&gt;我们把话题拉回到现实，现在央行开始货币放水大量印钞，货币贬值导致商品涨价，各类商品价格可能会膨胀，重量级实体商品房地产是充当起冲的，黄金由于得到全球国家的共识，拥有抗通胀能力，它的涨价也是大概率事件。现在可能是买房的好时刻，因为房贷利率本来就低，贷款后的钱又会因为央行放水而稀释，因此如果你有稳定的现金流现在买房会很划算，有央行印钞帮你稀释债务，还因商品涨价帮你抬升房子价格。很多人也没钱付首付还贷款包括我，那么买黄金也是大概率升值的方向虽然赚的少了点，它不像房子有个比较高的门槛，它的门槛非常低，谁都可以，买多少也没有限制，但比起房子，黄金它没有租金的利率(黄金是零利率产品)、也没有央行帮你稀释贷款，它只有抗通胀这一个功能。&lt;/h4&gt;

&lt;p&gt;股市和经济是密切相关的，因为股市就是企业的盈利状况，在经济下行，人们收入减少，风险偏好减弱的情况下，大部分企业都会直接或者间接陷入盈利减少或者滞涨的情况。只有极个别的公司会幸运的得到命运的垂青而收入大增，大部分企业都会陷入半危机状态，自保、转型将成为他们首要的任务。&lt;/p&gt;

&lt;h4 id=&quot;我们要量力而为适合自己的才是最好的投资也一样我们说激进的投资虽然能带来丰厚的回报但也同样会遭遇到毁灭性的打击一夜暴富和一夜变穷光蛋的概率是均等的每当你拥有一夜暴富的机会时一定要记住一夜破产的机会也在一旁窥视着&quot;&gt;我们要量力而为，适合自己的才是最好的，投资也一样。我们说，激进的投资虽然能带来丰厚的回报，但也同样会遭遇到毁灭性的打击，一夜暴富和一夜变穷光蛋的概率是均等的，每当你拥有一夜暴富的机会时，一定要记住一夜破产的机会也在一旁窥视着。&lt;/h4&gt;

&lt;h4 id=&quot;最终的结果可能是我们不愿意看到的国家为了救经济侧面导致了贫富差距拉大富人更富因为他们本身的资产比较多现金流比较稳定投资也更多元化而穷人则更穷因为我们资产少现金流也少而脆弱投资方向单一但现实中我们看到的真的会是这样吗可能会不一样我们现实感受到的可能有很大不同因为我们能感受到的大都是身边发生的情况这些都只是某个角度上的比较片面的情况对于整体的趋势来说也会感觉是完全相反的但如果把时间线拉长来看我们就能充分感受到这种趋势带来的结果&quot;&gt;最终的结果可能是我们不愿意看到的，国家为了救经济，侧面导致了贫富差距拉大，富人更富，因为他们本身的资产比较多，现金流比较稳定，投资也更多元化，而穷人则更穷，因为我们资产少，现金流也少而脆弱，投资方向单一。但现实中我们看到的真的会是这样吗？可能会不一样。我们现实感受到的可能有很大不同，因为我们能感受到的，大都是身边发生的情况，这些都只是某个角度上的比较片面的情况，对于整体的趋势来说也会感觉是完全相反的，但如果把时间线拉长来看，我们就能充分感受到这种趋势带来的结果。&lt;/h4&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(四十五) 思考方式的转变</title>
   <link href="http://www.luzexi.com/2020/05/11/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A845"/>
   <updated>2020-05-11T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/05/11/思路探讨45</id>
   <content type="html">&lt;h6 id=&quot;事情太多没有时间写文章其实都是借口看漫画看抖音怎么就有时间了呢这方面我一直在于自己斗争另外一方面其实对我来说有一个很大的障碍就是羞于把自己的想法说出来怕自己的想法不成熟会被人嘲笑其实根本就不必担心有人嘲笑因为没人在意大家只是借鉴我的想法对也好错也好都会成为一个大家心中的样本这个样本会与大家自己心理的想法做比对有认为我错了的读者们会把我当成反面教材认同我的看完后则点点头巩固一下自己的想法是我担心的太多了从根本上讲还是太在意自己的感受没有尽力去表现我一直为自己强调一点忘记自己的感觉尽力去做去表现&quot;&gt;事情太多、没有时间写文章其实都是借口，看漫画看抖音怎么就有时间了呢，这方面我一直在于自己斗争。另外一方面，其实对我来说有一个很大的障碍，就是羞于把自己的想法说出来，怕自己的想法不成熟会被人嘲笑，其实根本就不必担心有人嘲笑，因为没人在意，大家只是借鉴，我的想法对也好错也好，都会成为一个大家心中的样本，这个样本会与大家自己心理的想法做比对，有认为我错了的读者们会把我当成反面教材，认同我的看完后则点点头巩固一下自己的想法。是我担心的太多了，从根本上讲还是太在意自己的感受，没有尽力去表现。我一直为自己强调一点，“忘记自己的感觉，尽力去做去表现”。&lt;/h6&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;最近越来越觉得生活中的万事皆有习惯的影子，思考方式更是一个比较重要的习惯。一直觉得人的思维是有层级的，这个思维高度不是我告诉你XXX，然后你听懂了你就会了，以后你就到了那个层级，或者你某天领悟就会了，不是这样的。它是一种习惯，你习惯用怎么的方式去思考问题，就会有怎样的思维层级，假如你每天逼着自己用更高层级的思考方式去解决问题，去思考问题，那么日积月累后这种思维习惯就成了你的习惯，每每遇到问题遇到事情，就不自觉得去用习惯的方式思考，去解决。&lt;/p&gt;

&lt;h6 id=&quot;习惯这东西其实和命运有很大的关系命运是怎么来的呢其实就是我们从小养成的习惯决定而来的比如我们小时候在小学时学数学老喜欢跳步骤看书时喜欢跳着看有时为了着急做完作业字迹潦草草草写下答案一开始这些都只是孤立的行为但是时间一长就形成了习惯到了中学开始潦草粗心成了我们最大的借口慢慢性格也随着习惯慢慢在转变变的轻佻求快而不求质量考试考不好觉得只是自己失误粗心甚至有人开始自卑觉得不如别人其实这些都是一点一滴平时的习惯养成的到了成年大部分思维习惯都已成型因此我们在成年后想做出改变变得是极其困难和痛苦&quot;&gt;习惯这东西其实和命运有很大的关系。命运是怎么来的呢，其实就是我们从小养成的习惯决定而来的。比如我们小时候在小学时学数学，老喜欢跳步骤，看书时喜欢跳着看，有时为了着急做完作业，字迹潦草，草草写下答案。一开始这些都只是孤立的行为，但是时间一长就形成了习惯。到了中学开始，潦草，粗心成了我们最大的借口，慢慢性格也随着习惯慢慢在转变，变的轻佻求快而不求质量，考试考不好觉得只是自己失误，粗心，甚至有人开始自卑觉得不如别人，其实这些都是一点一滴平时的习惯养成的。到了成年，大部分思维习惯都已成型，因此我们在成年后想做出改变变得是极其困难和痛苦。&lt;/h6&gt;

&lt;h6 id=&quot;有人说为什么命运那么难改变其实只是因为改变习惯比较难而已特别是我们的思维习惯如果我们的思维习惯能从小就养成的比较良好的话我们的人生就会大不一样但是无论怎么样我们都会进入一个笼子只是这个笼子有大有小有些人进了小笼子有些人进了大笼子但都是笼子我说这个笼子就是思维层级的笼子我们要付出巨大的努力才能摆脱的思维惯性的笼子&quot;&gt;有人说为什么命运那么难改变，其实只是因为改变习惯比较难而已，特别是我们的思维习惯，如果我们的思维习惯能从小就养成的比较良好的话，我们的人生就会大不一样。但是无论怎么样，我们都会进入一个笼子，只是这个笼子有大有小，有些人进了小笼子，有些人进了大笼子，但都是笼子。我说这个笼子就是思维层级的笼子，我们要付出巨大的努力才能摆脱的思维惯性的笼子。&lt;/h6&gt;

&lt;p&gt;无论是哪个职业，哪个行业，都有自己的思维观想。我是做技术的写程序的，我们有自己的技术层级，每个层级都在技术力量上上升一个台阶，其实不只如此，技术力量上升的同时也伴随着思维层级的上升，两者是相辅相成的。这也是为什么很多人无法突破自己的技术瓶颈的原因，因为它的思维惯性一直没有被改变，习惯性思考方式一直没有改变，技术力量自然无法抬升。&lt;/p&gt;

&lt;p&gt;我们经常讲的笑话说，我们的进阶书单是如下这种形式的：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;X语言入门 —&amp;gt; X语言应用实践 —&amp;gt; X语言高阶编程 —&amp;gt; X语言的科学与艺术 —&amp;gt; 编程之美 —&amp;gt; 编程之道 —&amp;gt; 编程之禅—&amp;gt; 颈椎病康复指南
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;有没想过从书单的进阶过程来看，也是思维方式的转变，而不只是技术力量上的提高，特别是最后那个《颈椎病康复指南》，看上去是个搞笑的梗，其实蕴含着更高层级的思维，即长远的可持续发展比所谓的技术能力提升更为重要。&lt;/p&gt;

&lt;p&gt;说到在生活中也有很多例子，精英阶层与底层人民的思考方式就完全不同。越是富人、越是精英阶层，反倒是越勤奋，他们反倒是在追求更好的自己，也越喜欢采用补充型方式休闲娱乐，比如交流、阅读、学习。而越是穷人、越是底层的人，越喜欢即时满足的方式，比如打牌、打游戏、看肥皂剧。&lt;/p&gt;

&lt;p&gt;精英们认为苦就是甜，先苦后甜，精神上主动去折磨自己才是对自己未来最好的规划，而穷人则认为自己太苦了，于是见缝插针的想偷懒想休息，即时满足自己的。&lt;/p&gt;

&lt;h6 id=&quot;我们应该想想我们的思维习惯是什么怎么去转变记得每天都用新的更好的思维习惯去解决问题去做事去思考这样日积月累真的就会改变你的命运&quot;&gt;我们应该想想我们的思维习惯是什么，怎么去转变。记得每天都用新的更好的思维习惯去解决问题去做事去思考，这样日积月累真的就会改变你的命运。&lt;/h6&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(六) 《游戏开发者访谈录》</title>
   <link href="http://www.luzexi.com/2020/05/10/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B06"/>
   <updated>2020-05-10T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/05/10/读书笔记6</id>
   <content type="html">&lt;h4 id=&quot;游戏开发者访谈录集合100个日本游戏从业者思想的书&quot;&gt;《游戏开发者访谈录》集合100个日本游戏从业者思想的书。&lt;/h4&gt;

&lt;h4 id=&quot;第30篇高森大辅他是世嘉的技术美术&quot;&gt;第30篇，高森大辅，他是世嘉的技术美术。&lt;/h4&gt;

&lt;p&gt;他说自己原本是做设计的，因为喜欢程序才学了程序，但学了一点觉得困惑就放弃了。但有次机遇让他重新捡起了部分。因为要处理某次业务，必须要他来写Maya里的脚本作为工具，但他完全没写过，学习了很多后做出来了，从此踏上了技术美术之路。&lt;/p&gt;

&lt;p&gt;他对美术和程序之间的区别有自己的体会。他说，设计师的动机根源是在追求一种感觉上的“不错”，而程序员的动机根源则在于要做出“我自己想做的、最有效率且适当的处理逻辑”。&lt;/p&gt;

&lt;p&gt;多数情况下，设计师完成的指标很暧昧，“感觉不错”。而程序员的写的程序规格和指标如果不明确则无法处理。&lt;/p&gt;

&lt;p&gt;他认为只有认清这些才能越过技术美术那堵墙。&lt;/p&gt;

&lt;h4 id=&quot;第32篇大阪裕子企业家&quot;&gt;第32篇，大阪裕子，企业家。&lt;/h4&gt;

&lt;p&gt;她说自己跟一个有名的游戏制作人聊过游戏制作，有一次印象很深刻。以下都是对方说的；&lt;/p&gt;

&lt;p&gt;多亏了游戏开发技术的发展和进化，使得游戏开发者的工作量减轻，可以把游戏开发成一种能够像绘画、小说、电影一样，加入到艺术行列之中。&lt;/p&gt;

&lt;p&gt;要判断一款游戏是不是艺术作品，对她来说，就是游戏作品与玩游戏的每个人之间的“联系”。不光停留在追求画面漂亮，也要在玩起来能够体现高兴、害怕和悲伤等感情色彩层面上。一个非常重要的问题在于，游戏是不是一个改变了玩家生活方式的媒介。&lt;/p&gt;

&lt;p&gt;那么怎样才能开发出能改变玩家生活方式的游戏呢？她说，开发者要正视自己的内心，深入观察了解自我，跟自身产生共鸣，正视自己的想法、需求、畏惧。如果能坦然面对这一切，就能通过作品把它们分享出去。就像梵高早期作品用油画表现了贫穷生活的痛苦和恐惧，并将这些感情分享给许多人那样。&lt;/p&gt;

&lt;p&gt;《游戏开发者访谈录》集合100位日本游戏从业者的思想&lt;/p&gt;

&lt;h4 id=&quot;第34篇渡边雅央兼职从业者&quot;&gt;第34篇，渡边雅央，兼职从业者&lt;/h4&gt;

&lt;p&gt;他说了自己对工作的理解，“正确的答案不是唯一的”，这或许是一个非常狡猾的回答，但他从打心底里这么认为。&lt;/p&gt;

&lt;p&gt;世界上有很多事情都存在好几个正确答案。如果非得只有一个正确答案，那么世界就会变得憋屈和无聊。对立的意见，不要互相争斗，而是彼此指点学习，感受彼此。我们正因为不知道未来，才需要倾听各种意见来维持平衡。&lt;/p&gt;

&lt;h4 id=&quot;第35篇永由小白合游戏设计师兼pm&quot;&gt;第35篇，永由小白合，游戏设计师兼PM&lt;/h4&gt;

&lt;p&gt;她认为身边的事物都能拿来做游戏，通过从身边各种各样的东西来提取构成游戏要素，并将这些要素加以组合，就能够创造出各种各样的体验。&lt;/p&gt;

&lt;p&gt;她认为一个能在人心中引发强烈的情感的游戏，不是一朝一夕就能做出来的。为此我们需要深入了解玩游戏的人。从人心中产生的感情绝不只是“开心”“有干劲”等正面情感，我们也必须正视像卑鄙、丑陋、妒忌、利己和暴力等人类的负面情感。&lt;/p&gt;

&lt;h4 id=&quot;第37篇陈启蒙香港人在日本做游戏设计师&quot;&gt;第37篇，陈启蒙，香港人在日本做游戏设计师&lt;/h4&gt;

&lt;p&gt;他说并非是个游戏设计师就能想出远超常人的有意思的点子。而是，从众多点子中提取出最合适用于游戏的点子，然后用这些点子来合理设计游戏系统的人。&lt;/p&gt;

&lt;p&gt;游戏设计师不但要合理组合多种刺激，还要理解这种刺激的原理。这样一来，就意味着游戏设计师要把这种感觉、精神、社会领域全研究透彻，这可能几辈子都不够。&lt;/p&gt;

&lt;p&gt;所以对设计师来说，太深入研究时间不够，光浅尝则止又理解得不到位。因此要找到一个好的平衡点，他认为这个平衡点就是“一直学到知道自己到哪里就不会了为止”&lt;/p&gt;

&lt;p&gt;为了进一步理解，要怎么查，去查哪里，就算不是很清楚，也能掌握正确的思路。这是通往专家之路的分界线。&lt;/p&gt;

&lt;p&gt;《游戏开发者访谈录》集合100位日本游戏从业者的思想&lt;/p&gt;

&lt;h4 id=&quot;第38篇尾形美幸自由作家兼职编辑她说自己接触过的优秀的人都无一例外的会以超高的当事人意识去对待工作他们对待工作负责并且注入自己独特的价值即使工作交接后也会根据需要后续跟进&quot;&gt;第38篇，尾形美幸，自由作家兼职编辑，她说自己接触过的优秀的人都无一例外的会以超高的当事人意识去对待工作。他们对待工作负责并且注入自己独特的价值，即使工作交接后也会根据需要后续跟进。&lt;/h4&gt;

&lt;p&gt;她在经历过各种失败后认为，睡眠和运动也是工作的一部分，要认真对待。&lt;/p&gt;

&lt;h4 id=&quot;第40篇南云玲生ceo董事长他讲述了自己的经历和感悟&quot;&gt;第40篇，南云玲生，CEO董事长，他讲述了自己的经历和感悟。&lt;/h4&gt;

&lt;p&gt;起先他只是个印刷厂打工的，后来觉得没希望就逃跑了，他觉得自己玩过很多音乐游戏想从事游戏行业，但没人要他，最后找到一家做音乐游戏的公司，并且之后开发出很多受欢迎的游戏，也有了点钱。&lt;/p&gt;

&lt;p&gt;2000年时他去考大学并读书，生孩子了，出来后，因为找不到自己理想的职位，于是自己开公司了。一开始做外包业务，后来慢慢做自己的游戏，公司举步艰难小心翼翼。&lt;/p&gt;

&lt;p&gt;这些年他领悟到信息的不对称，要有平民视角，当我们洋洋得意说一些晦涩难懂的词语来描述未来前景时，要尝试着眼于平常生活，否则别人无法理解。&lt;/p&gt;

&lt;p&gt;之后他的兴趣爱好也越来越广泛，广告行业，财报分析，营销策划等等，他说正因为无法预测下一个潮流是什么，所以必须主动出击，努力成为时代的潮流。如果点子过时了就不要采纳，要不断挑战新的事物，要有颠覆常识的想法以及飞跃市场本身的长远眼光。&lt;/p&gt;

&lt;p&gt;最后他觉得现在游戏和应用占用了人们很长时间，却还要付钱，他想今后10年来改变这个商业常识。&lt;/p&gt;

&lt;p&gt;《游戏开发者访谈录》集合日本100位游戏从业者的思想&lt;/p&gt;

&lt;h4 id=&quot;第41篇土屋升平游戏音乐人他说做游戏就像找人们的共同点寻求500万人喜欢的游戏就得寻找他们的共同点这样游戏拔尖的地方就越来越少变的越来越圆滑&quot;&gt;第41篇，土屋升平，游戏音乐人，他说做游戏就像找人们的共同点，寻求500万人喜欢的游戏就得寻找他们的共同点，这样游戏拔尖的地方就越来越少，变的越来越圆滑。&lt;/h4&gt;

&lt;p&gt;他认为世界需要多样性，如果制作技术能进步到5个人花1个月就能出一款大作，那么就会产生五花八门的游戏。&lt;/p&gt;

&lt;p&gt;他认为游戏是体现个人嗜好的东西，极少人会因为大家都在玩才选择开心的玩下去，基本都是根据自己喜好来决定。&lt;/p&gt;

&lt;h4 id=&quot;第44篇南治一德公司董事长他认为不写程序或少写程序才能减少不敢所以我们人类一直从汇编到高级语言到程序库到结构化编程到面向对象编程到游戏引擎都是在使用一切方法避免写程序或少写程序&quot;&gt;第44篇，南治一德，公司董事长，他认为不写程序或少写程序才能减少不敢，所以我们人类一直从汇编到高级语言，到程序库，到结构化编程，到面向对象编程，到游戏引擎，都是在使用一切方法避免写程序或少写程序。&lt;/h4&gt;

&lt;p&gt;他认为我们应该时刻关注代码的优化效率的优化，以及如何才能减少写程序。&lt;/p&gt;

&lt;h4 id=&quot;游戏开发者访谈录集合100位日本游戏从业者的思想&quot;&gt;《游戏开发者访谈录》集合100位日本游戏从业者的思想。&lt;/h4&gt;

&lt;h4 id=&quot;50篇天谷大辅独立游戏制作人说自己制作一款游戏花了5年有几次状态消沉的差点坚持不下去在最后阶段想让大家帮忙测bug大家由此提出了很多游戏改进的点子自己虽然很累了但下决心尽力回应大家要求最后做出了受欢迎的作品&quot;&gt;50篇，天谷大辅，独立游戏制作人，说自己制作一款游戏花了5年，有几次状态消沉的差点坚持不下去。在最后阶段想让大家帮忙测bug，大家由此提出了很多游戏改进的点子，自己虽然很累了但下决心尽力回应大家要求，最后做出了受欢迎的作品。&lt;/h4&gt;

&lt;h4 id=&quot;55篇风之宫游戏制作人说自己制作游戏兼了很多职位包括设计摄影录音运营销售等以致于一次在录音现场某位声优问我来干嘛因为在她的记忆中作者是摄影师或录音人员这让作者很高兴因为自己本是业余兼职却被人误认为是本职工作这是对自己某方面的认可&quot;&gt;55篇，风之宫，游戏制作人，说自己制作游戏兼了很多职位，包括设计，摄影，录音，运营，销售等，以致于一次在录音现场，某位声优问我来干嘛，因为在她的记忆中，作者是摄影师或录音人员。这让作者很高兴，因为自己本是业余兼职，却被人误认为是本职工作，这是对自己某方面的认可。&lt;/h4&gt;

&lt;h4 id=&quot;59篇斋藤明宏大学教授说自己在游戏行业做了30年从30年的工作经历来看想沿用过去的经验比较难自己50岁了还和20多岁的年轻人一起制作游戏在高速进化的游戏业界技术性经验用处不是那么大企业更愿意雇用年轻便宜学习能力强的年轻人到了35岁拼命也不再好使努力也没有效果最终留下来的人不到百分之一所以他认为最重要的是坚持下去坚持是最难的事如果不喜欢没兴趣你可能坚持不下去最好找到自己喜欢的方向而不只是为了赚当前的几块钱工资&quot;&gt;59篇，斋藤明宏，大学教授，说自己在游戏行业做了30年，从30年的工作经历来看，想沿用过去的经验比较难。自己50岁了还和20多岁的年轻人一起制作游戏。在高速进化的游戏业界，技术性经验用处不是那么大。企业更愿意雇用年轻便宜学习能力强的年轻人，到了35岁，拼命也不再好使，努力也没有效果。最终留下来的人不到百分之一。所以他认为最重要的是坚持下去，坚持是最难的事，如果不喜欢没兴趣你可能坚持不下去，最好找到自己喜欢的方向，而不只是为了赚当前的几块钱工资。&lt;/h4&gt;

&lt;h4 id=&quot;60篇岸本好弘副教授他在推行教育游戏希望把寓学于乐的方式推入学校在实验游戏教学法后有9成学生表示自己的专注力和学习欲望提高了他自己努力用游戏化要素来授课让孩子积极交换意见最后结果显示所有孩子都表示喜欢制作游戏并有4成儿童认识到做东西很辛苦平时努力学习很重要&quot;&gt;60篇，岸本好弘，副教授，他在推行教育游戏，希望把寓学于乐的方式推入学校，在实验游戏教学法后，有9成学生表示自己的专注力和学习欲望提高了。他自己努力用游戏化要素来授课，让孩子积极交换意见。最后结果显示，所有孩子都表示喜欢制作游戏，并有4成儿童认识到“做东西很辛苦”“平时努力学习很重要”。&lt;/h4&gt;

&lt;h4 id=&quot;游戏开发者访谈录完结&quot;&gt;《游戏开发者访谈录》完结&lt;/h4&gt;

&lt;h4 id=&quot;68篇山口洋一总监制作人他认为应该用28定律来做游戏游戏的核心要素占2成其他占8成我们应该把精力集中在对2成创新和冒险上这样比较容易反复试错如果把试错放大到8成上基本上没有成功的可能性忙也忙不过来&quot;&gt;68篇，山口洋一，总监制作人，他认为应该用2:8定律来做游戏，游戏的核心要素占2成，其他占8成，我们应该把精力集中在对2成创新和冒险上，这样比较容易反复试错，如果把试错放大到8成上，基本上没有成功的可能性，忙也忙不过来。&lt;/h4&gt;

&lt;p&gt;另外不管2成点子有多棒，游戏制作都是赌局，做出来的游戏不一定火。重点在于我们是否能坚持适度挑战。主要坚持下去，赢的概率就会变大。独特新颖的点子万一火了，过段时间也会成为管用的点子，所以要一直找新的点子才行。&lt;/p&gt;

&lt;h4 id=&quot;69篇山口博光ios和安卓讲师他回顾了下计算机设备的发展史以往的游戏显然都在利用计算机性能的提升来追求现实感然而再十几年后恐怕影像上的现实感就会发展到游戏影像和现实世界看不出差别了到时计算机多出来的多余性能该如何使用呢作者认为会向人工智能上发展先进的人工智能会颠覆游戏产业也会颠覆我们的生活本身&quot;&gt;69篇，山口博光，ios和安卓讲师，他回顾了下计算机设备的发展史，以往的游戏显然都在利用计算机性能的提升来追求现实感。然而再十几年后，恐怕影像上的现实感就会发展到游戏影像和现实世界看不出差别了，到时计算机多出来的多余性能该如何使用呢？作者认为会向人工智能上发展，先进的人工智能会颠覆游戏产业、也会颠覆我们的生活本身。&lt;/h4&gt;

&lt;h4 id=&quot;77篇小高和刚脚本家兼小说家他对运动员的精神很感动他们一切以比赛为中心不停锻炼自己的体力和心力他觉得我们开发者也要如此在生活和工作中不断锻炼自己&quot;&gt;77篇，小高和刚，脚本家兼小说家，他对运动员的精神很感动，他们一切以比赛为中心，不停锻炼自己的体力和心力。他觉得我们开发者也要如此，在生活和工作中不断锻炼自己。&lt;/h4&gt;

&lt;p&gt;他引用一个名人的话，如果你觉得“我只有这点能力，就干这点活吧”，那你肯定不会接到活，因为肯定有很多超出你实力范围的活出现，这就是你的机会，一定不要害怕。&lt;/p&gt;

&lt;p&gt;为了能抓住未来的千载难逢的机会，为了到时你能够相信自己的能力去把握这种机会，现在我们要每天都不断锻炼自我，就像运动员那样。&lt;/p&gt;

&lt;h4 id=&quot;85篇前田境幸宣传制作人他说我们应该留意一个运营手法就是让玩家感受到我赢了是实力输了是运气不好出了点小差错&quot;&gt;85篇，前田境幸，宣传制作人，他说我们应该留意一个运营手法，就是让玩家感受到，“我赢了是实力，输了是运气不好（出了点小差错）”&lt;/h4&gt;

&lt;h4 id=&quot;95篇谷口敦技术分析师他说我们应该多参加社区活动参加社区有很多好处能学到很多东西在多次参加社区活动过程中能认识很多人你不仅可以认识其他公司的同行也可能认识来自不同行业的人可以尽情享受互相交流的乐趣&quot;&gt;95篇，谷口敦，技术分析师，他说我们应该多参加社区活动，参加社区有很多好处，能学到很多东西，在多次参加社区活动过程中能认识很多人，你不仅可以认识其他公司的同行，也可能认识来自不同行业的人，可以尽情享受互相交流的乐趣。&lt;/h4&gt;

&lt;h4 id=&quot;97篇ernest-wadsms游戏设计顾问作家教育家他说缺乏经验的游戏设计师总会以角色故事和自己想尝试的新技术为出发点来考虑游戏这是错误的我们应该从玩家要做的事情开始着手如果能从玩家的行为开始设计能经常想着玩家会觉得什么好玩你就可能做出成功的游戏&quot;&gt;97篇，Ernest W.Adsms，游戏设计顾问、作家、教育家，他说，缺乏经验的游戏设计师总会以角色、故事和自己想尝试的新技术为出发点来考虑游戏。这是错误的，我们应该从玩家“要做”的事情开始着手，如果能从玩家的行为开始设计，能经常想着玩家会觉得什么好玩，你就可能做出成功的游戏。&lt;/h4&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(五) 《态度》</title>
   <link href="http://www.luzexi.com/2020/05/02/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B05"/>
   <updated>2020-05-02T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/05/02/读书笔记5</id>
   <content type="html">&lt;h4 id=&quot;态度吴军&quot;&gt;《态度》吴军&lt;/h4&gt;

&lt;h4 id=&quot;第六封信&quot;&gt;第六封信&lt;/h4&gt;

&lt;p&gt;这是孩子在和吴军交流时抱怨最近运气不好怀疑自己命不好后吴军给孩子的信。&lt;/p&gt;

&lt;p&gt;作者首先承认运气确实占据了人生的一大半关键因素。“事实上，如果没有运气，再努力也未必有结果”&lt;/p&gt;

&lt;p&gt;作者又抛出了孔子的话，“孔子说，人到了一定年纪，就能认清自己的命运，从而做到不做违背命运的事（从心所欲，不逾矩）”&lt;/p&gt;

&lt;p&gt;那命运由什么决定呢？作者说，首先是基因决定命运，基因的作用比我们想象的强大。其次是思想、行为，习惯。&lt;/p&gt;

&lt;p&gt;作者拿英国著名女首相，撒切尔夫人对“基因”的分析：&lt;/p&gt;

&lt;p&gt;注意你的想法，因为它能决定你的言辞和行动。&lt;/p&gt;

&lt;p&gt;注意你的言辞和行动，因为它会主导你的行动。&lt;/p&gt;

&lt;p&gt;注意你的行动，因为它能变成你的习惯。&lt;/p&gt;

&lt;p&gt;注意你的习惯，因为它能塑造你的性格。&lt;/p&gt;

&lt;p&gt;注意你的性格，因为它能决定你的命运。&lt;/p&gt;

&lt;p&gt;作者认为，我们从小养成的习惯决定了我们的命运。
他说，孩子们从小学数学，喜欢跳步骤，为了着急做完，字迹潦草。这些一开始只是孤立的行为，但是时间一长就形成了思维习惯。
到了中学开始，潦草，粗心成了他最大的借口，慢慢的他发现自己怎么都会做错或记不住一些事，他开始怀疑自己，认为自己能力和智力水平不够，慢慢性格也变了，开始厌学开始放弃课程，就这样他的命运一步步被最初自己不重视的习惯拉入了深渊。&lt;/p&gt;

&lt;p&gt;接下来作者又对，特朗普和希拉里，两人的孩子做出评价，他们虽都出自豪门，但伊万卡从小颇为自立，十多年前就靠自己努力在社会上站稳脚跟。切尔西则不同，如果不是她父母是克林顿，恐怕没人注意她。&lt;/p&gt;

&lt;p&gt;那么为什么经济条件类似的家庭，孩子最后命运相差很大呢？
因为我们很多事情在过去的不知不觉中决定了命运。那些我们不注意的地方，每件小事开始，慢慢形成习惯，习惯塑造了性格，性格决定了命运。&lt;/p&gt;

&lt;p&gt;作者又说除了习惯，我们还要看到大浪潮，去能事半功倍的地方。比如1830-1840年出生的美国人就赶上了美国工业革命。1950-1960年出生的中国人赶上了改革开放。这些人很容易一生不愁吃穿，有一份体面的工作。现在世界的发展指望美国和中国，在这两个国家之间做点事，就会事半功倍。&lt;/p&gt;

&lt;p&gt;然后作者又从被困难打击的反应这一面来说明命运在不同人身上的原因。&lt;/p&gt;

&lt;p&gt;他说，从一个人被困难打击后的反应，一般有3种，第一是立刻反击回去，第二是认命回避，第三种是冷静下来分析下做出更有效的对策。&lt;/p&gt;

&lt;p&gt;作者说这是一种处事的习惯，这种习惯间接决定了我们的命运。被人扇一巴掌后是立刻回击，还是认命离开，还是冷静仔细想想后做出对策，争取我们合理的权力和应有的对策。&lt;/p&gt;

&lt;p&gt;最后作者提醒女儿，影响你一生的还有一个重要因素是，你的伴侣或丈夫。&lt;/p&gt;

&lt;p&gt;他举例林徽因，她一生交往的男性朋友都是大才子，后来丈夫是中国近代知名建筑师梁思成。当时他们家是中国最有学问的精英集会的地方，这样的环境对她帮助非常大。&lt;/p&gt;

&lt;p&gt;相反，台湾一个非常著名演员胡因梦，嫁给了非常叛逆的李敖。她后来评论自己这段经历说，嫁给一个渣男需要40年才能恢复。&lt;/p&gt;

&lt;p&gt;很多人都抱怨自己命不好，其实怨不得别人，一来他们读书少，二来他们圈子太狭窄，以至于见识和判断力也很低，被骗被忽悠投资失败钱打水漂都是正常的。&lt;/p&gt;

&lt;h4 id=&quot;态度吴军-1&quot;&gt;《态度》吴军&lt;/h4&gt;

&lt;h4 id=&quot;第78封信&quot;&gt;第7、8封信&lt;/h4&gt;

&lt;p&gt;这两篇讲格局，作者先叙述为什么要学让人文课程，再从扩展出，专业与格局的不同。人文课能让人眼界开阔些，格局大一些，从大处着眼，而不仅仅是技能比别人高。&lt;/p&gt;

&lt;p&gt;作者举例几个顶尖高材生做高频交易的例子说，高频交易本身无法创造什么，只是偷偷赚去了差价。所以他拒绝了他们的投资邀约，做高频交易的技术性很强没错，这几个高材生也很厉害，但格局太小。&lt;/p&gt;

&lt;p&gt;然后作者又拿谷歌、脸谱、苹果公司与高频交易公司做对比。高频交易只是让股票市场交易量上升，它的意义仅此而已。而谷歌、脸谱、苹果则不同，他们改变了世界，对世界对社会产生了正面的社会效应。简单来说，高频交易，以赚小钱、小富即安为目的。而谷歌、脸谱、苹果则以改变世界为目的。两者的格局相差巨大。&lt;/p&gt;

&lt;p&gt;作者总结，所以人在做一件事时，要尽可能往最好的目标努力，境界要高。所谓境界，可以理解为，目光能看多远。比如我能够看到3年后的事情，并根据看到的目标努力，而他则只能看到1年后的事情，他也就只能盯着1年后的目标努力，而这时我就比他有更高的境界。&lt;/p&gt;

&lt;p&gt;下一封信中，他又对格局和境界与孩子聊了些历史的典故。&lt;/p&gt;

&lt;p&gt;作者举例秦国时商鞅变法的例子。商鞅一开始向秦王讲，饶、舜、禹、汤的大道，秦王觉得无聊。第二次向他讲周文王、周武王的王道，秦王稍微有点兴趣。第三次向他讲五霸的霸道，秦王立刻重用了商鞅。&lt;/p&gt;

&lt;p&gt;有人不解的问商鞅，为什么前两次要谈帝道和王道？商鞅说，我怕秦王真是个境界特别高、有大志向的人，如果我一开始说霸道恐怕要被他看低。&lt;/p&gt;

&lt;p&gt;商鞅很清楚，制定功利性很强的法律地位负面后果，但这不是他所能说得动秦王的。在制定统一方针时，商鞅分别用帝道、王道、霸道游说秦王，秦王对它们的态度截然不同，最后采用了一种速效却危险的策略，让秦国走向了死胡同。秦国在统一中国后不仅很快就被灭亡，更可悲的是，它的宗室也被造反的人杀光。&lt;/p&gt;

&lt;p&gt;作者警示，绝大多数人依然只知道追求速效，无视长期利益。但是人一旦习惯获得短期利益，境界就高不起来了，可能永远不会追求更高的境界了。&lt;/p&gt;

&lt;p&gt;作者又举拿破仑的例子，他虽然在军事上很伟大，但最厉害的还是他的起草的《拿破仑法典》，他花了很大精力和时间去制定法典，以致于虽然拿破仑的军事胜利在1815年就终结了，但整个19世纪，欧洲都在拿破仑法典的影响下度过。&lt;/p&gt;

&lt;p&gt;作者最后又说说了现实现象。现在很多大学生，大多数关心的是学什么专业和技能可以赚大钱，而且最好快速获得成功。导致原本一些高薪专业被挤爆，毕业后又很多人挤破脑袋进专业公司里在里面打杂。这些人根本赚不到钱，追求太短视，境界太低，很难有大的发展机会。&lt;/p&gt;

&lt;p&gt;最好拿自己说了说经历。
“我很多时候想离开大学去挣钱，但是那样的话，我的学术水平就达不到世界一流水平，因此我两次辞职回到学校全心读书，并且很多次周围的人因为受到各种诱惑半途离开学校之后，我还能坚持把学业完成。那些在中途退学的人，都找到了安稳的工作，当时他们的生活水平不知道比我高多少，但他们大部分一辈子所能触及的高度基本上在那个时候就被限制住了。今天回头过来看，人追求多高的境界，最后就会得到多好的结果。”&lt;/p&gt;

&lt;h4 id=&quot;态度吴军-2&quot;&gt;《态度》吴军&lt;/h4&gt;

&lt;h4 id=&quot;第9封信&quot;&gt;第9封信&lt;/h4&gt;

&lt;p&gt;姐姐没打算做交换生，作者劝说的同时也聊了聊德国。&lt;/p&gt;

&lt;p&gt;他说，美国科技水平不差但为什么就是没有德国货的品质高，这很值得深思。
他想起了多年前在北京见到的老同学，他在德国生活了20多年。他聊起这事说，德国人经常挂在嘴边的一句话，“生活是具体的”。&lt;/p&gt;

&lt;p&gt;怎么理解呢？老同学讲了很多，大概的意思就是，德国人认为，每个细节都应该有标准，每个标准都达到了，整体才会更好。即，每个部分严丝合缝一点点，整体的质量就会有巨大的差别。&lt;/p&gt;

&lt;p&gt;作者提起了每次在饭桌上大家讨论中国腐败现象的场景。世界上所有国家都有腐败，美国也是，阿拉伯和北非国家只要愿意给克林顿基金会捐钱，就能见到当时担任国务卿的希拉里，澳大利亚为了巴结选举失败前的希拉里，向克林顿基金会捐了8000多万美金作为政治献金。
中国以前很腐败，无论以前是什么样，现在是否比以前好很多，即便有贪腐，也说明社会在进步。&lt;/p&gt;

&lt;p&gt;作者又举了几个同样模棱两可的例子，电动汽车是否是真的环保，太阳能发电是否真的降低了二氧化碳的排放。
根据数据计算，它们确实更加环保，只是没有那么明显。但不明显我们就不去做了吗，还记得我们前面说过的么，每个细节好一点点，整体就会好很多，这里的几个例子就是这个道理。&lt;/p&gt;

&lt;p&gt;作者开始阐述德国人的严谨态度。德国人不仅对国家、社会和生活态度如此，在工作中更是贯彻到底。&lt;/p&gt;

&lt;p&gt;他们对一个产品制定了一大堆非常具体的指标，如果每个都达到指标，就说明产品总体质量达到了预期。&lt;/p&gt;

&lt;p&gt;这种对每个细节的严苛处理，使得它产品的成本也高很多。&lt;/p&gt;

&lt;p&gt;作者举例莱卡镜头，莱卡在制造过程中，细到连一根刻度线的高度都有具体的规范和标准，这使得他们试图生产线移到日本时，日本花了很多年时间也没有达到德国的标准，最后干脆关闭了日本生产线。德国另一家著名光学仪器公司蔡司至今没能在日本制造出标准的设备，因此只能用不同的型号区分，价格差三倍。差一些的型号虽然用起来性能也不错，但在户外长时间使用时则会有大的差距了。&lt;/p&gt;

&lt;p&gt;最后，作者提议女儿去德国做半年交换生，去学习德国人做科研、写论文时严谨的态度和方法。他们把论文中每个实验细节的中间结果数据都写出来了，而大部分国家的科学家只是报告主要结果。&lt;/p&gt;

&lt;p&gt;“果然有机会去德国人的学校学习一段时间，亲身感受一下他们的做事方法，将受益匪浅。”&lt;/p&gt;

&lt;p&gt;记住他们的话，生活是具体的，慢慢体会，会有收获。&lt;/p&gt;

&lt;h4 id=&quot;态度吴军-3&quot;&gt;《态度》吴军&lt;/h4&gt;

&lt;h4 id=&quot;第10封信&quot;&gt;第10封信&lt;/h4&gt;

&lt;p&gt;作者的小女儿问他，“姐姐上了好大学，是不是将来就会有好工作、好生活”，作者说“不是的，她以后还要努力一辈子”，妹妹又问“既然以后要努力一辈子，为什么要接受好的教育？不是有很多人退学创业成功了吗？”&lt;/p&gt;

&lt;p&gt;作者针对小女儿的疑问给他写了封信。&lt;/p&gt;

&lt;p&gt;信中阐述了教育的必要性。&lt;/p&gt;

&lt;p&gt;第一个是教育改变命运。&lt;/p&gt;

&lt;p&gt;作者阐述，欧洲很早就意识到教育对人的重要性，在16世英国就开始兴办免费教育，为贫困子弟创造受教育的机会，牛顿就是靠在这种免费教育完成了中学学业并进入了剑桥大学的。&lt;/p&gt;

&lt;p&gt;到后来伊丽莎白一世规范学徒制度，由国家出钱帮助贫困子弟学习谋生技能。再后来，在英国和美国有了很多“星期日”学校给贫困家庭普及基础教育。&lt;/p&gt;

&lt;p&gt;这样很快人们就发现了，有没有受过教育，对将来的命运常常有非常大的影响。&lt;/p&gt;

&lt;p&gt;亚洲家长在20世纪后才开始重视教育，因为之前教育并不普及，也无法得到体面的工作。当亚洲人发现教育程度和水平对孩子未来发展的影响很大时，教育重视才成为一种风气。&lt;/p&gt;

&lt;p&gt;到现在很多人家里已经不缺钱了，但他们依然有主动学习的动力，这说明接受过高等教育的人已经认识到教育的益处是动力的来源了。&lt;/p&gt;

&lt;p&gt;第二个是有钱人更需要良好的教育。&lt;/p&gt;

&lt;p&gt;作者告诉女儿，在中国有个词语叫‘土豪’，是指那些发了财的但举止不是很体面的人。&lt;/p&gt;

&lt;p&gt;他说，举止不体面主要原因是接受教育不够多，读书比较少。这类人后来要么花时间接受了更多教育事业也会不断发展，要么仍然停留在低教育水平上，有了钱除了糟蹋，想不出能做什么更有意义的事，而他们自己是意识不到自己在糟蹋的，否则他们也干不出这些事来。&lt;/p&gt;

&lt;p&gt;作者又举例中国股市早期赚到钱的那些人，他们都是教育水平很低的并且敢于冒险的那类人，他们有了钱后就没有更高的理想和追求了，以至于后来这些人结局都很惨，除了2个人之外其余的人要么破产要么进了监狱要么被人杀害。&lt;/p&gt;

&lt;p&gt;作者再次强调，人不接受教育，就很难有见识，而没有见识，做事情就会事倍功半，疲于奔命。&lt;/p&gt;

&lt;p&gt;作者说也有一个在股市上赚到钱的人来咨询他本人，他最后发现无论他们多么努力，他的财富都不会随着他努力而增加，他一点办法都没有。因为他接受的教育太少了，看不懂发展很快的科技趋势，只能眼睁睁地看着周围人赶超自己。&lt;/p&gt;

&lt;p&gt;在中国财富剧增的家庭很多，有些非常重视孩子的教育，有些则只知道满足孩子的无知需求。前者的下一代人大多是有教养并且努力上进的人，后者的下一代则大多是轻狂之辈，除了玩跑车、滥交朋友，做不了什么事情，只能在社会上被大家当笑话。&lt;/p&gt;

&lt;p&gt;作者举出曾国藩的话，他对后代说，依靠财富和官位是很难保证家族兴盛的，唯有教育可以，因此他希望后代不求做官，而要多读书多学习。&lt;/p&gt;

&lt;p&gt;第三个他讲了讲退学创业的事。&lt;/p&gt;

&lt;p&gt;他说比较有名的退学创业成功的例子就只有5个，这5个人中，谷歌的佩奇和布林是在通过了博士入学考试后退学的，他们离博士学位只有一步之遥。而盖茨和扎克伯格都是哈佛大学的学生，他们也是创业成功之后由于无法兼顾学业才无奈退学的，至于乔布斯，他仅仅是因为不忍心花辛苦领养他父母的钱而退学，但在退学后他仍然在学校里蹭课。这和我们很多人认为的退学和创业的因果关系都是相反的。&lt;/p&gt;

&lt;p&gt;作者又阐述了好大学的好处，他说，倒不是因为好大学的课程就一定比二流大学的好，而是因为他们有相对比较好的学习环境。年轻人的好奇心和求知欲望，很容易受周围同学的影响。从这个角度来说，不能进入一流大学也没关系，只要自己用心找一些好同学为伴即可。&lt;/p&gt;

&lt;p&gt;最后作者希望女儿不要为进入一所好学校而读书，而是要为了让自己能够真正立足于社会并且能够成为有用之人而读书。&lt;/p&gt;

&lt;h4 id=&quot;态度吴军-4&quot;&gt;《态度》吴军&lt;/h4&gt;

&lt;h4 id=&quot;第1112封信&quot;&gt;第11、12封信&lt;/h4&gt;

&lt;h4 id=&quot;第11封信中&quot;&gt;第11封信中&lt;/h4&gt;

&lt;p&gt;作者的女儿刚上大学不久给家里打电话讲述了自己遇到的困难和挫折，说自己运气很坏，于是他就给女儿写了封信聊了聊今后的生活。&lt;/p&gt;

&lt;p&gt;作者说想谈谈如何避免坏运气。他拿自己的过去举例说自己虽然在外人眼里运气非常好，但自己知道其实也经常走坏运。之所以有他今天的成就，很大程度上是因为自己足够努力走出了坏运气，并且抓住了一生中不多的好运气。&lt;/p&gt;

&lt;p&gt;他举例一个美国将军在朝鲜战场上的故事，他问参谋长要打赢现在这场仗需要多少弹药，参谋长告诉他一个数字，然后将军说，要打出去5倍的这个数字的弹药。最后这位将军实现了他的战略目的。他聪明的地方在于他比其他人更多地估计了困难，留出了足够的余地。&lt;/p&gt;

&lt;p&gt;作者又举例打败拿破仑的那位将军，威灵顿，他没有拿破仑的感召力，也从不高估自己的军队超长发挥的可能性，他在战前考虑了每个坏情况的可能性，最终因为准备充分而获得了命运的垂青。&lt;/p&gt;

&lt;p&gt;由此他告诉女儿如果想得到10分，就要准备30分的努力。先假设自己将遇到比别人更坏的运气，所以要比别人做更多的准备工作，最后才能得到好运的垂青。&lt;/p&gt;

&lt;p&gt;然后作者谈了女儿在大学里的定位，大学里人才众多，不必为拿不到第一名而沮丧，只要每天心平气和地完成每件细小的工作，日积月累后自己的位置自然而然会提高，人的成长是自己不断提升自己的过程，而不是为了拿第一名。&lt;/p&gt;

&lt;p&gt;最后作者谈了下如何走自己的路，作者说，对任何人来说，失败并不可怕，因为任何尝试都会伴随失败。但是聪明人就能从失败中不断学习，只有这样人才能变得成熟。&lt;/p&gt;

&lt;p&gt;他借用教育家约翰.纽曼的话来描述“理想中的大学”，”即如果能让许多聪明、求知欲强、富有同情心且目光敏锐的年轻人聚到一起，即使没有人教，他们也能互相学习、互相交流，从而了解新的思想和看法，看到新鲜事物并掌握独到的行为判断力。”&lt;/p&gt;

&lt;p&gt;作者非常认同这段话，他认为上大学最重要的目的是向周围的人学习，而不仅仅是学习课程。&lt;/p&gt;

&lt;h4 id=&quot;第12封信中&quot;&gt;第12封信中&lt;/h4&gt;

&lt;p&gt;作者的女儿开始为升学做准备，为了不让女儿有升学压力，他写了封信想告诉女儿，为什么上什么大学不重要，重要的是做个好孩子。&lt;/p&gt;

&lt;p&gt;作者认为一些家长把孩子考入名牌大学视为终极目标的做法有点偏激。他认为很多事情远比上一所好大学重要。&lt;/p&gt;

&lt;p&gt;作者举例自己加入早期的谷歌公司，它一个特点是不作恶，这是当时加入谷歌成员们很认同的一个价值观，就因为这个价值观让每个员工都信守职责、兢兢业业，这使得这家小小的公司具备很强的竞争力。相反很多公司只有几十人，有了一点盈利很快就因为内斗而分崩离析，就因为它们在选人的时候只注重能力，而不注重品行。&lt;/p&gt;

&lt;p&gt;公司如此，人也是一样，相比能力，品行更为重要。一个人，能力不行可以继续努力，一次不行就两次。但品行不端，路走邪了，一辈子都会没有希望。&lt;/p&gt;

&lt;p&gt;作者说自己在面试员工时，更加注重品行，他认为品行有问题的人给团队带来的灾难会更巨大。&lt;/p&gt;

&lt;p&gt;缺乏好品行的人，大多是因为家长对孩子的教育有问题，有些是因为疏忽了孩子，有些则是对孩子的教育上出现了偏差。例如有些父母过分重视孩子的成绩，忽略了其他方面的成长，使得孩子产生了逆反心理，最后适得其反。&lt;/p&gt;

&lt;p&gt;作者说，很多人都以为成功能带来幸福，其实并不是这样的，很多成功人士过得并不幸福。但那些能够一心向善，懂得感恩的人，即使生活辛苦了一点，内心却感到很满足和幸福。&lt;/p&gt;

&lt;p&gt;作者不要求孩子一定要考上什么好大学，如何如何的成功，只要她是个好孩子。于是作者对什么好孩子做出了解释。&lt;/p&gt;

&lt;p&gt;“诚实，守信用。勤勉，自律、谦虚。友善，随和、有同理心。正直，公正。”&lt;/p&gt;

&lt;p&gt;这是作者对孩子要做一个好孩子的期望。&lt;/p&gt;

&lt;h4 id=&quot;态度吴军-5&quot;&gt;《态度》吴军&lt;/h4&gt;

&lt;h4 id=&quot;第13封信&quot;&gt;第13封信&lt;/h4&gt;

&lt;p&gt;这篇作者与女儿聊了下“贫穷”&lt;/p&gt;

&lt;p&gt;由于女儿一直生活在风衣足食的环境中对贫穷了解甚少，作者借此信与女儿聊了聊。&lt;/p&gt;

&lt;p&gt;他说，世界上并非世界上所有孩子都跟你一样过着丰衣足食的生活。你需要了解这个世界还有很多人正在等待着被帮助，需要让他们知道人必须通过自己的努力过好生活。&lt;/p&gt;

&lt;p&gt;作者述说了自己的幼年经历。因为赶上“文化大革命”，十年动乱，社会状况很糟糕。即使自己生活在当时一线城市南京，也过得很艰苦，衣食无着。&lt;/p&gt;

&lt;p&gt;现在的中国很多家庭跟过去那样，父母外出打工和子女分离生活。自己也是跟父母分离生活了好几年，当很多年第一次见到父母时甚至有点陌生。&lt;/p&gt;

&lt;p&gt;作者告诉女儿那时的自己很穷，看见什么食物都流口水，看起来很没教养。如果今天我们见到穷孩子表现出的对物质的某些贪婪，不要太责怪他们。&lt;/p&gt;

&lt;p&gt;作者说，贫穷可以让一些人沉沦，也可以让一些人奋起。自己就是奋起的那个，自己从小学习的动力就是将来有机会离开这个贫穷的地方。甚至长大后都不敢松懈，也不敢像赌徒那样投机。&lt;/p&gt;

&lt;p&gt;最后作者说，贫穷是把双刃剑，既会给人动力，也会让人沉沦。自己也曾非常贫穷，但贫穷不是错误，我们不能因为对方贫穷就觉得是因为他们不努力。&lt;/p&gt;

&lt;h4 id=&quot;态度吴军-6&quot;&gt;《态度》吴军&lt;/h4&gt;

&lt;h4 id=&quot;第14封信&quot;&gt;第14封信&lt;/h4&gt;

&lt;p&gt;作者接着跟女儿聊“贫穷”&lt;/p&gt;

&lt;p&gt;他说，贫穷是把双刃剑，它可以让人奋发向上，也可以让人颓废不能自拔。是什么力量让它激发了力量，又是什么让它成为一种诅咒呢？&lt;/p&gt;

&lt;p&gt;人的天性是不安于现状的，穷则思变，变则通达，因此贫穷可以成为一种动力。&lt;/p&gt;

&lt;p&gt;一个人需要在主流社会环境中生活和发展，如果遭人批评，白眼或被欺负，就会渐渐丧失信心，此时就会脱离主流社会，或者躲起来或者找一群更低级一些的找回自信，虽然他们摆脱了白眼和嘲讽，但这样他们就只能与更低阶的人为伍了。&lt;/p&gt;

&lt;p&gt;这样人就走进了一个舒适区，一个贫穷的舒适区，与更低阶人为伍的舒适区。&lt;/p&gt;

&lt;p&gt;为什么会丧失信心呢？作者说，人有很多弱点，比如难以调整的自尊心，自己的物质越缺少，心里就越脆弱，比如一个贫家子弟最怕别人说他穷，他认为别人看不起他，又比如越学得不好的孩子，越怕别人说他笨，这是人类普遍的特点。于是最后很多穷人选择了与穷人为伍，成绩差的孩子还是选择与成绩差的扎堆。&lt;/p&gt;

&lt;p&gt;他们喜欢这样选择趟在舒适区，久而久之，他们就无法摆脱原来的阶层了。&lt;/p&gt;

&lt;p&gt;其实历史上有很多人不怕被嘲笑的穷人。比如上海的杜月笙，他靠被人修脚、卖水果为生，不知道遭受了多少人的白眼和嘲笑。但他仍然一门心思要挤上上层社会，最后还真的做到了。要做到这点，首先要打碎自己易受伤的‘玻璃心’，忘掉自己的感觉，尽力去做。&lt;/p&gt;

&lt;p&gt;作者又回来聊聊女儿说，你并不贫穷，但是如果有些事情做的不好的时候，你所处的地位和情况可能和贫穷差不多。&lt;/p&gt;

&lt;p&gt;人的心理能否接受自己不如别人，在可能会被别人嘲笑的情况下，是否还能努力往前走，直到改变自己的状态，就是一个对自己的考验。其实这和走出贫穷差不多。关键是忘掉自己的感觉，尽力去表现。&lt;/p&gt;

&lt;p&gt;作者开始滔滔不绝说人生，“人一辈子会在很多时候感受到自己的‘贫穷’，这时你是否还愿意去追求一个更富有、更高层次的环境，就决定了你是否能够不断进步。很多人的进步到30多岁就停止了，只有很少的人一直能够坚持到老。永远要承认自己的贫穷，不要担心别人的白眼，只有这样才能真正富有起来。”&lt;/p&gt;

&lt;h4 id=&quot;态度吴军-7&quot;&gt;《态度》吴军&lt;/h4&gt;

&lt;h4 id=&quot;第1516封信&quot;&gt;第15,16封信&lt;/h4&gt;

&lt;h4 id=&quot;第15封信很短由于大女儿太节省作者写信跟她聊聊省钱和花钱的事&quot;&gt;第15封信很短，由于大女儿太节省，作者写信跟她聊聊省钱和花钱的事&lt;/h4&gt;

&lt;p&gt;作者首先表扬女儿生活极为节省，然后说自己不希望女儿过于节省，以至于不能让钱发挥它应有的作用。&lt;/p&gt;

&lt;p&gt;他说了说自己在年少时的经历，说自己进入大学后就没有太过拮据，在工作赚钱后也从来不吝啬花钱。但也从不乱花钱。&lt;/p&gt;

&lt;p&gt;他认为如果能够通过花钱更好地解决问题，他会很大方地花出去。实际上，绝大部分时候，这些花出去的钱，最终都会数倍地返回来。因此他希望女儿能够不为钱所惑，能从大处着眼，能比自己更能有见识。&lt;/p&gt;

&lt;h4 id=&quot;第16封信作者详细跟女儿聊了聊关于花钱的事&quot;&gt;第16封信，作者详细跟女儿聊了聊关于花钱的事。&lt;/h4&gt;

&lt;p&gt;作者前面问了女儿如果有100万和1000万美元时会如何花。女儿说想去游遍中国并记录和拍下所有景点，以及去农村里盖更多的学校。&lt;/p&gt;

&lt;p&gt;这里作者很欣慰女儿的想法，他说当人有钱后没有打算乱花，也没有打算仅仅把它们存入银行或者投资股市，更没有想从此不工作，而是想用它们来做一些有意义的事，是值得赞赏的。&lt;/p&gt;

&lt;p&gt;引用巴菲特的话说，“钱是为了让你做想做的事，而不是为了让你无所事事”&lt;/p&gt;

&lt;p&gt;作者认为，一个人有了财富，能够善用财富决定了一个人的格局，格局又决定了一个人能走多远。&lt;/p&gt;

&lt;p&gt;他举例徐霞客，他是明代末期一个家里比较富裕的人，他花了一辈子的时间游历了明朝大约一半的省份，并且记录了所到之处的各种地理、人文和动植物情况，特别研究了石灰岩地貌，他将所见所闻写成游记，成为当时中国最完整的地理、地质、水文、气候、商业、经济和文化史料。&lt;/p&gt;

&lt;p&gt;还有另一个人叫李希霍芬，是个德国人，他是个对了解中国地理和经济文化有重大的贡献的旅行者。在来中国前他是考察了加利福尼亚，掀起了当时的淘金热。后来去了中国7次，丝绸之路就是他提出来的。&lt;/p&gt;

&lt;p&gt;他考察过中国很多地方，包括景德镇的陶瓷制作过程，胶州湾宜人气候和天然良港，也就是后来选定了中国的德国租借地青岛。也到过四川，考察了水利工程都江堰，详细地将它介绍给了全世界。他还找到了古丝绸之路上消息的大湖罗布泊的位置。&lt;/p&gt;

&lt;p&gt;最后他作为学者，提出了中国黄土高原的成因、中国北方贫困的原因，并初步探明了中国产煤大省山西煤炭的总量。他的学生也在中国发现了楼兰古国的遗迹。&lt;/p&gt;

&lt;p&gt;除了古代，现代也有很多人在有钱了以后做了很多有意义的事。谷歌副总裁维恩.罗欣在谷歌上市自己有钱后，因为自己兴趣，制造了很多最先进的望远镜，并建立了一个全球联网的天文台，这个平台在引力波的证实、发现可持续爆发的超新星的两个重大发现上起了关键作用。&lt;/p&gt;

&lt;p&gt;最后作者希望女儿在运用财富这件事上能从大处着眼。如果以后实现了财富自由，应该利用财富做一点儿其他人没有做或者做不到的事。&lt;/p&gt;

&lt;h4 id=&quot;态度吴军-8&quot;&gt;《态度》吴军&lt;/h4&gt;

&lt;h4 id=&quot;第20封信&quot;&gt;第20封信&lt;/h4&gt;

&lt;p&gt;作者在这份信中跟女儿聊了聊金钱观。&lt;/p&gt;

&lt;p&gt;作者说自己赞同鲁迅的观点，就是人要生存，要温饱，要发展，即所谓生存并不是勉强度日，而是要过的体面些；所谓温饱，就是衣食无忧；所谓发展，是说物质上能够做到适当的享受。&lt;/p&gt;

&lt;p&gt;他感叹一些年轻在进入哈佛后对自己未来的理想有点小，他跟哈佛一些学生聊，他们说自己未来想进计算机和医学行业，这样能够保障有比较高的收入。&lt;/p&gt;

&lt;p&gt;其实每年进入哈佛的华裔学生非常少，他们其实承载了一个裔族的未来的希望，因此他们应该去从政，成为美国未来的领袖，而不是只是选择一条简单的收入高的职业。&lt;/p&gt;

&lt;p&gt;作者说在美国，衡量精英的标准不是钱和学历，而是影响力。这个道理对于那些每月勉强维持收支平衡的中低层大众来讲，有点空泛。而对于文博不是问题的人来说，在年轻时候能够懂得把钱看淡一点是有必要的。&lt;/p&gt;

&lt;p&gt;作者了解过一个学术精英为什么下海做生意，原因还是在大学里要尽太多的义务，如果你想增加影响力，就要尽各种专业机构的义务，对钱不能太执着。由于很多人对钱很执着，因此走不到学术金字塔的顶尖，更不用说有影响力的公众人物了。&lt;/p&gt;

&lt;p&gt;然后作者分析了下，美国富人愿意捐钱的3个原因，第一个宗教原因。第二个是不希望成为后代不工作、不上进的理由。第三是希望能够用钱来扩大自己的影响力，把钱作为实现自己的理想的工具。很多人在捐钱的时候是有附加条件的，甚至有一些是政治性的附加条件。这些条件通常不是为了自己，而是为了某种理想，或者为了一个族群的利益。&lt;/p&gt;

&lt;p&gt;作者觉得上哈佛、耶鲁、麻省这样的学校目的首先并不是学习赚钱的技能，而是学习如何成为精英，以便将来有钱了反哺社会。他们的眼界应该不在于看不看钱或挣不挣钱，而在于能否看透、认清，钱仅仅是媒介的性质。如果只是为了谋求某个职业，挣钱或许不少，但从社会的影响力来讲，依然没有摆脱穷人的心态，自私、守旧。&lt;/p&gt;

&lt;p&gt;诺贝尔奖获得者朗道，把物理学家分为5个等级，所列的等级核心思想是，人和人的差距，能力和能力的差距，是数量级的差别，而不是通常人们想象的那么一点点差别。&lt;/p&gt;

&lt;p&gt;作者觉得这5个等级，也可以用于我们任何的专业人士中来评定。&lt;/p&gt;

&lt;h4 id=&quot;第五级能独立解决问题完成工程工作&quot;&gt;第五级，能独立解决问题，完成工程工作。&lt;/h4&gt;

&lt;h4 id=&quot;第四级能知道和带领其他人一同完成更有影响力的工作&quot;&gt;第四级，能知道和带领其他人一同完成更有影响力的工作。&lt;/h4&gt;

&lt;h4 id=&quot;第三级能独立设计和实现产品并且在市场上获得成功&quot;&gt;第三级，能独立设计和实现产品，并且在市场上获得成功。&lt;/h4&gt;

&lt;h4 id=&quot;第二级能设计和实现别人不能做出的产品也就是说他的地位很难被取代&quot;&gt;第二级，能设计和实现别人不能做出的产品，也就是说他的地位很难被取代。&lt;/h4&gt;

&lt;h4 id=&quot;第一级开创一个产业&quot;&gt;第一级，开创一个产业。&lt;/h4&gt;

&lt;p&gt;这5个等级，如果一个人能够在能力水平上晋升一级，不仅贡献多10倍，所做的事情的影响力以及包括自己的收入也常常会多10倍。&lt;/p&gt;

&lt;p&gt;最后作者希望女儿在麻省能学习到具备超过同龄人的能力，做些更有影响力的事。&lt;/p&gt;

&lt;h4 id=&quot;态度吴军-9&quot;&gt;《态度》吴军&lt;/h4&gt;

&lt;h4 id=&quot;第21封信&quot;&gt;第21封信&lt;/h4&gt;

&lt;h4 id=&quot;作者跟女儿聊了聊交友&quot;&gt;作者跟女儿聊了聊交友&lt;/h4&gt;

&lt;p&gt;作者首先跟女儿说，大学里交友是很好的很纯粹的，以后在工作中，更多是业务上的关系，难免有点功利，但是大学却不同，同学愿意彼此互相照顾，这更多是处于年轻人的善意和天然的交友欲望。&lt;/p&gt;

&lt;p&gt;出了大学后，很多人创业即使创业成功了，他们也会不惜毁掉多年的交情，以谋求自己利益的最大化。&lt;/p&gt;

&lt;p&gt;作者举例了一个自己的朋友，他是作者进谷歌时认识的中国人，他是一个非常理性且大气的人，也从来没有摆过老资格，因为作者跟他很合得来，后来很多的经历也证明，这位朋友是非常值得深交的朋友。&lt;/p&gt;

&lt;p&gt;作者又说自己在腾讯担任副总裁时，每天都有人约吃饭，各种人推都推不走，一些人甚至把房子和车子都借给自己。但是当自己离开腾讯时，90%的人没有再和自己打过招呼。&lt;/p&gt;

&lt;p&gt;对于这种现象，作者告诉女儿，不必奇怪，因为人首先考虑的是自己的利益，这是人的本能，只要不刻意伤害我们，就不必太在意。&lt;/p&gt;

&lt;p&gt;我们人无法决定自己的出生、家人、亲戚的圈子，能自己决定的也就是和谁交朋友了。好的靠谱的朋友是巨大的财富。&lt;/p&gt;

&lt;p&gt;作者说自己平时交朋友都假设人是正直的、善良的、诚信的。如果自己上当了就不再给对方另一次机会。曾国藩也是同样的，他坚决不与荒唐的亲戚来往。&lt;/p&gt;

&lt;p&gt;最后作者给女儿一些交友的建议，交友时一定要真诚、大方和宽容。不要怕自己吃小亏，对别人的一些小毛病要容忍。毕竟人无完人，不要因为别人的一些缺点就否定整个人。&lt;/p&gt;

&lt;h4 id=&quot;态度吴军-10&quot;&gt;《态度》吴军&lt;/h4&gt;

&lt;h4 id=&quot;第22封信&quot;&gt;第22封信&lt;/h4&gt;

&lt;h4 id=&quot;作者跟女儿聊了聊恋爱&quot;&gt;作者跟女儿聊了聊恋爱&lt;/h4&gt;

&lt;p&gt;科学家发现了人类会分泌爱情物质，就是多巴胺、苯基乙安、内啡肽和去甲肾上腺素等，它们让男女愿意相处。&lt;/p&gt;

&lt;p&gt;作者认为没有爱情的人是有缺陷的，没有经历真正爱情的人生也是不完整的。&lt;/p&gt;

&lt;p&gt;作者想起了妹妹的历史老师说的一句话，要想客观公正地看待历史，我们需要读三种历史：胜利者写的历史，失败者写的历史，女人写的历史。这三种角度看待历史才能真实的还原真实的场景。这就说明男人和女人看同一件事情是真的不一样的。正是由于男人和女人的这种局限性，两个人在一起才能互补，人生才能完整。&lt;/p&gt;

&lt;p&gt;作者认为两个适合的人能一起相处，他们形成的合力一定是 1 + 1 &amp;gt; 2的，这也是作者不认为谈恋爱会耽误学习的原因。但是如果两个人勉强凑合在一起，将来一定会有较大的缝隙，不会严丝合缝，这可能对两个人带来巨大的伤害，因为找到合适的人很重要。&lt;/p&gt;

&lt;p&gt;有人说读名校的好处是容易找到合适的人，这种想法虽然有点功利，也不无道理。&lt;/p&gt;

&lt;p&gt;对于女人来讲，与男人不同的是，她应该展现出自己的柔美、温柔和端庄的一面。没有男人会喜欢穿着太随意的女人，就如同女人不喜欢邋里邋遢的男人一样。&lt;/p&gt;

&lt;p&gt;但是外表只能吸引人一段时间，时间长了即使是仙女也会让人有审美疲劳，所以只有外在是不够的，最好能有才情，以及广泛的兴趣。&lt;/p&gt;

&lt;p&gt;真正的爱情需要两个人一起培育，当两个人遇到矛盾和问题时，能否有效解决它们是维持长久爱情的基本能力。&lt;/p&gt;

&lt;p&gt;那么怎么去判断一个人是否合适呢？&lt;/p&gt;

&lt;p&gt;作者说：我觉得，一个合适的人会让你看到和得到全世界，而一个不合适的人会让你失去全世界。&lt;/p&gt;

&lt;p&gt;最后作者谨慎的说了一句：我说的可能只是1%，你要自己去体会剩下的99%。&lt;/p&gt;

&lt;h4 id=&quot;态度吴军-11&quot;&gt;《态度》吴军&lt;/h4&gt;

&lt;h4 id=&quot;第23封信&quot;&gt;第23封信&lt;/h4&gt;

&lt;h4 id=&quot;女儿似乎有些洁癖对那些夸夸其谈不愿意做事的人不齿对那些只愿意一个人做事不愿意合作的人反感作者跟女儿聊了聊社会上与不同人相处之道&quot;&gt;女儿似乎有些‘洁癖’，对那些夸夸其谈、不愿意做事的人不齿，对那些只愿意一个人做事不愿意合作的人反感，作者跟女儿聊了聊社会上与不同人相处之道&lt;/h4&gt;

&lt;p&gt;女儿想锻炼领导力，作者说，领导力第一是组织和工作能力，一件事交给你，你能否将它分解，组织大家完成。第二是团结大多数人，让每个人能够各尽其才，发挥作用。&lt;/p&gt;

&lt;p&gt;作者把周围处事的人分为4种，第一种人和我们关系非常好，做事总能配合我们而且能力很强。第二种和第一种一样但能力有限。第三种人有能力能做事，但他未必喜欢你，也很难相处。第四种人，没有能力也无法相处。&lt;/p&gt;

&lt;p&gt;很多人在生活中把不是自己朋友的人排斥在外，这导致了诸多问题，虽然他们很能干又聪明，但未必让我们喜欢。&lt;/p&gt;

&lt;p&gt;作者举例塔列朗，他的外交水平非常高，但这个人既不对任何人忠诚，也不是任何意义上的君子。&lt;/p&gt;

&lt;p&gt;我们在工作和生活中，会遇到很多这样的人，如果能和他们处好关系，是完成一项伟大事业的前提。我们要看到别人的长处，并且善用他们的长处。&lt;/p&gt;

&lt;p&gt;事实上，只要大家能设定一个共同目标，把彼此的利益绑在一起，遇到矛盾，对事不对人，就能团结大多数人，把我们的事情做好。&lt;/p&gt;

&lt;p&gt;另外我们也不能，因为对我们关系好，就凡事偏袒他们，也有有违做事的原则。&lt;/p&gt;

&lt;p&gt;工作和生活中有很多和我们不一样的人，争得他们的支持和帮助，是我们将来生活和事业成功的必要条件。&lt;/p&gt;

&lt;p&gt;作者最后告诉女儿，如果有幸成为领导，对同事和下属不要有洁癖，观察他们的长处发挥他们的特长。&lt;/p&gt;

&lt;h4 id=&quot;态度吴军-12&quot;&gt;《态度》吴军&lt;/h4&gt;

&lt;h4 id=&quot;第24封信说的是远离小人&quot;&gt;第24封信，说的是远离小人。&lt;/h4&gt;

&lt;p&gt;作者拿楚国的费无极，他特别爱讨好皇帝，讨好的目的都是为了自己的小小私利损害了国家长远的利益，最后被吴国灭了。小人通常都很聪明，要想远离小人的得自己有对欲望的克制力。&lt;/p&gt;

&lt;h4 id=&quot;第25封信说的是有效沟通&quot;&gt;第25封信，说的是有效沟通。&lt;/h4&gt;

&lt;p&gt;作者说沟通要以对方了解你的意思为目的，这样才有效，因此我们要以和对方共同认知的基础上讨论问题，或者用对方能听懂且马上能理解的语言来讨论，只有这样沟通效率才最高。否则就像一些比较糟糕的老师那样，只管自己讲课，讲完了就认为学生都听懂了，实际上学生们完全没有理解。&lt;/p&gt;

&lt;h4 id=&quot;态度吴军-13&quot;&gt;《态度》吴军&lt;/h4&gt;

&lt;h4 id=&quot;第27封信作者从上帝喜欢笨小孩出发讲了自知的重要性&quot;&gt;第27封信，作者从“上帝喜欢笨小孩”出发讲了自知的重要性。&lt;/h4&gt;

&lt;p&gt;作者说自己每个周末都花时间手工整理投资信息而不是用Excel等各种工具自动生成报表，是因为自己在手工工作的过程，就是一边整理一边思考的过程。任何工作都需要总结、反思，才能进步。&lt;/p&gt;

&lt;p&gt;做事情不能投机取巧，这个世界里上帝喜欢笨人。为什么呢？原因很简单，上帝不喜欢比自己聪明的人。这其实反映了一个人是否对自己的能力和本领有正确的认识。如果一个人觉得自己很了不起，觉得自己什么都做的到，无意中自认为高上帝一等。事实通常是人总会高估自己，以致于不断犯错而不自知。&lt;/p&gt;

&lt;h4 id=&quot;态度吴军-14&quot;&gt;《态度》吴军&lt;/h4&gt;

&lt;h4 id=&quot;第34封信聊写论文&quot;&gt;第34封信，聊写论文。&lt;/h4&gt;

&lt;h4 id=&quot;作者从写论文里得出几条经验&quot;&gt;作者从写论文里得出几条经验。&lt;/h4&gt;

&lt;p&gt;大部分人都是从前人的经验中得出自己的成果，所以介绍自己站在前人的工作是比较重要，讲清楚前人做了什么非常重要。&lt;/p&gt;

&lt;p&gt;自己做了什么工作得出什么结论是论文的灵魂，要实事求是不要啰嗦更不要夸大。一些急于求成的学者为了发表具有轰动效果的结果常常作假、以致于最后不再被同行认同。&lt;/p&gt;

&lt;p&gt;好论文不仅要得出自己的结论，还要比较自己为什么比前人做的好。实验结果需要前人的实验数据，以及自己的实验结果，这样才能有清晰的比较。&lt;/p&gt;

&lt;p&gt;论文实验数据的比较一定是要使用同行能够得到的数据。而不是一些非相关性的数据。这样在比较时才能让橘子和橘子比，而不至于造成橘子去和苹果比，削减了论文的质量和信服力。&lt;/p&gt;

&lt;h4 id=&quot;最后作者对写论文一定不要做的三件事&quot;&gt;最后作者对写论文一定不要做的三件事。&lt;/h4&gt;

&lt;h4 id=&quot;第一吹牛夸大自己的成果&quot;&gt;第一吹牛夸大自己的成果。&lt;/h4&gt;

&lt;p&gt;很多人喜欢宣传自己解决了一个天大的难题。其实同行对研究的背景和意义都非常清楚，意义是否重大，无须费太多口舌。大话，废话，除了占用篇幅对论文没有任何帮助。&lt;/p&gt;

&lt;h4 id=&quot;第二贬低同行&quot;&gt;第二贬低同行。&lt;/h4&gt;

&lt;p&gt;一些人为了显示自己的工作多么有水平，自吹自擂，把前人的工作贬得一无是处。其实是否做的好，有了数据自然明白了，不需要抬高自己贬低别人。&lt;/p&gt;

&lt;h4 id=&quot;第三把次要的东西写的篇幅比较多次要的东西篇幅多会让读者造成困惑&quot;&gt;第三把次要的东西写的篇幅比较多。次要的东西篇幅多会让读者造成困惑。&lt;/h4&gt;

&lt;p&gt;最后两个细节比较重要。别人的数据和观点要写明出处。对于写论文做研究过程中给予帮助的人要鸣谢。&lt;/p&gt;

&lt;h4 id=&quot;态度吴军-15&quot;&gt;《态度》吴军&lt;/h4&gt;

&lt;h4 id=&quot;第35封信聊了聊做事前算概率这事&quot;&gt;第35封信，聊了聊做事前算概率这事。&lt;/h4&gt;

&lt;p&gt;作者说，很多人决定是否继续读书的理由是能否找到更好的工作。只有少数人每天所做的事情都是他们喜欢的工作。&lt;/p&gt;

&lt;p&gt;其实一个人是否喜欢一件事，也是要尝试一下才知道的，特别是年轻的时候。&lt;/p&gt;

&lt;p&gt;作者说了一个故事，有个年轻人要离开家乡去世界闯荡，离开前一个老人给了他三封信。年轻人到国外打开了第一封信，里面就简单地写了几个字“往前走，去闯”。于是他便义无反顾去奋斗了。&lt;/p&gt;

&lt;p&gt;不过他的面前困难重重，人生地不熟，求学的道路也不顺利，他经历了失败也常常被人嘲笑，当他觉得坚持不下去时，打开了第二封信。信里内容依然很简单，“别灰心，继续闯”。于是这个年轻人又振作起来，艰辛地一步步往前走，最终闯出了一片天地。&lt;/p&gt;

&lt;p&gt;很多年后他回首已走过的路，有喜悦有悲伤，有成功有失败，虽然所得不少，但代价也巨大。他不知道自己走的路对不对，于是他打开了第三封信，信中依然只有几个字，“随缘，别后悔”&lt;/p&gt;

&lt;p&gt;回顾一个人成长的过程，其中的酸甜苦辣、各种滋味只有自己年能体会。&lt;/p&gt;

&lt;p&gt;年轻人做决定应该三思而行，但对于想清楚的事，做起来就不要犹豫。&lt;/p&gt;

&lt;p&gt;其实很多事在我们做成之后回头看只有5%不到的成功概率甚至更低，如果我们一开始就算概率，很多事就不会开始去做。&lt;/p&gt;

&lt;p&gt;最后作者说，努力了，至少还有一个希望，放弃了，则永远不可能有希望。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>励志英文(六) In Your 20's These Things Matter More Than You Think</title>
   <link href="http://www.luzexi.com/2020/04/26/%E5%8A%B1%E5%BF%97%E8%8B%B1%E6%96%876"/>
   <updated>2020-04-26T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/04/26/励志英文6</id>
   <content type="html">&lt;p&gt;这是我最近读了很多遍的英文文章，文章中作者是女生，已经41岁了，她回忆起20几岁时所做的事情，说了说如果自己能回去其实可以做的更好，她把应该在20几岁时该怎么生活才能拥有更好的未来写了下来。&lt;/p&gt;

&lt;p&gt;我读了很多遍后感触颇多，自己应该多向20多岁的人学习，保持乐观、保持积极、保持开放、保持勇往直前的心态。&lt;/p&gt;

&lt;p&gt;英文的单词都不难，本人英文只有高考水平，大家大可放心阅读，基本都是些平时常用的单词。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;原文如下：&lt;/p&gt;

&lt;h6 id=&quot;in-my-twenties-i-had-no-idea-what-would-matter-long-term-im-a-smart-person-but-i-didnt-think-much-about-my-future-i-lived-moment-to-moment-without-much-thought-or-planning-instead-i-put-my-energy-into-what-i-thought-mattered-at-the-time-getting-good-grades-dating-friends-going-out-to-bars-and-my-appearance-were-top-of-the-list&quot;&gt;In my twenties, I had no idea what would matter long term. I’m a smart person, but I didn’t think much about my future. I lived moment to moment, without much thought or planning. Instead, I put my energy into what I thought mattered at the time. Getting good grades, dating, friends, going out to bars, and my appearance were top of the list.&lt;/h6&gt;

&lt;h6 id=&quot;now-at-41-with-the-benefit-of-hindsight-i-can-see-that-some-of-my-decisions-mattered-a-lot-if-i-could-go-back-and-do-my-twenties-again-id-use-my-energy-in-quite-a-different-way&quot;&gt;Now, at 41, with the benefit of hindsight, I can see that some of my decisions mattered a lot. If I could go back and do my twenties again, I’d use my energy in quite a different way.&lt;/h6&gt;

&lt;p&gt;Here are some things that are more important long-term than I realized:&lt;/p&gt;

&lt;h6 id=&quot;developing-your-marketable-skills&quot;&gt;Developing your marketable skills&lt;/h6&gt;

&lt;p&gt;I was accepted into two amazing Master’s programs. The topics were exciting, but I turned them both down for a teaching position. I’d already spent 5 years at university and it felt irresponsible studying for so long. I’ll do my Masters later, I thought. I should probably be an adult and take this job.&lt;/p&gt;

&lt;p&gt;What I didn’t realize is how much harder it is to study later in life. I know people who are studying while parenting and working, but it’s tough. Life gets busier and more complex.&lt;/p&gt;

&lt;p&gt;Not everyone needs to get a degree, of course, but the world is competitive. Getting the highest level of education you are able to (and want to) can give you an advantage. Having a tertiary education or specialized training makes it easier to get work later, and the jobs available to you are often more interesting, creative, and satisfying.&lt;/p&gt;

&lt;h6 id=&quot;spending-your-time-wisely&quot;&gt;Spending your time wisely&lt;/h6&gt;

&lt;p&gt;I spent a lot of energy in my early 20’s on relationships that I knew were going nowhere. Most of my free time was taken up by my boyfriends — time I could have spent exploring hobbies, going on adventures with friends, meeting new people, and pursuing my own interests.&lt;/p&gt;

&lt;p&gt;I knew early on I wasn’t going to marry these guys. The time I invested in those relationships was mostly wasted. Instead of keeping my boyfriends happy, I could have used that time to work on becoming the person I wanted to be.&lt;/p&gt;

&lt;p&gt;That’s one of your main jobs in this time of life. In my 20’s I thought I’d just “find myself” but it doesn’t work like that. What I’ve realized is you don’t find yourself, you create yourself. You decide who you want to be, what kind of life you want, and create that for yourself.&lt;/p&gt;

&lt;h6 id=&quot;saying-yes-or-no-to-love&quot;&gt;Saying yes or no to love&lt;/h6&gt;

&lt;p&gt;Learning what makes a good relationship matters. At 23, I got engaged after six months of dating. Looking back, I barely knew my ex, but it didn’t feel like it mattered. It felt romantic and intense. It felt like the next logical stage in my life — mid twenties, get married, have kids, get a house…&lt;/p&gt;

&lt;p&gt;I didn’t know about stages of love, red flags, toxic patterns of communication, how to deal with conflict well, chemistry and compatibility, or how to make a marriage work. I was very green and it mattered more than I realized.&lt;/p&gt;

&lt;p&gt;Who you choose to say “yes” to makes a huge impact on your life for many years. Arm yourself with knowledge to choose well.&lt;/p&gt;

&lt;h6 id=&quot;facing-your-demons&quot;&gt;Facing your demons&lt;/h6&gt;

&lt;p&gt;Like many young people, I had issues left over from childhood that I didn’t want to face. I thought I was doing what was best: forget it and move on! But by not dealing with my past, I made life harder for myself than it needed to be. 
Not dealing with my problems had consequences. In my social life and relationships, it meant I worked too hard to please people. In my work and study, it meant I pushed myself to exhaustion.&lt;/p&gt;

&lt;p&gt;It wasn’t until a few years ago that I realized how important it is to deal with and take responsibility for your own issues. Ignoring your past issues can leave you vulnerable to depression, anxiety, low self-worth, and low self-awareness.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(四) 《如何有效整理信息》</title>
   <link href="http://www.luzexi.com/2020/04/25/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B04"/>
   <updated>2020-04-25T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/04/25/读书笔记4</id>
   <content type="html">&lt;h4 id=&quot;如何有效整理信息&quot;&gt;《如何有效整理信息》&lt;/h4&gt;

&lt;p&gt;看了序，我喜欢看序，因为我认为序是全书的精髓所在，所以每次都认真看。&lt;/p&gt;

&lt;p&gt;通过序了解到，本书介绍的是作者自己经过10年的新闻学和记者的工作总结出一套有效将信息保留在脑中并能灵活运用的方法。&lt;/p&gt;

&lt;p&gt;他把它称为“一元化笔记法”，简单来说就是，平日里只需将信息保存在一册可以反复翻阅的笔记本中。&lt;/p&gt;

&lt;p&gt;对就是这么简单，因为简单而实用。作者很自信的说，这种方法能让任何人半永久性地一次性解决“无法活用自己身边的信息”这一长期烦恼。&lt;/p&gt;

&lt;p&gt;就让我来看看他所说的，一元化笔记法有多神奇。&lt;/p&gt;

&lt;h4 id=&quot;如何有效整理信息-1&quot;&gt;《如何有效整理信息》&lt;/h4&gt;

&lt;p&gt;把所有日常中你认为重要的信息都记录在本子上，确实很麻烦，但只有麻烦过我们才能记得住这个印象，结果并不是关键，过程才是。&lt;/p&gt;

&lt;p&gt;把所有重要的信息记录后，如果能记上标签的话就更好。因为“创意不过是点子的组合”，通过乍一看之下毫无关联的信息，结合起来后我们常常可以获得新的视角和想法。&lt;/p&gt;

&lt;p&gt;让笔记本成为你脑中知识生产素材库，你脑中有的信息全都可以映射到笔记本中。&lt;/p&gt;

&lt;p&gt;“并不是将知识生产的素材汇集在笔记本中，而是将你觉得对你造成极大触动的信息集中在笔记本中，让它们成为知识生产的素材”&lt;/p&gt;

&lt;h4 id=&quot;如何有效整理信息-2&quot;&gt;《如何有效整理信息》&lt;/h4&gt;

&lt;p&gt;把所有重要的内容都放入一本笔记本中，这样就不会在需要查看时找不到记录的位置。&lt;/p&gt;

&lt;p&gt;其中和工作没什么关系却十分有趣的剪报和笔记大都会成为创意的灵感来源。&lt;/p&gt;

&lt;p&gt;笔记本的规格可以根据自己情况不同而选择，你用完的笔记本越多就会产生更多的积极情绪来改善你的学习效率和学习方式。&lt;/p&gt;

&lt;p&gt;当本子中有大量你感兴趣的信息和你思考的想法时，你能使用的任何信息都是自己记录和挑选的有趣信息，这会让你充满自信。&lt;/p&gt;

&lt;p&gt;“构建这种知识生产系统后，通过持续不断的操作，你可以解决平日琐碎的问题，也可以构思出重要的创意。”&lt;/p&gt;

&lt;p&gt;不管怎么说，无论是否亲笔记录在笔记本中，最重要的是如何以你喜欢的方式折腾重要的信息，并且保留至你可以随时拿到的地方。&lt;/p&gt;

&lt;h4 id=&quot;如何有效整理信息-3&quot;&gt;《如何有效整理信息》&lt;/h4&gt;

&lt;p&gt;作者介绍了时序化笔记和索引化规则，可以简单归纳为，每次记录笔记本的内容要有日期标签，这样在回忆时就能前后对比从而迅速记起当时的情景。索引化则更多的是把模糊的索引内容快速写在笔记本上，当想要查找时也能根据这个线索找到想要的信息。&lt;/p&gt;

&lt;p&gt;一元化笔记法，重要的关键就是简单容易，在简单容易之上会有些小技巧让记忆更深刻，回忆更容易。&lt;/p&gt;

&lt;p&gt;“我们可能记不清当初为什么要剪下支离破碎的资料。但是通过将资料案时间顺序排列并加入日期标签的做法，我们可以弄清资料之间的关系”&lt;/p&gt;

&lt;p&gt;“主要抓住线索，就能找到绝大多数被纳入笔记本的信息”&lt;/p&gt;

&lt;h4 id=&quot;如何有效整理信息-4&quot;&gt;《如何有效整理信息》&lt;/h4&gt;

&lt;p&gt;没忍住一口气把剩下的看完了。中间部分实在有点啰嗦，但后面确实有很多精髓值得我学习。&lt;/p&gt;

&lt;p&gt;这本书主要是告诉我们把生活中想到的事，感悟，喜欢的事，觉得重要的事，记录下来，有多么重要。&lt;/p&gt;

&lt;p&gt;重要到能为你今后的工作生活中有一个可供自己参考的水平线。首先我们花了很多精力去记录了自己的所思所想，其次后面的日子中反复阅读可以加深对自己的理解，并且能产生出新的思想。&lt;/p&gt;

&lt;p&gt;主动做这些事是最有效面对人生的态度。有这种主动态度我们就有很大的潜力突破自己的思维局限。因为我们在做笔记的同时不停得在思考，思想的不断输出是最好的学习方式。&lt;/p&gt;

&lt;p&gt;记笔记本身就是一件努力的事，多年后回头看努力的自己也是我们信心的最好基石。&lt;/p&gt;

&lt;p&gt;至于记笔记技巧，其实细节可以自己慢慢体会，最关键是你能否体会到作者因为记笔记而得到的实实在在的自信和成就感。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(三) 《微精通》</title>
   <link href="http://www.luzexi.com/2020/04/20/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B03"/>
   <updated>2020-04-20T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/04/20/读书笔记3</id>
   <content type="html">&lt;h4 id=&quot;微精通&quot;&gt;《微精通》&lt;/h4&gt;

&lt;h4 id=&quot;欢迎来到博识天堂速学者的俱乐部&quot;&gt;欢迎来到博识天堂，速学者的俱乐部。&lt;/h4&gt;

&lt;p&gt;作者指出一万个小时练习的错误理论。“好像要学会任何有价值的东西，都需要成年累月的学习才行似的，忘了它们吧”&lt;/p&gt;

&lt;p&gt;一万个小时练习的概念在开始时就吓退了大部分人。就像作者举例的那样。小时候喜欢摆弄组装收音机，但爸爸说只要我弄懂晶体管工作原理就给我买，我立刻兴趣索然，因为我喜欢组装收音机并乐在其中，可是解释工作原理是成年人的事，对我太难太陌生。&lt;/p&gt;

&lt;p&gt;学校里那种学习太枯燥，太乏味，难度太大，是绝对不行的。我们应该从更容易的方式入门，再解剖各个技能点，逐个击破。&lt;/p&gt;

&lt;p&gt;速学者从一开始学习时就想好了要如何建立反馈和回报的激励机制。如果在学习过程中不能获得一系列微小的成功，我们很容易失去信心，甚至放弃。&lt;/p&gt;

&lt;p&gt;要让一样事情成为微精通有6个点。&lt;/p&gt;

&lt;h4 id=&quot;1入门技巧用最容易最快速掌握的方式入门&quot;&gt;1，入门技巧。用最容易最快速掌握的方式入门。&lt;/h4&gt;

&lt;h4 id=&quot;2协同障碍要学一个东西通常里面包含了很多个技能这些技能通常融合在一起很难但拆分开来相对简单&quot;&gt;2，协同障碍。要学一个东西通常里面包含了很多个技能，这些技能通常融合在一起很难，但拆分开来相对简单。&lt;/h4&gt;

&lt;p&gt;特别是学习那些看起来很难的东西时，速学者通常会无意识的拆分每个元素，把注意力集中在某个单个元素上，这使得他们看上去似乎很有学问。&lt;/p&gt;

&lt;h4 id=&quot;3背景支持为你学习的道路扫除一切障碍比如购买你喜欢的质量好的适合的装备甚至多买几个放在不同的房间里我们记忆随时拿来用&quot;&gt;3，背景支持。为你学习的道路扫除一切障碍。比如购买你喜欢的，质量好的，适合的装备。甚至多买几个放在不同的房间里，我们记忆随时拿来用。&lt;/h4&gt;

&lt;p&gt;不只装备，还可以是环境，以及人。包括请私人教练，去更好的公司，更好氛围的学校等。对我们更有帮助，更能迅速解决心头疑惑。&lt;/p&gt;

&lt;h4 id=&quot;4成功回报时刻关注回报激励机制受到别人关注也好给自己一点小奖励也好激励机制是我们走向成功的催化剂&quot;&gt;4，成功回报。时刻关注回报激励机制，受到别人关注也好，给自己一点小奖励也好，激励机制是我们走向成功的催化剂。&lt;/h4&gt;

&lt;h4 id=&quot;5可复验性把技能元素拆分开来后对一些简单的任务不断重复练习就能感觉到自己越来越自信没有简单的任务我们就主动塑造一个比如写小说很难但写100字微小说可以算简单任务攀登珠穆朗玛峰很难但攀岩运动可以算简单任务我们可以用简单任务不断重复练习的方式来增强自信心&quot;&gt;5，可复验性。把技能元素拆分开来后，对一些简单的任务不断重复练习，就能感觉到自己越来越自信。没有简单的任务，我们就主动塑造一个，比如写小说很难，但写100字微小说可以算简单任务。攀登珠穆朗玛峰很难，但攀岩运动可以算简单任务。我们可以用简单任务不断重复练习的方式，来增强自信心。&lt;/h4&gt;

&lt;h4 id=&quot;6可实验性要主动尝试不同的方向作者举例自己在划皮艇时一开始一直使用j形滑桨方式后来咨询专家才知道要用c形其实应该早就尝试c形l形z形方式这样就能体会到更多的可能而不是照本宣科一味专研操作指南&quot;&gt;6，可实验性。要主动尝试不同的方向。作者举例自己在划皮艇时，一开始一直使用J形滑桨方式，后来咨询专家才知道要用C形，其实应该早就尝试C形L形Z形方式，这样就能体会到更多的可能，而不是照本宣科一味专研操作指南。&lt;/h4&gt;

&lt;h4 id=&quot;微精通-1&quot;&gt;《微精通》&lt;/h4&gt;

&lt;p&gt;作者认为，学习有很多种，微精通是动态的，而阅读书本是静态的。一旦在微精通中找到其中的门道，我们就能够通过读书来提高自己的技能水平。&lt;/p&gt;

&lt;p&gt;作者用世界一流乐器制作者举例，他在年轻时兴致上来了想做把小提琴。于是看了乐器制作书，但看不懂，沮丧一阵后他决定根据自己想象的样子做一把，结果做出来惨不忍睹。但是回头再翻书看，他终于明白了书中所说的意思，于是又做了一把，这次结果相当不错。&lt;/p&gt;

&lt;p&gt;其实学习的本质是观察，它能让我们清楚如何抓住每一步的重点所在。&lt;/p&gt;

&lt;p&gt;作者在学习合气道时有过这样的体会，在进攻和防守时都要找到自己的重心，这样就提高了平衡性，进而增强了自信心，然后更确信自己重心是稳固可控的，进而更加信心十足，从而进入一个良性循环。&lt;/p&gt;

&lt;p&gt;学习也是一样，我们首先最重要的是找到重点，比如煎蛋卷的重点是火候，劈材的重点是斧柄感受的重量等。&lt;/p&gt;

&lt;p&gt;因此我们学习时首先要找到的是重点，否则我们不知道在每个技术步骤里该投入多少精力，于是每个环节都拼命练习，搞得疲惫不堪。“这就是很多人失败的原因，他们不知道哪里是该重点努力的地方”&lt;/p&gt;

&lt;p&gt;作者又说，除了找重点，我们还要找乐趣，这能让学习效率翻倍。&lt;/p&gt;

&lt;p&gt;他举例说自己曾想写部小说，折腾了一整年也没有进展，焦虑加沮丧。后来朋友建议他写微小说，这样会很有趣，于是他尝试每天写几篇几百字的微小说，兴致盎然。随着时间的推移，他掌握了写长篇小说的技巧，最后真的写成了小说并出版了。&lt;/p&gt;

&lt;p&gt;不仅如此，我们最好能动用我们的所有感官来一个多维度的感知，这样乐趣和记忆深度就会放大。&lt;/p&gt;

&lt;p&gt;他举例自己在学习国际法这种极其枯燥的领域时，不仅仅只是每天啃书本，他还去会见当事人，与人聊天，挖掘真实的情况，直接去避难中心了解事件进展，跟深海鱼民唠嗑，还买了他们的鱼，甚至找有一个废弃的岛屿建立自己的国家，从而学习国际法时有更加深刻的认识和有趣的故事。&lt;/p&gt;

&lt;h4 id=&quot;微精通-2&quot;&gt;《微精通》&lt;/h4&gt;

&lt;p&gt;这章中作者指出，人类是学习型动物，我们一生不可避免地遇到各种变化，因此我们必须活到老学到老，以保持大脑的基本功能正常运转。
其实大脑神经系统的生长和改善是贯穿人的一生的，它并不只是某个时期的运作方式，这也是为什么人们常说，现在行动并不晚的原因。&lt;/p&gt;

&lt;p&gt;用大脑可塑性一流学者，迈克尔.莫山尼奇博士话说：&lt;/p&gt;

&lt;p&gt;大脑塑造是一个物理过程。灰质实际上能变厚也能缩小，神经连接可得到锻炼并加强，也可被削弱或切断。&lt;/p&gt;

&lt;p&gt;大脑上这些物理上的改变会体现在能力上的改变，如果我们学会了一个新舞步，就是在大脑上形成一个新的“连接”神经通路。而忘了某人的名字，则说明负责这部分的记忆连接退化或者断开了。因此‘用进废退’这个词语描述我们的大脑非常准确。&lt;/p&gt;

&lt;p&gt;不过在现在发达的科技下，智能系统越来越多的环境下，我们甚至什么都不需要做，光靠电脑屏幕就能实现媒体的生活方式，这使我们在很多维度都无法得到锻炼，特别是多维度的感官，这使得我们开始经历一场小小的灾难，即无法更容易的受到各维度上的感知训练。&lt;/p&gt;

&lt;p&gt;因此作者认为我们需要更多采用祖先狩猎采集式的自由博识的生活方式来增强我们自己的多维度感知力。&lt;/p&gt;

&lt;p&gt;他举证神经学专家赫布律(Hebb’s law)在一项基础研究的发现：&lt;/p&gt;

&lt;p&gt;某件事经历的越丰富，花样越多，对感官的刺激越大，这件事情在大脑中留下的印象就越深刻，大脑里形成的连通性就越强。这其实就是增强记忆的比较好的方法，也是提高学习效率的好方法，更是抗衰老的有效手段。&lt;/p&gt;

&lt;p&gt;如果有一天我们总是依靠人工智能的自动驾驶活着，就容易丧失定位，辨识方位的能力。相反如果我们用自由博识的方式，靠人脑的定位、辨识去旅行，即使使用导航也能让大脑得到锻炼，这样大脑就会充满活力。&lt;/p&gt;

&lt;p&gt;下面的内容作者开始讲述提高学习效率的方式。&lt;/p&gt;

&lt;h4 id=&quot;1锻炼记忆&quot;&gt;1.锻炼记忆。&lt;/h4&gt;

&lt;p&gt;记忆方法有很多，作者推荐了记笔记的方式：持续做内容精确的笔记，并附上照片或图片，回忆起来就会容易得多，因为大部分需要回忆的内容都已呈现在眼前。&lt;/p&gt;

&lt;h4 id=&quot;2书本靠后感官向前&quot;&gt;2.书本靠后，感官向前。&lt;/h4&gt;

&lt;p&gt;人们认为以文字为基础、书本为中心、再加评论的学习模式比其他方法的效率要更高，所以忽略了探寻和观察的作用。这是有巨大问题的，这是导致学习效率下降的重要原因，也是很多人最后自我怀疑的原因，即‘为什么，我这么努力而他却能轻轻松松超过我。’&lt;/p&gt;

&lt;p&gt;作者举例如果在我们吃培根前听到它在锅里发出‘滋滋’声，那会使得我们觉得嚼起来更加美味。同样的，如果我们听到不同节奏的音乐，就会联想到各种各样的色彩，这叫色听联觉。因为我们人类大脑能够同时记录声觉、触觉、嗅觉、甚至痛觉，这些感官互相沟通、交融、激发，这些多维度的感官刺激能建立起更加牢固的大脑神经连接，从而让我们对某件事印象更加深刻，记忆更加牢固。&lt;/p&gt;

&lt;p&gt;还有更快更好的学习方法
作者又举证迈克尔.莫山尼奇博士（就是最前面说大脑连接结构的那个）的话：&lt;/p&gt;

&lt;p&gt;如果事物足够新奇，或者我们足够投入，学起来就会更快。&lt;/p&gt;

&lt;p&gt;在这种状态下，刺激神经生长出来的脑源性神经营养因子，会变得更加活跃，并建立更强、更深、更好的神经连接。&lt;/p&gt;

&lt;p&gt;这里必须跳过一些内容与作者在后面的文章中提到的‘心流’联系在一起来说。&lt;/p&gt;

&lt;p&gt;‘心流’概念由匈牙利心理学家 米哈里.契克森米哈赖提出的，他为此写了本书《心流》正闲置在我的书柜上，其中心思想是:&lt;/p&gt;

&lt;p&gt;在完全沉浸于活动中时，我们会进入一种意识不到时间的精神状态。我们专注力增强，感觉充满活力。&lt;/p&gt;

&lt;p&gt;处于心流状态时，我们的思维批判闸门关闭了，因而学得更快。&lt;/p&gt;

&lt;p&gt;作者举例自己在不断尝试用钢笔或毛笔画出完美书法圆圈时，自己能轻松进入半冥想、全身关注的心流状态。在其他技能练习，或语言学习时经过10-15分钟的时间也同样可以轻松达到‘心流’状态。&lt;/p&gt;

&lt;p&gt;为了促进心流状态的实现，作者对心流状态练习了很多次，提出了练习心流几个状态的心得：&lt;/p&gt;

&lt;p&gt;他用数字标识心流状态，0-无聊，1-放松，2-可控，3-心流，4-兴奋，5-焦虑。用挑战的方式去练习心流体验，首先选出一个有能力攻克的挑战项目，如果这个项目超出了你的能力范围一点点，你会发现自己变得很兴奋，如果超出了你能力范围很多，则会变得很焦虑。反之如果挑战完全处于掌控中，则会觉得感到无聊。&lt;/p&gt;

&lt;p&gt;我们为了达到‘3-心流状态’，在练习时，如果觉得这个目标稀松平常，那就加大难度，如果觉得紧张不安，那就降低难度，放慢脚步逐步提高技能熟练度。如果到达并实现了‘3-心流’，那就太棒了，它能让人欣喜若狂，时间仿佛停止在那里，不知不觉4、5个小时就过去了，学习效率就在这段时间里处于比较高的水平。&lt;/p&gt;

&lt;h4 id=&quot;学习策略的偏见&quot;&gt;学习策略的偏见&lt;/h4&gt;

&lt;p&gt;这里谈到了偏见，他说，大脑的可塑性是可以延伸到我们的学习方式上的，换句话说，我们可以学会如何更好的学习。&lt;/p&gt;

&lt;p&gt;其实我们在成长的过程中，我们面对的表扬与责备、鼓励与打击的模式会形成一种不好的偏见，即好像某种学习策略会好于另一种。但事实上这种默认的标准未必对你有效。&lt;/p&gt;

&lt;p&gt;作者举例自己曾经也是偏见学习者中的一位，曾自认为自己是‘实践学习者’，所以买了台新相机后把一本厚1英尺的使用手册丢在一边，看都不看一眼就去摆弄相机。几个月后自己意识到他依然只了解到相机功能的一点点皮毛，于是只好坐下来仔细查看使用手册，并测试相应功能，以后的日子里每次按下按钮或试过新功能后，都会回查使用手册，在手册中努力学习每个相机的功能。&lt;/p&gt;

&lt;p&gt;读者能否感知到，其实多种学习策略的储备会增强思维流畅性，使得我们能自由转换不同的想法，不同的视角，从而看到问题和事物的不同方面，即便它们是相互对立的，这不仅丰富了我们的眼界也加快了学习的速度也加深了知识的深度。&lt;/p&gt;

&lt;p&gt;最后我想提一下作者对增强自信心的看法。
他认为‘微精通’(即博识与速学)能帮助你一步步树立信心，不仅让你拥有丰富的技能，更能储备多种学习策略，使得学习效率更高。其中自信心在学习中起着非常重要的作用。&lt;/p&gt;

&lt;p&gt;引用科幻作者 A.E.van Vogt 的话说，“自信是一种简单的能力，是被问到时候能清晰响亮地说出自己名字的能力，是向人温暖致意的能力，是及时向他人表示热烈祝贺的能力。”&lt;/p&gt;

&lt;p&gt;换句话说，意思是，忘掉内心的感觉，专心表现就行。&lt;/p&gt;

&lt;p&gt;其实自信就是看你愿不愿意确立目标，并把更多的精力投入到目标需要做的事情上。如果我们缺乏自信，我们可能会认为自己不具备足够的能力，这时我们需要做就是回顾下自己的目标，并且行动起来，投入更多的努力。&lt;/p&gt;

&lt;p&gt;作者说大多数人都没有滔滔不绝自信满满交流或演说的能力，但经过努力克服内心的胆怯后也能达到这种程度。一些极端的例子比如口吃者经过不断投入更多的努力最终成为天才演说家，常在我们生活中发生。&lt;/p&gt;

&lt;p&gt;因此缺乏自信往往意味着我们只是不去尝试，不去尝试的投入更多的努力，最后导致失败的原因往往是根本没有尝试过，出师未捷身先死。&lt;/p&gt;

&lt;h4 id=&quot;微精通-3&quot;&gt;《微精通》&lt;/h4&gt;

&lt;p&gt;如果你获得的微精通都在同一个领域，那么你离专家就不远了。
我们每天做同样的事会显得枯燥，可以在设定个目标，这样每天练习就会更加行之有效、完成后转向新的微精通或新的东西。&lt;/p&gt;

&lt;p&gt;协同效应在职业领域也很重要，作者举例诺贝尔奖得主的故事，他们对专业领域外的领域也很感兴趣，从中获得了很多崭新的视角，这反过来增强了他们的专业性。&lt;/p&gt;

&lt;p&gt;沃尔特.阿尔瓦雷斯博士就是跨领域通识教育的实践者。他儿子路易斯是个科学天才，但他却把他送进一所艺校和手工学校，以此增加路易斯更多额外的兴趣，路易斯最终于1968年获得诺贝尔物理学奖。&lt;/p&gt;

&lt;p&gt;你懂得越多，越能对事物提出不同的观点，对你也就越有利。不同领域间的知识常常以惊人的方式互相取长补短。&lt;/p&gt;

&lt;h4 id=&quot;微精通-4&quot;&gt;《微精通》&lt;/h4&gt;

&lt;p&gt;任何领域的学习都是懂得越多越容易掌握，因为技能，观点，洞察力都是有交叉的。作者举例三岛由纪夫学习剑道时说自己在黑带考试时运用了写作时的心态。&lt;/p&gt;

&lt;p&gt;博识让你的思维更加开放，更乐于学习，信心也变得十足。面对困难时会更加努力而不是退缩，你会努力从不同角度看问题，而不是单一的固执己见，从而你会对问题有了各种佯攻和侧翼攻击的试探，而不只是正面强攻，这将最终导向胜利。&lt;/p&gt;

&lt;p&gt;精通比微精通更加需要时间的积累。精通的关键是持之以恒，关乎如何坦然面对瓶颈，是否能长时间保持专注。&lt;/p&gt;

&lt;p&gt;为了能够长时间的重复练习，而不会无聊，通常大师们都会建立各种日常工作的仪式感，以此来实现长期的目标。&lt;/p&gt;

&lt;p&gt;仪式感使得枯燥乏味的重复工作变得有趣，给你更多期待或者提高忍耐度。
作者举例说比如你可以有一把“邮件查看帽”每次带上帽子就表示开始查看和回复邮件，也可以是一杯埃塞俄比亚咖啡，泡完咖啡就开始一天的工作。&lt;/p&gt;

&lt;p&gt;精通需要适度热情但也不能太热衷或痴迷，这会导致一叶障目不见森林。另一方面，精通必须不断实践，为了练习摄影技巧，你得拍成千上万的照片才行。&lt;/p&gt;

&lt;h4 id=&quot;微精通-5&quot;&gt;《微精通》&lt;/h4&gt;

&lt;p&gt;作者认为我们曾被错误得教导，只专注一件事是人生成功和幸福的关键。其实所有成功人士的百分百专注只是他们生活中的一小部分，大多数时间里他们要么休闲要么从其他地方汲取营养。&lt;/p&gt;

&lt;p&gt;大多数诺贝尔奖获得者都被认为是专注于自己领域的专家，但他们其实同时学习艺术、手工、音乐，只是没有张扬而已。因为他们的博识需要被特意的隐藏起来，以免引发圈外人的误解。&lt;/p&gt;

&lt;p&gt;博学家用他们掌握的多领域专业知识和兴趣来反哺自己的专业，为他们人生提供更多的视角和能量。&lt;/p&gt;

&lt;p&gt;这里提到了世界观的宽窄。作者认为，世界观越狭窄，外部世界对他们来说越没有意义。反之，见多识广的人对生活有更浓的兴趣和更强的能力，并且更快乐也更坚强。&lt;/p&gt;

&lt;p&gt;这里提到了一个在德国集中营里呆过的人维克多.弗兰尔克，他在极其痛苦的环境中活出了自我，并著作了《活出生命的意义》这一经典书籍。
以此引申到我们生活中总会遭遇伤痛和失败，在重整旗鼓的过程中，精神世界的宽与窄直接决定了生与死。
作者说，比起单一的狭窄思维，博闻广识的大脑帮助我们搜索到更多的资源来应对生活的打击。&lt;/p&gt;

&lt;p&gt;接下来他举例很多科学家是如何通过多种业余兴趣来反哺自己的专业的。&lt;/p&gt;

&lt;p&gt;很多科学家，用玩乐的态度对待问题，这也是他们有很多兴趣的原因，微精通本质上是一项好玩的活动。&lt;/p&gt;

&lt;h4 id=&quot;微精通-6&quot;&gt;《微精通》&lt;/h4&gt;

&lt;p&gt;作者提出一个问题，“这个世界迫切需要更多科学类毕业生和专家吗？”许多科学家和技术经理，以及CEO给出答案是“不需要”。&lt;/p&gt;

&lt;p&gt;大量毕业生和专家，缺乏组织、沟通、和管理能力，所以他们真正想要的是有智慧、有创造精神、并具有多项微精通的人。&lt;/p&gt;

&lt;p&gt;作者认为，在创造和创新上，基础知识越广，模式和案例的来源就越多，这说明，新思想的潜在基础越大，观点多样性和创造力就越大。&lt;/p&gt;

&lt;p&gt;作者也认为不只是发明家和作家需要创新，我们每个人每项工作，每个生活细节都需要创新，创新创造会带来更多乐趣，从而形成良性循环。&lt;/p&gt;

&lt;p&gt;开放思想在你的学习生活有非常重要的作用，一般现代专家都害怕跨出自己的领域，同时与外来入侵者殊死搏斗，这会导致固步自封，应该反过来，注重开放和分享，才能学到更多东西成为强者。&lt;/p&gt;

&lt;p&gt;微精通使你不再自我设限，不再认为自己兴趣幼稚或肤浅，慢慢的你将对任何事都饶有兴趣，这将开启有趣、专注和博学的人生。&lt;/p&gt;

&lt;h4 id=&quot;微精通-7&quot;&gt;《微精通》&lt;/h4&gt;

&lt;h4 id=&quot;作者接下来会介绍39项自己学习过的微精通以此来让读者明白什么是微精通怎样识别微精通怎样学习微精通&quot;&gt;作者接下来会介绍39项自己学习过的微精通，以此来让读者明白，什么是微精通，怎样识别微精通，怎样学习微精通。&lt;/h4&gt;

&lt;p&gt;每一项微精通，作者都从6个方面去解释，这6方面为，入门技巧、协同障碍、背景支持、成功回报、可复验性、可实验性。&lt;/p&gt;

&lt;h4 id=&quot;首先作者说的是手绘漂亮的线稿草图&quot;&gt;首先作者说的是手绘漂亮的线稿草图。&lt;/h4&gt;

&lt;p&gt;入门技巧为找张喜欢的线稿临摹，用黑色美工笔临摹画线和握笔。&lt;/p&gt;

&lt;p&gt;协同障碍是画的好玩和画的像，这两项技能需要协调，精华部分可以画的显眼点，无聊的东西可以省略，这样画起来更开心，效果也更好。&lt;/p&gt;

&lt;p&gt;背景支持是买好的贵的素描本，和喜欢钢笔，让自己画画的时候更舒适心情更美好。&lt;/p&gt;

&lt;p&gt;成功回报，无论多基础画完一张就有成就感，画过很多张摊在桌子上能作为更大的激励和动力。&lt;/p&gt;

&lt;p&gt;可复验性为，画很多次后，自己对物体的观察啊更加清晰透彻，同一个物体可以画出很多样子。&lt;/p&gt;

&lt;p&gt;可实验性为，可以尝试扩展到画水彩画，墨水画等会让自己有更多的绘画技巧。&lt;/p&gt;

&lt;h4 id=&quot;接着作者又说了皮划艇翻滚技巧的微精通&quot;&gt;接着作者又说了皮划艇翻滚技巧的微精通。&lt;/h4&gt;

&lt;p&gt;他说很多人做了大量滑桨后才开始学翻滚，其实完全可以先学翻滚，这样会有更大的成就感。&lt;/p&gt;

&lt;p&gt;入门技巧为，在码头或泳池边扶着栏杆练习，这样让自己感觉安全。&lt;/p&gt;

&lt;p&gt;协同障碍是，臀部发力和滑桨技巧的配合。反复练习真切感受，臀部、船体、滑桨三者之间的关联。&lt;/p&gt;

&lt;p&gt;背景支持，买好一点的装备让自己更舒服，包括，耳塞，干式潜水服，头盔，好的桨。&lt;/p&gt;

&lt;p&gt;成功回报，是在湍急的水流中知道了怎么翻滚就不会害怕了。也成就了自己的绝活可以在朋友面前一展身手。&lt;/p&gt;

&lt;p&gt;可实验性为，可以扩展到单手翻滚，双人翻滚等多项技能。&lt;/p&gt;

&lt;h4 id=&quot;第三个是测量洞有多深&quot;&gt;第三个是测量洞有多深。&lt;/h4&gt;

&lt;p&gt;入门技巧是，用石头扔进去，计时并用重力加速度计算深度。&lt;/p&gt;

&lt;p&gt;可以找一块高尔夫球大小的石头，这是装备。&lt;/p&gt;

&lt;p&gt;回报是，可以轻松的知道洞的深度。&lt;/p&gt;

&lt;p&gt;可实验性为，可扩展到，这条河有多宽？这棵树有多高？用简单的工具和物理知识就能计算得到。&lt;/p&gt;

&lt;h4 id=&quot;微精通-8&quot;&gt;《微精通》&lt;/h4&gt;

&lt;p&gt;第四个案例是砍树，很大很粗的那种。&lt;/p&gt;

&lt;p&gt;入门技巧，用斧子砍出V形缺口。&lt;/p&gt;

&lt;p&gt;协同障碍，如何用平衡用斧子上的力量，让斧子的自重发挥作用，这样砍起来会很轻松。&lt;/p&gt;

&lt;p&gt;背景支持，选适合大小的斧子，一把好斧子能省掉你很多力气。&lt;/p&gt;

&lt;p&gt;成功回报，砍原木很有成就感，飞出来的木块还能当柴烧。&lt;/p&gt;

&lt;p&gt;可复验性，斧子的保养和磨砺很容易，能吸引人不断练习，提高砍伐技能。&lt;/p&gt;

&lt;p&gt;可实验性，可以扩展到建造一座小木屋。&lt;/p&gt;

&lt;h4 id=&quot;第五个爬绳技术&quot;&gt;第五个爬绳技术&lt;/h4&gt;

&lt;p&gt;入门技巧，关键在脚不在手，双脚呈s形绕在绳子上，一上一下，高位时向下踩，低位时脚做支撑。&lt;/p&gt;

&lt;p&gt;协同障碍，脚和手的协调配合。&lt;/p&gt;

&lt;p&gt;背景支持，粗绳和细绳，先粗后细。&lt;/p&gt;

&lt;p&gt;成功回报，爬绳是很棒的全身运动，能锻炼上身、小腿及脚部肌肉的力量和协调性。&lt;/p&gt;

&lt;p&gt;可复验性，每次爬绳可以计时，练的足够好了去参加爬绳比赛。&lt;/p&gt;

&lt;p&gt;可实验性，爬绳能带你走进令人陶醉的攀爬世界，爬上巨树或山顶，一览众山小。&lt;/p&gt;

&lt;h4 id=&quot;第六个站上冲浪板&quot;&gt;第六个站上冲浪板&lt;/h4&gt;

&lt;p&gt;入门技巧，下水前练习靠脚跳到板上，而不是膝盖。在地上，肚子着地板，做俯卧撑状，眼睛向前，使劲拱起背，跳起来，前脚落在与肩膀平齐的地方。&lt;/p&gt;

&lt;p&gt;协同障碍，如何把握海浪向上推力，和冲浪板的移动速度，使之同时达到最佳时机，太早不稳定，太晚会失去对板的控制。&lt;/p&gt;

&lt;p&gt;背景支持，买耳塞，防寒泳衣，好的冲浪板，去海浪浪形好的海岸，因为复杂的海浪学习起来更困难。&lt;/p&gt;

&lt;p&gt;成功回报，冲浪会让你很爽，它是拯救你枯燥乏味的办公室日常的一剂良药。&lt;/p&gt;

&lt;p&gt;可复验性，冲浪会上瘾，叫上三五好友可以玩很久。&lt;/p&gt;

&lt;p&gt;可实验性，可扩展到不用冲浪板的人体冲浪。&lt;/p&gt;

&lt;h4 id=&quot;第七个自选话题演讲15分钟&quot;&gt;第七个自选话题演讲15分钟&lt;/h4&gt;

&lt;p&gt;众所周知，公开演讲比死亡更可怕，却无意是大多是职业需要掌握的关键技能之一。&lt;/p&gt;

&lt;p&gt;入门技巧，马上真实描述你的所思所想、所感、所做。如果觉得紧张、没准备好，就直接告诉听众。说清楚自己这方面知识的局限性，到底懂多少，这个主题让你联想到什么，给你什么样的感觉。&lt;/p&gt;

&lt;p&gt;协同障碍，中途卡壳马上请教听众。热情、尊重、谦恭对待自愿出来帮助你的人。首先问他们名字、职业、来自哪里，大家喜欢讲一些个人经历。然后仔细谨慎的提问，引出他们的内容。千万不要敷衍了事或带有嘲讽口吻妄加评论。&lt;/p&gt;

&lt;p&gt;背景支持，救命稻草是扭转情绪。感到不舒服时，静下心来，查看研究，这样才能获得更多客观可信的资料来帮助我们演讲。&lt;/p&gt;

&lt;p&gt;成功回报，公开演讲是一件美妙的事情。只要尽量放松，和听众积极互动，不去绞尽脑汁回忆讲稿内容，就算讲不是特别好，大家也会觉得你很有趣，会开怀大笑。&lt;/p&gt;

&lt;p&gt;可复验性，一有机会就主动演讲，这是增强信心和提升幽默感的方法，而自信和幽默通常是成功演讲的关键。&lt;/p&gt;

&lt;p&gt;可实验性，公开演讲会带你进入即兴表演的领域。在即兴表演中要努力让合作伙伴表现优秀，而不是炫耀自己。大家尽力成就对方。&lt;/p&gt;

&lt;h4 id=&quot;第八个砌墙&quot;&gt;第八个砌墙&lt;/h4&gt;

&lt;p&gt;入门技巧，关键在于水泥。按比例混合沙子和水泥再加入增塑剂。&lt;/p&gt;

&lt;p&gt;协同障碍，慢慢搅拌混合均匀是关键，太希砖块会在层层重压下下沉、错位。太稠太硬会使得砖块间难以粘合。&lt;/p&gt;

&lt;p&gt;背景支持，增塑剂、细沙、泥刀、水平测量仪。&lt;/p&gt;

&lt;p&gt;成功回报，在“升起墙体”的工作中，人们还能感受到一种近乎原始的成就感。&lt;/p&gt;

&lt;p&gt;可复验性，砌墙时能大量重复砌砖基本动作。&lt;/p&gt;

&lt;p&gt;可实验性，可扩展到五花八门的砌筑方式，甚至造一栋房屋。&lt;/p&gt;

&lt;h4 id=&quot;第九个剧本对话写作&quot;&gt;第九个剧本对话写作&lt;/h4&gt;

&lt;p&gt;入门技巧，对话者的身份地位的体现，用戏剧性的有趣对话来揭示身份地位的差异。精彩的对话，要赋予对话人物各自的地位层次，这不是指社会地位，而是交互地位。&lt;/p&gt;

&lt;p&gt;协同障碍，依靠有意义的内容来平衡对话中的现实主义。重要的是：不论地位高中低，人物都可能快乐而积极，也可能刻薄而消极。&lt;/p&gt;

&lt;p&gt;背景支持，要考虑角色间的关系，吸引我们的是关系而非怪诞的角色。例如一个变态连环杀手的怪异模样能让我们关注5分钟，如果给他设置几个受害者，以及一个追踪他的警察，我们会迷上好几小时。&lt;/p&gt;

&lt;p&gt;成功回报，想想自己认识的人，考虑他们身份地位方面的关系。&lt;/p&gt;

&lt;p&gt;可复验性，可阅读莫里斯的《男人女人行为观察》了解更多关于身份地位的观点。&lt;/p&gt;

&lt;p&gt;可实验性，可扩展到生活中的观察。有些人终日占据高地位，另一些人能在不同地位间转换。例如船王奥纳西斯，地位几乎凌驾一切，在丘吉尔登船时，为了避免冲突，他切换到低地位的状态。&lt;/p&gt;

&lt;p&gt;《微精通》&lt;/p&gt;

&lt;h4 id=&quot;第十个黏土头骨制作&quot;&gt;第十个黏土头骨制作&lt;/h4&gt;

&lt;p&gt;入门技巧，让自己局限于头部的关注。&lt;/p&gt;

&lt;p&gt;协同障碍，把头骨做的栩栩如生和把它做得像。&lt;/p&gt;

&lt;p&gt;背景支持，使用好的粘土。&lt;/p&gt;

&lt;p&gt;成功回报，可以逗孩子开心。&lt;/p&gt;

&lt;p&gt;可复验性，随身带着橡皮泥，随时见到的人制作头骨模型。&lt;/p&gt;

&lt;p&gt;可实验性，制作不同动物的头骨模型。&lt;/p&gt;

&lt;h4 id=&quot;第十一个烘培手工面包&quot;&gt;第十一个烘培手工面包&lt;/h4&gt;

&lt;p&gt;入门技巧，时间掌控和保湿。&lt;/p&gt;

&lt;p&gt;协同障碍，平衡烤箱的时间和温度。&lt;/p&gt;

&lt;p&gt;背景支持，好面粉。超级高劲面粉或自己喜欢的&lt;/p&gt;

&lt;p&gt;成功回报，做出美味的面包。&lt;/p&gt;

&lt;p&gt;可复验性，每周做一次面包让家里储备充足。&lt;/p&gt;

&lt;p&gt;可实验性，做各种面包实验。&lt;/p&gt;

&lt;h4 id=&quot;第十二个空中舞剑嗡嗡作响&quot;&gt;第十二个空中舞剑嗡嗡作响&lt;/h4&gt;

&lt;p&gt;入门技巧，轻握剑柄。&lt;/p&gt;

&lt;p&gt;协同障碍，用力与放松。&lt;/p&gt;

&lt;p&gt;背景支持，买一把木剑。&lt;/p&gt;

&lt;p&gt;成功回报，立于晨曦之中，一次次地挥剑而下，心绪交织于宁静和更加专注之间。&lt;/p&gt;

&lt;p&gt;可实验性，可以继续学习其他剑术。&lt;/p&gt;

&lt;h4 id=&quot;微精通最后一部分微精通你的人生&quot;&gt;《微精通》最后一部分，微精通你的人生&lt;/h4&gt;

&lt;p&gt;他指出，”一切始于兴趣。如果不感兴趣，我们不会关注，更不会学习。快乐的人有很多兴趣，人也很风趣。”&lt;/p&gt;

&lt;p&gt;作者用老虎与笼子来比喻我们的精神世界。他说老虎就是你拥有的所有被束缚的能量和热情，但它被关在一个笼子里。我们需要一把钥匙打开笼子，只要你能找到打开笼子的钥匙，所有的优柔寡断、怠惰懒散、缺乏进取都会不复存在。&lt;/p&gt;

&lt;p&gt;这个笼子就是被我们以各种理由或自卑束缚起来的精神世界。它有两道门，一道是大一点的，看着很容易钻出去，很诱人。另一道则小一点窄一点，看着似乎钻出去会很痛苦。&lt;/p&gt;

&lt;h6 id=&quot;起初我们都选择了大一点的那道门大门通向满是食物的地方一开始看上去自由的不可思议我们狼吞虎咽着所有食物变得又大又肥又笨拙令人厌恶慢慢地时间长了我们发现自己其实进入了另一个更大的笼子只是这个笼子大一点但还是笼子于是开始查看出口在哪里不久查看到一个出口无需钥匙但非常小和窄而且低自己无法穿越我们开始厌恶所有精美的食物可是又沉迷其中无法自拔&quot;&gt;起初我们都选择了大一点的那道门。大门通向满是食物的地方，一开始看上去自由的不可思议。我们狼吞虎咽着所有食物，变得又大又肥又笨拙令人厌恶。慢慢地时间长了，我们发现自己其实进入了另一个更大的笼子，只是这个笼子大一点，但还是笼子。于是开始查看出口在哪里，不久查看到一个出口，无需钥匙，但非常小和窄而且低，自己无法穿越，我们开始厌恶所有精美的食物，可是又沉迷其中无法自拔。&lt;/h6&gt;

&lt;h6 id=&quot;由于抵制不了美食的诱惑无法减轻体重我们的热情开始衰退我们变得疲惫衰老甚至害怕外面的世界与此同时我们开始意识到工作职业专业化其实是与这个有活力和热情的世界交流的手段但是太晚了然后我们又想其实能与世界交流了又如何那也只是另一个笼子于是就说服了自己继续呆在这个笼子里&quot;&gt;由于抵制不了美食的诱惑，无法减轻体重，我们的热情开始衰退，我们变得疲惫、衰老、甚至害怕外面的世界。与此同时我们开始意识到工作、职业、专业化其实是与这个有活力和热情的世界交流的手段，但是太晚了。然后我们又想，其实能与世界交流了又如何，那也只是另一个笼子，于是就说服了自己继续呆在这个笼子里。&lt;/h6&gt;

&lt;h6 id=&quot;我们另一些人清醒的稍微彻底一些开始意识到要控制自己我们开始研究怎么通过这道又小又窄又低的门经过很长一段时间克制自己对美食的欲望后通过了小门小门通往一个食物贫瘠的地方那里只足够支撑我们观察和学习同样一段时间后我们发现自己仍然身处在另一个笼子里笼子里也有一个小出口不过我们保持着纤瘦的身材一下子就穿了过去这之后便是丰富多彩的世界了我们不停的穿梭在不同的笼子之间虽然仍然在笼子中但相对来说我们已经是自由的了不再被某个特定的笼子所束缚&quot;&gt;我们另一些人清醒的稍微彻底一些，开始意识到要控制自己，我们开始研究怎么通过这道又小又窄又低的门。经过很长一段时间克制自己对美食的欲望后，通过了小门，小门通往一个食物贫瘠的地方，那里只足够支撑我们观察和学习。同样一段时间后我们发现自己仍然身处在另一个笼子里，笼子里也有一个小出口，不过我们保持着纤瘦的身材，一下子就穿了过去。这之后便是丰富多彩的世界了，我们不停的穿梭在不同的笼子之间，虽然仍然在笼子中，但相对来说我们已经是自由的了，不再被某个特定的笼子所束缚。&lt;/h6&gt;

&lt;p&gt;实现了自由后，我们的精力和热情再也没有减退，反而更加强烈，我们心态变得积极、开放、包容，对一切都充满兴趣。&lt;/p&gt;

&lt;p&gt;作者说，“生活的目标是，利用掌握的知识和技能跳出樊笼，过你能过的充实的日子。那意味着，你要变得更加博识，开放，有活力。”，“微精通能让你对任何期待的东西都饶有兴趣且积极投入，让兴趣保持和扩展，并且轻松流畅的转换，这样你就能走出笼子”&lt;/p&gt;

&lt;h3 id=&quot;接着作者开始阐述多个自我的理念&quot;&gt;接着作者开始阐述多个自我的理念&lt;/h3&gt;

&lt;p&gt;作者很认同多重人格心理学，它认为我们心里住着很多个不同的自我，每个内在的自我喜欢的东西都不一样，有的喜欢看电影，有的爱看书，有的爱运动，有的喜欢出去玩，有的喜欢技术，有的醉心于商业和赚钱。但是我们很多时候都束缚着她们，强制要求她们别捣蛋，专心伺候其中某一个自我，比如技术或者商业赚钱。&lt;/p&gt;

&lt;p&gt;我们试图通过扼杀多个自我来达到强化某一个自我，这种遏制各种自我的发展的手段，让事情变得更糟。&lt;/p&gt;

&lt;h6 id=&quot;作者举例熊猫功夫中的片段愤怒的师傅说道内心平和内心平和内心平和其实这里传达出的信息是我们无法仅仅用语言或一厢情愿的想法来控制自己的情绪状态我们需要进入的一个不同的自我而不是平静下来如果这时能转换到一个新的自我一个喜欢专研当前事物的自我我们就能立即平静下来&quot;&gt;作者举例《熊猫功夫》中的片段，愤怒的师傅说道，“内心平和！内心平和！内心平和！”，其实这里传达出的信息是，我们无法仅仅用语言或一厢情愿的想法来控制自己的情绪状态。我们需要进入的一个不同的自我，而不是平静下来，如果这时能转换到一个新的自我，一个喜欢专研当前事物的自我，我们就能立即平静下来。&lt;/h6&gt;

&lt;p&gt;很多情况下，我们如果处在不合适的自我人格中去面对当前的困难，就会加重可能导致疾病的压力。如果这时我们拥抱多种自我而非对抗，我们就能积极寻找切换自我的方式，这样就能更好更有效的处理当下的问题，微精通就能帮助我们做到这一点。&lt;/p&gt;

&lt;h6 id=&quot;作者举例一个水兵的例子他叫尼克在服役一段时间后他发现自己变得更加叛逆这个叛逆自我以毁坏其他自我的努力成果为乐因此他开始寻找各种解决办法他想着如何转换这个叛逆的自我幸运的是他找到了艺术艺术是对日常乏味生活的叛逆表现他开始画画开始吹口琴在服役期间一直忙着吹口琴和画画这两种技能小有所成后人们议论纷纷他渐渐意识到按部就班的工作是扼杀创造力灵魂的陷阱他需要更多的自由来开发有所发展的天赋&quot;&gt;作者举例一个水兵的例子，他叫尼克，在服役一段时间后，他发现自己变得更加叛逆，这个叛逆自我以毁坏其他自我的努力成果为乐。因此他开始寻找各种解决办法，他想着如何转换这个叛逆的自我，幸运的是他找到了艺术，艺术是对日常乏味生活的叛逆表现，他开始画画，开始吹口琴。在服役期间一直忙着吹口琴和画画，这两种技能小有所成后，人们议论纷纷。他渐渐意识到，按部就班的工作是扼杀创造力灵魂的陷阱，他需要更多的自由来开发有所发展的天赋。&lt;/h6&gt;

&lt;h6 id=&quot;退伍后尼克继续绘画并开始雕塑事业他雕塑各种罪犯头像引起了不少人的注意他又参加乐队这开辟了他艺术生涯第二条发展路线每项活动都有助于其他活动的发展例如商业音乐工作带给他更多佣金从而能够支持雕塑事业尼克进入一个全新的自我不但转换了叛逆的自我的方向也从更大更广阔的世界中得到了肯定&quot;&gt;退伍后尼克继续绘画，并开始雕塑事业，他雕塑各种罪犯头像，引起了不少人的注意，他又参加乐队，这开辟了他艺术生涯第二条发展路线。每项活动都有助于其他活动的发展，例如商业音乐工作带给他更多佣金，从而能够支持雕塑事业。尼克进入一个全新的自我，不但转换了叛逆的自我的方向，也从更大更广阔的世界中得到了肯定。&lt;/h6&gt;

&lt;p&gt;作者说，临时打破各种自我的运作，它们将回头重新争夺地位这将导致我们更多的‘内耗’。多个自我应该有效得到尊重，这样才能实现顺畅的转换，微精通就是其中最有效的方式。我们应该把多个自我想象成仍性的家庭成员，为了得到关注而打斗，直到被认可、受到尊重。&lt;/p&gt;

&lt;p&gt;作者又说，微精通其实和朋克精神很相似，朋克精神强调“为什么不试试呢？”，微精通也是，与其傻等理想时刻的到来，不如发现一个简单的版本，一项微精通，多玩多实验。&lt;/p&gt;

&lt;h3 id=&quot;微精通防止了悲观主义&quot;&gt;微精通防止了悲观主义&lt;/h3&gt;

&lt;p&gt;作者指出，悲观主义是个骗子，它吸引我们的注意力，却不让我们参与改变。悲观主义的口头禅是，“不值得”。我们常常会自我设限、以及自我破坏，只要有一点点微小的迹象，我们就觉得自己做不好，“我没时间”、“我只是个业余爱好者”。&lt;/p&gt;

&lt;h6 id=&quot;微精通实践了兴趣达到了博识防止了悲观主义它让我们着眼于建立发展自己的兴趣才干技能我们会发现真正的成功是在于不断完善自我其他一切都只是偶发事件钱只能带来一时的快感买一幢房子和很多汽车无论有无修饰你仍然是个傻瓜你无法购买思考方式而有了兴趣和博识则不同它能给你带来持久的乐趣知识成就感自信和充实的生活&quot;&gt;微精通实践了兴趣，达到了博识，防止了悲观主义，它让我们着眼于建立发展自己的兴趣、才干、技能，我们会发现真正的成功是在于不断完善自我，其他一切都只是偶发事件。钱只能带来一时的快感，买一幢房子和很多汽车，无论有无修饰，你仍然是个傻瓜，你无法购买思考方式。而有了兴趣和博识则不同，它能给你带来持久的乐趣、知识、成就感、自信和充实的生活。&lt;/h6&gt;

&lt;p&gt;作者认为，如果一个人学到了东西，关心了别人，磨练了技能，传授知识给了他人，发展了才华，找到并做好了事情，那么这个人的生活就可以看作是一种成功。&lt;/p&gt;

&lt;h3 id=&quot;最后作者希望读者能有更大的视野和蓝图&quot;&gt;最后作者希望读者能有更大的视野和蓝图&lt;/h3&gt;

&lt;p&gt;如果我们学习和实践了很多兴趣，就会变得更加多才多艺、更快乐、更成功。相反，如果我们以牺牲其他自我为代价，来过渡发展某种自我则会变得扭曲。如果过渡开发某方面能力，可能导致我们缺乏同理心，到头来我们可能会轻视他人，这是导致不幸的根本原因。过渡开发某方面的副作用很大，我们应该开发所有的方面，为每个自我一个寻找一个微精通，通过这样的方式释放自己。&lt;/p&gt;

&lt;h6 id=&quot;拥有自由博识的世界观会带你涉足很多事情这能让你在涉猎过程中探知自己真实而惊人的潜力&quot;&gt;拥有自由博识的世界观会带你涉足很多事情，这能让你在涉猎过程中探知自己真实而惊人的潜力。&lt;/h6&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(四十四) 自由博识</title>
   <link href="http://www.luzexi.com/2020/04/19/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A844"/>
   <updated>2020-04-19T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/04/19/思路探讨44</id>
   <content type="html">&lt;p&gt;这是《微精通》中最后一部分内容的提炼与总结。我们在前面的文章中介绍过前半部分的内容，前半部分作者更多强调的速学，而后半部分作者更强调的是博识，在这部分中作者希望读者能更多的释放自己的兴趣，不要让任何事情阻碍了自己兴趣的发展。他指出，”一切始于兴趣。如果不感兴趣，我们不会关注，更不会学习。快乐的人有很多兴趣，人也很风趣。”&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;作者用老虎与笼子来比喻我们的精神世界。他说老虎就是你拥有的所有被束缚的能量和热情，但它被关在一个笼子里。我们需要一把钥匙打开笼子，只要你能找到打开笼子的钥匙，所有的优柔寡断、怠惰懒散、缺乏进取都会不复存在。&lt;/p&gt;

&lt;p&gt;这个笼子就是被我们以各种理由或自卑束缚起来的精神世界。它有两道门，一道是大一点的，看着很容易钻出去，很诱人。另一道则小一点窄一点，看着似乎钻出去会很痛苦。&lt;/p&gt;

&lt;h6 id=&quot;起初我们都选择了大一点的那道门大门通向满是食物的地方一开始看上去自由的不可思议我们狼吞虎咽着所有食物变得又大又肥又笨拙令人厌恶慢慢地时间长了我们发现自己其实进入了另一个更大的笼子只是这个笼子大一点但还是笼子于是开始查看出口在哪里不久查看到一个出口无需钥匙但非常小和窄而且低自己无法穿越我们开始厌恶所有精美的食物可是又沉迷其中无法自拔&quot;&gt;起初我们都选择了大一点的那道门。大门通向满是食物的地方，一开始看上去自由的不可思议。我们狼吞虎咽着所有食物，变得又大又肥又笨拙令人厌恶。慢慢地时间长了，我们发现自己其实进入了另一个更大的笼子，只是这个笼子大一点，但还是笼子。于是开始查看出口在哪里，不久查看到一个出口，无需钥匙，但非常小和窄而且低，自己无法穿越，我们开始厌恶所有精美的食物，可是又沉迷其中无法自拔。&lt;/h6&gt;

&lt;h6 id=&quot;由于抵制不了美食的诱惑无法减轻体重我们的热情开始衰退我们变得疲惫衰老甚至害怕外面的世界与此同时我们开始意识到工作职业专业化其实是与这个有活力和热情的世界交流的手段但是太晚了然后我们又想其实能与世界交流了又如何那也只是另一个笼子于是就说服了自己继续呆在这个笼子里&quot;&gt;由于抵制不了美食的诱惑，无法减轻体重，我们的热情开始衰退，我们变得疲惫、衰老、甚至害怕外面的世界。与此同时我们开始意识到工作、职业、专业化其实是与这个有活力和热情的世界交流的手段，但是太晚了。然后我们又想，其实能与世界交流了又如何，那也只是另一个笼子，于是就说服了自己继续呆在这个笼子里。&lt;/h6&gt;

&lt;h6 id=&quot;我们另一些人清醒的稍微彻底一些开始意识到要控制自己我们开始研究怎么通过这道又小又窄又低的门经过很长一段时间克制自己对美食的欲望后通过了小门小门通往一个食物贫瘠的地方那里只足够支撑我们观察和学习同样一段时间后我们发现自己仍然身处在另一个笼子里笼子里也有一个小出口不过我们保持着纤瘦的身材一下子就穿了过去这之后便是丰富多彩的世界了我们不停的穿梭在不同的笼子之间虽然仍然在笼子中但相对来说我们已经是自由的了不再被某个特定的笼子所束缚&quot;&gt;我们另一些人清醒的稍微彻底一些，开始意识到要控制自己，我们开始研究怎么通过这道又小又窄又低的门。经过很长一段时间克制自己对美食的欲望后，通过了小门，小门通往一个食物贫瘠的地方，那里只足够支撑我们观察和学习。同样一段时间后我们发现自己仍然身处在另一个笼子里，笼子里也有一个小出口，不过我们保持着纤瘦的身材，一下子就穿了过去。这之后便是丰富多彩的世界了，我们不停的穿梭在不同的笼子之间，虽然仍然在笼子中，但相对来说我们已经是自由的了，不再被某个特定的笼子所束缚。&lt;/h6&gt;

&lt;p&gt;实现了自由后，我们的精力和热情再也没有减退，反而更加强烈，我们心态变得积极、开放、包容，对一切都充满兴趣。&lt;/p&gt;

&lt;p&gt;作者说，“生活的目标是，利用掌握的知识和技能跳出樊笼，过你能过的充实的日子。那意味着，你要变得更加博识，开放，有活力。”，“微精通能让你对任何期待的东西都饶有兴趣且积极投入，让兴趣保持和扩展，并且轻松流畅的转换，这样你就能走出笼子”&lt;/p&gt;

&lt;h3 id=&quot;接着作者开始阐述多个自我的理念&quot;&gt;接着作者开始阐述多个自我的理念&lt;/h3&gt;

&lt;p&gt;作者很认同多重人格心理学，它认为我们心里住着很多个不同的自我，每个内在的自我喜欢的东西都不一样，有的喜欢看电影，有的爱看书，有的爱运动，有的喜欢出去玩，有的喜欢技术，有的醉心于商业和赚钱。但是我们很多时候都束缚着她们，强制要求她们别捣蛋，专心伺候其中某一个自我，比如技术或者商业赚钱。&lt;/p&gt;

&lt;p&gt;我们试图通过扼杀多个自我来达到强化某一个自我，这种遏制各种自我的发展的手段，让事情变得更糟。&lt;/p&gt;

&lt;h6 id=&quot;作者举例熊猫功夫中的片段愤怒的师傅说道内心平和内心平和内心平和其实这里传达出的信息是我们无法仅仅用语言或一厢情愿的想法来控制自己的情绪状态我们需要进入的一个不同的自我而不是平静下来如果这时能转换到一个新的自我一个喜欢专研当前事物的自我我们就能立即平静下来&quot;&gt;作者举例《熊猫功夫》中的片段，愤怒的师傅说道，“内心平和！内心平和！内心平和！”，其实这里传达出的信息是，我们无法仅仅用语言或一厢情愿的想法来控制自己的情绪状态。我们需要进入的一个不同的自我，而不是平静下来，如果这时能转换到一个新的自我，一个喜欢专研当前事物的自我，我们就能立即平静下来。&lt;/h6&gt;

&lt;p&gt;很多情况下，我们如果处在不合适的自我人格中去面对当前的困难，就会加重可能导致疾病的压力。如果这时我们拥抱多种自我而非对抗，我们就能积极寻找切换自我的方式，这样就能更好更有效的处理当下的问题，微精通就能帮助我们做到这一点。&lt;/p&gt;

&lt;h6 id=&quot;作者举例一个水兵的例子他叫尼克在服役一段时间后他发现自己变得更加叛逆这个叛逆自我以毁坏其他自我的努力成果为乐因此他开始寻找各种解决办法他想着如何转换这个叛逆的自我幸运的是他找到了艺术艺术是对日常乏味生活的叛逆表现他开始画画开始吹口琴在服役期间一直忙着吹口琴和画画这两种技能小有所成后人们议论纷纷他渐渐意识到按部就班的工作是扼杀创造力灵魂的陷阱他需要更多的自由来开发有所发展的天赋&quot;&gt;作者举例一个水兵的例子，他叫尼克，在服役一段时间后，他发现自己变得更加叛逆，这个叛逆自我以毁坏其他自我的努力成果为乐。因此他开始寻找各种解决办法，他想着如何转换这个叛逆的自我，幸运的是他找到了艺术，艺术是对日常乏味生活的叛逆表现，他开始画画，开始吹口琴。在服役期间一直忙着吹口琴和画画，这两种技能小有所成后，人们议论纷纷。他渐渐意识到，按部就班的工作是扼杀创造力灵魂的陷阱，他需要更多的自由来开发有所发展的天赋。&lt;/h6&gt;

&lt;h6 id=&quot;退伍后尼克继续绘画并开始雕塑事业他雕塑各种罪犯头像引起了不少人的注意他又参加乐队这开辟了他艺术生涯第二条发展路线每项活动都有助于其他活动的发展例如商业音乐工作带给他更多佣金从而能够支持雕塑事业尼克进入一个全新的自我不但转换了叛逆的自我的方向也从更大更广阔的世界中得到了肯定&quot;&gt;退伍后尼克继续绘画，并开始雕塑事业，他雕塑各种罪犯头像，引起了不少人的注意，他又参加乐队，这开辟了他艺术生涯第二条发展路线。每项活动都有助于其他活动的发展，例如商业音乐工作带给他更多佣金，从而能够支持雕塑事业。尼克进入一个全新的自我，不但转换了叛逆的自我的方向，也从更大更广阔的世界中得到了肯定。&lt;/h6&gt;

&lt;p&gt;作者说，临时打破各种自我的运作，它们将回头重新争夺地位这将导致我们更多的‘内耗’。多个自我应该有效得到尊重，这样才能实现顺畅的转换，微精通就是其中最有效的方式。我们应该把多个自我想象成仍性的家庭成员，为了得到关注而打斗，直到被认可、受到尊重。&lt;/p&gt;

&lt;p&gt;作者又说，微精通其实和朋克精神很相似，朋克精神强调“为什么不试试呢？”，微精通也是，与其傻等理想时刻的到来，不如发现一个简单的版本，一项微精通，多玩多实验。&lt;/p&gt;

&lt;h3 id=&quot;微精通防止了悲观主义&quot;&gt;微精通防止了悲观主义&lt;/h3&gt;

&lt;p&gt;作者指出，悲观主义是个骗子，它吸引我们的注意力，却不让我们参与改变。悲观主义的口头禅是，“不值得”。我们常常会自我设限、以及自我破坏，只要有一点点微小的迹象，我们就觉得自己做不好，“我没时间”、“我只是个业余爱好者”。&lt;/p&gt;

&lt;h6 id=&quot;微精通实践了兴趣达到了博识防止了悲观主义它让我们着眼于建立发展自己的兴趣才干技能我们会发现真正的成功是在于不断完善自我其他一切都只是偶发事件钱只能带来一时的快感买一幢房子和很多汽车无论有无修饰你仍然是个傻瓜你无法购买思考方式而有了兴趣和博识则不同它能给你带来持久的乐趣知识成就感自信和充实的生活&quot;&gt;微精通实践了兴趣，达到了博识，防止了悲观主义，它让我们着眼于建立发展自己的兴趣、才干、技能，我们会发现真正的成功是在于不断完善自我，其他一切都只是偶发事件。钱只能带来一时的快感，买一幢房子和很多汽车，无论有无修饰，你仍然是个傻瓜，你无法购买思考方式。而有了兴趣和博识则不同，它能给你带来持久的乐趣、知识、成就感、自信和充实的生活。&lt;/h6&gt;

&lt;p&gt;作者认为，如果一个人学到了东西，关心了别人，磨练了技能，传授知识给了他人，发展了才华，找到并做好了事情，那么这个人的生活就可以看作是一种成功。&lt;/p&gt;

&lt;h3 id=&quot;最后作者希望读者能有更大的视野和蓝图&quot;&gt;最后作者希望读者能有更大的视野和蓝图&lt;/h3&gt;

&lt;p&gt;如果我们学习和实践了很多兴趣，就会变得更加多才多艺、更快乐、更成功。相反，如果我们以牺牲其他自我为代价，来过渡发展某种自我则会变得扭曲。如果过渡开发某方面能力，可能导致我们缺乏同理心，到头来我们可能会轻视他人，这是导致不幸的根本原因。过渡开发某方面的副作用很大，我们应该开发所有的方面，为每个自我一个寻找一个微精通，通过这样的方式释放自己。&lt;/p&gt;

&lt;h6 id=&quot;拥有自由博识的世界观会带你涉足很多事情这能让你在涉猎过程中探知自己真实而惊人的潜力&quot;&gt;拥有自由博识的世界观会带你涉足很多事情，这能让你在涉猎过程中探知自己真实而惊人的潜力。&lt;/h6&gt;
</content>
 </entry>
 
 <entry>
   <title>思路探讨(四十三) ‘随记’内容反思</title>
   <link href="http://www.luzexi.com/2020/04/12/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A843"/>
   <updated>2020-04-12T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/04/12/思路探讨43</id>
   <content type="html">&lt;p&gt;每次内心有想说的东西时，我都及时的记在了印象笔记里，这得从2018年10月份算起了。经过了这么长时间的记录，好几篇记录都被用到思路探讨里去，把当时记录的心思和感想都扩展了一下，更加细致和全面的描述在了‘思路探讨’系列文章里，但还有很大一部分的记录都没有能放入到文章中来，因为这些在我脑海中一闪而过的想法太不成熟，有些没有经过验证，另一些则完全是自己傲慢和偏见的想法，虽然是有问题的想法我也一直存着，因为有些我也不知道问题出在哪，等待我更加成熟和博识时再来揭开它们的真相。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;我随机挑选了一部分来做一下反思。文章中先标出记在随记里的文字，下面再用粗体起一行对这些记录做一些反思。​&lt;/p&gt;

&lt;p&gt;问题文字1：“能力太大就会很容易越过界，造成严重损失”&lt;/p&gt;

&lt;h6 id=&quot;这是我当时对人太过自信的反思当时我比较自大人会因为各种原因而自大有时甚至会闭起眼睛来告诉自己我是最强的文字虽然看起来是在客观评判某个想法但实质上在说我的能力还不够强大不要越过界事实却是如此内心的恐惧反应了真实的状况&quot;&gt;这是我当时对人太过自信的反思。当时我比较自大，人会因为各种原因而自大，有时甚至会闭起眼睛来告诉自己‘我是最强的’。文字虽然看起来是在客观评判某个想法，但实质上在说我的能力还不够强大，不要越过界，事实却是如此，内心的恐惧反应了真实的状况。&lt;/h6&gt;

&lt;p&gt;问题文字2：“枯燥，乏味，充斥着大部分生活，如果没有目标，很快就会陷入困惑，从而降低学习和工作效率”，“游戏太浪费时间和精力，能否找到有效的放松方式很关键，健康的放松方式和习惯让人非常受益，每个人每个阶段都不一样”，&lt;/p&gt;

&lt;h6 id=&quot;这是当时我正在寻找提高工作效率和学习效率时的记录我的本意是能否更加有效的结合学习工作娱乐三者当时学习工作很枯燥去玩会娱乐下又感觉浪费了精力感觉更累于是自己问自己该怎么办才好当时不知道该怎么自律怎么找节奏只知道瞎拼后来我才从读书打卡时间管理精力管理中逐渐找到了自律的节奏慢慢将学习工作生活打理的更加有条不紊&quot;&gt;这是当时我正在寻找提高工作效率和学习效率时的记录，我的本意是能否更加有效的结合学习、工作、娱乐三者，当时学习工作很枯燥，去玩会娱乐下又感觉浪费了精力感觉更累。于是自己问自己，该怎么办才好。当时不知道该怎么自律，怎么找节奏，只知道瞎拼，后来我才从读书、打卡、时间管理、精力管理中逐渐找到了自律的节奏，慢慢将学习、工作、生活打理的更加有条不紊。&lt;/h6&gt;

&lt;p&gt;问题文字3：“经济环境好，所以赚钱的途径多，可自创，可打工，可投资，可股票，可债券。反之，则逐渐转为自给自足模式，对外界的不信任程度增加，这也是贫穷的根源“&lt;/p&gt;

&lt;h6 id=&quot;当时是2018年10月份经济环境很不好的情况下记录了一些自己的想法当时正在看光荣与梦想第一本书正描述的是美国大萧条时的情景触景生情写了这些2018年底的经济到达了最糟糕的地步自己体会了从经济过分繁荣到经济逐渐下滑的过程明白了个人的盛衰很多时候都被大环境所影响后半段延伸思考到了封闭与开放的思想状态最近我才越来越对开放这个词语有了更多的理解这也是我读了微精通后才有的另一个人生视角即尽最大努力让自己博识尽量保持乐观最大限度理解和包容他人的视角用这样的方式和心态去面对工作和生活&quot;&gt;当时是2018年10月份，经济环境很不好的情况下记录了一些自己的想法。当时正在看《光荣与梦想》第一本书，正描述的是美国大萧条时的情景，触景生情写了这些。2018年底的经济到达了最糟糕的地步，自己体会了从经济过分繁荣到经济逐渐下滑的过程，明白了个人的盛衰很多时候都被大环境所影响。后半段延伸思考到了，封闭与开放的思想状态，最近我才越来越对‘开放’这个词语有了更多的理解，这也是我读了《微精通》后才有的另一个人生视角，即尽最大努力让自己博识、尽量保持乐观、最大限度理解和包容他人的视角，用这样的方式和心态去面对工作和生活。&lt;/h6&gt;

&lt;p&gt;问题文字4: ”要努力形成良性循环，才能成就目标。把恶性循环转为良性循环才是重点，我们永远都在处理恶性循环，如何改良就看一个人的能力了。越恶劣的改良过程越痛苦，不付出点代价，恶性循环是很难转为良性的。所以在还没形成恶性循环，或者在恶劣程度还没有太大的情况下要即使刹车或者寻找改良途径。“&lt;/p&gt;

&lt;h6 id=&quot;这是我写良性循环的建立与恶性循环的改善这篇文章的初期想法事情的缘由是在处理工作事务时遇到了一些工作习惯和交流方式上的问题看到了很多人包括自己在工作时不知不觉已经形成了一些习惯性思维这使得工作开展比较缓慢甚至遇到很多阻碍于是我一直在想能够用什么方法或者自己应该保持什么样的态度来解决这些问题这些问题大都涉及到人与人之间的交流氛围工作态度甚至生活态度我反思自己也同样不能陷入惯性思维或者成为那个恶性循环的参与者&quot;&gt;这是我写《良性循环的建立与恶性循环的改善》这篇文章的初期想法。事情的缘由是，在处理工作事务时遇到了一些工作习惯和交流方式上的问题，看到了很多人（包括自己）在工作时不知不觉已经形成了一些习惯性思维，这使得工作开展比较缓慢，甚至遇到很多阻碍。于是我一直在想能够用什么方法，或者自己应该保持什么样的态度来解决这些问题，这些问题大都涉及到人与人之间的交流氛围，工作态度，甚至生活态度。我反思自己也同样不能陷入惯性思维，或者成为那个恶性循环的参与者。&lt;/h6&gt;

&lt;p&gt;问题文字5: ”要达到无我的境界，什么是无我？“&lt;/p&gt;

&lt;h6 id=&quot;很多前辈都跟我说过这个词无我当时我正在想我的人生是否太狭隘了有没有更高的境界以及什么是更高的境界当时感觉自己比较固执虽然现在也还是固执经过升华和脱变也并没有逃脱得了人性的弱点无我成了我想弄明白人生更高境界的突破口直到现在也只是略懂我心中略懂的地步大概就是你做事的时候无论你的能力多强大都尽量把自己缩到最小不要去刷存在感不要去抢风头你的目标是把事做好其他的包括名誉利益功劳都主动地并且悄悄地给别人只做事有时连事也不亲自去做而是帮助别人去做事解决别人遇到的困难给他最好的装备最好的助手最多的资金自己什么都不要也没有心理障碍什么都可以接受唯一的目的就是事做成这就是我现在对无我的理解离真懂还有很大的距离&quot;&gt;很多前辈都跟我说过这个词‘无我’。当时我正在想我的人生是否太狭隘了，有没有更高的境界，以及什么是更高的境界。当时感觉自己比较固执，虽然现在也还是固执，经过升华和脱变也并没有逃脱得了人性的弱点。‘无我’成了我想弄明白人生更高境界的突破口，直到现在也只是略懂。我心中略懂的地步大概就是，你做事的时候，无论你的能力多强大都尽量把自己缩到最小，不要去刷存在感，不要去抢风头，你的目标是把事做好，其他的包括名誉、利益、功劳都主动地、并且悄悄地给别人，只做事，有时连事也不亲自去做，而是帮助别人去做事，解决别人遇到的困难，给他最好的装备最好的助手最多的资金，自己什么都不要，也没有心理障碍，什么都可以接受，唯一的目的就是事做成，这就是我现在对‘无我’的理解，离真​懂还有很大的距离。&lt;/h6&gt;

&lt;p&gt;问题文字6: “成功的道路上从来不缺放弃者，大多数人都放弃时，才是你真正的机会到来之时。”&lt;/p&gt;

&lt;h6 id=&quot;大家应该听说过当别人去玩耍的时候是你弯道超车的好机会这句话吧我们小时候老师们常讲给我们听随记里记录这句话时我不止疲惫生活还枯燥乏味写下这句话也就为了鼓励自己一下这句话显示出我当时的急躁总是把成功挂在嘴上这也导致相反的效果不但没有让自己继续坚持下去反而情绪很快就崩溃了请假回家休息了好多天&quot;&gt;大家应该听说过“当别人去玩耍的时候，是你弯道超车的好机会”这句话吧，我们小时候老师们常讲给我们听。随记里记录这句话时，我不止疲惫、生活还枯燥乏味，写下这句话也就为了鼓励自己一下。这句话显示出我当时的急躁，总是把‘成功’挂在嘴上，这也导致相反的效果，不但没有让自己继续坚持下去，反而情绪很快就崩溃了，请假回家休息了好多天。&lt;/h6&gt;

&lt;p&gt;问题文字7: “站在金字塔顶尖才能看到别人看不到的画面，所以很多时候，我们在做教育时不是为了赢在起跑线那么简单，而是为了让孩子看到别人看不到的那一面，才能真正做到与别人在层级上的差别，如果一直能在层级上做到差别化，那么视角、视野、眼光、思维方式就会大不一样，于是会从根本上与其他人拉开一个距离。“，”平级上的竞争基本都是苦涩的忍耐，如果差距到了层级上的距离，那么超越将变得异常困难。”&lt;/p&gt;

&lt;h6 id=&quot;这是我在当时在思考女儿教育问题时的想法当时的想法有点固执一直有竞争二字在脑海中挥之不去金字塔三字也充分体现了我对女儿教育理解的狭隘养育一个孩子很复杂是我想的太简单了同时也很简单是我想的太复杂了复杂在我们要用一辈子的时间去不断的观察理解引导一个人从孩子到成年简单在我们只要用心就可以用最真诚的心而不是虚荣和欺骗后来我明白了即使我们不在她们身边我们的所思所想以及行为方式也依然能够影响孩子们因为我们是孩子的父亲或母亲我们对她们的影响力最大即使是一个小小的眼神和动作&quot;&gt;这是我在当时在思考女儿教育问题时的想法。当时的想法有点固执，一直有‘竞争’二字在脑海中挥之不去，‘金字塔’三字也充分体现了我对女儿教育理解的狭隘。养育一个孩子很复杂，是我想的太简单了，同时也很简单，是我想的太复杂了。复杂在我们要用一辈子的时间去不断的观察、理解、引导一个人从孩子到成年，简单在我们只要用心就可以，用最真诚的心而不是虚荣和欺骗。后来我明白了，即使我们不在她们身边，我们的所思所想以及行为方式也依然能够影响孩子们，因为我们是孩子的父亲或母亲，我们对她们的影响力最大，即使是一个小小的眼神和动作。&lt;/h6&gt;

&lt;p&gt;问题文字7: “太用力的兴趣无法持续，太用力就会很累，难以维系持续的累”，“通常最后赢的那个人不是最聪明的，也不是最漂亮的，也不是最帅的，而是由于兴趣而坚持最久的那个。”&lt;/p&gt;

&lt;h6 id=&quot;写文字当时我正在对自己的兴趣爱好做总结因为总是有三天热度的这种情况所以在想为什么会有这种情况以及我该如何避免这问题直到今年年初时看了一本叫微习惯的书才有点明白过来它里面不只讲了如何正确建立新的习惯也分析了很多我们兴趣爱好正热头上的问题如何让这种兴趣爱好能够不成为三分钟热度而是成为你生活和学习中的一部分也让这部分兴趣爱好涉及的知识能更加专业它说应该缩小对兴趣爱好的期望拆分成小任务坚持每天都做一点增加更多的回报机制这样我们就能把看起来很累的坚持变得更加容易&quot;&gt;写文字当时我正在对自己的兴趣爱好做总结，因为总是有三天热度的这种情况，所以在想为什么会有这种情况，以及我该如何避免。这问题直到今年年初时看了一本叫《微习惯》的书才有点明白过来，它里面不只讲了如何正确建立新的习惯，也分析了很多我们兴趣爱好正热头上的问题，如何让这种兴趣爱好能够不成为三分钟热度，而是成为你生活和学习中的一部分，也让这部分兴趣爱好涉及的知识能更加专业。它说应该缩小对兴趣爱好的期望，拆分成小任务，坚持每天都做一点，增加更多的回报机制，这样我们就能把看起来很累的坚持，变得更加容易。&lt;/h6&gt;

&lt;p&gt;问题文字8: “积少成多，人弃我取”&lt;/p&gt;

&lt;h6 id=&quot;这是我在电影中看到的一句话这部分电影叫白银帝国积少成多我体会的比较多也能理解些但人弃我取这4个字至今也没有完全理解这句话背后隐含的意义就留给后面的岁月吧&quot;&gt;这是我在电影中看到的一句话，这部分电影叫《白银帝国》，积少成多我体会的比较多也能理解些，但‘人弃我取‘这4个字至今也没有完全理解这句话背后隐含的意义，就留给后面的岁月吧。&lt;/h6&gt;

&lt;p&gt;问题文字9: “权利越大，名气越大越危险，同样的，财富也越大，掌控面也越大。这世界是公平的，开启了一扇门的同时另一扇门就关闭了。“，“做事的格局要大，否则成不了事，所以很多大佬的口中要做的事很大，听上去像是吹牛，虽然做不做的到是另外一回事，但如果格局不够大的，基本都成不了事”&lt;/p&gt;

&lt;h6 id=&quot;人只有对自己没有的东西才会幻想所以我幻想了我什么都没有所以我当时胡思乱想了一通完全没有什么用并且还让自己有了不切实际的高目标有时还试图认为我已经有了自我催眠前段日子在书中看到一句话说的挺好自信就是忘掉自己的感觉直接去做想太多太不切实际对自己没有好处其实人只要看着比自己稍微高一点的目标就可以了更高的目标只会扰乱自己的心境不切实际的目标会让自己寸步难行&quot;&gt;人只有对自己没有的东西才会幻想，所以我幻想了。我什么都没有，所以我当时胡思乱想了一通，完全没有什么用并且还让自己有了不切实际的高目标，有时还试图认为我已经有了(自我催眠)。前段日子在书中看到一句话说的挺好，“自信就是忘掉自己的感觉，直接去做”，想太多太不切实际，对自己没有好处。其实人只要看着比自己稍微高一点的目标就可以了，更高的目标只会扰乱自己的心境，不切实际的目标会让自己寸步难行。&lt;/h6&gt;

&lt;p&gt;问题文字10: “ 这个世界是需要行动力的，理论和想象虽然也要，但如果没有行动，就是空想。在实际生活中基本上都是行动中思考，边做边想，从做事中纠正，改变，调整，改善，改良，更换，迭代中升级，进阶，完善。”&lt;/p&gt;

&lt;h6 id=&quot;这段文字是在上一段文字写完后过了一段日子记下的文字我把自己从幻想的世界拉回了现实现实生活中的行动更为重要实际生活中能够及时跟进事情的变化并且做出行动因为它在不停的发展不要纠结过去直面当前问题直面当下是最好的选择&quot;&gt;这段文字是在上一段文字写完后过了一段日子记下的文字，我把自己从幻想的世界拉回了现实。现实生活中的行动更为重要，实际生活中能够及时跟进事情的变化并且做出行动，因为它在不停的发展，不要纠结过去，直面当前问题，直面当下是最好的选择。&lt;/h6&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(四十二) 速学者的天堂</title>
   <link href="http://www.luzexi.com/2020/04/06/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A842"/>
   <updated>2020-04-06T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/04/06/思路探讨42</id>
   <content type="html">&lt;p&gt;此文为《微精通》中部分内容的总结和概括，它是一本教大家如何速学，如何高效率学习，如何快速掌握技巧的书，作者提倡博识、速学，抛开‘一万小时学习‘的旧观念，想尽办法用最快最高效最有效的策略去学习。&lt;/p&gt;

&lt;p&gt;这章中作者指出，人类是学习型动物，我们一生不可避免地遇到各种变化，因此我们必须活到老学到老，以保持大脑的基本功能正常运转。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;其实大脑神经系统的生长和改善是贯穿人的一生的，它并不只是某个时期的运作方式，这也是为什么人们常说，现在行动并不晚的原因。&lt;/p&gt;

&lt;p&gt;用大脑可塑性一流学者，迈克尔.莫山尼奇博士话说：&lt;/p&gt;

&lt;h6 id=&quot;大脑塑造是一个物理过程灰质实际上能变厚也能缩小神经连接可得到锻炼并加强也可被削弱或切断&quot;&gt;大脑塑造是一个物理过程。灰质实际上能变厚也能缩小，神经连接可得到锻炼并加强，也可被削弱或切断。&lt;/h6&gt;

&lt;h6 id=&quot;大脑上这些物理上的改变会体现在能力上的改变如果我们学会了一个新舞步就是在大脑上形成一个新的连接神经通路而忘了某人的名字则说明负责这部分的记忆连接退化或者断开了因此用进废退这个词语描述我们的大脑非常准确&quot;&gt;大脑上这些物理上的改变会体现在能力上的改变，如果我们学会了一个新舞步，就是在大脑上形成一个新的“连接”神经通路。而忘了某人的名字，则说明负责这部分的记忆连接退化或者断开了。因此‘用进废退’这个词语描述我们的大脑非常准确。&lt;/h6&gt;

&lt;p&gt;不过在现在发达的科技下，智能系统越来越多的环境下，我们甚至什么都不需要做，光靠电脑屏幕就能实现媒体的生活方式，这使我们在很多维度都无法得到锻炼，特别是多维度的感官，这使得我们开始经历一场小小的灾难，即无法更容易的受到各维度上的感知训练。&lt;/p&gt;

&lt;p&gt;因此作者认为我们需要更多采用祖先狩猎采集式的自由博识的生活方式来增强我们自己的多维度感知力。&lt;/p&gt;

&lt;p&gt;他举证神经学专家赫布律(Hebb’s law)在一项基础研究的发现：&lt;/p&gt;

&lt;h6 id=&quot;某件事经历的越丰富花样越多对感官的刺激越大这件事情在大脑中留下的印象就越深刻大脑里形成的连通性就越强这其实就是增强记忆的比较好的方法也是提高学习效率的好方法更是抗衰老的有效手段&quot;&gt;某件事经历的越丰富，花样越多，对感官的刺激越大，这件事情在大脑中留下的印象就越深刻，大脑里形成的连通性就越强。这其实就是增强记忆的比较好的方法，也是提高学习效率的好方法，更是抗衰老的有效手段。&lt;/h6&gt;

&lt;p&gt;如果有一天我们总是依靠人工智能的自动驾驶活着，就容易丧失定位，辨识方位的能力。相反如果我们用自由博识的方式，靠人脑的定位、辨识去旅行，即使使用导航也能让大脑得到锻炼，这样大脑就会充满活力。&lt;/p&gt;

&lt;p&gt;下面的内容作者开始讲述提高学习效率的方式。&lt;/p&gt;

&lt;h6 id=&quot;1锻炼记忆&quot;&gt;1.锻炼记忆。&lt;/h6&gt;

&lt;p&gt;记忆方法有很多，作者推荐了记笔记的方式：持续做内容精确的笔记，并附上照片或图片，回忆起来就会容易得多，因为大部分需要回忆的内容都已呈现在眼前。&lt;/p&gt;

&lt;h6 id=&quot;2书本靠后感官向前&quot;&gt;2.书本靠后，感官向前。&lt;/h6&gt;

&lt;p&gt;人们认为以文字为基础、书本为中心、再加评论的学习模式比其他方法的效率要更高，所以忽略了探寻和观察的作用。这是有巨大问题的，这是导致学习效率下降的重要原因，也是很多人最后自我怀疑的原因，即‘为什么，我这么努力而他却能轻轻松松超过我。’&lt;/p&gt;

&lt;p&gt;作者举例如果在我们吃培根前听到它在锅里发出‘滋滋’声，那会使得我们觉得嚼起来更加美味。同样的，如果我们听到不同节奏的音乐，就会联想到各种各样的色彩，这叫色听联觉。因为我们人类大脑能够同时记录声觉、触觉、嗅觉、甚至痛觉，这些感官互相沟通、交融、激发，这些多维度的感官刺激能建立起更加牢固的大脑神经连接，从而让我们对某件事印象更加深刻，记忆更加牢固。&lt;/p&gt;

&lt;h3 id=&quot;还有更快更好的学习方法&quot;&gt;还有更快更好的学习方法&lt;/h3&gt;

&lt;p&gt;作者又举证迈克尔.莫山尼奇博士（就是最前面说大脑连接结构的那个）的话：&lt;/p&gt;

&lt;h6 id=&quot;如果事物足够新奇或者我们足够投入学起来就会更快&quot;&gt;如果事物足够新奇，或者我们足够投入，学起来就会更快。&lt;/h6&gt;

&lt;h6 id=&quot;在这种状态下刺激神经生长出来的脑源性神经营养因子会变得更加活跃并建立更强更深更好的神经连接&quot;&gt;在这种状态下，刺激神经生长出来的脑源性神经营养因子，会变得更加活跃，并建立更强、更深、更好的神经连接。&lt;/h6&gt;

&lt;p&gt;这里必须跳过一些内容与作者在后面的文章中提到的‘心流’联系在一起来说。&lt;/p&gt;

&lt;p&gt;‘心流’概念由匈牙利心理学家 米哈里.契克森米哈赖提出的，他为此写了本书《心流》正闲置在我的书柜上，其中心思想是:&lt;/p&gt;

&lt;h6 id=&quot;在完全沉浸于活动中时我们会进入一种意识不到时间的精神状态我们专注力增强感觉充满活力&quot;&gt;在完全沉浸于活动中时，我们会进入一种意识不到时间的精神状态。我们专注力增强，感觉充满活力。&lt;/h6&gt;

&lt;p&gt;处于心流状态时，我们的思维批判闸门关闭了，因而学得更快。&lt;/p&gt;

&lt;p&gt;作者举例自己在不断尝试用钢笔或毛笔画出完美书法圆圈时，自己能轻松进入半冥想、全身关注的心流状态。在其他技能练习，或语言学习时经过10-15分钟的时间也同样可以轻松达到‘心流’状态。&lt;/p&gt;

&lt;p&gt;为了促进心流状态的实现，作者对心流状态练习了很多次，提出了练习心流几个状态的心得：&lt;/p&gt;

&lt;h6 id=&quot;他用数字标识心流状态0-无聊1-放松2-可控3-心流4-兴奋5-焦虑用挑战的方式去练习心流体验首先选出一个有能力攻克的挑战项目如果这个项目超出了你的能力范围一点点你会发现自己变得很兴奋如果超出了你能力范围很多则会变得很焦虑反之如果挑战完全处于掌控中则会觉得感到无聊&quot;&gt;他用数字标识心流状态，0-无聊，1-放松，2-可控，3-心流，4-兴奋，5-焦虑。用挑战的方式去练习心流体验，首先选出一个有能力攻克的挑战项目，如果这个项目超出了你的能力范围一点点，你会发现自己变得很兴奋，如果超出了你能力范围很多，则会变得很焦虑。反之如果挑战完全处于掌控中，则会觉得感到无聊。&lt;/h6&gt;

&lt;h6 id=&quot;我们为了达到3-心流状态在练习时如果觉得这个目标稀松平常那就加大难度如果觉得紧张不安那就降低难度放慢脚步逐步提高技能熟练度如果到达并实现了3-心流那就太棒了它能让人欣喜若狂时间仿佛停止在那里不知不觉45个小时就过去了学习效率就在这段时间里处于比较高的水平&quot;&gt;我们为了达到‘3-心流状态’，在练习时，如果觉得这个目标稀松平常，那就加大难度，如果觉得紧张不安，那就降低难度，放慢脚步逐步提高技能熟练度。如果到达并实现了‘3-心流’，那就太棒了，它能让人欣喜若狂，时间仿佛停止在那里，不知不觉4、5个小时就过去了，学习效率就在这段时间里处于比较高的水平。&lt;/h6&gt;

&lt;h3 id=&quot;学习策略的偏见&quot;&gt;学习策略的偏见&lt;/h3&gt;

&lt;p&gt;这里谈到了偏见，他说，大脑的可塑性是可以延伸到我们的学习方式上的，换句话说，我们可以学会如何更好的学习。&lt;/p&gt;

&lt;p&gt;其实我们在成长的过程中，我们面对的表扬与责备、鼓励与打击的模式会形成一种不好的偏见，即好像某种学习策略会好于另一种。但事实上这种默认的标准未必对你有效。&lt;/p&gt;

&lt;p&gt;作者举例自己曾经也是偏见学习者中的一位，曾自认为自己是‘实践学习者’，所以买了台新相机后把一本厚1英尺的使用手册丢在一边，看都不看一眼就去摆弄相机。几个月后自己意识到他依然只了解到相机功能的一点点皮毛，于是只好坐下来仔细查看使用手册，并测试相应功能，以后的日子里每次按下按钮或试过新功能后，都会回查使用手册，在手册中努力学习每个相机的功能。&lt;/p&gt;

&lt;h6 id=&quot;读者能否感知到其实多种学习策略的储备会增强思维流畅性使得我们能自由转换不同的想法不同的视角从而看到问题和事物的不同方面即便它们是相互对立的这不仅丰富了我们的眼界也加快了学习的速度也加深了知识的深度&quot;&gt;读者能否感知到，其实多种学习策略的储备会增强思维流畅性，使得我们能自由转换不同的想法，不同的视角，从而看到问题和事物的不同方面，即便它们是相互对立的，这不仅丰富了我们的眼界也加快了学习的速度也加深了知识的深度。&lt;/h6&gt;

&lt;h3 id=&quot;最后我想提一下作者对增强自信心的看法&quot;&gt;最后我想提一下作者对增强自信心的看法。&lt;/h3&gt;

&lt;p&gt;他认为‘微精通’(即博识与速学)能帮助你一步步树立信心，不仅让你拥有丰富的技能，更能储备多种学习策略，使得学习效率更高。其中自信心在学习中起着非常重要的作用。&lt;/p&gt;

&lt;p&gt;引用科幻作者 A.E.van Vogt 的话说，“自信是一种简单的能力，是被问到时候能清晰响亮地说出自己名字的能力，是向人温暖致意的能力，是及时向他人表示热烈祝贺的能力。”&lt;/p&gt;

&lt;h6 id=&quot;换句话说意思是忘掉内心的感觉专心表现就行&quot;&gt;换句话说，意思是，忘掉内心的感觉，专心表现就行。&lt;/h6&gt;

&lt;p&gt;其实自信就是看你愿不愿意确立目标，并把更多的精力投入到目标需要做的事情上。如果我们缺乏自信，我们可能会认为自己不具备足够的能力，这时我们需要做就是回顾下自己的目标，并且行动起来，投入更多的努力。&lt;/p&gt;

&lt;p&gt;作者说大多数人都没有滔滔不绝自信满满交流或演说的能力，但经过努力克服内心的胆怯后也能达到这种程度。一些极端的例子比如口吃者经过不断投入更多的努力最终成为天才演说家，常在我们生活中发生。&lt;/p&gt;

&lt;p&gt;因此缺乏自信往往意味着我们只是不去尝试，不去尝试的投入更多的努力，最后导致失败的原因往往是根本没有尝试过，出师未捷身先死。&lt;/p&gt;

&lt;h6 id=&quot;书本的内容太多暂时写到这里&quot;&gt;书本的内容太多，暂时写到这里。&lt;/h6&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(四十一) 向内看自己</title>
   <link href="http://www.luzexi.com/2020/03/28/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A841"/>
   <updated>2020-03-28T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/03/28/思路探讨41</id>
   <content type="html">&lt;p&gt;我们时常关注太多外界的反应，包括别人的意见和看法，工作的上的任务，领导的要求，妻子的埋冤，周围朋友的相互比较，社会的动态，国家的新闻等等。很少向内看看自己，好好仔细静下心来，看看这个内在残破不堪，年久失修的自己。看看自己在内心上究竟有哪些缺陷，它们是如何产生的，自己恐惧的究竟是什么。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	著有《被讨厌的勇气》《自卑与超越》等经典心理书籍的著名精神病学家阿德勒曾说：“幸福的人用童年治愈一生，不幸的人用童年治愈一生。”
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;向内看，我们更多的看到的是自己内心最深处自卑，它们大多数都是由童年的创伤引起的。但这并不是父母的错，因为没有人是绝对幸福和绝对不幸的，所有人都或多或少的在童年时受到创伤，这是父母无法控制的，他们不知道什么时候会不经意间的伤害到孩子，他们自己都不知道，有时他们无法控制住自己，甚至有时候外界的因素对孩子的影响会比较大。&lt;/p&gt;

&lt;p&gt;因此在《自卑与超越》中阿德勒说：“所有人在童年时都多多少少会受到心灵的创伤，这使得他们在成长过程中都会一直携带着某种特质。”，实际上童年的心灵创伤要比我们想象中的大，即使只是一件在成年人眼中看来很小的事，在孩子心里可能会引起巨大的波澜，甚至会因此受到巨大的创伤。&lt;/p&gt;

&lt;h6 id=&quot;起初这些体会我并不能理解直到我为人父时后的第3年才开始慢慢的一点点理解我开始小心翼翼地与孩子们相处尽管看了各种孩子教育的文章和相关的心理学书籍但在实际中仍然无法做到妥当比如我想教孩子们有自律的习惯但要保持这种习惯总会伴随很多艰难的挑战于是会在与她们一起挣扎与未来会更好的之间徘徊又比如我不得不去外省工作而且至少3-6个月才能与孩子们有1次见面这或许已经在孩子们心中留下了创伤因为当她们同班同学说起我爸爸时心中会认为爸爸能及时出现在身边而我的孩子她却不行她自然会因为这件事而受到伤害&quot;&gt;起初这些体会我并不能理解，直到我为人父时后的第3年才开始慢慢的一点点理解。我开始小心翼翼地与孩子们相处，尽管看了各种孩子教育的文章和相关的心理学书籍，但在实际中仍然无法做到妥当，比如我想教孩子们有自律的习惯，但要保持这种习惯总会伴随很多艰难的挑战，于是会在与她们一起“挣扎”与“未来会更好”的之间徘徊，又比如我不得不去外省工作而且至少3-6个月才能与孩子们有1次见面，这或许已经在孩子们心中留下了创伤，因为当她们同班同学说起“我爸爸”时心中会认为爸爸能及时出现在身边，而我的孩子她却不行，她自然会因为这件事而受到伤害。&lt;/h6&gt;

&lt;p&gt;我看着孩子们遇到困难时的样子，看着她们挣扎的样子，时常回想起我童年时的情景，当时我也遇到跟她们一样的情景，我那时受伤的心情到现在我都依然能依稀记起来一些来。相对于那时的父母，现在的我能否做的更好些，发现我也一样存在各种各样的无奈，做不到孩子心中想要的那样，但我唯一能做到的就是与孩子相处时，把她们当成我身边那些成年朋友那样对待。这些事情导致我更发自内心的想了解我的童年创伤究竟给我带来了什么，既然无法避免，我应该如何对待它们。&lt;/p&gt;

&lt;h6 id=&quot;孩童时代的问题导致我最大的问题就是自卑这么多年我看过也聊过很多人他们触及内心世界的话题其中也不乏在外人眼里看起来非常优秀的人他们大都跟我一样时常会因为很多事情而自卑虽然这些我们所在意的事其实很小在外人眼里根本不算什么甚至有时我们自己也知道这不算什么但就是过不去这道坎不肯放过自己&quot;&gt;孩童时代的问题导致我最大的问题就是自卑，这么多年我看过也聊过很多人他们触及内心世界的话题，其中也不乏在外人眼里看起来非常优秀的人，他们大都跟我一样时常会因为很多事情而自卑。虽然这些我们所在意的事其实很小，在外人眼里根本不算什么，甚至有时我们自己也知道这不算什么，但就是过不去这道坎，不肯放过自己。&lt;/h6&gt;

&lt;p&gt;孩童时代的创伤更重要的是改变了我看待世界的方式，这种方式没有对错，或许就因为这种创伤导致你看待的世界的方式，在某段时间或某些方面让你赢得了世界。因为事情总是在不断发展的，在某段时间看起来是对的方式，在另一段时间里看起来却是错的，当事情有所转变时我们大部分人很难及时改变，进而导致自我怀疑和自我否定。&lt;/p&gt;

&lt;p&gt;于我而言也是一样，我像大多数刚毕业的人那样一直在不断成长，即使是一个35岁的人，对世界的理解仍然在不断更新，我也认为自己一直落后于世界，因此奋力的追赶着，因为内心总有句话告知我：现在行动还不算晚。&lt;/p&gt;

&lt;h6 id=&quot;人们常说35岁后的人是最脆弱的时候上有老下有小有车贷有房贷前有领导后有小年轻怕裁员怕动荡怕改变没有人会关心我们的问题因为我们已经成了社会的顶梁柱应该是个成熟的独立的综合性个体不应该去制造问题相反要主动看到问题并着手去解决它们而大多数人却不知道我们内心仍然是那个受到创伤的孩子&quot;&gt;人们常说35岁后的人是最脆弱的时候，上有老，下有小，有车贷，有房贷，前有领导，后有小年轻，怕裁员，怕动荡，怕改变。没有人会关心我们的问题，因为我们已经成了社会的顶梁柱，应该是个成熟的独立的综合性个体，不应该去制造问题，相反要主动看到问题并着手去解决它们，而大多数人却不知道我们内心仍然是那个受到创伤的孩子。&lt;/h6&gt;

&lt;p&gt;所以我向内看自己，希望有一天能正确看待自己的自卑，放过自己，与孩童时代的自己和解，真正能于世界一起走上正轨。&lt;/p&gt;

&lt;p&gt;以往我不喜欢小时候那个自卑、敏感、固执己见、自私的自己，想把过去的我和现在的我剥离，但反而那些特质却一直跟在我身上甩不掉。事情在自从我关注我自己孩子的成长后开始转变，我渐渐理解自己是普通人这句话，我应该放过自己，人本身就是不完美的，就因为不完美才美丽。于是我开始拥抱自己的过去，让自己与过去和解，过去的种种不再是我的负担，但仍然有许多我不能释怀的童年往事，弱小的人格暴露无遗。&lt;/p&gt;

&lt;h6 id=&quot;我开始明白存在即合理包括暴富的朋友读博士的同学不断升迁的同事出国定居的亲戚甚至包括报复我的敌人打击我的同事陷害我的陌生人都是正常且合理的我开始认为这些都是我们的世界里合理存在的我应该放下执念放过他们也放过自己继续走我觉得自己应该走的路只是在路上我应该把这些意外的因素考虑的周全些把事办好把周围的人安置妥当把人和事考虑周到点然后默默的走自己的路&quot;&gt;我开始明白存在即合理，包括暴富的朋友，读博士的同学，不断升迁的同事，出国定居的亲戚，甚至包括报复我的敌人，打击我的同事，陷害我的陌生人，都是正常且合理的。我开始认为这些都是我们的世界里合理存在的，我应该放下执念，放过他们，也放过自己，继续走我觉得自己应该走的路，只是在路上我应该把这些意外的因素考虑的周全些，把事办好，把周围的人安置妥当，把人和事考虑周到点，然后默默的走自己的路。&lt;/h6&gt;

&lt;p&gt;我时常观察自己是怎样看待世界的，是否理解错了。我总是这样挑战性的问自己，有没有完全相反的观点。我希望看到更多的视角下的答案，这样才能看到更多可能。&lt;/p&gt;

&lt;p&gt;向内看我看到了我根深蒂固的自卑，人人都有自卑情结，就看我们是如何克服它，更确切的说，应该是如何看待它，因为它可能无法避免。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(三十八) 如何才能保持高效的节奏</title>
   <link href="http://www.luzexi.com/2020/03/22/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A838"/>
   <updated>2020-03-22T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/03/22/思路探讨38</id>
   <content type="html">&lt;h3 id=&quot;精力旺盛活力充沛健康强壮是一个人走向成功的前提&quot;&gt;精力旺盛，活力充沛，健康强壮是一个人走向成功的前提。&lt;/h3&gt;

&lt;p&gt;看了美国叙事史《光荣与梦想》第一部和第二部，罗斯福在后期的健康状况与前中期的对比判若两人，对事物的处理也不再明锐和精明。国内国外事无巨细的指导工作，迫使他放弃了游泳健身，放弃了最重要的支撑工作的身体健康，这个举动得不偿失。生活中常常发生这样的情况，当长远的利益受到了破坏时我们虽然知道但并没有加以归正，这种长期计划在短期内无法看见而看见时则为时已晚，这种做法其实就是短视，是不以长期效益为前提的人生体验。罗斯福是个很厉害的人物，但在60多岁时却也忘了这些简单的道理。我能理解他这么做肯定是迫不得已的，因为大家都是在迫不得已的时间做出的选择。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;现实中很多人都没有弄明白，自己为什么跟不上别人的节奏。有些人的节奏越来越快，为什么？其实并不是他们节奏越来越快，而是我们自己的节奏越来越慢，精力耗尽疲惫不堪导致我们慢下了脚步，甚至停止不前。为什么大家都是人，你我一天都是24小时，8-11小时的工作强度，有些人就能拥有饱满的精力面对困难，行动起来主动积极注意力能很快集中。而我们却感觉跟不上，累得只想快点下班早点休息？我带着这些问题来写文章。&lt;/p&gt;

&lt;h3 id=&quot;强健的身体是所有的前提&quot;&gt;强健的身体是所有的前提。&lt;/h3&gt;

&lt;p&gt;活到现在我发现有一副强健的身体是做事和学习的首要之本。我们不是活这几年就没了，我们还需要活40-60年左右，为了能在后面的几年几十年里活的更好，我们应该做好打持久战的准备，有一副强健的身体是持久战的基础，没有它我们什么都不是，即使有钱也得吐回去。&lt;/p&gt;

&lt;p&gt;我认为无论我们有没条件都应该创造条件，每天运动一下以保持身体的强健，为防止衰老也为了防止钝化，更多时候不止如此，还可以是劳逸结合的一部分，以及脑袋换个思路的好方法。&lt;/p&gt;

&lt;h3 id=&quot;主动去劳逸结合&quot;&gt;主动去劳逸结合&lt;/h3&gt;

&lt;p&gt;我们应该从长远考虑生存问题，不一味的埋头苦干，而要找到自己的节奏。每个人的节奏不一样因此没有统一的答案。这个节奏是身体的节奏，也是自己生活的节奏。&lt;/p&gt;

&lt;p&gt;我们一天24小时，刨去睡觉8小时还有16小时，我们不可能用尽这16小时都在学习或者工作，因为大脑吃不消，短时间内确实可以长时间专注于某事，但长期如此则大脑和身体会剧烈反抗。&lt;/p&gt;

&lt;p&gt;为了能让脑袋在这个持久战中，能持续的正常和高效的工作，我们应该主动去劳逸结合，做到效率优先，否则很容易会让自己看起来很努力，实则什么也没做什么也没记住。&lt;/p&gt;

&lt;h6 id=&quot;记得主动去做劳逸结合而不是被动的让大脑迫使你休息的那种我们应该成为大脑的主人而不是它的奴隶&quot;&gt;记得主动去做劳逸结合，而不是被动的让大脑迫使你休息的那种。我们应该成为大脑的主人，而不是它的奴隶。&lt;/h6&gt;

&lt;p&gt;为了能控制大脑的节奏，我们应该制定自己的习惯和计划。就像人们口中常说的做“时间管理”。没错，不过“时间管理”只是其中很小的一部分。除了时间管理，我们更应该关注“精力管理”，以及习惯养成和计划执行。&lt;/p&gt;

&lt;h3 id=&quot;培养多样性和多元化&quot;&gt;培养多样性和多元化&lt;/h3&gt;

&lt;p&gt;什么是人身上的多样性和多元化，即你有很好几种感兴趣方向，以及好几种擅长的技能。这样你就能在生活中不断切换着来玩你的兴趣，学习和工作就不会变得枯燥，大脑的接受知识的能力也更加高。&lt;/p&gt;

&lt;p&gt;以前我一直认为单一性会更好，学程序就应该把所有的时间和精力都集中在这项技术上，才能集中力量办大事。这种想法其实是比较狭隘的，多样性不仅能让我们更加愉悦从而使得学习更加高效，也能给我们带来更多的灵感。&lt;/p&gt;

&lt;h6 id=&quot;多样性帮助我们撇开纯粹的娱乐在娱乐和紧张中间找到中间平衡点当我们学会了如何放轻松从而提高了效率高效率又促使我们获得更多成就感从而更加愉悦周而复始形成良性循环&quot;&gt;多样性帮助我们撇开纯粹的娱乐，在娱乐和紧张中间找到中间平衡点。当我们学会了如何放轻松从而提高了效率，高效率又促使我们获得更多成就感，从而更加愉悦，周而复始形成良性循环。&lt;/h6&gt;

&lt;p&gt;多样性有很多种，可以是弹琴，滑板，跳舞，游泳，健身，读书，讨论交流，甚至放风筝，划船，冲浪等。这其中关键点是我们认清什么是适合的尺度，以及如何节制。学会用多样性主动去放松，而不是过分娱乐，损耗了过多精力使自己还没开始学习前就筋疲力尽。&lt;/p&gt;

&lt;h3 id=&quot;确保被动学习和主动学习双向进行&quot;&gt;确保被动学习和主动学习双向进行&lt;/h3&gt;

&lt;p&gt;很多人忽视了被动学习的重要性，更多人忽视了主动学习的重要性。什么是被动学习呢？我们每天工作生活都会遇到问题，这些问题就是我们被动学习的时间，为了能处理好这些问题，我们每天都在想办法，试探各种途径、方法、可能性，都在为了解决这些生活和工作的问题绞尽脑汁。&lt;/p&gt;

&lt;p&gt;没错我们不知不觉的都在被动学习着，就是生活教育了我们。虽说如此但也有很多没有进行被动学习的人，比如工作和生活非常安逸的人们，他们每天基本上都坐着同样的无聊的事，每天面对都是同样的无聊的情景，没有新的事物注入，没有感兴趣的事，每天过着安逸的生活，他们希望世界永远这样下去不要变化。世界可不是我们说了算的，当真正危机降临时安逸的人们就很容易崩溃。&lt;/p&gt;

&lt;p&gt;我建议没有被动学习的人应该去做主动学习者，为自己的知识储备主动看更多的书学习更多的知识和技能，主动去发掘自己的兴趣，主动去沟通和交流外面的世界。&lt;/p&gt;

&lt;p&gt;主动学习的人也不要忽视了被动学习，工作和生活中的问题，我们也要尽心尽力去尝试各种方法解决，被动学习的力量非常强大，不亚于主动学习。&lt;/p&gt;

&lt;p&gt;为什么这么说呢，人们常说“选择大于努力”，被动学习就是选择后的动作，这里不是说选择好了就不需要努力，而是说在大环境下，在大的浪潮中只要你不断积极处理眼前的问题，被动学习能让走上人生的巅峰。如果没有浪潮可赶，我们最好能和优秀的人一起学习和工作，与他们一起交流，一同进步。&lt;/p&gt;

&lt;p&gt;被动学习和主动学习都同样重要，我们应该确保被动和主动学习的双向进行。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>读书笔记(二) 《如何有效阅读一本书》</title>
   <link href="http://www.luzexi.com/2020/03/15/%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B02"/>
   <updated>2020-03-15T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/03/15/读书笔记2</id>
   <content type="html">&lt;p&gt;2020-03-04日买入到手，2020-03-14日读完第一遍并划重点。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;2020-03-15日第二遍读书并筛选重要内容并评论&lt;/p&gt;

&lt;h3 id=&quot;序-&quot;&gt;序-&lt;/h3&gt;

&lt;h6 id=&quot;书里的内容都不会被保存在脑子里这些内容既不能融入你的身心也不能变成你无形的财富与此相反尽管抄完书也不一定能记住全部内容但某段文章某个词作者的语气和思想等等都会随着抄写时的身体感觉被深深地刻印在头脑和身体里面&quot;&gt;书里的内容都不会被保存在脑子里，这些内容既不能融入你的身心，也不能变成你无形的财富。与此相反，尽管抄完书也不一定能记住全部内容，但某段文章、某个词、作者的语气和思想等等，都会随着抄写时的身体感觉被深深地刻印在头脑和身体里面。&lt;/h6&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;@@@ 想把书中的内容保留在脑子里，就得寻求各种各样的方法来加深印象，比如作者所说的，把自己觉得重要的内容抄到笔记本里，在抄的过程中这种印象就被记录在脑子里。这其实就和我们生活中那些特别美好的或者特别糟糕的回忆会被永远保留在脑子里一样。读书很少有特别美好的回忆，但为了把这些内容保留在脑子里，就得想出各种方法加深印象。&lt;/p&gt;

&lt;h3 id=&quot;前言-&quot;&gt;前言-&lt;/h3&gt;

&lt;h6 id=&quot;我得记忆力不太好对亲身经历或是旁观过得事物印象自然会深刻但对人名地名或者读过的文章就算看多少遍都容易遗忘读过的书亦是如此所以我很久以前就意识到了自己在这方面的缺点我记不得书里的内容也不太能理解书里的内容&quot;&gt;我得记忆力不太好，对亲身经历或是旁观过得事物，印象自然会深刻，但对人名，地名或者读过的文章，就算看多少遍都容易遗忘，读过的书亦是如此。所以我很久以前就意识到了自己在这方面的缺点：我记不得书里的内容，也不太能理解书里的内容。&lt;/h6&gt;

&lt;p&gt;@@@ 其实我也一样，而且我更痛苦的是，很多时候旁观过的也记不住，只有亲身经历过的才会刻在脑子里。这可能彻彻底底的普通人的表现。所以我想方设法的，变着法子的折腾，就是为了让自己能够加深印象，随记，写书，写博客，总结，写框架文档等等，为了让自己能够有更深刻的印象，有更深刻的认识，会花很大的精力和积极主动性去‘折腾’。&lt;/p&gt;

&lt;h6 id=&quot;读书的时候我就会不自觉地想所谓理解精髓就好只是个美好的愿望那是机会主义让自己更安心的理由&quot;&gt;读书的时候我就会不自觉地想：“所谓‘理解精髓就好’只是个美好的愿望，那是机会主义让自己更安心的理由”。&lt;/h6&gt;

&lt;p&gt;@@@ 确实如此，我当年在学习读书的时候，总抱着这种机会主义的思想，认为我理解精髓就好，就好像别人都是笨蛋似的，其实自己才是笨蛋，不仅笨还懒。&lt;/p&gt;

&lt;h6 id=&quot;只是这样读过书就等于没有读过如果只是用眼睛看过而不是记在脑子里读再多的书也没有用&quot;&gt;只是这样读过书，就等于没有读过。如果只是用眼睛看过，而不是记在脑子里，读再多的书也没有用。&lt;/h6&gt;

&lt;p&gt;@@@ 这不禁让我有点疑问，昨天跟老婆大人讨论，读很多书，和精读几本书各自的意义，我认为，需要两者结合，有时可以快有时可以慢。觉得重要的就精读，觉得不太重要但有兴趣的可以快读，以了解自己感兴趣的内容。&lt;/p&gt;

&lt;h6 id=&quot;读完每一本书都有扎实的收获至于一个月能读很多书读一本书只用几十分钟这样的能力在这个目标前都是苍白无力的到达获得扎实的收获这个目标以后再去训练更快的读书速度或是买大量书一口气读完或是锻炼严密的逻辑思考能力都为时不晚&quot;&gt;读完每一本书，都有扎实的收获。至于一个月能读很多书、读一本书只用几十分钟这样的能力，在这个目标前都是苍白无力的。到达获得扎实的收获这个目标以后，再去训练更快的读书速度，或是买大量书一口气读完，或是锻炼严密的逻辑思考能力，都为时不晚。&lt;/h6&gt;

&lt;p&gt;@@@ 读多读快，确实不一定是什么好事，如果我记不住，时间长了我也会感觉到一种挫败感，我会“想为什么我读了这么多书还是脑袋空空”。所以精读是很有必要的。&lt;/p&gt;

&lt;h3 id=&quot;第一章-用笔记管理读书生活&quot;&gt;第一章 用笔记管理读书生活&lt;/h3&gt;

&lt;h6 id=&quot;利用笔记实现的目标&quot;&gt;利用笔记实现的目标：&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	读书不再是’随随便便‘地读，而是带着明确的目的、充满主动性地去读

	真正消化书中的信息，使之成为属于自己的东西

	深入理解书中的要点或思想，并随时拿来参考。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;@@@ 很好，我还没尝试过带着如何做笔记这个目标读书的，感觉很有针对性。还没开始读就已经在盘算怎么做笔记了，感觉很容易记住书中的内容，边读边思考把哪些内容放入笔记中，哪些内容是重要的。这种级别的折腾，会给自己在读书的过程中增加很多记忆点，可以更大程度的加深印象。&lt;/p&gt;

&lt;h6 id=&quot;本书所讲的读书方法包括以下四个方面&quot;&gt;本书所讲的读书方法包括以下四个方面&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	如何选书：怎样选择自己真正想读的书

	如何购书：怎样买到对自己真正有益的书

	如何读书：怎样加深理解、深入思考

	如何活用：怎样运用从书中获得的知识
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;@@@ 前两者我不太感兴趣，虽然我都看完了，但跟我想象的差不多，因为自己也是从各种渠道的文章中获得书本的名字，或者在书本中作者的引用和介绍中获得好书，然后把它们放入购物车中，等真的很想看的时候再买回来。后两者我着重的划了很多重点，我想学习作者是如何讲书的内容装进脑子里去的，他是如何做笔记的。&lt;/p&gt;

&lt;h6 id=&quot;介绍笔记的时候方法可以分为随想笔记购书清单各种报道的剪报读书笔记&quot;&gt;介绍笔记的时候方法，可以分为：随想笔记，购书清单，各种报道的剪报，读书笔记。&lt;/h6&gt;

&lt;p&gt;@@@ 我有自己的随记，每次当灵感来了，想说点什么时就写进印象笔记中去，标题通常都是’随记xx‘，xx为数字从1开始到现在已经记到了55。很多中途被我用在了文章中就会删除他们。购物清单我使用淘宝购物车，想买的书都记在那里。剪报的习惯没有，还没感觉到它的好处。读书笔记是我正在学习的事。&lt;/p&gt;

&lt;h6 id=&quot;通过在笔记本上记录的过程你也可以加深对书的理解使写下的内容更容易融入自己的身心促使你把一本书切实地转变为自己的无形财富&quot;&gt;通过在笔记本上记录的过程，你也可以加深对书的理解，使写下的内容更容易融入自己的身心，促使你把一本书切实地转变为自己的无形财富&lt;/h6&gt;

&lt;p&gt;@@@ 确实，我在如何加深对书的理解和对书中内容的印象要做更多的努力。&lt;/p&gt;

&lt;h6 id=&quot;只要坚持就一定会有效读书笔记的宝贵内容是你将来总有一天你能用到知识储备&quot;&gt;只要坚持就一定会有效。读书笔记的宝贵内容是你将来总有一天你能用到知识储备。&lt;/h6&gt;

&lt;p&gt;@@@ 坚持读书笔记，让自己获得更多更高效的读书生活。&lt;/p&gt;

&lt;h6 id=&quot;如果在做笔记的过程中感觉很疲惫创造自己的方式可以激发做笔记的动力而充足的准备也会让人对笔记本充满留恋&quot;&gt;如果在做笔记的过程中感觉很疲惫，创造自己的方式可以激发做笔记的动力，而充足的准备也会让人对笔记本充满留恋。&lt;/h6&gt;

&lt;p&gt;@@@ 疲惫是正常的，为了消除乏味疲惫，我们可以尝试各种自己喜欢的方式去做，比如我就用Macbook来写读书笔记，我喜欢这种方式。&lt;/p&gt;

&lt;h6 id=&quot;作者对普通读书法和笔记读书法做了比较&quot;&gt;作者对普通读书法和笔记读书法做了比较&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	普通读书法在读书时---读书没有重点，需要很长时间。读书过程容易变得单调乏味。读过以后很难提炼要点。

	笔记读书法在读书时---目的明确，读书速度快。通过做记号加深印象。读后可以迅速提炼要点。

	普通读书法在记录时---很难反复阅读。甚至不记得曾经读过，更别说内容了。很难提取信息。

	笔记读书法在记录时---方便反复阅读。通过书写加深印象。以书为媒介丰富思想。

	普通读书法在活用时---很难进行参考。书被遗忘在角落。书太多浪费空间。

	笔记读书法在活用时---便于参考。通过读书笔记吸取知识，并得到提高。可以脱离原书。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;@@@ 读书笔记好，加深印象，方便复习，精选知识内容&lt;/p&gt;

&lt;h6 id=&quot;坚持只选好书只读好书的做法可能不太现实但只要我们张弛有度地贯彻好书要细细研读其他书就粗度略读的原则还是很有可能实现的&quot;&gt;坚持’只选好书，只读好书‘的做法可能不太现实，但只要我们张弛有度地贯彻’好书要细细研读，其他书就粗度略读‘的原则，还是很有可能实现的。&lt;/h6&gt;

&lt;p&gt;@@@ 要张弛有度有度，就像我看的一本书名为《微精通》里写的那样，很多事情都有两个极点，只有平衡好两个极点之间，我们才能真正的精通一门学问。&lt;/p&gt;

&lt;h6 id=&quot;作者介绍了一元化信息管理术这是在他的另一本书里写的这里概括的提了些内容他说如果整理信息的方法烦冗拖沓那么是无法坚持下去的为了能长久地坚持就要把读书获得的信息不做加工地放在同一个容器里&quot;&gt;作者介绍了一元化信息管理术，这是在他的另一本书里写的，这里概括的提了些内容。他说，如果整理信息的方法烦冗拖沓，那么是无法坚持下去的。为了能长久地坚持，就要把读书获得的信息不做加工地放在同一个容器里。&lt;/h6&gt;

&lt;p&gt;@@@ 与《微习惯》一书的作者观点一样，首先要容易骗过自己的大脑，如果太烦琐太难，大脑就会第一时间拒绝，意志力消耗过于严重，很难坚持长久。&lt;/p&gt;

&lt;h3 id=&quot;第二章-用购书清单指名购书&quot;&gt;第二章 用购书清单指名购书&lt;/h3&gt;

&lt;p&gt;@@@ 这部分我很快的看过，因为我觉得内容很简单，所以快速的读过就结束了。他里面讲了如何从各种信息渠道中获取书本的名字，了解书本的内容，以便记录到自己的购书清单中。对我来说他里面介绍的最重要的是如何防止冲动购书。他说很多时候我们买书都太冲动，这导致买了很多不必要的书，或者买了太多书看不过来也会有焦虑和挫败感。如果你想买书，就先把书放在购书清单中，冷静下，第二天，或者第二周再来看，这样就会冷静很多。确实如此，对我来说，很多时候都太冲动，甚至有段时间因为买的书太多看不完而焦虑的很，挫败感也时常从中而来，时常会想“我什么时候能看完这些书啊”，以及“别人都看过了我怎么还没看”之类的，让自己有混乱的情绪发生。学习到这种冷静的处理买书的冲动，会让我舒服很多。&lt;/p&gt;

&lt;h3 id=&quot;第三种-用笔记把读过的书变为精神财富&quot;&gt;第三种 用笔记把读过的书变为精神财富&lt;/h3&gt;

&lt;h6 id=&quot;p75-人们经常说你为别人讲解书中的内容时才会真正理解它把记读书笔记作为目标去读书得到的效果也是一样当你以思想输出为前提去读书时思想输入的质量也会有所提升而且亲手写文章的好处比口头叙述要多&quot;&gt;p75 人们经常说“你为别人讲解书中的内容时，才会真正理解它”，把记读书笔记作为目标去读书，得到的效果也是一样。当你以思想输出为前提去读书时，思想输入的质量也会有所提升，而且亲手写文章的好处比口头叙述要多。&lt;/h6&gt;

&lt;p&gt;@@@ 通过传授知识的方式来加深对知识的理解是效率最高的，但这种机会很少。所以我们只能用另一种方式，去记读书笔记，把思想输出为前提去读书，效率会增加很多。&lt;/p&gt;

&lt;h6 id=&quot;p76-当你像这样一边思考一边选择要摘抄的文章时会不由自主地想多读几遍尤其是对还没有读懂的部分在不断思考不断重读的过程中你会惊讶地发现自己已经读过三五遍了&quot;&gt;p76 当你像这样一边思考一边选择要摘抄的文章时，会不由自主地想多读几遍，尤其是对还没有读懂的部分，。在不断思考、不断重读的过程中，你会惊讶地发现自己已经读过三五遍了。&lt;/h6&gt;

&lt;p&gt;@@@ 强迫自己多读几遍会引起大脑的反感和反抗。我们可以换种方法，一边思考一边选择要摘抄的文章，把注意力转移到选择的内容上，从而骗过要反抗的大脑，在思考和摘抄中发现自己已经重读了很多遍。&lt;/p&gt;

&lt;h6 id=&quot;p78-积极地跳读和速读保持张弛有度的节奏可以把更多精力集中在好书的精华内容上读书的目的是用自己的方式学习而不是模仿评论家的做法学习吸收对自己真正有用的智慧和语句才是最重要的反过来说如果有些信息对自己来说无关紧要那就干脆丢掉读书的重点是自己认为哪些内容重要并将其彻底消化&quot;&gt;p78 积极地‘跳读’和‘速读’，保持张弛有度的节奏，可以把更多精力集中在好书的精华内容上。读书的目的是用自己的方式学习，而不是模仿评论家的做法，学习、吸收对自己真正有用的智慧和语句才是最重要的。反过来说，如果有些信息对自己来说无关紧要，那就干脆丢掉。读书的重点是自己认为哪些内容重要，并将其彻底消化。&lt;/h6&gt;

&lt;p&gt;@@@ 还是要积极运用速读、精读，两者张弛有度才是好方法。读重点内容，把精力有效地用在重要的内容上。&lt;/p&gt;

&lt;h6 id=&quot;p80-读书笔记就像日记一样一旦停止就很难恢复如果搁置很久重新开始做笔记时就会产生抵触感&quot;&gt;p80 读书笔记就像日记一样，一旦停止就很难恢复，如果搁置很久，重新开始做笔记时就会产生抵触感。&lt;/h6&gt;

&lt;p&gt;@@@ 所以要每天记一点，就像《微习惯》一样，要每天都保持，如果到读完了再记恐怕大脑的抵触情绪会很大。&lt;/p&gt;

&lt;h6 id=&quot;p81-做好读书笔记会让自己对书中的内容印象更加深刻在写读书笔记前提下读书方法会发生改变你会进入主动寻找书中打动人心的内容的工序&quot;&gt;p81 做好读书笔记，会让自己对书中的内容印象更加深刻。在写读书笔记前提下，读书方法会发生改变，你会进入主动寻找书中“打动人心的内容”的工序。&lt;/h6&gt;

&lt;p&gt;@@@ 做好读书笔记，会使得自己主动去寻找书中的精髓。这点我非常肯定&lt;/p&gt;

&lt;h6 id=&quot;p83-笔记读书法需要我们进行读书划重点写读书笔记三个步骤每个步骤都需要阅读这种多次阅读留下的印象肯定比普通的阅读深刻好几倍如果是亲手摘抄效果会更好&quot;&gt;p83 笔记读书法需要我们进行读书、划重点、写读书笔记三个步骤，每个步骤都需要阅读。这种多次阅读留下的印象肯定比普通的阅读深刻好几倍。如果是亲手摘抄，效果会更好。&lt;/h6&gt;

&lt;p&gt;@@@ 读书分几遍，第一遍可以通读，然后通读中或者通读后划出重点，第二遍看着重点写读书笔记，第三遍看着读书笔记写读后感文章。&lt;/p&gt;

&lt;h6 id=&quot;p84-p88-作者给出了几种筛选程序通读重读标记通读时把读起来不错的折一角重读时读折过的页仍然觉得很好时再折一角再重读时仍然觉得值得一读则做好标记像这样经过思考后再在树上画线还可以避免书中出现无意义的线条最后连原文都看不清又说也可以利用书签来读通读书签重读书签标记书签分别在通读重读标记后休息时夹在书中&quot;&gt;p84-p88 作者给出了几种筛选程序。通读、重读、标记，通读时把读起来不错的折一角，重读时读折过的页仍然觉得很好时再折一角，再重读时仍然觉得值得一读则做好标记。像这样经过思考后再在树上画线，还可以避免书中出现无意义的线条，最后连原文都看不清。又说也可以利用书签来读，通读书签，重读书签，标记书签，分别在通读、重读、标记后休息时夹在书中。&lt;/h6&gt;

&lt;p&gt;@@@ 方法确实不错，但我觉得还是划线方便，第二遍重读时只读划线部分，筛选出来记入笔记，第三遍只读筛选部分，对其再进行筛选后写入读后感，方法越简单越容易坚持，毕竟是长期的过程，操作如果太复杂意志力消耗大，就难以坚持。&lt;/p&gt;

&lt;h6 id=&quot;p90-p99-作者介绍了葱鱼火锅式读书笔记笔记要写读书笔记的日期书名作者名摘抄时格式为自己觉得重要的内容和自己对这段的感想摘抄和评论交替进行是为了在摘抄以后趁着印象还鲜明写下自己的感想要是等摘抄完所有文章再去从头开始顺序写评论一定会忘记之前看文章时的感想就像每做完一道数学题都要马上对答案一样自己的思考也需要立即落实到纸上&quot;&gt;p90-p99 作者介绍了‘葱鱼火锅’式读书笔记。笔记要写读书笔记的日期、书名、作者名。摘抄时格式为，自己觉得重要的内容和自己对这段的感想，摘抄和评论交替进行，是为了在摘抄以后趁着印象还鲜明写下自己的感想。要是等摘抄完所有文章再去从头开始顺序写评论，一定会忘记之前看文章时的感想。就像每做完一道数学题都要马上对答案一样，自己的思考也需要立即落实到纸上。&lt;/h6&gt;

&lt;p&gt;@@@ 记日期，书名，作者，摘抄和评论交替进行，确实很有效果，加深了印象的同时，方便以后再次翻看。不过我喜欢写在macbook上，所以一般都会先看完一大段，后再把它们摘抄到macbook上去。这种方式对我来说更轻松，切入读书笔记更容易。不过亲手摘抄确实能很大程度加深对书的记忆，也同时加深理解。我应该尝试下。&lt;/p&gt;

&lt;h6 id=&quot;p100-自己当下的想法才是最重要的摘抄这一段文章的原因和自己当时的想法才是日后值得参考的贵重资料读书时获得的感想正是原创思想的源泉这样的一本读书笔记是以书为媒创出属于自己的思想舞台&quot;&gt;p100 自己当下的想法才是最重要的，摘抄这一段文章的原因和自己当时的想法才是日后值得参考的贵重资料。读书时获得的感想正是原创思想的源泉。这样的一本读书笔记，是以书为媒，创出属于自己的思想舞台&lt;/h6&gt;

&lt;p&gt;@@@ 自己的原创思想最重要，读书笔记能帮助我们做到。虽然我不同意作者说一定要读书的时候下评论，我觉得再读的时候写评论会更好更清晰更理智。&lt;/p&gt;

&lt;h6 id=&quot;p109-p112-重读书评以加深思考通过自己的书评我脱离了我习惯的角度看到了完全不同的一本书由此可见对比书评和自己的感想可以让自己变得不那么绝对化只要把书评保存好多读几遍就可以享受多重得读书体验&quot;&gt;p109-p112 重读书评以加深思考。通过自己的书评，我脱离了我习惯的角度，看到了完全不同的一本书。由此可见，对比书评和自己的感想，可以让自己变得不那么绝对化。只要把书评保存好，多读几遍，就可以享受多重得读书体验。&lt;/h6&gt;

&lt;p&gt;@@@ 人在当时的思考方式记录下来是很重要的，这方便日后的反思，我在当时是这么想的，现在这么想的，有什么变化， 为什么会有变化，有没有值得自己借鉴的事情。&lt;/p&gt;

&lt;h6 id=&quot;p115-丢弃不重要的书籍你会有一丝不舍因为可能是你与这本书最后的诀别所以至少要做好读书笔记在想起那本书时才能有所慰藉读书体验比书本身重要&quot;&gt;p115 丢弃不重要的书籍，你会有一丝不舍，因为可能是你与这本书最后的诀别。所以至少要做好读书笔记，在想起那本书时才能有所慰藉，读书体验比书本身重要。&lt;/h6&gt;

&lt;p&gt;@@@ 确实，读书体验比书本重要。如果一些书读过后觉得是浪费时间或者是有问题的，可以丢弃，但最好有些笔记可以留下来，以后还能有点念想，让自己记起还读过这本书，以及书中的内容。&lt;/p&gt;

&lt;h6 id=&quot;p116-请找到那本能够成就自己的书失落的时候走投无路的时候那本书都会成为自己的心灵支柱我们需要一些能与自己长期共处的书比如旅行时想读的书难过时想读的书而寻找它们正是我们平时读书的目的通过读书笔记你就可以找到一本读了它我就可以做自己的书给自己坚实的心理支撑&quot;&gt;p116 请找到那本能够成就自己的书。失落的时候，走投无路的时候，那本书都会成为自己的心灵支柱。我们需要一些能与自己长期共处的书，比如旅行时想读的书、难过时想读的书，而寻找它们正是我们平时读书的目的。通过读书笔记，你就可以找到一本“读了它我就可以做自己”的书，给自己坚实的心理支撑。&lt;/h6&gt;

&lt;p&gt;@@@ 寻找到一本可以和自己作伴很久的书确实很重要，我有一两本书，翻看了很多很多遍也看不厌，每次心情不好时就会拿出来读一下，让自己从悲伤的心情中脱离出来。或者无聊时也是一样，它能置换我的心情，让我更加积极面对生活。&lt;/p&gt;

&lt;h3 id=&quot;第四章-通过重读笔记提高自我&quot;&gt;第四章 通过重读笔记提高自我&lt;/h3&gt;

&lt;h6 id=&quot;p121-书上写的这些我是这么理解的以此为契机我想到了这件事对比之下你会发现只有在读完书后对书中的内容做出反应进行主动思考才能真正掌握这本书的内容读书笔记是一种升华工具可以让读书活动完成从生搬硬套到独创思维的飞跃&quot;&gt;p121 ‘书上写的这些，我是这么理解的…’，‘以此为契机，我想到了这件事…’，对比之下你会发现，只有在读完书后对书中的内容做出反应，进行主动思考，才能真正掌握这本书的内容。读书笔记是一种升华工具，可以让读书活动完成从生搬硬套到独创思维的飞跃。&lt;/h6&gt;

&lt;p&gt;@@@ 这让我想起了以前读书和写作时的情景，我边看书边将书中的内容和我自己的理解写入我的文章当中去，这种主动思考的方式，很容易让我掌握书中的内容。就像作者说的，它可以让独创思维飞跃。&lt;/p&gt;

&lt;h6 id=&quot;p123-向别人表达自己的想法可算是最常见的思维输出哪怕是你自认为非常了解的事情一旦需要向别人说明或写下来还是会出现很多模糊不清的内容正因为这样演讲和写作都需要经过全面的准备才能开始---知道自己要在别人面前表达自己的想法后我们才会去调查那些我们从未在意过的出处资料重新审视自己的观点建立起系统的知识体系你会发现这个补充学习的过程就像从零学起一样需要花费不少精力而能够正视这些辛苦的人最后都是很厉害的人如果掌握再多的信息如果不去积极输出是无法形成知识体系的&quot;&gt;p123 向别人表达自己的想法可算是最常见的思维输出。哪怕是你自认为非常了解的事情，一旦需要向别人说明或写下来，还是会出现很多模糊不清的内容。正因为这样，演讲和写作都需要经过全面的准备才能开始。   知道自己要在别人面前表达自己的想法后，我们才会去调查那些我们从未在意过的出处、资料，重新审视自己的观点，建立起系统的知识体系。你会发现，这个补充学习的过程就像从零学起一样，需要花费不少精力。而能够正视这些辛苦的人，最后都是很厉害的人。如果掌握再多的信息，如果不去积极输出，是无法形成知识体系的。&lt;/h6&gt;

&lt;p&gt;@@@ 没错向别人输出知识，输出思想的方式，是学习效率最高的方式，它会逼迫我们去重新审视自己的观点是否正确，是否严谨，逻辑是否清晰。只有不断去输出的人才能真正掌握这门知识。&lt;/p&gt;

&lt;h6 id=&quot;p124-这里作者提到了重读笔记偶尔还需要把读书笔记完完整整地翻一遍了解自己到目前为止都读了什么书这样的反复输出也可以说是在对自己灌输思想而通过这样的过程已经消化过的信息又经历了一次精细的咀嚼&quot;&gt;p124 这里作者提到了重读笔记。偶尔还需要把读书笔记完完整整地翻一遍，了解自己到目前为止都读了什么书。这样的反复输出，也可以说是在对自己灌输思想。而通过这样的过程，已经消化过的信息又经历了一次精细的咀嚼。&lt;/h6&gt;

&lt;p&gt;@@@ 这里作者强调了重读的重要性，有了读书笔记在重读时就方便多了。多重读几次，可以让知识消化的更彻底，或许还有新的知识或感兴趣的事物出现。&lt;/p&gt;

&lt;h6 id=&quot;p126-p132-这里提了重读的习惯和重读的好处感觉进入死胡同或者有些疲劳时习惯把读书笔记拿出来翻翻看就这样读书笔记成了我日常生活中一个小小的乐趣作者还推荐了几本书卡雷尔恰佩克的各种各样的人美国派乔治奥维尔的一九八四你可以通过读书笔记来丰富本次读书体验也可以偶尔从书架上拿下笔记重读一遍这样的积累会让你真正掌握书中的内容如果说读书笔记是为了丰富自己的思想那么读书笔记的作用就是让自己更有深度&quot;&gt;p126-p132 这里提了重读的习惯和重读的好处。感觉进入死胡同或者有些疲劳时习惯把读书笔记拿出来翻翻看。就这样，读书笔记成了我日常生活中一个小小的乐趣。作者还推荐了几本书，卡雷尔.恰佩克的《各种各样的人》，《美国派》，乔治.奥维尔的《一九八四》。你可以通过读书笔记来丰富本次读书体验，也可以偶尔从书架上拿下笔记重读一遍，这样的积累会让你真正掌握书中的内容。如果说读书笔记是为了丰富自己的思想，那么读书笔记的作用就是让自己更有深度。&lt;/h6&gt;

&lt;p&gt;@@@ 重读读书笔记确实重要，既然我把文字写在电脑中，我就可以用打印机的方式，把它们打印出来再看一遍。用打印出来的方式，可以轻松拿在手上，方便阅读，不断去积累书中的精华，丰富自己的思想，提高自己的深度。&lt;/p&gt;

&lt;h6 id=&quot;p133-比如看完一本书的前半部分后就写一部分笔记写好再去读剩下的部分会比只是从头到尾读一遍理解得更深刻&quot;&gt;p133 比如看完一本书的前半部分后就写一部分笔记，写好再去读剩下的部分，会比只是从头到尾读一遍理解得更深刻。&lt;/h6&gt;

&lt;p&gt;@@@ 确实是这样，读书的时候应该读一部分停下来，做一下笔记，再继续读下面的内容。这样对前面的内容理解了，后面读起来理解的就会更快。&lt;/p&gt;

&lt;h6 id=&quot;p140-写读书笔记可以锻炼对文章结构的组织能力提高思想输出的质量写作技巧会得到大幅提升&quot;&gt;p140 写读书笔记可以锻炼对文章结构的组织能力、提高思想输出的质量，写作技巧会得到大幅提升。&lt;/h6&gt;

&lt;p&gt;@@@ 确实会这样，在写读书笔记的同时，也是对文章结构的一种观察。对于写作能力，特别是阅读理解能力有大幅的提高。&lt;/p&gt;

&lt;h6 id=&quot;p143-这里作者介绍了一种借用三本书的读书笔记来相互交流辩论的方式写读后感文章并得到了自己的学习经验这就好像请三位专家围绕某个问题给自己提意见并让他们进行对比和讨论一样经过整理总结以后渐渐就会得出一个假设其实就是这样的吧但这始终是个假设还需要重读一遍做过记号的内容和写好的读书笔记来验证刚才的假设&quot;&gt;p143 这里作者介绍了一种借用三本书的读书笔记来相互交流辩论的方式写读后感文章并得到了自己的学习经验。这就好像请三位专家围绕某个问题给自己提意见，并让他们进行对比和讨论一样。经过整理总结以后，渐渐就会得出一个假设–‘其实就是这样的吧’但这始终是个假设，还需要重读一遍做过记号的内容和写好的读书笔记，来验证刚才的假设。&lt;/h6&gt;

&lt;p&gt;@@@ 通过三本书的读书笔记，尽然可以做到这样有趣的辩论，真是好厉害。下次我可以试试看。把三本书的读书笔记放在一起，写一部有趣的辩论赛，最后得出一个自己的答案。&lt;/p&gt;

&lt;h6 id=&quot;p163-p165-过去的读书法经常会强调读书要多看多重复确实要想加深理解只能多读几遍有句话叫读书百遍其义自见由此可见重读是加深理解的必要方法如果有了读书笔记就可以立即满足这种找书的重读参考读书笔记也是一项以笔记内容为线索进行回忆的过程只要认真阅读笔记就能在重读时会想起目标书籍里大部分内容这样看来根据需要进行重读才是重现实的做法&quot;&gt;p163-p165 过去的读书法经常会强调‘读书要多看多重复’。确实，要想加深理解，只能多读几遍。有句话叫‘读书百遍，其义自见’，由此可见重读是加深理解的必要方法。如果有了读书笔记，就可以立即满足这种找书的重读。参考读书笔记，也是一项以笔记内容为线索进行回忆的过程。只要认真阅读笔记，就能在重读时会想起目标书籍里大部分内容。这样看来，根据需要进行重读才是重现实的做法。&lt;/h6&gt;

&lt;p&gt;@@@ 作者提到了重读的重要性，‘读书百遍，其义自见’。利用读书笔记就能快速回忆起自己读过的书，那么重读会更加容易。在想看想读的时候拿出读书笔记来重读，是最佳的重读方式。&lt;/p&gt;

&lt;h6 id=&quot;有兴趣的问题时不要无动于衷应该马上查找翻阅笔记在参考读书笔记的同时回想当时的思路再与现在更成熟的思想相比较一定会获得更多的知识和信息这样的读书方法才能真正让书融入自己的思想是普通的重读无法比拟的&quot;&gt;有兴趣的问题时不要无动于衷，应该马上查找、翻阅笔记。在参考读书笔记的同时回想当时的思路，再与现在更成熟的思想相比较，一定会获得更多的知识和信息。这样的读书方法才能真正让书融入自己的思想，是普通的重读无法比拟的。&lt;/h6&gt;

&lt;p&gt;@@@ 兴趣驱动是关键，有了兴趣要立马执行同样关键。刨根问底的精神在所有领域中都是值得提倡的，在学习中特别有效。&lt;/p&gt;

&lt;h3 id=&quot;第五章-让读书体验更充实的19个技巧&quot;&gt;第五章 让读书体验更充实的19个技巧&lt;/h3&gt;

&lt;h6 id=&quot;p169-p200&quot;&gt;p169-p200&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	1.不依赖网络多用参考书。

	2.多读百科全书，百科全书就是一种能将好奇心与思想输出结合起来的工具。

	3.通过订阅来提醒自己，通过订阅杂志来打破大脑内容程式化的协调关系。

	4.书店里不只有新书旧书，还有意外的发现。旧书中那些与现代视角截然不同的思想输入可以让自己对事物的见解得到进一步的飞跃。

	5.由浅入深吃透难懂图书。如果书里的内容太难，实在看不进去，倒不如退一步，看看漫画版，解说版，图解版，精编版，讲演录，交流版，打字版，现代译制版。遇到难题时，不要勉强自己正面强攻，要先找到一个比较简单的切入点再发起进攻。把理解难题的过程或感想写在笔记本里，也是可以促进理解的。

	6.把书堆成山，读书不再难。‘一字板书架’

	7.把名著放在枕边。名著读起来有些乏味，但经过几年的沉淀以后就会知道它的价值所在。经得起时光洗礼的书一定有流芳百世的价值。

	8.常带三本书同时阅读。以应对看腻了某本书的情况。

	9.在家里的每个角落放满书。

	10.去掉外封，让读书变得轻松

	11.杂志要边撕边读

	12.不能小看了有声文学。有声文学可以成为比较难的内容的‘软肋’。有时候针对某个领域，听演讲、看视频比读书更容易。所以有声图书也是种‘软肋’。

	13.准备一支你想珍藏的钢笔。心理学家指出，处于大脑构造的原因，简单作业的过程会带动起人们的热情，使其专心致志投入工作，这就是工作热情。晚上收拾好房间以后反而会变的精神饱满也是因为这个原因。

	14.名言要贴在显眼处

	15.读后感从摘抄开始。‘引用是魔杖’，引用文章不仅可以增加说服力，还更容易组织文章，会让下笔更流畅。

	16.用复印机让读书更方便。

	17.用‘独创版权页’记录书的履历。记录内容有：购买日期，阅读结束日期，标记结束日期，制作读书笔记日期。

	18.书与笔记交叉使用。既然有时候看了读书笔记会想再看看原书，那么自然有时候再看过原书后又会想看读书笔记。就这样，我们可以交叉使用书与笔记，让读书生活变得更加丰富。

	19.书架是读书生活的基地。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;@@@ 这里有些我无法体会到，比如，1，2，9，19，11，14，19。我无法感受到这几点，所以我并不认为这几点是多么有效的。另外一些又觉得做起来确实有难度但又感觉很有用，包括，3，4，7，13，18。&lt;/p&gt;

&lt;p&gt;@@@ 其余的我认为是需要的，包括5中的，对比较难懂的书应该由浅入深的方式，这样才不会打击到自信心。6的一字板堆放方式会很有成就感。12的有声图书确实降低了不少门槛，让更多有读书障碍的人慢慢养成读书的习惯包括我自己。15的引用文章可以带来大量的思考，增加文章的说服力。16的复印机确实是帮助我们学习的好帮手，大大便捷了学习途径。17的有助于回忆自己所读过的书当时的情景。18的笔记与原书交叉阅读，会更加快更加深的帮助我们理解书中的内容。&lt;/p&gt;

&lt;hr /&gt;

&lt;h2 id=&quot;第三遍读摘抄和评论的内容并筛选更精简内容并融合成文章&quot;&gt;第三遍读摘抄和评论的内容并筛选更精简内容并融合成文章&lt;/h2&gt;

&lt;p&gt;未开始&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>励志英文(五) 永不屈服 Unbroken</title>
   <link href="http://www.luzexi.com/2020/03/07/%E5%8A%B1%E5%BF%97%E8%8B%B1%E6%96%875"/>
   <updated>2020-03-07T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/03/07/励志英文5</id>
   <content type="html">&lt;iframe frameborder=&quot;0&quot; src=&quot;https://v.qq.com/txp/iframe/player.html?vid=o3077srfgyn&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;h3 id=&quot;原文如下&quot;&gt;原文如下:&lt;/h3&gt;

&lt;p&gt;you can’t connect the dots looking forward, you can only connect them looking backwards.&lt;/p&gt;

&lt;p&gt;So you have to trust that the dots will somehow connecting your future, you have to trust in something.&lt;/p&gt;

&lt;p&gt;Your god, destiny, life, karma, whatever. Because believing with the dots will connect down the road, will give you the confidence to follow your heart, even when it leads you off the well-worn path and that will make all the difference.&lt;/p&gt;

&lt;p&gt;Your time is limited so don’t waste it living someone else’s life, don’t be trapped by dogma which is living with the results of other people’s thinking.&lt;/p&gt;

&lt;p&gt;Don’t let the noise others opinion ground drown out your own in the voice. You’ve got to find what you love and that is a true for work as it is for your lovers.&lt;/p&gt;

&lt;p&gt;Your work is going to fill large part of your life and the only way to be truly satisfied is to do what you believe this great work and the only way to do great work is to love what you do. If you haven’t found it yet, keep looking and don’t settle. Have the courage to follow your heart and intuition. They somehow already know what you truly wanted to become.&lt;/p&gt;

&lt;p&gt;﻿But you’re going to have some ups then you’re gonna have some downs.&lt;/p&gt;

&lt;p&gt;Most people give up on themselves easily. You know the human spirit is powerful. There’s nothing is powerfull, it’s hard to kill the human’s spirit.&lt;/p&gt;

&lt;p&gt;Anybody can feel good when they have their health, their bills the pain, they have happy relationships. Anybody could be positive then, anybody can have a large vision then, anybody can have faith under those kinds of circumstances.&lt;/p&gt;

&lt;p&gt;The real challenge of growth mentally emotional spiritual comes when you get knocked down.&lt;/p&gt;

&lt;p&gt;It takes courage to act.&lt;/p&gt;

&lt;p&gt;Part being hungry when you have been defeated. That takes courage to start over again.&lt;/p&gt;

&lt;p&gt;Fear kills dreams, fear kills hope, fear put people in the hospital, fear can age you, can hold you back from doing something that you know within yourself that you’re capable of doing but it will paralyze you.&lt;/p&gt;

&lt;p&gt;At the end of your feelings is nothing, but at the end of every principle is a promise. Behind your liitle feelings, it might not be absolutely nothing at the end of your lip feelings, but behind every principle is a promise.&lt;/p&gt;

&lt;p&gt;And some of you and your life the reason why you not at your goal right now, because you just all about your feelings.&lt;/p&gt;

&lt;p&gt;﻿You all on your feelings you don’t feel like wakeing up, so who does.&lt;/p&gt;

&lt;p&gt;Every day you say no to your dreams, you might be pushing your dreams back a whole six months a whole year. That one single day that one day you didnt get up could push your stuff back, I dont know how long.&lt;/p&gt;

&lt;p&gt;Dont allow your emotions to control you, we are emotional but we you want begin to discipline your emotion. If you dont discipline and contain your emotions they wont use you.&lt;/p&gt;

&lt;p&gt;You want it and you’re gonna go all out to have it. It’s not going to be easy when you want to change. It’s not easy if it were in fact easy everybody to do it, but if you’re serious you’ll go out.&lt;/p&gt;

&lt;p&gt;I’m in control here. I’m not gonna let this get me down. I’m going to let this destroy me. I’m coming back, 
and I’ll be stronger and better because of you have got to make a declaration.&lt;/p&gt;

&lt;p&gt;This is what you stand for. You’re standing up for your dreams. You’re standing up for peace of mind. You’re standing up for helping take full responsibility for your life.&lt;/p&gt;

&lt;p&gt;It’s suck where you are and the responsibility that you’re going to take yourself where you want to go.&lt;/p&gt;

&lt;p&gt;You could decide that I’m going to live each day as if it were my last.&lt;/p&gt;

&lt;p&gt;Living your life with passion with some dry.&lt;/p&gt;

&lt;p&gt;﻿That you’re going to push yourself the last chapter to your life has not been written yet. It doesn’t matter about what happened yesterday, it doesn’t matter what happen to you what matters what do you wanna talk about.&lt;/p&gt;

&lt;p&gt;This year I will make this goal become a reality. I wont talk about it more. I can, I can, I can.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(三十七) 良性循环的建立与恶性循环的改善</title>
   <link href="http://www.luzexi.com/2020/02/29/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A837"/>
   <updated>2020-02-29T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/02/29/思路探讨37</id>
   <content type="html">&lt;p&gt;良性循环和恶性循环存在于我们生活工作的方方面面，我们应该如何建立良性循环，如何改善恶性循环呢。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;最近看了很多大萧条时期的文章，在那个时间段的人，面对大环境不好的情况折射出来的无力感，造成人们的沮丧和低落情绪弥漫着整个社会。&lt;/p&gt;

&lt;h6 id=&quot;一个人情绪的低落还可以通过被周围人和环境的影响而恢复活力但整个社会的情绪低落导致的更加根深蒂固的情绪下沉让那些本来可以开开心心的情绪被周围的环境拉下去变得低沉&quot;&gt;一个人情绪的低落，还可以通过被周围人和环境的影响而恢复活力，但整个社会的情绪低落导致的，更加根深蒂固的情绪下沉，让那些本来可以开开心心的情绪，被周围的环境拉下去变得低沉。&lt;/h6&gt;

&lt;p&gt;对于整个社会的情绪我们能做的很少，我们能做的就是管理好个人的情绪与积极性，从而用积极的一面来影响周围的人和事。&lt;/p&gt;

&lt;p&gt;那么怎么才能使自己恢复活力，恢复情绪，或者应该说是恢复对生活的信心呢？&lt;/p&gt;

&lt;h6 id=&quot;要努力让自己高兴起来即使假笑也行让情绪假假的恢复正常运转大部分时候情绪都是通过行动来恢复的即使是假笑也会慢慢变成真笑从而恢复情绪的低落&quot;&gt;要努力让自己高兴起来，即使假笑也行，让情绪假假的恢复正常运转。大部分时候情绪都是通过行动来恢复的，即使是假笑也会慢慢变成真笑，从而恢复情绪的低落。&lt;/h6&gt;

&lt;p&gt;为了恢复情绪，我们可以在行动上改变下自己，比如可以看喜欢看的电影特别是喜剧电影，让自己情绪可以假装被恢复。也可以去运动一下(如果是一直在运动的人，可以改变一下运动方式)，让行动和汗水恢复理智。可以去与很久没见的朋友见上一面，聊聊过去开心的往事。&lt;/p&gt;

&lt;h6 id=&quot;万事开头难想要人从一个环境或情绪中跳出来是比较困难我们应该更加积极去做第一步起了个开头后面就会好多了就像动摩擦比静摩擦要在阻力上要小的多那样动起来就没有静止的那么糟糕了我们可能看到的会是另一番景象&quot;&gt;万事开头难，想要人从一个环境或情绪中跳出来，是比较困难，我们应该更加积极去做第一步，起了个开头后面就会好多了，就像动摩擦比静摩擦要在阻力上要小的多那样，动起来就没有静止的那么糟糕了，我们可能看到的会是另一番景象。&lt;/h6&gt;

&lt;p&gt;先动起来开个头，只是第一步，我们要做的还有很多。想建立良性循环，我们需要坚持的理念比较重要，这个理念可能是’自律‘，抑或可能是’吃得苦中苦方为人上人‘，等等。理念一定要是积极的，正向的，才是能够被我们接受或者被大众接受从而影响到我们自己，形成一个良性的反馈循环。&lt;/p&gt;

&lt;p&gt;建立从无到有的良性循环的难度比从恶性循环开始改善再到转换到良性循环要容易的多。我们身边到处都有这样的恶性循环的存在，包括我们自己的生活在内，情绪常失控，失落的情绪环绕在身上，懒惰懒散的习惯，夫妻不和，家庭不和，父子不和，父女不和，工作事业不顺，同事间的隔阂，团体内形成的恶性竞争，甚至是国家之间的仇视等，我们冷静下来想想其实就是日积月累的过程，在某个点彼此的观点存在了差异，后面机缘巧合下没有去进行改善，最终导致越来越严重，最终一发不可收拾形成了一个恶性循环。&lt;/p&gt;

&lt;p&gt;说实在，恶性循环确实很难处理。但是如果你能冷静下来，跳出恶性循环，看到人的本质和本性，从利他的角度去处理事情的话，就会慢慢得到改善。&lt;/p&gt;

&lt;p&gt;这需要我们有足够的耐心，耐力，毅力，冷静的态度，开放的心态，还包括抗压能力等。即使拥有这样的多种能力，也不是百分之百处理好日就积累起来的恶性循环的，有时我们应该放过自己。当我们去处理这种类型的恶心循环时，我们需要放平心态，告诉自己要尽自己努力去改善循环，尽力去改变各个环节的情绪、环境、氛围，但也要时刻做好准备告诉自己这是场持久战，即使自己败下阵来也是正常的，只要我做了贡献，接下去的工作将会交给另一个人继续工作，这样长年累月的积极改善，总会有看到出头的日子的，我即使调节失败也已经为整个事情做出了应有的贡献。&lt;/p&gt;

&lt;h6 id=&quot;我们应该首先调整自己不能让自己卷入恶性循环自己不能崩溃才有希望对事情做出大的贡献在积极面对的同时我们也应该评估恶性循环的改善百分比以及还需要多久才能彻底扭转局面好让自己有些许心理准备&quot;&gt;我们应该首先调整自己，不能让自己卷入恶性循环，自己不能崩溃，才有希望对事情做出大的贡献。在积极面对的同时，我们也应该评估，恶性循环的改善百分比，以及还需要多久才能彻底扭转局面，好让自己有些许心理准备。&lt;/h6&gt;

&lt;h6 id=&quot;最后我们应该吸取教训我们在改善恶性循环的同时应该吸取教训如何在以后的工作生活中避免这样的事情发生也同时要对建立生活中和工作中的良性循环的工作增加更多的积极性用利他心态去做事用平和的心态面对失败释放压力减少焦虑让自己可以做事时更专心更专注思路更宽广心态更开放接纳度更高包容性更强更能明事理世界都是有规则的遵循规则推动规则导向成功才是真正的有意义的&quot;&gt;最后我们应该吸取教训，我们在改善恶性循环的同时，应该吸取教训，如何在以后的工作生活中，避免这样的事情发生。也同时要对建立生活中和工作中的良性循环的工作增加更多的积极性，用利他心态去做事，用平和的心态面对失败，释放压力，减少焦虑，让自己可以做事时更专心更专注，思路更宽广，心态更开放，接纳度更高，包容性更强，更能明事理。世界都是有规则的，遵循规则，推动规则，导向成功才是真正的有意义的。&lt;/h6&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(四十) 对赚钱和学习的一些思考(下)</title>
   <link href="http://www.luzexi.com/2020/02/27/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A840"/>
   <updated>2020-02-27T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/02/27/思路探讨40</id>
   <content type="html">&lt;h3 id=&quot;前面讲的都是概念层的东西也可以认为是战略方向下面讲些战术上的战略上要对只有方向正确了战术才有效即做人要做对做事才能事半功倍&quot;&gt;前面讲的都是概念层的东西也可以认为是战略方向，下面讲些战术上的。战略上要对，只有方向正确了，战术才有效。即做人要做对，做事才能事半功倍。&lt;/h3&gt;

&lt;p&gt;这里除了拥有深度的技能外，还需要我们拥有一些普世的能力，即对世界的理解，和对人性的洞察力，这是两种能力是基本功，基本功不扎实，我们的天花板(瓶颈)就会比较低。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;其实赚钱最容易的方法是，你专心做好自己喜欢的事，做的深点做的专一点至少比普通人做深做精2-3倍，肯定能赚到钱，温饱基本不用愁。&lt;/p&gt;

&lt;p&gt;如果你能做精好几样那就会很厉害。只是我们的精力和意志力有限，对一样事物做到比普通人精2-3倍不容易。但仍然有人能做到拥有多种比较深的能力，为什么呢？我们来探讨下。&lt;/p&gt;

&lt;h6 id=&quot;困难到底在哪&quot;&gt;困难到底在哪？&lt;/h6&gt;

&lt;p&gt;1.持续的积累&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;各种能力的学习和练习，需要长年累月的积累。

这是一个不可逾越的障碍，没有谁能在短时间里获得某种成就，都是靠着长时间的积累，成年累月的坚持才有突出重围的机会。

很多人都是十几年，甚至是几十年的积累才有现在的成就、知识、智慧(包括经验)、人脉(包括人气、信誉)、团队、家庭(或家族)。

原来我对有效积累有些固执，以前我认为有些积累是无效的(比如我一个程序员去学如何做裁缝，学习如何制作一件衬衫)，但我现在不这么认为了，现在我认为这是学习能力的体现，你对你敢兴趣的事情，能，并且，愿意，抽出时间来去学习到达精通，这是非常难能可贵的表现。现在反而担心的有人对自己敢兴趣的事物冷漠不懈，不去埋头研究而只在浅层的皮毛上看看，没有学习的欲望，这是比较不好的表象。

有时候想想，这人生确实是一场马拉松赛跑，有的人是兔子，有的人乌龟，也有极少数人是兔子的速度乌龟的毅力(只能望其项背)。确实一山还有一山高，人比人比死人，不过没关系，大部分人(99.9%)要么是乌龟要么是兔子，我们总是停停走走，而且越往上走越难越慢，还会时不时倒退。我们有的是机会和时间来追赶他们，就像古人说的，任何时候奋发图强都为时不晚，我们只需静下心来，沉淀自己。

持续的积累很难，不是我们笨而是真的意志力不够用。每天的坚持都要消耗许多意志力，导致当意志力消耗殆尽时，我们就再也无法坚持下去了，放弃是大概率事件。

为什么有人就能坚持下来呢？我原来一直以为是人与人之间的意志力是有区别的。后来慢慢改正了这个观点，特别是看了《微习惯》这本书后，再经过我实践的检验和对理论知识的梳理，明白了这本书所教我的’如何建立习惯‘的技巧相当有用。其实坚持可以不用这么痛苦，越痛苦得坚持越难以持续下去，一意孤行还容易导致心理扭曲。书中主要讲解了，大脑对难度比较高的事物抵制的过程，书中教会我们应该如何运用巧妙的方法骗过大脑的抵抗，让困难的习惯养成计划变得简单轻松。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;2.对人性的洞察&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;不经历风雨怎能见彩虹，我们都是人，脱离大众的思维不容易，能从上帝的视角洞察人性的通常不是一般人，需要积累足够多的知识，并且受到足够多的挫折，经历足够多的的曲折，才能比别人看到更多的东西，感受到更多别人感受不到的体会。

我们需要深刻和彻底的明白，这个世界是多样化的，是个由60亿不同种类的人创造而成，每个人都是一个种类而不是某个种类中的一个，他们有自己的共性也有自己的不同。

理解这点，才能明白包容和同理心占据了整个人性洞察的重要位置。能看到多少个角度的心里状态，能明白和理解多少人，才是洞察人性的重要标志。

比如《乌合之众》这本书就很好的诠释了人性在大众舆论中的表现。

以及股票的K线图。如果你对股票的K线图有过深入的学习的话，会发现K线图其实就是大众的情绪表现，大众情绪的起伏与宣泄从K线图上表现的淋漓尽致。

包括我前面说的，人与人之间的信任机制，也是有用的洞察人性的途径。我们建立信任，也同时在摧毁信任，它既是强大的也是脆弱的，洞察其中的奥秘，能让我们受益匪浅。

我们要看清世界是扭曲的，但也要明白这种扭曲是正常的，人性是永远不会改变的，但也要明白人性是可以理解和包容的。

在一个朋友的签名上写着，“贪安稳就没有自由，要自由就要历些风险，只有这两条路”。确实如此，他洞察到了世界和人性，我们这个世界不存在安稳的自由，虽然我们一直在追求这个目标，但大部分时候我们都身不由己。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;3.积极勤奋，自我学习自我完善&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;我们无论如何都逃不开积极和勤奋两个关键点，更多时候积极乐观的态度是长胜的重要关键。倘若没有积极乐观的一面，消极和悲观的情绪只会让你的思维变得更狭窄并且固步自封。倘若在积极乐观的同时如果能做到谨慎行事，就会有更好的效果。

勤奋则是所有做人做事的基础，它是所有基础的基础，就像我们脚下的地面一样，勤奋就是这个地，没有地面我们就会坠入万丈深渊。我们要保持这个地，有的人时时刻刻保持勤奋，地就厚，起点会高一点，有的点不是很勤奋，地就薄，起点会低一点。

高低，前后，这种你追我赶的事，大部分时候，落后一点没有关系，甚至落后很多也无妨，现在开始追赶从来都不会晚。我们更多时候是被自己的情绪打败，失落，焦虑，害怕，自责，导致最终放弃自己，丧失勇气，丧失信心，丧失动力。

勤奋虽然是基础，但它是有顶的，这个顶部其实很容易就达到，只靠勤奋是行不通的，天花板会比较低，很快就会到达瓶颈点。

因此我们还需要自我学习和自我完善，以及不停的学习和不停的完善。我从一本名为《微精通》的书中受到颇多的启发。

它说，见多识广的人对生活有更浓厚的兴趣和更强的能力，并且更快乐，更坚强。他们思维开放而不因循守旧。创业，创新，创造性活动都需要有博识的眼光。博学存在于科学的顶峰，生机勃勃。

它说，人不仅要从知识的深度上学习到更精湛的技艺，还要从广度上学习到更多种的思路并将这些知识融合在一起创造出更多的解决方案。

我很认同书中的观点，在工业发展还没这么发达的时期，那时我们分工并没有这么明确，一个设计师一个工程师，其实什么都要懂，什么都要学，所以那时很多科学家都拥有惊人的创造力，而现在我们的分工太过于明细，每个人只关心自己那条线上的知识线，那时完全不够的，或者说这样是不能拥有良好的创造力的。

其实拥有多条线的兴趣和知识，就能拥有更多的机会从多个方向上获得成就感，良好的正向反馈是继续向前迈进的关键，不间断的获得来自各个方向的成就或赞扬，就能随时保持积极乐观的心态面对学习，对学习这种本身看起来枯燥乏味的事情就会显得更有趣。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;4.积极的寻求正向的反馈&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;积极的寻求正向的反馈尤为重要，它是我们坚持下去的很大一个关键。正向的鼓励与成就能很大程度上促进我们的继续坚持下去继续往前奔跑，就像孩子们在学习的过程中，每完成一项任务，每拼完一个积木城堡，每完成一次作业，每练习一次钢琴，每听完一次线上的英语课，都要寻求父母的鼓励和赞扬那样，我们成年人也同样需要，并且需要的更多。

但大部分时候没人会来‘表扬’你，鼓励你，赞同你，我们就得自己主动需求反馈。这其实是种能力，一种寻求自信，寻求肯定的能力。

虽然很多时候我们的成就实在太小，根本不起眼，没有人会在意，我们一方面要明白自己的积累还不足够的多，另一方面要积极改变自己寻求正向反馈的方式，比如分享的文章太枯燥，一方面要改善自己的写作技巧，另一方面就试着多加点有趣的图片，又比如英语学习太枯燥，那就把有趣的英文文章朗读出来分享给大家听，得到大家的赞许，又比如写代码太枯燥，那就把每日写代码的页面用视频记录下来，加入歌曲和快进，大家看了都会觉得可爱有趣生动。

积极寻求正向反馈很多时候需要我们以不害臊的心态去面对。这很正常，每个人的视角和理解程度都不同，不是每个人都认同你，有时甚至完全没有人欣赏你也不用气馁，我们练的是内功，自己欣赏自己最重要，做到自己能够做的，今天就足够了。剩下的便是别人的事。
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;最后总结积累是我们沉淀自我的最好方法及早的认识到这个人生的重要方向对我们来说至关重要但成年人的世界已经不再是那么简单随着年龄的逐渐增大我们需要处理的事情越来越多越来越复杂积极勤奋是积累过程中最重要的法宝积极勤奋还不够自我学习与自我完善也是我们需要提倡的在这个过程中枯燥乏味和疲惫阻碍着我们于是积极的寻求正向反馈就变得尤为重要它是击败枯燥乏味和疲惫的重要武器&quot;&gt;最后总结，积累是我们沉淀自我的最好方法，及早的认识到这个人生的重要方向对我们来说至关重要。但成年人的世界已经不再是那么简单，随着年龄的逐渐增大，我们需要处理的事情越来越多，越来越复杂。积极勤奋是积累过程中最重要的法宝，积极勤奋还不够，自我学习与自我完善也是我们需要提倡的，在这个过程中，枯燥乏味和疲惫阻碍着我们，于是积极的寻求正向反馈就变得尤为重要，它是击败枯燥乏味和疲惫的重要武器。&lt;/h6&gt;

&lt;h6 id=&quot;我们都想变得更强大什么是强大我一直在琢磨这件事是学习成绩高吗还是编程技术好还是财富多还是名誉大还是权利大还是眼界广阔还是知识渊博还是生活中的智慧这些都不是绝对的地球上60亿人每个人都不一样而且每个人在人生中的每个阶段也不一样有些人有知识有些人有钱有些人有智慧有些人有眼界我们不需要做到这么完美什么都要只要在当下这个阶段里拼尽自己全力以赴就足够少一点欲望多一点积极的态度不要辜负了当下这个阶段的时光当下全力以赴的你就是对人生下一个阶段最好准备工作&quot;&gt;我们都想变得更强大。什么是强大？我一直在琢磨这件事，是学习成绩高吗，还是编程技术好，还是财富多，还是名誉大，还是权利大，还是眼界广阔，还是知识渊博，还是生活中的智慧，这些都不是绝对的，地球上60亿人每个人都不一样，而且每个人在人生中的每个阶段也不一样，有些人有知识，有些人有钱，有些人有智慧，有些人有眼界，我们不需要做到这么完美什么都要，只要在当下这个阶段里拼尽自己全力以赴就足够，少一点欲望，多一点积极的态度，不要辜负了当下这个阶段的时光。当下全力以赴的你就是对人生下一个阶段最好准备工作。&lt;/h6&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(三十九) 对赚钱和学习的一些思考(上)</title>
   <link href="http://www.luzexi.com/2020/02/15/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A839"/>
   <updated>2020-02-15T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/02/15/思路探讨39</id>
   <content type="html">&lt;h3 id=&quot;一直来提倡要想尽办法开拓自己的思路对事物对世界对人性我们都要想方设法从远到近从宏观到微观各个角度观察思考但人的思维定式很难突破很多事物概念想法我们都无法理解没关系我们还有时间总能想明白的不要被自己的情绪打败静下心来沉淀自己&quot;&gt;一直来提倡要想尽办法开拓自己的思路，对事物，对世界，对人性，我们都要想方设法从远到近，从宏观到微观各个角度观察思考。但人的思维定式很难突破，很多事物、概念，想法我们都无法理解。没关系，我们还有时间，总能想明白的。不要被自己的情绪打败，静下心来，沉淀自己。&lt;/h3&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;疫情面前我又重新对人生的道路审视了一下，当然健康是人生的第一重要的事情，没有人本身这个主体其他都是没有意义，所以健康最重要。只有健康肯定也满足不了我们日益增长的需求，所以我们探讨下生活，看看怎么走才，正确，得体。&lt;/p&gt;

&lt;p&gt;我们把生活想的太单纯，比如说很多人认为成年后都是为了挣钱而活，这个想法就太单纯。享受愉快的生活不需要多少钱，有些情况下不需要钱，而且有不少情况’钱‘只会破坏愉快的生活。&lt;/p&gt;

&lt;h6 id=&quot;我们生活在群体里人是群居动物离不开群体对群体有帮助有贡献的受人追捧对群体有害的则受人排斥钱在这个大群体中代表了信任度有些时候因为人性的问题它变的有点扭曲但大体上都是按照正规的方向在运转就像教科书里写的钱是国家的信用货币是建立国家信用体系的重要指标&quot;&gt;我们生活在群体里(人是群居动物，离不开群体)，对群体有帮助有贡献的受人追捧，对群体有害的则受人排斥。钱在这个大群体中代表了信任度(有些时候因为人性的问题它变的有点扭曲，但大体上都是按照正规的方向在运转)，就像教科书里写的，钱是国家的信用货币，是建立国家信用体系的重要指标。&lt;/h6&gt;

&lt;h5 id=&quot;因此如何赚钱其中一个比较重要的要点是如何取得别人的信任特别是如何取得陌生人的信任只有取得了他们的信任他们才能相信你愿意把钱交给你买你的产品信任和金钱的关系就体现在这里&quot;&gt;因此如何赚钱其中一个比较重要的要点是，如何取得别人的信任，特别是如何取得陌生人的信任，只有取得了他们的信任，他们才能相信你，愿意把钱交给你买你的产品，信任和金钱的关系就体现在这里。&lt;/h5&gt;

&lt;p&gt;人生的复杂度是随着年龄逐级上升的，从只关心吃，后来关心玩，再后来关心成绩的竞争，再后来关心男女关系，人际关系，再后来关工作技能，工资收入，婚嫁，升职加薪创业，孩子，教育，家族兴旺，40-50岁的生存，退休，儿女的婚嫁，孙子孙女的养育等等等。&lt;/p&gt;

&lt;p&gt;我们一直在一步步往更加复杂的层级走去。于是我的想法也越来越复杂，就像我以前写的关于很多我所想我所做的那些文章那样，想法越来越多，思路越来越复杂，但最终所有复杂的事物都还是归于简单的几个道理来支撑，这就是我们人类的思维模式，从简单到复杂，再从复杂归于简单。&lt;/p&gt;

&lt;h6 id=&quot;我不是有钱人也没赚到钱我希望与大家一起共同摸索与探讨我知道最终的那个点其实是简单的只是现在我还没领悟到比如曾经就有前辈告诉我只要好好工作就行了生活就会美好简单的道理蕴含着复杂的思考与经历那时我无法理解这些年通过自己在生活遇到的错误与正确后通过自己的思考与反思才能理解他们其实我也知道赚钱的奥义最终肯定归于一个或几个简单的道理只是现在我无法理解我正在一步步逼近真相&quot;&gt;我不是有钱人，也没赚到钱，我希望与大家一起共同摸索与探讨。我知道最终的那个点其实是简单的，只是现在我还没领悟到，比如曾经就有前辈告诉我只要好好工作就行了生活就会美好，简单的道理蕴含着复杂的思考与经历，那时我无法理解，这些年通过自己在生活遇到的错误与正确后通过自己的思考与反思才能理解他们。其实我也知道赚钱的’奥义‘最终肯定归于一个或几个简单的道理，只是现在我无法理解，我正在一步步逼近真相。&lt;/h6&gt;

&lt;p&gt;随着我的自律生活的加强，我的人生体验也有更高的境界，与之带来的思路也更加开阔，最近感悟到，赚钱有许多种方法，当你的能力的种类变多了，各种能力上有比较高的深度时，加上你对这个世界的洞察，特别是对人性的洞察到了一个比较深的程度时，会发现其实赚钱并不难。所以我现在才明白为什么超级学霸(超强能力者)喜欢学习这么多不同种类的技能，因为他们知道能力越多，面对困难和问题时思路越多，解决问题的可能性越大，成功的概率就越大。&lt;/p&gt;

&lt;h3 id=&quot;上面讲的都是概念层的东西下篇讲战术战略上要对只有方向正确了战术才有效即做人要做对做事才能事半功倍&quot;&gt;上面讲的都是概念层的东西，下篇讲战术。战略上要对，只有方向正确了，战术才有效。即做人要做对，做事才能事半功倍。&lt;/h3&gt;
</content>
 </entry>
 
 <entry>
   <title>防电话骚扰防呼死你日志</title>
   <link href="http://www.luzexi.com/2020/01/31/%E9%98%B2%E7%94%B5%E8%AF%9D%E9%AA%9A%E6%89%B0-%E9%98%B2%E5%91%BC%E6%AD%BB%E4%BD%A0%E6%97%A5%E5%BF%97"/>
   <updated>2020-01-31T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/01/31/防电话骚扰-防呼死你日志</id>
   <content type="html">&lt;p&gt;前段时间去二手市场买了个东西，因为东西是坏的给退了回去，没想到收到满满恶意，对方在网上买了呼死你恶意骚扰服务开始攻击，模拟各种国外的电话打进来来骚柔你，还可以定时或自动打，一直持续了好几个月，现在还在持续。前面被骚扰时也很心烦，但后来慢慢变得从容了，也积累了一些经验，能够防治这些‘垃圾’人的恶意。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;我本身是防为主，而不是攻，所以只谈防，至于攻则另说，因为攻容易上火，容易影响生活和工作，我不希望影响正常的生活和工作。如果对方有进一步激进或违法的行为则再进行反击。我也记录了很多对方的证据，通过打电话报警和去派出所报警的方式试图通过法律手段警告和阻止对方，也曾想过用律师诉讼的方式将对方绳之以法。但警方辩解说难于取证收效甚微，律师则费用比较大，所以不到万不得已不会浪费金钱和时间去与之对抗。&lt;/p&gt;

&lt;p&gt;下面是我总结的一些防治措施:&lt;/p&gt;

&lt;h6 id=&quot;1开启陌生人来电挂断设置只有电话簿中的人才能打通和接听&quot;&gt;1.开启陌生人来电挂断设置，只有电话簿中的人才能打通和接听。&lt;/h6&gt;

&lt;h6 id=&quot;2取消移动的境外电话提醒业务具体查百度或者拨打10086人工服务器要求取消&quot;&gt;2.取消移动的境外电话提醒业务。具体查百度或者拨打10086人工服务器要求取消。&lt;/h6&gt;

&lt;h6 id=&quot;3下载防骚扰大师或者手机管家等app录入允许拨通的电话比如快递等这个只能防住被录入的骚扰电话没有被录入的无法拦截&quot;&gt;3.下载防骚扰大师，或者手机管家等app，录入允许拨通的电话，比如快递等。这个只能防住被录入的骚扰电话，没有被录入的无法拦截&lt;/h6&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;h6 id=&quot;4开通移动的高频骚扰电话防护措施拨打10086人工服务器要求开通这个是针对高频次电话的号码进行屏蔽的感觉不是很有用&quot;&gt;4.开通移动的高频骚扰电话防护措施。拨打10086人工服务器要求开通。这个是针对高频次电话的号码进行屏蔽的，感觉不是很有用。&lt;/h6&gt;

&lt;h6 id=&quot;5开启移动电话服务公众号机伶防御各种骚扰她里面有个服务是开启国外电话防骚扰但也不能完全屏蔽呼死你的商家他们会利用一些漏洞用模拟普通手机的电话来骚扰你所以呼死你服务是防不胜防开启陌生人屏蔽是必要的手段机伶可能只有移动服务商才提供联通或电信的暂时不清楚需要问下客服&quot;&gt;5.开启移动电话服务公众号，“机伶”，防御各种骚扰。她里面有个服务是开启国外电话防骚扰，但也不能完全屏蔽呼死你的商家，他们会利用一些漏洞，用模拟普通手机的电话来骚扰你。所以呼死你服务是防不胜防，开启陌生人屏蔽是必要的手段。‘机伶’可能只有移动服务商才提供，联通或电信的暂时不清楚需要问下客服。&lt;/h6&gt;

&lt;h6 id=&quot;6开通移动的电话验证业务需要拨号验证才能打通电话有效期3天可在移动app中开通也可以在机伶这个微信小程序中开通只有3天有效期我们可以设个闹钟每三天去打开一下就可以开启电话验证的服务功能后计算机自动拨打的电话是进不来了但人工手动骚扰还是可以通过的所以只是提高了骚扰的时间成本和精力成本提高了骚扰的时间成本和精力成本就足够了因为对方为了购买这个防骚扰服务还需要付出金钱成本所以三重成本会增加他撒气的困难&quot;&gt;6.开通移动的电话验证业务，需要拨号验证才能打通电话。(有效期3天)可在移动app中开通，也可以在‘机伶’这个微信小程序中开通，只有3天有效期，我们可以设个闹钟，每三天去打开一下就可以。开启电话验证的服务功能后，计算机自动拨打的电话是进不来了，但人工手动骚扰还是可以通过的，所以只是提高了骚扰的时间成本和精力成本。提高了骚扰的时间成本和精力成本就足够了，因为对方为了购买这个防骚扰服务还需要付出金钱成本，所以三重成本会增加他撒气的困难。&lt;/h6&gt;

&lt;h6 id=&quot;7最后还是要与对方拼意志力会使用这种下三烂手段的人意志力通常都不强所以即使没能完全能防住对方骚扰也可以耐心等待对方意志力耗尽不停的骚扰我们的同时他的意志力也同时以比较快的速度在消耗对他的日常生活会有很不利的影响设想谁会在负面情绪的生活中活得开心呢越不开心意志力越薄弱&quot;&gt;7.最后还是要与对方拼意志力，会使用这种下三烂手段的人意志力通常都不强，所以即使没能完全能防住对方骚扰，也可以耐心等待对方意志力耗尽，不停的骚扰我们的同时，他的意志力也同时以比较快的速度在消耗，对他的日常生活会有很不利的影响，设想谁会在负面情绪的生活中活得开心呢，越不开心意志力越薄弱。&lt;/h6&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;h6 id=&quot;最后总结下因为网络电话可以模拟任何电话号码所以要防住呼死你电话骚扰首先要开启陌生人电话屏蔽取消境外电话提醒业务然后开启移动服务器提供商的防骚扰服务让其需要手动拨号才能骚扰再下载一个防骚扰软件比如防骚扰大师或腾讯手机管家最后用类似机伶这样的服务屏蔽国外电话这些方式和手段的目标都是提高骚扰者的成本时间成本精力成本和金钱成本&quot;&gt;最后总结下，因为网络电话可以模拟任何电话号码，所以要防住‘呼死你’电话骚扰，首先要开启陌生人电话屏蔽，取消境外电话提醒业务，然后开启移动服务器提供商的防骚扰服务让其需要手动拨号才能骚扰，再下载一个防骚扰软件，比如防骚扰大师或腾讯手机管家，最后用类似‘机伶’这样的服务，屏蔽国外电话。这些方式和手段的目标都是提高骚扰者的成本，时间成本，精力成本和金钱成本。&lt;/h6&gt;

</content>
 </entry>
 
 <entry>
   <title>励志英文(四) Believe 相信</title>
   <link href="http://www.luzexi.com/2020/01/30/%E5%8A%B1%E5%BF%97%E8%8B%B1%E6%96%874"/>
   <updated>2020-01-30T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/01/30/励志英文4</id>
   <content type="html">&lt;iframe frameborder=&quot;0&quot; src=&quot;https://v.qq.com/txp/iframe/player.html?vid=s3056u8mp4w&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;我常常会自己写鸡汤来激励自己，如今想换个方式来做此事，用励志英文来替代。励志英文挺不错的，每次看后都热血澎湃，对生活和工作的理念都非常赞，时常引起我的共鸣，我对每篇文章的体会和重要内容的理解和翻译都会在文章中记录下来，供大家参考和鉴赏。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;第一段就引起了我的共鸣：在你成功的道路上最大的障碍不是缺少文凭，不是缺少背景，不是缺少金钱，而是缺少你对自己的信心。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	The biggest single obstacle standing in your way to success, it's not lack of education, it's not lack of background, it's not lack of money, it's lack of belief in yourself. 
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;第三段也能让我感同身受，乐观积极的人与悲观被动的人，在人生的道路上有着巨大差别：在通向成功的道路上最重要的是你的态度。态度决定你的人生。拥有乐观积极思想的人最终赢得了胜利，充满悲观思想的人最终收获了失败。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	The most important thing to winning is your attitude about you. It's your attitude about life. Positive thinkers win, and negative thinkers lose.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;第六段一击即中我心，我们首先应该从精神层面上来赢得世界，才能最终在现实中战胜困难：最重要的是你的激情。你获胜是因为你的心，而不是你的头脑。你获胜是因为你的情感，而不是逻辑。通向胜利的道路上最重要的你有多相信自己。你有多相信自己可以赢得胜利，有多相信许多好事发生在你身上，有多相信你是特别的人。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	The most important thing is your passion. You win with your heart, hot your head. You win on emotion, not on logic. The most important thing to winning is the power of believing in you. The power of believing that you can win. The power of believing that good things will happend to you. The power of believing that you're special.
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;原文如下&quot;&gt;原文如下：&lt;/h3&gt;

&lt;p&gt;The biggest single obstacle standing in your way to success, it’s not lack of education, it’s not lack of background, it’s not lack of money, it’s lack of belief in yourself. I believe the most important thing to winning is you.&lt;/p&gt;

&lt;p&gt;I believe the most important thing to winning is you. It’s what kind of person you are, what kind of husband you are, what kind of wife you are, what kind of dad you are, what kind of mom you are, what kind of example you are, see folks, you are it. You are everything.&lt;/p&gt;

&lt;p&gt;The most important thing to winning is your attitude about you. It’s your attitude about life. Positive thinkers win, and negative thinkers lose.&lt;/p&gt;

&lt;p&gt;Whatever you expect to happen with confidence happens. The fundamental key to success is what you believe is true for yourself. Not what you want, It’s what do you believe.&lt;/p&gt;

&lt;p&gt;The first thing you’ve gotta do to get what you want is believe that you can. Believe it’s possible for you to have it. And if there’s someone out there that has done it, why the fuck ain’t it possible for you? We’re all born with the same amount of time in a day, and anything that they can do, you can do too.&lt;/p&gt;

&lt;p&gt;The most important thing is your passion. You win with your heart, not your head. You win on emotion, not on logic. The most important thing to winning is the power of believing in you. The power of believing that you can win. The power of believing that good things will happend to you. The power of believing that you’re special.&lt;/p&gt;

&lt;p&gt;Believe that you can, and once you want something and believe that you can get it, you gotta block out all the noise around you that’s gonna tell you you can’t do it. Don’t listen to all that shit. Just believe that you can.&lt;/p&gt;

&lt;p&gt;The difference in being great and average and ordinary, the difference in winning and losing, is this little much. And this much is what I call a special kind of attitude. A special kind of mental toughness. A special kind of hunger to be somebody.&lt;/p&gt;

&lt;p&gt;The single most important thing you’ll develop in your entire lifetime is a positive attitude. Focus on how your life will be better, and what your life will look like when you get what you want. You do this, and your brain will think that by helping you get what you want, you’re moving away from pain and towards pleasure.&lt;/p&gt;

&lt;p&gt;Greatness isn’t achieved by that one massive thing you do, it’s those tiny little consistent baby steps we take every single day that add up to achieving that goal.&lt;/p&gt;

&lt;p&gt;Amazing things happen when you believe you have something. Your brain actively goes out to make it happen. Believe that you can do it, and work hard to get it, and it’s yours.&lt;/p&gt;

&lt;p&gt;You can do anything you want in this life, anything at all. But you gotta believe it’s possible and work your ass off to get it.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>励志英文(三) Destiny 命运</title>
   <link href="http://www.luzexi.com/2020/01/18/%E5%8A%B1%E5%BF%97%E8%8B%B1%E6%96%873"/>
   <updated>2020-01-18T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/01/18/励志英文3</id>
   <content type="html">&lt;iframe frameborder=&quot;0&quot; src=&quot;https://v.qq.com/txp/iframe/player.html?vid=a0689csu8is&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;我常常会自己写鸡汤来激励自己，如今想换个方式来做此事，用励志英文来替代。励志英文挺不错的，每次看后都热血澎湃，对生活和工作的理念都非常赞，时常引起我的共鸣，我对每篇文章的体会和重要内容的理解和翻译都会在文章中记录下来，供大家参考和鉴赏。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;h6 id=&quot;这是一篇鼓励我们朝着梦想前进的文章&quot;&gt;这是一篇鼓励我们朝着梦想前进的文章。&lt;/h6&gt;

&lt;p&gt;开头中，鼓励我们向更高的成就前进，“If you feel you have something to give, if you feel that your particular talent is worth developing, is worth caring for, then there’s nothing you can’t achieve.”，如果你感到有什么需要去奉献，或者如果你感觉你的聪明才智值得去开发值得去关注，那么世界上就没有你达不到的成就。&lt;/p&gt;

&lt;p&gt;在中间里，鼓励我们超越自己，“When you grow up, you tend to get told that the world is that the way it is. Your life is just to live your life inside the world, trying not to bash into the walls too much. That’s a very limited life. Life can be much broader, once you discover one simple fact: Everything around you that you can life, was make up by people that were no smarter than you. Once you learn that, you’ll never be the same again. ”，当你长大后，往往会被告知世界就是这样的。你的人生就在这样的世界里，不要试图超越这个范围太厉害。其实这是个非常受束缚的人生。人生可以更加广阔些，一旦你发现一个简单的事实：你周围所有你能生活的，是被那些不比你聪明的人创造出来的。一旦你领悟到这点，你将不再是以前的你了。&lt;/p&gt;

&lt;p&gt;最后，鼓励我们创造历史。“Let me share something with you: History is being read, But it is also being written by people with imagination. Dont stop, Dont stop, Dont stop running toward your dream! ”，让我与你分享些事：历史一直在被阅读，但它也一直在被充满想象的人书写。不要停，不要停，不要停止奔向你的梦想。&lt;/p&gt;

&lt;h3 id=&quot;原文如下&quot;&gt;原文如下：&lt;/h3&gt;

&lt;p&gt;The two most important days in your life are the day you are born and the day you find out why.&lt;/p&gt;

&lt;p&gt;I do not belive, that any of us have dreams,
that were not given to us, for the purpose of accomplishing those particular dreams.&lt;/p&gt;

&lt;p&gt;If you feel you have something to give, if you feel that your particular talent is worth developing, is worth caring for, then there’s nothing you can’t achieve.&lt;/p&gt;

&lt;p&gt;So applaud you for your dreaming, for your running toward your dream. I applaud you for believing in yourself, because that’s what life is about: stretching and challenging, looking for ways that you can begin to improve yourself.&lt;/p&gt;

&lt;p&gt;Not only it is possible for you to have your dream, but it’s necessary. It’s necessary that you go for what is yours in the universe.&lt;/p&gt;

&lt;p&gt;Logical, practical thinking says ‘you can’t do it today’&lt;/p&gt;

&lt;p&gt;But if you want to produce unreasonable results in your life, like living your dream and taking charge of your destiny, you’ve got to be an unreasonable person.&lt;/p&gt;

&lt;p&gt;When you grow up, you tend to get told that the world is that the way it is. Your life is just to live your life inside the world, trying not to bash into the walls too much. That’s a very limited life. Life can be much broader, once you discover one simple fact: Everything around you that you can life, was make up by people that were no smarter than you. Once you learn that, you’ll never be the same again.&lt;/p&gt;

&lt;p&gt;If it’s hard, why do people do it? Why did they go? People who climb mountains. Why would Nelson Mandela give up 26 years of his life? Why do people do that? Even though it’s hard, it’s worth it. It’s worth it!&lt;/p&gt;

&lt;p&gt;The people who go after their stuff, what makes it worth it? It’s gotta be your passion!&lt;/p&gt;

&lt;p&gt;You gotta love it, and it’s gotta be what you are supposed to do! You do what it is you supposed to do. You supposed to build something, you supposed to create something. I dont know how to do it. LEARN!&lt;/p&gt;

&lt;p&gt;Let me share something with you: History is being read, But it is also being written by people with imagination. Dont stop, Dont stop, Dont stop running toward your dream!&lt;/p&gt;

&lt;p&gt;Our deepest fear is not that we are inadequate. Our deepest fear is that we are powerful beyond measure. It is our light, not our darkness, that most frightens us.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>励志英文(二) Today is yours</title>
   <link href="http://www.luzexi.com/2020/01/12/%E5%8A%B1%E5%BF%97%E8%8B%B1%E6%96%872"/>
   <updated>2020-01-12T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/01/12/励志英文2</id>
   <content type="html">&lt;iframe frameborder=&quot;0&quot; src=&quot;https://v.qq.com/txp/iframe/player.html?vid=t0503w4dby9&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;我常常会自己写鸡汤来激励自己，如今想换个方式来做此事，用励志英文来替代。励志英文挺不错的，每次看后都热血澎湃，对生活和工作的理念都非常赞，时常引起我的共鸣，我对每篇文章的体会和重要内容的理解和翻译都会在文章中记录下来，供大家参考和鉴赏。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;h6 id=&quot;这是一篇激励自己的文章情景是当前运动比赛考试测试考验前的心理准备长久以来的辛苦要在今天得到结果了赛前的心理暗示对自我充满信心的鼓舞&quot;&gt;这是一篇激励自己的文章，情景是当前运动比赛、考试、测试、考验前的心理准备，长久以来的辛苦要在今天得到结果了，赛前的心理暗示对自我充满信心的鼓舞。&lt;/h6&gt;

&lt;p&gt;文中说，我们已经做好了准备迎接这一刻 “All the training, the hours of preparation, the pain you felt, they have brought you to this moment, they are the armor that you wear.” 所有的训练，所有的准备，所有的忍耐过来的痛苦把我们带到了现在这个时刻，我们已经穿上装甲做好最后的准备。&lt;/p&gt;

&lt;p&gt;然后说，质变即将到来 “It’s about taking all those hours and turning them into the best version of yourself. Today is about being better than you ever dreamed you could be.” 所有训练和花费的精力都将我们变为更好的自己，我们梦想中能达到的更好的自己将要到来。&lt;/p&gt;

&lt;p&gt;接着说，抛开怀疑的态度坚定自己， “Let people doubt you they’ll see with time. Let the obstacle stand, you will run right through them. Let everything in this universe tell you no, you have already told yourself yes.” 让怀疑我们的人们看看我们的实力，让障碍停止在那里，我们会垮过去。当整个世界都对你说不行的时候，我们已经知道自己是可以的。&lt;/p&gt;

&lt;p&gt;然后说，是时候抬升自己了，”you were the god of your world, And in your world you write the script. There is no tired, you left that in the wait room years ago. There is no one sure, push yourself to exhaustion every day took care of that, There is only when, when do you rise up.” 我们是自己世界里的神，可以编写自己的剧本。前几年我们在等候室里不知疲倦。没人会知道我们每天精疲力尽会如何，只有当我们抬升自己时才会感觉到。&lt;/p&gt;

&lt;p&gt;最后说，我们总有一天会向世界证明自己，“It will not disappear with time, it will live on, you have earned today. Go make sure the world knows.” 所有的努力都不会消失，它会存留在那里，我们已经赢得了今天，让世界知道我的努力。&lt;/p&gt;

&lt;h3 id=&quot;以下就是全文&quot;&gt;以下就是全文：&lt;/h3&gt;

&lt;p&gt;The thing about time is that comes and goes. Tomorrow will make today a distant memory And as quick as you were here, you’ll be gone. What’s in front of you is an opportunity to make right now something you’ll never forget. You have the ability to write history to create a story that will live with you forever.&lt;/p&gt;

&lt;p&gt;Think back to all the days leading up to today. All the training, the hours of preparation, the pain you felt, they have brought you to this moment, they are the armor that you wear.&lt;/p&gt;

&lt;p&gt;Right now is about execution. It’s about taking all those hours and turning them into the best version of yourself. Today is about being better than you ever dreamed you could be. These are the moments you live for.&lt;/p&gt;

&lt;p&gt;Gone are the insecurities the doubt the disbelief, this day is yours. Look your opponents in the eye, they cannot give what you can, you have built this opportunity piece by piece, you have created an unstoppable force and it’s finally time to reveal that to the world.&lt;/p&gt;

&lt;p&gt;Let people doubt you they’ll see with time. Let the obstacle stand, you will run right through them. Let everything in this universe tell you no, you have already told yourself yes.&lt;/p&gt;

&lt;p&gt;The hardest thing in this world to stop is someone who knows they’ve won before the game is started. This is your everything, embracing. Make it, so that you never forget today.&lt;/p&gt;

&lt;p&gt;you were the god of your world, And in your world you write the script. There is no tired, you left that in the wait room years ago. There is no one sure, push yourself to exhaustion every day took care of that, There is only when, when do you rise up.&lt;/p&gt;

&lt;p&gt;There some a point when you just given too much, you sacrifice is too great, to accept anything but the very best, everything else is noise. This is your day.&lt;/p&gt;

&lt;p&gt;It will not disappear with time, it will live on, you have earned today. Go make sure the world knows.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(三十二) 2019年我做了哪些金融投资与经济系统的学习和思考</title>
   <link href="http://www.luzexi.com/2020/01/10/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A832"/>
   <updated>2020-01-10T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/01/10/思路探讨32</id>
   <content type="html">&lt;p&gt;2019年我做了哪些金融投资与经济系统的学习和思考。&lt;/p&gt;

&lt;p&gt;2019年是继2018后经济回暖的一年，2018年的萧条景象还记忆忧心，在2020年1月这个点我们来回顾和总结下2019年我对金融体系的思考。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/life/idea-talking32-1.png&quot; alt=&quot;且慢投资组合&quot; /&gt;&lt;/p&gt;

&lt;p&gt;(图为我在且慢上的投资方案)&lt;/p&gt;

&lt;p&gt;2019年我在金融投资上只有买股票这个操作，从股债房，三个投资方向来看，房价确确实实已经到了一个瓶颈，房价的上升本来就需要国民钱包的支撑，如果国民钱包没有鼓起来，再上升就是不理性的，也更容易出现崩盘的机会，值得庆幸的是国家控制住了房价，以‘房住不炒’的理念来抑制房价过大的起与伏，为中国未来的发展又一次扫平了障碍。&lt;/p&gt;

&lt;p&gt;为什么说扫平障碍呢？基建是国家的命脉，是国家兴旺发展的基础，但房市太活跃升值太快，导致大量资金流入房地产而不是基建，就会伤害实体经济，因为钱是有限的，往房地产市场流入的多，那么在实体经济里的钱就会变少(如果股市太旺也是同样的道理，钱都用来投机倒把)，渐渐的房地产就会成为国家经济支柱，炒房成了国民赚钱手段，这不是道德问题，而是未来发展的问题，钱都去搞房地产的话实体经济就没钱创新和发展，因为没钱在实体经济导致实体经济的发展和创新受到了阻碍。&lt;/p&gt;

&lt;h6 id=&quot;房地产毕竟是靠涨价来吸引投资市场的而且涨价是有限的价格不符合国民钱包就会很危险价格除了涨还有跌跌下来时伤害的不只是老百姓的钱包还有国家的经济和对国家经济的信心所以国家对此处理的很谨慎也处理的很对智囊团很厉害所以总的来说现在房价已经涨到了国民的钱包无法支撑的阶段了价格的瓶颈也在此停住了但国家经济仍然支撑的房地产因此没有恐慌也没有必要恐慌只是恢复了平静而已我们继续各自工作和等待暂时来说房地产的机会还需要等待&quot;&gt;房地产毕竟是靠涨价来吸引投资市场的，而且涨价是有限的，价格不符合国民钱包就会很危险，价格除了涨还有跌，跌下来时伤害的不只是老百姓的钱包，还有国家的经济和对国家经济的信心，所以国家对此处理的很谨慎，也处理的很对，智囊团很厉害。所以总的来说现在房价已经涨到了国民的钱包无法支撑的阶段了，价格的瓶颈也在此停住了，但国家经济仍然支撑的房地产，因此没有恐慌，也没有必要恐慌，只是恢复了平静而已，我们继续各自工作和等待，暂时来说房地产的机会还需要等待。&lt;/h6&gt;

&lt;p&gt;对股市来说2019年是值得欢喜的一年，很多人在2018年所遭受的损失在2019年这一年里弥补了回来，甚至有人还有盈余。股市是国家经济的晴雨表，总体上反应了国家的情况，所以其实可以根据今年的股市来判断我国经济回暖的情况是比较好的。&lt;/p&gt;

&lt;p&gt;2019年里我买了些股票也卖了些股票，对于平民百姓来说想靠股市赚钱就得学习很多知识，分析很多问题，亲身去接触企业，感受企业经营状况。&lt;/p&gt;

&lt;h6 id=&quot;今年我终于明白了为什么要亲自去感受企业的产品最好能亲自去他们厂里店里去感受他们的经营状况我也开始抛售了一些我不了解的企业的股票和一些我自己亲自使用过并且体验很差的产品的企业买入了更多我使用过他们家产品并且有很好体验的企业&quot;&gt;今年我终于明白了，为什么要亲自去感受企业的产品，最好能亲自去他们厂里、店里去感受他们的经营状况。我也开始抛售了一些我不了解的企业的股票，和一些我自己亲自使用过并且体验很差的产品的企业，买入了更多我使用过他们家产品，并且有很好体验的企业。&lt;/h6&gt;

&lt;p&gt;今年我更多的感受到了股票市场的情绪化，更深刻的记住了短期的情绪是导致的价格波动主要原因，于是我开始去情绪化，更加冷静和沉着的看待股票投资。比如，我不再被涨价所心动，也不再被下跌所恐慌，而更多的关注横盘平稳阶段的投资点。&lt;/p&gt;

&lt;h6 id=&quot;我也开始明白对一个企业来说财报的局限性财报是给老板看的是给大众看的是给懂事和股东看的是给领导看的要好看否则要你这个财务总监干什么做账当然是要漂亮一点所以财报是人为的漂亮报告但也有实在兜不住的时候那就破罐子破摔把前两年的问题都暴露出来一次性把最坏的情况兜底也是常有的事所以财报不可全信只能看个大概30的内容还是有用的比如固定负债流动负债以及负债率还有现金流和固定资产其余的只能相信30-50&quot;&gt;我也开始明白，对一个企业来说，财报的局限性，财报是给老板看的，是给大众看的，是给懂事和股东看的，是给领导看的，要好看，否则要你这个财务总监干什么，做账当然是要漂亮一点，所以财报是人为的’漂亮报告‘。但也有实在兜不住的时候，那就破罐子破摔把前两年的问题都暴露出来，一次性把最坏的情况兜底也是常有的事。所以’财报‘不可全信，只能看个大概，30%的内容还是有用的，比如固定负债，流动负债，以及负债率，还有现金流和固定资产，其余的只能相信30-50%。&lt;/h6&gt;

&lt;p&gt;我也开始意识到股东结构的重要性，以及股东作用的局限性。这个机构，这个自然人，为什么要投这个公司，而且还投的这么多，进了前10的股东名单，他们肯定分析过，慎重考虑过，否则不会花这么多钱去玩这场没有把握的游戏，他们是否考虑了5-10甚至20年的投资回报，因此在这个点买入。但即使买入，股东的局限性也很多，因为在市场面前他们无法力挽狂澜，即使红杉集团或腾讯或阿里进入了10大股东列表那又怎么样，只能说他们分析后有几成的把握5年左右有大回报的可能，但毕竟他们的投资有撒网式的嫌疑，把投资额度分成了20-100份，我们看到的只是其中一份而已，对他们而言这一份没有起色还有其他的，因此他们并不是孤注一掷。另外他们看准的是投资回报，还是股票长期分红，或者领域上的战略投资，也是与我们的投资目标有相当大的不一样的。我们老百姓更多期望的是当下的收益，而他们则有多种情况的考虑，并不一定全为了收益，还有战略。&lt;/p&gt;

&lt;h6 id=&quot;我也开始意识到对股票的理解应该换个角度看问题比如从宏观角度看股市股市毕竟是跟随着国家的经济发展而不断前进的如果国家经济衰落或不稳定那么股市就会下行或动荡因此我们都在赌国家兴旺发达如果国家遭遇灾难或困难我们就大概率是赌输的&quot;&gt;我也开始意识到，对股票的理解应该换个角度看问题，比如从宏观角度看股市，股市毕竟是跟随着国家的经济发展而不断前进的，如果国家经济衰落或不稳定，那么股市就会下行或动荡，因此我们都在赌国家兴旺发达，如果国家遭遇灾难或困难，我们就大概率是赌输的。&lt;/h6&gt;

&lt;p&gt;这些都是我们只投资自己国家的视角下的，因为身在祖国，投资祖国是必然的。我们无法决定国家的命运，那么有没可能去别的国家投资股票，现在有老虎证券可以投资美股、港股，不过外汇有额度每年35w左右额度，对于平民百姓也够用了。如果在国内投资国外可以通过基金，或使用ETF来投资纳斯达克指数，标普指数，港股指数，以及德国30的指数。说实话国家并没有封死平民百姓日常的对外投资，只是封锁了富豪的资产外流和转移。&lt;/p&gt;

&lt;h6 id=&quot;从国际经济条件来讲第一霸主的美国和我国确实有很大的差距从当下看投资美股确实是稳当的但这个差距也一直在缩小但天下哪有稳当的生意稳当安逸永远都是最危险的处境难道美国就一直会霸主下去吗中国就不可能取代它或与它一起成为霸主吗谁都不知道所以假如我们投资了美股则要担心美国是否会犯错也要担心中国的崛起是否会影响到美国的发展从这个角度看我们去投资美国会有诸多问题&quot;&gt;从国际经济条件来讲，第一霸主的美国和我国确实有很大的差距，从当下看投资美股确实是稳当的，但这个差距也一直在缩小。但天下哪有稳当的生意，稳当安逸永远都是最危险的处境。难道美国就一直会霸主下去吗，中国就不可能取代它或与它一起成为霸主吗？谁都不知道，所以假如我们投资了美股，则要担心美国是否会犯错，也要担心中国的崛起是否会影响到美国的发展。从这个角度看我们去投资美国，会有诸多问题。&lt;/h6&gt;

&lt;p&gt;况且我们不在美国，不是生活在美国，并不了解美国，就像我前面说的，我们不了解一个企业，却买了它的股票一样，心中的疑惑和担心会与日俱增。我觉得是得不偿失的，我们毕竟是人，首先是要寻求自己内心的平静，如果心是平静的，做事就会更加坚定，投资也是这样，越是了解的越能平静对待，越是陌生的越是猜疑担心。所以我决定主战场还是国内，除非中国腐败到有一天我对国内彻底绝望。&lt;/p&gt;

&lt;h6 id=&quot;我们再换个角度看股市从涨跌的方向看股市我们说过股票的涨跌短期看情绪中期看业绩长期看国家经济那么我们能不能用情绪的涨跌来赚钱呢答案是能却非常很难首先因为机会很少一年可能就出现1次甚至可能一次都没有其次很难感受到情绪的反转点需要老练的经验和冷静的头脑分析最后选股还要谨慎对于想躺着赚钱的人来说根本是不可能的这种操作需要大量的时间学习和经验积累专业程度比较高我的体会是两个点的位置可以尝试一个是恐慌情绪结束时一个是回暖情绪起来时其他时候都不合适涨跌互现会让我们心力交瘁得不偿失&quot;&gt;我们再换个角度看股市，从涨跌的方向看股市。我们说过股票的涨跌，短期看情绪，中期看业绩，长期看国家经济。那么我们能不能用情绪的涨跌来赚钱呢？答案是能却非常很难，首先因为机会很少，一年可能就出现1次，甚至可能一次都没有，其次很难感受到情绪的反转点，需要老练的经验和冷静的头脑分析，最后选股还要谨慎。对于想躺着赚钱的人来说，根本是不可能的，这种操作需要大量的时间学习和经验积累，专业程度比较高。我的体会是两个点的位置可以尝试，一个是恐慌情绪结束时，一个是回暖情绪起来时，其他时候都不合适，涨跌互现会让我们心力交瘁，得不偿失。&lt;/h6&gt;

&lt;p&gt;再换个角度看问题，其实投资过程中，不只是标的的问题，我们自身也存在诸多的变化，例如，工作上的，生活上的开支使得现金流紧张时，我们自然需要从稳定的角度看现有的整体资金投资比例，可能需要缩小一部分投资金额用于生活。生活中各种层出不穷的情况千变万化，并不是我们想象的有余钱就投这么简单，我们需要平衡生活与投资，需要预留备用金以备正常生活和以便不时之需，因此投资并不是简简单单的买入卖出，它需要根据我们当前生活的状态动态平衡，只有找到平衡，生活才能如意的走下去，如果我们连生活都无法顺畅的走下去，投资带来的情绪会让我们走入地狱。不过，没有什么困难是过不去的，只是大部分人都难过情绪这关，柳暗花明又一村的事常有，我们量力而为。&lt;/p&gt;

&lt;h6 id=&quot;最后我们再来换个角度看问题分析和执行保持一致的难度假设说现在我们确认并且肯定某个买入的机会无论是股票还是房产我们是否能做到坚定的去执行是否应该全力去执行我想这个问题肯定每个人都不一样执行前首先我们要清楚没有什么是机会百分百肯定的如果你想清楚了后果那么无论是否执行都应该坚定得去做&quot;&gt;最后我们再来换个角度看问题，分析和执行保持一致的难度。假设说，现在我们确认并且肯定某个买入的机会，无论是股票还是房产，我们是否能做到坚定的去执行，是否应该全力去执行。我想这个问题肯定每个人都不一样，执行前首先我们要清楚没有什么是机会百分百肯定的，如果你想清楚了后果那么无论是否执行都应该坚定得去做。&lt;/h6&gt;

&lt;h6 id=&quot;每个人的理念都不同我的理念只是其中一个在我的理念里我们应该首先保证当前生活能够继续下去的前提下来投资确保即使彻底失败也不至于无法生活投资和生活和工作一样是个循序渐进的过程长期的痛苦才是真痛苦我们需要用毅力慢慢磨平长时间的痛苦让快乐回归正途就像我提倡的在工作上的积累那样不要幻想短期的就能收到巨大的回报有很多人就不这么认为没有对错有人就会不惜一切代价去赌一把我们称他们为亡命之徒就像梭哈那样all-in一把结束一切痛苦他们这所谓的痛苦都太短暂根本无法磨练人的意志只是一味的图个简单方便痛苦是短了结局不怎么样当然也有成功的时候就会一夜暴富传为佳话但这种毕竟是少数大部分都是要么抗不过去还没看到黎明就败下阵来要么就是彻底失败走上邪路从此坑蒙拐骗要么自毁前程断送性命&quot;&gt;每个人的理念都不同，我的理念只是其中一个，在我的理念里，我们应该首先保证当前生活能够继续下去的前提下来投资，确保即使彻底失败也不至于无法生活，投资和生活和工作一样是个循序渐进的过程，长期的痛苦才是真痛苦，我们需要用毅力慢慢磨平长时间的痛苦，让快乐回归正途，就像我提倡的在工作上的积累那样，不要幻想短期的就能收到巨大的回报。有很多人就不这么认为，没有对错，有人就会不惜一切代价去赌一把，我们称他们为‘亡命之徒’，就像梭哈那样ALL In一把结束一切痛苦，他们这所谓的痛苦都太短暂根本无法磨练人的意志，只是一味的图个简单方便，痛苦是短了结局不怎么样，当然也有成功的时候，就会一夜暴富传为佳话，但这种毕竟是少数，大部分都是要么抗不过去还没看到黎明就败下阵来，要么就是彻底失败走上邪路从此坑蒙拐骗，要么自毁前程断送性命。&lt;/h6&gt;

&lt;p&gt;我们来看债券市场中2019年并不是很好，继2018后2019的雷暴虽然相对减少了但依然有不少。我们现在应该只关注国债和国家的逆回购比较安全，可以存余额宝等货币基金以度过冬天等待春天。&lt;/p&gt;

&lt;p&gt;投资之道当然没有这么简单，如果能简单到只要随便学习一下就能掌控，那就不是人类社会的。他需要深度学习，不仅如此还需要耐心等待机会，并且在机会到来时小心谨慎的执行。其实没有那么精彩、风光，只是回头看再编一编夸大一下就成了神话。&lt;/p&gt;

&lt;h6 id=&quot;今年我也发现了一个我一直忽视的事其实我们所有人无论哪个阶层的人都一直在找能永远躺着赚钱的投资标的即使只是看起来是永远但能永远躺着赚钱的投资标的大部分要么很贵性价比不高要么是个坑其实没有完美的投资标的都是有这样那样的缺陷我们还是不要盯着缺陷而是要看是否合适我们自己我们关注的是什么是长期分红是租金还是当下的涨幅是短期还是长期是保值还是想升值每个人的心态状态和理念都不一样找到合适我们的并且找对标的的长处是我们的重中之重&quot;&gt;今年我也发现了一个我一直忽视的事，其实我们所有人无论哪个阶层的人，都一直在找能’永远‘躺着赚钱的投资标的(即使只是看起来是‘永远‘)，但能永远躺着赚钱的投资标的，大部分要么很贵、性价比不高，要么是个坑。其实没有完美的投资标的，都是有这样那样的缺陷，我们还是不要盯着缺陷，而是要看是否合适我们自己，我们关注的是什么，是长期分红，是租金，还是当下的涨幅，是短期还是长期，是保值还是想升值，每个人的心态、状态和理念都不一样，找到合适我们的，并且找对标的的长处，是我们的重中之重。&lt;/h6&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(三十六) 英语励志文章1</title>
   <link href="http://www.luzexi.com/2020/01/08/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A836"/>
   <updated>2020-01-08T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/01/08/思路探讨36</id>
   <content type="html">&lt;p&gt;半年前我把这篇文章打印了下来，每天读一遍读了很多遍，每次读都有不同的体会，文字是视频中的对话。很多时候当我们还没到那个高度的时候，催眠自己也不妨是一种途径。&lt;/p&gt;

&lt;iframe frameborder=&quot;0&quot; src=&quot;https://v.qq.com/txp/iframe/player.html?vid=n0147njlz3k&quot; allowfullscreen=&quot;true&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;I dont know what that dream is that you have, I dont care how disappointing it might have been, as you’ve been working towards that dream. But that dream that you’re holding in your mind, that it’s possible.&lt;/p&gt;

&lt;p&gt;That some of you already know that it’s hard. It’s not easy. It’s hard changing your life.&lt;/p&gt;

&lt;p&gt;That in the process of working on your dreams, you are going to incur, incur a lot of disappointment, a lot of failure, a lot of pain , there are moments when you’re going to doubt yourself, you will say “ God why? why is this happening to me? I’m just trying to take care of my children and my mother I’m not trying to steal or rob from anybody. How did this have to happen to me?”&lt;/p&gt;

&lt;p&gt;For those of you who have experienced some hardships, don’t give up on your dream.&lt;/p&gt;

&lt;p&gt;There are rough times that gonna come but they have not come to stay, they have come to pass.&lt;/p&gt;

&lt;p&gt;Greatness is not this wonderful esoteric, elusive, god-like feature that, only the special among us will ever taste, you know, it’s something that truly exists in all of us.&lt;/p&gt;

&lt;p&gt;It’s very important for you to believe, that you are the one!&lt;/p&gt;

&lt;p&gt;Most people, they raise a family, they earn a living and then they die.&lt;/p&gt;

&lt;p&gt;They stop growing, they stop working on themselves. They stop stretching. They stop pushing themselves.&lt;/p&gt;

&lt;p&gt;Then a lot of people like to complain, but they don’t wanna do anything about their situation, and most people don’t work on their dreams. why?&lt;/p&gt;

&lt;p&gt;One is because of fear, the fear of failure, “what if things don’t work out?” And the fear of success, “what if they do and I can’t handle it?”. These are not risk-takers.&lt;/p&gt;

&lt;p&gt;You spend so much time with other people, you spend so much time trying to get people to like you, you know other people more than you know yourself, you’ve studied them, you know about them, you wanna hang out like them, you wanna be JUST LIKE THEM!&lt;/p&gt;

&lt;p&gt;And you know what? You’ve invested so much time in them, you don’t even know who you are, I’m telling you to spend time by yourself.&lt;/p&gt;

&lt;p&gt;It’s necessary that you get the losers out of your life, if you want to live your dream.&lt;/p&gt;

&lt;p&gt;But for people who are running towards their dreams, life has a special kind of meaning.&lt;/p&gt;

&lt;p&gt;When you become the “right person”, what you do is you start separating yourself from other people, you begin to have a certain uniqueness.&lt;/p&gt;

&lt;p&gt;As long as you follow other people, as long as you’re being a copy-cat, you will never ever be the best copy-cat in the world. But you will be the best that you could be. I’m telling you to find your value.&lt;/p&gt;

&lt;p&gt;That everybody won’t see it, that everybody won’t join you, that everybody won’t have the vision, it’s necessary to know that. That you are an uncommon breed!&lt;/p&gt;

&lt;p&gt;It’s necessary that you align yourself with people and attract people into your business, people who are hungry.&lt;/p&gt;

&lt;p&gt;People who are unstoppable and unreasonable, people who are refusing to live life just as it is and who want more.&lt;/p&gt;

&lt;p&gt;The people that are living their dream are fighting winners. They attach themselves to. The people who are living their dreams are the people that know that if it’s gonna happen, it’s up to them.&lt;/p&gt;

&lt;p&gt;If you wanna be more successful, if you wanna do and have stuff you’ve never done before, No.2, I’m asking you to invest in you.&lt;/p&gt;

&lt;p&gt;Someone’s opinion of you does not have to become your reality, that you don’t have to go through life being a victim. And even though you face disappointments, you have to know within yourself that I can do this, even if no one else sees it in me, I must see it for myself.&lt;/p&gt;

&lt;p&gt;This is what I believe and I’m willing to die for it, period.&lt;/p&gt;

&lt;p&gt;No matter how bad it is or how bad it gets, I’m going to make it!&lt;/p&gt;

&lt;p&gt;I wanna represent an idea. I wanna represent possibilities.&lt;/p&gt;

&lt;p&gt;Some of you right now, you wanna be, you wanna say, you wanna go to next level, “I want a council, you know, I wanna be an engineer, I wanna be a doctor.” Listen to me: You can’t get to that level, you can’t get to the level economically where you wanna be, until you start investing in your mind. You are not reading books, I challenge you all to go to the conference. I dare you to invest time, I dare you to be alone, I dare you to spent an hour to get to know yourself. When you become who you are, when you become the person that you are created to be, designed to be, who you were designed to be. When you become an individual, what you do is you take yourself and you start separating yourself from other people. I’m telling you, to get to a place where people do not like you or do not even bother you any more. Why? Cause you are not concerned with trying to make them happy, because you are trying to blow, you are trying to get to the next level. I need you to invest in your mind.&lt;/p&gt;

&lt;p&gt;If you’re still talking about your dream, if you’re still talking about your goal, but you have not done anything, just take the first step. That you can make your parents proud, you can make your school proud, you can touch millions of people‘s lives, and the world will never be the same again, because you came this way.&lt;/p&gt;

&lt;p&gt;Don’t let nobody steal your dreams! After we face a rejection or a ‘NO’ or we have a meeting and no one shows up. or somebody said ” you can count on me ” and they don’t come through.&lt;/p&gt;

&lt;p&gt;What if we have that kind of attitude, that cause reposes, nobody believes in you. You’ve lost again, and again, and again! The lights are cut off, but you’re still looking at your dream, renewing it every day and saying to yourself: IT’S NOT OVER, UNTIL I WIN!&lt;/p&gt;

&lt;p&gt;YOU CAN LIVE YOUR DREAM!&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(三十五) 合作带来自信</title>
   <link href="http://www.luzexi.com/2020/01/05/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A835"/>
   <updated>2020-01-05T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/01/05/思路探讨35</id>
   <content type="html">&lt;p&gt;我们常常会做出强迫别人执行自己的指令，古时有指腹为婚的习惯，今有强制学习、强制加班等。我们的控制欲太强，常常会导致别人有被捆绑被束缚的感觉，这种感觉导致降低了合作的发生几率。我们需要寻求合作，不管是工作上的合作，还是男女朋友或夫妻家庭生活上的合作，亦或我们与孩子之间的合作上，都增进了效率，增加了感情，最终间接提高了生活和工作的效率。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;《自卑与超越》一直围绕着讲的，也是“合作”这个关键词。合作对我们非常重要，但由于我们不好的习惯和思维方式的沉淀，常常不由自主的去破坏合作。&lt;/p&gt;

&lt;h6 id=&quot;婚姻也本该是合作为基础的合作是婚姻的首要条件夫妻本身彼此应该像是很要好的朋友和外界也应该保持良好的亲密的友谊关系只有这样他们的孩子才会觉得在他们的家庭之外也有值得信赖的人&quot;&gt;婚姻也本该是合作为基础的，合作是婚姻的首要条件。夫妻本身彼此应该像是很要好的朋友，和外界也应该保持良好的亲密的友谊关系。只有这样，他们的孩子才会觉得在他们的家庭之外也有值得信赖的人。&lt;/h6&gt;

&lt;h6 id=&quot;如果每对夫妻对于其伴侣的兴趣都高于对自己的兴趣那么他们之间便会有真正的平等如果他们都很诚心地奉献出自己他们便不会觉得自己低声下气或者受家庭拖累只有男女双方都有这种态度平等才有出现的可能他们两人都应该努力使得对方的生活平稳和富裕这样他们才会有安全感他们会觉得自己是有价值的并且觉得自己是被需要的&quot;&gt;如果每对夫妻对于其伴侣的兴趣都高于对自己的兴趣，那么他们之间便会有真正的平等。如果他们都很诚心地奉献出自己，他们便不会觉得自己低声下气或者受家庭拖累。只有男女双方都有这种态度，平等才有出现的可能。他们两人都应该努力使得对方的生活平稳和富裕，这样他们才会有安全感。他们会觉得自己是有价值的，并且觉得自己是被需要的。&lt;/h6&gt;

&lt;h6 id=&quot;没有人能够心平气和地忍受卑下的地位伴侣们或者共事的人们必须是平等的人们只有在平等的时候才能找出克服共同困难的方法也只有人与人之间感到平等时合作才能激发出最惊人的力量&quot;&gt;没有人能够心平气和地忍受卑下的地位。伴侣们或者共事的人们必须是平等的，人们只有在平等的时候才能找出克服共同困难的方法，也只有人与人之间感到平等时合作才能激发出最惊人的力量。&lt;/h6&gt;

&lt;h6 id=&quot;如果我们能成为身边的人的好朋友并有美满的婚姻和有价值的工作我们就不会觉得自己不如别人或者被人别所击败我们会觉得这个世界是个友善的世界在哪里我们都能坦然处之我们会遇到很多喜欢我们的人也会遇到很多我们喜欢的人应付困难时也能得心应手因为合作与人合作带给你愉快与所有人合作愉快带给你实实在在的自信&quot;&gt;如果我们能成为身边的人的好朋友，并有美满的婚姻和有价值的工作，我们就不会觉得自己不如别人，或者被人别所击败。我们会觉得这个世界是个友善的世界，在哪里我们都能坦然处之，我们会遇到很多喜欢我们的人也会遇到很多我们喜欢的人，应付困难时也能得心应手。因为合作，与人合作带给你愉快，与所有人合作愉快带给你实实在在的自信。&lt;/h6&gt;

&lt;p&gt;在我们现代的文化里，人们经常都没有做好合作的准备。我们的教育都太注重个人的成功，都太强调要考虑我们能够从生活中获得什么，而不是我们能付出什么。我们一直都是遵照着我们生活样式做出种种应急的反应，这些反应大都是只考虑自身的利益，因此我们非常不习惯于考虑另一个人的利益、他的目标、他的欲望、他的野心和他的希望。这也是我们因为缺少合作能力而造成的，无法换位思考，导致无法体会到对方的处境和情绪。&lt;/p&gt;

&lt;h6 id=&quot;如果一个人真正对另一个人感兴趣他会成为对方真诚的友伴他会勇于负责他还会使得自己忠实可靠最坏的情况是一个人只顾及自己的利益他终日都在盘算着我能从中得到什么他会一直要求自由和解脱从不考虑要怎样才能使对方生活的更轻松或更富裕虽然如此但这种情况也不是种罪恶而只是一种错误的方法就像所有老谋深算千方百计想从婚姻中得到什么或者逃脱什么的人一样最终都走上了错误的道路&quot;&gt;如果一个人真正对另一个人感兴趣，他会成为对方真诚的友伴，他会勇于负责，他还会使得自己忠实可靠。最坏的情况是一个人只顾及自己的利益，他终日都在盘算着，“我能从中得到什么？”，他会一直要求自由和解脱，从不考虑要怎样才能使对方生活的更轻松或更富裕。虽然如此，但这种情况也不是种罪恶，而只是一种错误的方法。就像所有老谋深算、千方百计想从婚姻中得到什么或者逃脱什么的人一样，最终都走上了错误的道路。&lt;/h6&gt;

&lt;p&gt;这个世界确实并非能够这么理想，也真的有很多邪恶、困难、偏见和悲哀，但这就是我们的世界，它的优点和缺点也是我们自身的优点和缺点。对于我们个人生活而言，在面对周围人和事环境时，无论是顺境还是困境，都必须要以合作的方式来加以改进，这是最有效的方法和途径。&lt;/p&gt;

&lt;p&gt;最后引用一段阿尔佛雷德.阿德勒的话来结束最近几篇对《自卑与超越》的探讨：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	每个人都有不同程度的自卑感，因为没有一个人对其当前的地位感到满意，对优越感的追求是所有人的通性。然而，并不是人人都能超越自卑，关键在于正确对待职业、社会和性，在于正确理解生活。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;参考文献:&lt;/p&gt;

&lt;p&gt;《自卑与超越》 阿尔佛雷德.阿德勒&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(三十四) 纠正错误观念</title>
   <link href="http://www.luzexi.com/2020/01/02/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A834"/>
   <updated>2020-01-02T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2020/01/02/思路探讨34</id>
   <content type="html">&lt;p&gt;平时会用‘印象笔记’来记录自己在脑中一闪而过的想法，这些想法很细碎，很难形成连贯的大篇幅的文章，于是我把它们都命名为‘随记’1、2、3、4…到现在已经记录到了60多个随记片段，部分已经写入到文章中已经被删除，留下了45个片段还没有被连接进入大篇幅的文章中。&lt;/p&gt;

&lt;p&gt;在平时写文章的过程中会用到一些片段，但很多片段都没有成形的理论体系，连自己都想不明白也就无法拿出来与大家分享。其中有一些理论在当时看来觉得很对，现在回过头去看却是错误的观念。今天就把一部分记录在‘随记’里的错误观念，拿出来复盘一下，当时自己是怎么想的，为什么错。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;h3 id=&quot;记在随记3中的&quot;&gt;记在‘随记3‘中的:&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;	大多数成功是可以根据现有的经验推出来的。
	虽然不能说一定成功，不过，一旦有了先前成功经验，就能在大概率上，或者说关键点加强成功概率。

	这样说有点泛泛而谈，有些成功经验确实是没有意义的，比如躺赢，突然被风口吹上天，突然政策转变你刚好被砸中等，一些完全没有自己全程经历过的事情，没有辛苦琢磨过的，都有可能是伪成功经验，这种经验是大坑。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这段想法错从本质上就错误的理解了’成功‘。’成功‘就是赚到钱吗？不是的。但当时记录我就是这么认为的，话中透露出我对赚到钱的渴望，这种渴望会诱导我走向错误的道路，比如只关注短期结果，对长期的计划和展望关注度减少，形象到生活中比如只关注与钱有关的事物，而对人与人之间的合作比如情绪、交流、人情交际上关注度减少，最终将导致原本想找捷径更快走向目标’赚钱‘，反而绕了更大的弯路甚至走向了完全相反的方向’费钱‘。&lt;/p&gt;

&lt;h6 id=&quot;其实只对钱感兴趣的必定会和合作之道背道而驰在这个我们光怪陆离的时代致富之道何止万千即使是旁门左道有时候也会为人带来巨富对此我们不必感到惊讶虽然我们绝不敢说做人正直有所不为的人一定能够成功但是我们却敢断言守正道明事理必能使其勇气保持不坠并且不失尊严与自信&quot;&gt;其实只对钱感兴趣的必定会和合作之道背道而驰，在这个我们光怪陆离的时代，致富之道何止万千，即使是旁门左道，有时候也会为人带来巨富。对此我们不必感到惊讶，虽然我们绝不敢说做人正直、有所不为的人一定能够成功，但是我们却敢断言，守正道、明事理必能使其勇气保持不坠，并且不失尊严与自信。&lt;/h6&gt;

&lt;h3 id=&quot;记在随记2中的&quot;&gt;记在’随记2‘中的:&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;	能力太大就会很容易越过界，造成严重失误。

	枯燥，乏味，充斥着大部分生活，如果没有目标，很快就会陷入困惑，从而降低学习和工作效率。

	游戏太浪费时间和精力，能否找到有效的放松方式很关键，健康的放松方式和方式习惯让人非常受益，每个人每个阶段都不一样。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;言语和文字中看出本质，这段想法的本质是对’个人能力‘的错误理解。我当时太关注’个人能力‘。’个人能力‘能解决所有问题吗？不能。但当时的记录来看我就是这么认为的，认为个人能力可以解决所有问题，至少认为是可以解决所有核心问题。对个人能力的错误理解，其实就是对’人与人合作‘的错误理解，人类是合作的社会，没有合作就没有人类发展，工作、生活也是同样的道理因为它反映了人类生活的结构规则。人类社会得益于分工制度，它为人类生产生活提供巨大效益。如果每个人都是什么都要完全自己来，没有人合作，没有人站在前人的肩膀上工作、学习，那么整个社会就是停留在原始的动物阶段，没有工具、知识、文化的积累，一生下来就是重新开始，只靠着基因进化适应环境，跟动物没有分别。&lt;/p&gt;

&lt;h6 id=&quot;对于合作产生的效益来说个人能力实在微不足道工作生活中亦是如此我们应该放弃个人能力改变世界的愚蠢想法转而致力于促进更多的合作改进合作向更好的合作走去当每个个人奉献出自己的力量的时让-1人-加-1人-或者-10人--10人-或者更多组合-10人--12人--18人----等于原来结果数倍还多的能量&quot;&gt;对于合作产生的效益来说，个人能力实在微不足道。工作、生活中亦是如此，我们应该放弃个人能力改变世界的愚蠢想法，转而致力于促进更多的合作，改进合作向更好的合作走去，当每个个人奉献出自己的力量的时，让 1人 加 1人 或者 10人 + 10人 或者更多组合 10人 + 12人 + 18人 + … = 等于原来结果数倍还多的能量。&lt;/h6&gt;

&lt;h3 id=&quot;记在随记7中的&quot;&gt;记在’随记7‘中的:&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;	我们要理解蠢货的存在，不要去计较蠢货的思维逻辑。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;没有人是蠢货，存在即合理。我们心中的世界本质上是围绕我们自己为中心的世界，本来就是一个扭曲的世界，用扭曲的世界看真实的世界，怎么看怎么不合理。归根结底还是我们自己太狭隘，太固执，没有放平心态，敞开怀抱拥抱世界，这种不寻求合作反而倒退去固步自封的想法，是错上加错。&lt;/p&gt;

&lt;h3 id=&quot;记在随记7中的-1&quot;&gt;记在’随记7‘中的:&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;	我的智慧足以教育女儿登顶同龄人成为人生赢家，但我的财力不足以支撑我这么做。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我把教育想的太简单，也高估了自己，这是在我还没有深入接触幼儿教育之前的想法，那时完全由我妻子来负责，而我只负责空想。当我深入接触和了解时则与我想象的完全不同，一下子感觉到了自己的渺小，对方的伟大。《自卑与超越》中写下了大量人类儿童时期的心理过程让我深受启发。一些浅见是，我们应该更多的思考，如何让孩子对周围的环境和人发生足够的兴趣，以使得他们在日常活动中形成更多的合作，我们也应该致力于让孩子尽早了解社会分工制度，尽早确立自己感兴趣的职业方向。&lt;/p&gt;

&lt;h6 id=&quot;其实还有许多愚蠢的想法愚蠢到自己都羞涩难忍就放在后面的文章中再一一显露出来好了写思维探讨的目的就是进阶思维方式和思维角度靠什么进阶呢光靠自己空想没用要多看书而且是精读再和周围的朋友们多聊聊各自的想法从中碰撞出思维的火花&quot;&gt;其实还有许多愚蠢的想法，愚蠢到自己都羞涩难忍，就放在后面的文章中再一一显露出来好了。写’思维探讨‘的目的就是进阶思维方式和思维角度，靠什么进阶呢，光靠自己空想没用，要多看书而且是精读，再和周围的朋友们多聊聊各自的想法，从中碰撞出思维的火花。&lt;/h6&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》 第一章，程序逻辑优化技巧</title>
   <link href="http://www.luzexi.com/2019/12/31/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-%E7%A8%8B%E5%BA%8F%E9%80%BB%E8%BE%91%E4%BC%98%E5%8C%96%E6%8A%80%E5%B7%A7"/>
   <updated>2019-12-31T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/12/31/Unity3D高级编程之进阶主程-程序逻辑优化技巧</id>
   <content type="html">&lt;p&gt;我们常常说起优化，总是不免想到渲染，Drawcall，Overdraw，引擎算法，底层组件，以及大型的整套的解决方案等，却很少关注普通业务逻辑中问题，其实不只是算法能大幅度提高业务逻辑的效率，普普通通的业务代码也同样可以有质的飞越。&lt;/p&gt;

&lt;p&gt;一行普通的代码，高手写和普通人写可能就会不一样，优秀程序员常关注代码对性能的影响，普通人只关注是否能完成功能，这样日积月累代码质量就会形成差距。这节我们就来讲讲，在我们平时的业务逻辑编程中，我们应该怎么去优化这些不起眼但却蕴含着巨大潜力的性能问题。&lt;/p&gt;

&lt;h3 id=&quot;使用list和dictionary时提高效率&quot;&gt;使用List和Dictionary时提高效率&lt;/h3&gt;

&lt;p&gt;前面几节我们讲解了List、Dictionary的源码，知道了它的实质都是数组，Dictionary有两个数组，一个数组存索引一个数组存数据，我们遍历List和Dictionary都是在遍历数组。&lt;/p&gt;

&lt;p&gt;了解底层的逻辑有助于我们更好的运用的它们，比如当我们使用List插入时，我们知道其实就是向数组中写入元素并遍历其后面的数据依次向后移动的过程。了解了这些，每次当我们使用List的Insert时都会注意一些。还有Contains函数，它是一个以遍历形式来寻找结果的函数，每次使用它都会从头到尾遍历一下直到寻找到结果，Remove也是一样，都是以遍历的形式存在。如果我们在代码中使用它们的频率比较高，就会带来很多不必要的性能消耗，这是我们很多人常常不注意的。我们常常因为不了解或图方便而使用了这些接口而并没有考虑它们带来的性能损耗。&lt;/p&gt;

&lt;p&gt;Dictionary也有诸多问题，首先它是一个使用哈希冲突解决关键字的字典组件，因此哈希值与容器中数组的映射关键和GetHashCode获取哈希值的函数比较关键。哈希冲突与数组大小有很大关系，数组越大哈希冲突率就越小，因此我们在写程序时应该注意设置Dictionary的初始大小，尽量设置一个合理的大小，而不是什么不做，任由它自己扩容，这不但让哈希冲突变得频繁，而且扩容时数组的回收也加重了GC的负担。除了之外，在C#中所有类都继承自Object类，Dictionary使用Object的GetHashCode来获取类实例的哈希值，而GetHashCode是用算法将内存地址转化为哈希值的过程，因此我们可以认为它只是一个算法，并没有做任何值做缓存，每次调用它都会计算一次哈希值，这是比较隐形的性能损耗。如果频繁使用GetHasCode作为关键字来提取Value，那么我们应该关注下GetHashCode的算力损耗，是否可以用唯一ID的方式来代替GetHashCode算法。&lt;/p&gt;

&lt;h3 id=&quot;巧用struct&quot;&gt;巧用Struct&lt;/h3&gt;

&lt;p&gt;Struct和Class的区别常常被人遗忘，Struct结构是值类型，它与Class不同的是Struct传递时并不是靠引用(指针)形式来传递而是靠复制，我们可以通俗的认为它是通过内存拷贝来实现传递(真实的情况是通过字节对齐规则循环多次复制内存)，也就是说我们在传递Struct时，其实是在不断的克隆数据。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;struct A
{
	public int gold;
}

public void main()
{
	A a = new A();
	a.gold = 1;
	A b = a;
	b.gold = 2;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们来简单举个例子，上述Struct中有一个整数变量gold，实例a的gold值为1，将a赋值给b后，b的gold设置为2，此时a中的gold依然为1，因为a和b是两份不同的内存。&lt;/p&gt;

&lt;p&gt;Struct这样值类型对我们做性能优化有什么好处呢？首先如果Struct被定义于函数中的局部变量，则Struct的值类型变量分配的内存是在栈上的，栈是连续内存，并且在函数调用结束后，栈的回收非常快速和简单只要将尾指针置零就可以了(并非真正意义上的释放内存)，这样我们即不会产生内存碎片，也不需要内存垃圾回收，CPU读取数据对连续内存也非常友好高效。&lt;/p&gt;

&lt;p&gt;除了上述这些，Struct数组也对我们提高内存访问速度有所帮助。我们要明白由于Struct是值类型，所以它的内存也与值类型一样是连续的，Class数组则只是引用(指针)变量空间连续，这是大不同的。连续内存在CPU读取数据时，CPU的缓存可以帮助我们提高命中率，因为CPU在读取内存时会把一个大块内容放入缓存，当下次读取时先从缓存中找如果命中则不需要再向内存读取数据(缓存比内存快100倍)，而非连续内存则在缓存使用时的命中率比较低，因此CPU缓存命中率的高低很影响CPU效率。&lt;/p&gt;

&lt;p&gt;但是也不是所有Struct都能提高缓存命中率，如果Struct太大超过了缓存拷贝的数据块，则缓存就不再起作用了，因为拷贝进去的数据只有1个甚至半个Struct。于是就有很多架构就抛弃了Struct，彻底使用值类型连续空间的方式来提高CPU缓存命中率，把所有的数值都集合起来用数组的形式存放，而具体对象上则只存放一个索引值，当需要存取时都通过索引来操作数组。我们来看一个例子就知道是怎么回事。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;class A
{
	public int a;
	public float b;
	public bool c;
}

class B
{
	public int index;
}

class C
{
	private static C _instance;
	public static C instance
	{
		get
		{
			if(null == _instance)
			{
				_instance = new C();
				return _instance;
			}
			return _instance;
		}
	}

	public int[] a = new int{2, 3, 5, 6};
	public float[] b = new float{2.1f, 3.4f, 1.5f, 5.4f};
	public bool[] c = new bool{false, true, false, true};
}

public void main()
{
	A[] arrayA = new A[3]{new A(), new A(), new A()};

	print(&quot;A class this is a {0} b {1} c {3}&quot;,Aa.a, Aa.ba, Aa.c);
	
	B b = new B()
	b.index = 2;

	C c = C.instance;

	print(&quot;B class this is a {0} b {1} c {3}&quot;,c.a[b.index], c.b[b.index], c.c[b.index]);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述代码中A类使用了我们非常熟悉的面向对象的编程方式，把所有属性变量都放在自己身上，数据的集合则以引用的方式存储在数组上，而B则将数据集中存储在了C类中。当两者都对数据进行存取时，A类数据的内存是分散的，因为每次分配A实例时都是从内存中找一块空地来分配并不保证相邻，arrayA中只是引用连续而非内存连续，而B类的数据是连续内存的数组，因为它将所有同类的数据集中在了值类型的数组中，值类型的数组分配内存是一定是内存连续的，这样就能更多的利用缓存提高CPU读取数据的命中率。缓存机制是将最近使用过的数据存入最近的最快的空间中，离CPU最近的就是一级和二级缓存，它们是珍贵的，我们应该充分利用他们。&lt;/p&gt;

&lt;h3 id=&quot;尽可能的使用对象池&quot;&gt;尽可能的使用对象池&lt;/h3&gt;

&lt;p&gt;说到类实例，我们应该清楚的明白，内存分配和消耗对我们程序的影响，这也是提高程序效率的关键所在。Unity3D使用的C#语言，它使用垃圾回收机制回收内存，即使Unity3D在发布后将C#转为了C++也依然使用垃圾回收机制来执行分配和销毁内存。作为高级程序员，我们应该要感受到，在创建类实例时内存分配的CPU耗时，以及垃圾回收时的艰难。&lt;/p&gt;

&lt;p&gt;垃圾回收有多艰难呢？我们来解释一下，我们在C#中随意的new创建类实例，由不管它们的死活所以的丢弃或置空引用变量，类实例不断的被引用和间接引用，又不断被抛弃，垃圾回收器就要负责仔仔细细的收拾我们的烂摊子。内存不可能永远被分配而不回收，于是垃圾回收只能在当内存不够用的时候去到处问和检查(意思是遍历所有已分配的内存块)，看看哪个类实例完全被遗弃了就捡回来(意思是完全没有被人引用了)，将内存回收掉。因此当我们业务逻辑越庞大数据量越多时，垃圾回收需要检查的内容也越来越多，如果回收后依然内存不足，就得向系统请求分配更多内存。&lt;/p&gt;

&lt;p&gt;垃圾回收过程如此艰难，它每次回收时都会占用大量CPU算力，因此我们应该尽可能的用对象池来重复利用已经创建的对象，这有助于减少内存配合时的耗时，也减少堆内存的内存块数量，最终减少了垃圾回收时带来的CPU损耗。&lt;/p&gt;

&lt;p&gt;除了new 某个类创建内存导致的GC耗时增加外，以我的经验来看，很容易被我们忽略的是 new List&lt;T&gt; 这种类型的使用，我们在平时编程时会大量使用动态数组，并且随时将它抛弃，因为它太容易使用以及丢弃了。类似的 Dictionary&amp;lt;int,List&lt;T&gt;&amp;gt; 也是众多被忽略的内存分配消耗之一，被装进Dictionary字典中的List常被随意的丢弃(Remove掉)，没去注意它是否能被再次利用。&lt;/T&gt;&lt;/T&gt;&lt;/p&gt;

&lt;p&gt;C#中一个简单的通用对象池就能解决问题，但我们常常嫌弃它，并觉得麻烦。在我的编程经验中，图方便图好用的往往要付出性能损耗的代价，而性能高的代码通常都有点反人性，我们应该尽量找到一个平衡点，即有高的代码可读性，也尽量不要被人性所驱使了去做一些图方便的事，这在任何时候都是非常有用的价值的。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C#&quot;&gt;internal class ObjectPool&amp;lt;T&amp;gt; where T : new()
{
    private readonly Stack&amp;lt;T&amp;gt; m_Stack = new Stack&amp;lt;T&amp;gt;();
    private readonly UnityAction&amp;lt;T&amp;gt; m_ActionOnGet;
    private readonly UnityAction&amp;lt;T&amp;gt; m_ActionOnRelease;

    public int countAll { get; private set; }
    public int countActive { get { return countAll - countInactive; } }
    public int countInactive { get { return m_Stack.Count; } }

    public ObjectPool(UnityAction&amp;lt;T&amp;gt; actionOnGet, UnityAction&amp;lt;T&amp;gt; actionOnRelease)
    {
        m_ActionOnGet = actionOnGet;
        m_ActionOnRelease = actionOnRelease;
    }

    public T Get()
    {
        T element;
        if (m_Stack.Count == 0)
        {
            element = new T();
            countAll++;
        }
        else
        {
            element = m_Stack.Pop();
        }
        if (m_ActionOnGet != null)
            m_ActionOnGet(element);
        return element;
    }

    public void Release(T element)
    {
        if (m_Stack.Count &amp;gt; 0 &amp;amp;&amp;amp; ReferenceEquals(m_Stack.Peek(), element))
            Debug.LogError(&quot;Internal error. Trying to destroy object that is already released to pool.&quot;);
        if (m_ActionOnRelease != null)
            m_ActionOnRelease(element);
        m_Stack.Push(element);
    }
}

internal static class ListPool&amp;lt;T&amp;gt;
{
    // Object pool to avoid allocations.
    private static readonly ObjectPool&amp;lt;List&amp;lt;T&amp;gt;&amp;gt; s_ListPool = new ObjectPool&amp;lt;List&amp;lt;T&amp;gt;&amp;gt;(null, l =&amp;gt; l.Clear());

    public static List&amp;lt;T&amp;gt; Get()
    {
        return s_ListPool.Get();
    }

    public static void Release(List&amp;lt;T&amp;gt; toRelease)
    {
        s_ListPool.Release(toRelease);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这两个对象池的类都是从Unity的UI库中提取出来的，都是非常实用的对象池工具，我们应该尽可能的使用他们。上述对象池使用了栈队列，将废弃的对象存储起来，并在需要时从栈队列中推出实例交给使用者。对象池并不复杂，麻烦的是使用，程序中对所有需要创建对象实例、销毁对象实例、移除对象实例的部分都需要用对象池去调用。&lt;/p&gt;

&lt;p&gt;我们来举几个使用上述ObjectPool和ListPool对象池的例子：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C#&quot;&gt;public class A
{
	public int a;
	public float b;
}



public void Main()
{
	Dictionary&amp;lt;int,A&amp;gt; dic2 = new Dictionary&amp;lt;int, A&amp;gt;(16);
	for(int i = 0 ; i&amp;lt;1000 ; i++)
	{
		A a = ObjectPool&amp;lt;A&amp;gt;.Get(); //从对象池中获取对象
		a.a = i;
		a.b = 3.5f;

		A item = null;
		if(dic.TryGetValue(a.a, out item))
		{
			ObjectPool&amp;lt;A&amp;gt;.Release(item); //值会被覆盖，所以覆盖前收回对象
		}

		dic[a.a] = a;

		int removeKey = Random.RangeInt(0,10);
		if(dic.TryGetValue(removeKey, out item))
		{
			ObjectPool&amp;lt;A&amp;gt;.Release(item); //移除时收回对象
			dic.Remove(removeKey);
		}
	}

	Dictionary&amp;lt;int,List&amp;lt;A&amp;gt;&amp;gt; dic2 = new Dictionary&amp;lt;int, List&amp;lt;A&amp;gt;&amp;gt;(1000);
	for(int i = 0 ; i&amp;lt;1000 ; i++)
	{
		List&amp;lt;A&amp;gt; arrayA = ListPool&amp;lt;A&amp;gt;.Get(); // 从对象池中分配List内存空间

		dic2.Add(i,arrayA);

		List&amp;lt;A&amp;gt; item = null;
		int removeKey = Random.RangeInt(0,1000);
		if(dic.TryGetValue(removeKey, out item))
		{
			ListPool&amp;lt;A&amp;gt;.Release(item); // 移除时收回对象
			dic.Remove(removeKey);
		}
	}

}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述代码中，A类和List&lt;a&gt;需要被创建1000次，每次创建都使用对象池，并在字典Dictionary移除时将对象送回对象池。这样我们就可以不断利用被回收的对象池，然不用总是创建新的对象，所有被遗弃的对象都会被存储起来，并不会被垃圾回收程序回收，最终内存不断被重复利用，减少内存分配和释放所带来的消耗。&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;字符串导致的性能问题&quot;&gt;字符串导致的性能问题&lt;/h3&gt;

&lt;p&gt;说实在字符串性能在大部分语言中都是比较难解决的问题，C#中尤其如此。在C#中string是引用类型，每次我们动态创建一个string时C#都会在堆内存中分配一个内存用于存放字符串。我们来看看它到底有多恐怖。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;string strA = &quot;test&quot;;
for(int i = 0 ; i&amp;lt;100 ; i++)
{
	string strB = strA + i.ToString();

	string[] strC = strB.Split('e');

	strB = strB + strC[0];

	string strD = string.Format(&quot;Hello {0}, this is {1} and {2}.&quot;,strB, strC[0], strC[1]);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这是一段恐怖的程序，循环中每次都会将strA字符串和 i整数字符串连接，strB所得到的值是从内存中新分配的字符串，然后将strB切割成两半成为了strC，这两半又新分配了两段新的内存，再将strB与strC[0]连接起来，这又申请了一段内存，这段内存装上了strB和strC[0]两者连接的内容赋值给了strB，strB原来的内容因为没有变量指向就找不到了，最后用string.Format的形式将4个字符串串联起来，新分配的内存中装有4者的连接内容。&lt;/p&gt;

&lt;p&gt;这里要注意一点，字符串常量是不会被丢弃的，比如这段程序中 “test” 和 “Hello {0}, this is {1} and {2}.” 这两个常量，它们常驻与于内存，即使下次没有变量指向它们，它们也不回被回收，下次使用时也不需要重新分配内存。原因我们放到下面计算机执行原理里去说。&lt;/p&gt;

&lt;p&gt;每次循环中，都向内存申请了5次内存，并且抛弃了1次strA + i.ToString()的字符串内容，因为没有变量指向了这个字符串。这还不是最恐怖的，最恐怖的是，每次循环结束都会将前面所有分配的内存内容都抛弃，重新分配一次，就这样向不断抛弃和申请，总共向内存申请了500次内存段，并全部抛弃，内存被浪费的很厉害。&lt;/p&gt;

&lt;p&gt;为什么会这样呢？究其原因是C#语言对字符串并没有任何缓存机制，每次使用都需要重新分配string内容，据我所知很多语言都没有字符串的缓存机制，因此我们的字符串连接、切割、组合等操作都会向内存申请新的内容，并且抛弃没有变量指向的字符串，等待GC回收。我们知道GC一次会消耗很多CPU，如果我们不注意字符串的问题，不断浪费内存将导致程序不定时卡顿，并且随着程序运行时间越来越长，各程序模块不良代码的运行积累，程序卡顿次数会逐步提高，运行效率也将越来越差。&lt;/p&gt;

&lt;p&gt;解决字符串问题有两个方法，第一是自建缓存机制，可以用一些标志性的Key键值来一一对应字符串，比如游戏项目中常用ID来构造某个字符串，如下伪代码：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;int ID = 101;

ResData resData = GetDataById(ID);

string strName = &quot;This is &quot; + resData.Name;

return strName;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;一个ID变量对应一个字符串，这种形式，我们可以建立一个字典容器将它缓存起来，下次用的时候就不需要重新申请内存了，如下伪代码：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;Dictionary&amp;lt;int,string&amp;gt; strCache;

string strName = null;
if(!strCache.TryGetValue(id, out strName))
{
	ResData resData = GetDataById(ID);
	string strName = &quot;This is &quot; + resData.Name;
	strCache.Add(id, strName);
}

return strName;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们用Dictionary字典容器将字符串缓存起来，每次先查询字典中的内容是否存在，有则直接使用，没有则创建一个并将其植入字典容器中，以便下次使用。&lt;/p&gt;

&lt;p&gt;第二中方式需要用C#到一些‘不安全’的native方法，也就是类似C++的指针方式来做处理string。&lt;/p&gt;

&lt;p&gt;由于string类本身一定会申请新的内存，因此我们需要突破这个瓶颈，直接使用指针来改变string中字符串的值，这样就能重复利用string，不需要重新分配内存。&lt;/p&gt;

&lt;p&gt;C#虽然委托了大部分内存内容，但它也允许我们使用非委托方式来访问和改变内存内容，这对C#来说是不安全的(C#中有unsafe关键字)。我们可以通过非委托方式来改变string中的内容，使它能够被我们再利用。我们看个例子，如下代码：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
string strA = &quot;aaa&quot;;

string strB = &quot;bbb&quot; + &quot;b&quot;;

fixed(char* strA_ptr = strA)
{
	fixed(char* strB_ptr = strB)
	{
		memcopy((byte*)strB_ptr， (byte*)strA_ptr, 3*sizeof(char));
	}
}

print(strB); // 此时strB内容为 “aaab”
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;注意这里用”bbb” + “b”的方式生成新字符串，是因为我们不打算改变常量字符串内存块，所以新分配了内存来做实验。&lt;/p&gt;

&lt;p&gt;我们把strB的前3个字符的内容变成了strA中的内容，但我们并没有增加任何内存，因为我们使用了不安全的非托管方法，自己来控制内存。通过这样的方式来再利用已经申请的string内存，将已经有的string缓存起来再利用。我们来看看如何再利用的例子。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;Dictionary&amp;lt;int,string&amp;gt; cacheStr;

public unsafe string Concat(string strA, string strB)
{
	int a_length = a.Length;

	int b_length = b.Length;

	int sum_length = a_Length + b_Length;

	string strResult = null;

	if(!cacheStr.TryGetValue(sum_length, out strResult))
	{
		//如果不存在sum_length长度的缓存字符串，那么久直接连接后存入缓存。
		strResult = strA + strB;

		cacheStr.Add(sum_length, strResult);

		return strResult;
	}

	//将缓存字符串再利用，用指针方式直接改变它的内容
	fixed(char* strA_ptr = strA)
	{
		fixed(char* strB_ptr = strB)
		{
			fixed(char* strResult_ptr = strResult)
			{
				//将strA中内容拷贝到strResult中
				memcopy((byte*)strResult_ptr, (byte*)strA_ptr, a_length*sizeof(char));

				//将strB中内容拷贝到strResult的a_Length长度后面内存中
				memcopy((byte*)strResult_ptr+a_Length, (byte*)strB_ptr, b_length*sizeof(char));
			}
		}
	}

	return strResult;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;当我们需要将连个字符串连接起来时，先去看看缓存中是否有可用长度的字符串，如果没有就直接连接并缓存，如果有则取出来，用针的方式改变缓存字符串的值。其中memcopy并不是系统函数而需要自己写，写法也很简单，拿到两个指针根据长度便利指针便利并赋值。如下可见：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;public unsafe void memcopy(byte* dest, byte* src, int len)
{
	while((--len)&amp;gt;=0)
	{
		dest[len] = src[len];
	}
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;字符串的隐藏问题&quot;&gt;字符串的隐藏问题。&lt;/h6&gt;

&lt;p&gt;string的Length方法可不是省油的灯，由于string类中只存放了字符串char[]数据，没有其他变量，因此string.Length需要通过遍历字符串来获取长度。类似于如下伪代码：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C#&quot;&gt;int sum = 0;
for( ; data[sum] != '\0' ; ++sum);
return sum;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;如果我们大量使用Length 就会浪费很多CPU，因为每次它都会遍历整个字符串。特别是是循环中的判断，如下伪代码：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;string str = &quot;Hello world.&quot;;
for(int i = 0 ; i&amp;lt;str.Length ; ++i)
{
	// do some thing.
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述代码就相当于两层循环，每次判断时都会重新遍历一遍字符串以获得长度。&lt;/p&gt;

&lt;p&gt;字符串比较也有隐藏问题，当两个字符串比较时，string首先会比较两个字符串的指针是否是一致的，一致则返回true，如果指针不一致则会遍历两者判断是否每个字符都是相等的。我们来看看它究竟是怎么做的：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;public bool Equals(String value) {
    if (this == null)                        //this is necessary to guard against reverse-pinvokes and
        throw new NullReferenceException();  //other callers who do not use the callvirt instruction

    if (value == null)
        return false;

    if (Object.ReferenceEquals(this, value))
        return true;
    
    if (this.Length != value.Length)
        return false;

    return EqualsHelper(this, value); //遍历两者的字符
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这段代码从源码中提取，在判空后，先判定两个string的引用是否相等，如果不相等再判断两者的长度是否相等，如果相等，再去遍历字符串的每个字符，每个字符是否相等，最终判定字符串是否相等。&lt;/p&gt;

&lt;p&gt;倘若我们操作后的两个字符串来自不同的内存段，那么它们在比较是否相等时就会遍历所有字符来判定是否相等。如下伪代码：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;string strA = &quot;Hello &quot;;

string strB = &quot;Hello &quot;;

strA = strA + &quot;C&quot;;

strB = strB + &quot;C&quot;;

if(Object.ReferenceEquals(strA, strB))
{
	return true;
}

if(strA == strB)
{
	return false;
}

return true;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这段代码最终会返回false，strA与strB看似相等，实则为不同内存段内容，当它们两使用等号比较时，就会遍历所有字符串里的字符来确定它们是否相等。&lt;/p&gt;

&lt;p&gt;string源码地址:https://referencesource.microsoft.com/#mscorlib/system/string.cs&lt;/p&gt;

&lt;h3 id=&quot;程序运行原理&quot;&gt;程序运行原理&lt;/h3&gt;

&lt;p&gt;为了能更好的优化我们写的程序，我们应该再深入些底层来了解下，计算机是如何执行我们写的程序的。这次我想我们得脱离下c#语言，因为c#为我们包装了太多东西，我们用起来很方便，同时也蒙蔽了我们的眼睛，让我们看不清底层原理。&lt;/p&gt;

&lt;p&gt;计算体系结构比较复杂也脱离了我们的主题内容，这里我想简单陈述下程序运行的原理，从而让我们可以擦亮眼睛看清一些我们被蒙蔽的原理。&lt;/p&gt;

&lt;p&gt;最终计算机要的都是机器码，那么机器码是怎么产生的呢，是通过编译产生的，机器码太难记，汇编就是用来帮助我们记机器码的，每个汇编指令都对应一个机器指令，所有由‘1’和‘0’组成的机器指令码都能一一对应到汇编指令，这么来看，一个可执行文件或一个库文件通常可以转化成汇编代码，很多黑客也是通过这种方式来查看我们写的程序的，厉害的黑客他们通常都精通汇编。&lt;/p&gt;

&lt;p&gt;一个程序在内存中运行时，通常由几个内存块组成，其中一个是指令内存块，里面存储的都是已经编写设计好的执行的指令，需要执行的指令都会从指令内存块中去取，指令计数器也不断跳跃在这些指令中。另一个为数据内存，里面存放的都是我们设置好的数据以及分配过的内存，其数据块中有一块内容可以称为静态数据块，通常里面存放的都是不变的数据，比如字符串常量，常量整数，常量浮点数，以及一些静态数据，这些数据在程序启动时最先被放入内存中。&lt;/p&gt;

&lt;p&gt;数据内存中，除了静态内存数据块外就是我们所说的堆内存数据。我们所有的动态内存申请都来自堆内存，我们可以认为它是一个很长的byte数组，当我们申请内存时，会从数组中找出一块我们指定大小的内存，这个内存不一定是空的，因为内存回收从来不会对内存单位有清理操作，那样太浪费算力了，从来都是将这段数据的指针回收或偏移，所以实际上，我们申请的内存块，在没初始化前都是未知的，有可能刚好前面用过的与我们相似的内容，如果不进行初始化直接使用就有可能会出现逻辑问题，例如本以为bool数值是false却得到的true。&lt;/p&gt;

&lt;p&gt;我们用惯类对象很容易以为内存中就是某个类的实例内存，其实在机器指令和内存中可没有这个说法，它只是块连续byte内存单元，具体其代表哪个类的实例只是我们想象的而已，这些都是编译器的功劳，这让我们不需要取关心某个内存块到底指的是什么，我们只需要知道程序里类是怎么写就可以了，高级语言让我们更方便，也让我们更傻瓜。其中最容易混淆的就是类的方法，很多人认为它也被放入了对象实例中，其实并不是这样，它被编译成指令序列，放在了指令内存块中，所有的方法，函数都在那里集中存放着。&lt;/p&gt;

&lt;p&gt;因此一个可执行文件或程序库里，几乎都是指令机器码，以及指令附带的常量数据，如果常量比较多可执行文件也跟着会变大。我们运行这段程序时，可执行文件和库被装入内存成为指令段内存，里面装着所有类的方法和函数，包括静态、公共的、私有的，都只是名字上的不同，比如 Class_A_public_GetData 可以认为是类对象A的public方法的GetData，这些字符串也通常代表指令的地址。&lt;/p&gt;

&lt;p&gt;除了上述这些，栈内存块也是比较重要的内容，它通常都是函数方法执行的重要部分，它与堆内存不同的是它是有秩序的，只允许遵守先进后出的规则，每分配一块内存，回收时也必须按照先进后出的秩序回收，这个规则使得栈内存永远是连续的，不会因为很多次使用后变出很多内存碎片从而导致有内存而无法分配的现象。我们所说的值类型数据大都在栈中分配，除非它被用于构造其他类型，比如类、数组等。&lt;/p&gt;

&lt;p&gt;说了这么多，其实就是汇编里的数据段、代码段、栈段三个段，他们分别使用了段地址和偏移量来表示数据和指令内容。当指令数据需要数据段内容时，就用数据段地址+偏移量去存取数据内存中的数据，当指令跳转时则使用代码段地址+偏移量来指向新的指令内存地址，当需要用到栈时则使用pop和push的汇编指令来偏移栈顶指针从而存取栈上的数据。除了内存，寄存器是离cpu最近也最快的存储单元，它一般都用来临时存放数据的，当然我们也可以自己写汇编让某些寄存器长期存放些数据以加快读取某数据的速度。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(三十三) 2019年我做了什么</title>
   <link href="http://www.luzexi.com/2019/12/28/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A833"/>
   <updated>2019-12-28T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/12/28/思路探讨33</id>
   <content type="html">&lt;p&gt;最近忙了好多事情，原以为休息在家可以轻松点，但依然忙的不可开交，我没常回家的1年多时间里有这么多事情堆积着。原本想着把《算法导论》再看一遍后把关于‘算法’的文章写完，可惜算法这块知识量和知识深度有点大，还需待点时日。&lt;/p&gt;

&lt;p&gt;2019年我做了什么呢？虚的我觉得讲出来可能大家都有不同的意见（什么人生哲学，生活大道理，思维方式，智慧之举等等），我们就讲讲实实在在能看到的，我把这些实实在在能看到的学习生活记录了下来，尽量记录的详尽，虽然有部分遗漏但大部分都被记录了下来。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;一整年我都在使用一个叫‘滴答’的任务app，我在里面设置了每天要做的事情，每天做完把它勾掉，做不完也没事，留到下一天来做。不想给自己太多的压力，也为了放过自己，既然做不完，那就让它过去，第二天里的任务继续有，只要我今天做了一个任务我就把它勾掉。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/life/idea-talking33-3-2.png&quot; alt=&quot;滴答详情&quot; /&gt;&lt;/p&gt;

&lt;p&gt;于是就有了上面这张图。其实不止这些，还加入了简简单单就能完成的任务，比如每天白天累了趴下休息10分钟，每天早中晚三餐规律饮食，每天不看娱乐新闻，每天下午3点吃点东西补充能量，每天12点前睡等等等，有28条每日任务。&lt;/p&gt;

&lt;p&gt;我做了这么多年游戏已经彻底清楚的明白对于一个平民玩家，每日任务就是其最宝贵的追上土豪玩家的途径，土豪玩家有钱没毅力快速提升靠钱砸，平民玩家没钱只能靠毅力靠肝靠做任务坚持几个月，最终还是能追上一部分土豪玩家的，毕竟他们整体上来说总是在不断放弃与重新开始中，平民玩家也就只有肝，那么肝之中，每日任务最为精华，其他途径都是效率相对比较低的。结合到实际生活中也是同样的道理，我们可以把人生看成一次游戏，要么你出生就有钱有地位，要么就只有肝这条路。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/life/idea-talking33-3.png&quot; alt=&quot;滴答等级&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我每天都会点掉很多任务，也留下了很多任务。每点掉一个任务就会增加一些经验，就像游戏人生一样，我每天做一些任务学一些东西就会增加一些人生阅历和知识经验。这个软件也是一样，于是我就有了如上图所示的经验等级。其实它的满级只有10级，但是在里面用的人真的很厉害，我花了很大的力气，坚持了这么久，还有百分之5的人没有被我击败，可想而知他们该有多努力。&lt;/p&gt;

&lt;p&gt;这是在我去年9月份时看了一本《做时间的朋友》后开始做的事情，当初的想法是把时间利用起来，跟时间一起‘玩’，这样就能让自己每天的生活很充实，也很高效。虽然事情一开始比较顺利，但随着日子一天天过，枯燥和疲惫的冲击，做这些事变得越来越困难，甚至一度停止过。&lt;/p&gt;

&lt;p&gt;这其实跟人生在岔道口的挑战一样，当挑战来临时，有明显的征兆，比如当挑战来临时你一定很疲惫，你一定很痛苦，甚至有时你很无聊。熬过去很多时候会是柳暗花明又一村的另一个美好世界的景象。我想我熬过来了，这个过程我加入了很多反馈机制，来调节我的感官上的感觉，以达到刺激自我感觉的目的。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/life/idea-talking33-8.png&quot; alt=&quot;anki&quot; /&gt;&lt;/p&gt;

&lt;p&gt;比如这张图，我用Anki软件来背英语单词，每天的背诵量被记录下来，并且用这种日历方格的统计形式展示出来，让我很有成就感，每天都为打满这个图而不懈的坚持，越多越有劲，越坚持越停不下来。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/life/idea-talking33-7.png&quot; alt=&quot;github&quot; /&gt;&lt;/p&gt;

&lt;p&gt;也比如这张图，这是我在github上写文章的记录，原本计划是每天写半小时文章，但计划并没有顺利的进行，这个任务难度确实很大，我不跟自己死磕到底，我也怕自己的身体受不了，于是我饶了自己，尽自己所能的每天写点，在身体允许的范围下尽量做到每周一篇文章。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/life/idea-talking33-9.jpeg&quot; alt=&quot;github&quot; /&gt;&lt;/p&gt;

&lt;p&gt;写书写文章的灵感并不是每天都有的，一开始会有很多灵感，是因为我这么多年积累了很多自己的生活，工作的知识和经验，想说的想表达的，但毕竟我的经历还是单薄，狭窄的世界观有时任然显露无疑，为了能让自己能有更多的知识，更深刻的理论体系，更广泛的见识，我坚持每天看书，每天风雨无阻坚持看半小时-两小时。看书的速度与日俱增，但始终无法敌过我买书的速度，看书的速度越快，买书的量就越凶残，最后导致办公桌上全是书。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/life/idea-talking33-4.png&quot; alt=&quot;github&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/life/idea-talking33-5.png&quot; alt=&quot;github&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/life/idea-talking33-6.png&quot; alt=&quot;github&quot; /&gt;&lt;/p&gt;

&lt;p&gt;其中我看了《精力管理》这本书后让我对时间和精力的管理，有了更深刻的认识。先前知识对时间的认识，也只是想着要好好利用时间，提高时间的利用率，但并没有结合人的属性来制定计划和规划。在看了《精力管理》后，对人本身的精力和时间上的规划有了更深刻的认识。先前我不屑于对时间和精力的精确管理，在看了《精力管理》后，让我对时间和精力管理有了更加浓厚的兴趣。于是就有了上面三张图的内容，这是我日常中记录下来的每天做事的大致内容，内容并没有精确细致到细节，但是时间块上的记录让我知道了我每天运用精力都在什么地方上。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/life/idea-talking33-1.png&quot; alt=&quot;github&quot; /&gt;&lt;/p&gt;

&lt;p&gt;就这样用昨天的记录来调整今天的状态，每天周而复始，从今年3月份开始，记录下了我每天都把精力花在哪了，用每日、每周、每月的统计图表的方式，让自己了解自己的状态如何。于是就有了上图这张，至今为止记录下来的统计图。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/life/idea-talking33-10.png&quot; alt=&quot;github&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这些记录与练习仍然不够，我在不断发掘自己的缺点，比如我的说话表达能力很差，因为30几年来一直认为‘没什么好说的，成绩和实干决定一切’的错误观念，导致我脸部的肌肉非常糟糕，软软的只能用来装可爱，因为没有得到充分的锻炼，在说话时完全没有脸部肌肉工作的成分，使得说话非常吃力，完全由大脑来控制该说什么话，如何说，如何开口，如何动口，大脑工作得很累，久而久之就会觉得说话很累，不想说话，于是情况更加糟糕。&lt;/p&gt;

&lt;p&gt;为了改进这个缺点，我找到了一个方法，就是朗读，用朗读书本的方式去锻炼脸部的肌肉，当脸部肌肉得到了锻炼在我说话时就不会那么累了，部分词语完全可以用脸部的肌肉记忆代替大脑的工作，减轻大脑的负担。于是就有了上图，在‘喜马拉雅app’上每天对着书朗读10-20分钟，并且上传到个人文件夹保存起来留作纪念，也是一种正向的反馈机制让自己能够更加有兴趣继续下去。&lt;/p&gt;

&lt;h6 id=&quot;我发现我越来越向务实靠近脱虚务实的趋向越来越严重这并不是什么绝对的好现象但至少我现在需要它最近在公众号上看到的一个人提出来心上学事上练的口号我很是赞成接下来我希望自己少写些哲学类话题而更多的是事实在在的从事情上学习研究分析真正能够做到事上练心上学&quot;&gt;我发现我越来越向务实靠近，脱虚务实的趋向越来越严重，这并不是什么绝对的好现象，但至少我现在需要它。最近在公众号上看到的一个人提出来，”心上学，事上练”的口号，我很是赞成。接下来我希望自己少写些，哲学类话题，而更多的是事实在在的从事情上学习、研究、分析，真正能够做到‘事上练，心上学’。&lt;/h6&gt;

&lt;h6 id=&quot;2019年只是一个开始既然开始了我不想再停下来别人说曾国藩自律了20年才有这样的成就那我可以先试试自律10年我看见了另一个世界一个更加积极更加自律更加善于合作的世界&quot;&gt;2019年只是一个开始，既然开始了，我不想再停下来，别人说曾国藩自律了20年才有这样的成就，那我可以先试试自律10年。我看见了另一个世界，一个更加积极，更加自律，更加善于合作的世界。&lt;/h6&gt;

&lt;h6 id=&quot;题外话我很赞同自卑与超越里说的合作能力是大多数人所缺乏的我们要努力成为一个容易合作能够主动建立合作帮助别人加入合作的人&quot;&gt;题外话：我很赞同《自卑与超越》里说的，”合作能力是大多数人所缺乏的“，我们要努力成为一个容易合作、能够主动建立合作、帮助别人加入合作的人。&lt;/h6&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第七章，渲染管线与图形学(五) - Projector投影原理</title>
   <link href="http://www.luzexi.com/2019/12/10/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-%E6%B8%B2%E6%9F%93%E7%AE%A1%E7%BA%BF%E4%B8%8E%E5%9B%BE%E5%BD%A2%E5%AD%A610"/>
   <updated>2019-12-10T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/12/10/Unity3D高级编程之进阶主程-渲染管线与图形学10</id>
   <content type="html">&lt;h3 id=&quot;projector-投影的原理与应用&quot;&gt;Projector 投影的原理与应用&lt;/h3&gt;

&lt;p&gt;Unity3D中的 Projector 组件投影像是一个很神秘的组件，但其实它依然运用的是以着色器为基准的渲染流程，和普通的3D模型渲染从本质上来看并没有实质上的区别，唯一的区别是它从它自己的视体(视锥体或平视体)中检测到的模型，并根据默认或者自定义的材质球与Shader着色器，将这些物体又重新绘制了一遍。&lt;/p&gt;

&lt;p&gt;从Unity3D引擎上来讲，可以这样解释 Projector组件：&lt;/p&gt;

&lt;p&gt;根据 Projector组件自身的视体的范围，平视体或视锥体，遍历并计算出视体范围内与视体占边的所有物体。接着 Projector组件取得这些物体模型数据，并计算投影矩阵。这个投影矩阵是什么呢，其实就是前面说的 Projector 组件视体空间的投影矩阵。最后将投影矩阵传入Shader中，根据这个投影矩阵，对这些物体再渲染一次。&lt;/p&gt;

&lt;p&gt;投影中的Shader着色器是投影绘制的主要手段，Projector组件的工作只是检测了所有范围内的模型，并传递了投影空间的矩阵而已。投影Shader中，通常会结合传入的投影矩阵，将顶点转为 Projector 组件投影空间中，并以此为投影贴图的UV来渲染模型。&lt;/p&gt;

&lt;p&gt;例如这个简单的投影着色器：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;
sampler2D _MainTex;
float4x4 unity_Projector;
struct v2f{
	float4 pos:SV_POSITION;
	float4 texc:TEXCOORD0;
}

v2f vert(appdata_base v)
{
	v2f o;
	o.pos = mul(UNITY_MAXTRIX_MVP, v.vertex);
	o.texc = mul(unity_Projector, v.vertex);
	return o;
}

float4 frag(v2f i) : COLOR
{
	float4 c = tex2Dproj(_MainTex, i.texc);
	return c;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;投影着色器中 unity_Projector 变量就是由 Projector 组件传入到材质球的投影矩阵。在顶点着色器中 unity_Projector 矩阵被用来构造投影坐标，和普通的空间投影转换矩阵MVP(Model View Project)不同的是，其中的V是 Projector 空间的相关矩阵，即 Projector 组件所属的 Transform 的 worldToLocalMatrix变量，而P则是和Projector远近裁切相关的矩阵。用 unity_Projector 矩阵计算出vertex(顶点)在投影空间中的坐标后，我们就可以以此坐标为uv坐标绘制物体了。&lt;/p&gt;

&lt;p&gt;投影着色器中为什么可以将顶点坐标视为uv坐标呢？当坐标变换到投影空间后，其坐标空间就变成了投影平面视角，在这个视角中如果我们将平面视为纹理大小，就相当于一一匹配上顶点的uv坐标。&lt;/p&gt;

&lt;p&gt;这个转化后的投影空间中的坐标，也就是我们常说的”投影纹理坐标”。”投影纹理坐标”不能直接当作uv来作为纹理坐标来使用，需要调用 tex2Dproj 方法来获取纹理坐标:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	tex2Dproj(texture,uvproj);

	这个纹理投影函数，其实就是在使用之前会将该投影纹理坐标除以透视值

	可以等价于按如下方法使用普通二维纹理查询函数

	float4 uvproj = uvproj/uvproj.w;

	tex2D(texture,uvproj);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;效果如下图：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;缺图
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;​###### 投影的技巧还有很多，我们下面介绍几种投影技巧在游戏项目中的运用。&lt;/p&gt;

&lt;h6 id=&quot;平面阴影&quot;&gt;平面阴影&lt;/h6&gt;

&lt;p&gt;平面阴影也是投影技巧的一种，它稍微需要运用些图形计算，主要的原理是在着色渲染模型时，另外做一个Pass将模型上顶点转换到平面上再渲染一次，以此作为平面的阴影，无论地上有没地形，都以平面呈现。如下图:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺图
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;要计算这个平面阴影需要我们从光源点，顶点，法线，平面这些已知数据出发计算顶点转换到平面上的点。&lt;/p&gt;

&lt;p&gt;我们首先已知的是地面法线向量 TerrainNormal，和平面上随便一个初始点点的坐标 TerrainPos，我们假设我们需要计算的点为p点&lt;/p&gt;

&lt;p&gt;由平面表达公式得知，平面上的任意向量与该平面的点乘所得值为0，因此地面上的方向向量 TerrainNormal 与 要投影的坐标与初始点所形成的方向向量点乘为零，即：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	(p - TerrainPos) 点乘 TerrainNormal = 0
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;又由于平面映射的P点是由光射到顶点延伸到平面而得到的，所以&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	p = d*L + L0 其中L0为顶点，的l为光到顶点的射线方向，d为L0射到p点的距离。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;因此根据这两个公式，代入得到:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	(dL + L0 - TerrainPos) 点乘 TerrainNormal = 0
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;解析后为&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	dL 点乘 TerrainNormal + (L0 - TerrainPos) 点乘 TerrainNormal = 0
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;于是再得到d为:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	d = ((TerrainPos - L0) 点乘 TerrainNormal) / (l 点乘 TerrainNormal)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;再代入进 p = d * l + L0 这个公式得到p，即如下Shader中的顶点函数所写&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;Pass
{
	CGPROGRAM

	#pragma vertex vert
	#pragma fragment frag
	#include &quot;UnityCG.cginc&quot;

	struct appdata
	{
		float4 vertex : POSITION;
	};

	struct v2f
	{
		float4 vertex : SV_POSITION;
	};            

    float4 TerrainPos, TerrainNormal;
	v2f vert (appdata v)
	{
		v2f o;
        float4 wPos = mul(unity_ObjectToWorld, v.vertex);
        // 光的方向
        float3 direction = normalize(_WorldSpaceLightPos0.xyz);
        // d 值的计算
        float dist = dot(TerrainPos.xyz - wPos.xyz, TerrainNormal.xyz) / dot(direction, TerrainNormal.xyz);
        // 代入 p = d * l + L0 公式
        wPos.xyz = wPos.xyz + dist * direction;
        // 空间顶点转换
		o.vertex = mul(unity_MatrixVP, wPos);
		return o;
	}
	
	fixed4 frag (v2f i) : SV_Target
	{
		return fixed4(0,0,0,1);
	}
	ENDCG
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述Shader中，利用了_WorldSpaceLightPos0来确定光的反向，也可以用一个光的坐标与顶点的差值来得到一个光的方向。计算过程的公式转换可以以这张手绘的图作为参考。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/7/pingmianyinying.png&quot; alt=&quot;平面阴影&quot; /&gt;&lt;/p&gt;

&lt;p&gt;图中清晰的标明了所有已知向量，和可计算向量，以及最终需要结算的点，上述所说的这些公式的转换都是基于这个图来做的。&lt;/p&gt;

&lt;h6 id=&quot;利用深度信息计算图片投影&quot;&gt;利用深度信息计算图片投影&lt;/h6&gt;

&lt;p&gt;图片投影其实就是贴花的动态版，其做法也有很多种，我们在这里简单讲讲其中一种方法为:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	绘制一个box，其纹理的展示方式则以深度信息为依据，纹理贴近其绘制的模型上。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这种方式可以随着这个box的移动贴到不同物体上。&lt;/p&gt;

&lt;p&gt;大致的方向是，以一个BOX为渲染对象，渲染时在片元着色器中重新判定渲染纹理与渲染坐标。怎么判定呢？&lt;/p&gt;

&lt;p&gt;先从顶点着色器上获得顶点坐标的屏幕坐标，在片元着色器中传入的屏幕坐标时成了片元在屏幕上的坐标，用屏幕坐标获取对应的深度值，再让深度值作为z坐标来形成一个三维坐标，这时三维坐标只是屏幕空间上的坐标，我们让其屏幕空间转换到相机空间，再从相机空间转换到世界空间，再转换到投影空间，这样坐标就到了投影空间，再除以透视值，就得到坐标在0-1范围的坐标值，用这个值去提取纹理上的颜色，最后用这个颜色绘制片元。&lt;/p&gt;

&lt;p&gt;这个方法比较费的点为片元着色器中重新计算渲染坐标，这个坐标会以深度信息为z轴信息需要经过几个空间的转换，导致计算量比较大，不能放在顶点着色器中的原因是因为只有在片元着色器中才能得到片元深度信息。&lt;/p&gt;

&lt;h6 id=&quot;贴花喷图&quot;&gt;贴花(喷图)&lt;/h6&gt;

&lt;p&gt;贴花的制作，可以以摄像机为节点向外计算一个长方体，所有与长方体有交集的模型上的面上的顶点都被存储起来，另外如果有顶点里外都有的面，则计算与长方体的面相交的新节点并加入进来，最后把所有得到的顶点和三角面制作成一个新的模型，这个模型的顶点转换到立方体空间再除以透视值，则成为了uv点，以此来呈现一个贴花(喷图)的效果。&lt;/p&gt;

&lt;p&gt;参考文献：&lt;/p&gt;

&lt;p&gt;《Unity3D ShaderLab 开发实战详解》&lt;/p&gt;

&lt;p&gt;《维基百科 Line–plane intersection》 https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection&lt;/p&gt;

&lt;p&gt;Planar Shadow http://qiankanglai.me/2016/12/23/planar-shadow/&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第七章，渲染管线与图形学(四) - 多重采样以及着色器编译原理</title>
   <link href="http://www.luzexi.com/2019/12/04/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-%E6%B8%B2%E6%9F%93%E7%AE%A1%E7%BA%BF%E4%B8%8E%E5%9B%BE%E5%BD%A2%E5%AD%A69"/>
   <updated>2019-12-04T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/12/04/Unity3D高级编程之进阶主程-渲染管线与图形学9</id>
   <content type="html">&lt;h3 id=&quot;gpu上的多重采样multisampling与反走样antialiasing&quot;&gt;GPU上的多重采样(Multisampling)与反走样(Antialiasing)&lt;/h3&gt;

&lt;p&gt;多重采样(Multisampling)是一种对几何图元的边缘进行平滑处理的技术，也称为反走样技术之一。&lt;/p&gt;

&lt;p&gt;OpenGL支持几种不同的反走样技术，比如多重采样、线段反走样、多边形反走样、纹理图像压缩的质量以及导数精度设置等。事实上这几种反走样技术都是以开关的形式在OpenGL中存在，我们无法修改它们只能开启关闭或设置几个简单的参数。其中图形上走样算法大致是将原本单一的线条或像素块周围填充更多的像素块，具体的填充算法细节比我们想象的要复杂的多，而且不同OpenGL的版本算法中也有细微的差异，我们在这里不深入。&lt;/p&gt;

&lt;p&gt;多重采样也是反走样技术中的一种，它的工作方式是对每个像素的几何图元进行多次采样。在多次采样后，每个像素点不仅仅只是单个颜色(以及除了颜色外的深度值、模板值等信息)，还记录了许多样本值。&lt;/p&gt;

&lt;p&gt;这些样本值类似于将一个像素分拆成了更小型的像素，每个像素都存储着颜色、深度值、模板值等信息，当我们需要呈现最终图像的内容时，这个像素的所有样本值会被综合起来成为最终像素的颜色。也就是说采样的数量越多，线条与周围的像素点的融合越平滑，简单说就是颜色与颜色之间会有平滑过渡的颜色例如红色的线条周围是白色的背景于是红色线条上的像素与白色背景的交接处会有更多的粉色、浅粉色来过渡。&lt;/p&gt;

&lt;p&gt;在Unity3D中对这方面的反走样功能也提供了支持，我们可以通过Quality Settings中的AntiAliasing来设置，它将开启图形接口(OpenGL或DirectX)中的多边形的反走样算法，并且开启多重采样，根据多重采样信息对多边形边缘进行像素填充。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	AntiAliasing 可以设置3档采样质量分别是 2倍, 4倍 and 8倍的多重采样。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;GPU上的反走样代价是消耗更多的GPU算力和显存，它并不消耗任何CPU算力。&lt;/p&gt;

&lt;h3 id=&quot;着色器编译过程与变体&quot;&gt;着色器编译过程与变体&lt;/h3&gt;

&lt;p&gt;我们在知道GPU渲染管线如何运作后，对着色器编译过程仍然需要深入了解一下，我们还是以使用OpenGL为例来学习着色器在Unity3D中从编译到执行的全过程。&lt;/p&gt;

&lt;p&gt;着色程序的编译过程与C语言等编译语言的编译过程非常类似，只是C语言在编译时是以离线的方式进行，而着色器程序的编译则是当引擎需要时，通过引擎调用图形接口(OpenGL或DirectX)的方式将代码读进来再编译，着色器程序只需要编译一次后面可以重复利用，这和我们通常所说的JIT(Just in time 即时编译)有点相似。&lt;/p&gt;

&lt;h6 id=&quot;着色器编译前的准备工作都是unity3d控制和执行的当需要某个着色器程序时unity3d引擎通过判断是否存在已经编译好的着色器程序来决定是否编译着色器代码或是重用已经编译好的着色器程序&quot;&gt;着色器编译前的准备工作都是Unity3D控制和执行的，当需要某个着色器程序时Unity3D引擎通过判断是否存在已经编译好的着色器程序，来决定是否编译着色器代码或是重用已经编译好的着色器程序。&lt;/h6&gt;

&lt;p&gt;那么着色程序从编译到执行过程到底是怎样的呢？让我们来了解一下。&lt;/p&gt;

&lt;p&gt;首先当Unity3D引擎得知渲染需要用到的Shader不曾被编译过时，就会调用图形接口OpenGL的 glCreateShader 为着色器创建一个新的着色器对象。&lt;/p&gt;

&lt;p&gt;然后通过文件程序从Shader文件中获取Shader内容(字符串)并调用OpenGL的 glShaderSource 将源代码(字符串)交给刚刚创建的着色器对象上。&lt;/p&gt;

&lt;h6 id=&quot;这时一个空的着色器对象已经关联了着色器源代码我们可以通过调用编译接口opengl的-glcompileshader-对这个着色器对象进行编译&quot;&gt;这时一个空的着色器对象已经关联了着色器源代码，我们可以通过调用编译接口OpenGL的 glCompileShader 对这个着色器对象进行编译。&lt;/h6&gt;

&lt;p&gt;编译完成后，我们可以通过OpenGL的 glGetShaderInfoLog 来获得编译信息以及是否成功的结果。&lt;/p&gt;

&lt;p&gt;到此仅仅是1个着色器对象编译完成，这个着色器对象可能是顶点着色器，也可能是片元着色器，或也许是细分着色器或几何着色器。通常情况下，有好几个着色器需要编译，顶点着色器和片元着色器通常都成对出现，则会创建相应的着色器对象来分别编译它们的源代码。&lt;/p&gt;

&lt;p&gt;有了着色器对象还不够，我们需要把这些着色器关联起来。首先Unity3D引擎会使用OpenGL的 glCreateProgram 接口需要创建一个空的着色器程序。然后多次调用 glAttachShader 来一个个地绑定着色器对象。&lt;/p&gt;

&lt;h6 id=&quot;当所有必要的着色器对象关联到着色器程序之后就可以链接对象来生成可执行程序了引擎将调用opengl的-gllinkprogram-接口将所有关联的着色器对象生成一个完整的着色器程序&quot;&gt;当所有必要的着色器对象关联到着色器程序之后，就可以链接对象来生成可执行程序了，引擎将调用OpenGL的 glLinkProgram 接口将所有关联的着色器对象生成一个完整的着色器程序。&lt;/h6&gt;

&lt;p&gt;当然，着色器对象也可能存在某些问题，因此在链接过程中依然可能失败，通常引擎会通过 glGetProgramiv 来查询链接操作的结果，也会通过 glGetProgramInfoLog 接口来获取程序链接的日志信息，由此我们就可以判断错误原因。&lt;/p&gt;

&lt;p&gt;成功完成了着色器程序的链接后，Unity3D引擎就可以通过调用 glUseProgram 来运行着色器程序。&lt;/p&gt;

&lt;p&gt;我们平常在Unity3D中用到的Shader中的Pass，每个Pass中都有着色器需要编译，因此每次在绘制不同的Pass时都会对Pass中的顶点着色器和片元着色器进行编译。也就是说，Unity引擎会为每个Pass标签生成一个着色器程序，生成这些着色器程序后，执行顺序仍然按照Pass的先后次序来。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/7/shader_compile.jpg&quot; alt=&quot;着色器编译流程&quot; /&gt;&lt;/p&gt;

&lt;h6 id=&quot;现在我们了解了着色器的编译过程它通过编译和连接的方式将一个shader中的多个着色器制作成一个着色程序当渲染需要时交给gpu去执行渲染只是这样的编译过程都是在以阻塞的方式进行的因此我们在引擎运行的过程中常常会在某针消耗些许cpu来编译shader如果shader的量非常大的话可能就会有卡顿的现象发生&quot;&gt;现在我们了解了着色器的编译过程，它通过编译和连接的方式将一个Shader中的多个着色器制作成一个着色程序，当渲染需要时交给GPU去执行渲染。只是这样的编译过程都是在以阻塞的方式进行的，因此我们在引擎运行的过程中常常会在某针消耗些许CPU来编译Shader，如果Shader的量非常大的话可能就会有卡顿的现象发生。&lt;/h6&gt;

&lt;h3 id=&quot;shader变体常常就会发生这种我们不希望的编译卡顿因为它的量比较大我们在unity3d中使用shader-varant变体时常常有比较严重的编译和内存困扰&quot;&gt;Shader变体常常就会发生这种我们不希望的编译卡顿，因为它的量比较大，我们在Unity3D中使用Shader Varant(变体)时常常有比较严重的编译和内存困扰。&lt;/h3&gt;

&lt;p&gt;那么什么是“变体”呢。其实它是由Unity3D自身的宏编译指令引发的多种变化实体生成的Shader文件，它为不同情况而编译生成不同的着色器程序。从引擎端的做法来看，Unity3D把不同的编译版本拆分成了不同的着色器源代码文件，在运行时选择对应适合的着色器源文件，再通过图形接口将这些着色器源代码编译成着色器程序关联到渲染中。&lt;/p&gt;

&lt;p&gt;为什么要使用宏编译指令导致生成这么多的着色器程序呢？因为要简化Shader，Unity3D要让一个Shader在不同材质球上的应用不同的效果时更加便捷，有了引擎识别变体的功能，我们修改和完善Shader起来会更加方便和高效。&lt;/p&gt;

&lt;h6 id=&quot;假如没有变体我们在编写很多同一个风格但不同效果的shader时在使用和维护过程中会有诸多的麻烦和不便为了统一风格为了提高工作效率也为了能更好的打通各部门之间的沟通渠道以及能让美术同学能更好的发挥对画面效果的调整将同一个风格不同效果的shader写在同一个shader文件里是必不可少的这样能更加容易的统一美术风格和制作流程目的就是为了让风格更加统一沟通更加便捷效率更加高&quot;&gt;假如没有变体，我们在编写很多同一个风格但不同效果的Shader时，在使用和维护过程中会有诸多的麻烦和不便。为了统一风格，为了提高工作效率，也为了能更好的打通各部门之间的沟通渠道，以及能让美术同学能更好的发挥对画面效果的调整，将同一个风格不同效果的Shader写在同一个Shader文件里是必不可少的，这样能更加容易的统一美术风格和制作流程，目的就是为了让风格更加统一，沟通更加便捷，效率更加高。&lt;/h6&gt;

&lt;p&gt;我们来看看Unity3D是怎么通过编译指令来编写变体的，编写变体后它是怎么生成着色器源代码的。&lt;/p&gt;

&lt;p&gt;在Unity3D的Shader中我们使用&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;#pragma multi_compile
#pragma shader_feature
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;两个指令来实现着色器程序的自定义宏，它既适用于顶点片元着色器也适用于表面着色器。我们通过 multi_compile 指令编写例如：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;#pragma multi_compile A_ON B_ON
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;这样会生成并编译两个shader变体一个是a_on的版本一个是b_on的版本&quot;&gt;这样会生成并编译两个Shader(变体)，一个是A_ON的版本，一个是B_ON的版本。&lt;/h6&gt;

&lt;p&gt;在运行时，Unity3D会根据材质(Material)的关键字(Material的对象方法EnableKeyword和DisableKeyword)或者全局着色器关键字(Shader的类方法EnableKeyword和DisableKeyword)来选择使用对应的着色器。运行的时候Unity3D会根据材质(Material)的关键字或者Shader全局关键字判断应该使用哪个Shader，如果两个关键字都为false，那么会使用第一个(A_ON)Shader变体。&lt;/p&gt;

&lt;p&gt;我们也可以创建多个组合关键字例如:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;#pragma multi_compile A B C
#pragma multi_compile D E
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这种多组合关键字会使得Shader的变体成倍的增加，例如上述的预编译方式，会生成 3x2 = 6 个变体，分别是 A+D、 B+D、 C+D、 A+E、 B+E、 C+E 六种。&lt;/p&gt;

&lt;p&gt;假如multi_compile组合多到10行，每行2个，就是2的10次方个Shader(变体)就是1024个，这样生成这1024个Shader(变体)，把这1024个变体Shader全部加载到内存的话恐怕互占用非常多的内存，1024个Shader每个50K的话就会占用50MB内存。不仅如此，实时编译Shader是非常耗时的操作，如果没有提前编译Shader而在场景中使用Shader，就会不断有不同的Shader被实时的编译，这常常是导致游戏卡顿的重要原因之一。&lt;/p&gt;

&lt;h6 id=&quot;除了-multi_compile-之外另外一个指令-shader_feature-也可以设置预编译宏与-multi_compile-的区别是-shader_feature-不会将没有被使用到的shader变体打包进包内因此-shader_feature-更适合材质球的关键字指定预编译内容因为unity3d只生成和编译被使用的预编译情况而-multi_compile-更适合全局shader指定关键字因为它会把所有组合都编译一遍无论有没有用到&quot;&gt;除了 multi_compile 之外，另外一个指令 shader_feature 也可以设置预编译宏，与 multi_compile 的区别是 shader_feature 不会将没有被使用到的Shader(变体)打包进包内，因此 shader_feature 更适合材质球的关键字指定预编译内容，因为Unity3D只生成和编译被使用的预编译情况，而 multi_compile 更适合全局Shader指定关键字，因为它会把所有组合都编译一遍，无论有没有用到。&lt;/h6&gt;

&lt;p&gt;除了这两个自定义预编译指令，Unity3D 本身自带的一些内建的 multi_compile 的快捷写法也会导致Shader变体的产生：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	multi_compile_fwdbase 为前向渲染编译多个变体，不同的变体处理不同的光照贴图的计算，并且控制了主平行光的阴影的开关。
	
	multi_compile_fwdadd 为前向渲染额外的光照部分编译多个变体，不同的变体处理不同灯光类型，平行光，聚光灯，点光，以及他们附带的cookie纹理版本。
	
	multi_compile_fwdadd_fullshadows 和 multi_compile_fwdadd 一样，并且包含了灯光的实时阴影功能。
	
	multi_compile_fog 为处理不同的雾效类型（off/linear/exp/exp2）扩展了多个变体。
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;总结无论是-multi_compile-还是-shader_feature-亦或内建预编译指令都会造成-shader变体数量的增多使得内存增加运行时编译次数增多每次编译shader都会消耗cpu当unity3d在运行时检测到需要渲染的材质球里是不曾被编译的shader时则会将与自己匹配的shader变体拎出来编译一下生成一个着色器程序因此为了应对变体在运行时的编译消耗通常会在运行时提前将所有shader变体编译一下使得运行中不再有shader编译的cpu消耗&quot;&gt;总结，无论是 multi_compile 还是 shader_feature 亦或内建预编译指令，都会造成 Shader(变体)数量的增多，使得内存增加，运行时编译次数增多，每次编译Shader都会消耗CPU。当Unity3D在运行时检测到需要渲染的材质球里是不曾被编译的Shader时，则会将与自己匹配的Shader变体拎出来编译一下生成一个着色器程序，因此为了应对变体在运行时的编译消耗，通常会在运行时提前将所有Shader变体编译一下，使得运行中不再有Shader编译的CPU消耗。&lt;/h6&gt;

&lt;p&gt;参考文献:&lt;/p&gt;

&lt;p&gt;《OpenGL编程指南》&lt;/p&gt;

&lt;p&gt;《Unit3D Documentation》 https://docs.unity3d.com/Manual/SL-MultipleProgramVariants.html&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第七章，渲染管线与图形学(三) - 渲染原理与知识3</title>
   <link href="http://www.luzexi.com/2019/11/20/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-%E6%B8%B2%E6%9F%93%E7%AE%A1%E7%BA%BF%E4%B8%8E%E5%9B%BE%E5%BD%A2%E5%AD%A68"/>
   <updated>2019-11-20T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/11/20/Unity3D高级编程之进阶主程-渲染管线与图形学8</id>
   <content type="html">&lt;h3 id=&quot;gpu-instancing-的来龙去脉&quot;&gt;GPU Instancing 的来龙去脉&lt;/h3&gt;

&lt;p&gt;GPU Instancing 初次听到这个名词时还有点疑惑，其实翻译过来应该是GPU多实例化渲染，它本身是GPU的一个功能接口，Unity3D将它变得更简单实用。&lt;/p&gt;

&lt;p&gt;前面讲过一些关于Unity3D的动态合批(Dynamic batching)与静态合批(Static batching)的功能，GPU Instancing 实际上与他们一样都是为了减少Drawcall而存在。那么有了动态合批和静态合批为什么还需要 GPU Instancing 呢，究竟他们之间有什么区别呢，我们不妨来简单回顾一下Unity3D动态合批(Dynamic batching)与静态合批(Static batching)。&lt;/p&gt;

&lt;h6 id=&quot;开启动态合批dynamic-batching时unity3d引擎会检测视野范围内的非动画模型通过遍历所有渲染模型计算包围盒在视锥体中的位置如果完全不在视锥体中则抛弃筛选符合条件的模型进行合批操作将他们的网格合并后与材质球一并传给gpu去绘制&quot;&gt;开启动态合批(Dynamic batching)时，Unity3D引擎会检测视野范围内的非动画模型(通过遍历所有渲染模型，计算包围盒在视锥体中的位置，如果完全不在视锥体中则抛弃)，筛选符合条件的模型进行合批操作，将他们的网格合并后与材质球一并传给GPU去绘制。&lt;/h6&gt;

&lt;p&gt;动态合批需要符合什么条件呢：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	1，900个顶点以下的模型。

	2，如果我们使用了顶点坐标，法线，UV，那么就只能最多300个顶点。

	3，如果我们使用了UV0，UV1，和切线，又更少了，只能最多150个顶点。

	4，如果两个模型缩放大小不同，不能被合批的，即模型之间的缩放必须一致。

	5，合并网格的材质球的实例必须相同。即材质球属性不能被区分对待，材质球对象实例必须是同一个。

	6，如果他们有lightmap的数据，必须是相同的才有机会合批。

	7，多个pass的Shader是绝对不会被合批。

	8，延迟渲染是无法被合批。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;动态合批的条件比较苛刻，很多模型都无法达到合并的条件。为什么它要使用这么苛刻的条件呢，我们来了解下设计动态合批这个功能的意图。&lt;/p&gt;

&lt;h6 id=&quot;动态合批dynamic-batching这个功能的目标是以最小的代价合并小型网格模型减少drawcall&quot;&gt;动态合批(Dynamic batching)这个功能的目标是以最小的代价合并小型网格模型，减少Drawcall。&lt;/h6&gt;

&lt;p&gt;很多人会想既然合并了为什么不把所有的模型都合并呢，这样不是更减少Drawcall的开销。其实如果把各种情况的大小的网格都合并进来，就会消耗巨大的CPU算力，而且它不只一帧中的计算量，而是在摄像机移动过程中每帧都会进行合并网格的消耗算力，这使得CPU算力消耗太大，相比减少的Drawcall数量，得不偿失。因此Unity3D才对这种极其消耗CPU算力的功能做了如此多的的限制，就是为了让它在运作时性价比更高。&lt;/p&gt;

&lt;p&gt;与动态合批不同，静态合批(Static batching)并不实时合并网格，而是在离线状态下生成合并的网格，并将它以文件形式存储合并后的数据，这样在当场景被加载时，这些合并的网格数据也一同被加载进内存中，当渲染时提交给GPU。因此场景中所有被标记为静态物体的模型，只要拥有相同实例的材质球都会被一并合并成网格。&lt;/p&gt;

&lt;p&gt;静态合批能降低不少Drawcall，但也存在不少弊端。被合批的模型必须是静态的物体，它们是不能被移动旋转和缩放的，也只有这样我们在离线状态下生成的网格才是有效的（离线的网格数据不需要重新计算），因为合并后的网格，内部是不能动的，它也必须与原模型吻合，因此静态是必须的条件。其生成的离线数据被放在Vertex buffer和Index buffer中。&lt;/p&gt;

&lt;h6 id=&quot;静态合批生成的离线网格将导致存放在内存的网格数据量剧增因为在静态合批中每个模型都会独立生成一份网格数据无论他们所使用的网格是否相同也就是说场景中有多少个静态模型就有多少个网格与原本只需要一个网格就能渲染所有相同模型的情况不一样了&quot;&gt;静态合批生成的离线网格将导致存放在内存的网格数据量剧增，因为在静态合批中每个模型都会独立生成一份网格数据，无论他们所使用的网格是否相同，也就是说场景中有多少个静态模型就有多少个网格，与原本只需要一个网格就能渲染所有相同模型的情况不一样了。&lt;/h6&gt;

&lt;p&gt;其好处是静态合批后同一材质球实例(材质球实例必须相同，因为材质球的参数要一致)调用Drawcall的数量合并了，合批也不会额外消耗实时运行中的CPU算力，因为它们在离线时就生成的合批数据(也就是网格数据)，在实时渲染时如果该模型在视锥体范围内，三角形索引将被部分提取出来简单的合并后提交，而那些早就被生成的网格将被整体提交，当整体网格过大时则会导致CPU和GPU的带宽消耗过大，整个数据必须从系统内存拷贝到GPU显存或缓存，最后由GPU处理渲染。&lt;/p&gt;

&lt;h6 id=&quot;简而言之动态合批为了平衡cpu消耗和gpu性能优化将实时合批条件限制在比较狭窄的范围内静态合批则牺牲了大量的内存和带宽以使得合批工作能够快速有效的进行&quot;&gt;简而言之，动态合批为了平衡CPU消耗和GPU性能优化，将实时合批条件限制在比较狭窄的范围内。静态合批则牺牲了大量的内存和带宽，以使得合批工作能够快速有效的进行。&lt;/h6&gt;

&lt;p&gt;GPU Instancing 没有动态合批那样对网格数量的限制，也没有静态网格那样需要这么大的内存，它很好的弥补了这两者的缺陷，但也有存在着一些限制，我们下面来逐一阐述。&lt;/p&gt;

&lt;p&gt;与动态和静态合批不同的是，GPU Instancing 并不通过对网格的合并操作来减少Drawcall，GPU Instancing 的处理过程是只提交一个模型网格让GPU绘制很多个地方，这些不同地方绘制的网格可以对缩放大小，旋转角度和坐标有不一样的操作，材质球虽然相同但材质球属性可以各自有各自的区别。&lt;/p&gt;

&lt;p&gt;从图形调用接口上来说 GPU Instancing 调用的是 OpenGL 和 DirectX 里的多实例渲染接口。我们拿 OpenGL 来说:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;void glDrawArraysInstanced(GLenum mode, GLint first, GLsizei count, Glsizei primCount);

void glDrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei primCount);

void glDrawElementsInstancedBaseVertex(GLenum mode, GLsizei count, GLenum type, const void* indices, GLsizei instanceCount, GLuint baseVertex);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这三个接口正是 GPU Instancing 调用OpenGL多实例渲染的接口，第一个是无索引的顶点网格集多实例渲染，第二个是索引网格的多实例渲染，第三个是索引基于偏移的网格多实例渲染。这三个接口都会向GPU传入渲染数据并开启渲染，与平时渲染多次要多次执行整个渲染管线不同的是，这三个接口会分别将模型渲染多次，并且是在同一个渲染管线中。&lt;/p&gt;

&lt;p&gt;如果只是一个坐标上渲染多次模型是没有意义的，我们需要将一个模型渲染到不同的多个地方，并且需要有不同的缩放大小和旋转角度，以及不同的材质球参数，这才是我们真正需要的。GPU Instancing 正为我们提供这个功能，上面三个渲染接口告知Shader着色器开启一个叫 InstancingID 的变量，这个变量可以确定当前着色计算的是第几个实例。&lt;/p&gt;

&lt;p&gt;有了这个 InstancingID 就能使得我们在多实例渲染中，辨识当前渲染的模型到底使用哪个属性参数。Shader的顶点着色器和片元着色器可以通过这个变量来获取模型矩阵、颜色等不同变化的参数。我们来看看在Unity3D的Shader中我们应该做些什么:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;Shader &quot;SimplestInstancedShader&quot;
{
    Properties
    {
        _Color (&quot;Color&quot;, Color) = (1, 1, 1, 1)
    }

    SubShader
    {
        Tags { &quot;RenderType&quot;=&quot;Opaque&quot; }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_instancing // 开启多实例的变量编译
            #include &quot;UnityCG.cginc&quot;

            struct appdata
            {
                float4 vertex : POSITION;
                UNITY_VERTEX_INPUT_INSTANCE_ID //顶点着色器的 InstancingID定义
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                UNITY_VERTEX_INPUT_INSTANCE_ID //片元着色器的 InstancingID定义
            };

            UNITY_INSTANCING_BUFFER_START(Props) // 定义多实例变量数组
                UNITY_DEFINE_INSTANCED_PROP(float4, _Color)
            UNITY_INSTANCING_BUFFER_END(Props)
           
            v2f vert(appdata v)
            {
                v2f o;

                UNITY_SETUP_INSTANCE_ID(v); //装配 InstancingID
                UNITY_TRANSFER_INSTANCE_ID(v, o); //输入到结构中传给片元着色器

                o.vertex = UnityObjectToClipPos(v.vertex);
                return o;
            }
           
            fixed4 frag(v2f i) : SV_Target
            {
                UNITY_SETUP_INSTANCE_ID(i); //装配 InstancingID
                return UNITY_ACCESS_INSTANCED_PROP(Props, _Color); //提取多实例中的当前实例的Color属性变量值
            }
            ENDCG
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述的Shader是一个很常见的GPU Instancing 写法，使用 Instancing 在Shader作为选取参数的依据。Shader中_Color 和 unity_ObjectToWorld (模型矩阵)是多实例化的，它们通过 InstancingID 作为索引来确定取数组中的变量。&lt;/p&gt;

&lt;p&gt;InstancingID被包含在了宏定义中我们无法看到，我们来看看上述Shader中包含有 INSTANCE 字样的宏定义是怎样的，以此来剖析GPU Instancing是怎样用InstancingID来区分不同实例的变量的。&lt;/p&gt;

&lt;p&gt;首先编译命令 multi_compile_instancing 会告知着色器我们将会使用多实例变量。&lt;/p&gt;

&lt;p&gt;其次在顶点着色器和片元着色的输入输出结构中，加入 UNITY_VERTEX_INPUT_INSTANCE_ID 告知结构中多一个变量即：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	uint instanceID : SV_InstanceID;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这么看来我们就知道了每个顶点和片元数据结构中都定义了 instanceID 这个变量，这个变量将被用于确定使用多实例数据数组中的索引，它很关键。&lt;/p&gt;

&lt;p&gt;接着Shader中把需要用到的多实例变量参数定义起来:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;		UNITY_INSTANCING_BUFFER_START(Props)
            UNITY_DEFINE_INSTANCED_PROP(float4, _Color)
        UNITY_INSTANCING_BUFFER_END(Props)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述中的宏很容易从字面看出它们为”开始多实例宏定义”，”对多实例宏属性定义参数”，以及”结束多实例宏定义”。这三个宏定义我们可以在 UnityInstancing.cginc 中看到，即如下:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;		#define UNITY_INSTANCING_BUFFER_START(buf)      UNITY_INSTANCING_CBUFFER_SCOPE_BEGIN(UnityInstancing_##buf)  struct {
        #define UNITY_INSTANCING_BUFFER_END(arr)        } arr##Array[UNITY_INSTANCED_ARRAY_SIZE]; UNITY_INSTANCING_CBUFFER_SCOPE_END
        #define UNITY_DEFINE_INSTANCED_PROP(type, var)  type var;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们可以从上述的宏定义代码中理解到，这三个宏组合起来可以对GPU Instancing的属性数组进行定义。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;        UNITY_INSTANCING_BUFFER_START(Props)
            UNITY_DEFINE_INSTANCED_PROP(float4, _Color)
        UNITY_INSTANCING_BUFFER_END(Props)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;就等于&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;        UNITY_INSTANCING_CBUFFER_SCOPE_BEGIN(UnityInstancing_Props) struct {
            float4 _Color;
        } arrPropsArray[UNITY_INSTANCED_ARRAY_SIZE]; UNITY_INSTANCING_CBUFFER_SCOPE_END
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们看到三个宏加起来组成了一个颜色结构的数组，我们从Unity3D接口传进去的参数就会装入这样的结构中被顶点和片元着色器使用。&lt;/p&gt;

&lt;p&gt;在顶点着色器与片元着色中，我们对 InstancingID 进行装配，它们分别为：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	UNITY_SETUP_INSTANCE_ID(v) 和 UNITY_SETUP_INSTANCE_ID(i);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;装配过程其实就是从基数偏移的过程 unity_InstanceID = inputInstanceID + unity_BaseInstanceID。最终我们通过 UNITY_SETUP_INSTANCE_ID 装配得到了 unity_InstanceID 即当前渲染的多实例索引ID。&lt;/p&gt;

&lt;p&gt;有了多实例的索引ID，我们就可以通过这个变量获取对应的当前实例的属性值，于是就有了以下的宏定义 UNITY_ACCESS_INSTANCED_PROP，通过这个宏定义我们能提取多实例中的我们需要的变量。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;#define UNITY_ACCESS_INSTANCED_PROP(arr, var)   arr##Array[unity_InstanceID].var

UNITY_ACCESS_INSTANCED_PROP(Props, _Color); //提取多实例中的当前实例的Color属性变量值

等于

arrPropsArray[unity_InstanceID]._Color
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;有了类似_color的多实例属性操作在模型矩阵变化中也需要具备同样的操作我们没看到模型矩阵多实例是因为unity在shader编写时用宏定义把它们隐藏起来了它就是-unityobjecttoclippos&quot;&gt;有了类似_Color的多实例属性操作，在模型矩阵变化中也需要具备同样的操作，我们没看到模型矩阵多实例是因为Unity在Shader编写时用宏定义把它们隐藏起来了，它就是 UnityObjectToClipPos。&lt;/h6&gt;

&lt;p&gt;UnityObjectToClipPos 其实是一个宏定义，当多实例渲染开启时，它被定义成了如下:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;#define unity_ObjectToWorld     UNITY_ACCESS_INSTANCED_PROP(unity_Builtins0, unity_ObjectToWorldArray)

inline float4 UnityObjectToClipPosInstanced(in float3 pos)
{
    return mul(UNITY_MATRIX_VP, mul(unity_ObjectToWorld, float4(pos, 1.0)));
}
inline float4 UnityObjectToClipPosInstanced(float4 pos)
{
    return UnityObjectToClipPosInstanced(pos.xyz);
}
#define UnityObjectToClipPos UnityObjectToClipPosInstanced
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这个定义也同样可以在 UnityInstancing.cginc 中找到，其中unity_ObjectToWorld是关键，它从多实例数组中取出了当前实例的模型矩阵，再与坐标相乘后计算投影空间的坐标。也就是说当开启 Instancing 多实例渲染时，UnityObjectToClipPos 会从多实例数据数组中取模型矩阵来做模型到投影空间的转换。而当不开启 Instancing 时，UnityObjectToClipPos 则只是用当前独有的模型矩阵来计算顶点坐标投影空间的位置。&lt;/p&gt;

&lt;p&gt;到此我们就从着色器中获取了多实例的属性变量，根据不同实例的不同索引获取不同属性变量包括模型矩阵，从而渲染到不同的位置，以及不同的旋转角度和不同的缩放大小，更多的比如颜色、反射系数等属性都可以通过传值的方式传入Shader中，用GPU Instancing的方式渲染。&lt;/p&gt;

&lt;h6 id=&quot;知道了-gpu-instancing-是如何渲染任然还不够最好我们还知道数据是怎么传进去的我们拿-opengl-接口代码来分析下&quot;&gt;知道了 GPU Instancing 是如何渲染任然还不够，最好我们还知道数据是怎么传进去的。我们拿 OpenGL 接口代码来分析下：&lt;/h6&gt;

&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;
//获取各属性的索引
int position_loc = glGetAttribLocation(prog, &quot;position&quot;);
int normal_loc = glGetAttribLocation(prog, &quot;normal&quot;);
int color_loc = glGetAttribLocation(prog, &quot;color&quot;);
int matrix_loc = glGetAttribLocation(prog, &quot;model_matrix&quot;);

//按正常流程配置顶点和法线
glBindBuffer(GL_ARRAY_BUFFER, position_buffer); //绑定顶点数组
glVertexAttribPointer(position_loc, 4, GL_FLOAT, GL_FALSE, 0, NULL); //定义顶点数据规范
glEnableVertexAttribArray(position_loc); //按上述规范，将坐标数组应用到顶点属性中去
glBindBuffer(GL_ARRAY_BUFFER, normal_buffer); //绑定发现数组
glBertexAttribPointer(normal_loc, 3, GL_FLOAT, GL_FALSE, 0, NULL); //定义发现数据规范
glEnableVertexAttribArray(normal_loc); //按上述规范，将法线数组应用到顶点属性中去

//开始多实例化配置
//设置颜色的数组。我们希望几何体的每个实例都有一个不同的颜色，
//将颜色值置入缓存对象中，然后设置一个实例化的顶点属性
glBindBuffer(GL_ARRAY_BUFFER, color_buffer); //绑定颜色数组
glVertexAttribPointer(color_loc, 4, GL_FLOAT, GL_FALSE, 0, NULL); //定义颜色数据在color_loc索引位置的数据规范
glEnableVertexAttribArray(color_loc); //按照上述的规范，将color_loc数据应用到顶点属性上去

glVertexattribDivisor(color_loc, 1); //开启颜色属性的多实例化，1表示每隔1个实例时共用一个数据

glBindBuffer(GL_ARRAY_BUFFER, model_matrix_buffer); //绑定矩阵数组
for(int i = 0 ; i&amp;lt;4 ; i++)
{
	//设置矩阵第一行的数据规范
	glVertexAttribPointer(matrix_loc + i, 4, GL_FLOAT, GL_FALSE, sizeof(mat4), (void *)(sizeof(vec4)*i));

	//将第一行的矩阵数据应用到顶点属性上去
	glEnableVertexAttribArray(matrix_loc + i);

	//开启第一行矩阵数据的多实例化，1表示每隔1个实例时共用一个数据
	glVertexattribDivisor(matrix_loc + i, 1);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这个示例很精准的表达了数据是如何从CPU应用层传输到GPU上再进行实例化的过程。我在代码上做了比较详尽的注释，首先获取需要推入顶点属性的数据的索引，再将数组数据与OpenGL
缓存进行绑定，这样才能注入到OpenGL里去，接着告诉 OpenGL 每个数据对应的格式，然后再根据前一步描述的格式应用到顶点属性中去，最后开启多实例化属性接口，让 InstancingID 起效。&lt;/p&gt;

&lt;h6 id=&quot;总结我们解析了-gpu-instancing-在-unity3d-中的工作方式得知了它能用同一个模型同一个材质球渲染不同的位置角度缩放大小以及不同颜色等属性gpu-instancing-并没有对模型网格做任何限制也没有占用大量内存的来换取性能很好的弥补了动态合批dynamic-batching与静态合批static-batching的不足&quot;&gt;总结，我们解析了 GPU Instancing 在 Unity3D 中的工作方式，得知了它能用同一个模型同一个材质球渲染不同的位置、角度、缩放大小、以及不同颜色等属性。GPU Instancing 并没有对模型网格做任何限制，也没有占用大量内存的来换取性能，很好的弥补了动态合批(Dynamic batching)与静态合批(Static batching)的不足。&lt;/h6&gt;

&lt;p&gt;只是它毕竟是只能围绕一个模型来操作，只有相同网格(Mesh)和相同的材质球实例(参数可以不同，但必须使用API来设置不同参数)的情况下才能启动多个实例在同一个渲染管线中渲染的优化操作，而动态合批和静态合批却只需要材质球实例一致，网格是可以有差别的。&lt;/p&gt;

&lt;h6 id=&quot;gpu-instancing动态合批静态合批三者所擅长的各不相同有互相弥补的地方各自本身也存在着不同程度的限制和优缺点从整体上来看gpu-instancing-更适合同一个模型渲染多次的情况而动态合批dynamic-batching更适合同一个材质球并且模型面数较少的情况静态合批static-batching更适合当我们能容忍内存扩大的情况&quot;&gt;GPU Instancing、动态合批、静态合批三者所擅长的各不相同，有互相弥补的地方，各自本身也存在着不同程度的限制和优缺点。从整体上来看，GPU Instancing 更适合同一个模型渲染多次的情况，而动态合批(Dynamic batching)更适合同一个材质球并且模型面数较少的情况，静态合批(Static batching)更适合当我们能容忍内存扩大的情况。&lt;/h6&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(三十一) 钱、才、运</title>
   <link href="http://www.luzexi.com/2019/11/09/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A831"/>
   <updated>2019-11-09T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/11/09/思路探讨31</id>
   <content type="html">&lt;p&gt;最近几年一直在突破自己的认知，可能是我本身从小对世界的认知水平就比较狭小的缘故，努力了好几年才只是普通人的水平，但对我来说已经是很厉害了，我很满足，山外有山，人外有人，一山还有一山高，一水还有一水深，做好自己最重要。&lt;/p&gt;

&lt;p&gt;从小到大一直无法想明白的一件事是，‘有才’为什么不一定有钱，有钱人为什么不是因为‘有才’才赚到钱。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;h6 id=&quot;后来才渐渐明白这是我人生中一个比较大的认知错误认为钱和才是对等的&quot;&gt;后来才渐渐明白，这是我人生中一个比较大的认知错误，认为钱和才是对等的。&lt;/h6&gt;

&lt;p&gt;钱和才，虽然谈不上完全是两码事，但他们之间的联系确实没有这么紧密，不过‘有才’至少能让你解决温饱问题。&lt;/p&gt;

&lt;h6 id=&quot;一个人有价值就是有知识有技能有经验和赚到大钱是两码事很多时候有价值的人能被人看重但并不一定能赚到钱因为公司或者说组织不一定能成事他是厉害了周围的人不配合不协作各怀鬼胎一个人再厉害也无法扭转乾坤即使周围的人协同性很好而时代潮流不配合也同样无济于事&quot;&gt;一个人有价值(就是有知识，有技能，有经验)和赚到大钱是两码事，很多时候有价值的人能被人看重，但并不一定能赚到钱，因为公司或者说组织不一定能成事，他是厉害了，周围的人不配合不协作，各怀鬼胎，一个人再厉害也无法扭转乾坤，即使周围的人协同性很好而时代潮流不配合也同样无济于事。&lt;/h6&gt;

&lt;p&gt;所以想要赚到大钱，‘天时、地利、人和’一样不能少，这个就是我们俗称的‘运气’。说起来很俗气，‘运气’很神秘，就像神棍一样，没有科学的依据，但确实这个东西真真正正存在我们的周围。&lt;/p&gt;

&lt;p&gt;这个‘运气’，有两种诠释：&lt;/p&gt;

&lt;h6 id=&quot;一种是像稻盛和夫日本经营之神创业百分百成功做什么成什么企业破产了求他接手立刻妙手回春这类的认为运气是靠自己争取来的以精神世界为中心自己积善行德才能积攒足够多的运气用利他心态来做事加上不屑努力最后全社会都会回馈给他以运气最终靠着这股运气走上成功的道路&quot;&gt;一种是像稻盛和夫(日本经营之神，创业百分百成功，做什么成什么，企业破产了求他接手，立刻妙手回春)这类的，认为‘运气’是靠自己争取来的，以精神世界为中心，自己积善行德才能积攒足够多的‘运气’，用利他心态来做事，加上不屑努力，最后全社会都会回馈给他以‘运气’，最终靠着这股‘运气’走上成功的道路。&lt;/h6&gt;

&lt;p&gt;另一种是认为‘运气’是随机，就像守株待兔里的农夫，指不定什么时候兔子就自己撞到枪口上了就能有一顿饱肉吃，也像彩票的中奖概率，不知道什么时候就来那么一下，你就什么都有了。&lt;/p&gt;

&lt;p&gt;这两种我更偏向于稻盛和夫的理念，我也一直向他老人家在学习，用利他的心态做事，不屑努力，来感动全宇宙的量子(这个说法是从一本叫做《The Secret》中文叫秘密里看到的，它其实是一部鸡汤成功学的书，它告诉你其实全世界都是量子，你只要拿出你的诚意并且全力拼搏，量子就会被你吸引来帮助你)。&lt;/p&gt;

&lt;p&gt;我们不偏执任何一种，事实上两种都存在，既有自身的因素，也有随机的因素。&lt;/p&gt;

&lt;p&gt;随机的运气也并不是我们表面上看上去那么容易，它很多时候都需要毅力支撑。毅力二字是大多数人成功的秘诀，即使日子再苦再累再无趣，受压迫得再厉害，如果依然能够扛得住，运气就有可能降临到他们身上。&lt;/p&gt;

&lt;p&gt;为什么？因为风险是波动的，当震荡加剧，波幅增大，低谷更低，时间更长时，精神上和肉体上的压力就会增大，无法承受导致崩溃消亡也是正常的现象。大部分人无法承受这种非人的精神压力都选择了退缩，但只要扛过去，通常都是拨云见日，柳暗花明又一村，只是人是脆弱的，99%都选择了退缩，那最后1%中也有一半中途崩溃了，最后剩下的才是苦字熬出头的人，最终运气降临在他们身上，这种看似运气而实非运气的事也很常见。&lt;/p&gt;

&lt;h6 id=&quot;这个世界还是公平的我们无法把控的运气确实占了成功因素的很大一部分也只有这样才不是任何人想怎么样就怎么样的么王思聪有钱有人有才理论上说没有失败的理由实际上他开的公司创的业投资标的不照样该垮的还是垮不经历风雨怎能见彩虹没有人能随随便便成功&quot;&gt;这个世界还是公平的，我们无法把控的‘运气’确实占了成功因素的很大一部分，也只有这样才不是任何人想怎么样就怎么样的么，王思聪有钱有人有才，理论上说没有失败的理由，实际上，他开的公司，创的业，投资标的，不照样该垮的还是垮，”不经历风雨怎能见彩虹，没有人能随随便便成功“。&lt;/h6&gt;

&lt;p&gt;说实在的，我们可以避开赚钱二字不谈，人不一定要赚到足够多的钱才会幸福。&lt;/p&gt;

&lt;p&gt;“没有赚到钱的人生也能幸福”这句话很多人不理解。因为人们把钱看得太重，以至于认为成功就是有钱，有钱才能幸福，把三者划上了等号。&lt;/p&gt;

&lt;p&gt;这个偏执的观念会让人变得很痛苦，人常常会被欲望所控制，越想有钱，越痛苦，越不幸福，是一个恶性循环的开始。&lt;/p&gt;

&lt;h6 id=&quot;我们应该坦然一点才是我希望我们能想明白我们赚钱的目的是什么如果是养家糊口那是应该的如果是追求富足的生活我觉得该量力而行别最后钱没赚到人没了情也没了或者钱有了情没了身体没了都得不偿失如果是追求更高的精神境界我觉得应该有计划的有进也有退的以更加稳健的姿态走向目标&quot;&gt;我们应该坦然一点才是。​我希望我们能想明白，我们赚钱的目的是什么，如果是养家糊口，那是应该的。如果是追求富足的生活，我觉得该量力而行，别最后钱没赚到人没了情也没了，或者钱有了情没了身体没了，都得不偿失。如果是追求更高的精神境界，我觉得应该有计划的，有进也有退的，以更加稳健的姿态走向目标。&lt;/h6&gt;

&lt;p&gt;我在知乎的问答里看到这么一句话，虽然偏题了但我觉得挺值得深思，他说：”如果一个人到了40几岁，夫妻感情好，每个周还能有性生活，孩子学习成绩好，全家人身体强壮有力，这就是成功“，体会一下。&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>思路探讨(三十) 改变自己才能改变孩子</title>
   <link href="http://www.luzexi.com/2019/11/04/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A830"/>
   <updated>2019-11-04T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/11/04/思路探讨30</id>
   <content type="html">&lt;p&gt;这4年半的父亲生涯着实让我改变了很多，我从我的女儿身上学到了很多很多东西。&lt;/p&gt;

&lt;p&gt;我知道她是我的镜子，我不经意间的坏习惯都能在她的身上放大很多倍，每次责备她时我都会想到自己的问题，让我心生愧疚。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;我着实明白孩子是张白纸，大部分的行为方式都是从我们日常的行为中学到，她没有这么多的经历和生活经验，所以也无法判断好与坏，只是顺着我们行为方式照着模仿罢了。&lt;/p&gt;

&lt;h6 id=&quot;只是我们也很讨厌我们自己大部分时候我们自己知道的自己的缺陷和自卑而通常自己选择了原谅自己而大部分时候无法原谅孩子有同样的问题&quot;&gt;只是我们也很讨厌我们自己，大部分时候我们自己知道的自己的缺陷和自卑，而通常自己选择了原谅自己，而大部分时候无法原谅孩子有同样的问题。&lt;/h6&gt;

&lt;p&gt;这就像我们生活和工作中那样，总是拿自己做不到的事情来要求别人，我一直记得一句话：对别人是马克思主义，对自己却是自由主义。&lt;/p&gt;

&lt;p&gt;所以我想到了改变自己，我想让女儿知道，并且以自己的行为方式和思维方式让女儿知道，“我们应该怎样做才是正确的，并且做正确的事有很多很多的好处。”&lt;/p&gt;

&lt;p&gt;在正确的道路上行走着实不容易，就像我们以前在学校里上课学习那样，平时的上课学习着实枯燥乏味，如果没有小伙伴能与你一起共同努力共同学习共同生活，我们很难在读书学习这条路上直线的走下去，总是会把注意力跑偏向更加有趣的事物上去。&lt;/p&gt;

&lt;p&gt;因此我知道她要有个伴，这个伴至少应该是个精神上的伙伴，如果还能成为生活中的伙伴那是最好不过的了。(通常我的工作并不允许经常性的陪伴在她身边，但一有时间我就围绕着她)&lt;/p&gt;

&lt;p&gt;我们也像大部分父母那样严格要求女儿去纠正自己的行为习惯，不同的是我们还是觉得我们需要以洞察和理解人性的角度来对待我的女儿，只因为我深刻的认识到其实我们大部分时候，知道和明白她所要做的事情很难突破，很枯燥，很复杂，连我们自己都无法做到，却让孩子去完成。&lt;/p&gt;

&lt;h6 id=&quot;不过无论对女儿有多心疼多理解我还是没有放弃对孩子正确的行为习惯和思维方式的教育只是我想换种方式我希望这种方式更加彻底我也明白这种方式的艰难程度&quot;&gt;不过无论对女儿有多心疼，多理解，我还是没有放弃对孩子正确的行为习惯和思维方式的教育，只是我想换种方式，我希望这种方式更加彻底，我也明白这种方式的艰难程度。&lt;/h6&gt;

&lt;h6 id=&quot;抱着这种既要女儿纠正行为习惯要学习要突破也同时要理解女儿是我们自己缺点和自卑心理写照的心态我希望自己走上改变自我再影响她的路线&quot;&gt;抱着这种既要女儿纠正行为习惯，要学习，要突破，也同时要理解女儿是我们自己缺点和自卑心理写照的心态，我希望自己走上改变自我再影响她的路线。&lt;/h6&gt;

&lt;p&gt;于是，“改变自己，同时改变女儿，我优秀了女儿也会优秀”，成为了我的教育理念。&lt;/p&gt;

&lt;p&gt;这个教育理念是从我这些年对社会的理解和反思的所得。&lt;/p&gt;

&lt;p&gt;那么我应该怎么改变自己，改变成什么样才是对的。如果我的方向错了，或者偏了，改变的最终效果还是会有问题。&lt;/p&gt;

&lt;p&gt;于是我把这个问题的思考引向了，改变自己到什么样才是对的这个问题上。&lt;/p&gt;

&lt;h6 id=&quot;我首先发现的是安心的心理环境是促成的理智心理的重要因素&quot;&gt;我首先发现的是安心的心理环境是促成的理智心理的重要因素。&lt;/h6&gt;

&lt;p&gt;理智沉稳的心理状态对学习和生活都是非常有效的帮助。相反，焦躁、恐惧、不安的心态时常会导致效率低下，无法集中注意力，思考的角度也会变得狭窄和阴暗。&lt;/p&gt;

&lt;p&gt;于是我首先让自己时常保持理智，尽量减少情绪的波动，心平气和的与女儿对话，让她感觉到这个氛围是平和的。&lt;/p&gt;

&lt;p&gt;我希望创造一种环境，这种环境中，女儿能很好的思考，包括自己的问题，和别人的问题，环境对她只有促进作用而不是阻碍作用，因此我首先要保证我是理智的，平和的，否则她的思维习惯会跑偏。&lt;/p&gt;

&lt;h6 id=&quot;其次我发现最应该原谅的是别人而不是自己最应该去帮助别人而不是期待别人的帮助&quot;&gt;其次我发现最应该原谅的是别人而不是自己，最应该去帮助别人而不是期待别人的帮助。&lt;/h6&gt;

&lt;p&gt;我们大部分时候都觉得，自己没问题，全是别人的错。这其实是人性的弱点，我们最容易原谅的是自己，最容易责备的是别人，也最容易把希望寄托在别人身上，而自己什么都不做。&lt;/p&gt;

&lt;p&gt;原谅应该更多的体现在对待别人上，只有原谅了别人，事情才会有进展。教育女儿也是一样，我更多的选择理解和原谅女儿的缺点和坏习惯，理解并且明白在困难面前人都是软弱的。&lt;/p&gt;

&lt;p&gt;帮助则更应该体现在对待别人身上，只有通过帮助别人，事情才会有十足的推进。教育女儿也是一样，我更多的去辅助女儿改掉坏习惯，在对她面对的困难的理解之上，逐步纠正她的缺点与习惯，而不是逼迫她。&lt;/p&gt;

&lt;h6 id=&quot;我发现对精神世界改造的重要性&quot;&gt;我发现对精神世界改造的重要性&lt;/h6&gt;

&lt;p&gt;面对这个充满焦虑的时代，欲望与恐惧充斥着满世界，我们时常都会不由自主的随波逐流的被世界带着走，被金钱、名誉、权利、地位所吸引，被捆绑，被遏制。&lt;/p&gt;

&lt;p&gt;我反复思考着我们最终人生的意义，我想它肯定不是这些金钱、名誉、权利、地位这些东西，虽然是它们都是我们所渴望的、期盼的，也是避不开的实实在在的‘必需品’。&lt;/p&gt;

&lt;p&gt;但它们毕竟是靠机缘巧合出现的，也时常机缘巧合的消失(眼看他起高楼，眼看他宴宾客，眼看他楼坍塌的事数不甚数)，唯有精神力量才会一直存在下去。&lt;/p&gt;

&lt;h6 id=&quot;精神力量能让我们在面对幸运时更加稳健在面对低谷时更加冷静沉着也只有精神力量才能让我们更加持久幸福并且优秀的生活下去&quot;&gt;精神力量能让我们在面对幸运时更加稳健，在面对低谷时更加冷静沉着，也只有精神力量才能让我们更加持久、幸福、并且优秀的生活下去。&lt;/h6&gt;

&lt;p&gt;于是我希望改造自己成为精神力量的源泉，而不是精神力量的消耗者，锻炼自己拥有更加强大的毅力、耐力、抵抗力，并且将这颗精神力量的种子种在女儿身上，始终鼓励她勇敢的挑战自己，突破自己，让她变得坚强、勇敢、积极乐观。&lt;/p&gt;

&lt;h6 id=&quot;我发现人生中无论大小孩积累对我们的重要性&quot;&gt;我发现人生中无论大小孩，积累对我们的重要性&lt;/h6&gt;

&lt;p&gt;我发现社会是由‘积累’构成的，无论是财富，名誉，智慧，知识，经验，都是逐日积累而成，而不是一蹴而就的。&lt;/p&gt;

&lt;p&gt;就因为不是一蹴而就的，所以才形成了一种牢固的‘优势’，这种‘优势’不是一日所成(人们总是幻想着能拼几天几星期几个月就能换来‘优势’)，也并不会因一日所垮。‘劣势’或者说坏习惯，也同样不是一蹴而就的，也是通过日复一日，年复一年的积累而成，我们时常担心着潮流会瞬息间转向导致自己无法适应。&lt;/p&gt;

&lt;h6 id=&quot;于是我提出了每日一小步每年一大步的生活和学习理念我不但要自己每日践行这种自律的理念也希望女儿心中也能逐步体会到积累的重要性在我们生活中每天都积累一些小优势每年就能积累一个大优势年复年的坚持下去最终我和她都将得益于积累也得以于自律&quot;&gt;于是我提出了“每日一小步，每年一大步”的生活和学习理念，我不但要自己每日践行这种自律的理念，也希望女儿心中也能逐步体会到积累的重要性，在我们生活中，每天都积累一些小优势，每年就能积累一个大优势，年复年的坚持下去，最终我和她都将得益于积累，也得以于自律。&lt;/h6&gt;

&lt;h6 id=&quot;对女儿的教育始终源自于我对人性的洞察我们无法避免的承受着各种各样的缺点和自卑的情节有时我也原谅自己但始终未放弃前进我希望最终我的努力奋进和自我突破的意识能感动女儿在她心中种下一颗小小的种子这颗种子在她潜意识里生根发芽&quot;&gt;对女儿的教育始终源自于我对人性的洞察，我们无法避免的承受着各种各样的缺点和自卑的情节，有时我也原谅自己，但始终未放弃前进，我希望最终我的努力奋进和自我突破的意识，能感动女儿，在她心中种下一颗小小的种子，这颗种子在她潜意识里生根发芽。&lt;/h6&gt;

&lt;h6 id=&quot;女儿的性格是我性格的写照女儿的习惯是我生活中的点滴所形成的改变女儿最有效的方式就是改变自己这句话始终刻印在我心中警醒着我不断向更好的自己更好的家庭更好的未来前进&quot;&gt;女儿的性格是我性格的写照，女儿的习惯是我生活中的点滴所形成的，“改变女儿最有效的方式就是改变自己”这句话始终刻印在我心中，警醒着我不断向更好的自己，更好的家庭，更好的未来前进。&lt;/h6&gt;
</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第五章，资源的加载与释放</title>
   <link href="http://www.luzexi.com/2019/11/02/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-%E8%B5%84%E6%BA%90%E7%9A%84%E5%8A%A0%E8%BD%BD%E4%B8%8E%E9%87%8A%E6%94%BE"/>
   <updated>2019-11-02T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/11/02/Unity3D高级编程之进阶主程-资源的加载与释放</id>
   <content type="html">&lt;p&gt;我们在计算机上编程，始终逃不过计算机的体系范围，其实对于编程来说不过是进程，线程，CPU，CPU缓存，内存，硬盘，GPU，GPU显存，我们无非就是围绕着这个几个关键点在做文章。从比较宏观的角度上来看，计算机本身的内容就这么些，假如我们暂时不去细想具体的逻辑细节，我们可以从大体上明白我们需要做的工作与这些内容有多大的关系。&lt;/p&gt;

&lt;p&gt;我们制作的软件运行在进程上，进程是我们的载体，线程是进程的员工可以分担进程的负担，主线程是进程的一号员工，还有二号线程、三号线程等，这些线程有利于我们更大限度的利用多核CPU，这样就有可以让不只一个内核为我们工作。&lt;/p&gt;

&lt;p&gt;无论在PC还是手机设备上，CPU都负担着多个进程的计算请求，它们不断的以时间片切换的概念来使用CPU，其进程中大部分都是由运算、硬盘的读写即IO、内存读写、分配与回收内存消耗着CPU的算力。因此当我们对程序做性能优化时大部分都是围绕着，如何减少运算量、如何减少硬盘读写、如何加速内存读写、如何减少内存分配与回收、以及如何更多的利用多核CPU加速运算等，这几个关键点来做的。&lt;/p&gt;

&lt;p&gt;除了上述中普通的这几个概念，CPU缓存的存在加速了CPU执行效率，它让数据离CPU跟接近使CPU执行效率更高。只是CPU只认机器码由1和0组成的数据与指令，于是在机器码之上又有了汇编这种语言做助记符，使得我们能够不用去记住0和1的世界。只是这助记符还需要自己操作寄存器等直面硬件的事务让人们觉得太繁琐，对于现代越来越复杂和庞大的软件系统来说人类难以承受如此的复杂度，于是就有了更高级的语言+编译器来让编程变得更加简单，编译器它翻译了我们能更容易运用的各种语言包括C++、Java、C#等，其中C++被直接编译成机器语言，Java和C#则先翻译成中间语言再由虚拟机VM将中间语言翻译成的二进制码被CPU识别。&lt;/p&gt;

&lt;p&gt;这些高级语言能够让人类更加专注于编写复杂和庞大的软件系统，从而解放了我们直面2进制指令和重新编写高级语言结构的痛苦。Unity3D引擎从这层意义上来说也是做了同样的事情，它将大部分对OpenGL/DirectX等图形接口底层的调用都封装在了引擎中，只需要我们了解业务层面的事务即可快速构建项目，解放了我们需要学习枯燥复杂底层的时间，可以将更多的注意力放在对业务的探索上。只是我们在制作过程中始终绕不过去的是底层的工作原理与流程，如果我们想要编写更加优秀的程序，就得学习和理解这些底层原理，也只有这样才能明白应该如何改善我们自己编写的框架逻辑使得它们在计算机中运行的效率比较高。&lt;/p&gt;

&lt;p&gt;内存已经是除了CPU缓存外最快的数据存取地点了，所以要想更快的取得内容就要借助内存空间，但也需要适度使用内存。比如我们的移动设备中内存还不是那么廉价或者说容量还不足以可以肆无忌惮的任意使用，即使在PC机上内存已经足够大的情况下也要考虑其他软件进程的内存消耗，给PC机留出更多可用空间以支撑其他操作。&lt;/p&gt;

&lt;p&gt;硬盘在现代已经是很廉价了，硬盘占用的大小已经很少被大家所诟病，不过背后还需要考虑网络宽带和IO读写问题。虽然硬盘廉价但宽带并没有那么多，大部分磁盘文件都需要从网络上下载下来，这也使得宽带的占用量是紧张的，我们经常需要控制文件资源大小，约束项目对硬盘的占用量使得我们制作的产品能更快的被用户下载到。由于硬盘是读写数据最慢的部件，因此我们在程序中也应该注意尽量减少对磁盘的读写操作，特别是日志这块内容，特别容易疏忽，大部分IO都是日志造成的，我们应该尽量想办法避免大量或频繁的操作日志文件。&lt;/p&gt;

&lt;p&gt;GPU对CPU的优势是在处理运算上，例如在图形图像的计算上就得到了很好的体现，因此我们也常常将部分CPU运算转移到GPU去处理以此来分担CPU的负担。即使是现代显卡普及的情况下，GPU的好坏也参差不齐，我们仍然要尽最大的努力去学习和理解GPU的运作原理，以尽可能得发挥其最大优势。虽然移动设备架构中，GPU并没有附带显存，但显存的原理与系统内存是一样的，都是为了能让它让内核读取数据变得效率更高，更何况还有GPU缓存的存在，和CPU缓存都是异曲同工之妙，不同的是GPU缓存分的更细，它让每个内部单元都拥有自己的缓存，这也使得并行计算更高效。&lt;/p&gt;

&lt;h6 id=&quot;前面的章节说了这么多关于算法框架结构数学图形学等我希望能用更大的角度来观察总结我们所天天需要接触的编程工作每条语句每个结构每个编码都能知道我们现在所要围绕的是哪个节点是否能通过对节点的优化来让当前程序的执行更加高效让程序跑在设备上时更加流畅&quot;&gt;前面的章节说了这么多关于算法、框架、结构、数学、图形学等，我希望能用更大的角度来观察总结我们所天天需要接触的编程工作。每条语句，每个结构，每个编码都能知道我们现在所要围绕的是哪个节点，是否能通过对节点的优化来让当前程序的执行更加高效，让程序跑在设备上时更加流畅。&lt;/h6&gt;

&lt;h6 id=&quot;说了这些关于计算机本身的事其意图是想从宏观的角度看问题抛开架构系统逻辑框架结构等细节来看看我们所面对的工作到底是个怎样的世界从根本上看我所说这些并不是什么特别具体底层的东西但是也可以从另一个角度了解我们所面对的编程工作对我们未来的技术方向兴许会有些帮助&quot;&gt;说了这些关于计算机本身的事，其意图是想从宏观的角度看问题，抛开架构、系统逻辑、框架结构等细节来看看我们所面对的工作到底是个怎样的世界。从根本上看我所说这些并不是什么特别具体底层的东西，但是也可以从另一个角度了解我们所面对的编程工作，对我们未来的技术方向兴许会有些帮助。&lt;/h6&gt;

&lt;h3 id=&quot;前面讲了很多关于计算机本质的东西这节我们主要来讲一下关于资源的内容&quot;&gt;前面讲了很多关于计算机本质的东西，这节我们主要来讲一下关于资源的内容。&lt;/h3&gt;

&lt;h3 id=&quot;资源加载的多种方式&quot;&gt;资源加载的多种方式&lt;/h3&gt;

&lt;p&gt;资源的数据格式其实有很多种并不一定要依照引擎来，但如果自己另开辟一种格式来做为自定义资源格式确实耗时耗力，性价比太低。也有极少项目的那种资源保密性要求很高的，自己会去定制资源的格式，但既然使用了Unity3D商业引擎来提高开发效率，加快迭代速度，完全可以借助Unity3D自身的机制来完成加密工作。这里我们主要还是来说说以Unity3D自身资源格式为基础的加载方式。&lt;/p&gt;

&lt;p&gt;我们可以把资源加载分为阻塞式和非阻塞式。到底什么是阻塞式什么是非阻塞式呢？简单来说，阻塞是当前资源文件加载完了才能执行下一条语句，非阻塞则是开启另一个线程加载资源文件，而主线程则可以继续执行下面的程序，当加载完毕时再通知主线程。&lt;/p&gt;

&lt;h3 id=&quot;下面我们来介绍下在unity3d中阻塞式的加载方式&quot;&gt;下面我们来介绍下在Unity3D中阻塞式的加载方式。&lt;/h3&gt;

&lt;h6 id=&quot;1resourceload&quot;&gt;1.Resource.Load&lt;/h6&gt;

&lt;p&gt;Resource.Load是Unity3D中最传统最古老的资源加载方式，Unity3D以Resources这个名字的文件夹作为根目录来加载资源其下面的资源文件。&lt;/p&gt;

&lt;p&gt;当Unity3D的项目被Build构建时，Unity3D打包了Resources文件夹的所有资源文件成为1个或几个资源文件(将资源文件合并成了1个或几个资源包文件)放入包内。当我们在程序中调用Resource.Load时则从这几个资源文件中查找并从中提取数据作为资源放入内存。&lt;/p&gt;

&lt;p&gt;这个资源包文件会被Unity3D在打包时压缩，这使得包体的大小会适度的减少，压缩的另一面是解压，因此在当我们通过调用Resource.Load加载资源时也增加了解压的算力损耗。这也是很多项目不乐意使用Resources的缘由，解压消耗带给他们不必要的开销，CPU算力资源比硬盘资源珍贵的多。&lt;/p&gt;

&lt;h6 id=&quot;2file-read--assetbundlecreatefrommemory--assetbundleload&quot;&gt;2.File read + AssetBundle.CreateFromMemory + AssetBundle.Load&lt;/h6&gt;

&lt;p&gt;我们也可以先通过文件操作加载资源文件，再通过AssetBundle.CreateFromMemory的方式把byte数据转换成AssetBundle格式，再通过AssetBundle.Load从AssetBundle中加载某个资源。&lt;/p&gt;

&lt;p&gt;这种方式虽然费时费力，几乎消耗了2倍的内存以及1.3倍左右的算力，但是这种方式可以让我们加入些许自定义功能。比如能在加载AssetBundle前做加解密操作，由于加载AssetBundle前自主加载了文件，使得文件数据在变为Assetbundle实例前可以自主的把控，我们可以先用文件操作获得的数据解密，再转换成AssetBundle实例，最后交给资源控制程序处理。只是这种获得加解密AssetBundle的能力，是需要付出代价的，代价就是内存和GC(内存的分配与销毁)。&lt;/p&gt;

&lt;p&gt;由于用文件操作时完全读入了整个资源文件的数据，导致当前还不需要的资源也一并读入了内存，增大了内存消耗，另外转换成AssetBundle后的byte数据也不再有用处，只能等待GC处理掉，这大大增加了内存分配和销毁的CPU负荷。&lt;/p&gt;

&lt;h6 id=&quot;3assetbundlecreatefromfile--assetbundleload&quot;&gt;3.AssetBundle.CreateFromFile + AssetBundle.Load&lt;/h6&gt;

&lt;p&gt;我们还可以使用通过直接加载文件变成AssetBundle的方式，再通过AssetBundle.Load接口来获得资源。这也是项目中常用的方式，它即没有压缩与解压，也可以不用一下子将所有内容加载到内存，能够做到按需加载。&lt;/p&gt;

&lt;p&gt;这种加载方式最大的好处是能够按需分配内存。AssetBundle.CreateFromFile接口并不会把资源文件整个加载进内存中，而是先加载文件中的数据头，通过数据头中的数据去识别各个资源在文件中的偏移位置。当我们调用AssetBundle.Load时，先从数据头中的数据查找对数据应偏移量，根据数据头中对应资源偏移量的记录，找到对应的资源位置，从而将数据加载到内存，因此我们可以说它是按需加载，更合理的利用了内存节省了CPU消耗。&lt;/p&gt;

&lt;h3 id=&quot;除了阻塞式的加载在unity3d中我们还有非阻塞式的加载方式&quot;&gt;除了阻塞式的加载，在Unity3D中我们还有非阻塞式的加载方式：&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;	1.AssetBundle.CreateFromFile + AssetBundle.LoadAsync
	2.WWW + AssetBundle.Load
	3.WWW + AssetBundle.LoadAsync
	4.File Read all + AssetBundle.CreateFromMemory + AssetBundle.Load
	5.File Read all + AssetBundle.CreateFromMemory + AssetBundle.LoadAsync
	6.File Read async + AssetBundle.CreateFromMemory + AssetBundle.Load
	7.File Read async + AssetBundle.CreateFromMemory + AssetBundle.Load
	8.File Read async + AssetBundle.CreateFromMemory + AssetBundle.LoadAsync
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这几种方式分别是由文件读取和AssetBundle异步加载的接口组合而成。这8种组合中前2种为主流的异步加载方式，其中第1种用的比较多，因为大多数资源文件都会在游戏开始前进行比对和下载，所以没必要使用WWW的形式从本地读取或从网络下载。&lt;/p&gt;

&lt;p&gt;我们在实际项目中常常会困扰是否要使用非阻塞加载的问题。常有人说阻塞式加载这么好用，为什么还要用非阻塞式。首先我们不要为了异步而异步，有人会觉得异步更高级，如果只是为了异步而做异步并没有意义。大部分情况下我们在使用阻塞式接口加载资源时，都会遇到一个问题，在某一帧加载的资源很多，加载完毕后需要实例化的资源也很多，从而导致画面在这一帧耗时特别长，画面卡顿现象特别严重，这种情况用运营同学们的话说“对用户来说不友好”。通常我们为了能更好更平滑的过度场景，我们需要把要加载和实例化的时间跨度拉长，这样在每帧时间中消耗的CPU被分配的均匀些，也就不会集中在某一帧处理所有资源问题，虽然增加了些许等待时间，却能平滑过渡到最终我们需要的画面。&lt;/p&gt;

&lt;h6 id=&quot;具体怎么做呢其实并不复杂的可以先获取所有需要加载的资源放入队列中每次加载限制n个n可以根据实际情况调整如果已经加载过的就直接通知逻辑程序实例化不曾被加载的则调用加载程序并将调用后的加载信息assetbundlerequest放入加载中队列不开协程而是用update帧更新去判断加载中队列中是否有完成的每加载完毕一个资源先从加载中队列里移除再通知逻辑程序再进行实例化在实例化期间我们也要注意为每帧分配数量合适的实例化如果实例化太多也容易造成集中消耗cpu算力的现象导致卡顿就这样直到队列中的请求加载完毕为止继续下一个n个加载请求当然这里也需要做些判断例如已经在加载队列里的资源不重复加载等一些避免重复加载的判断&quot;&gt;具体怎么做呢，其实并不复杂的，可以先获取所有需要加载的资源，放入队列中，每次加载限制N个(N可以根据实际情况调整)，如果已经加载过的就直接通知逻辑程序实例化，不曾被加载的则调用加载程序并将调用后的加载信息(AssetBundleRequest)放入‘加载中’队列，不开协程而是用Update帧更新去判断‘加载中’队列中是否有完成的，每加载完毕一个资源先从‘加载中’队列里移除，再通知逻辑程序再进行实例化，在实例化期间我们也要注意为每帧分配数量合适的实例化，如果实例化太多也容易造成集中消耗CPU算力的现象导致卡顿。就这样直到队列中的请求加载完毕为止，继续下一个N个加载请求。当然这里也需要做些判断，例如已经在加载队列里的资源不重复加载等一些避免重复加载的判断。&lt;/h6&gt;

&lt;h3 id=&quot;assetbundle的引用计数方式卸载&quot;&gt;AssetBundle的引用计数方式卸载&lt;/h3&gt;

&lt;p&gt;Assetbundle在加载后我们需要寻求释放，只有加载没有释放内存只会不断攀升。该怎么释放就成了问题，因为资源使用的地方太多，太庞杂，所以为了能更好的知道什么时候该释放资源，我们需要制定一个规则，在遵守这个规则的前提下，我们就能知道什么时候资源没有被再使用了，有多少个地方仍然在使用。&lt;/p&gt;

&lt;p&gt;引用计数就是判断这种释放依据很好的技巧，具体方式为如下：&lt;/p&gt;

&lt;h6 id=&quot;我们先对assetbundle包装一个计数器是个整数当需要某个assetbundle时先加载所有依赖的assetbundle每加载一个assetbundle就为该assetbundle的引用计数加1&quot;&gt;我们先对AssetBundle包装一个计数器(是个整数)，当需要某个AssetBundle时先加载所有依赖的AssetBundle，每加载一个AssetBundle就为该AssetBundle的引用计数加1。&lt;/h6&gt;

&lt;p&gt;我们在调用加载AssetBundle时其UnityEngine.Object则通常会通过Instantiate进行实例化，每次实例化时对该AssetBundle引用计数都必须加1处理。用UnityEngine.Object实例化时做引用计数加1的手法，又消耗了些许我们的注意力而且容易遗漏，所以我们通常选择封装接口，不让UnityEngine.Object暴露在外面，引用计算的操作只在封装的接口中进行，这样就节省了人额外的注意力，少一点主意力的消耗，就少一些遗漏。&lt;/p&gt;

&lt;p&gt;如果是Texture贴图这种不需要进行实例化的资源则最好不要被再次被引用，因为被再次引用会导致引用计数的错乱，我们可以选择每次当需要Texture时通过查看AssetBundle是否加载，有则直接取，没有则加载后再取，每次取资源时都对相应的AssetBundle计数加1。&lt;/p&gt;

&lt;h6 id=&quot;当destroy销毁实例或者不需要用资源时则统一调用某个自定义的unload假设这个接口名字是自定义类assetbundlemrgunload接口并附上加载时的关键字为了能更快的找到assetbundle实例从而将对应的assetbundle的引用计数减1卸载资源时会减少引用计数当该assetbundle引用计数为0则认为可以进行assetbundle卸载此时可以立即卸载&quot;&gt;当Destroy销毁实例或者不需要用资源时，则统一调用某个自定义的Unload(假设这个接口名字是自定义类AssetBundleMrg.Unload)接口并附上加载时的关键字(为了能更快的找到AssetBundle实例)，从而将对应的AssetBundle的引用计数减1。卸载资源时会减少引用计数，当该AssetBundle引用计数为0，则认为可以进行AssetBundle卸载，此时可以立即卸载。&lt;/h6&gt;

&lt;p&gt;不过问题又来了，及时的卸载也会有问题，因为每次都卸载后又会有需要该资源的时候，这样我们又会需要再次加载Assetbundle，这样就重复消耗了IO和许多CPU算力，为了避免这种情况的频繁发生，我们可以通过增加空置倒计时时间来给卸载AssetBundle一个预留时间。当需要卸载时，让AssetBundle进入倒计时，比如5秒，5秒内仍然没有任何程序使用这个资源则立即进行卸载，如果5秒内又有程序加载该AssetBundle资源则继续使用引用计数来判断是否需要进入卸载倒计时。&lt;/p&gt;

&lt;p&gt;不过还是会有个小问题，如果大量资源在同一时间卸载，就会造成大量资源同一时间进入倒计时，倒计时完毕同时进行卸载，也会带来1帧消耗过大的问题，毕竟资源的卸载时内存的消耗，大量的内存在同一时间销毁会带来大量的CPU消耗。为了避免大量资源同一时间卸载，我们可以对倒计时进行随机，在2-5秒的时间内随机一个值，让卸载分散在这个时间段内，让卸载的消耗分散在不同时间点上让CPU消耗更加平滑。&lt;/p&gt;

&lt;h3 id=&quot;assetbundle的打包与颗粒度大小&quot;&gt;AssetBundle的打包与颗粒度大小&lt;/h3&gt;

&lt;p&gt;Unity3D对AssetBundle的封装做的很好，当我们在打包AssetBundle时Unity3D会自动去计算AssetBundle与AssetBundle之间的依赖关系，所以我们能很轻松的将资源打的很细(贴图，网格，Shader，Prefab，每个资源可以独立存在于文件)。&lt;/p&gt;

&lt;p&gt;这使得我们能很轻松得让一个AssetBundle文件只装一个资源并且控制起来也很顺手，只要在加载时读取存有依赖关系的AssetBundle(AssetBundleManifest实例数据)就能得到AssetBundle之间的依赖关系数据，根据取得的依赖关系数据我们就能轻松的加载相关的AssetBundle。&lt;/p&gt;

&lt;p&gt;在Unity3D中既然AssetBundle颗粒度可以很容易的缩放，那么我们就需要考虑颗粒度的大小到底对项目产生多大的影响。我们说说左右两种极端状态下的表现。&lt;/p&gt;

&lt;h6 id=&quot;一种为颗粒度极粗状态所有资源集合起来只打成一个assetbundle包所有逻辑程序要的资源都从这个仅有的assetbundle里取引用计数在这里已经完全没有了用处由于只有一个assetbundle已经完全没有卸载的可能了这导致了内存只会逐步增大而绝不会因为不再需要某资源而卸载assetbundle当前assetbundle的卸载机制中没有只销毁某部分资源的功能&quot;&gt;一种为颗粒度极粗状态，所有资源集合起来只打成一个AssetBundle包，所有逻辑程序要的资源都从这个仅有的AssetBundle里取。引用计数，在这里已经完全没有了用处，由于只有一个AssetBundle已经完全没有卸载的可能了。这导致了内存只会逐步增大，而绝不会因为不再需要某资源而卸载AssetBundle(当前AssetBundle的卸载机制中没有只销毁某部分资源的功能)。&lt;/h6&gt;

&lt;p&gt;我们来看看整个过程，一个很大的资源文件从网络上下载下来，解压后成为仅有的一个AssetBundle文件，然后我们读取它并从中获得资源。从这个过程来看只有一个AssetBundle的极限状态下，更新资源非常方便，文件操作的次数极低，读取AssetBundle文件信息完全没有障碍，解压的IO效率也非常高，甚至解压时不需要创建很多文件从IO上会相对比较快些，同时由于只有一个文件内容所以打包的压缩率也是最大化的。我们看到除了不能不能更好的管理内存，热更新资源时会更费流量外，其他方面还是比较方便的。&lt;/p&gt;

&lt;h6 id=&quot;另一种为颗粒度极细状态所有贴图网格动画shaderprefab都各自打自己的一份assetbundle一份assetbundle只带一个资源为了能更有效的控制内存assetbundle之间的依赖关系和引用计数在这里的用处会非常大通过引用计数和依赖关系我们能很有效的控制逻辑系统中需要的资源和内存中的资源是一致的&quot;&gt;另一种为颗粒度极细状态，所有贴图、网格、动画、Shader、Prefab都各自打自己的一份AssetBundle(一份AssetBundle只带一个资源)。为了能更有效的控制内存，AssetBundle之间的依赖关系和引用计数在这里的用处会非常大。通过引用计数和依赖关系，我们能很有效的控制逻辑系统中需要的资源和内存中的资源是一致的。&lt;/h6&gt;

&lt;p&gt;我们来看看整个过程，根据资源文件列表从网上下载下来所有AssetBundle资源文件，对每个压缩过的资源文件进行解压，当需要某个资源时从AssetBundle读取资源并且读取前先根据依赖关系读取需要的AssetBundle资源，并且对所有加载过的AssetBundle引用计数加1。当我们调用卸载接口时，对当前卸载的AssetBundle引用计数减1，并且对存有需求上依赖关系的其他AssetBundle也相应减1（由于当前资源卸载后对其他依赖资源不再引用），如果引用计数为0则启动卸载程序。&lt;/p&gt;

&lt;p&gt;我们从这个过程看来，一个极限细分颗粒度状态下的AssetBundle机制，文件操作数量会很大，IO操作的时间会因为文件增多的增大许多，导致下载时间拉长，下载完毕后解压的总时间也同样会更长，打包时由于每个文件单独打包压缩因此压缩比率会降低，压缩时间更长。当然这些‘坏处’都是次要的，最重要的是我们很好的控制了内存，当我们需要热更新资源时会更小巧和方便。&lt;/p&gt;

&lt;h6 id=&quot;大多数项目都选择中间状态颗粒度会选择使用prefab和material两种形式打包很少会细化到贴图和网格除了ui上的icon但也不少会将动画另外拎出来打包因为这样就可以让动画按需加载了或者以模块化的形式打包将某一个模块下的资源统一打包成一个资源文件等各个项目都有所不同太细化的打包方式也会让路径查找变得困难但也不用太担心因为可以我们可以想出更好地解决方案比如自动生成代码将资源以枚举方式生成数据结构让查找变得更高效&quot;&gt;大多数项目都选择中间状态，颗粒度会选择使用prefab和material两种形式打包，很少会细化到贴图和网格(除了UI上的Icon)，但也不少会将动画另外拎出来打包，因为这样就可以让动画按需加载了，或者以模块化的形式打包将某一个模块下的资源统一打包成一个资源文件，等各个项目都有所不同。太细化的打包方式也会让路径查找变得困难，但也不用太担心，因为可以我们可以想出更好地解决方案，比如自动生成代码，将资源以枚举方式生成数据结构，让查找变得更高效。&lt;/h6&gt;

&lt;p&gt;上述分析了两种极限状态下的利弊，我们可以根据自己项目的需求来定制AssetBundle打包机制。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第七章，渲染管线与图形学(三) - 渲染原理与知识2</title>
   <link href="http://www.luzexi.com/2019/10/26/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-%E6%B8%B2%E6%9F%93%E7%AE%A1%E7%BA%BF%E4%B8%8E%E5%9B%BE%E5%BD%A2%E5%AD%A67"/>
   <updated>2019-10-26T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/10/26/Unity3D高级编程之进阶主程-渲染管线与图形学7</id>
   <content type="html">&lt;p&gt;这节我们将继续补充前面渲染管线中没有讲到的渲染知识和原理。&lt;/p&gt;

&lt;h3 id=&quot;纹理贴图的-filter-滤波方式&quot;&gt;纹理贴图的 Filter 滤波方式&lt;/h3&gt;

&lt;p&gt;纹理贴图的Filter滤波其实在图形引擎中被用到的地方有很多，我们在做项目时却很少察觉到，它的重要性不容忽视，我们来讲讲它的来龙去脉。&lt;/p&gt;

&lt;p&gt;每张纹理贴图可能都是大小不一的贴图，渲染时它们被映射到网格三角形的表面上，转换到屏幕坐标系之后，纹理上的独立像素(纹素)几乎不可能直接和屏幕上的最终画面像素直接对应起来。这是因为物体在屏幕上的显示的大小会随着物体离摄像机远近而改变在屏幕上的占有率，当物体非常靠近摄像机时屏幕上的一个像素有可能对应纹理贴图上一个纹素中一小部分（因为物体覆盖了摄像机视口的大部分面积），而当物体离摄像机很远时，屏幕上一个像素包含了纹理贴图上很多个纹素（因为物体只覆盖了相机视口的很小一部分）。因此贴图中的一个纹素与屏幕上的一个像素通常都是无法有一比一的对应关系。&lt;/p&gt;

&lt;p&gt;无论哪种情况我们都无法精确的知道应该对这些纹素做怎样的插值。OpenGL就为我们提供了多种Filter 滤波方式，不同的滤波方式在速度和画质上会有所不同，这也是我们需要做出了的权衡的事。&lt;/p&gt;

&lt;p&gt;滤波方式分三种，一种是最近采样即Nearest，一种是线性采样即Linear，另一种各向异性采样。在Unity3D中Point类型的采样就是最近采样(Nearest Point Sampling)，线性采样又分为双线性采样(Bilinear)和三线性采样(Trilinear)。&lt;/p&gt;

&lt;p&gt;其中Nearest最近采样，当纹素与像素大小不一致时，它会采样取最接近的纹素。这种方法取的只是寻找了位置最接近的纹素所以并不能保证连续性，即使使用了Mipmap技术，像素点与纹素也仍然没有得到很好的匹配，因此这种方法使得纹理在屏幕上显得有些尖锐。&lt;/p&gt;

&lt;p&gt;线性滤波技术的含义是：使用坐标值从一组离散的采样信号中选择相邻的采样点，然后将信号曲线拟合成线性近似的形式。在图像采样中，OpenGL会将用户传递的纹理坐标视为浮点数值，然后找到两个离它最近的采样点。坐标到这两个采样点的距离也就是两个采样点参与计算的权重，从而得到加权平均后的最终结果。双线性滤波采取的是离纹素最近的4个纹素，这4个文素在线性计算上的权重值为纹素与中心点的距离，把所有采样得到的纹素进行加权平均后得到最终的像素颜色。&lt;/p&gt;

&lt;p&gt;我们来看看Nearest最近采样与双线性采样的不同之处，假设源图像长度m像素，宽度为n像素，即m x n像素大小，目标图像为a x b像素。那么两幅图像的边长比分别为：m/a和n/b。目标图像的第（i,j）个像素点（i行j列）可以通过边长比对应到源图像。其对应坐标关系为（i&lt;em&gt;m/a,j&lt;/em&gt;n/b）。显然这个对应坐标一般来说不是整数，非整数的坐标是无法在图像中无法取得正确的像素。Nearest最近采样直接取小数最接近的整数(小数部分四舍五入取整)作为纹理对应的坐标点，显然这样做有些突兀，双线性采样则通过寻找距离坐标附近的4个像素点，再通过这4个像素点做加权平均来计算该点的像素坐标。双线性滤波映射点计算方法:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	srcX=dstX* (srcWidth/dstWidth)+0.5*(srcWidth/dstWidth-1)

	srcY=dstY* (srcWidth/dstWidth)+0.5*(srcWidth/dstWidth-1)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/7/g1.png&quot; alt=&quot;双线性过滤&quot; /&gt;&lt;/p&gt;

&lt;p&gt;双线性滤波在像素之间的过渡显然比最近采样方式更加平滑。不过双线性采样只选取一个MipMap Level，它选取纹素和像素之间大小最接近的那一层MipMap进行采样，这导致当像素大小匹配的纹素大小在两层Mipmap Level之间时，双线性过滤在有些情况效果就不太好，这时三线性过滤则能更好的做到平滑的效果。&lt;/p&gt;

&lt;p&gt;三线性过滤在双线性过滤基础上对像素大小与纹素大小最接近的上下两层Mipmap Level分别再进行一次双线性过滤，然后再对两层Mipmap纹理上得到的像素结果再进行插值计算最终得到合理的纹素。&lt;/p&gt;

&lt;p&gt;除了上面几种过滤外还有各向异性过滤(Anisotropic Filtering)。什么是各向异性和同性呢：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	各向同性，当需要贴图的三维表面平行于屏幕就是各向同性。

	各向异性，当要贴图的三维表面与屏幕有一定角度的倾斜时。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;各向异性过滤，除了会把Mipmap因素考虑进去外，还会把纹理与屏幕空间的角度这个因素考虑进去。它会考滤一个像素对应到纹理空间中在u和v方向上与u和v的比例关系，如果u:v不是1:1时，将会按比例在各方向上采样不同数量的点来计算最终的结果。各向异性采样的多少取决于Anisotropic Filtering的X值，所以在Unity3D的纹理图片设置上有一个Aniso Level的设置选项，用来设置Anisotropic Filtering的级别。&lt;/p&gt;

&lt;p&gt;这里我们介绍了纹理的滤波方式，它们主要是采样和计算方式不同，在计算中融入了更多的因素。采样方式从最近采样、双线性滤波、三线性滤波，再到各向异性滤波，采样次数也在逐级提高。最近采样的采样次数为1次，双线性滤波采样4次，三线性滤波采样8次，各向异性采样随着等级不同各有不同，效果也是逐级提高，随着采样次数的提高需要消耗的GPU也会逐级提高(这些采样与计算都是在GPU中完成的)，因此我们在设置图片滤波时需要考虑这里画质与性能开销。&lt;/p&gt;

&lt;h3 id=&quot;光照阴影是如何生成的&quot;&gt;光照阴影是如何生成的&lt;/h3&gt;

&lt;p&gt;前面讲了很多关于Mipmap和纹理采样的知识，对Mipmap和纹理采样的理解对底层画面渲染的理解有很大的帮助。这节我们来讲讲实时光照阴影的生成，它也同样具有重要意义，3D渲染阴影模拟了实际生活中的光照知识，让原本虚拟的画面更加拟真现实生活。&lt;/p&gt;

&lt;p&gt;为了能让画面中场景和人物看起来更加贴近真实被人意识所接受，光影效果是不可或缺的。我们经常能在画面中看到阴影跟随着物体摆动而变动，并且物体被光照遮挡的阴影投射在其他物体上，这样的效果十分动人，那么阴影是如何产生的呢？我们来细致的解析一下，通过解析我们能够更加深刻的理解阴影的生成原理，还可以通过对阴影原理的理解来有针对性的优化阴影对性能的消耗。&lt;/p&gt;

&lt;p&gt;我们可以先想想真实生活中阴影的产生的过程，当一个光源发射一条光线遇到一个不透明物体时，这条光想不能再继续照亮它背后的物体，它周围的地面和物体都被照亮了只有这个背后的物体没有被光照到使得这块区域的变成了阴影，因为光线无法到达这块区域。&lt;/p&gt;

&lt;p&gt;在计算机的实时渲染中我们无法用表达出每条光照的射线，这样的算力计算机承受不了，那么我们是如何表达阴影的投射的呢？&lt;/p&gt;

&lt;p&gt;其实可以很简单，假设我们将摄像机放在光源的位置上，摄像机的方向与光源照射的方向一致，相机中那些看不到的区域就是阴影产生的地方。只是我们不可能真的将摄像机放在那里，但可以用这种方式单独渲染一次摄像机在该位置的图像。只是我们需要的不是图像，我们需要的是阴影，刚好物体从该位置渲染出来的片元的深度值提供了我们需要的参照数据，在光源位置上摄像机渲染的所有片元的深度值都被写入深度缓存中，我们可以用这个深度缓存做阴影计算，深度值越大的片元被遮挡的可能性越大，深度值最小的片元不会被遮挡。&lt;/p&gt;

&lt;p&gt;这就是阴影映射纹理(Shadow Map)技术，在渲染中第一个渲染管线(pass)负责在光源点位置计算得到深度值，输出像素到阴影映射纹理(Shadow Map)。我们实质上得到是一张深度图，它记录了从该光源的位置出发，能看到的场景中距离它最近的表面位置的深度信息。&lt;/p&gt;

&lt;p&gt;那么阴影图有了，应该怎么投射呢？&lt;/p&gt;

&lt;p&gt;主动计算投射到其他物体产生阴影是比较难的，但反过来，根据阴影图主动计算当前渲染物体上的片元是否被阴影是相对比较容易。我们会看到Unity3D在渲染物体上看到有生成阴影和接受阴影两个选项，即Cast Shadows 和 Receive Shadows。&lt;/p&gt;

&lt;p&gt;传统的接受阴影的方式，是将当前顶点的位置变换到光源点的空间下得到它在光源空间中的位置，再根据xy轴分量对阴影映射纹理(Shadow Map)进行采样，从而得到阴影映射纹理中该位置的深度值，如果这个深度值小于该顶点的深度值即z轴分量，那么说明该点位于阴影中，于是在片元颜色输出上加深阴影颜色，反之则没有被阴影遮盖。&lt;/p&gt;

&lt;p&gt;另一种方式为屏幕空间的阴影投影技术(Screenspace Shadow Map)，它需要显卡支持MRT(Multiple Render Targets)，有些移动平台并不支持这种特性。屏幕空间阴影投射技术(Screenspace Shadow Map)对从光源出发的深度图与摄像机产生的深度图做比较，如果摄像机的深度图中记录的点的表面深度大于转化到光源出发生成的深度图的点的深度，那么就说明表面虽然是可见的但却处于该光源的阴影中。&lt;/p&gt;

&lt;p&gt;通过这样的方式，屏幕空间阴影投射技术(Screenspace Shadow Map)得到了当前摄像机屏幕空间中的阴影区域，即得到了当前摄像机屏幕的阴影图。因为已经得到了当前摄像机整个屏幕的阴影图，在当前像素位置对阴影图进行采样便能知道该像素是否在阴影下，即只要根据像素坐标采样阴影图中的像素即可得到阴影系数，不需要再将坐标转换到光源空间。相对于传统的阴影渲染来说，屏幕空间阴影映射技术提高了更多的GPU性能效率。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;Shader &quot;Example ShadowCaster&quot;
{
     SubShader
     {
        Tags { &quot;Queue&quot; = &quot;Geometry&quot; }
        //渲染阴影图
		Pass 
		{
			Name &quot;ShadowCaster&quot;
			Tags { &quot;LightMode&quot; = &quot;ShadowCaster&quot; }
			
			ZWrite On ZTest LEqual Cull Off

			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma target 2.0
			#pragma multi_compile_shadowcaster
			#include &quot;UnityCG.cginc&quot;

			struct v2f { 
				V2F_SHADOW_CASTER;
				UNITY_VERTEX_OUTPUT_STEREO
			};

			v2f vert( appdata_base v )
			{
				v2f o;
				UNITY_SETUP_INSTANCE_ID(v);
				UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
				TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
				return o;
			}

			float4 frag( v2f i ) : SV_Target
			{
				SHADOW_CASTER_FRAGMENT(i)
			}
			ENDCG
		}
    }

    // Fallback &quot;Legacy Shaders/VertexLit&quot;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在Unity3D中使用 LightMode 为 ShadowCaster 的Pass标记为阴影生成管线，它为渲染产生阴影图。当Unity3D在渲染时会首先在当前Shader中找到LightMode为ShadowCaster的Pass，如果没有则会在Fallback指定的Shader中继续寻找，如果没有则无法产生阴影，因为无论传统的阴影投射还是屏幕空间阴影投射都需要第一步先产生阴影纹理图(Shadow Map)。当找到LightMode为ShadowCaster的Pass后，Unity3D会使用该Pass来绘制该物体的阴影映射纹理(Shadow Map)。&lt;/p&gt;

&lt;p&gt;有了阴影图就可以将物体的阴影部分绘制出来了:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;Shader &quot;Example ShadowReceive&quot;
{
     SubShader
     {
        //从阴影图中绘制阴影
		Pass
        {
        	struct a2v {
                float4 vertex : POSITION;
            };

            struct v2f {
                float4 pos : SV_POSITION;
                SHADOW_COORDS(1) //阴影uv变量
            };
        	v2f vert(a2v v)
        	{
        		v2f o;
        		o.pos = UnityObjectToClipPos(v.vertex); //转换顶点空间

        		TRANSFER_SHADOW(o); //计算顶点坐标再阴影纹理中的位置
        		return o;
        	}

        	fixed4 frag(v2f i) : SV_Target
        	{
        		fixed4 _color = fixed4(1,1,1,1);

        		fixed shadow = SHADOW_ATTENUATION(i); //从阴影纹理图中计算阴影系数

        		_color.rgb *= shadow; //颜色与阴影系数相乘，系数从0-1，完全在阴影中为0，完全不在阴影中为1

        		return _color;
        	}
        }
    }

    Fallback &quot;Example ShadowCaster&quot;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述Shader代码我们将其他干扰因素去除，只剩下阴影绘制，至于阴影图的绘制我们使用了Fallback策略，引用了Fallback中的ShadowCaster管线。这个简易的Shader中 SHADOW_COORDS、TRANSFER_SHADOW、SHADOW_ATTENUATION这三个宏，Unity3D已经为我们准备好了，在AutoLight.cginc中。这三个宏都有针对不同情况不同设备的变种，大致分为，实时阴影，离线烘培阴影，传统阴影，屏幕空间阴影这几个变种。我们可以理解为，SHADOW_COORDS是定义阴影图uv的变量，TRANSFER_SHADOW则计算顶点坐标再阴影纹理中的位置，SHADOW_ATTENUATION是从阴影纹理图中获得深度值从而计算阴影的明暗系数，完全被遮挡的情况下系数为0，此时颜色应该为黑色，相反完全没有被遮挡的情况下为1，此时不影响像素颜色显示，当然还有中间状态，阴影的绘制也很有讲究，有软阴影和硬阴影的计算方式区分，这里不再深入。&lt;/p&gt;

&lt;h3 id=&quot;lightmap烘培原理&quot;&gt;Lightmap烘培原理&lt;/h3&gt;

&lt;p&gt;随着硬件技术的发展，人们对场景的画质效果越来越高，实时光照早已经满足不了人们对画质的需求，想要更加细腻真实光照效果，只能通过离线的烘培技术才能达到理想画质的效果。&lt;/p&gt;

&lt;p&gt;全局光照，简称GI(Global Illumination)，是在真实的大自然中，光从太阳照射到物体和地面再经过无数次的反射和折射，使得地面的任何物体和地面反应出来的光亮都叠加着直接照射的光和许许多多物体反射过来的间接光(反射光)，使得我们眼睛里看到画面是光亮又丰富的。这种无数次反射和折射形成的高质量画面，才符合人们意识当中的真正世界的模样。但是即使今天硬件技术发展的如此迅速，也无法做到实时的进行全局光照(Realtime Global Illumination)，实时的计算量太大CPU和GPU无法承受。&lt;/p&gt;

&lt;p&gt;离线全局光照就担负起了这个丰富画面光照效果的重任，它不需要实时的CPU和GPU算力，只要一张或几张光照图(Lightmap)就能将全局光照的效果复原到物体上，不过也仅限于场景静态物体的光照烘培。&lt;/p&gt;

&lt;p&gt;其实烘培这趟水很深，如果要具体深入到工程上的实现，涉及到的算法和图形学知识比较多，这里并不打算深究，而是讲讲我们能相对容易能获得的关于Lightmap的原理和知识。根据这个原理知识，我们在项目的制作和优化中能起到很好的作用。&lt;/p&gt;

&lt;p&gt;什么是烘焙？个人认为从英文‘Bake’翻译过来有点偏差，导致很多工具按钮用‘Bake’表示时，很多人都同样把它理解成了烘培，其实‘Bake’更应该理解为‘制作’或‘制造’。场景烘培简单来说 就是把物体光照的明暗信息保存到纹理上, 实时绘制时不再需要进行光照计算，因为结果就在光照纹理(Lightmap)中，只需从光照纹理(Lightmap)中采样便能得到光照计算的结果。&lt;/p&gt;

&lt;p&gt;我们在渲染3D模型时用到的基本元素有顶点、UV、纹理贴图等(这里不多展开)，顶点上的UV数据在形成片元后就成了顶点间的插值后的UV数据，我们通常使用这个UV坐标去纹理贴图上采样取得文素作为像素，再将像素填充到帧缓存中最后显示到画面上。光照纹理(Lightmap)的显示也是同样道理，用UV坐标来取得光照纹理(Lightmap)上的文素作为像素，将这个像素叠加到片元颜色上输出给缓存。&lt;/p&gt;

&lt;p&gt;这其中的UV有一点讲究，我们在制作模型时顶点数据中的UV数据可以不只一个，其中UV0通常情况下是为了映射贴图纹理而存在的，它在模型的蒙皮制作过程中就在模型数据中记录下来了，而UV1也就是我们程序中的uv2或俗称的2u，通常都是为Lightmap所准备的。除了UV0，UV1还有UV2，它是为实时全局光照准备的。只有UV3开始才是我们程序可以自定义使用的UV数据，其实UV可以有很多个UV4，UV5，不过Unity3D的网格类(Mesh)暂时只提供到UV3的获取接口即程序中的mesh.uv4。&lt;/p&gt;

&lt;p&gt;既然光照纹理(Lightmap)存储的是光照信息，那么它到底存了哪些信息呢？我们先来看下这幅图：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺图
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这幅图解释了烘培的简单模型，它分为三个部分，第一部分为光线射到墙壁后反射过来照到模型上，第二部分为光线照射过来时被其他模型挡住，导致当前的模型没有被光线照射到并且有阴影产生，第三部分为光线直接照射到模型上产生的颜色信息。&lt;/p&gt;

&lt;p&gt;这三者之和最终形成了完全的光照颜色。可以用一个简单的公式来说明这三者的结合方式：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	光照颜色 = 间接光照颜色 + 直接光照颜色 * 阴影系数(0到1)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;其中直接光的计算代价不高，在一些光照并不复杂的场景中并不记录直接光信息，而是由Shader自己计算直接光照。因此我们能看到，很多项目中并没有记录直接光，而只是记录间接光，即光照纹理中只记录了从其他物体反射过来的光产生颜色的总和。除了直接光照和间接光照，场景烘培还会产生一张阴影纹理来记录阴影信息。如果你希望记录主要光的方向，也可以开启Directional Model的Directional来获得另一张存有光照方向的图，这个贴图上存储的光方向信息可以被用在Shader中作为计算的变量。&lt;/p&gt;

&lt;p&gt;现在我们知道了烘培(Bake)会最多产生3种贴图，一种是光照纹理图(可能是间接光照纹理图，也可能是间接光照+直接光照+阴影合并的纹理图，取决于你在Unity3D中Lighting Mode的设置)，一种是阴影纹理图，一种是主要光方向纹理图，以及模型的UV1数据。&lt;/p&gt;

&lt;p&gt;其中UV1会被存储到模型网格信息中去，也就是烘培后模型prefab的mesh.uv2的数据会被改写。我们在制作和导出模型时要稍加注意，烘培需要用到模型的UV1数据，在导出模型时如果没有加入UV1数据则可能无法得到正确的烘培结果。&lt;/p&gt;

&lt;h6 id=&quot;那么烘培器是如何生成uv和贴图的呢我们需要理解下uv-chart&quot;&gt;那么烘培器是如何生成uv和贴图的呢？我们需要理解下UV Chart&lt;/h6&gt;

&lt;p&gt;在烘培时，烘培器会对所有场景中的静态物体上的Mesh网格进行扫描，按块大小和折线角度大小来制作和拆分Mesh上的对应的UV块，这个UV块就是UV Chart。&lt;/p&gt;

&lt;p&gt;UV Chart是静态物件在光照纹理(Lightmap)上某块Mesh对应的UV区块，一个物体在烘培器预计算后会有很多个UV Chart。因此每个物件的UV Charts是由很多个UV Chart组成，每个UV Chart为一段连续的UV片段。默认情况下，每个Chart都至少是4x4的纹素，无论模型的大小一个Chart都需要16个纹素。UV Chart之间预留了0.5个像素的边缘来防止纹理的溢出。如下图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/7/uvchart.png&quot; alt=&quot;UV Chart0&quot; /&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	图0
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/7/LightingGiUvs-2.png&quot; alt=&quot;UV Chart1&quot; /&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	图1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/7/LightingGiUvs-4.png&quot; alt=&quot;UV Chart2&quot; /&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	图2
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/7/LightingGiUvs-0.jpg&quot; alt=&quot;UV Chart3&quot; /&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	图3
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;图中1描述了，当一个场景只有1个立方体物体时，这个立方体网格物体被烘培后，6个面上的UV Chart是如何映射到烘培纹理上的。图2描述了场景中当有多个简单的立方体时，每个物体被扫描后制成UV Chart的情况。图3描述了当烘培场景更加复杂时，扫描后UV Chart被制作的情况，不同规格的模型UV被映射到Lightmap纹理贴图上。&lt;/p&gt;

&lt;p&gt;我们能从图中直接清晰的了解到，在烘培时每个场景中的静态物体都会被扫描Mesh网格，并且将其计算出来的UV Chart合并起来制作成一张或几张(可能场景太大一张不够用)光照纹理贴图lightmap。&lt;/p&gt;

&lt;h6 id=&quot;那么什么决定了烘培中扫描网格时形成的uv-chart大小和数量呢相邻顶点间的最大简化距离和最大夹角值&quot;&gt;那么什么决定了烘培中扫描网格时形成的UV Chart大小和数量呢？相邻顶点间的最大简化距离和最大夹角值。&lt;/h6&gt;

&lt;p&gt;烘培器为了能更加快速的计算制作出UV Chart，烘培器需要对模型网格顶点扫描进行简化。简化方式为，将相邻顶点间距离小于某个数值的顶点归入一个UV Chart，这个合并间距的数值越大UV Chart生成的速度就会越快。如果只是顶点距离上的简化往往会出现很多问题，我们需要从相邻面的角度上对合并进行约束，当相邻面间的角度大于某个值时，即使顶点距离符合合并间距也不能简化成同一个UV Chart。这两个参数在Unity3D中都有设置，点击静态物体在右边的版面上就能看到。如图位置：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/7/LightingGiUvs-3.png&quot; alt=&quot;UV Chart1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;图中展示了静态物体Mesh Renderer中设置Lightmap UV生成参数，参数包括最大简化顶点距离，最大邻接面角度。&lt;/p&gt;

&lt;p&gt;当设置的最大简化距离和邻接面最大角度数值比较大时，计算生成UV Chart的数量就会比较少，反之设置的最大简化距离和最大邻接面角度比较小时则需要计算和生成的UV Chart会比较多，此时烘培的速度也会比较慢，因为在预计算实时全局光照(GI)时，每个UV Chart上的像素都会计算灯光，预计算的时间跟Chart的数量直接的关系。&lt;/p&gt;

&lt;h6 id=&quot;上述描述了烘培的前置制作中lightmap纹理分布和场景中物体的uv映射的原理那么绘制lightmap纹理贴图时纹理上颜色是怎么生成的呢&quot;&gt;上述描述了烘培的前置制作中Lightmap纹理分布和场景中物体的UV映射的原理，那么绘制Lightmap纹理贴图时纹理上颜色是怎么生成的呢？&lt;/h6&gt;

&lt;p&gt;我们知道如果不用烘培技术，在实时渲染中，因为算力的原因我们只能计算直接光对物体的明暗影响。如果想要在实时渲染中计算间接光的影响是非常消耗GPU的算力的，即使有足够强大的显卡支撑使用光线跟踪计算，也只能在带有RTX的显卡计算机上使用。暂时还没有做到普及的程度，因此离线烘培成了我们解决间接光的主要手段。&lt;/p&gt;

&lt;p&gt;在一个场景中如果这些物体只考虑直接光的影响，则会缺乏很多光影细节，导致视觉效果很“平”。而间接光则描述了光线在物体表面之间的折射，增加了场景中明暗变化以及光线折射的细节，提高了真实感。&lt;/p&gt;

&lt;p&gt;光照纹理贴图的像素主要根据光的折射与反射现象来计算得到，那么它具体是用怎样的算法来计算得到光照纹理图中的像素颜色的呢，这里我们来了简单解一下Unity3D中采用的Enlighten和Progressive Lightmapper两种算法的解决方案。&lt;/p&gt;

&lt;p&gt;全局照明可以用一个称为渲染方程的复杂方程来描述：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/7/LightingGi-1.png&quot; alt=&quot;渲染方程&quot; /&gt;&lt;/p&gt;

&lt;p&gt;上述的渲染方程定义了光线是如何离开表面上某个点的，可是这个积分方程太复杂以至于计算机无法在较短时间内计算得出结果，Unity3D中Enlighten采用的近似方法即辐射算法，这可以大大提高计算渲染方程式的速度。&lt;/p&gt;

&lt;p&gt;辐射算法假设了场景中存在一组有限的静态元素，以及仅有漫射光传输来简化计算。在计算过程中它把场景拆分成很细很细的面片，分别计算它们接收和发出的光能，逐次迭代直到每个面片的光能数据不再变化(或者到一定的阀值)为止，得到最终的光照图。场景拆分后的以及每个面片之间的作用，如下图所示：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/7/LightingGi-2.png&quot; alt=&quot;渲染方程&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Enlighten将场景切割成很多个面片我们称它们为Cluster(Cluster大小可以通过Unity3D的烘培参数设置数值大小)，这些Cluster会对其映射的静态物体的纹理中的反射系数进行采样，然后计算Cluster之间的关系使得光在Cluster之间传递。&lt;/p&gt;

&lt;p&gt;Enlighten将渲染方程简化成了迭代公式即：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/7/LightingGi-3.png&quot; alt=&quot;渲染方程&quot; /&gt;&lt;/p&gt;

&lt;h6 id=&quot;其中bi指的是在i点最终的光le是i点本身的光而两个cluster之间光的反弹系数由fij来决定lj则是j点的光这也是为什么enlighten能够支持场景物体不变的情况下允许光源发生变化的原因因为几何体素化和辐射系数计算代价比较大需要离线计算而迭代每个cluster形成最终结果则计算量相对比较小可以实时进行&quot;&gt;其中Bi指的是在i点最终的光，Le是i点本身的光，而两个Cluster之间光的反弹系数由Fij来决定，Lj则是J点的光。这也是为什么Enlighten能够支持场景物体不变的情况下允许光源发生变化的原因：因为几何体素化和辐射系数计算代价比较大，需要离线计算，而迭代每个Cluster形成最终结果则计算量相对比较小可以实时进行。&lt;/h6&gt;

&lt;p&gt;Progressive Lightmapper即渐进式光照贴图，是Unity3D 2018版本后才能使用的烘培算法。&lt;/p&gt;

&lt;p&gt;Progressive Lightmapper是一种基于路径追踪（fast path-tracing-based）的光照贴图系统，它能在编辑器中逐步刷新的烘焙光照贴图（baked lightmaps）和光照探针（Light Probes）。&lt;/p&gt;

&lt;p&gt;Progressive Lightmapper主要的优势是能随着时间的推移逐步细化输出画面，及时逐步的看到画面效果，这样能够实现更完善的交互式照明工作流。另外Progressive Lightmapper还提供了一个预估的时间，所以烘焙时间更加可预测。&lt;/p&gt;

&lt;p&gt;参考文献：&lt;/p&gt;

&lt;p&gt;《OpenGL编程指南》&lt;/p&gt;

&lt;p&gt;《OpenGL ES 3.0编程指南》&lt;/p&gt;

&lt;p&gt;《Unity移动平台下的烘焙使用及优化》&lt;/p&gt;

&lt;p&gt;《浅析Unity中的Enlighten与混合光照》&lt;/p&gt;

&lt;p&gt;《Progressive CPU Lightmapper》&lt;/p&gt;

&lt;p&gt;《光照贴图Lightmap初探》&lt;/p&gt;

&lt;p&gt;《辐射度算法(radiosity)原理》&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>思路探讨(二十九) 有没有可能是我们理解错了</title>
   <link href="http://www.luzexi.com/2019/10/13/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A829"/>
   <updated>2019-10-13T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/10/13/思路探讨29</id>
   <content type="html">&lt;p&gt;#有没有可能是我们理解错了&lt;/p&gt;

&lt;p&gt;我经常这样问自己，有没有可能是我理解错了。有没有可能完全不是我理解的那么回事。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;h6 id=&quot;这个世界的真相总是在我们背后但我们总认为自己的前方所见就是全世界因此完全推翻自己的认知是有必要的&quot;&gt;这个世界的真相总是在我们背后，但我们总认为自己的前方所见就是全世界。因此完全推翻自己的认知是有必要的。&lt;/h6&gt;

&lt;p&gt;“有没有可能是我们理解错了”，这个问题是来自于我们的反思，不过看起来似乎像是用好奇心来破解当前面对的困局。&lt;/p&gt;

&lt;h6 id=&quot;当我们遇到困境遇到疑惑遇到迷茫时不妨用好奇的口吻来表达我们渴望突破&quot;&gt;当我们遇到困境，遇到疑惑，遇到迷茫时，不妨用好奇的口吻来表达我们渴望突破。&lt;/h6&gt;

&lt;p&gt;其实我们都是井底的那只蛙，我们弹跳力不够很难跳出这口井，就像我们无法用旁观者的角度看自己那样，在井中我们总是认为自己所思所想都是对的，以至于认为世界就是我们所认为的方式在运作。&lt;/p&gt;

&lt;p&gt;映射到实际生活中，比如像我一样的程序员编程技术工作者们，我们很难跳出这个技术视野去看事物。很多像我一样以为只要技术好就能升职加薪赢取白富美走上人生巅峰，这可能是程序员思维中最大的错误理解。因为我们身处在具体的工作细节中，这个具体工作本身就是那口井，我们无法跳出这口井去看外面的世界，就像我们无法用旁观者的角度看清问题的本质一样。&lt;/p&gt;

&lt;h6 id=&quot;面对程序员的职业发展与事业道路我对自己表达了好奇有没可能是我理解错了我希望透过现象看本质&quot;&gt;面对程序员的职业发展与事业道路，我对自己表达了好奇“有没可能是我理解错了”，我希望透过现象看本质。&lt;/h6&gt;

&lt;p&gt;就此我提出四个点来表达透过现象看到的本质：&lt;/p&gt;

&lt;h3 id=&quot;技能可以用来打工产品才能用来赚钱&quot;&gt;技能可以用来打工，产品才能用来赚钱。&lt;/h3&gt;

&lt;p&gt;为什么说“只要技术好就能升职加薪赢取白富美走上人生巅峰”是错的呢？&lt;/p&gt;

&lt;p&gt;不是技术没用，而是只有技术还远远不够。口才和心理素质比技术有用的多。&lt;/p&gt;

&lt;p&gt;技术只不过是熟能生巧而已，可以理解为经验和知识在脑中的肌肉记忆。&lt;/p&gt;

&lt;p&gt;而口才是嘴巴和脸部肌肉的肌肉记忆，心理素质则是环境在脑中的肌肉记忆。&lt;/p&gt;

&lt;p&gt;只有这几个肌肉记忆联结起来才会产生实质性的效果。而产品其实就是这几个因素联结起来的结果。&lt;/p&gt;

&lt;p&gt;一个好的产品不只要有好的技术，还要有好的营销，好的运营，好的耐心和好的专注。&lt;/p&gt;

&lt;p&gt;如果只有技术，那就会什么都不是。&lt;/p&gt;

&lt;h3 id=&quot;开源只是种学习的方式并不能成为崛起财富的途径&quot;&gt;开源只是种学习的方式，并不能成为崛起财富的途径。&lt;/h3&gt;

&lt;p&gt;很多人都喜欢开源，因为他们弄不清目标，只是觉得开源很酷。&lt;/p&gt;

&lt;p&gt;开源是种精神，这种精神很多种，有的纯粹为了分享，虽然对世界有益，但对个人而言毫无益处。但有的开源就是为了锻炼自己，把自己的行为、希望、作品抛到全世界的平台上去让大家去检验去抨击你，（前提是这种行为是你这个领域一无所有或者投资太多关注太少的时候，如果你已经拥有很多，开源就会变成了一种浪费），那是真正对自己有益的行为。&lt;/p&gt;

&lt;h3 id=&quot;技术驱动的企业必定失败只有利益和市场才是财富的依靠&quot;&gt;技术驱动的企业必定失败，只有利益和市场才是财富的依靠。&lt;/h3&gt;

&lt;p&gt;世界上没有以技术为导向的公司，所有声称以技术导向的公司都死了，因为这并不符合市场的规律，不符合人类的天性。&lt;/p&gt;

&lt;p&gt;人类的天性是逐利的，技术并不能决定市场的逐利行为，技术不迎合市场一样遭遇淘汰。&lt;/p&gt;

&lt;h3 id=&quot;市场是不道德的遵循道德跟是否得到市场认可无关但遵循道德可以让人安心踏实&quot;&gt;市场是不道德的，遵循道德跟是否得到市场认可无关，但遵循道德可以让人安心踏实。&lt;/h3&gt;

&lt;p&gt;市场跟道德无关，很多现象看似不道德其实只是人们心里的偏见而已，其实并无他，即使站在道德的制高点也不会让市场回头。&lt;/p&gt;

&lt;p&gt;不过虽然道德跟很多逐利的事物无关，但跟人本身却有很大关系，它能让人安心踏实，人生在世，图的其实就是个安心，道德对人来说就是最好的抚慰剂，让人安心踏实。&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;最后说说我自己。我有好奇的毛病，我想大多数人都和我一样，它时常让我陷入困境，比如我时常有奇思怪想，去体验一把没有体验过的人生旅途，最后发现没有人去体验不是因为他们不敢而是因为这条路体验很差或许还很受伤。不过好奇也同样时常让我豁然开朗，因为体验后自己就经历了比别人更多的遭遇，也有了更多人生体验和感悟。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第七章，渲染管线与图形学(三) - 渲染原理与知识1</title>
   <link href="http://www.luzexi.com/2019/10/13/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-%E6%B8%B2%E6%9F%93%E7%AE%A1%E7%BA%BF%E4%B8%8E%E5%9B%BE%E5%BD%A2%E5%AD%A66"/>
   <updated>2019-10-13T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/10/13/Unity3D高级编程之进阶主程-渲染管线与图形学6</id>
   <content type="html">&lt;p&gt;前面的几篇非常详尽的讲述了渲染管线的整个流程以及渲染管线上的每个节点的来龙去脉。这节我们来说说，一些渲染概念和原理，以及上几章中对渲染管线上没有说到的细节，以及在现代GPU中已经被优化的部分。&lt;/p&gt;

&lt;h3 id=&quot;为什么要有渲染顺序&quot;&gt;为什么要有渲染顺序&lt;/h3&gt;

&lt;p&gt;前面章节中我们介绍了深度测试这个模块阶段，它用片元的深度值与深度缓存中的值对比，测试的结果决定是否要写入深度缓存中，如果判断不符合则抛弃片元不再继续下面的流程。这其中涉及到了 ZTest On/Off 状态开关，和，ZWrite On/Off 状态开关，其中ZTest 用于控制是否开启深度测试，ZWrite 用于控制是否写入深度缓存。&lt;/p&gt;

&lt;p&gt;渲染管线中的深度测试最大的好处是帮助我们尽早的发现不需要渲染的片元，及时抛弃它们以节省GPU开销从而提高了效率。大部分情况下我们都使用 ZTest LEqual 来做深度测试的判断，也就是离摄像机越近的物体绘制的偏远越容易遮挡住离得远的物体。从这个角度看渲染机制，如果能先把离屏幕近的物体放前面渲染，那么离屏幕远的物体则能在深度测试的机制下早早的闭屏掉很多片元的渲染，提升不少的GPU效率。&lt;/p&gt;

&lt;p&gt;我们发现从上述角度看，渲染顺序就成了提高GPU效率的关键，Unity3D引擎对所有不透明物体在渲染前都做了排序的工作，离摄像机近的排在前面渲染，离的远的排在后面渲染，这个渲染队列就有了排序规则。&lt;/p&gt;

&lt;p&gt;那么半透明物体怎么办呢？因为半透明物体需要Blend混合，Blend混合就需要先将不透明物体先渲染完成再做Blend混合操作，因此它通常被引擎安排在所有不透明物体渲染后才渲染，只有这样才能发挥出它半透明的效果。在半透明物体中，ZWrtie通常都是关闭状态，如果将ZWrite开启后半透明部分在深度测试时就会抛弃比它深度高的像素，这导致多个半透明物体在叠加渲染时由于深度测试而被抛弃，丢失了Blend混合的效果使得画面有点错乱，这也是半透明物体通常不开启ZWrite的原因，它的主要方式还是混合而非测试。&lt;/p&gt;

&lt;p&gt;Unity3D引擎在提交渲染时就有这么条规则，即对所有半透明物体的渲染都排在了不透明物体的后面，这样就确保了半透明物体能在不透明物体渲染完毕后才开始渲染，以保证半透明物体的Blend混合效果。其半透明物体的队列也同样使用排序算法在渲染前排序，只是排序负责与不透明物体相反，即离摄像机越远的物体越先渲染。&lt;/p&gt;

&lt;p&gt;那么怎么判定物体是不透明还是半透明呢，虽然Blend是半透明的特色但也不是唯一标准，不透明物体同样可以使用Blend混合增强效果。Unity3D引擎为了解决这个问题在Shader中使用了标记功能，将渲染顺序放在Shader中去标记，即用Shader中的 Queue 标签来决定我们的模型归于哪个渲染队列。&lt;/p&gt;

&lt;p&gt;Unity3D在内部使用了一系列整数索引来表示渲染的次序，索引越小表示排在前面被渲染。Queue 标签：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	Background，背景层，索引号1000

	Geometry，不透明物体层，索引号2000

	AlphaTest，AlphaTest物体层，索引号2450

	Transparent，半透明物体层，索引号3000

	Overlay，覆盖层，索引号4000
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Shader中我们选择Queue标签就会指定索引类型。我们来看一个例子：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;Shader &quot;Transparent Queue Example&quot;
{
     SubShader
     {
        Tags { &quot;Queue&quot; = &quot;Transparent&quot; }
        Pass
        {
            // rest of the shader body...
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这里例子中将物体标记为半透明队列，当被标记为标记为半透明物体时，Unity3D引擎就会将这些物体放在不透明物体渲染之后做渲染。&lt;/p&gt;

&lt;p&gt;我们前面了解到，不透明物的排序与半透明物的排序是相反的，因为半透明物需要Blend混合，必须先绘制远处的物体，这样Blend混合的效果才正确。Unity3D在渲染队列标签中，每个标签都有一个索引号，Unity3D规定2500以下的索引号，排序规则以根据摄像机的距离由近到远顺序渲染，2500索引号以上的渲染队列标号则相反，排序规根据摄像机的距离由远到近顺序渲染排列。&lt;/p&gt;

&lt;p&gt;为什么要这么排序呢？因为2500以下物体都是不透明物体，渲染在深度测试阶段越早剔除掉越好，所以对摄像机由近及远的渲染方式对早早的剔除不需要渲染的片元有莫大的帮助，这种方式提高了GPU效率。而2500索引以上的物体，通常都是半透明物体或者置顶的物体(例如UI)，如果依然保持由近到远的渲染规则，其中的半透明物体就无法混合到被它覆盖的物体。因此2500索引及以后的物体与2500索引以前的物体规则是相反的。&lt;/p&gt;

&lt;p&gt;半透明的排序问题通常是头疼的，为什么呢？因为前面我们说的它是需要由blend混合完成半透明部分的操作，而Blend操作必须在前面物体已经绘制好的情况下才能有Blend混合后成为半透明或全透明效果。&lt;/p&gt;

&lt;p&gt;Shader中的Queue标签在Transparent半透明索引号下，相同索引号是从远到近渲染的，在粗糙颗粒的排序上尚可以解决部分叠加问题，即两个物体模型没有相交部分，前后关系的blend混合是可以依靠模型中点离摄像机的远近做排序的，Unity3D引擎就是这么做的。但是如果两个物体网格面片相交，或者同一个物体中面片相互交错，则无法再区分片元的前后关系了。原因是他们没有写入片元的深度值，即ZWrite为关闭状态，不能用深度值去判定片元是否覆盖或被覆盖，倘若开起来则又会出现Blend混合失效，因为片元底下的覆盖的片元被彻底抛弃了，就无从Blend混合一说了。&lt;/p&gt;

&lt;p&gt;因此使用Blend混合做半透明物体，在复杂的半透明交叉情况下通常很难做到前后关系有秩序，特别是当模型物体有交集的时候。此时我们通常都采用手动排序的方法来纠正排序问题，例如在Queue标签上用+1的方式表明层级被优先渲染，即Tag{ Queue = “Transparent+1” } 的形式。&lt;/p&gt;

&lt;p&gt;我们说所有物体的渲染顺序都是引擎自主排列的，而不是由GPU排序的，GPU只知道渲染、测试、裁切，完全不会去管物体的前后次序，这也是为什么称GPU渲染为“渲染流水线”的原因，它就像工厂里的作业流水线一样，每个工人都只是一个节点的螺丝钉(代表渲染流水线中各个阶段的结点)，它们大部分时候只要记住一个动作就可以“无脑”的重复劳动，GPU里就是这样的做法。&lt;/p&gt;

&lt;h3 id=&quot;alpha-test&quot;&gt;Alpha Test&lt;/h3&gt;

&lt;p&gt;上面几节讲了好多关于半透明物体的知识，而Alpha Test其实也是属于半透明物体的特征，不过它不是混合，而是裁切。&lt;/p&gt;

&lt;p&gt;我们在制作模型过程中，很多模型的边角都需要极其细微的面片，比如树上的叶子，一堆乱糟糟的草，还有许许多多圆形的洞等，这些模型如果用网格来表达的话会多出很多很多面片，制作时间长，调整起来慢，同屏面数高，问题滚滚而来。&lt;/p&gt;

&lt;p&gt;那么怎么办呢，Alpha Test能很好的解决这些问题，Alpha Test 用纹理图片中的 Alpha 来测试判定该片元是否需要绘制。当我们尝试展示一些很细节的模型时，如果使用Alpha Test，原本要制作很多细节网格，现在只要用一张图片和两三个面片就能代替巨量的面片制作效果，即使有时需要调整，也只需要调整纹理图片和少量顶点就可以完成工作。&lt;/p&gt;

&lt;p&gt;这种方式被大量用在节省面片数量的渲染上，因为它的制作简单，调整容易，被众多模型设计师和开发人员所喜爱。其渲染的过程也相对比较简单，在片元着色器中判断该片元 Alpha 值是否小于了某个阈值，一旦判定小于某个阈值就调用clip或者discard丢弃该片元，该片元的不再进行后面的流水线。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;Shader &quot;Example Alpha Test&quot;
{
	Properties
	{
        _MainTex (&quot;Base (RGB)&quot;, 2D) = &quot;white&quot; {}
        _Cutoff(&quot;Cut off&quot;,range(0,1))=0.5
    }

    ...

    SubShader
    {
     	...

        //Alpha Test示例
		Pass
        {
            struct v2f {
                float4 pos : SV_POSITION;
                float4 uv:TEXCOORD0;
            };

        	v2f vert(appdata_base v)
        	{
        		v2f o;
        		o.pos = UnityObjectToClipPos(v.vertex); //转换顶点空间
        		o.uv = v.texcoord; // 传递uv值
        		return o;
        	}

        	fixed4 frag(v2f i) : SV_Target
        	{
        		fixed4 _color = tex2D(_MainTex,i.uv.xy); // 根据uv获取纹理上的纹素

        		//clip函数非常简单，就是检查它的参数是否小于0。如果是，就调用discard舍弃该fragment；否则就放过它。
        		clip(_color.a - _Cutoff);

        		return _color;
        	}
        }
    }

    ...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述Shader剥离了干扰因素，代码极简的表现了Alpha Test。先将顶点uv从顶点着色器上传递到片元着色器上，再用uv坐标数据取出纹理中的颜色，使用clip函数判定片元是否通过测试。clip函数非常简单，就是检查它的参数是否小于0。如果是，就调用discard舍弃该fragment，否则就放过它。我们来看看用Alpha Test制作草地的画面效果:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺图
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;图中这些小草都只是使用了少许的面片，GPU在渲染片元时会先去判定该片元的 Alpha 是否小于某个阀值，如果小于则不渲染该片云，否则继续渲染。&lt;/p&gt;

&lt;p&gt;这种方式的裁剪片元对于只需要不透明和全透明的物体来说很好用，而且 Alpha Test 不需要混合，它完全可以开启 ZTest 的深度测试，和 ZWrite 的深度写入，在渲染遮挡问题上完全没有问题。不过它并不是万能的，也存在很多缺陷，我们下面就要讲讲在现代GPU中Alpha Test出现的问题。&lt;/p&gt;

&lt;h3 id=&quot;early-z-gpu硬件优化技术&quot;&gt;Early-Z GPU硬件优化技术&lt;/h3&gt;

&lt;p&gt;前面我们介绍过些深度测试的知识，即深度测试在片元着色器之后对片元之间的前后做了遮挡测试，这使得GPU对哪些片元需要绘制又有哪些片元因被遮挡而不需要绘制有了数据依据。不过深度测试是在片元计算完毕后才做的测试，使得大部分被遮挡的片元在被剔除前就已经经历过了着色器的计算，这使得当片元重叠遮挡比较多时许多片元的前期计算浪费的较为严重，被遮挡部分的片元计算完就被抛弃，浪费了算力。&lt;/p&gt;

&lt;p&gt;这种情况频繁发生，特别是在摄像机需要渲染很多物体的时候，相互叠加遮挡的情况会越来越严重，每个物体生成的片元无论是否被遮挡都经过了差不多是一整个的渲染流程，深度测试前的渲染计算几乎全部浪费掉。&lt;/p&gt;

&lt;h6 id=&quot;early-z-技术就专门为这种情况做了优化我们可以称它为前置深度测试由于渲染管线中深度测试作用在片元着色器之后这时候再进行深度测试时所有渲染对象的像素都已经经历了计算的整个过程没有半点优化因此深度测试几乎没有性能提升仅仅是为了得出正确的遮挡结果造成大量的无用计算算力浪费每个像素点上重叠了许多次计算&quot;&gt;Early-Z 技术就专门为这种情况做了优化，我们可以称它为前置深度测试。由于渲染管线中，深度测试作用在片元着色器之后，这时候再进行深度测试时，所有渲染对象的像素都已经经历了计算的整个过程没有半点优化，因此深度测试几乎没有性能提升，仅仅是为了得出正确的遮挡结果，造成大量的无用计算算力浪费，每个像素点上重叠了许多次计算。&lt;/h6&gt;

&lt;h6 id=&quot;在现代gpu中更多的运用了early-z的技术它在几何阶段与片元着色器之间光栅化之后片元着色器之前先进行一次深度测试如果深度测试失败就认为是被遮挡的像素直接跳过片元阶段的计算过程节省了大量的gpu算力&quot;&gt;在现代GPU中更多的运用了Early-Z的技术，它在几何阶段与片元着色器之间（光栅化之后，片元着色器之前）先进行一次深度测试，如果深度测试失败，就认为是被遮挡的像素，直接跳过片元阶段的计算过程，节省了大量的GPU算力。&lt;/h6&gt;

&lt;p&gt;我们来看下它的具体流程，我们来看看如下图：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	Early-Z--|
	|        |no
	|yes     |
	|    片元着色计算
	|        |
	|        |
	ZTest 深度测试 -- 抛弃
	|
	|
	屏幕像素缓冲
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上图中展示了Early-Z 前置深度测试的流程，光栅化后的片元先进入Early-Z 前置深度测试阶段，如果片元测试被遮挡，则直接跳过片元着色计算，如果没有被遮挡则继续片元着色的计算，无论是否通过Early-Z 前置深度测试，最终都会汇集到ZTest 深度测试再测试一次，由后置的深度测试来最终决定是否抛弃该片云，由于前置深度测试已经测试完毕了片元的前后关系，因此所有跳过片元着色计算的片元都会在后置深度测试的节点上被抛弃，反之则会继续渲染流程像素最终进入屏幕像素缓冲区。&lt;/p&gt;

&lt;p&gt;Early-Z的实现是GPU硬件自动调用的，它主要是通过两个pass来实现，即第一个是Z-pre-pass，对于所有写入深度数据的物体，先用一个超级简单的pass不写入像素缓存，只写深度缓存，第二个pass关闭深度写入，开启深度测试，用正常渲染流程进行渲染。&lt;/p&gt;

&lt;h6 id=&quot;由于alpha-test的做法让我们在片元着色器中可以自主的抛弃片元因此问题又出现了&quot;&gt;由于Alpha Test的做法让我们在片元着色器中可以自主的抛弃片元，因此问题又出现了。&lt;/h6&gt;

&lt;p&gt;片元在着色器中被主动抛弃后，Early-Z 前置深度测试的结果就会出现问题，因为测试通过的可见片元被抛弃后，被它遮挡的片元就成为了可见片元，导致前置的深度测试结果出现问题。因此GPU在优化算法中，对片元着色器抛弃片元和修改深度值的操作做了检测，如果检查到片元着色器中存在抛弃片元和改写片元深度的操作时，Early-Z 将被放弃使用。&lt;/p&gt;

&lt;h6 id=&quot;简单来说early-z-对遮挡处理做了很大的优化但是如果我们使用了alpha-test-来渲染物体时early-z-的优化功能将被弃用&quot;&gt;简单来说，Early-Z 对遮挡处理做了很大的优化，但是如果我们使用了Alpha Test 来渲染物体时，Early-Z 的优化功能将被弃用。&lt;/h6&gt;

&lt;h3 id=&quot;mipmap的原理&quot;&gt;Mipmap的原理&lt;/h3&gt;

&lt;p&gt;Mipmap是目前应用最为广泛的纹理映射技术之一，Mip来源于拉丁文中的multum in parvo，意思是“在一个小区域里的很多东西”。引擎将Mipmap技术与材质贴图技术结合，根据物体距摄像机远近距离的不同，分别使用不同分辨率的纹理贴图，不仅提高了画面效果还提高了GPU渲染效率。Mipmap功能在3D游戏中非常常见，但很多人还是不太了解Mipmap的来龙去脉，我们在这里详细的讲一讲。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺图(Mipmap分级生成的图)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在我们为3D物体渲染纹理贴图时，经常出现物体离摄像机很远的情况，屏幕像素与纹理大小的比率变得非常低，此时纹理采样点的变化会非常大，这样会导致渲染图像上的瑕疵。&lt;/p&gt;

&lt;p&gt;我们举例来说，假设我们要渲染一面墙，这面墙纹理有 1024 x 1024像素的大小，当摄像机距离墙适当时渲染的图像是没有问题的，因为每个像素都有各自对应的纹理贴图上合理的像素。但是当摄像机向这面墙渐渐远离，慢慢它在屏幕上的像素范围越来越小时就出现问题了，原因是物体所呈现的像素点越来越少，这使得纹理采样的坐标变化比较大，可能会在某个过度点上发生突然的变化导致图像产生瑕疵。特别是在屏幕上不断前后运动的物体可能会使得屏幕上渲染产生类似闪烁的劣质效果。&lt;/p&gt;

&lt;p&gt;Mipmap为了修正这种劣质效果，将纹理贴图提前存储成不同级别大小的纹理贴图，并在渲染时将它们传入OpenGL，OpenGL会判断当前应当使用纹理贴图的哪个层级大小的贴图，判断的依据是基于物体在屏幕上所渲染的像素大小决定的。&lt;/p&gt;

&lt;p&gt;除了能更好的平滑渲染远近物体像素上的瑕疵和闪烁问题外，Mipmap还能很好的提高采样的效率，由于采用从已经缓存的不同分辨率纹理的采样对象，那些远离摄像机的物体采用了更小分辨率的纹理贴图，这使得采样时内存与GPU缓存之间传输的带宽减轻了不少压力从而获得更高的效率。实际项目中大部分物体都离摄像机较远，这使得Mipmap的采样效率提升在渲染中发挥了重要的作用。&lt;/p&gt;

&lt;p&gt;在使用Mipmap时，通常OpenGL负责计算细节层次并得到所应该选择的Mipmap层级，再将采样结果返回给着色器。不过我们也可以自己取代这个计算过程再通过OpenGL纹理获取函数(textureLod)来选取指定的纹理层次。&lt;/p&gt;

&lt;p&gt;那么在OpenGL中到底 Mipmap 是怎么决定采用哪层分辨率的贴图的呢？我们来详细的讲解一下。首先我们有2个概念要复习一下：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	1.屏幕上的颜色点叫像素，纹理贴图上的颜色点叫纹素。

	2.屏幕坐标系我们用的是XY坐标系，纹理贴图坐标系用的是UV坐标系。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在片元着色器中，每个片元即屏幕空间XY上的像素都会找到对应的纹理贴图中的纹素来确定像素的颜色。这个查找纹素的过程就是一个从XY空间到UV空间的一个映射过程。我们可以通过分别求x和y偏导数来求屏幕单个像素宽度纹理坐标的变化率。&lt;/p&gt;

&lt;p&gt;由于物体离的远，像素覆盖屏幕的范围比较小，这使得屏幕上的像素区块，对应到实际的纹理贴图中可能是一个矩形的区域。那么x轴方向上的纹理贴图大小和屏幕上的像素区域大小有一个比例，y轴方向上的也同样有一个比例。&lt;/p&gt;

&lt;p&gt;例如，获取到的纹理贴图上的纹素大小为 64x64，屏幕上的像素区域大小为32x32，那么它们在x轴上的纹素和像素大小比例为 2.0 (即64/32)，y轴上的也同样是 2.0 (即64/32)。如果纹理贴图上的纹素大小为 64x32，屏幕上的像素区域大小为 8x16，那么它们在x轴上的纹素和像素大小比例为 8.0(即64/8)，在y轴上的纹素和像素大小比例为2.0(即32/16)。&lt;/p&gt;

&lt;p&gt;这个比例就是纹素的覆盖率，当物体离摄像机很远时，纹素的覆盖率就很大，当物体离摄像机很近时则很小，甚至小于1(当纹素覆盖率小于1时则会调用纹理放大滤波器，反之则用到了Mipmap，如果刚好等于1则使用原纹理)。&lt;/p&gt;

&lt;p&gt;在着色器中为了求得覆盖率，我们可以用ddx和ddy求偏导的方式分别求这个两个方向上的覆盖率，然后取较大的覆盖率。为什么ddx和ddy偏导函数就能计算覆盖率呢。这里稍微复习一下，我们知道在光栅化的时刻，GPU会在同一时刻并行运行很多片元着色器，但是并不是一个像素一个像素的放入到片元着色器去执行的，而是将其组织成 2x2 为一组的像素块再去并行执行。而偏导数就正好能计算这一块像素中的变化率。&lt;/p&gt;

&lt;p&gt;我们来看下偏导的真相：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	ddx(p(x,y)) = p(x+1,y) - p(x,y)

	ddy(p(x,y)) = p(x,y+1) - p(x,y)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;x轴上的偏导就是 2x2 像素块中 x轴方向上附近的数值之差。同理，y轴上的偏导就是 2x2 像素块中 y轴方向上附近的数值之差。因此MipMap层级的计算可以描述为：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;float MipmapLevel(float2 uv, float2 textureSize)
{
    float dx = ddx(uv * textureSize.x);
    float dy = ddy(uv * textureSize.y);
    float d = max(dot(dx, dx), dot(dy, dy));  
    return 0.5 * log2(d);
} 
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述函数中，先求出x轴和y轴方向上的覆盖率后，再取得dx和dy的最大值(dot(dx,dx)其实就是dx的平方，同理dy)，再log2后获得Mipmap层级，这里0.5是技巧，本来应该是d的平方。大部分时候OpenGL已经帮我们做了Mipmap层级的计算，也就是说我们在Shader中使用tex2D(tex, uv)获取颜色的时候就相当于在GPU内部展开成了如下面所示：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;tex2D(sampler2D tex, float4 uv)
{
    float lod = CalcLod(ddx(uv), ddy(uv));
    uv.w= lod;
    return tex2Dlod(tex, uv);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们可以从这段代码中得知uv所求的导数越大，在屏幕中占用的纹理范围就越大。当我们在片元计算中发现uv导数很大时，就说明这个片元离摄像机很远，从这个角度来理解uv在片元着色器中的求偏导会稍微容易些，这样我们就只需要通过uv的求偏导就能间接计算出x轴和y轴方向的覆盖率。在OpenGL中Mipmap的计算就依赖于片元中的uv求偏导值，片元所映射的uv范围越大，计算出来的Mipmap层级越高，纹理贴图选取的分辨率就越小。&lt;/p&gt;

&lt;h3 id=&quot;从显存里看问题&quot;&gt;从显存里看问题&lt;/h3&gt;

&lt;p&gt;显存经常被我们忽视，因为近几年流行的手机端的游戏项目比较多，手机设备上的没有显存的概念，它让GPU与系统共用一块内存，所以通常显存被理解为只在PC端存在。显卡除了有图像传给处理单元GPU外，还拥有自己的内存，即显存VRAM(Video Random Access Memory)，像安卓和IOS这样的架构的设备中，虽然没有大块独立的显存但GPU仍然有自己的缓存。&lt;/p&gt;

&lt;p&gt;GPU可以在显存中存储很多数据，包括贴图、网格、着色器实例等，除了这些渲染所必须的资源，缓存自然是更接近GPU内核的地方，顶点缓存、深度缓存、模版缓存、帧缓存大都存放在那里。GPU自己的缓存就相当于GPU内部的共享缓存部分，GPU中有很多个独立的处理单元，每个处理单元都有自己的缓存以存储一部分自己需要处理的数据。&lt;/p&gt;

&lt;p&gt;除了这几个必要的缓存外，显卡中存放着渲染时需要用到的贴图纹理、网格数据等，这些内容都需要从系统内存中拷贝过来的。在调用渲染前，应用程序可以调用图形应用接口OpenGL将数据从系统内存中拷贝到显卡内存中，当然这个过程只存在于PC端和主机端，因为只有它们拥有显存。显存更接近GPU处理器，这直接导致存取数据会更快，因此从系统内存中拷贝过来是值得的。&lt;/p&gt;

&lt;p&gt;手机端就没有这样的拷贝过程，手机端大都是ARM架构，芯片中嵌入了各种各样的硬件系统，包括SoC(即芯片级系统，包含了完整系统并有嵌入软件的全部内容)、图像处理GPU、音频处理器等。而显存由于种种限制没有被设计加入到ARM中去，因此在手机端中CPU和GPU共用同一个内存控制器，也就是说没有独立的显存只有系统内存。不过GPU仍然需要将数据拷贝到自己的缓存当中，只是这一步原本是从显存拷贝的，现在从系统内存中拷贝而已，GPU中每个处理单元也仍然要从共享缓存中拷贝自己需要处理的数据。&lt;/p&gt;

&lt;p&gt;由此可见，GPU处理数据前拷贝的过程仍然存在，只是原来从显存拷贝到缓存的过程变成了系统内存直接拷贝到缓存，速度自然没有原来的快，这种拷贝的过程每帧都在进行，当然也有缓存命中的情况，但仍避免不了重复拷贝，图片大小、网格大小也会成为拷贝的瓶颈点，我们通常称它们为带宽压力，由此看来压缩纹理贴图、使用大小适中的纹理贴图、减少网格数据也是优化性能的一个重要部分。&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第七章，渲染管线与图形学(二) - 渲染管线2</title>
   <link href="http://www.luzexi.com/2019/09/28/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-%E6%B8%B2%E6%9F%93%E7%AE%A1%E7%BA%BF%E4%B8%8E%E5%9B%BE%E5%BD%A2%E5%AD%A65"/>
   <updated>2019-09-28T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/09/28/Unity3D高级编程之进阶主程-渲染管线与图形学5</id>
   <content type="html">&lt;p&gt;继上篇讲解了渲染管线的应用阶段、几何阶段和光栅化阶段，这一节我们来讲讲最后的逐片元操作阶段，以及着色器中我们常见的一些概念和原理。&lt;/p&gt;

&lt;h6 id=&quot;逐片元操作per-fragment-operations是opengl的说法在directx称为输出合并阶段output-merger其实只是说法不同而已包含的内容都是相同的它包括了剪切测试scissor-test多重采样的片元操作模板测试stencil-test深度测试depth-test混合blending以及最后还有个逻辑操作&quot;&gt;逐片元操作(Per-Fragment Operations)是OpenGL的说法，在DirectX称为输出合并阶段(Output-Merger)，其实只是说法不同而已包含的内容都是相同的，它包括了，剪切测试(Scissor test)、多重采样的片元操作、模板测试(Stencil Test)、深度测试(Depth Test)、混合(blending)、以及最后还有个逻辑操作。&lt;/h6&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;这几个节点都是以片元为基础的元素操作，它们大都决定了片元的去留问题。所以逐片元操作阶段是决定片元的可见性问题的一个重要阶段，如果片元在这几个节点上任意一个节点没有通过测试，管线就会停止并丢弃它，之后的测试或操作都不会被执行，反之执行测试全部通过就会进入帧缓存等待输出到屏幕。每个节点实际测试的过程是个相对复杂的过程，而且不同的图形接口实现的细节也不一样，但我们要理解到它们的基本原理和大致的过程则相对简单一些。&lt;/p&gt;

&lt;p&gt;所有这些测试和操作其实都可以看做是以开关形式存在，因为他们的操作命令大都包含了On和Off操作指令，在OpenGL里以glEnable()和glDisable()来表示功能是否被开启或关闭，只是除了开关还需要我们指定参数。&lt;/p&gt;

&lt;p&gt;第一步可见性测试就是剪切测试(Scissor)，它主要针对的是片元是否在矩形范围内的测试判断，如果片元不在矩形范围内则被丢弃。这个范围是一个矩形的区域，我们可以通过OpenGL的函数调用来设置矩形位置和大小，我们称它为剪切盒。实际上我们可以设置很多个剪切盒，只是默认情况下所有渲染测试都在第一个剪切盒上完成，要访问其他剪切盒就需要几何着色器。剪切测试在Unity3D并不常用，它并不是视口设置不同，后者不会限制屏幕的清理操作。&lt;/p&gt;

&lt;p&gt;第二步是多重采样的片元操作。普通的采样只采一个样本或者可以说一个像素，而多重采样则是分散取得多个样本，这些样本可能是附近的几个位置也可能是通过其他算法得到的。因此在多重采样中，采样的片元都有各自的颜色、深度值、纹理坐标，而不是只有一种（具体有多少个取决于子像素的样本数目）。这里的多重采样操作有所不同，它是我们可以自定义的操作模式，自定义部分为alpha影响的采样覆盖率，以及我们可以设置掩码与采样的片元进行‘与’操作。默认情况下，多重采样在计算片元的覆盖率时不会考虑alpha的影响。&lt;/p&gt;

&lt;p&gt;第三步模板测试(Stencil Test)，模板测试说白了和比大小无异，只是在模板测试中比的方式和比的数字我们可以自定义设置。&lt;/p&gt;

&lt;p&gt;在模板测试中模板缓存块是必要的内存块，它与屏幕缓冲大小一致，每个片元在测试时都会先取得自己位置上的模版缓冲位置并与之比较，在通过测试后才被写入到模板缓存中，在整个渲染帧结束前它是不会被重置的，也就是说所有模板测试共享一个模板缓存块。&lt;/p&gt;

&lt;p&gt;在模板测试中，开发者通常需要指定一个引用参考值(Reference value)，这个参考值为当前物体的片元的提供了标准参考，然后这个参考值会与模板缓存(Stencil Buffer)中当前片元位置的模板值进行比较，模板缓存中的值是被前面的物体的片元通过测试时写入的值，比较两个值后根据比较的结果做判断是否抛弃片元，判断依据可以是大于、等于、小于或其他等，一旦判断失败片元将被抛弃反之则继续向下传递，另外即使判断成功后我们也可以对其值做其他操作，这类操作可以是替换旧的片元、或增加一定的参考值后再替换、或参考值置零等等。&lt;/p&gt;

&lt;p&gt;我们来看看到底有多少种判断和多少种对模板缓存的操作。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	Greater	大于模板缓存时判断通过

	GEqual	大于等于模板缓存时判断通过

	Less	小于模板缓存时判断通过

	LEqual	小于等于模板缓存时判断通过

	Equal	等于模板缓存时判断通过

	NotEqual	不等于模板缓存时判断通过

	Always	总是通过

	Never	总是不通过
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述这些是对于是否通过测试的判断种类，看上去像是各种比大小的方式。其在Unity3D的Shader中的完整的模板测试写法如下：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	Stencil {
	    Ref 2 //指定的引用参考值
	    Comp Equal //比较操作
	    ReadMask 255 //读取模板缓存时的掩码
	    WriteMask 255 //写入模板缓存是的掩码
	    Pass Keep //通过后对模板缓存的操作
	    ZFail IncrSat //如果深度测试失败时对模板缓存的操作
	}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;从上述看模板测试的步骤简单明了，当前值2与模板缓冲比较，2这个数值可以是从业务层传递进Shader内的参数，倘若2这个数值与模板缓存中的值比较后通过，则做Keep操作保留当前模板缓存中的值，倘若数值2并没通过测试，则片元被抛弃，并且在模板缓冲位置加上2这个数值。读取与写入掩码，意思就是读取模板缓存和写入模板缓存时都要与相应的数值进行与操作，这里的255就是16进制的0xFF，相当于2进制的8个1。&lt;/p&gt;

&lt;p&gt;Unity3D模板命令中 Pass 的操作是对通过测试后的参考值与模板缓存做操作，它有如下几种方式可选：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	Keep	不做任何改变，保留当前缓存中的参考值
	Zero	当前Buffer中置零
	Replace	将当前的参考值写入缓存中
	IncrSat	增加当前的参考值到缓存中，最大为255
	DecrSat	减少当前的参考值到缓存中，最小为0
	Invert	翻转当前缓存中的值
	IncrWrap	增加当前的参考值到缓存中，如果到最值255时则变为0
	DecrWrap	减少当前的参考值到缓存中，如果到最小为0时则变为255
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;不同的物体可以自己有不同的引用值，我们可以通过Unity3D设置材质球参数的接口将数值传递进入着色器。比较操作也可以多种多样，包括掩码值、成功后的操作动作。这让本来看上去一个简简单单的比大小行为赋予了更多的花样。不止如此，模板缓存里的值除了比较和通过测试后的操作指令外，深度测试也可以影响模板缓存中的值。我们用一幅图就能理解模板测试的美妙：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺图
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;图中三个球一起叠加在一起却只显示了一个球的，并且在这个球上显示了三个球叠加的部分。因为其他两个球的模板测试并没有通过，但叠加部分则通过了模板测试。&lt;/p&gt;

&lt;h6 id=&quot;片元幸运的通过了模板测试则会来到第四步深度测试深度测试主要作用是根据深度判断和覆盖在帧缓冲中的片元片元中有深度信息它的来源就是在归一化坐标后三角形顶点z轴上的值三角形经过光栅化后三角内的片元的深度是三个顶点的z坐标的插值深度测试依靠这个深度来判定是否需要覆盖已经写进帧缓冲里的片元可别忘了我们说片元就是带着诸多信息的像素&quot;&gt;片元幸运的通过了模板测试，则会来到第四步深度测试。深度测试主要作用是根据深度判断和覆盖在帧缓冲中的片元。片元中有深度信息，它的来源就是在归一化坐标后三角形顶点Z轴上的值，三角形经过光栅化后，三角内的片元的深度是三个顶点的z坐标的插值，深度测试依靠这个深度来判定是否需要覆盖已经写进帧缓冲里的片元(可别忘了我们说片元就是带着诸多信息的像素)。&lt;/h6&gt;

&lt;p&gt;我们说深度测试是为了判定最后片元是否写入帧缓存，那么在判定过程中深度测试自己也有自己的缓存，即深度缓存，它可读可写就是为片元深度信息判定而存在的。深度测试工作分为两块，其中一块是片元与缓存的比较即ZTest，另一块是片元信息写入缓存即ZWrite。所有片元只有在ZTest中与缓存中的数据比较并被判定通过的才有ZWrite写入深度信息的资格。&lt;/p&gt;

&lt;p&gt;当然我们也可以把ZWrite写入权限关闭，这样渲染物体的所有片元都只能做比较操作判定是否覆盖前面的片元而无法写入深度信息，这也同时导致了它的片元深度无法与其他物体比较。这种写法在半透明物体中很常见，其他时候大部分都是默认开启深度值写入即ZWrite On。&lt;/p&gt;

&lt;p&gt;深度测试是怎么比较的呢？还记得前面介绍的模板测试么，重点就是“比大小”，这次比的是片元上的深度与深度缓存中的深度信息，判定通过的就有权利写入深度缓存，深度测试的方法和模板测试的流程和方法简直就是一个妈生出的俩个孩子。深度测试的 ZTest 对应模板测试的 Comp指令，深度测试的 ZWrite 对应模板测试的 Pass指令，先拿当前片元比较缓存中的值再操作缓存，两者简直一模一样。我们来看看例子：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	Pass
	{
		ZTest LEqual
		ZWrite On
	}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这组深度测试参数表明了，深度判定规则为当物体的这个像素的Z值小于当前深度缓存中相同位置的深度时通过ZTest，通过ZTest后将该像素深度信息写入深度缓存。其中ZTest的可选参数为如下：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	Less 小于深度缓存中的值
	LEqual 小于等于深度缓存中的值
	Greater 大于深度缓存中的值
	GEqual 大于等于深度缓存中的值
	Equal 与深度缓存中的值相等
	NotEqual 与深度缓存中的值不相等
	Always 永远通过ZTest
	Never 永远不通过ZTest
	Off 等同于 ZTest Always
	On 等同于ZTest LEqual
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;深度测试与模板测试一样有自己的缓存块，不同的是深度测使用的是像素深度值而不是固定某个值，写入缓存的操作没有模板测试那么多花样。&lt;/p&gt;

&lt;p&gt;那么什么是深度值？这个深度值是从哪来的呢？还记得前面顶点着色器中介绍的，顶点在变化坐标空间后z轴被翻转成为了视口朝外的轴。摄像机裁剪空间从锥视体变成了长方体，x、y轴则成为了视口平面上的平面轴，z轴上的坐标成了顶点前后关系的深度值。我们再来看看这幅图片：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺图
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;正是因为空间坐标的转换和最后的的归一化，使得所有顶点的坐标都在一个长方体空间内，长方体的大小被限制在了-1到1的大小，于是x、y成为了屏幕相对参考坐标，而z则成为了前后关系的深度参考值。我们说的这些只是顶点上的坐标变化，在后面的步骤中三角面被光栅化，每个像素加入了更多信息形成了片元，三角形顶点信息在插值后z坐标也进入了片元数据中，于是z值成为了片元在深度测试中判断的依据。&lt;/p&gt;

&lt;h6 id=&quot;如果片元很幸运冲破前面这么多种测试终于来到了第五步混合阶段混合阶段实质上并没有丢弃任何片元但却可以让片元消失不见&quot;&gt;如果片元很幸运冲破前面这么多种测试终于来到了第五步混合阶段，混合阶段实质上并没有丢弃任何片元，但却可以让片元消失不见。&lt;/h6&gt;

&lt;p&gt;如果一个片元通过了上面所有的测试，那么它就有资格与当前帧缓存中的内容进行混合了。最简单的混合方式就是直接覆盖已有的颜色缓冲中的值，实际上这样不算混合，只是覆盖而已，我们需要两个片元的真正混合。&lt;/p&gt;

&lt;p&gt;那么什么叫混合？为什么要混合？混合是两个像素对颜色和alpha值的计算过程，其中一个像素来自要渲染的物体，另一个像素则来自已经写入的帧缓存。我们可以自己指定的操作来制定混合公式，通过配置的公式运算我们能得到想要的效果。&lt;/p&gt;

&lt;p&gt;由于渲染物体的像素都是一个接一个的被写入缓冲中，当前物体网格被光栅化成为片元后要写入缓存时，前面已经渲染的物体的片元会被直接覆盖掉，这样的话前后两个片元就没有了任何关联性的操作，开启混合则可以对前后两个片元在颜色上做更多的操作，这可能是我们所期望的。&lt;/p&gt;

&lt;p&gt;大多数情况混合与像素的 alpha 值有关，也可以只与颜色有关，混合阶段用的最多的是半透明材质。alpha 是颜色的第四个分量，OpenGL中片元的颜色都会带有 alpha 无论你是否需要它，无论是否你显性地设置了它，alpha默认为1不透明，我们可以用它实现各种半透明物体的模拟就像有色玻璃那样。&lt;/p&gt;

&lt;p&gt;说白了混合就是当前物体的片元与前面渲染过的物体的片元之间的操作，那么混合具体有哪些操作呢？我们来看下Unity3D中的混合指令：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	Blend SrcFactor DstFactor

	Blend SrcFactor DstFactor, SrcFactorA DstFactorA
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这里有两种操作方式，第一种是混合时颜色包括了alpha，第二种是将颜色和Alpha分开混合。其中 SrcFactor 这个因子(变量)会与刚刚通过测试的物体片元(即当前物体片元)上的颜色相乘，DstFactor 这个因子(变量)则会取已经在帧缓存中的片元(即缓存中的像素)的颜色相乘，类似的 SrcFactorA 这个因子(变量)会与刚刚通过测试的物体片元(即当前物体片元)上的 alpha 相乘，而 DstFactorA 这个因子(变量)会取帧缓存中的像素并与之 alpha相乘。&lt;/p&gt;

&lt;h6 id=&quot;这个过程有两个步骤第一步是相乘操作第二步是相乘后的两个结果再相加还可以选择其他操作模式我们称为混合方程&quot;&gt;这个过程有两个步骤，第一步是相乘操作，第二步是相乘后的两个结果再相加(还可以选择其他操作模式)。我们称为混合方程&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	即 Src * SrcFactor + Dst * DstFactor

	或 SrcColor * SrcFactor + DstColor * DstFactor, SrcAlpha * SrcFactorA + DstAlpha * DstFactorA
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;通常情况下相乘的结果再相加得到最终的混合片元。我们也可以改变这种方程式，让两个结果相减、或者调换位置后相减、取得最大值函数、取得最小值函数，来代替源数据与目标数据之间的操作符。即我们可以选择以下操作符：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	BlendOp Add 加法
	BlendOp Sub 减法
	BlendOp RevSub 置换后相减
	BlendOp Min 最小值
	BlendOp Max 最大值
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述5种操作符的修改就分别代表了因子相乘后相加、或相减、或置换后相减、或取得最小值、或取得最大值。我们这里拿Sub，Max来举个例子：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	当写入BlendOp Sub时，方程式就变成了：

	Src * SrcFactor - Dst * DstFactor

	当写入BlendOp Max时，方程式就变成了：

	Max(Src * SrcFactor, Dst * DstFactor)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;除了操作符可以变化外，SrcFactor、DstFactor、SrcFactorA、DstFactorA 这四个变量的可以选择为：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	One 	代表1，就相当于完整的一个数据
	Zero	代表0，就相当于抹去了整个数据
	SrcColor	代表当前刚通过测试的片元上的颜色(即当前物体片元)，相当于乘以当前物体片元的颜色
	SrcAlpha	代表当前刚通过测试的片元上的alpha(即当前物体片元)，相当于乘以当前物体片元的alpha
	DstColor	代表已经在缓存中的颜色，相当于乘以当前缓存颜色
	DstAlpha	代表已经在缓存中的alpha，相当于乘以当前缓存alhpa
	OneMinusSrcColor	代表缓存上的片元做了 1 - SrcColor 的操作，再相乘
	OneMinusSrcAlpha	代表缓存上的片元做了 1 - SrcAlpha 的操作，再相乘
	OneMinusDstColor	代表当前刚通过测试的片元上的颜色做了 1 - DstColor 的操作，再相乘
	OneMinusDstAlpha	代表当前刚通过测试的片元上的颜色做了 1 - DstAlpha 的操作，再相乘
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;通过操作符号的选择，以及变量因子的选择，我们可以在Blend混合中玩出很多花样来。&lt;/p&gt;

&lt;p&gt;SrcFactor、DstFactor、SrcFactorA、DstFactorA 这4个变量因子的选择和操作符的选择决定了混合后的效果，我们来看看常用的混合方法和效果：&lt;/p&gt;

&lt;h6 id=&quot;1-透明度混合blend-srcalpha-oneminussrcalpha即常用半透明物体的混合方式&quot;&gt;1， 透明度混合Blend SrcAlpha OneMinusSrcAlpha，即常用半透明物体的混合方式。&lt;/h6&gt;

&lt;p&gt;这是最常用的半透明混合，首先要保证半透明绘制的顺序比实体的要后面，所以Queue标签是必要的Tags {“Queue” = “Transparent”}。Queue标签告诉着色器此物体是透半透明物体排序。至于渲染排序Queue的前因后果将在后面的文章介绍。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/7/shader36.png&quot; alt=&quot;blend&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Blend SrcAlpha OneMinusSrcAlpha 我们来解释下，以上图为例，图中油桶是带此Shader的混合目标。&lt;/p&gt;

&lt;p&gt;当绘制油桶时，后面的实体BOX已经绘制好并且放入屏幕里了，所以ScrAlpha与油桶渲染完的图像相乘，部分区域Alpha为0即相乘后为无(颜色)，这时正好另一部分由OneMinusSrcAlpha(也就是1-ScrAlpha)为1即相乘后原色不变，两个颜色相加后就相当于油桶的透明部分叠加后面实体Box的画面，于是就形成了上面的这幅画面。&lt;/p&gt;

&lt;p&gt;反过来也是一样，当ScrAlpha为1时，源图像为不透明状态，则两个颜色在相加前最终变成了，源图像颜色+无颜色=源图像颜色，于是就有了上图中油桶覆盖实体Box的图像部分。&lt;/p&gt;

&lt;h6 id=&quot;2加白加亮叠加混合-blend-one-one即在原有的颜色上叠加屏幕颜色更加白或亮&quot;&gt;2，加白加亮叠加混合 Blend One One，即在原有的颜色上叠加屏幕颜色更加白或亮。&lt;/h6&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/7/shader37.png&quot; alt=&quot;blend&quot; /&gt;&lt;/p&gt;

&lt;p&gt;第一参数One代表本物体的颜色。第二个参数代表缓存上的颜色。两种颜色没有任何改变并相加，导致形成的图像更加亮白。这样我们就看到了一个图像加亮加白的图像。&lt;/p&gt;

&lt;h6 id=&quot;3保留原图色彩blend-one-zero即只显示自身的图像色彩不加任何其他效果&quot;&gt;3，保留原图色彩Blend One Zero，即只显示自身的图像色彩不加任何其他效果。&lt;/h6&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/7/shader38.png&quot; alt=&quot;blend&quot; /&gt;&lt;/p&gt;

&lt;p&gt;本物体颜色，加上，零，就是本物体颜色。&lt;/p&gt;

&lt;h6 id=&quot;4自我叠加加深混合blend-srccolor-zero即源图像与源图像自我叠加&quot;&gt;4，自我叠加（加深）混合Blend SrcColor Zero，即源图像与源图像自我叠加。&lt;/h6&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/7/shader39.png&quot; alt=&quot;blend&quot; /&gt;&lt;/p&gt;

&lt;p&gt;与上面相比，加深了本物体的颜色。先是本物体的颜色与本物体的颜色相乘，加深了颜色，第二个参数为零，使得缓冲中的颜色不被使用。所以形成的图像为颜色加色的图像。&lt;/p&gt;

&lt;h6 id=&quot;5目标源叠加正片叠底混合blend-dstcolor--srccolor即把目标图像和源图像叠加显示&quot;&gt;5，目标源叠加（正片叠底）混合Blend DstColor  SrcColor，即把目标图像和源图像叠加显示。&lt;/h6&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/7/shader40.png&quot; alt=&quot;blend&quot; /&gt;&lt;/p&gt;

&lt;p&gt;第一个参数，本物体颜色与缓存颜色相乘，颜色叠加。第二个参数，缓存颜色与本问题颜色相乘，颜色叠加。两种颜色相加，加亮加白。这个混合效果就如同两张图像颜色叠加后的效果。&lt;/p&gt;

&lt;h6 id=&quot;6软叠加混合blend-dstcolor--zero即把刚测试通过的图像与缓存中的图像叠加&quot;&gt;6，软叠加混合Blend DstColor  Zero，即把刚测试通过的图像与缓存中的图像叠加。&lt;/h6&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/7/shader41.png&quot; alt=&quot;blend&quot; /&gt;&lt;/p&gt;

&lt;p&gt;与前面的叠加混合效果相似，这个只做一次叠加，并不做颜色相加操作，使得图像看起来在叠加部分并没有那么亮白的突出。因为第二个参数为零，表示后面的屏幕颜色与零相乘即为零。&lt;/p&gt;

&lt;h6 id=&quot;7差值混合blendop-subblend-one-one即注重黑白通道的差值&quot;&gt;7，差值混合BlendOp Sub，Blend One One，即注重黑白通道的差值。&lt;/h6&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/7/shader42.png&quot; alt=&quot;blend&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在这个混合中使用了混合操作改变，从默认的加法改成了减法，使得两个颜色从加法变为了减法，不再是变白变亮的操作，而是反其道成为了色差的操作。&lt;/p&gt;

&lt;h6 id=&quot;除了对源片元和目标片元相乘再相加的操作还可以改变相乘后的加法操作比如减法取最大值取最小值等&quot;&gt;除了对源片元和目标片元，相乘再相加的操作，还可以改变相乘后的加法操作。比如减法，取最大值，取最小值等。&lt;/h6&gt;

&lt;h6 id=&quot;blend混合如同-photoshop-中对图层操作photoshop中每个图层都可以选择混合模式混合模式决定了该层与下层图层的混合结果而我们看到的都是是混合后的图片&quot;&gt;Blend混合如同 Photoshop 中对图层操作，Photoshop中每个图层都可以选择混合模式，混合模式决定了该层与下层图层的混合结果，而我们看到的都是是混合后的图片。&lt;/h6&gt;

&lt;h3 id=&quot;逻辑操作&quot;&gt;逻辑操作&lt;/h3&gt;

&lt;p&gt;在像素混合结束后，片元将被写入缓存中去，在写入缓冲前还会做一次逻辑操作，这是片元的最后一个操作。它作用于当前刚通过测试的片元和当前帧缓存中的数据之间，逻辑操作会在它们之间进行一次操作，最后再写入到帧缓存。&lt;/p&gt;

&lt;p&gt;由于这个过程的实现代价对于硬件来说是非常低廉的，因此很多系统都会允许这种做法。逻辑操作不再有因子，只在两个像素之间操作，操作可以选择为，异或(XOR)操作，与(AND)操作，或(OR)等。只是由于它使用的比较少，也可以由其他方式代替，因此Unity3D中并没有自定义设置逻辑操作的功能。&lt;/p&gt;

&lt;h6 id=&quot;双缓冲机制&quot;&gt;双缓冲机制&lt;/h6&gt;

&lt;p&gt;片元最后都会以像素的形式写入帧缓冲中，帧缓冲一边由GPU不断写入，一边由显示器不断输出，这会导致画面还没形成前就绘制到屏幕的情况，所以GPU通常采用双缓冲机制，即前置缓存用于呈现画面，后置缓存则继续由GPU不断写入，写入所有像素后再置换两个缓存，置换时只要置换指针地址就可以了。&lt;/p&gt;

&lt;p&gt;当整个画面绘制完成时，后置缓冲与前置缓冲进行调换，于是后置缓存成为了前置缓冲并呈现到屏幕上，原来的前置缓存成为了后置缓冲交由GPU作为帧缓冲继续绘制下一帧，这样就可以保证显示与绘制不会相互干扰。&lt;/p&gt;

&lt;h6 id=&quot;整个渲染管线已经全部呈现在这里了我们来总结一下&quot;&gt;整个渲染管线已经全部呈现在这里了，我们来总结一下。&lt;/h6&gt;

&lt;p&gt;整个渲染管线从大体上分，应用阶段，几何阶段，光栅化阶段。&lt;/p&gt;

&lt;p&gt;从应用阶段开始，数据在应用阶段被记录、筛选(或者也可以叫裁剪)、合并。在筛选、合并中，有些运用了算法来达到裁剪的目的，有些放大了颗粒度以使得加速筛选(裁剪)，有些则利用了GPU工作原理合并了渲染数据提高了GPU工作效率。&lt;/p&gt;

&lt;p&gt;几何阶段则着重于处理顶点的数据，顶点着色器是其中最为重要的一个着色器，它不但需要计算顶点在空间上的转换，还要为下一个阶段光栅化阶段做了充分的准备。在顶点着色器中，计算和记录了片元着色器需要的数据，这些数据都会被放入顶点(图元)数据中，顶点上数据会在下一个阶段经过插值计算后放入片元中，每个像素上的数据都是三角形顶点上的数据做插值后所得。&lt;/p&gt;

&lt;p&gt;光栅化阶段主要任务是将三角形面转化为实实在在的像素，并且根据顶点上的数据做插值得到片元信息，一个片元就相当于一个像素附带了很多插值过的顶点信息。片元着色器在光栅化阶段起了重要的作用，它为我们提供了可自定义计算片元颜色的可编程节点，不但如此，我们还可以根据自己的喜好抛弃(discard)某些片元。&lt;/p&gt;

&lt;p&gt;片元在片元着色器计算后还需要经过好几道测试才能最终呈现在画面上，包括判断片元前后覆盖的深度测试，可以自定义条件的模板测试，以及常用来做半透明的像素混合操作，片元只有经过这几道’关卡‘才能最终被写入帧缓存中，双缓冲机制使得GPU可以尽情的写缓存而无需关心是否会因为写了一半被呈现到画面的问题。&lt;/p&gt;

&lt;h6 id=&quot;关于渲染管线我们讲解了很多但还是有很多很多细节被忽略我们会在后面的章节中详细为大家解剖这些细节可能会根据各个图形编程接口opengl和directx背后的实现原理而来也可能是某种渲染方法和技巧其背后的原理&quot;&gt;关于渲染管线我们讲解了很多，但还是有很多很多细节被忽略，我们会在后面的章节中详细为大家解剖，这些细节可能会根据各个图形编程接口(OpenGL和DirectX)背后的实现原理而来，也可能是某种渲染方法和技巧其背后的原理。&lt;/h6&gt;

&lt;h6 id=&quot;unity3d为我们封装了很多东西使得我们能很快的上手去运用但也由于太方便导致其本身隔离了很多原理上的知识使得我们在面对底层问题时会感到迷茫本书虽然不是致力于shader的教学但也将尽最大的努力使读者们从根本上理解gpu的工作原理以及引擎背后的理论知识从而面对工作上的困难时能一眼看透问题的本质从根本上解决问题&quot;&gt;Unity3D为我们封装了很多东西，使得我们能很快的上手去运用，但也由于太方便导致其本身隔离了很多原理上的知识，使得我们在面对底层问题时会感到迷茫。本书虽然不是致力于Shader的教学，但也将尽最大的努力使读者们从根本上理解GPU的工作原理，以及引擎背后的理论知识，从而面对工作上的困难时能一眼看透问题的本质，从根本上解决问题。&lt;/h6&gt;

</content>
 </entry>
 
 <entry>
   <title>杭州景点体验概览</title>
   <link href="http://www.luzexi.com/2019/09/21/%E6%9D%AD%E5%B7%9E%E6%99%AF%E7%82%B9%E4%BD%93%E9%AA%8C%E6%A6%82%E8%A7%88"/>
   <updated>2019-09-21T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/09/21/杭州景点体验概览</id>
   <content type="html">&lt;p&gt;杭州景点体验概览&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;1.西湖–西湖音乐喷泉–三潭印月–断桥残雪–苏堤–白堤–西湖游船–曲院风荷–六和塔–花港观鱼–梅家坞–云栖竹径–龙井路与满觉陇路–孤山公园–虎跑公园–杨公堤&lt;/p&gt;

&lt;p&gt;2.灵隐寺（飞来峰）景区—-北高峰索道&lt;/p&gt;

&lt;p&gt;3.宋城景区 – 宋城千古情演出&lt;/p&gt;

&lt;p&gt;4.杭州动物园 – 有点小，需要自带食物，有点旧，便宜。&lt;/p&gt;

&lt;p&gt;5.吴山广场，河坊街 – 小吃很多，有手工艺品，街道晚上夜景好&lt;/p&gt;

&lt;p&gt;6.雷峰塔&lt;/p&gt;

&lt;p&gt;7.杭州长乔极地海洋公园 – 好，贵，大&lt;/p&gt;

&lt;p&gt;8.杭州野生动物世界 – 良好，停车难，略贵，不大&lt;/p&gt;

&lt;p&gt;9.西溪国家湿地公园 – 差，管理无章法，太大以至于找不到重点&lt;/p&gt;

&lt;p&gt;10.九溪 – 周末人很多，停车很难，最好打的去，自带食物。&lt;/p&gt;

&lt;p&gt;11.南宋御街 – 晚上好玩，小吃，手工艺品，街景，类似于河坊街。&lt;/p&gt;

&lt;p&gt;12.杭州乐园&lt;/p&gt;

&lt;p&gt;13.太子湾公园 – 看花花草草，风景很美，可野餐，最好打车。&lt;/p&gt;

&lt;p&gt;14.千岛湖中心湖区–千岛湖景区–千岛湖森林氧吧–千岛湖东南湖区景区–千岛湖游船&lt;/p&gt;

&lt;p&gt;16.大明山景区&lt;/p&gt;

&lt;p&gt;17.杭州烂苹果乐园 – 游乐园&lt;/p&gt;

&lt;p&gt;18.杭州海底世界 – 挺大的，值得二刷，人多不值得，有点小贵，自带食物。&lt;/p&gt;

&lt;p&gt;19.浙西大峡谷&lt;/p&gt;

&lt;p&gt;20.钱塘江观赏堤坝&lt;/p&gt;

&lt;p&gt;21.浙江省科技馆，西湖文化广场 – 博物馆类型的地方，增长知识，长见识，人太多，有食堂。&lt;/p&gt;

&lt;p&gt;22.杭州浪浪浪水公园&lt;/p&gt;

&lt;p&gt;23.杭州Hello Kitty乐园 – 还不错，小孩子喜欢，打折季去便宜不少。&lt;/p&gt;

&lt;p&gt;24.桐庐县雅鲁激流探险漂流—-临安市龙井峡漂流—-淳安县九龙溪漂流—-桐庐县虎啸峡激流探险漂流—-余杭区双溪漂流景区&lt;/p&gt;

&lt;p&gt;25.桐庐县瑶琳仙境，垂云通天河景区&lt;/p&gt;

&lt;p&gt;26.杭州DO都城，亚洲最大的少年儿童体验类教育场馆&lt;/p&gt;

&lt;p&gt;27.桐庐县大奇山国家森林公园&lt;/p&gt;

&lt;p&gt;28.临安太湖源&lt;/p&gt;

&lt;p&gt;29.良渚，彻天彻地童玩中心，让孩子训练协调能力，培养冒险精神&lt;/p&gt;

&lt;p&gt;30.江干区江和美海洋公园 – 没啥好玩的，体验差。&lt;/p&gt;

&lt;p&gt;31.临安大明山景区&lt;/p&gt;

&lt;p&gt;32.慈溪杭州湾国家湿地公园&lt;/p&gt;

&lt;p&gt;33.胡雪岩故居 – 看古代的房子，体验不好&lt;/p&gt;

&lt;p&gt;34.京杭大运河，余杭塘栖 – 体验不错，有博物馆，也有河，有坐船，有吃的有喝的&lt;/p&gt;

&lt;p&gt;35.杭州国际博览中心，萧山区&lt;/p&gt;

&lt;p&gt;36.多乐岛蹦床公园杭州馆，江干区&lt;/p&gt;

&lt;p&gt;37.桐庐县大奇山疯狂森林主题乐园，冒险探险&lt;/p&gt;

&lt;p&gt;38.城西银泰，娱乐场–滑冰–玩具店 – 逛买逛买&lt;/p&gt;

&lt;p&gt;39.浙江大学紫金港校区 – 好风景好环境&lt;/p&gt;

&lt;p&gt;40.塘西古镇  – 逛吃逛吃，小&lt;/p&gt;

&lt;p&gt;41.杭州北高峰 – 交通不方便&lt;/p&gt;

&lt;p&gt;42.良储遗址 – 大，美，便宜，自带食物，能游览大半天&lt;/p&gt;

&lt;p&gt;43.井山湖农耕文化公园(塘子堰，中小学生实践基地) – 免费，向日葵基地，大片草地，搭帐篷，风景美，自带食物&lt;/p&gt;

&lt;p&gt;44.宁波方特 – 好几个人说很好玩&lt;/p&gt;

&lt;p&gt;45.径山花海 – 余杭区，开车上高速30分钟，花园挺大的，需要自带食物，我们去的时候是过年，没有花，免费进，可以搭帐篷，是当地的公园。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>上海景点体验概览</title>
   <link href="http://www.luzexi.com/2019/09/21/%E4%B8%8A%E6%B5%B7%E6%99%AF%E7%82%B9%E4%BD%93%E9%AA%8C%E6%A6%82%E8%A7%88"/>
   <updated>2019-09-21T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/09/21/上海景点体验概览</id>
   <content type="html">&lt;p&gt;上海景点体验概览&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;1.理工大学  大草坪，小溪流，小森林，运动场，食堂，能玩半天。 !!&lt;/p&gt;

&lt;p&gt;2.理工大学边上商场游乐场  儿童游乐设施，能玩半天 !!&lt;/p&gt;

&lt;p&gt;3.上海植物园  各类植物，大众活动区，能野餐，有风景，有花有草，有大草坪，玩半天-1天。 !!&lt;/p&gt;

&lt;p&gt;4.上海动物园  自带食物和水。各类动物，游乐设施，大草坪，能玩一天。 !!&lt;/p&gt;

&lt;p&gt;5.上海野生动物园  自带食物和水。各类动物，野生动物，动物喂食(喂鸽子，喂鱼比较人性化，其他没试过)，游乐设施，能玩一整天。 !!&lt;/p&gt;

&lt;p&gt;6.上海长风海洋公园  自带食物和水。各类海洋动物，能玩半天。 !!&lt;/p&gt;

&lt;p&gt;7.宜山路博库书城周边包括光启城  各类书籍，玩具，旁边有商场，有好东西吃，有逛有吃。对面有85度c，停车到对面写字楼便宜。里面有小孩子的补习班，兴趣班。 !!&lt;/p&gt;

&lt;p&gt;8.南京路步行街  商场，人群多，路开阔，有玩有吃，就是贵。 !!&lt;/p&gt;

&lt;p&gt;9.东方明珠塔  175元/人(携程），自带食物和水。263米主观光层 + 259米透明悬空走廊 + 78米“更上·海”环动多媒体秀 + 0米上海城市历史发展陈列馆 + 0米魔法光影欢乐园。能玩半天-1天。可订旋转餐厅自助餐，需要提前1个工作日订。电梯排队人多，很多时间会浪费在排队进电梯上。 !!&lt;/p&gt;

&lt;p&gt;10.上海海洋水族馆&lt;/p&gt;

&lt;p&gt;11.上海科技馆  不错的科学知识普及地点 !!&lt;/p&gt;

&lt;p&gt;12.上海博物馆  免费，但人多要排队，11点半左右去人最少，因为出来的人多了。空调很冷，因为要给文物降温。自带食物和水。&lt;/p&gt;

&lt;p&gt;13.上海自然博物馆&lt;/p&gt;

&lt;p&gt;14.中国航海博物馆&lt;/p&gt;

&lt;p&gt;15.上海汽车博物馆&lt;/p&gt;

&lt;p&gt;16.上海电影博物馆&lt;/p&gt;

&lt;p&gt;17.银行博物馆&lt;/p&gt;

&lt;p&gt;18.宋庆龄故居纪念馆&lt;/p&gt;

&lt;p&gt;19.上海邮政博物馆&lt;/p&gt;

&lt;p&gt;20.上海外滩美术馆&lt;/p&gt;

&lt;p&gt;21.上海当代艺术馆&lt;/p&gt;

&lt;p&gt;22.上海当代艺术博物馆&lt;/p&gt;

&lt;p&gt;23.上海琉璃艺术博物馆&lt;/p&gt;

&lt;p&gt;24.BUS TOUR 上海观光游 自带食物和水。50/人(携程)。携程上购买半价，需提前1个工作日购买，1.3米以下儿童免费，能够观光一整天。&lt;/p&gt;

&lt;p&gt;25.上海儿童博物馆&lt;/p&gt;

&lt;p&gt;26.上海工艺美术博物馆&lt;/p&gt;

&lt;p&gt;27.龙美术馆西岸馆&lt;/p&gt;

&lt;p&gt;28.钟书阁&lt;/p&gt;

&lt;p&gt;29.上海环球金融中心观光天阁&lt;/p&gt;

&lt;p&gt;30.上海天文博物馆&lt;/p&gt;

&lt;p&gt;31.上海图书馆&lt;/p&gt;

&lt;p&gt;32.chi K11美术馆&lt;/p&gt;

&lt;p&gt;33.上海老相机制造博物馆&lt;/p&gt;

&lt;p&gt;34.外滩历史纪念馆&lt;/p&gt;

&lt;p&gt;35.上海电信博物馆&lt;/p&gt;

&lt;p&gt;36.打字机博物馆&lt;/p&gt;

&lt;p&gt;37.上海观复博物馆&lt;/p&gt;

&lt;p&gt;38.上海少年儿童图书馆&lt;/p&gt;

&lt;p&gt;39.复旦大学博物馆&lt;/p&gt;

&lt;p&gt;40.多伦现代美术馆&lt;/p&gt;

&lt;p&gt;41.星空错觉艺术馆&lt;/p&gt;

&lt;p&gt;42.闵行区博物馆&lt;/p&gt;

&lt;p&gt;43.浦东图书馆少儿馆&lt;/p&gt;

&lt;p&gt;44.上海浦东展览馆&lt;/p&gt;

&lt;p&gt;45.上海历史收藏馆&lt;/p&gt;

&lt;p&gt;46.科学节能展示馆&lt;/p&gt;

&lt;p&gt;47.天空画画水族馆&lt;/p&gt;

&lt;p&gt;48.民生现代美术馆&lt;/p&gt;

&lt;p&gt;49.上海木文化博物馆&lt;/p&gt;

&lt;p&gt;50.上海杜莎夫人蜡像馆&lt;/p&gt;

&lt;p&gt;51.朱家角古镇景区&lt;/p&gt;

&lt;p&gt;52.宜山路中山西路，宜家家居。可购物，可逛街，可吃饭。 //早上10点才开门，早餐可以在里面吃。里面路很长，有上下两层，能逛半天。中午吃饭人多，价格适中。 !!&lt;/p&gt;

&lt;p&gt;53.安徒生童话乐园  //孩子的乐园，自带食物，门票贵的，一大一小200元。 !!&lt;/p&gt;

&lt;p&gt;54.锦江乐园  //孩子的乐园100元多玩几次，小吃多，吃的贵。可以自带食物。 !!&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第七章，渲染管线与图形学(二) - 渲染管线1</title>
   <link href="http://www.luzexi.com/2019/09/21/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-%E6%B8%B2%E6%9F%93%E7%AE%A1%E7%BA%BF%E4%B8%8E%E5%9B%BE%E5%BD%A2%E5%AD%A64"/>
   <updated>2019-09-21T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/09/21/Unity3D高级编程之进阶主程-渲染管线与图形学4</id>
   <content type="html">&lt;p&gt;编程的世界里其实是很纯粹的，说起来似乎没有什么高深的技术，无非就是进程、线程、内存、硬盘、CPU。随着现代编程技术的发展，很多时候引擎或者框架帮我们屏蔽了很多底层上的接口调用和引擎的业务逻辑使得我们程序员可以将所有精力放在应用层的业务架构和逻辑，这是符合社会进步规律的，我们需要站在巨人的肩膀上才能走得比巨人更快更远。Unity3D就是帮助我们快速建立业务架构的好引擎，有了Unity3D我们才能在较低门槛的情况下快速制作出自己的想法和创意。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;除了进程、线程、内存、硬盘、CPU这些通常我们程序员在编程时要考虑的因素外，对于我们前端程序员来说还多了一个GPU的技术范畴，因此我们需要学习和考虑的问题比其他程序员会更加多一些也会更加高阶一些，Unity3D虽然封装了所有引擎需要的GPU接口但我们还是必须要了解渲染管线是如何处理的，它是如何将渲染数据显示在屏幕上的，底层的知识和原理是打通任督二脉比较关键的一步。&lt;/p&gt;

&lt;h3 id=&quot;opengldirectx究竟是什么&quot;&gt;OpenGL、DirectX究竟是什么&lt;/h3&gt;

&lt;p&gt;OpenGL 和 DirectX 其实是一回事，它们都是图形渲染的应用程序编程接口，它们都是一种可以对图形硬件设备特性进行调用的软件库。它们的区别只是服务的系统可能有所不同，接口的命名方式也有所不同，DirectX专门服务于微软开发出来的系统，比如Windows和Xbox，他们本身分别由不同的两个组织开发出来。&lt;/p&gt;

&lt;p&gt;为什么会是两个不同群体开发出来的两套差不多功能的软件，并且还同时运行在现有的世界中呢？OpenGL是由SGI(Silicon Graphics 美国硅图公司)开发的，而DirectX是由微软开发的，由于市场竞争的关系两家公司做了同样的事，最后导致现在的局面。这种局面也很正常，打个比方，你会用微信去聊天也有时会用旺旺去聊天，都是聊天只是聊天的场景不同心情和目标有点不同而已，图形接口也是一样，不只是OpenGL和DirectX还有专门为苹果系统服务的Metal。从现在的局面来看，我们可以想象的到，在当初还没有形成统一的硬件渲染接口时，各家公司的图形编程接口有多混乱情况有多复杂，对标准统一接口的标准竞争有多激烈。在这种严峻的环境下才使得两家公司为了各自的利益，一直在不断维护和升级着各自的驱动接口直到今天。&lt;/p&gt;

&lt;p&gt;幸运的是 Unity3D 已经帮我们封装好了 OpenGL 和 DirectX 的接口，我们无需关心到底是调用 OpenGL 还是 DirectX。我们这里会以OpenGL为例来讲解渲染过程，DirectX也是相似的原理与过程。&lt;/p&gt;

&lt;h6 id=&quot;opengl究竟处在哪个位置&quot;&gt;OpenGL究竟处在哪个位置&lt;/h6&gt;

&lt;p&gt;Unity3D通过调用 OpenGL 的图形接口来渲染图像，OpenGL 定义了各种标准接口就是为了让像 Unity3D 这样的应用程序在面对不同类型的显卡硬件时可以不必慌张，也由于 OpenGL 的存在Unity3D完全不需要去关心硬件到底是哪个厂家生产的，以及它们的驱动是什么。与其说OpenGL在标准接口中适配了硬件厂商的驱动程序，不如说硬件厂商的驱动程序适配了OpenGL，事实上确实是这样。&lt;/p&gt;

&lt;p&gt;当Unity3D渲染调用时会去设置OpenGL的渲染状态，OpenGL就会去检查显卡驱动程序里是否有该功能，有就会调用没有就不调用，那些比较特殊的渲染功能接口有些底端的硬件上并没有该功能。显卡驱动与OpenGL不同的是，显卡驱动是在硬件之上的专门为硬件服务的程序，它是用来将指令翻译成机器语言并调用硬件的那个程序，而OpenGL则调用的只是显卡驱动。&lt;/p&gt;

&lt;p&gt;显然OpenGL是在驱动程序之上的应用程序接口，我们可以把它看做是适配了很多不同驱动程序的中间件。如下图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/7/render-pipeline1.png&quot; alt=&quot;opengl&quot; /&gt;&lt;/p&gt;

&lt;p&gt;上图指出了OpenGL所处的位置，图形引擎Unity3D调用OpenGL图形接口，告诉OpenGL某个模型数据需要渲染或者说某个渲染状态需要要设置，再由OpenGL发送指令给显卡驱动程序，显卡驱动程序将指令翻译为机器码后，将指令机器码发送到GPU，最后GPU根据指令做相应的处理。我们看到这个过程中显卡驱动程序只是做了传递指令消息的工作，指令从OpenGL那里发起到GPU接收到指令，显卡驱动起到了翻译的工作。&lt;/p&gt;

&lt;p&gt;当然这里GPU不只会处理一次，OpenGL会通过显卡驱动发送很多次指令给GPU，让它处理一连串的操作，每次指令都有可能不一样，经过一系列的处理过程后，最终形成了一张屏幕大小的图像存放在缓存中，这时GPU才向屏幕输出最终画面。&lt;/p&gt;

&lt;p&gt;下面我们就来详细介绍一下这一整条渲染管线的处理过程。&lt;/p&gt;

&lt;h3 id=&quot;究竟渲染管线是什么&quot;&gt;究竟渲染管线是什么？&lt;/h3&gt;

&lt;p&gt;上面所说的 OpenGL 通过驱动程序向 GPU 发送很多个指令，这一系列指令加起来才形成一整个渲染画面。&lt;/p&gt;

&lt;p&gt;渲染管线就是指令中完成一个绘制命令(drawcall)的流水线。这条流水线中有很多个环节，每个环节都自己干自己的事，就像工厂里的流水作业一样，每个节点的工人都会拧属于自己的螺丝，完全不会去管前面节点发生了什么事情。在现代GPU中也会做些流程上的优化，比如某种情况下跳过某些节点的以节省开销，但节点还是自顾自的工作，这部分会在后面的文章中提到。&lt;/p&gt;

&lt;p&gt;我们可以描述说渲染管线是一系列数据处理的过程，这个过程最终的目的是将应用程序的数据经过计算最终输出到帧缓存上最后输出到屏幕。渲染管线从接受到渲染命令后开始，分几个阶段处理了这些数据，这几个阶段分别是应用阶段，几何阶段，光栅化阶段，经过这几个阶段处理最终输出到屏幕上。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺图片
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;图中每个阶段都有各自细分的流程，我们来一一讲述下。&lt;/p&gt;

&lt;h6 id=&quot;应用阶段&quot;&gt;应用阶段&lt;/h6&gt;

&lt;p&gt;应用阶段就是我们执行Unity3D引擎和业务逻辑的过程，在逻辑代码执行过程中，我们实例化了很多个模型或者UI（UGUI的UI也是网格，跟渲染场景中的3d模型从根本上是没有区别的），这些模型有贴图，有材质球，有网格，对于引擎来说，在这个阶段代码执行完毕后它知道了哪些模型需要被渲染，有哪些光源存在，摄像头的位置大小，总的来说这个阶段是准备渲染数据的阶段，为调用渲染准备。&lt;/p&gt;

&lt;p&gt;引擎除了知道有哪些东西(数据)需要被提交到GPU渲染外，在提交前还会对这些数据做很多优化工作，从而省去很多不必要的渲染工作提高性能。优化工作有很多，前面的章节中我们也讲到了很多很多关于在逻辑端上优化的工作，这里我们来重点说一下引擎的‘剔除’优化部分。&lt;/p&gt;

&lt;p&gt;Unity3D引擎会对不需要渲染的物体进行剔除，原本这是在GPU中做的事搬到了CPU上做。为什么要搬到CPU上做呢？因为在引擎端掌握的是第一手数据信息，引擎大可以从粗颗粒上下手做剔除工作，这样GPU的负担就会减轻很多，倘若所有的网格都放到GPU去剔除，会浪费很多算力同时降低了渲染的功效。&lt;/p&gt;

&lt;p&gt;引擎在粗颗粒上是怎么剔除的呢，引擎当然不会像GPU那样去计算每个三角面的有效性，而是去计算比较粗的模型颗粒，即以包围盒形式判断模型是否需要被剔除。&lt;/p&gt;

&lt;h6 id=&quot;引擎会计算一个模型的包围盒包围盒信息存放在unity3d的meshbounds变量中这个包围盒是一个长方体我们常称它为aabb包围盒即一个顶点与长宽高四个变量组成我们可以理解为包围盒是个立方体有8个顶点这8个顶点决定了这个模型是否会被剔除在判断模型是否有被需要渲染的可能时只要有一个顶点在摄像机可视范围内锥视体内或正交范围就不会被剔除否则将被无情的抛弃&quot;&gt;引擎会计算一个模型的包围盒，包围盒信息存放在Unity3D的Mesh.bounds变量中，这个包围盒是一个长方体我们常称它为AABB包围盒，即一个顶点与长宽高四个变量组成。我们可以理解为包围盒是个立方体有8个顶点，这8个顶点决定了这个模型是否会被剔除，在判断模型是否有被需要渲染的可能时只要有一个顶点在摄像机可视范围内(锥视体内或正交范围)，就不会被剔除，否则将被无情的抛弃。&lt;/h6&gt;

&lt;p&gt;引擎通过这种快速的判断包围盒与锥视体的关系来剔除不需要渲染的物体，以达到对模型粗颗粒的渲染优化。除此之外，对粗颗粒的剔除判断还有 occlusion culling 即遮挡剔除，也是属于应用阶段的优化剔除，它其实也是属于业务逻辑层的优化方案并不是所有项目都会使用，而通常只在第一人称视角的游戏上使用这种剔除方式，通过对场景的离线计算来确定每个点所看能看到的物体的列表数据，将这份数据保存下来，当角色在此场景中时根据当前点的位置去寻找已经计算好的可见物体列表既然展示物体，这样就不需要实时去判定物体是否可见，也能轻松得到可见物体的列表，即在离线时在该位置被计算为被遮挡的物体不会进入渲染队列。&lt;/p&gt;

&lt;p&gt;应用阶段的最后时刻就是向GPU提交需要渲染的数据，通常数据会被拷贝到显存中，接着设置渲染参数，最后调用渲染接口。在PC端中显存的位置是最接近GPU的内存设备，将数据拷贝到显存中会加速GPU的工作效率，但在移动端里并没有显存，安卓和IOS的架构决定了它们只能用内存来为GPU提供服务，因此在手机端中没有拷贝数据到显存的这个过程，它们使用的都是同一个物理内存地址，除非我们需要读写这块内存的内容才会将它们另外复制一份以条件CPU与GPU之间的协作(三重缓存机制)。&lt;/p&gt;

&lt;h6 id=&quot;什么是渲染状态&quot;&gt;什么是渲染状态&lt;/h6&gt;

&lt;p&gt;很多人都很困惑，其实就是一连串的开关或方法以及方法的地址指向。比如要不要开启混合，使用哪张纹理，使用哪个顶点着色器，使用哪个片元着色器，剔除背面还是剔除前面亦或都不剔除，使用哪些光源等等。通俗的来说，设置渲染状态，就是设置并决定接下来的网格如何渲染，有了渲染的具体方法，至于具体的渲染工作则是由GPU来执行。&lt;/p&gt;

&lt;p&gt;有了渲染的具体方法，就要调用渲染的具体对象，这就是渲染调用做的工作。实际上 Draw call 就是一个渲染指令，发起方是CPU接收方是GPU，这个指令仅仅指向了一连串的图元（即点,线,面，我们可以理解为网格被拆分后的状态），并不会包含其他任何材质信息。每个 Draw call 前面都伴随着一连串渲染状态的设置，因此整个渲染命令队列中都是渲染指令与渲染状态交替出现。&lt;/p&gt;

&lt;p&gt;为什么要有渲染命令队列呢？因为CPU和GPU相当于是两个大脑，它们是分离的就像两个线程那样如果没有很好的协调机制，它们无法正常梳理自己的工作。命令缓冲队列就是用来协调CPU与GPU的，CPU向命令缓冲队列中推入指令，GPU则从中取得指令并处理。这个命令缓冲队列成了CPU与GPU的关系纽带，这条关系纽带(命令缓冲队列)很好的连接了CPU与GPU，但也成了它们之间交互的瓶颈，即我们通常所说的 Draw call 太多时GPU的工作效率比较差。其根本原因就是 CPU 发送了渲染指令后，只是空转的等待GPU完成渲染操作。&lt;/p&gt;

&lt;h6 id=&quot;几何阶段&quot;&gt;几何阶段&lt;/h6&gt;

&lt;p&gt;在几何阶段前引擎已经准备好了要渲染的数据，同时向GPU发送了渲染状态的设置命令和渲染调用命令，接下来的工作就完全属于GPU了。首先进入的是几何阶段的工作，几何阶段的工作目标是将需要绘制的图元(三角形、点、线、面)转化到屏幕空间中，因此它也决定了哪些图元可以绘制以及怎么绘制。图元即点、线、面，我们可以理解为网格的拆分状态，是着色器中的基础数据，在几何阶段作用最大。&lt;/p&gt;

&lt;h6 id=&quot;几何阶段会经过几个处理过程按顺序排列为顶点着色器曲面细分着色器细分计算着色器几何着色器图元装配裁减如下图所示&quot;&gt;几何阶段会经过几个处理过程，按顺序排列为，顶点着色器、曲面细分着色器、细分计算着色器、几何着色器、图元装配、裁减。如下图所示：&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	缺图
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;图中几何阶段分拆成了，顶点着色器、曲面细分着色器、细分计算着色器、几何着色器、图元装配、裁剪6个节点。其中顶点着色器会对每个顶点进行逐一的计算，OpenGL会调用我们在渲染状态设置时的顶点处理函数来处理顶点数据。&lt;/p&gt;

&lt;p&gt;顶点处理函数就是我们可编程的部分，它可以很简单只将数据传递到下一个节点，也可以用变换矩阵的方式来计算顶点在投影空间的位置，或者通过光照公式计算来得到顶点的颜色，或者计算并准备其他下一个阶段需要的信息。&lt;/p&gt;

&lt;p&gt;曲面细分着色器、细分计算着色器、几何着色器，这三个着色器是非必须着色器，很多手机设备上的GPU并没有这几个功能。细分着色器包括曲面细分着色器和细分计算着色器会使用面片来描述一个物体的形状，并且增加顶点和面片数量使得模型外观更加平顺。几何着色器则允许自定义增加和创建新的图元，这是唯一一个能自定义增加新图元的着色器。&lt;/p&gt;

&lt;p&gt;前面几个着色器节点处理的都是顶点数据，到了图元装配节点，它将这些顶点与相关的几何图元之间组织起来为下一步的裁剪工作做准备。&lt;/p&gt;

&lt;h6 id=&quot;经历过前面几个阶段的变换特别是在顶点着色器中将顶点从模型空间转换到投影空间这个转换过程为从模型空间到世界空间再到视口空间再到投影空间unity3d的shader中常见的unity_mvp宏定义变量就是坐标空间转换的变化矩阵它在渲染前就已经计算好并存储起来不需要再次计算在转换了坐标空间之后会经硬件上的透视除法得到归一化的设备坐标归一化的设备坐标会使裁剪更加容易不仅如此还对后面的深度缓冲和测试有很大的帮助&quot;&gt;经历过前面几个阶段的变换，特别是在顶点着色器中将顶点从模型空间转换到投影空间，这个转换过程为从模型空间到世界空间再到视口空间再到投影空间，Unity3D的Shader中常见的UNITY_MVP宏定义变量就是坐标空间转换的变化矩阵，它在渲染前就已经计算好并存储起来不需要再次计算。在转换了坐标空间之后会经硬件上的透视除法得到归一化的设备坐标，归一化的设备坐标会使裁剪更加容易，不仅如此还对后面的深度缓冲和测试有很大的帮助。&lt;/h6&gt;

&lt;p&gt;其中归一化后的设备坐标(Normalized Device Coordinates, NDC)可以看做是一个矩形内的坐标体系，这个经转化后的坐标体系是个限制在立方体内的坐标体系，所有在这个坐标体系内的顶点的坐标都不会超过1到-1之间，无论x、y、z轴。&lt;/p&gt;

&lt;p&gt;为了能更好的理解经过空间转换后的顶点在后面几个阶段上应用的数据，有必要在这里来理解一下空间坐标系转换前后的样子，如下图：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺图1

	缺图2
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述图1中我们看到原本在视锥体上的物体，在经过空间矩阵转换后，视口从锥体变为了立方体，而原本在视锥体中的物体则从长方体变成了锥体，这是空间坐标系转化后的结果。其中需要特别注意的是原本在视锥体坐标系上向前方向(forward)的X坐标轴变为了Z轴方向，平面由x、y组成二维坐标。&lt;/p&gt;

&lt;h6 id=&quot;相当于xy坐标成为了可以映射到屏幕上的相对坐标z坐标则被用来作为离视口有多远的数值参考这是因为归一化后的ndc坐标系与原本视锥体坐标系相比其z轴方向发生了翻转&quot;&gt;相当于，x、y坐标成为了可以映射到屏幕上的相对坐标，z坐标则被用来作为离视口有多远的数值参考，这是因为归一化后的NDC坐标系与原本视锥体坐标系相比其Z轴方向发生了翻转。&lt;/h6&gt;

&lt;p&gt;归一化坐标让坐标范围固定在1到-1之间，使得后续对图元数据的处理变得更为简单。不过归一化坐标范围在OpenGL和DirectX上标记也有所不同，在OpenGL上x、y、z坐标范围在[-1,1]之间，而在DirectX上则是[0,1]之间，但这并不影响最终在屏幕上的表达，只是规则不同而已。最终他们都会进行简单的线性变换映射到屏幕的平面矩形范围内。在屏幕映射时，OpenGL和DirectX两者的也有所差异，OpenGL以左下角为(0,0)点，而DirectX则以左上角为(0,0)点，显然这不统一的做法是由于两个商家的竞争而故意造成的，不管怎样差异已经存在了我们只能小心留意。&lt;/p&gt;

&lt;p&gt;说了这么多就是为了更好的理解几何阶段最后一步裁剪。我们将顶点转化到了归一化的坐标空间后，裁剪就容易多了。通过图元装配，有了线段和三角形数据，裁剪就可以开始了。&lt;/p&gt;

&lt;p&gt;一个三角是否需要被裁剪，由归一化后的坐标判断，它或许完全在范围内，或许完全在范围外，或许部分在里面部分在外面。倘若三角形完全在可视的长方体范围内的则数据会被继续传递下去，完全在范围外的则被剔除掉不再进入到后面的阶段，若是部分在视野内则需要进一步做切割处理，把在范围外的部分剔除掉并在边界处生成新的顶点来连接没有被剔除的顶点。如下图：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺图
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;图中一个完整的三角形两个顶点在范围内，一个顶点在范围外，剪切后在边界上增加了两个顶点，再由这两个顶点与原来的三角形两个顶点生成2个不同的三角形。另一种情况相对比较简单，即一个顶点在范围里，两个顶点在范围外，剪切后形成新的两个顶点与范围内的顶点结合后替换了原来的三角形。&lt;/p&gt;

&lt;p&gt;我们来分析下那种复杂的情况，因为经过裁剪后原来的三角形由3个顶点变成了4个顶点，成为了4边形，所以需要对这个四边形进行切割。切割的方法其实很简单，选一个新增的顶点与原本的两个顶点替换原来的三角形，另一个新增的顶点与前一个新增的顶点再加上一个旧顶点(这个旧顶点一定是剪切后新增的这个顶点的线段里的)形成新的三角形。如下图所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;缺图
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;不仅如此，裁剪不仅仅是视口的裁剪，还有会有背面裁剪(Back-Face Culling)，即剔除面朝视口反方向的面片，将在后面的章节中详细讲述。&lt;/p&gt;

&lt;p&gt;至此所有几何阶段的操作都结束了，总体来说几何阶段处理的是顶点，以及计算和准备下一个阶段需要用到的数据。&lt;/p&gt;

&lt;h6 id=&quot;光栅化阶段&quot;&gt;光栅化阶段&lt;/h6&gt;

&lt;p&gt;光栅化阶段分为三个节点，光栅化、片元着色器、逐片元操作。其中光栅化又可以分成，三角形设置、三角形遍历两个节点。&lt;/p&gt;

&lt;p&gt;三角形设置即Triangle Setup。前面阶段都是空间意义上的顶点和三角形，到了光栅化阶段我们更加需要的是屏幕上的像素，于是三角形设置可以认为是将所有三角形都铺在屏幕坐标平面上，这样就知道了三角形面片在屏幕上的情况，三角形面片会以三条线的边界形式来表达面片的覆盖面积。其实覆盖面积面积并不是关键，因为屏幕中展示的画面都是以像素为单位计算的，因此一个三角形覆盖哪些像素需要依靠扫描变换(Scan Conversion)得到，这个像素扫描阶段就是三角形遍历(Triangle Traversal)的环节。如下图：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺图
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;图中，在三角形遍历节点中像素依据三角形三条边计算得到像素覆盖范围，再通过三个顶点中信息的插值获得每个像素中需要具备的信息，这些信息包括像素坐标，苏像素深度，像素颜色，像素法线，像素纹理坐标等都是从三个顶点上的信息经过插值得到。&lt;/p&gt;

&lt;h6 id=&quot;经过光栅化过程我们得到了三角形覆盖的像素上的信息我们称这些像素为片元每个片元包含了经过三个顶点中的信息插值后的信息接着这个片元被传递到下一个阶段即片元着色器&quot;&gt;经过光栅化过程，我们得到了三角形覆盖的像素上的信息，我们称这些像素为片元，每个片元包含了经过三个顶点中的信息插值后的信息。接着这个片元被传递到下一个阶段即片元着色器。&lt;/h6&gt;

&lt;p&gt;片元着色器(Fragment Shader)就如字面意思那样，是处理片元的地方，就是我们上面介绍的光栅化后三角覆盖的像素。在现代的渲染管线上它已经被设计成可编程的阶段，我们在这里可以编写很多技巧来改变片元的颜色，或者也可以直接丢弃该片元(discard 或者 clip)。&lt;/p&gt;

&lt;p&gt;每个片元就相当于一个像素，只是比起像素，片元装载了很多的信息，这些信息都是通过前面三角形遍历时对三个顶点中的信息插值得到的。经过片元着色器的处理，也就是我们编写的片元着色程序的处理后，最终输出的也是片元，我们通常都在片元着色器中计算改变片元的颜色，最终得到一个我们想要的输出到屏幕的片元。&lt;/p&gt;

&lt;h6 id=&quot;这里有一个重点每次片元着色器处理片元时都只是单个片元只是处理片元的单元有很多很多个他们相互独立对于片元着色器来说它并不知道相邻的片元是什么样因此每个片元在处理时无法得到邻近的片元的信息&quot;&gt;这里有一个重点，每次片元着色器处理片元时都只是单个片元(只是处理片元的单元有很多很多个，他们相互独立)，对于片元着色器来说它并不知道相邻的片元是什么样，因此每个片元在处理时无法得到邻近的片元的信息。&lt;/h6&gt;

&lt;p&gt;得不到邻近的片元信息并不代表我们就没有方法让片元受到邻近的片元影响，虽然每次片元着色器传入的和处理的都是单个片元，但GPU在跑片元着色器时并不是只跑一个片元着色器，而是将其组织成2x2的一组片元块同时跑4个片元着色器。如下图：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺图
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上图中，描绘了4个片元组成的片元组，以及偏导数函数对它们的计算过程。我们可以通过ddx和ddy这两个偏导数函数来求得邻近片元的差值。偏导数函数可以用于片元着色器中的任何变量。对于向量和矩阵类型的变量，该函数会计算变量的每一个元素的偏导数。偏导数函数是纹理Mipmaps实现的基础，我们将在后面的章节中详细讲解。&lt;/p&gt;

&lt;p&gt;除了计算片元的颜色，我们还可以在片元着色器中丢弃某些片元(discard 或者 clip)，我们常说的Alpha Test就是一个用丢弃片元函数来实现的效果，我们将在后面的章节中详细讲解 Alpha Test的原理与利弊。&lt;/p&gt;

&lt;h6 id=&quot;片元着色器和顶点着色器是我们在着色器编程时最重要的两个节点如果我们想要更通俗简单的理解顶点着色器和片元着色器的区别的话可以认为顶点着色器包括细分着色器和几何着色器决定了一个三角形应该放在屏幕的什么位置而片元着色器则用这个三角形面片计算三角形范围内的像素拥有什么样的颜色&quot;&gt;片元着色器和顶点着色器是我们在着色器编程时最重要的两个节点，如果我们想要更通俗简单的理解顶点着色器和片元着色器的区别的话，可以认为：顶点着色器(包括细分着色器和几何着色器)决定了一个三角形应该放在屏幕的什么位置，而片元着色器则用这个三角形面片计算三角形范围内的像素拥有什么样的颜色。&lt;/h6&gt;

&lt;p&gt;片元着色器输出片元后，进入了逐片元操作阶段，也是渲染管线的最后一步。&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>思路探讨(二十八) 如何应对彷徨</title>
   <link href="http://www.luzexi.com/2019/09/09/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A828"/>
   <updated>2019-09-09T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/09/09/思路探讨28</id>
   <content type="html">&lt;p&gt;人时不时的总会彷徨的，每个人都一样。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;经常性的会迷失方向，看不清未来。无论我们前面有过多少清晰的认识，或者说多少深刻的觉悟，总是会在某个时刻感受到无法言语的痛苦。&lt;/p&gt;

&lt;p&gt;痛苦从何而来？是前面未知的道路吗，有可能，是疲劳吗，也有，是周遭向我们袭来的负能量吗，肯定有的，是因为没有计划使得自己总感觉是在胡乱的东奔西跑吗，是的。&lt;/p&gt;

&lt;p&gt;精神上和身体上的疲惫，说实在的无论怎么样都会累的，疲劳了就休息一下么，可能太久没有回家了，好想家，那就回家，感受下家的温暖。&lt;/p&gt;

&lt;p&gt;不工作也会累，工作也会累，说实在的其实很多时候精神力是要运作的，我们怎么运作有限的精力和时间，让最饱满的精力用在最重要的事情上，让零星的时间能变得更加充实，这是门学问，这一年多时间我一直在学习时间管理和自律，现在顿悟到精力管理也很重要。&lt;/p&gt;

&lt;p&gt;周遭的负能量能不能屏蔽掉，可以的，人很容易被周遭的环境打乱自己的节奏，我们需要更强大屏蔽能力，闭屏掉所有负能量。这一点我认为明星们做的很好，作为大众的明星，对他/她褒贬不一的评价从全国四面八方袭来，如果没有强大的屏蔽负能量的能力，人很容易就崩溃，但他们做的很好，可以做到自然的屏蔽掉负能量全力做自己。&lt;/p&gt;

&lt;p&gt;能不能为自己提早制定计划，计划确实赶不上变化，至少制定了计划可以让自己知道后面会遇到哪些困难，我需要制定每周的计划，每个月的计划，这样才能有效预知未来前进过程中可能遇到的问题、瓶颈、障碍、困难。&lt;/p&gt;

&lt;p&gt;彷徨无法避免，但我想能不能减少彷徨的频率？怎样才能减少彷徨的频率呢，我可不可以，自己给自己一点多一些安全感，这样就会少一点彷徨。&lt;/p&gt;

&lt;p&gt;怎么给自己多一些安全感？&lt;/p&gt;

&lt;p&gt;为未来优秀的自己积累知识、能力、财富。如果我足够优秀，知识量足够多，能力足够强大，财富足够多，我彷徨的频率肯定就少一些，对未来和当前肯定更加坚定一些，专注力也会更加好一些。&lt;/p&gt;

&lt;p&gt;我要怎样才能达到这个目标？&lt;/p&gt;

&lt;p&gt;关键在于，’积累‘！每天几页书，每天抽出时间学习一下，每天运动一下保持精力充沛，少一些娱乐把精力用在更加重要的事情上，每天这样做，每天。&lt;/p&gt;

&lt;p&gt;只有你肯执行下去、坚持下去，无论现在处于什么样的状态，5年后你一定就是人才，10年后你将是名人，20年后你将是巨星。&lt;/p&gt;

&lt;p&gt;有一次看到一个公众号发的一个消息，上面标题写着“如果一个人20岁开始自律20年会如何”，好奇的点进去一看，三个大字刻进我心中，“曾国藩”。&lt;/p&gt;

&lt;p&gt;于是我阅读了曾国藩的书，我并没有要成为曾国藩，但我明白了自律的重要性。&lt;/p&gt;

&lt;p&gt;从那一刻开始，我想，他自律20年，我能不能自律10年，我想试试，我要挑战自己，要突破自己的瓶颈。&lt;/p&gt;

&lt;h6 id=&quot;良好的心态是人生的重要组成部分&quot;&gt;良好的心态是人生的重要组成部分&lt;/h6&gt;

&lt;p&gt;保持一种辛苦的感觉才是正确的，辛苦表明你正在努力突破自己，当你感觉不到辛苦时，很有可能你正在慢慢向下滑坡，只是现在外在表象还没发生任何变化，所以无法让你引起足够的重视，一旦表象上发生了什么变化，说明量变已经造成了质变，这时已经来不及了，能做的也只有补救，如果也没有做什么及时补救的行动那么可能前面自己所建立的优势也就会像过往云烟那样消散而去，自己重新回到了被动和劣势的位置。&lt;/p&gt;

&lt;p&gt;不过只有辛苦的感觉其实是还不够的，在辛苦的基础上，每天都要感觉自己活得很充实，活得很对得起昨天的感觉，只有这样一步步往前走下去才是正确的道路。&lt;/p&gt;

&lt;p&gt;人在生命中的道路上行走，犹如在黑暗中行走那样，是看不到前方清楚的道路的，只有当你主动积极的去做一些事情时才能看清前面几米距离的道路，就这样每次看见的都只是前方几米距离的道路，永远都是这种状况。&lt;/p&gt;

&lt;h6 id=&quot;如果不努力我们会一直处于这种看不清前方道路的处境中&quot;&gt;如果不努力我们会一直处于这种看不清前方道路的处境中。&lt;/h6&gt;

&lt;p&gt;只有当你很努力并且持续不断的努力，其实也只有持续不断的努力才能让你渐渐的看清前方的道路，让你更少的时间处于焦虑之中，也只有在我们不断努力下才能强大自我，才能在如此残酷的社会竞争中找到自己的定位。&lt;/p&gt;

&lt;p&gt;我们自己是很难找到自己的问题的，愚蠢的人是不知道自己愚蠢的，但其实我们自己也不知道自己是不是愚蠢。&lt;/p&gt;

&lt;p&gt;有没这种可能，其实我们是愚蠢的，但在自己看自己是聪明的，自己看别人是愚蠢的。每个人确实是这样认为的。&lt;/p&gt;

&lt;p&gt;几乎所有人都这么认为，’我是聪明的，别人是愚蠢的‘，就因为这种盲目乐观的思维方式根深蒂固，我们才难以察觉和发现自己的问题。&lt;/p&gt;

&lt;h6 id=&quot;不努力我们是看不到自己的问题的我很确信不努力学习不努力工作不努力地去克制自己我们可能永远都无法发现自己的问题所谓的突破自我也是因为在努力很久的情况下看清了自己的问题从而解决了自己身上的问题才最终得到了进阶和升华&quot;&gt;不努力，我们是看不到自己的问题的。我很确信。不努力学习，不努力工作，不努力地去克制自己，我们可能永远都无法发现自己的问题。所谓的突破自我，也是因为在努力很久的情况下看清了自己的问题，从而解决了自己身上的问题，才最终得到了进阶和升华。&lt;/h6&gt;

&lt;h6 id=&quot;人永远都是处于坏环境下&quot;&gt;人永远都是处于‘坏’环境下&lt;/h6&gt;

&lt;p&gt;刚工作那几年我一直以为是因为我个人的命途多舛才造就的这么多阻碍和困境，也一直以抱怨的态度对待环境，但渐渐的我发现，其实不只是我，天底下所有的人都处于这种‘坏’环境下，我们永远都感觉被束缚，被阻碍，被捆绑，被遏制，无论这个环境在外人看来有多好有多向往有多羡慕。&lt;/p&gt;

&lt;p&gt;其实最理智的方法是不去抱怨环境，环境永远是受限的，资源永远是缺少的，做好自己，解决自己身上的毛病才是最重要的。如果环境实在差的离谱，有更好的环境就直接去更好的环境，那里会更专注，努力的效果可能会更好，如果没有机会去更好的环境，那么就做好自己。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第七章，渲染管线与图形学(一) - 图形学基础2</title>
   <link href="http://www.luzexi.com/2019/09/07/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-%E6%B8%B2%E6%9F%93%E7%AE%A1%E7%BA%BF%E4%B8%8E%E5%9B%BE%E5%BD%A2%E5%AD%A62"/>
   <updated>2019-09-07T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/09/07/Unity3D高级编程之进阶主程-渲染管线与图形学2</id>
   <content type="html">&lt;h3 id=&quot;理解矩阵表达缩放的几何意义&quot;&gt;理解矩阵表达缩放的几何意义&lt;/h3&gt;

&lt;p&gt;缩放矩阵与旋转矩阵在几何意义上有着类似的解释，我们同样用二维坐标系来表达缩放矩阵的两个 a,b 向量。举例二维标准轴的旋转矩阵：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	[1.5,  0]  等  [a]
	[0, 0.75]  于  [b]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们把 a和b 表现在二维坐标系中，即如图：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;图中a，b两个向量可以形成了一个矩形，我们就假设这个矩形图像表达了a，b向量的缩放关系。当a，b被拉长时，这个矩形图像被也同样被拉长。其原理为向量与矩阵的乘法，我们知道:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	二维向量与二维矩阵相乘 = (x * m11 + y * m21, x * m12 + y * m22)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上图中标准坐标系上的缩放矩阵来说，我们带入向量得到：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	(x*m11 + y*m21, x*m12 + y*m22) = (x*1.5 + y*0, x*0 + y*0.75) = (1.5*x, 0.75*y)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这个结果比较非常直观的表达了缩放矩阵对向量的缩放，矩阵除了对角线上的数字，其他位置的数字都是0。这种简单的旋转矩阵，也仅限于标准坐标系中对标准轴的缩放，我们在使用缩放矩阵时更多时候需要对任意轴上进行缩放。那么怎么计算对任意轴方向上的缩放呢？我们来看如下图所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;沿 N 方向缩放 v 向量，其实和前一节中绕 N 轴旋转 v 向量有相似的原理。我们根据所知道的推导这个公式，即我们已知v，已知 N，已知缩放因子k (k是一个单纯的数字) ，要求得v’。可以通过 N 和 v 求得投影 v1，通过 v 和 v1 求得他们的垂直向量 v2，再通过缩放因子 k 求得v1和v2缩放后的向量 v1‘ 和 v2’，最后通过 v1‘ 和 v2’ 求得缩放后的结果向量 v‘，即如下图公式。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;同样的，三维空间中，如果是标准坐标系上缩放，则缩放矩阵可以是简单的对角线矩阵，即。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	[scale_x, 0, 0]
	[0, scale_y, 0]
	[0, 0, scale_z]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;也同样的，这种标准坐标系上的缩放满足不了我们的计算需求，我们需要计算沿任意向量上的缩放矩阵公式。这里不再重复叙述推导的过程，我们只要理解缩放矩阵的由来和推导的方式就可以了，我们最终的目的通过是解释原理来理解缩放矩阵的几何意义。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;与二维缩放矩阵的推导类似，同样的向量同样的变化同样的推导，最终我们获得了，沿任意方向缩放的矩阵。&lt;/p&gt;

&lt;h3 id=&quot;平行投影镜像切变的理解和几何意义&quot;&gt;平行投影，镜像，切变的理解和几何意义&lt;/h3&gt;

&lt;h6 id=&quot;平行投影&quot;&gt;平行投影&lt;/h6&gt;

&lt;p&gt;上面我们了解到了可以用矩阵去缩放任意点和向量，假设如果在缩放时某个轴上的缩放因子为零会变成是什么样的呢，如图所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;图中模型所有的点都被’挤‘到了一个平面上，这种所有点都被拉平至垂直轴或平面上的做法就叫，平行投影，或者也可以叫正交投影。在标准坐标轴上的平行投影可以用一个简单的矩阵来表示，它只要把那个投影轴上的缩放因子置零就可以，即：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	二维上x轴平行投影

	[1, 0]
	[0, 0]

	二维上y轴平行投影

	[0, 0]
	[0, 1]

	三维上xy平面上的平行投影

	[1, 0, 0]
	[0, 1, 0]
	[0, 0, 0]

	三维上xz平面上的平行投影

	[1, 0, 0]
	[0, 0, 0]
	[0, 0, 1]

	三维上yz平面上的平行投影

	[0, 0, 0]
	[0, 1, 0]
	[0, 0, 1]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;当然只是标准坐标轴上的平行投影还不够用，我们也需要任意直线或任意平面的投影矩阵，不过这次我们不需要再次计算任意轴上的平行投影，只需要通过前面计算过的缩放矩阵在任意方向或任意轴上的缩放矩阵就可以得到平行投影的矩阵，即如下图：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;通过使缩放方向上的缩放因子变为零的方式，来获得平行投影矩阵。三维空间中也是一样，只是我们要注意的是，在对任意轴三维缩放矩阵中要让 N 方向为垂直平面的方向而不是平行于平面的方向。&lt;/p&gt;

&lt;h6 id=&quot;镜像&quot;&gt;镜像&lt;/h6&gt;

&lt;p&gt;镜像其实挺容易理解的，平行投影是将某个轴上的缩放因子变为零，镜像就是在某个轴上将缩放因子为负1，这样在缩放时就形成了‘翻转’的局面。如图所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;坐标轴的四个象限，右上角的图为正常的图案，左边为对x轴翻转后的图案，下边为y轴翻转后的图案，右下角为x轴和y轴同时翻转的图案。镜像矩阵也和平行投影矩阵一样，可以用任意缩放矩阵来得出对任意方向和轴的镜像矩阵，如图所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;图中为任意方向的缩放矩阵在当缩放因子为负1时的公式，不难理解镜像不过是缩放的一种特殊形式。&lt;/p&gt;

&lt;h6 id=&quot;切变&quot;&gt;切变&lt;/h6&gt;

&lt;p&gt;切变非常特殊和有趣，它其实是一种坐标系‘扭曲’变换。这种坐标系的‘扭曲’变换将会被运用到对次级空间的坐标平移上。切变的形式如图：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;图中y轴方向的向量中的x坐标被平移了。这是切变在x轴上的变化，我们也可以让切变在y轴上发生变化，如图：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这两种形式的切变都是通过位移x轴方向上的坐标或y轴方向上的坐标来达到的，其实我们可以理解为对次级维度坐标系的‘扭曲’。为什么要这么理解呢，因为它在仿射变化中起到了非常关键的作用，我们将在下面几节的内容中介绍。&lt;/p&gt;

&lt;p&gt;切变的矩阵为：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	二维x轴方向的‘扭曲’
	[1, 0]
	[s, 1]

	二维y轴方向的‘扭曲’
	[1, s]
	[0, 1]

	三维x,y轴方向的的切变
	[1, 0, 0]
	[0, 1, 0]
	[s, t, 1]

	三维x,z轴方向的切变
	[1, 0, 0]
	[s, 1, t]
	[0, 0, 1]

	三维y,z轴方向的切变
	[1, s, t]
	[0, 1, 0]
	[0, 0, 1]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们用向量与矩阵相乘来看看它们的结果会是如何：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	向量p与3x3矩阵M的乘法 p * M = [x * M11 + y * M21 + z * M31, x * M12 + y * M22 + z * M32, x * M13 + y * M23 + z * M33]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;当p向量与上面5个切变矩阵相乘时：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	第一个切变矩阵为向量x轴方向的偏移，(x + y * s, y)

	第二个切变矩阵为向量y轴方向的偏移，(x, x * s + y)

	第三个切变矩阵为向量x、y轴方向的起偏移，(x + s * z, y + t * z, z)

	第四个切变矩阵为向量x、z轴方向的起偏移，(x + s * y, y, t * y + z)

	第五个切变矩阵为向量y、z轴方向的起偏移，(x, s * x + y, t * x + z)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述经过矩阵乘法后得到的切变的结果可知，切变中坐标的变化都是在当前轴中向以其他轴坐标方向为标准进行偏移。下面一节平移中就会应用到切变的变化。&lt;/p&gt;

&lt;h6 id=&quot;齐次坐标的平移矩阵&quot;&gt;齐次坐标的平移矩阵&lt;/h6&gt;

&lt;p&gt;我们前面介绍的3x3矩阵的线性变换中并不包括平移这个操作，因为它无法在三维空间中做平移操作，零向量就能很好证明这一点，由于矩阵乘法的性质，任何矩阵乘以零向量都是零，因此零向量无法平移。矩阵乘法其实很强大，经过我们上面的介绍我们已经知道了矩阵乘法可以表达旋转，缩放，投影，镜像，切变，可惜的是无法表达平移，怎么办？齐次矩阵就恰好满足了我们的需求，齐次矩阵它在原来的维度上增加了一个维度，用多出来的那个维度来表达了平移操作。即如下图：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	[1, 0, 0, 0]
	[0, 1, 0, 0]
	[0, 0, 1, 0]
	[x, y, z, 1]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上图中用增加一个维度的方式用x，y，z分别表示了在x，y，z轴上的偏移。&lt;/p&gt;

&lt;p&gt;为什么这种方式就能表达平移操作？我们回忆下上一节说的切变的原理和过程，在某个轴不变的情况下对其他轴进行偏移，这就是切变。与此同时，在同一维度下我们无法对当前维度空间的矩阵进行偏移，于是可以通过增加一个维度，用新增的维度切变方式来偏移次级维度，齐次矩阵正好用切变解决了次级维度的平移问题。&lt;/p&gt;

&lt;p&gt;在上述的图中描述了4维矩阵，我们可以认为它在第四维用到了切变，平移了次级维度的空间。为了能顺利让向量与齐次矩阵相乘，我们的坐标也必须是齐次的，即在坐标末尾补上1&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(x,y,z,1) x [1,  0,  0,  0] = (x + x`, y + y`, z + z`, 1)
			[0,  1,  0,  0]
			[0,  0,  1,  0]
			[x`, y`, z`, 1]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;现在我们有了平移矩阵，我们可以通过用3x3矩阵增加一个维度的方式把原来无法表达的操作只用一个矩阵全都表达出来。假设我们先旋转后平移，我们先将向量 v 乘以旋转矩阵，旋转后得到的结果再乘以平移矩阵，平移得到 v‘ ，假设旋转矩阵为R，平移矩阵为T，就可以用向量与矩阵的乘法，以及矩阵的结合律来表达：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	v' = vRT

	R为：
	[r11, r12, r13, 0]
	[r21, r22, r23, 0]
	[r31, r32, r33, 0]
	[0, 	0, 	 0, 1]

	T为：
	[1, 0, 0, 0]
	[0, 1, 0, 0]
	[0, 0, 1, 0]
	[x, y, z, 1]

	通过结合律 v' = vRT = v(RT)

	R与T相乘的结果为
	[r11, r12, r13, 0]
	[r21, r22, r23, 0]
	[r31, r32, r33, 0]
	[x, 	y, 	 z, 1]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们可以通过先乘以旋转矩阵再乘平移矩阵得到结果，也可以使用结合律先将矩阵相乘。在实际计算过程中，如果矩阵的操作比较频繁，我们可以先用结合律计算一个固定的变化矩阵，后面所有的变化计算都通过变化矩阵来计算得到结果，这样省去了重复计算矩阵的算力。我们常用的顶点着色器中的顶点坐标空间转换就是这样做的，它在整个程序开始执行前就已经计算好了一个变化的矩阵，这个变化的矩阵就叫做 MVP，即Model，View，Projection，它是由模型空间矩阵、观察者空间矩阵、投影空间矩阵相乘得到的结果，乘以此矩阵就会从模型坐标系变换为世界坐标系，再变换为观察者坐标系，最后变化为视锥体的裁剪空间供渲染绘制所用。&lt;/p&gt;

&lt;p&gt;我们通过齐次矩阵，用增加一个维度的方式来表达当前维度无法表达的计算，用高一个维度的切变去操作次级维度的平移。&lt;/p&gt;

&lt;h3 id=&quot;理解-quaternion-四元数&quot;&gt;理解 Quaternion 四元数&lt;/h3&gt;

&lt;h6 id=&quot;为什么不是欧拉角&quot;&gt;为什么不是欧拉角&lt;/h6&gt;

&lt;p&gt;欧拉角的定义是，x,y,z 分别表达了x轴上的旋转角度，y轴上的旋转角度，z轴上的旋转角度，即(20,40,50) 表达了在x轴上旋转20度，y轴上旋转40度，z轴上旋转50度。看起来简单易懂就能定义坐标系上的旋转角度，为什么就不能使用它来表达所有的旋转角度，却还要使用四元数呢？其实是有原因的。&lt;/p&gt;

&lt;p&gt;欧拉角存在别名，100度的旋转角度可以用-260度来表示，370度角与10度角以及-350度角是相同的旋转角度，这种表达方式在计算上特别难统一。欧拉角在插值上存在些问题，一个-260度的角度和一个50度的角度进行插值是需要先进行转换的，先将-260度转换成100度，再进行插值才可以得到正确的结果。简单的别名问题虽然讨厌，但是可以转换角度的方式解决，只是转换角度这种方式并不是一个靠谱的方式，在计算过程中会遇到相当多的麻烦。&lt;/p&gt;

&lt;p&gt;欧拉角这种周期性和旋转之间的不独立性造成了欧拉角在线性变化的计算中比较困难，因此我们需要寻找在计算过程中更加便捷的方法，不需要转换，没有别名，统一规格。四元数恰好就满足了这些需求，它在线性变换中能统一且灵活，它有个致命缺陷就是在表现上让人比较难理解，一般人一眼看不出一个四元数所表达的旋转的方向和角度。&lt;/p&gt;

&lt;h6 id=&quot;四元数的由来&quot;&gt;四元数的由来&lt;/h6&gt;

&lt;p&gt;四元数可以说是图形数学中的“异次元”，这也是为什么普通人难以理解它的原因，为什么说它是一个“异次元”呢？我们所有的向量，坐标，矩阵，旋转，缩放，平移，都是建立在使用坐标系和空间矩阵计算的这一套计算体系上的，而四元数则不是，它特立独行，它计算所用的公式，并不是建立在传统的坐标系矩阵上，而是拥有自己一套“自有”的公式体系，即复数体系。&lt;/p&gt;

&lt;p&gt;四元数有两种记法即：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	向量v 加 w分量 (v, w)

	四个分量都分开 (x, y, z, w)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;某些情况下v分量更方便，而在另一些情况下分开记会更清楚。但其实这个w分量和x,y,z的相关度有但不是很大，因此我们不要被迷惑了，以为他们的值越大或者越小会怎样怎样的，其实和我们肉眼看到的是有所差别的。&lt;/p&gt;

&lt;p&gt;最早数学家们是用复数系统来表达二维中的旋转的。我们来回忆下什么是复数？&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	我们把形如 z=a+bi（a,b均为实数）的数称为复数，其中a称为实部，b称为虚部，i称为虚数单位，其中虚数 i * i = -1。
	
	当z的虚部等于零时，常称z为实数；
	
	当z的虚部不等于零时，实部等于零时，常称z为纯虚数。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;数学家使用复数对(a, b)来表达二维平面中的旋转，他们把向量也定义为复数对，如果(a,b)为向量的话，那么就定义了 a + b * i，i为虚数，满足 i * i = -1，a为实轴的坐标，b为虚轴坐标，我们可以理解为x，y轴上的坐标。&lt;/p&gt;

&lt;p&gt;对于定义一个旋转复数，为 (cos(β), sin(β)) 表达式为 cos(β) + sin(β) * i，i为虚数，β为旋转的角度。他们发现从平面上求得某个向量旋转 β 的结果，可以用向量复数对乘以旋转复数对的方式来获得，即&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片

	v = x + y * i

	r = cos(β) + sin(β) * i

	v' = vr = (x + y * i)(cos(β) + sin(β) * i)

	   = (x * cos(β) - y * sin(β)) + (x * sin(β) + y * cos(β)) * i
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述图中的公式推导，乘法的运算跟传统的计算有点差异，因为我们表达式中有复数运算，其中复数i满足 i * i = -1。&lt;/p&gt;

&lt;p&gt;这是最早发明的二维向量旋转运算法则，由十六世纪被意大利米兰学者卡当提出复数后，经过达朗贝尔、棣莫弗、欧拉、高斯等人的工作，最后成形的数学体系。不过二维的上的旋转公式很快就不够用，无法对三维的旋转操作，旋转复数局限性太大。爱尔兰数学家 William Hamilton 终于在1843年找到了一种表达三维旋转的复数表达方式，即四元数就此诞生。&lt;/p&gt;

&lt;h6 id=&quot;四元数的几何意义&quot;&gt;四元数的几何意义&lt;/h6&gt;

&lt;p&gt;四元数扩展了复数系统，它使用了三个虚部，即i，j，k，因此四元数的表达复数表达方式为&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	q = w + i * x + j * y + k * z

	其中i，j，k为虚数，即满足

	i*i = j*j = k*k = -1

	i*j=k, ji=-k

	j*k=i, k*j=-i

	k*i=j, i*k=-j
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;与复数能用来旋转二维中的向量类似，四元数也能用来旋转三维中的向量。&lt;/p&gt;

&lt;p&gt;四元数被解释为角位移的轴一角方式。什么是轴一角？就是绕某个单一轴旋转一个角位移就能表达旋转的方式就叫轴一角，这个角位移其实就是一个和向量类似的表达方式，即(x,y,z)，只不过四元组用4个元素来表达罢了。说白了，四元组可以理解为绕 某个轴N 旋转的角位移，和欧拉角用x,y,z表达绕标准坐标轴旋转是同样的道理，只不过这个轴不再是标准轴，而是任意轴。&lt;/p&gt;

&lt;p&gt;这也就说明了，四元组不再受到标准轴的限制，它可以表达绕任意轴旋转的角位移。四元组表示为对任意轴 N 的角位移即：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	q = [cos(β/2), sin(β/2) * N]

	  = [cos(β/2), sin(β/2) * Nx, sin(β/2) * Ny, sin(β/2) * Nz]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;利用上图的四元组表达方式，假设我们绕的是某个单轴旋转，也就是在x轴上旋转 A 度，或者在y轴上旋转 B 度，或者在z轴上旋转 C 度，根据上述的公式，我们可以得到三个供旋转的四元数，即：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	A' = [cos(-A/2), sin(-A/2) * 1, 0, 0]

	B' = [cos(-B/2), 0, sin(-B/2) * 1, 0]

	C' = [cos(-C/2), 0, 0, sin(-C/2) * 1]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上图中的角度为什么是负的呢，因为我们在旋转点时的角度，和在旋转坐标系时的角度恰恰是相反的，因此操作旋转点的角度其实就是旋转坐标系反方向的角度，即负的角度。&lt;/p&gt;

&lt;p&gt;现在我们要计算绕x轴上旋转A角度，并且绕y轴上旋转B角度，并且绕z轴上旋转C角度的四元组时，可以把 A‘，B’，C‘，这三个四元组乘起来，即：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	A'B'C' = (A'B'C') 最终计算得到

	[x,]
	[y,]
	[z,]
	[w]

	等于

	[cos(B/2)sin(A/2)cos(C/2) + sin(B/2)cos(A/2)sin(C/2),]
	[sin(B/2)cos(A/2)cos(C/2) - cos(B/2)sin(A/2)sin(C/2),]
	[cos(B/2)cos(A/2)sin(C/2) - sin(B/2)sin(A/2)cos(C/2),]
	[cos(B/2)cos(A/2)cos(C/2) + sin(B/2)sin(A/2)sin(C/2)]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上图中得到的最后公式就是从欧拉角转换到四元数的计算公式。&lt;/p&gt;

&lt;p&gt;四元数的虽然在计算上很方便且通用，但在辨识度上却存在着严重的缺陷，人肉眼很难分辨某个四元数的旋转情况。但是没关系，我们只要理解它的原理，并且已经知道了四元数的几何意义，在运用四元数过程中便能更加自如。&lt;/p&gt;

&lt;p&gt;参考文献：&lt;/p&gt;

&lt;p&gt;《3D数学基础:图形与游戏开发》&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第七章，渲染管线与图形学(一) - 图形学基础1</title>
   <link href="http://www.luzexi.com/2019/09/01/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-%E6%B8%B2%E6%9F%93%E7%AE%A1%E7%BA%BF%E4%B8%8E%E5%9B%BE%E5%BD%A2%E5%AD%A61"/>
   <updated>2019-09-01T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/09/01/Unity3D高级编程之进阶主程-渲染管线与图形学1</id>
   <content type="html">&lt;p&gt;游戏项目编程除了业务逻辑、以及业务逻辑形成的模块、框架、架构、算法外，还有比较重要的图形学的支撑，很多人在业务层面打拼了很多年也始终无法突破的原因就是对图形学研究还不够深，这对优化游戏性能来说是一块比较大的内容，CPU和GPU两边的知识面都需要我们扩充和深入。本质上来说我们面对的是图形绘制和计算，业务逻辑虽然支撑起了图形绘制的骨架，底层知识仍然需要我们有足够多了了解，只是在使用了现代图形引擎之后，特别是使用了Unity3D之后，它为我们包装了很多工具和接口，用起来非常方便，初期我们可以不顾图形原理，但越到后期越不得不关注图形绘制原理。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;我们也不能有偏见，其实CPU与GPU的知识面缺哪块都不行，没有逻辑不懂得各个模块的编写手法，或者不懂得框架的搭建，或者不懂架构，或者不懂算法，或者不懂图形绘制原理，都是无法完整的体现一个优秀程序员的知识面宽度，特别是对于需要在一个项目中担当起顶梁柱的主程同学，更是需要对于每个部分的都有深刻的了解。特别是图形绘制原理，它在初期编程中比较容易被忽视，也特别需要我们静下心来学习、理解和掌握。本非常想从最基本的讲起将基础部分写的详尽些，但篇幅有限只能挑些重点的说，并以言简意赅的形式说明部分细节。&lt;/p&gt;

&lt;p&gt;特别挑出了图形学比较重要部分与Unity3D结合的来细致讲解一下。&lt;/p&gt;

&lt;h3 id=&quot;vector3-的意义&quot;&gt;Vector3 的意义&lt;/h3&gt;

&lt;p&gt;Vector3 有x，y，z三个变量，通常来说它是坐标的数据。不仅如此，它还可以代表距离、速度、位移、加速度、以及方向。&lt;/p&gt;

&lt;p&gt;两个Vector3点坐标变量别分为a和b，相减后就能得到一个从b点到a点的向量c。向量c可以认为是一个长度为a点到b点的长度并且拥有b点到a点方向的向量，向量c与a和b同样是Vector3类型。&lt;/p&gt;

&lt;p&gt;那么为什么a和b是坐标，而c则是向量呢。其实任何一个Vector3变量，确定它代表的是坐标还是向量，全靠我们如何定义它。我们可以定义a是坐标，也可以定义a是速度向量，至于如何定义以及怎么计算，全靠我们在公式中或者说在程序中怎么对待它，速度、位移、加速度都是一个道理，我们举个例子。&lt;/p&gt;

&lt;p&gt;还是a和b定义为Vector3的坐标点，a减去b，除以常量1，可以认为我们向量除以了一个时间单位1秒得到了我们需要的有方向的速度c，此时c被认为是速度矢量，因为距离除以时间等于速度，c就是这个有方向的速度。再比如，坐标a点减去b点得到c向量，任意坐标加上c就会得到与a和b同样的相对位置，此时c就被认为是偏移量。&lt;/p&gt;

&lt;p&gt;现在有四个坐标a，b，c，d，当a减去b除以1秒得到了a到b的时的速度，那么b减去c除以1秒得到也是b到c时的速度，这两个速度再相减依然是Vector3类型，而此时得到的结果Vector3已经不再代表坐标和速度，而是一个由两个速度相减得到的加速度结果了。&lt;/p&gt;

&lt;h6 id=&quot;最终我们发现我们赋予vector3什么样的意义依赖于我们用它们来计算的过程&quot;&gt;最终我们发现，我们赋予Vector3什么样的意义依赖于我们用它们来计算的过程。&lt;/h6&gt;

&lt;p&gt;为了能更深入理解Vector3的计算意义，我们挑选了几个Vector3比较重要的几何意义。&lt;/p&gt;

&lt;h3 id=&quot;vector3点乘的几何意义&quot;&gt;Vector3点乘的几何意义&lt;/h3&gt;

&lt;p&gt;向量a，与，向量b点乘的计算公式为&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	a·b = (x1,y1,z1)·(x2,y2,z2) = x1*x2 + y1*y2 + z1*z2
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;除了上述的公式外点乘又有另外一个计算公式即为&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	a·b = ||a||*||b||*cos(β)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们可以用更直观的方式来表示它们，如图所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺图1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;图中a向量与b向量的夹角为β，当a向量和b向量长度不变的情况下，计算出来的值越大，cos(β)值也就越大，要让cos(β)值越大则a和b的夹角就得越小。当β大于90度时，计算出来的是一个负数，也就说明b指向的方向其实与a指向的方向是相反的。&lt;/p&gt;

&lt;p&gt;我们用这种方式可以得到一个判断依据，即当a和b两个向量点乘得到的数为正数时，两者的方向比较一致，并且这个结果越大a和b两个向量的方向越一致，得到最大正数时两者的方向完全相同。当点乘结果为负数时，a和b两个向量的方向则比较相反，得到结果的数值负得越厉害，a和b的方向相反的程度越厉害，最大负数时两者方向完全相反。&lt;/p&gt;

&lt;p&gt;点乘除了可以判断两个向量的方向外，我们还可以用它来计算出β的角度，即：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	β = arcos( (a·b) / (|a|*|b|) )
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;现在我们用程序来表达计算a和b夹角的计算过程，其中Vector3.Dot为Unity3D中点乘的计算接口，Vector3.magnitude为矢量距离值，如下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;public static float Dot(Vector3 lhs, Vector3 rhs)
{
	return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z;
}

public float magnitude { get { return Mathf.Sqrt(x * x + y * y + z * z); } }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们用Mathf.Acos(float f)来获取反三角函数值，于是就有了:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;float β = Mathf.Acos( Vector3.Dot(a,b) / (a.magnitude * b.magnitude) )
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;vector3叉乘的几何意义&quot;&gt;Vector3叉乘的几何意义&lt;/h3&gt;

&lt;p&gt;与Vector3点乘一样Vector3的叉乘也是向量与向量之间的计算公式，不同的是叉乘的结果不再是一个数值，而是一个同样维度的向量，即:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	a x b = (a1, a2, a3) x (b1, b2, b3) = (a2*b3 - a3*b3, a3*b1 - a1*b3, a1*b2 - a2*b1) = c
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;当两个向量a，b叉乘后，得到的是与a，b向量形成的平面垂直的向量c。其中c的长度又可以用另一个公式来表示，我们可以说 a x b的长度等于向量的大小与向量的夹角sin值的积，即:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	|c| = |a x b| = |a|*|b|*sin(β)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;公式中c的长度大小与a和b向量的夹角有关。这样看来，我们可以反推为当c的长度为0时，a和b是相互平行的两个矢量，以及，当a和b的叉乘的模等于a的模乘以b的模时，a和b就为互相垂直。&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;此外a向量和b向量叉乘的模就是，a向量和b向量形成的四边形的面积，即&lt;/td&gt;
      &lt;td&gt;a&lt;/td&gt;
      &lt;td&gt;*&lt;/td&gt;
      &lt;td&gt;b&lt;/td&gt;
      &lt;td&gt;*sin(β) 就是a、b两条矢量形成的四边形的面积值，如图所示&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片
&lt;/code&gt;&lt;/pre&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;我们说四边形的面积公式为一条边乘以它的垂直高度即&lt;/td&gt;
      &lt;td&gt;b&lt;/td&gt;
      &lt;td&gt;* h，在四边形中垂直高度h怎么得到呢，我们可以用&lt;/td&gt;
      &lt;td&gt;a&lt;/td&gt;
      &lt;td&gt;* sin(β) 得到，因此就有了这个公式的演变，即：&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;pre&gt;&lt;code&gt;	四边形面积 = |b| * h

	由于 h = |a| * sin(β) 代入后 =&amp;gt; 四边形面积 = |b| * |a| * sin(β)

	=&amp;gt; 四边形面积 = |b| * |a| * sin(β) = |a x b|
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;经过几个公式的转换，我们得到向量叉乘后的模就是四边形的面积。这在3D中也是同样适用，因为两个向量确定一个平面，所以两个向量可以确定给一个对等边四边形的平面，从而可以用叉乘可以算出他们的面积。&lt;/p&gt;

&lt;p&gt;在Unity3D中叉乘的函数就是Vector3.Cross，如下代码所示:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;// Cross Product of two vectors.
public static Vector3 Cross(Vector3 lhs, Vector3 rhs)
{
    return new Vector3(
        lhs.y * rhs.z - lhs.z * rhs.y,
        lhs.z * rhs.x - lhs.x * rhs.z,
        lhs.x * rhs.y - lhs.y * rhs.x);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;向量之间的投影&quot;&gt;向量之间的投影&lt;/h3&gt;

&lt;p&gt;在几何计算过程中，我们经常用到‘投影’这种方式，在向量中投影也是常用的技巧。如图：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;图中向量b往向量a投影得到向量c，其实表象上来看c就是向量a乘以某个系数得到的。这个系数可以认为是c的模除以a的模得到的。即&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	c = a * (|c|/|a|)
&lt;/code&gt;&lt;/pre&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;我们不知道&lt;/td&gt;
      &lt;td&gt;c&lt;/td&gt;
      &lt;td&gt;的值，但是c和b的夹角β又能得到计算cos的公式即：&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;pre&gt;&lt;code&gt;	|c| = |b| * cos(β)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;再套入到计算投影向量c的公式上去时，就变成了：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	c = a * (|b| * cos(β)/|a|)

	进一步，在除号的两边乘以a模 =&amp;gt; c = a * (|b| * cos(β) * |a|) / (|a| * |a|)

	由于 |b| * cos(β) * |a| 组合起来等于a向量与b向量的点乘的结果，于是就有了

	投影向量c =&amp;gt; c = a * (a · b) / (|a|^2)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;至此经历了几个公式的转换，得到了b向量向a向量投影，得到c向量的公式，下面我们给出Unity3D的Vector3中向量间的投影程序:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;// Projects a vector onto another vector.
public static Vector3 Project(Vector3 vector, Vector3 onNormal)
{
    float sqrMag = Dot(onNormal, onNormal);
    if (sqrMag &amp;lt; Mathf.Epsilon)
        return zero;
    else
        return onNormal * Dot(vector, onNormal) / sqrMag;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;上述代码中，Dot(onNormal,onNormal) 很巧妙的计算了矢量模的平方，即 Dot(onNormal, onNormal) 等于&lt;/td&gt;
      &lt;td&gt;onNormal&lt;/td&gt;
      &lt;td&gt;*&lt;/td&gt;
      &lt;td&gt;onNormal&lt;/td&gt;
      &lt;td&gt;* cos(β)，很明显β为0，cos(β)为1，于是就有了 Dot(onNormal,onNormal) =&lt;/td&gt;
      &lt;td&gt;onNormal&lt;/td&gt;
      &lt;td&gt;*&lt;/td&gt;
      &lt;td&gt;onNormal&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;矩阵的意义&quot;&gt;矩阵的意义&lt;/h3&gt;

&lt;p&gt;矩阵看上去有点复杂，很多人看到矩阵这个词就头疼，其实耐下心来研究，会发现矩阵是很可爱。矩阵五花八门，其实大部分五花八门的矩阵使用频率非常低，我们常用矩阵就只有几种。&lt;/p&gt;

&lt;p&gt;在图形学计算中，我们常用的矩阵大小是2x2，3x3，4x4 矩阵，这种行与列的数量相同的矩阵，我们称为‘方阵’矩阵。在众多方阵矩阵中，常用到也是这些比较特殊的矩阵，比如对角矩阵，即只有行列号相同的位置有数字，其他位置都是0的方阵矩阵，以及单位矩阵，即行列号相同的对角线上的数字都为1，其他位置都为0的方阵矩阵。这两种特殊的矩阵简单易懂，在图形学计算过程中也是非常常用的矩阵。&lt;/p&gt;

&lt;p&gt;矩阵间的计算我们可以罗列一下其实并不多。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	转置矩阵，就是把矩阵沿着对角线翻转一下，由于我们常用的是‘方阵’矩阵，所以转置矩阵后方阵矩阵还是同样的大小，只不过对角线两侧的数字对调了一下。

	矩阵乘法，如果是数字和矩阵相乘则，直接带入矩阵中的所有变量即可，这种标量的乘法其实就是扩大矩阵中所有的数值。如果是矩阵与矩阵相乘，A矩阵 x B矩阵，则需要一些附加条件，条件是矩阵A的列数必须与矩阵B的行数相等，否则无法相乘或者说相乘无意义。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;矩阵相乘后得到的矩阵，里面每个位置Cij(即C矩阵的第i行第j列)都是A矩阵的第i行向量与B矩阵的第j列向量点乘的计算结果，如下我们拿2x2方阵相乘做示意：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	A x B = [a11, a12]  x  [b11, b12]
	        [a21, a22]     [b21, b22]

	= [a11*b11 + a12*b21, a11*b12 + a12*b22]
	  [a21*b11 + a22*b21, a21*b12 + a22*b22]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上图对矩阵乘法公式做出了清晰的描述，即Cij = Ai1 * B1j + Ai2 * B2j + Ai3 * B3j … 我们可以归纳为ij位置的值等于，A的i行向量与B的j列向量点乘的值。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	逆矩阵，逆矩阵由运算矩阵相乘而来，由于矩阵与矩阵相乘也会得到标准的单位矩阵，即对角线都是1其余都是0的方阵矩阵，于是就有了当一个矩阵与某个矩阵相乘等于单位矩阵时，这‘某个’矩阵就为该矩阵的‘逆矩阵’。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;不过不是每个矩阵都有逆矩阵的，一个明显的例子是若矩阵的某一行或列上的元素都是0，用任何矩阵乘以该矩阵，结果都是一个带有某行或某列全零的矩阵，这样的结果一定不是单位矩阵。因此我们通常称一个有逆矩阵的矩阵为，这个矩阵可逆，相反如果这个矩阵没有逆矩阵，那么就称这个矩阵不可逆。&lt;/p&gt;

&lt;p&gt;最后我们了解一下齐次矩阵，齐次矩阵也并没有我们想象的神秘，它只是从我们认知的角度上划分了矩阵分量和w分量，我们来了解下。&lt;/p&gt;

&lt;p&gt;我们听说过齐次坐标，齐次坐标就是将一个原本是n维的向量用一个n+1维向量来表示，例如我们的三维向量用四维来表示即Vector3变为Vector4，除了x、y、z又多了一个w，齐次矩阵也是同样的道理，n维表达不了的事情用n+1维来表达，3 x 3 矩阵表达不了的事情用 4 x 4 来表达，下面我们就由浅入深来理解齐次坐标与齐次矩阵。&lt;/p&gt;

&lt;p&gt;(引用自《齐次坐标》)在欧几里得几何空间里，两条平行线永远都不会相交。但是在投影空间中，如右图中的两条铁轨在地平线处却是会相交的，因为在无限远处它们看起来相交于一点。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[缺图，两条并行轨道在图中远处相交]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在欧几里得（或称笛卡尔）空间里描述2D/3D 几何物体是很理想的，但在投影空间里面却并不见得。我们用 (x, y) 表示笛卡尔空间中的一个 2D 点，而处于无限远处的点 (∞,∞) 在笛卡尔空间里是没有意义的。投影空间里的两条平行线会在无限远处相交于一点，但笛卡尔空间里面无法搞定这个问题（因为无限远处的点在笛卡尔空间里是没有意义的），因此数学家想出齐次坐标这个点子来了。由 August Ferdinand Möbius 提出的齐次坐标（Homogeneous coordinates）让我们能够在投影空间里进行图像和几何处理，齐次坐标用 N + 1个分量来描述 N 维坐标。比如，2D 齐次坐标是在笛卡尔坐标(X, Y)的基础上增加一个新分量 w，变成(x, y, w)，其中笛卡尔坐标系中的大X，Y 与齐次坐标中的小x，y有如下对应关系：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;X = x/w

Y = y/w
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;笛卡尔坐标中的点 (1, 2) 在齐次坐标中就是 (1, 2, 1) 。如果这点移动到无限远(∞,∞)处，在齐次坐标中就是 (1, 2, 0) ，这样我们就避免了用没意义的”∞” 来描述无限远处的点。&lt;/p&gt;

&lt;p&gt;那么为什么叫齐次坐标呢？前面提到，我们分别用齐次坐标中的 x 和 y 除以 w 就得到笛卡尔坐标中的 x 和 y，如图所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[缺图]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;仔细观察下面的转换例子，可以发现些有趣的东西：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[缺图]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上图中，点 (1, 2, 3), (2, 4, 6) 和 (4, 8, 12) 对应笛卡尔坐标中的同一点 (1/3, 2/3)。 任意数量积的(1a, 2a, 3a) 始终对应于笛卡尔坐标中的同一点 (1/3, 2/3)。因此这些点是“齐次”的，因为他们始终对应于笛卡尔坐标中的同一点。换句话说，齐次坐标描述缩放不变性（scale invariant）。&lt;/p&gt;

&lt;p&gt;向量在空间中的运用还不够广泛，矩阵可以表达空间中的缩放、旋转、切变，但无法表达偏移，因此我们增加一个维度的矩阵叫做齐次矩阵来表达当下维度的偏移，即齐次矩阵。&lt;/p&gt;

&lt;h3 id=&quot;由矩阵带来的旋转缩放投影镜像和仿射&quot;&gt;由矩阵带来的旋转，缩放，投影，镜像，和仿射。&lt;/h3&gt;

&lt;p&gt;很多人都难以理解矩阵为什么能做到旋转和缩放，或者怎么理解矩阵的旋转和缩放，这节我们就来讲讲如何理解矩阵的旋转和缩放。&lt;/p&gt;

&lt;h6 id=&quot;我们先来了解下向量与矩阵的乘法&quot;&gt;我们先来了解下，向量与矩阵的乘法。&lt;/h6&gt;

&lt;p&gt;首先我们要明白，我们用的都是方阵矩阵，即2x2，3x3，4x4的矩阵。由于矩阵与矩阵相乘必须是前置的列与后置的行数要相等才有意义，向量与矩阵相乘也是一样，如果向量不是前置的那个即左乘矩阵，或者向量是以竖列表达方式右乘矩阵，对于向量与矩阵的乘法来说都是无意义的，即如下图所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	[缺图]

	[缺图]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;图中，第一个例子，当向量左乘矩阵，是无定义的结果，第二个例子，向量以竖列的表达方式右乘矩阵，也是无定义的结果，只有第三个例子和第四个例子，向量横向表达右乘矩阵，或竖向表达左乘矩阵，才有意义。&lt;/p&gt;

&lt;p&gt;我们知道向量与矩阵的乘法公式，其中我们最常用的是三维向量与三维方阵矩阵相乘，即公式为：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	(x,y,z) x 	[m11,m12,m13]
				[m21,m22,m23]
				[m31,m32,m33]

	= (x*m11 + y*m21 + z*m31, x*m12 + y*m22 + z*m32, x*m13 + y*m23 + z*m33)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;至于向量的行向量的表达方式，和列向量的表达方式，其实都是可以行得通的，那么我们为什么要选择行向量呢，因为行向量表达更方便，无论是书写还是计算，行向量更加适合我们人类的习惯，因此我们选择了行向量来表达向量。&lt;/p&gt;

&lt;h6 id=&quot;理解旋转矩阵&quot;&gt;理解旋转矩阵&lt;/h6&gt;

&lt;p&gt;理解旋转矩阵我们应该从 2x2 矩阵讲起，比起3x3矩阵2x2矩阵更加容易理解。我们假设一个 2x2 的矩阵：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	[2, 1]
	[-1,2]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们可以认为它是由两个行向量a,b构成，a为(2,1)，b为(-1,2)，即&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	[2, 1]  等  [a]
	[-1,2]  于  [b]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在平面坐标系中，a和b的表达如图：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;图中a，b向量展现在2D坐标系中是两个互相垂直的向量。我们把这两个a,b向量可以看做是，从两个标准向量(1,0)和(0,1)旋转并且放大后的向量，即如图下所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;图中，(1,0)和(0,1)向量，缓缓的旋转，并且放大，逐步变成了(2,1)和(-1,2)，即从原来的(1,0)和(0,1)向量上，旋转了β度并放大了2.236倍。我们可以认为矩阵由标准矩阵旋转并缩放而来，这是矩阵的几何解释。对于标准矩阵来说，旋转缩放后形成了另一个矩阵，这个结果矩阵就是我们计算的‘变换矩阵’。对于任何一个向量来说，乘以‘变换矩阵’就能得到我们所要表达的旋转和缩放值，我们举例的矩阵就表达了向量旋转β度和2.236倍的缩放，那么任何二维向量乘以这个矩阵，就会得到在标准坐标系中以标准轴为基准旋转β度，并且以标准轴为基准放大2.236倍。&lt;/p&gt;

&lt;p&gt;这样看矩阵还是不够直观，我们来看看更直观的表达方式，如果一个矩阵要表达旋转β度，那么它的a,b的向量该是如下图：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;a就是 [cosβ, sinβ]，b就是 [-sinβ, cosβ]，它们分辨表达了标准向量(1,0)和(0,1)旋转β度后的向量。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	[cosβ,  sinβ]
	[-sinβ, cosβ]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;因此任何向量乘以这个这个旋转矩阵都会在标准坐标系中以标准轴为基准旋转β度。&lt;/p&gt;

&lt;h6 id=&quot;理解了二维空间的矩阵旋转原理我们延伸到三维空间就容易多了&quot;&gt;理解了二维空间的矩阵旋转原理，我们延伸到三维空间就容易多了。&lt;/h6&gt;

&lt;p&gt;三维空间的矩阵也可以像二维空间一样理解，用三个向量来表示空间中的矩阵，即&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	[a]   [Ax, Ay, Az]
	[b] = [Bx, By, Bz]
	[c]   [Cx, Cy, Cz]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;一个绕x轴旋转β度的旋转矩阵为：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	[1, 0,     0]
	[0, cosβ,  sinβ]
	[0, -sinβ, cosβ]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;那么一个绕y轴旋转β度的旋转矩阵为：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	[cosβ, 0, -sinβ]
	[0, 1, 0]
	[sinβ, 0, cosβ]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;于是一个绕z轴旋转β度的旋转矩阵为：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	[cosβ, sinβ, 0]
	[-sinβ, cosβ, 0]
	[0, 0, 1]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;用β度所形成的向量来表达坐标空间中的旋转矩阵，这样的表达可以帮助我们更加清晰的理解旋转矩阵的几何表达意义。上述只是对某一个轴进行旋转β度，如果我们要对某个向量做各个方向轴上的旋转，比如在x轴上旋转20度，再在y轴上旋转30度，最后在z轴上旋转15度，相当于这个向量在坐标系中旋转了20°,30°,15°，这时我们该怎么办？其实有了上述对各轴的旋转矩阵，我们就能很容易计算出各方向上的矩阵，就如上面提的问题，在各轴上都有旋转角度，我们就可以用先旋转x轴，再旋转y轴，最后旋转z轴的方式来计算最后的结果，如图：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;图中，c向量乘以x轴旋转矩阵，再乘以y轴旋转矩阵，最后乘以z轴旋转矩阵，即：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;c * Mx * My * Mz = c`(得到旋转后的结果向量)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;其中Mx为以x轴为基准旋转20度的旋转矩阵，My为以y轴为基准旋转30度的旋转矩阵，Mz为以z轴为基准旋转15度的旋转矩阵。c乘以Mx得到旋转x轴后的向量，再乘以My得到旋转y轴后的向量，最后乘以Mz得到旋转z轴后的向量，最终得到结果。矩阵乘法具有结合律，也就是说:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;c * Mx * My * Mz = c * (Mx * My * Mz)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;其中 Mx * My * Mz 得到的结果就是我们需要的‘x轴上旋转20度，再在y轴上旋转30度，最后在z轴上旋转15度’的旋转变化矩阵。任何一个向量乘以我们计算出来的这个变化矩阵都会同样在各轴上旋转同样的度数。&lt;/p&gt;

&lt;h6 id=&quot;绕任意轴旋转&quot;&gt;绕任意轴旋转&lt;/h6&gt;

&lt;p&gt;我们前面说的都是绕标准轴即x轴，y轴，z轴的旋转，其实这个标准空间上得到的矩阵还远远不够我们需要的计算，很多时候我们需要计算绕任意向量或者说绕任意轴的旋转。&lt;/p&gt;

&lt;p&gt;我们来推导一下绕轴 N 旋转角度 β 的矩阵，先定义旋转矩阵为 F(N, β)，v为需要旋转的向量，于是公式满足:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	v * F(N, β) = v' (v向量绕 N 轴，旋转β度后的结果v‘)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这个公式中v向量为已知，N绕轴为已知，β绕轴度数为已知，还需要知道哪些变量，如图所示：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;图中，v 到 N 的投影分量可以计算得到 v1，进而可以计算得到 v 到 v1 的垂直向量 v2，最后用垂直向量 v2 与角度 β 可以计算得到 v’ 与 v 到 N 的投影分量的垂直向量 v‘’，最后由 v‘’ 和 v1 计算得到 v‘。其中用到的计算方式有，向量投影计算公式，向量旋转计算公式，垂直向量计算公式，最后结果为：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;一个看起来很复杂的，只要一步步推导就能得到的结果矩阵，虽然推导的结果公式是一个相对复杂的公式，但其实结果并不重要，很多时候我们也记不住，只是我们用心推导的过程比较重要，也只有这样我们可以加深对旋转矩阵的理解，推导的过程也运用到了很多其他方面的知识巩固了投影等公式。’用进废退‘的我们很容易忘记这些公式，但只要印象深刻我们就知道有这些公式的存在，它们的原理是什么，在需要用到时搜索一下很快就能进入逻辑数学的心流状态。&lt;/p&gt;

&lt;p&gt;参考文献：&lt;/p&gt;

&lt;p&gt;《3D数学基础:图形与游戏开发》
《齐次坐标》Daisycv http://www.360doc.com/content/10/1226/10/3843418_81406265.shtml&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第十章，地图与寻路(四) 地图的制作与优化</title>
   <link href="http://www.luzexi.com/2019/08/17/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-%E5%9C%B0%E5%9B%BE%E4%B8%8E%E5%AF%BB%E8%B7%AF4"/>
   <updated>2019-08-17T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/08/17/Unity3D高级编程之进阶主程-地图与寻路4</id>
   <content type="html">&lt;h3 id=&quot;地图的制作与优化&quot;&gt;地图的制作与优化&lt;/h3&gt;

&lt;p&gt;场景的制作占到所有项目制作中非常大的一块工作内容，我们来说说场景制作在技术层面上技巧。上节把地图编辑器学习了一下，这节我们就在地图编辑器之外说说，地图场景的制作与优化。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;我们先来说说场景中的地图，准确的说应该是地形，地形是场景中比较关键的一部分，在项目中地形的制作分为几种：&lt;/p&gt;

&lt;h3 id=&quot;定制式地形&quot;&gt;定制式地形&lt;/h3&gt;

&lt;p&gt;定制式地形比较常见，由3D设计师制作出来的网格模型作为地面的地形放置在场景中，相当于一个模型放置在场景上一样，给这个网格模型加入MeshCollider碰撞组件(这里因为是静态的地形所以静态的碰撞体使用 Mesh Collider 性能还尚可，如果是动态物体则不能使用 Mesh Collider)。也有不需要网格模型的，比如使用高度图来计算坐标。&lt;/p&gt;

&lt;p&gt;有的项目会使用Unity3D内置的地形，也相当于将一个网格模型放置在场景中，Unity3D的地形可以通过更改地形高度图来改变起伏，不同通常地图项目用 Maya 和 3DMax 等模型软件来构建地形，这种做法能更加容易的被设计师掌控以及自由发挥，也更加节省资源，对程序来说在处理性能优化时会更加容易一些。其他3D物体，比如石头，小石块，草，树，动物等也是同种方式放置在场景中。&lt;/p&gt;

&lt;p&gt;定制式地形的好处在于地形和地图的制作不受限制，地形设计师和场景制作师能根据自己的喜好和想象中的画面来自由得定制场景，他们可以任意的移动、旋转、缩放、更换、修整、完善等，这能让他们对场景上的把控有很大的自由度，这种方式是比较常用的单一场景的制作方式。&lt;/p&gt;

&lt;h3 id=&quot;程序拼接地形方式&quot;&gt;程序拼接地形方式&lt;/h3&gt;

&lt;p&gt;用程序拼接地形的主旨意图是希望能在一定的规则下更好的为设计人员自由设计更好的地图而选择的方式，这样在规则下去做一些事情从设计和性能上求得一个平衡点，整个地图由程序生成，通常会一个或者几个模型的结构铺满整个地图。&lt;/p&gt;

&lt;p&gt;用这种方式制作地图最常见的要属2D的RPG游戏，2D角色扮演类游戏中，几乎整个屏幕的地形都需要用地图编辑器来建立和拼凑，运行时程序会将地图数据加载进来后，对每一块的地图都进行动态的拼合，这样一来整个地图就只有一个drawcall，因为它只使用了一个网格(程序生成的)和一个材质球。对此种方式我们做一些分析：&lt;/p&gt;

&lt;p&gt;我们先来看看2D RPG游戏中，是如何动态拼合整个地图。现在很多2D游戏都通过Unity3D来实现，其实2D也是有Mesh、顶点、UV的概念，和3D比起来只是少了1个维度，因此在2D游戏中的地图程序拼合手法也是通过生成顶点和三角来完成的。我们可以设想整个地图就是一个大的矩形，地图中每个方块是这个大矩形中的一个格子，而每个格子都由2个三角形构成，既然我们能计算出每个方块的位置，我们就能计算出每个三角的顶点和位置。简单来说地图是一个大矩形，它由很多个大小相同的小方块构成，小方块的大小可以通过配置文件的形式来调整，这样有规则、大小一致、位置可寻的网格可以通过程序生成。&lt;/p&gt;

&lt;p&gt;我们用三角形的方式拼接每个方块，并调整地图方块上顶点的UV，UV指向的是地图贴图中的某一块，而地图贴图以图集的方式存储地图上不同类型的贴图。就这样所有的方块拼合成了一个大网格，每个方块上的顶点上的UV都指向地图贴图中的某一块地块内容，地块贴图内容全部集中在一张图集里。&lt;/p&gt;

&lt;p&gt;伪代码：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;		//生成地图
		void Generate_map()
		{
			//遍历每块矩形的方块位置
			for i to width_count then
				for j to height_count then
					mesh = generate_trangle_by_rectangle(i,j,_type, texture_info);
					AddCombine(mesh);
				end
			end
			CombineMeshList();
		}

		//生成矩形方块所需要的，4个顶点，4个索引，2个三角形，2个三角形的顶点索引，以及三角形的uv位置。
		void generate_trangle_by_rectangle(int _x, int _y, int _type, texture_info _tex)
		{
			//矩形的4个顶点
			point1 = vector2( (_x - 0.5) * width, (_y + 0.5) * height);
			point2 = vector2( (_x + 0.5) * width, (_y + 0.5) * height);
			point3 = vector2( (_x + 0.5) * width, (_y - 0.5) * height);
			point4 = vector2( (_x - 0.5) * width, (_y - 0.5) * height);

			//顶点增加后的索引位置
			point_index1 = add_point(point1);
			point_index2 = add_point(point2);
			point_index3 = add_point(point3);
			point_index4 = add_point(point4);

			//三角形的生成时的顶点
			trangle1 = [point1, point2, point3];
			trangle2 = [point3, point4, point1];

			//三角形顶点的索引信息
			trangle_index1 = [point_index1, point_index2, point_index3];
			trangle_index2 = [point_index3, point_index4, point_index1];

			//4个uv点位的信息
			point_uv1 = vector2(_tex.uv_x, _tex.uv_y);
			point_uv2 = vector2(_tex.uv_x + _tex.width, _tex.uv_y);
			point_uv3 = vector2(_tex.uv_x + _tex.width, _tex.uv_y + _tex.height);
			point_uv4 = vector2(_tex.uv_x, _tex.uv_y + _tex.height);

			Mesh mesh = new Mesh();
			mesh.trangles = [trangle1 , tangle2];
			mesh.trangles_index = [trangle_index1 , trangle_index2];
			mesh.uvs = [point_uv1, point_uv2, point_uv3, point_uv4];

			return mesh;
		}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述伪代码中，对所有地图中的方块进行遍历，在遍历中生成了每个方块所需要的顶点、顶点索引、uv数据，进而生成三角形网格。遍历完毕后将所有网格数据合并，使得整个地图是一个一体化的网格，渲染时只产生一个Drawcall。用程序拼接的地图可以通过地图编辑器来改变地形地貌，拼接完成的地图在渲染上的代价也相当的小，假如想换个地图样式，只需更换贴图就可以非常方便。&lt;/p&gt;

&lt;p&gt;我们需要注意整个地图需要些许工具链支撑，由于地图元素仅限于图集中的元素，每次增加、删除元素都需要操作地图图集，图集太大时就会有拆分图集的必要，把不同地图图集分为共享图集和各自地图的图集，这样就需要我们改变一个地图一个Drawcall的策略，渲染多个图集让多个Mesh凑成一个地图，每个Mesh代表一种类型的地图图集。&lt;/p&gt;

&lt;h6 id=&quot;3d-rpg角色扮演类游戏中也常常使用这种技巧来绘制游戏地图曾经在日本风靡一时的白猫计划就使用了这种方式&quot;&gt;3D RPG角色扮演类游戏中，也常常使用这种技巧来绘制游戏地图，曾经在日本风靡一时的《白猫计划》就使用了这种方式。&lt;/h6&gt;

&lt;p&gt;这种程序拼合的地图，策划设计师、关卡设计师可以在地图编辑器上，任意的绘制、拼接、同种类型的不同样式的地图（即在同一张贴图内容中的模型和地图元素），因此大大缩短了大量的场景试错时间。它能大量的生成出不同样式的地图，不需要制作大量的不同类型的3D模型，大大缩短了项目时间进度，深受游戏制作人的喜爱。&lt;/p&gt;

&lt;p&gt;那么在3D地图中是怎么拼接方块地形的呢？看上去比2D更加复杂的事情其实并不是。&lt;/p&gt;

&lt;p&gt;首先，我们在制作3D地形前需要制定一下模型的规范。3D地图中不需要我们自己来拼接三角形，我们用固定的地形模型，把每个地形模型都做成一个固定大小的立方体。假设规定每块地形规定为 10 x 10，在制作和拆分地形模型时按规定来拼接，每个3D地形模型都必须以 10 x 10 的标准来定制。单个模型的尺寸大小按照项目的地图拆分来，也可以是20 x 20 或 30 x 30，只是当标准制定完毕后，地形模型的制作必须要按照标准来做，每个元素都相当于一个地图块。&lt;/p&gt;

&lt;p&gt;其次，同一个地图上的地形模型的纹理贴图都最好并在一张图集上来制作，因为只有这样才能在合并模型才能合并材质球，并且在合并后让这么多的3D小方块只需要1个Drawcall渲染调用就可以绘制所有的地图，也只有这样才能解决太多模型需要渲染导致的爆量Drawcall问题。&lt;/p&gt;

&lt;p&gt;然后，在3D地形模型制作时需要些提前的设计和考虑，因为地图都被拆分成了N种类型的地形立方体，所以在制作的初期需要对整个地形有哪些类型的需求进行设计和探讨。在读取地图编辑器编辑的地图数据后，除了拼合地形模型立方体，实例化场景地图时也需要增加些许逻辑，为的就是适应更好的模型拼合，就像我们在制作和拆分城墙时那样，当城墙处于拐角处时，我们需要选择不同的拐角模型代替，判断它的左右前后有没有其他城墙，如果有则应该选择适当的连接城墙来适应周边的模型，左连接、右连接、前连接、后连接、以及前后双向连接，左右双向连接等等总共9种情况，有不同的9种拐角城墙所代替，这样才能完美契合周围的地块模型。&lt;/p&gt;

&lt;p&gt;最后的合并模型步骤就相对简单的多，按地形模型放在地图数据文件所描述的指定位置上，并如上面所说的选择好适配的模型，调用Unity3D的API方法 Mesh.CombineMeshes 合并所有模型。在合并时也会有三角面数的限制，合并后的模型三角形数量不能超过2的16次方减1个面(即65535个面)，如果超过则应该另起一个模型进行合并。&lt;/p&gt;

&lt;h3 id=&quot;常规场景的性能优化&quot;&gt;常规场景的性能优化&lt;/h3&gt;

&lt;p&gt;上面几节阐述了地图的拼接方式，其中用程序拼接合并地图块的方式确实大大降低了 Drawcall 的数量，提升了渲染的性能，但这也只是针对可拆分的地图类型项目，对大多数游戏类型来说地图是不可拆分成小块的立方体进行拼接的，因此我们还是需要更多的针对常规场景讲解优化的方法和技巧。&lt;/p&gt;

&lt;p&gt;首先我们要清楚的是，是什么造成了场景的低渲染效率。我们在这里罗列一下主要的几个问题：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	1.同屏渲染面数太多，GPU压力太大。

	2.渲染管线调用次数太多(Drawcall太多)GPU的并行处理没有很好的发挥作用

	3.贴图太重太大占内存大，导致显存的带宽负荷太重

	4.动画太多，蒙皮的计算消耗的CPU计算量太多

	5.Shader开销太大GPU的计算量开销太大
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;其实还有很多很多问题需要解决，比如实时阴影导致的 Drawcall 太多，以及透贴太多导致Overdraw 重绘问题比较严重，还有比如物体需要的Pass渲染太多，一个物体需要绘制多次才能完成，这些问题也是很重要的，这节我们就来说聊聊场景的优化。&lt;/p&gt;

&lt;h3 id=&quot;1渲染面数太多gpu压力太大&quot;&gt;1.渲染面数太多，GPU压力太大。&lt;/h3&gt;

&lt;p&gt;渲染面数太多一般都是同屏展示的面数太多，整个场景的网格面数太多只会让内存上升，摄像机里同屏的渲染面数才是比较重要的。&lt;/p&gt;

&lt;h6 id=&quot;unity3d引擎会裁切掉在摄像头之外的物体不让他们进入到渲染管线以减少消耗虽然裁切也会消耗小部分cpu但毕竟比起渲染整个物体要小的多&quot;&gt;Unity3D引擎会裁切掉在摄像头之外的物体，不让他们进入到渲染管线以减少消耗，虽然裁切也会消耗小部分CPU，但毕竟比起渲染整个物体要小的多。&lt;/h6&gt;

&lt;p&gt;很多45度固定摄像头视角的游戏并不会将摄像机抬起来让更多的画面进入摄像机，但有很多游戏摄像机的旋转角度是自由的，这导致在摄像机抬起时会让许多模型进入摄像机的渲染范围。大部分引擎包括Unity3D都有对模型物体在提交GPU前进行一次裁剪，在进入渲染管线前每个3D物体能计算出或者已经计算好了一个包围盒即Bounds，这个包围盒由8个顶点组成，加上旋转矩阵就能计算出每个顶点是否在摄像头的锥形体范围内。&lt;/p&gt;

&lt;p&gt;裁切的算法中包围盒8个点中只要有一个点在锥视体内，就认为是需要渲染的不可被裁剪，如果8个顶点都不在摄像头的锥形体范围内，才认为是不需要被渲染的，这时Unity3D引擎会阻止这个模型渲染，在渲染调用前就抛弃它。因此我们要格外注意模型的包围盒，有些包围盒在模型制作时由于错误的操作导致包围盒并不适应模型大小，过大的包围盒导致GPU性能浪费。这种Unity3D引擎上的裁剪，帮助我们屏蔽掉了很多不需要渲染的物体，虽然裁剪也会花去些许CPU，但比起要绘制一个3D物体的计算量来说要少的多。&lt;/p&gt;

&lt;p&gt;场景中如果是一个模型很大很长会让裁剪失效，常常在我们项目中有模型范围从南延伸到北贯穿整个场景，这样的模型包围盒会很大很长，常常让引擎的裁剪优化失效。一旦模型长到覆盖了整个场景，包围盒总有一段在摄像头内导致不会被引擎裁剪，这样就会浪费很多不必要的GPU计算，因为网格被传入后由GPU来负责对每个三角形裁剪，比起用包围盒的方式裁剪要费力的多。因此我们在场景中要考虑对过大过长的模型进行拆分，不能让它跨很多个区域范围，不过注意拆分的过细也不行，因为这样会带来更多的Drawcall，理解了裁剪原理会对场景物件的颗粒度大小的把控会更加清晰一些。&lt;/p&gt;

&lt;h6 id=&quot;摄像机的远切面太长就会包含太多的物体在视野范围内这导致我们前面说的引擎内裁减失效更多的物体会被送去渲染增大了gpu的压力&quot;&gt;摄像机的远切面太长就会包含太多的物体在视野范围内，这导致我们前面说的引擎内裁减失效，更多的物体会被送去渲染，增大了GPU的压力。&lt;/h6&gt;

&lt;p&gt;最简单直接的方法就是，拉近摄像头的远切面距离，减少模型进入视野内的数量。视野范围内的物体减少，需要渲染的模型面数也就减少了，CPU和GPU的消耗自然下降，让视线范围在一个合理的范围内是我们需要关注的摄像机参数。&lt;/p&gt;

&lt;p&gt;这种方法毕竟不能大用，因为它缩短了视野直接裁减三角形，让画面不那么细腻美观，我们很多时候希望看到更远的地方。我们既希望能看到更多更远的物件，也想降低渲染带来的压力。世上没有免费的午餐，想降低CPU和GPU的消耗，至少得用大量内存来换。LOD，即Level of detail，就精通此道，用内存换CPU和GPU。&lt;/p&gt;

&lt;p&gt;LOD需要我们把3D模型做成多个不同细节级别的模型文件，每个级别的模型都比上一级模型的面数少一些，这样面数少的模型用于远距离的渲染，而面数多的则用于近距离渲染。当摄像头拉远时，启用面数更少的模型，当摄像头拉近时，则启用面数更多的模型。这样即使众多的模型在摄像头范围内等待渲染，也只有几个面数多的模型靠近摄像头，而大部分模型都离摄像头比较远启用的是面数比较少的模型。这些远离摄像头的3D模型面数很少，即使数量众多，它们所展示的低面数的总和也是可以接受的，当同屏需要渲染的面数少时，GPU需要处理的三角面也少，虽然Drawcall数量没变化，GPU需要处理的渲染指令数量没有变化(渲染状态和渲染指令时串行处理的)但处理的量少了，处理速度自然就快了。&lt;/p&gt;

&lt;p&gt;我们常提到的 Mipmap 其实也点LOD方式的运作意味，只是Mipmap的主要意图不是优化性能而是物体像素比导致的画面瑕疵问题，由于Mipmap会根据远近来选择贴图大小，因此在绘制场景时也节省了不少GPU与内存之间传输数据的带宽，我们将在后面的渲染章节更详细的讲解，这里只是简单陈述一下。Mipmap和LOD有差不多的理念，对于离摄像机远的物体启用更小的纹理贴图，对离摄像机距离近的物体使用正常的纹理贴图，只是Mipmap使用的不是距离而是渲染时的像素与贴图像素块的大小比例。&lt;/p&gt;

&lt;p&gt;当然天下没有免费的午餐，提升了CPU和GPU的性能就得增大内存的消耗，内存消耗太多也会有得不偿失的时候，因此在使用LOD时我们考虑分多少级的细节级别上也需要斟酌一下。针对不同的项目分级的层数不同，拿大型MMORPG来说，那种可以在天上飞的游戏，看到的物体自然就很多，如果LOD分级少了画面则会显得突兀，而中小项目LOD分级太多则加大了内存量和制作时间得不偿失。&lt;/p&gt;

&lt;h3 id=&quot;2渲染管线调用次数太多drawcall太多gpu的并行处理没有很好的发挥作用&quot;&gt;2.渲染管线调用次数太多(Drawcall太多)GPU的并行处理没有很好的发挥作用&lt;/h3&gt;

&lt;p&gt;除了渲染面数太多带来的GPU压力，渲染调用次数太多也是造成GPU瓶颈的一大问题。Drawcall太多的问题大都因为场景中的模型物体太多引起的，也可能附带着Shader中的Pass太多使得渲染压力更大。&lt;/p&gt;

&lt;p&gt;这个问题是最简单粗暴就是‘干掉’在场景中众多的模型物体，以及‘注释掉’Shader中不必要的管线。这无疑将美术设计师和场景设计师的辛勤工作的成果给白白的浪费了，我们尽量不考虑这种方式，不过适当的移除些许不必要模型的其实也有助于场景制作。&lt;/p&gt;

&lt;p&gt;我们希望在不毁坏场景的前提下做些优化，关键点就在于‘合并模型’。我们不希望破坏场景的美观，不想让设计师们辛苦设计的场景毁于性能优化，为了两全其美我们需要合并渲染。在制作完场景后同屏内需要渲染的面片数量已经确定，我们在不减模型在场景中的数量以及不减少单个模型面数的情况下，同屏画面上需要渲染的三角形面数已经不会变化。从整体上看当我们每次调用管线渲染（即调用Drawcall）都会传送给渲染管线一些三角形，如果传送的三角形数量比较少，则调用的Drawcall次数就会增多，因为三角形数量时确定的。我们需要合并这些三角形面片然后一并传递给管线，将原来需要多次的调用管线变成一次，只有这样才能大大降低 Drawcall 的数量，降低渲染调用次数。&lt;/p&gt;

&lt;p&gt;那么减少渲染管线即减少 Drawcall 的数量究竟有什么好处？减少了渲染管线的数量，即降低了Drawcall的数量后，尽管并没有减少任何需要渲染的面片数量，只是每次传递给渲染管线的顶点和面片数增多了传递的次数减少了，三角形数据在进入渲染管线后可以开启GPU并行处理，原本单行线处理变成了多线处理，将像是单车道扩展为8车道那样速度成倍的增加。&lt;/p&gt;

&lt;p&gt;原本调用一次渲染就要在队列中等待管线渲染完毕后才进行下一次渲染，合并后就不同了。不再是像以前那样调用这么多次Drawcall，渲染画面时Drawcall的队伍都要排的老长，都等在那排着队等待GPU处理。合并后就不再是这样了，排队的数量少了队伍短了，虽然每个排队的数据都很‘胖’（数据量很多），但是一旦进入GPU处理阶段上百条生产线并行处理这些数据，因此我们要想方设法的合并各种模型减少Drawcall。&lt;/p&gt;

&lt;p&gt;Unity3D引擎自带有多种合批方式，前面几个章节有详细的介绍这里我们再简要的阐述一下，Unity3D引擎中有动态合批、静态合批、GPU Instancing 三种合批方式，其中动态合批在合并的模型时要求比较高，包括对模型面数、法线、切线、Pass数等都有要求，静态合批则放宽一些但也有自己的规则，它通过消耗的内存来换取CPU，而GPU Instancing 的原理是通过对一个模型传递更多的信息数据从而让它绘制在不同的位置与不同的样式以达到减少Drawcall的目的。&lt;/p&gt;

&lt;p&gt;这里我们不讨论通过Unity3D引擎里的功能来做‘合并’的操作，我们希望能够自己掌控‘合并’效率和效果。部分原因也是因为引擎自带的合批方式通常都是通用的合批方式，规则比较严格，合批率比较低，我们假设搁置它而选择自己动手丰衣足食。&lt;/p&gt;

&lt;h6 id=&quot;合并分实时合并与非实时合并两种方法&quot;&gt;‘合并’分实时合并，与，非实时合并两种方法&lt;/h6&gt;

&lt;p&gt;实时合并会消耗大量的CPU，因为它要读取多个模型，并创建新模型数据，不断的创建新模型数据，就会导致CPU的消耗。这里由于前面模型的章节已经详细阐述过因此我们简单阐述一下，合并模型时要求模型使用相同材质球，读取所有具有相同材质球的模型数据，生成一个新的合并后的模型并隐藏原有的模型。倘若我们在制作时发现很多模型材质球使用的Shader和Shader的参数一样，只是贴图不一样，我们可以离线合并它们的贴图后再放到实时渲染时进行合并。&lt;/p&gt;

&lt;p&gt;非实时合并则全是线下的合并，即大家常说的静态合并。Unity3D静态批处理的规则比较严格，即要求相同材质球相同贴图还要相同的模型，而且内存消耗比较大，因此我们还是建议使用自己手动合并的方式，即可以自己手动拼接场景中静态不动的、有相同材质球的模型，也可以用程序或插件来拼接，比如 MeshBaker，通过 MeshBaker 来制作和合并模型与纹理贴图。静态合并Mesh的好处就是，游戏中不需要实时消耗CPU去合并模型，节省了不少实时开销的CPU，同时也降低了很多Drawcall的数量。&lt;/p&gt;

&lt;p&gt;合并这档子事当然也会过犹不及的，如果把所有的在场景内的物体全部合成为一个模型，无论摄像头能不能看到的地方都合并了，那么GPU的压力也同样会很大，因为这样的话我们前面提到的引擎自身做的第一层包围盒裁剪就不能生效了。渲染时会一股脑的将整个模型数据塞进渲染管线中由GPU来裁剪，这样会增大GPU的计算压力，GPU要裁剪整个地图的所有模型面片，计算量会是巨大的。因此我们在静态合并时也要适度，即合并附近距离不太远的、可以称为一块范围内的模型，这样即减少 Drawcall 次数，也为引擎裁剪让出了空间，降低了GPU裁剪的压力。&lt;/p&gt;

&lt;h3 id=&quot;3贴图太重太大占内存大导致显存的带宽负荷太重&quot;&gt;3.贴图太重太大占内存大，导致显存的带宽负荷太重&lt;/h3&gt;

&lt;p&gt;贴图太多宽带压力太大导致的问题，主要是因为GPU在渲染时需要将内存的纹理拷贝到显存中去才能使用在GPU中渲染造成的。所以拷贝的消耗和显存的消耗也是很大的。现代设备显存只存在于主机和PC游戏中，在手机中没有显存的概念，它只有内存，手机设备中的显存都是内存内部的拷贝，即手机中会为GPU预留出一块用于渲染的内存块作为缓存。&lt;/p&gt;

&lt;p&gt;其实手机与PC机的架构完全不一样，这导致内存的存取方式也不同，PC机中向GPU传输纹理贴图是从内存向显存拷贝，这其实意味着贴图纹理有两份，一份在内存，一份在显存，当引擎调用渲染指令时会向GPU传输纹理贴图，通常GPU会拷贝一份纹理到现存中，不过这也是短暂的，显存只是相当于缓存作用，引擎的每次渲染调用都会重置渲染状态，向GPU传输纹理贴图，如果纹理贴图已经存在于显存中则不用再传输，如果显存不够则要清除掉显存中一部分内容以空出空间来应付当前的指令。显存理论上说是越大越好，这样每次渲染调用就不用被覆盖，在下一帧渲染时就可以重复利用前面已经传输过的纹理。&lt;/p&gt;

&lt;p&gt;现代手机设备，安卓与IOS的架构则完全不同，手机中完美没有显存这个缓存硬件，只能从内存中分离出一部分来使用。而且在安卓和IOS的架构下也不再存在着纹理在内存和显存的拷贝，CPU和GPU的纹理地址指向同一个物理地址。当手机设备需要纹理时，系统从纹理文件中直接加载进入指定的物理内存不再拷贝到其他地方。&lt;/p&gt;

&lt;p&gt;除此之外有两份内存的情况也时常在引擎内发生，Unity3D中贴图有个选项 Read/Write，当Write被勾上时就会在引擎的内存中有另一份拷贝，这是由于Write属性的贴图随时会被更改像素内容，为了不影响GPU贴图的渲染更好的在CPU与GPU之间运作，系统采用三重缓存机制，即系统会另起了一份贴图来使得CPU与GPU之间无障碍协作。因此我们在贴图设置时，首先要注意 Read/Write 选项是否需要被开启，绝大部分贴图是不需要被开启的，这些贴图被开启后造成的2倍内存也是不必要的。&lt;/p&gt;

&lt;p&gt;贴图太大太多会影响渲染，主要影响的是GPU的拷贝过程，最好的办法是缩小贴图和压缩贴图。看起来挺简单的，其实我们在实际项目中，我们不能为了性能随意去缩小和压缩贴图，这样为导致项目因贴图质量太糟糕而影响画面效果。我们需要针对每个部分的贴图逐一去了解和设置参数，或者用Unity Editor脚本对某些文件夹下的贴图统一做强制性的设置。其实每个功能部分的贴图都有其用途，比如UI中的贴图分，大部分是图集和Icon图片，图集一般都是无损质量，不同级别的设备也会离线LOD的方式做多份拷贝，每份的设置都会有所区别，大部分UI贴图都不需要 Mipmap采样，Icon图也是一样。针对低端设备会对UI贴图做些压缩，压缩后UI的质量会有所降低，换来的是性能速度会快很多，这是由于无损的贴图在内存上和压缩的贴图通常有5-10倍的差距，贴图传入GPU所执行的拷贝过程会消耗一部分算力。&lt;/p&gt;

&lt;p&gt;3D模型的贴图也是其自己的特点，模型纹理贴图通常都是2的幂次的大小存在。2的幂次大小的纹理GPU处理起来比较顺手，以前GPU只能处理2次幂大小的贴图，现在GPU已经强大到可以处理非2次幂大小的贴图了，但速度和效率还是会相对慢一些。这些贴图也通常是可以压缩的，并且大都需要带有 Mipmap 采样生成标记的。要压缩多少压缩到什么比例才适合，可能并没有绝对统一的标准，大部分项目都会针对设备平台去压缩，以及针对高中低端设备做压缩设置，如果你没有很好的压缩策略可以选择中位数策略即Normal Compress普通压缩。&lt;/p&gt;

&lt;p&gt;贴图的大小也比较重要，每个项目在开始前最好能好好的规范一下，我们在模型章节里讲了许多规范的制定，只有遵守良好的规范项目才能稳步前进，毕竟不是一个人在努力，大家的努力要转化为有效的合作与更长远的发展。幸运的是，美术设计师无论将贴图做的多大，我们都可以在Unity3D里重新设置成我们需要的大小，Unity3D会将所有贴图都重新制作导出成我们指定大小的贴图和格式。因此即使前期贴图太大而导致的问题，可以在Unity3D的项目中将贴图设置回我们希望的大小尺寸。&lt;/p&gt;

&lt;h3 id=&quot;4动画太多蒙皮的计算消耗的cpu计算量太多&quot;&gt;4.动画太多，蒙皮的计算消耗的CPU计算量太多&lt;/h3&gt;

&lt;p&gt;模型动画确实是最令人头疼的一块内容，它是动态的而且时时刻刻都是计算网格的位置，不像静止的3D物件即使它们有时会出现或消失也不会消耗网格算力。而模型动画则不同，它们时时刻刻都在你眼前动来动去，即使不再屏幕上，大部分时候也需要一直保持动画的计算过程。&lt;/p&gt;

&lt;p&gt;对于模型动画的优化，在模型动画章节中讲的比较详细，这里主要是在前面讲解内容基础上以实际的应用场景简单的讨论一下。&lt;/p&gt;

&lt;p&gt;模型动画的消耗量最大的地方是CPU的蒙皮计算，如果有100个动画在屏幕中播放，CPU会极大的消耗在蒙皮计算上。蒙皮计算，其实质就是骨骼与顶点的计算，骨骼动画用骨骼点去影响顶点，每帧都需要计算骨骼点与顶点的偏移、缩放与旋转。如果动画模型的顶点数量很多，骨骼数量很多，由于顶点关联着骨骼点，多个骨骼点影响着顶点，那么计算量就会很大，消耗的CPU的算力自然就会很多。为了能减少计算量，我们减少顶点数是最直接的办法，又或者减少骨骼点的数量也是，这样能使得CPU降低消耗。&lt;/p&gt;

&lt;p&gt;让3D模型设计师和动画师去做减面和减动画骨骼的工作绝非易事，它涉及到画面质量削减平衡，在削减的时候设计师们需要顾及画面质量。这些都是我们程序员无法控制的，我们能做的就是从程序上尽量的降低开销。由于每帧都要计算网格的形状所以很费CPU，如果能把计算好的每帧顶点偏移量存起来，播放的时候直接偏移过去就会好很多，这样就不需要计算了只需要存储与偏移。我们可以把每个动画网格分批计算好另存为一个文件，这样有多少帧动画就会有多少个具体的网格存放起来，每次播放动画时，直接拿出这些网格一个个替换就省去了网格顶点的计算。&lt;/p&gt;

&lt;p&gt;先不说空间的占用量，毕竟这么多模型网格无法合并Mesh，每个动画模型都需要至少一个Drawcall来支撑，如果Shader中有多个Pass管线，就有更多，比如描边，实时阴影等。假设有100个这样的动画，就需要至少100个Drawcall来支撑。消耗还是太大，能不能合并Drawcall，就像合并普通Mesh一样，相同的材质球合并成为一个Drawcall呢？Unity3D的 GPU Instancing 为我们提供了合并的可能。它可以合并相同材质球，相同模型的Drawcall。它原理是将一个模型以不同的状态，在不同位置渲染只需提交一次GPU。&lt;/p&gt;

&lt;p&gt;SkinMesh Instancing 就借此解决了这个问题，它建立在GPU Instancing功能之上的动画优化方案。它把动画的模型数据在离线状态下计算好并存储在贴图中，这样解放了CPU的算力并转移到GPU中渲染。它把动画文件中的数据与模型数据结合分批计算好网格顶点的偏移量并存储在贴图中，由可编程的顶点着色器来根据参数来完成顶点的偏移，这样我们就不仅不需要计算而且还能一次提交就能渲染很多个位置的模型，每次渲染只需将顶点偏移就可以了，省去了大量的CPU计算蒙皮的消耗。&lt;/p&gt;

&lt;p&gt;与之相似的，也在着色器中解决动画问题的还有一些场景中的草、树、飘带和红旗的摇动，我们可以用顶点动画蒙皮计算的CPU消耗。这些摇动的动画是纯顶点算法计算出来的摇动，它用了一些摇动算法计算出顶点的偏移位置，使用消耗GPU算力来代替CPU算力，因为GPU它是并行的且更擅长顶点计算。&lt;/p&gt;

&lt;p&gt;当然这几种方法也各有利弊，在实际项目中需要根据实际情况做出选择甚至混合使用，我们在使用时应该衡量他们在项目中的限制和作用，尽量做最适合的选择。&lt;/p&gt;

&lt;h6 id=&quot;5shader开销太大消耗gpu算力&quot;&gt;5.Shader开销太大消耗GPU算力&lt;/h6&gt;

&lt;p&gt;？？？&lt;/p&gt;

&lt;p&gt;片段着色器中计算复杂逻辑，将计算转移到顶点着色器中
使用过多复杂的数学函数，使用近似公式代替
使用变量精度多大，减少不必要的精度
使用过多ifelse，编译器并不会分支
使用需要等待的功能，避免使用等待缓冲需要功能
使用过多pass，合并计算
使用mutile compile后的初始化，变体导致编译出有很多的shader，引擎在识别功能时会选择对应的shader，这导致很多shader没有被初始化的会被临时初始化
AlphaTest在手机设备上使用会让pre depth test 失效，导致性能开销反而增加
尽量能够在离线计算好的数值，尽量离线计算完后，使用常量来代替实时计算消耗&lt;/p&gt;

&lt;p&gt;参考资料：&lt;/p&gt;

&lt;p&gt;《Triple Buffering》apple developer 三重缓存机制&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>思路探讨(二十七) 如何应对30岁的躁动</title>
   <link href="http://www.luzexi.com/2019/08/10/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A827"/>
   <updated>2019-08-10T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/08/10/思路探讨27</id>
   <content type="html">&lt;p&gt;&lt;img src=&quot;/assets/金融/idea-talk-27.jpeg&quot; alt=&quot;idea-talk 27&quot; /&gt;&lt;/p&gt;

&lt;p&gt;最近对自己影响比较深的一段话。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;台风日没带电脑，只能在手机上打字。&lt;/p&gt;

&lt;p&gt;一整天只在小小房子里原地看看书，原地跑步。&lt;/p&gt;

&lt;p&gt;我依然坚持着自己独有一套理论，即，&lt;/p&gt;

&lt;p&gt;首先要想方设法每天都保持充沛的体力和旺盛的精力，即保持健身、正常的饮食、规律的作息。&lt;/p&gt;

&lt;p&gt;然后，每天必须看几页书以丰富自己的知识、看法、见解和视野，&lt;/p&gt;

&lt;p&gt;其他时间专注于工作，在工作中历练，在工作中积累经验和财富。&lt;/p&gt;

&lt;p&gt;无论处在什么样的状态下，开始努力都为时不晚。&lt;/p&gt;

&lt;p&gt;人只要一直努力下去，肯定比他不努力的强百倍。&lt;/p&gt;

&lt;h6 id=&quot;努力应该被称为一种特质是自驱力的体现如果你拥有这种特质总有一天你会发现所有你所拥有的都因努力而得不管你最终有没有成功努力这两个字将成了你的优势&quot;&gt;“努力”应该被称为一种特质，是自驱力的体现，如果你拥有这种特质，总有一天你会发现，所有你所拥有的都因“努力”而得，不管你最终有没有成功，“努力”这两个字将成了你的优势。&lt;/h6&gt;

&lt;p&gt;最近难得平静下来能安安静静的度过一天。也可以梳理下最近的生活和工作。&lt;/p&gt;

&lt;p&gt;30几岁依然是躁动的年纪，只是相比于20几岁，更加让我们躁动的是事业和家庭，比起40几岁，30几岁的我们还不够沉稳，心力还不够强大，经受的狂风暴雨还不够多，这种局面是无法改变，只有我们熬过30岁来到了40岁时，回头看才会知道这一切就是个“轮回”，只是40几岁又有了新的烦恼和痛苦。60后下去了70后起来了，70后下去了80后起来了，80后下去了90后起来了，世界从没因任何人停止过潮汐潮落。我们所感受到的只是我们以自我为中心的视角所看到的和感受到的情景罢了，其实并非我们所想象的那样美好或者那样糟糕，世界就这样悄无声息又平静的路过你，我，他身边，继续向前。&lt;/p&gt;

&lt;h6 id=&quot;如果说世界上有什么办法能够掌控未来也只有努力并且是持续的长时间的努力也只有这样才能在下一个阶段来临前打好结实的基础&quot;&gt;如果说世界上有什么办法能够掌控未来，也只有“努力”，并且是持续的长时间的努力，也只有这样才能在下一个阶段来临前打好结实的基础。&lt;/h6&gt;

&lt;p&gt;当下一个阶段来临时，才有足够强的实力，足够多的经验，足够大的视野，足够强壮的身体应对更加复杂的局面。&lt;/p&gt;

&lt;p&gt;也只有我们在当前阶段持续不断的努力，才能在下一个阶段来临时拥有足够多的优势，到那时才能更加从容一点。&lt;/p&gt;

&lt;p&gt;但也同样的，我们会不断面临再下一个阶段的挑战，努力永远都不可能停下来，我们一生都是艰苦而又美好的。&lt;/p&gt;

&lt;h3 id=&quot;我们在努力中寻找苦涩的快乐在汗水中绽放疲惫的笑容&quot;&gt;我们在“努力”中寻找苦涩的快乐，在“汗水”中绽放疲惫的笑容。&lt;/h3&gt;

&lt;h3 id=&quot;我们一起苦中作乐&quot;&gt;我们一起：苦中作乐&lt;/h3&gt;
</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第十章，地图与寻路(三) 地图编辑器</title>
   <link href="http://www.luzexi.com/2019/08/05/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-%E5%9C%B0%E5%9B%BE%E4%B8%8E%E5%AF%BB%E8%B7%AF3"/>
   <updated>2019-08-05T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/08/05/Unity3D高级编程之进阶主程-地图与寻路3</id>
   <content type="html">&lt;p&gt;对于任何游戏来说，地图与场景的是比较重要的，特别是对于中大型游戏来说，在地图和场景上花费的时间和精力占去了大部分。而对于大部分游戏类型来说，布置场景，优化场景，为地图场景写套编辑器是必不可少的，我们通常称他们为‘地图编辑器’。地图编辑器有哪几种实现方式，顺便讲一讲从哪些方面下手优化场景。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;h3 id=&quot;地图编辑器的基本功能&quot;&gt;地图编辑器的基本功能&lt;/h3&gt;

&lt;p&gt;什么是地图编辑器？Unity3D本身的编辑器就是属于场景编辑器，它为场景中3D物体以及逻辑脚本服务，我们通过Unity3D的场景编辑器向场景里添加物件，同时可以做移动、旋转、缩放等操作。它也提供将场景里的物体数据保存下来成为具体的文件数据，即Unity3D的Scene资源文件，只是这并不是我们需要的，我们需要的是可自定义的可解析的地图数据文件。&lt;/p&gt;

&lt;p&gt;对于一张完整的地图来说，我们需要的是能生成一个包含地图中所有元素数据的文件，并且我们可以通过这个文件还原整个地图。这个数据文件不只是可以在视觉上还原地图，还可以还原我们已经设定好的地图中的逻辑参数，这些参数包括障碍碰撞检测范围，触发事件，机关走向，剧情发展等。这时我们需要制作一个跟游戏逻辑有关的地图编辑器，以便设计师们能在地图中发挥对地图的设计理念。下面就让我们来说说地图编辑器是怎么实现的。&lt;/p&gt;

&lt;p&gt;地图编辑器一般分为3部分，一是可行走区域与障碍编辑，二是地形与物件编辑，三是游戏逻辑包括关卡、触发、事件、怪物出生点等参数配置。可行走区域与障碍物的构建，我们在前面寻路网格章节中有讲到过一些，在地图编辑器中可以用不同颜色的多边形来展示可行走区域与障碍区域，如果是二维方格形式的寻路可以通过展示方格来编辑可行走区域和障碍，如果是三角网格则可以通过多边形来设置障碍区域，或通过标记障碍物体的flag来设置障碍物，最后再通过读取地表网格与障碍物网格来生成三角寻路网格，也可以使用RecastNavigation Navmesh来扫描、制作三角寻路网格，最好是能够理解它的原理熟悉它的API，这样就可以在编辑器中用颜色区分出生成的可行走区域和障碍区域。&lt;/p&gt;

&lt;p&gt;地形一般都是一个或几块大的网格模型，也有用高度图来编辑的地形，与Unity3D的地形组件一样通过一张高度图和元素分布图来确定场景中的地形高低和植被范围。物件则以元素形式存在以坐标，旋转角度，缩放大小为基准形成的数据，大部分元素都是节点，模型，特效，因此坐标，角度，缩放这三样的数据记录是必不可少的。其他需要记录的数据也包括配置表ID，物件类型，范围大小，脚本名字等，即通常每个元素的数据为：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;struct map_unit
{
	position, //坐标
	rotation, //旋转角度
	scale, //缩放
	type, //类型
	table_id, //配置表ID
	size, //大小
	function_name, //功能名
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;其中position, rotation, scale 是基础的数据类型，它记录了需要被展示在场景中的位置，角度，缩放大小。而其他的数据，例如 type 可以用来表示这个物件的类型，是人，是怪，是门，是机关，还是不会动的静态场景物件。talbe_id 可以用来表达这个前面 type 类型所对应的配置表ID，用 table_id 可以映射到具体数据表里或者说Excel表里的某一行数据。各种type下的展示效果可以根据这个 talbe_id 的不同而不同，例如怪物有很多种，每个talbe_id 都代表了怪物表里的一种怪物。size 大小则可以认为是物体所触发事件的范围，比如当角色进入5 * 5 这个size大小范围时将触发机关、触发剧情、触发任务、触发生成怪物等。&lt;/p&gt;

&lt;p&gt;function_name 一般都会指向某个功能性逻辑或者功能性逻辑系统的配置文件。当某个物件size范围内被触发时，功能性逻辑就根据function_name调用配置文件执行操作或直接执行操作。使用它的意图是通过它指向的是某个具体的功能，每个物件都有可能具有不同类型的操作指令，指令可能是纷繁复杂的，因此它通常都不指向某个函数，而是指向某个配置文件，这个配置文件背后是一套执行流水线的系统，根据这个配置文件可执行一套固定的指令流水线。&lt;/p&gt;

&lt;p&gt;为了能配合地图上的其他系统逻辑，地图管理类管理地图上的元素是必不可少的，这样其他系统才能有接口来调用地图上的元素从而执行逻辑。地图管理类更多的起到了存储、更新地图元素，以及存储和更新可行走区域数据的作用。&lt;/p&gt;

&lt;h3 id=&quot;数据协议格式在编辑器中的选择&quot;&gt;数据协议格式在编辑器中的选择&lt;/h3&gt;

&lt;p&gt;关于数据的存储与解析，我们在前面的数据协议章节中专门做了详细讲解，这里我们做一些应用，该怎么选择地图数据的数据格式和存储协议。各类协议在这里也能体现其不同的优势。&lt;/p&gt;

&lt;p&gt;我们把数据都存储到文件中，当我们选定一种协议格式来存储数据文件，就得用相同的协议读取。使用每种协议都有其原因，假设说我们在众多协议中选择了使用Json协议，我们选择Json协议的意义是什么呢，乍一眼看来Json占用的空间又大，解析又慢，导致很多人都摒弃它，那么我们为什么还用它。肯定是因为Json的特性，简单、易学、明文易懂，只要有一点点编程知识的人都知道Json的格式，即使不知道也只需要花几分钟就能明白其原理，这对于众多新手来说是适合的门槛线，他们能快速上手快速融入团队。除此之外Json的容错率和对协议更新的支持能力也很强，这让Json在整个项目不断更新迭代中有着很好的适应能力。&lt;/p&gt;

&lt;p&gt;也有很多协议比Json空间占用小，解析快，效率高，自定义格式的数据协议就是其中一种，如果我们要使用自定义格式的数据协议会是什么理由呢？假设说我们使用自定义格式的协议，把每个变量都转换成byte流形式存储，这种形式的存储是最能够节省空间，也是最能掌控数据的方式。用自定义协议做存储格式的理由，可能是想把空间压缩到最小，并且同时能掌控存储过程不让其他第三方干扰。自定义协议在开发过程中也有很大的缺陷，整个项目是在不断的迭代的因此数据常有变化，自定义二进制流格式对变化适应能力非常弱，虽然也不是没办法但确实有点代价。代价就是要为每个版本的数据格式各自写一个完整的数据读取和存储的程序。每增加一个版本，为了维护旧的数据，都要在原有的数据解析的程序外，增加一个新的数据解析程序。就如下面的伪代码：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;void ReadData(io_stream)
{
	version = io_stream.read_int();
	if( version == 1 )
	{
		Read_version1(io_stream);
	}
	else if(version == 2)
	{
		Read_version2(io_stream);
	}
}

voi Read_version1(io_stream)
{
	id = io_stream.read_int();
	level = io_stream.read_int();
}

voi Read_version2(io_stream)
{
	id = io_stream.read_int();
	name = io_stream.read_str();
	gold = io_stream.read_int();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;代码中读取数据时考虑了多种版本的兼容，使用了不同函数应对不同版本的办法，为每个版本写一个特有的读取顺序，在读取开头，用一个int元素来代表是应该使用哪个版本来读取数据，得到数据版本号后就能对应到不同版本的读取方法。&lt;/p&gt;

&lt;p&gt;自定义方式比Json大大减少了空间提高了效率，但也同时增加了维护的复杂度。如果非要使用二进制不如使用 Proto Buff 来的更好。使用Protobuff协议来作为存储格式，即使是数据格式升级和改变也都能轻松的应对，对于存储二进制形式的数据格式来说确实是一个比较好的选择，具有协议数据小、解析速度快、协议升级方便的优点。可惜它最大的缺点是不能明文显示，我们在项目迭代过程中常有对协议有重大改动，或者希望查找数据中的某项内容，由于不是明文，查找起来就会很不方便。&lt;/p&gt;

&lt;p&gt;Json就具备了明文的特点，我们一眼就能知道内容，使用文本查找就能找出数据的问题和位置。因此我们在项目中可以使用两种协议，一种是Json这样明文的协议，在编辑时使用，另一种是使用 Protobuff 在真正发布的游戏中使用。即平时用Json编辑存储和编辑数据，发布打包时将数据转换成Protobuff，使得加载和使用更加高效，这种方式对工程项目来说可能会是比较两全其美的方案。&lt;/p&gt;

&lt;h3 id=&quot;地图加载方式&quot;&gt;地图加载方式&lt;/h3&gt;

&lt;p&gt;我们说地图编辑器主要的作用是场景地图的编辑功能并能有一个地图数据文件可供前后端使用，可视化体验良好的地图编辑器能够更好的帮助场景设计师、关卡设计师编辑他们觉得更好更绚丽更好玩的地图场景。也正因为有了地图数据，我们在游戏中实例化地图场景时更能够更有序，在加载地图时有更明确的目标。&lt;/p&gt;

&lt;p&gt;地图数据到场景还原通过加载资源实例化资源的方式进行，这里我们来了解一下地图加载的几种形式。地图加载的形式有两种，一种是根据地图数据加载全部地图资源后实例化到场景，一种是异步的方式边玩边加载，在加入地图前先加载一些必要的资源然后边玩边加载和替换，其实边玩边加载也分好几种，其中一种就是按需加载地图中的元素模型。&lt;/p&gt;

&lt;p&gt;全部加在资源并展示显然是最容易和方便的，根据地图数据文件把所有数据反序列化到内存，针对每个元素的数据，加载它们所指定的模型或效果的资源并实例化到指定的位置，设置旋转和缩放，如果需要的话再对挂在物体上的脚本参数进行设置。&lt;/p&gt;

&lt;p&gt;一次性加载这么容易和方便，我们有时可以完全不需要地图编辑器，一个prefab搞定整个场景。这样说来话为什么还需要地图编辑器？对于场景比较单一的项目来说确实如此，但随着功能和需要的扩大，实际场景中开始时很多物件并不需要加载到场景中，而是根据个人玩家的游戏进度来判断加载的内容。这时如果还是按一个prefab搞定一个场景，把所有资源都加载进来，势必浪费很多内存和CPU。地图编辑器就能帮助我们根据需要加载物件，帮助我们节省不必要的开销。&lt;/p&gt;

&lt;p&gt;场景比较大的项目，随着场景不断扩大，场景中物体的种类和数量也越来越多，加载全部资源需要消耗的CPU和时间也越来越多。从原本只要加载几个面片当做地形的prefab，发展成了带有众多山、路、草、石头、桥、人、建筑等的一整个大场景。这时即使是按需加载也可能会在加载整个场景中的阻塞很长时间，加在的体验会越来越差。异步边玩边加载的方式就在这时能够体现出更加好的体验。&lt;/p&gt;

&lt;p&gt;根据地图编辑器的数据，进行异步流式的动态加载，让人能有逐步出现的视觉体验，相对画面等待的阻塞方式会更好。异步加载缓解了CPU在某个瞬间的消耗，使得CPU在场景加载和实例化上的消耗更加平滑。&lt;/p&gt;

&lt;h3 id=&quot;地图九宫格&quot;&gt;地图九宫格&lt;/h3&gt;

&lt;p&gt;RPG游戏场景中场景常常比较大，很多时候一张大地图无缝连接，这样才能体会到真实世界无缝的行走和旅行的体验。我们不可能把整个世界都加载进内存里，因此分块分批加载成了迫切需求。&lt;/p&gt;

&lt;p&gt;九宫格方式原本是在服务器端上用来寻找人物角色的结构，每个角色的信息都存放在一个格子里，当一个角色信息变动只要通知周围8个格子的玩家要求它们更新角色状态，更远的格子由于太远而不需要同步，每次有新角色进入附近的格子都需要通知玩家，告诉他有新的角色信息以便玩家的客户端添加模型更新状态，或者附近格子中的角色有离开的也通知玩家，好让玩家的客户端程序可以删除指定角色，每次变化其实通知的范围都是以自己为中心的九宫格内的玩家，因为只有他们是离自己最近的并且在视线范围内出现在场景里的。&lt;/p&gt;

&lt;p&gt;我们可以把一整个世界横竖切N和M刀，这样就有了分成 (N+1) * (M+1) 个块，每个块之间的地形可以拆分成相同大小或者用另一个更大的方格来描述地形，这样每块放个的内容都是独立的，可以被独立加载或独立卸载的内容。&lt;/p&gt;

&lt;p&gt;在游戏中当玩家进入地图场景，一般来说我们会限制玩家看到场景内容的范围，因为范围太广的话，视野内的内容太多会导致渲染压力过大，因此我们一般只能看到一部分的画面，即周围的800-1200米范围内的画面，越远的地形和风景意义越来越少，远处的景色通常会用迷雾颜色遮起。我们使用九宫格规则来加载场景地图的话，假设每块500x500米大小，当前所在的地块加上周围的八个分割块足以能展示我们需要的画面，即九块的地图内容足以成为我们展示的画面内容。我们来看看用数据表示九宫格地图展示规则：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	[-][-][-][-][-][-][-]
	[-][-][-][-][-][-][-]
	[-][-][2][2][2][-][-]
	[-][-][2][1][2][-][-]
	[-][-][2][2][2][-][-]
	[-][-][-][-][-][-][-]
	[-][-][-][-][-][-][-]
	[-][-][-][-][-][-][-]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上图中角色所在的地图块标记为1，周围8块已经被加载进来的地图内容比较为2。&lt;/p&gt;

&lt;p&gt;我们来设想下的我们控制的角色不断向前行进，离开了当前的地图块进入了另一个地图块，这时九宫格内容发生了变化，以我们角色为中心点的地图块周围的九块与原先我们所在的九块内容发生了变化。我们需要加载周围九块内容中，没有被加载进来的那三块内容，即：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	[-][-][-][-][-][-][-]
	[-][-][-][-][-][-][-]
	[-][3][2][2][2][-][-]
	[-][3][1][1][2][-][-]
	[-][3][2][2][2][-][-]
	[-][-][-][-][-][-][-]
	[-][-][-][-][-][-][-]
	[-][-][-][-][-][-][-]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;图中1为角色所在块，向前移动到了另一块地图块中，原来所在的地块不再是角色所在地块了，到了新的地图块上，那么周围的九块地图的也发生了变化。此时我们需要加载新的地图块，来确保我们展示的内容仍然是完整的，即图中标记为3的内容块，同时我们还需要卸载被废弃的三块内容，即：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	[-][-][-][-][-][-][-]
	[-][-][-][-][-][-][-]
	[-][3][3][3][2][-][-]
	[-][3][1][3][2][-][-]
	[-][3][3][3][2][-][-]
	[-][-][-][-][-][-][-]
	[-][-][-][-][-][-][-]
	[-][-][-][-][-][-][-]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;图中标记为2的内容块是被废弃的内容块，是需要我们卸载的内容块，卸载不必要的地图内容元素，包括物体实例、角色实例、模型、贴图、音效等元素。就这样我们根据九宫格的加载和卸载规则，在我们这个角色不断向不同方向行走，不断的跨越不同区块的内容，地图模块不断加载需要的地图内容块并卸载不需要的内容块，角色能始终看到完整的地图内容，内存仍然能在保持一个范围内上下波动，因为我们在不断的卸载那些不需要的地图块。&lt;/p&gt;

&lt;p&gt;当然我们可以把它划分的更细致一些，将地图分的更细，然后再采用25宫格，49宫格形式来做加载和释放，在玩家不断移动的同时加载那些进入范围内的地图元素，卸载那些离开我们的地图方块。加载时我们也可以将阻塞加载和异步加载结合使用，把最关键部分用阻塞式的加载方式，比如地形和碰撞体以及主要景观模型，其他物件则用异步加载用由近到远的加载顺序，这会让加载导致的CPU消耗更加平滑，平滑的加载完成整个场景所带来的游戏体验将是绝佳的。&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第十章，地图与寻路(二) 寻路网格的构建</title>
   <link href="http://www.luzexi.com/2019/07/26/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-%E5%9C%B0%E5%9B%BE%E4%B8%8E%E5%AF%BB%E8%B7%AF2"/>
   <updated>2019-07-26T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/07/26/Unity3D高级编程之进阶主程-地图与寻路2</id>
   <content type="html">&lt;h3 id=&quot;寻路网格构建&quot;&gt;寻路网格构建&lt;/h3&gt;

&lt;p&gt;上一节我们了解了A星寻路的算法及其优化，A星寻路只是算法，需要配套的模块和工具链支撑，单独的一个A星无法运行在游戏项目中因为它只是一个算法。那么我们需要什么样的模块和工具链来支撑整个寻路系统呢，我们这节就来讲一讲寻路周边工具链中最重要的配套模块‘寻路网格构建’。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;h6 id=&quot;1用二维数组构建虚拟的方形网格&quot;&gt;1.用二维数组构建虚拟的方形网格&lt;/h6&gt;

&lt;p&gt;最简单的也是最易于理解的网格构建方式要属二维数组网格，它是一个二维数组，每个元素就代表一个可行走位置，我们可以假设元素中数字0代表无障碍，1代表有障碍，或者也可以用数字代表障碍阻碍难度，比1大的数字代表更高的障碍难度，数字越大障碍难度越大，到达某个数字比如999则认为该障碍难度无法跨越。&lt;/p&gt;

&lt;p&gt;举个二维网格的例子：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	[0][0][0][0][0][0]
	[1][1][0][0][1][1]
	[1][1][0][1][1][1]
	[0][1][0][0][1][1]
	[0][0][0][1][1][1]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这是一个 5 * 6 的二维数组，0代表无障碍，1代表有障碍。我们能从中很清晰的看到这张地图中有哪些是障碍点，张地图点都是连同的我们从任意一个可行走点出发都能到达任意终点，我们可以想象寻路时一步步按照方格的方式去行走。&lt;/p&gt;

&lt;p&gt;用二维数组代表地图相对比较抽象，它需要与地图的尺寸相匹配，怎么与地图真正的关联起来呢？我们在脑海中需要把一整块地图也切成 5 * 6 这样30块地，比如这个张地图总共大小为 50米 * 60米 的大小，假如左下角为[0,0]位置，我们从0，0点开始，从10，10点到0，0点为一个方块与[0,0]这个点关联，从坐标10，10到坐标20，20为另一个方块与[1,1]这个点关联，从0，10点开始，10，20点到0，10点的方块与[0,1]这个点关联，依次类推。&lt;/p&gt;

&lt;p&gt;在地图上每个10 * 10大小的一块正方形的区域都与数组关联，这样我们在用A星寻路中以及寻路后的结果都可以对应到地图上的坐标。&lt;/p&gt;

&lt;p&gt;比如，我们寻路到从[0,0]点开始，到[1,4]点的路径为，[0,0]-&amp;gt;[1,0]-&amp;gt;[2,0]-&amp;gt;[2,1]-&amp;gt;[2,2]-&amp;gt;[2,3]-&amp;gt;[2,4]-&amp;gt;[1,4]，反应到地图上时，是0，0点开始移动，先移动到第一个方块也就是(0,0)到(10,10)这个方块的中点，也就是坐标(5,5)的点位上，再移动到(10,0)与(20，10)这个方块区域的中点上，即(15,5)坐标点位上，再移动到(20,0)与(30,10)这个方块区域的中点上，即(25,5)坐标点位上，依次类推，直到移动到最后一个点位，即(10,40)与(20,50)这个方块区域的中点上，即(15,45)这个坐标上，到达终点。&lt;/p&gt;

&lt;p&gt;因此A星寻路结束后，给出了[0,0]-&amp;gt;[1,0]-&amp;gt;[2,0]-&amp;gt;[2,1]-&amp;gt;[2,2]-&amp;gt;[2,3]-&amp;gt;[2,4]-&amp;gt;[1,4]，即数组上的坐标路径，在实际地图中则移动的路径为(5,5)-&amp;gt;(15,5)-&amp;gt;(25,5)-&amp;gt;(25,15)-&amp;gt;(25,25)-&amp;gt;(25,35)-&amp;gt;(25,45)-&amp;gt;(15,45)的坐标点位顺序。&lt;/p&gt;

&lt;p&gt;相当于把整个地图想象成一个矩形，把矩形横切N刀，竖切N刀，成了一个与二维数组匹配的方块地图，每个方块与数组中的一个元素相关联，当我们使用A星算法从数组中算出一个具体的路径时，可以根据这个数组中的路径，来匹配地图上的点位，即方块的中点。&lt;/p&gt;

&lt;p&gt;在数组与地图的匹配中，如果这个地图很大，但数组的大小很小，就无法实现细腻的路径与障碍，所以我们需要在内存占用量与地图寻路细节之间权衡。多大的数组与当前的地图才能匹配的更好呢，我们的数组不能过大因为过大的数组会造成内存的浪费，又不能过小因为过小的数组使得地图的障碍细节无法得到完美的体现。所以我们在决定数组多少的时候，需要考虑的是整个地图的大小，以及最小障碍物为多大，来决策究竟需要用多大的数组。&lt;/p&gt;

&lt;h6 id=&quot;可视化地图编辑界面&quot;&gt;可视化地图编辑界面&lt;/h6&gt;

&lt;p&gt;这些切割与关联都是由我们的脑袋抽象出来的，毕竟我们在做游戏项目的时候，抽象的东西如果无法可视化的话，就会变得难以灵活运用，至少是难以灵活的编辑和扩展，于是可视化编辑也是一个比较重要的节点。&lt;/p&gt;

&lt;p&gt;我们可以用最简单的方法Excel方式，建立一个Excel，在Excel表中填入一个与数组相等大小的矩形方格块，在方格内填入颜色或数字，绿色代表可行方块，红色代表不可行方块。这样在Excel内就可以设置地图的元素以及障碍物，可行走与不可行走区域一眼就能知道。那些标记为红色的方格是障碍物，那里是不可通行的，那些标记绿色的地方为空地，那地方是可通行的，整个地图下来哪些地方不可通行，哪些地方可通行一目了然。最后我们把这个Excel里的数据导出到文件，再在游戏中读取文件中的数据，就能得到我们需要的地图二维数组的可行走与不可行走的地图数据。&lt;/p&gt;

&lt;p&gt;除了Excel，我们还可以使用UnityEditor提供的编辑器API编写的地图编辑窗口，把地图大小，方格大小，障碍物等以可视化的形式放在 UnityEditor UI编辑窗口中，并附上保存数据到文件和从文件读取地图数据的功能，这样的地图编辑窗口就能更形象的体现地图的元素。&lt;/p&gt;

&lt;p&gt;如果这种可视化还不够，就在具体的3D场景中的地图上做些编辑功能，先要把整个地图加载并渲染在画面上，然后在地图上用颜色方块的方式画出不同颜色的块状，用具体地图为背景来编辑地图障碍和可行走区域。当然这也带来更加多的代码的编写和维护工作，但也同时是非常值得的可视化网格编辑器，为设计师提供了更加良好的编辑工具，这对游戏的创新设计会有更好的支持作用。&lt;/p&gt;

&lt;p&gt;除了以上形式的地图编辑工具，也可以用类似地形贴图的形式来做为可行走点的依据，就像地形高度图那样，我们也可以用一张图来存储障碍数据。用一整张1024 * 1024图代表一整个地形的大小，或者也可以为 2048 * 256等按不同需求规格的大小，每个像素点代表二维数组的一个元素，(255,255,255)白色代表可行走区域，(0,0,0)黑色为不可行走区域，那么这张图就可以压缩成只有RGB通道的8比特的图，也加入更多元素，泥潭，沼泽等等分别用其他颜色代表，这样就可以用图片代替了Excel的数据，Unity3D自带的地形就是这么做的，它的地形图中包括了一张高度图和地图元素图，贴图中每个颜色像素都代表一种植被。当需要加载二维数组作为可行走数据时，则加载该图片读取像素中的每个元素录入到内存中，然后就可以依据内存中的二维数组来判定是否是可行走区域，以及相邻的格子是否是障碍物的判断。&lt;/p&gt;

&lt;p&gt;由于一维数组在分配内存块时的紧凑度往往比二维数组要来的好，用一维数组来读取索引中的数值会更加的快，因此我们常常使用一维数组来代替二维数组，在调用索引时也只是多了一个简单的乘法和加法的操作，即 二维数组 [a, b] 等于 一维数组 [a * width + b]，这样一来只是整个内存访问都是连续的而不再需要内存地址的跳转，这有利于提高指令缓存命中率，加速了指令运算。&lt;/p&gt;

&lt;h3 id=&quot;2用路点系统构建寻路路线图&quot;&gt;2.用路点系统构建寻路路线图&lt;/h3&gt;

&lt;p&gt;用二维数组的方式来构建寻路的基础数据有一定的局限性。当场景特别大，我们就要存储特别大的一个数组，编辑可行走区域的工作量也有所提高。假设我们的地图场景有 2048 * 2048 个数组这么大甚至更大，我们在制作可行走区域时就要把所有的障碍点都细致的设置一遍这样的工作量比较大。除了数组太大和需要编辑的工作量扩大外，二维数组下的寻路路径最多是8方向的，上，下，左，右，左上，左下，右上，右下，寻路后的行走时也会感觉比较不真实，感觉是直来直去行走方向，要么90度方向行走，要么180度方向行走，要么45度方向行走，没有其他角度的行走姿势，让人感觉不真实，在像素游戏上还感觉不出什么来，但在3D地形中则会感觉明显的怪异。&lt;/p&gt;

&lt;p&gt;路点系统就弥补了一些二维数组形式的缺点。路点系统是一个易于理解的系统，它由很多个点构成，这些点我们称它们为路点，即路上的点。我们将路点放入地图中，并为每个路点标记ID，以及与哪些路点相连的数据。我们在地图中放入了很多个路点，并且为这些路点配置了连线，于是就有了这幅画面：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;地图中每个路点都会有与其他某些路点相连接的线，我们可以称它们为路线，由路点与路点之间连线而成。如果把地图里的路点和路线铺设的更加复杂一点时，所有的点和线都拼成了一张网：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这张网中，只要我们得到某个点，就能根据这些路点和路线，用A星的算法来寻路到目的地。图中当起点为A，与起点最近的路点寻路到与终点最近的路点的一条路径。如图：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;路点系统相对容易理解，因为它本身就是点与点之间的连路，我们在制作时也需要一些可视化的编辑器的支撑，因为所有的路点都需要在既有的地图上编辑，所以编写一个专门的地图编辑器也是必不可少的，没有地图编辑器至少也需要路点编辑器，可用来保存路点数据到具体的数据文件、从文件加载路点数据，以及增加路点，减少路点，添加和减少路点连接信息等功能。&lt;/p&gt;

&lt;p&gt;路点系统相对简单，它的缺点也是比较严重的。当我们大范围设置可行走区域时它任然需要大量的工作来编辑可寻路的路点信息与连线。它的寻路方式也无法识别碰撞而只能用路点的形式来绕过障碍。当在大块空地上做寻路时，需要在这个大块空地上添加比较多的路点，才能平滑的适应各种寻路路径。&lt;/p&gt;

&lt;h3 id=&quot;3平面三角形网格的构建&quot;&gt;3.平面三角形网格的构建&lt;/h3&gt;

&lt;p&gt;路点系统直观、门槛低、上手简单，一般情况下路点的数量相对比其他方式的网格少很多，因此内存消耗和CPU消耗都比较少。但它的缺点也比较大，行走路线的平滑程度依赖于添加的点的密度与形状，更糟糕的是它无法识别障碍区域，行走的路线也依赖路点之间的连线。三角形网格形式的寻路网格就很好的解决了路点系统的缺陷，它用算法自动生成的网格，无需手动编辑就能避开障碍区域。&lt;/p&gt;

&lt;p&gt;那么三角形寻路网格是怎么生成的呢，这个问题在计算机图形学里叫做 “平面多边形的三角剖分问题”，意思是说我们在一张平面图上有很多的颜色，这些颜色就形成了很多的点，根据这些点将这幅图分解为由许多三角形组成的多边形。&lt;/p&gt;

&lt;p&gt;平面多边形的三角形剖分问题是计算几何研究的一个基本问题，它广泛应用于模式识别、图像处理、计算机图形学以及机器人领域。一方面，三角形作为最简单的平面图形，较其他平面图形在计算机表示、分析及处理时方便得多。另一方面，三角剖分是研究其他许多问题的前提。&lt;/p&gt;

&lt;p&gt;Delaunay三角剖分算法是一种三角剖分的标准，它是由前苏联数学家Delaunay提出来的：“对于任意给定的平面点集，只存在着唯一的一种三角剖分方法，满足所谓的“最大-最小角”优化准则，即所有最小内角之和最大。”这种剖分方法遵循“最小角最大”和“空外接圆”准则，“最小角最大”准则是在不出现奇异性的情况下，Delaunay三角剖分最小角之和均大于任何非Delaunay剖分所形成三角形最小角之和，三角形的最小内角之和最大，从而使得划分的三角形不会出现某个内角过小的情况，比较有利于有限的后续计算。“空外接圆”准则是Delaunay三角剖分中任意三角形的外接圆内不包括其他结点。因此在各种二维三角剖分中，只有Delaunay三角剖分才同时满足全局和局部最优。&lt;/p&gt;

&lt;p&gt;Delaunay三角剖分算法由好几种，但都遵循Delaunay三角剖分特点，即其剖分后的多边形里的三角形必须满足：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	1.除了端点，三角形的边不包含其他任何点。

	2.除了在点上的连接，没有任何一条边是相交的。

	3.所有的面都是三角形，且所有三角形的合集是所有点集合的凸包。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;第一、二点要求三角形的边上没有任何其他的点和相交的线，第三个说的是所有的点都是三角形的点并且最后所有三角形所形成后是个凸多边形。&lt;/p&gt;

&lt;p&gt;Delaunay三角剖分算法有翻边算法、逐点插入算法、分割合并算法、Bowyer-Watson算法，它们都只适用于凸多边形的三角剖分。这里我们仅对Bowyer-Watson算法进行讲解。Bowyer-Watson算法的基本步骤是：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	1.构造一个超级三角形或多边形，把所有数据点都包围起来。

	2.依次逐个加入新的顶点，找到包含新顶点的所有外接圆对应的所有三角形。

	3.删除包含在所有外接圆中的边，这时围绕新插入的点构成了一个凸多边形。

	4.将新插入的点与这个凸多边形的所有点相连，形成多个新的三角形。

	5.返回到第二步，继续加入新的点直到所有顶点增加完毕则结束。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们用图示来说明算法步骤：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	[缺图1]

	[缺图2]

	[缺图3]

	[缺图4]

	[缺图5]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这5张图清晰解释了Bowyer-Watson算法的步骤，从把所有的散点用凸多边形包围起来到插入新点再到形成删除边形成新的三角形最后将所有散点都加入进来形成了三角形网格。&lt;/p&gt;

&lt;p&gt;可惜Delaunay三角剖分只合适凸多边形，对于我们在场景中凹形区域占到了大量的面积和数量则需要另外考虑其他算法，Delaunay则可以用于其他凸多边形的情况我们将在后面介绍。现在我们需要的不只是凸多边形和凹多边形的三角形剖分，也需要考虑凹凸多边形中含有‘洞’(即不规则多边形阻挡物)的情况，甚至还有更复杂的‘洞’中有孤岛的情况，因此单单是凸多边形的三角形剖分不能满足我们实际的需求。&lt;/p&gt;

&lt;h6 id=&quot;2002年-david-eberly-在triangulation-by-ear-clipping论文中提出了用切耳法构建简单多边形的三角化正好解决了我们的问题&quot;&gt;2002年 David Eberly 在《Triangulation by Ear Clipping》论文中提出了用切耳法构建简单多边形的三角化，正好解决了我们的问题。&lt;/h6&gt;

&lt;h3 id=&quot;切耳算法&quot;&gt;切耳算法&lt;/h3&gt;

&lt;p&gt;Ear Clipping 切耳算法是一个简单实用的三角形分割算法，其步骤可以简单的分为三步，在解释三步骤之前，我们先解释下几个名词的意思。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	1.简单多边形是，所有顶点都是顺时针或者逆时针排列的顶点，每个顶点只连接两条边，边与边之间没有交叉的多边形，就叫做简单多边形。

	2.耳点，耳点的意思是，多边形中相邻的三个顶点V0,V1,V2形成的三角形里，不包含任何的其他顶点，并且如果V1点是凸点，即V0-V1的连线与V1-V2的连线之间形成的夹角小于180度，则认为V1是耳点。所以一个由4个顶点组成的多边形中，至少有2个耳点。

	3.耳朵三角形，三角形顶点中有耳点的就叫耳朵三角形。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们知道耳点和耳朵三角形再来看这三步骤就容易理解的多，其三步骤为：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	第一，找到一个耳点。

	第二，记录这个耳朵三角形，然后去掉这个耳朵点，在剩余的顶点中，继续回到第一步

	第三，直到剩下最后3个点形成一个三角形并记录下来，把所有记录的三角形拼接起来就形成了三角化网格。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;经过这三个步骤的计算，所有的耳点都被切掉后，再把所有记录的三角形拼装成三角形网格，就完成了整个三角形剖分步骤。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;多边形含洞的情况&quot;&gt;多边形含‘洞’的情况&lt;/h6&gt;

&lt;p&gt;除了普通的简单多边形(包括凹凸多边形)的三角剖分外，如果简单多边形中有‘洞’的情况怎么办。论文中也给出了解决方案，即，依旧使用上述三步骤来做三角形剖分，只是剖分之前定把‘洞’并入外围的简单多边形，即：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	1，用外围的简单多边形上的点，连接‘洞’的简单多边形，因此为了保持所有点的一致性，‘洞’必须是与外围的多边形的点的顺序是相反的。即外围如果是逆时针的顺序，‘洞’则需要顺时针的顺序。

	2，在连接处，产生两个一模一样的点，即连接点。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;用这种方式来将‘洞’并入成为一个单独的简单多边形，如果有多个洞，则先并入的洞为，拥有x轴方向最大的点的‘洞’，依次并入。&lt;/p&gt;

&lt;p&gt;也就是说，最终计算的还是一个单独的简单多边形，只是在计算之前，将‘洞’以凹形形态并入最外围的简单多边形。&lt;/p&gt;

&lt;p&gt;我们以图为例，可以看的更加清楚一点：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;图中展示了，如何将一个‘洞’以凹形形态的方式并入外围简单多边形的，就如同从外围简单多边形上，修了一条小小的通路到‘洞’中那样，其实我们完全可以理解为，下图那样：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;图中从外围简单多边形的点上延伸出一条路径来连接‘洞’，使得‘洞’的空白与外围的空白联通，就像贴膜里的气泡开了个口把空气放出去了那样。&lt;/p&gt;

&lt;p&gt;如果’洞‘并不是完全包含在外围简单多边形下，有可能是一半在外面，一半在里面，这时只要做多边形裁剪就可以了，将原来外围的简单多边形根据这个’洞‘裁剪成一个凹形，就与’洞‘彻底分离开来了，形成了新的简单多边形。&lt;/p&gt;

&lt;h6 id=&quot;洞中的岛&quot;&gt;‘洞’中的’岛‘&lt;/h6&gt;

&lt;p&gt;除了有’洞‘，以及’洞‘包含在里面和’洞‘一半在里面一半在外面的情况，还有种情况是‘洞’中有’岛‘。这个‘岛’就像是湖中的‘孤岛’，虽然它也是需要三角剖分，但与外界是无法取得连接的，也就没有与最外围的简单多边形连接的需求。&lt;/p&gt;

&lt;p&gt;因此’洞‘中有’岛‘，这个’岛‘就相当于另一个独立的简单多边形范围，可以另外单独拎出来，自己计算自己的三角化部分。&lt;/p&gt;

&lt;h6 id=&quot;到此这样就形成了一整个算法即如果有洞则先合并洞如果有岛则拎出来作为与外围的简单多边形同级别的简单多边形自行计算所有三角化的计算过程可简单描述为找耳朵去耳朵记录耳朵三角形最后得到了所有三角形这四个步骤&quot;&gt;到此，这样就形成了一整个算法，即，如果有‘洞’则先合并‘洞’，如果有岛，则拎出来作为与外围的简单多边形同级别的简单多边形自行计算。所有三角化的计算过程可简单描述为，找耳朵，去耳朵，记录耳朵三角形，最后得到了所有三角形，这四个步骤。&lt;/h6&gt;

&lt;p&gt;应用到实际项目中，最外层的简单多边形，就是我们在地图中定义的可行走的多边形范围。而‘洞’则是地图上的那些静态的障碍区域，而‘洞’中的‘岛’，则是不可行走范围内的可行走的‘孤岛’。我们在构建三角寻路网格时，首先需要找出这个最外围的简单多边形以及孤岛，再根据切耳算法来构建三角形网格。因此我们需要根据地形来生成相应的可行走三角形网格，通过读取地图中的可行走区域的Mesh，以及读取障碍物Mesh，将它们的竖直方向y轴的值忽略后，再通过多边形合并算法来合并成为最外层的多边形，操作还包括裁切‘洞’一半在里面的情况，最后可以得到需要三角化的简单多边形，以及‘洞’的数据。最后将这些数据用切耳算法得到一个具体的三角网格。&lt;/p&gt;

&lt;h3 id=&quot;4多层寻路网格&quot;&gt;4.多层寻路网格&lt;/h3&gt;

&lt;p&gt;前面讲了2D平面上的寻路网格构建，在实际项目中大部分时候，2D平面上的寻路就已经够用了，即使是有起伏的地面寻路，也可以用 2D寻路 + y轴射线碰撞的形式 获得位置坐标的方式，在服务器端以2D平面数据的方式去保存和运算数据，这样即满足了寻路的需求也满足了高低起伏的地形。&lt;/p&gt;

&lt;p&gt;有一种解决方案可以用多层级的2D网格做3D寻路，它在2D的RPG游戏中比较流行，也曾在PC端的RPG网络游戏中流行过，在现代的3D网络游戏中相对比较少见，因为现在的高度网格构建算法已经有了比较成熟的解决方案，但这种多层级2D网格的做法任然有比较好的实用价值。&lt;/p&gt;

&lt;p&gt;我们暂且称它为‘多层寻路网格’，‘多层寻路网格’需要把所有可行走的区域分成多个层级，每一层都有自己的网格数据，第一层与第二层之间也可以多出一个中间连接层，这就像我们拥有一个多层楼梯的古堡中，古堡有4层楼这么高，每一层都用楼梯连接着，这个楼梯就是连接层与层之间的中间层。&lt;/p&gt;

&lt;p&gt;我们任然使用2D三角形网格构建法构建每一层的可行走区域，用这种方法我们假设构建出四层楼的寻路网格，每一层都有自己可行走的平面三角形网格数据，每一层的数据网格数据中可以包含当前层的楼梯部分的数据，例如二层的寻路网格数据可以包含一到二层的楼梯部分的数据，也将楼梯部分的数据单独拆分出来成为一个独立的层级。&lt;/p&gt;

&lt;p&gt;有了这‘多层寻路网格’的数据，我们就可以开始寻路了，由于我们只关心我当前层的数据，以及当前层上一层与下一层的数据，所以遍历起来非常方便高效。当要跨越层级寻路时，首先必须确定的是‘我’在哪一层，目的地是哪个层级，按次序一层层往上走或者往下寻路。比如我们去的目的地是二楼，所在的起点是大厅的一楼，那么我们就必须由地面层级开始，先找到楼梯层的入口点，从起点寻路到入口点，从入口点进入楼梯层后，从楼梯层数据中找到与二层楼衔接的入口点并寻路，最后到达二层楼并向目的地寻路。&lt;/p&gt;

&lt;p&gt;每一层都有自己独立的数据网格，跨越层级的寻路时需要层与层之间的连接点或连接信息，因此我们在制作时需要对层级之间的入口区域做记录，比如第一层某个矩形范围内为第一层与第二层的衔接，只要进入这个范围就认为我们上升了一层或者下降了一层，角色身上的层级标记也相应地变化，此时索引到的寻路网格数据也变成了当前层的数据。下楼也是同样的道理，在每一层中都有几个层与层之间的斜街区域可以通往下一个层级，只要到达这个范围以内就认为是进入了下一个层级，接着就在下一个层级的网格数据中寻路，每次跨层级寻路时都要先寻找上楼或下楼的衔接区域。&lt;/p&gt;

&lt;p&gt;多层寻路网格在2D的RPG游戏中很实用，特别是那种有多个楼层的场景，也在王者荣耀、Dota的竞技游戏中用到，这些游戏的场景里面用到了有符号距离场，它是一个方格形式的网格数据，多各层级的网格数据对低洼和高起部分做了分层的可行走区域定制。&lt;/p&gt;

&lt;h3 id=&quot;5三角形网格中的a星寻路&quot;&gt;5.三角形网格中的A星寻路&lt;/h3&gt;

&lt;p&gt;前面说的都是网格构建，那么怎样才能让A星与网格数据结合呢。&lt;/p&gt;

&lt;p&gt;在二维数组下的A星寻路很好理解，邻近节点就是周围的4个点或者8个点，并且与目的地的期望值可以直接用方块之间的距离来计算。在路点系统中，也有邻近节点概念，它的邻近节点就是与该节点有线条连接的点，与目的地的期望值可以直接用点与点之间的距离计算得到。在平面三角形网格中，以三角形为单位，每个三角邻接三角形为与它们有共享边的三角形，与目的地的期望值可以用三角形的中点与目的地之间的距离计算得到，我们来看下它们三者在寻路后得到的结果的数据示例：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	二维数组下，计算出来的路径，[0,0]-&amp;gt;[1,0]-&amp;gt;[2,0]-&amp;gt;[2,1]-&amp;gt;[2,2]-&amp;gt;[2,3]-&amp;gt;[2,4]-&amp;gt;[1,4]。

	路点系统中，计算出来的路径，(id=1)-&amp;gt;(id=3)-&amp;gt;(id=6)-&amp;gt;(id=12)-&amp;gt;(id=21)。

	平面三角形网格中，计算出来的路径，(trangle_id=1)-&amp;gt;(trangle_id=4)-&amp;gt;(trangle_id=8)-&amp;gt;(trangle_id=13)。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;前两者的路径我们很好理解，而三角形网格寻路后，虽然知道了路径上的三角形，但是如果我们行走的路径定位在三角形中心点，行走的路径会比较诡异，每次都要先到达三角形的中点才能去下一个三角形，这样导致中点与中点连线的路径还不够平滑。折中的办法我们可以考虑用边的中点来记录路径，因为相邻三角形之间的穿越都是靠邻边来穿越的，所以邻边的中点更符合三角形穿越，从ID为1的三角形穿越到ID位2的三角形上时，穿越的是ID 1与ID 2三角形的共同边，这种邻边中点计算出来的路径，有更好更平滑的路径效果。&lt;/p&gt;

&lt;p&gt;但是这种三角形进出领边中点的方式使路径依然会存在许多折线的情况，为了更好的解决路径平滑问题，我们需要用拐点路径算法来优化寻路后的路径。拐点算法有点像射线，所以也常常被称作为射线优化路径算法，我们来看看算法的步骤：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	1.从起始坐标点出发，往与下一个三角形的入口边的两个顶点v1,v2产生两个向量line1、line2，然后再往下一个三角形的入口边的两个顶点v3,v4产生两个向量line3、line4。

	2.通过计算这四个向量的叉乘，可以判定一个向量是在另一个向量的左边或者右边。我们可以计算出v3，v4是否在line1，line2形成的夹角。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们主要用这两步来计算拐点，特别是第二步的结果，我们对第二步结果中的几种情况分别做了对应的操作：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	1.line3和line4在line1和line2的夹角范围内，则把实用line3和line4的线作为下一次判断的基线。

	2.line3或line4超出了line1和line2的夹角范围(这里指的是line3超出line1外或line4超出line2外，而不是line3反向超出line2或line4反向超出line1)，则使用未超出的线或原始的线作为下次判断的基线，例如line3超出line1和line2夹角，而line4未超出，则使用line1和line4最为下次判断的基线。如果line3和line4都在line1和line2夹角范围外，则使用line1和line2作为下次判断的基线。

	3.当line3和line4都在line1左边时，则line1的坐标点成为拐点。类似的，当line3和line4都在line2的右边时，则line2的坐标点成为拐点。

	4.当line3和line1是同一个坐标时，它们的坐标点成为拐点。同样，当line4和line2是同一个坐标是，这个坐标点成为拐点。

	5.当寻路达到最后一个多边形，直接判断终点是否在line1和line2的中间，如果不是则用line1或line2的坐标点增加一个拐点，依照夹角偏向判定使用line1还是line2的坐标。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;举例说明：从右边的开始点往左边的结束点寻路&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;line3，line4在line1，line2夹角内，左右都缩进。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片2
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;v4在外面，左边缩进。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片3
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;v3,v4都在line1 左边，v1成为拐点&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片4
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;从v1出发继续按上面步骤计算拐点&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	缺少图片5
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;最后到达终点，收集所有拐点，加上起点和终点后就是条经过优化后的路径节点。拐点算法能将原本在三角形网格寻路后诡异的路径转化为更加平滑的直线路径。&lt;/p&gt;

&lt;h3 id=&quot;体素化寻路网格构建&quot;&gt;体素化寻路网格构建&lt;/h3&gt;

&lt;p&gt;前面说的大都是2D上的寻路，即使多层寻路网格也只是用多个2D寻路网格数据来建立多个层次的寻路数据，但依然无法达到3D高度上自由寻路的功能。如果说要在高度上做到自由寻路则要加入’体素化‘这个概念。体素化就是将空间分割成一个个立方体小方块，每个立方体都标志着是否可行走的状态，如果人物角色有高度和宽度的细节，那么如果不够高或者不够宽的角色就无法通过狭窄的门缝或者矮小的洞穴。&lt;/p&gt;

&lt;p&gt;RecastNavigation Navmesh 解决方案在各大引擎上非常流行，各大引擎都根据RecastNavigation Navmesh 对其做了相应的优化修改，不过其核心算法并没有变化。由于内容太多太深，我们并不打算把RecastNavigation Navmesh 里所有的算法都讲解一遍，而是将其制图的步骤和思路讲解一下，以便各位读者能在深入时能更好的理解。&lt;/p&gt;

&lt;p&gt;RecastNavigation Navmesh 大体流程如下：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	1. 体素化

	2. 生成区域

	3. 生成轮廓

	4. 生成多边形网格

	5. 生成三角形网格和高度细节
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;体素化概念比较重要，相当于我们把空间分割成三维方块，每一块都是一个立方体，每个立方体都有着是否可行走的标记。对一个场景进行体素化分析后，就有了如下图所示的标记。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	[缺图1]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上图中，标记绿色立方体部分为可行走区域，标记红色部分为不可行走区域。&lt;/p&gt;

&lt;p&gt;接着对这些’体素‘进行整理，生成不同的区块为后面的生成多边形做准备。生成区域时需要对一些不可行走区域进行过滤，比如障碍物以及障碍物周边角色宽度的体素部分，邻接体素的高度不符合要求的部分，邻接有空心体素的部分，都要进行过滤。再对剩下部分的体素集合起来进行体素分区，这里区分区域有好几种算法可以选择，它们都是将上下左右连续的体素进行识别分割成不同的区域，例如构成阶梯的区间能够当做邻居被连接在一起，阻挡物前方和阻挡物后方两块区域被分开来，狭窄的入口前与后两块部分被区分开来等。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	[缺图2]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;区域化后就有了上图，去除障碍物周围角色宽度的体素，剔除邻接高度衔接不符的体素，去除邻接有空心的体素，再对剩下的体素识别他们的连续部分，分割成为不同的区域。&lt;/p&gt;

&lt;p&gt;接着是检测划分出来区域的轮廓并构造成简单多边形，再将轮廓分割成多个凸多边形，好在后面使用三角剖分算法构建三角网格。如图：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	[缺图3]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;最后将所有的多边形网格三角化，并且对多边形中，高低不平的地面部分，插入顶点去构建三角形得到高度细节，最后得到如图所示的三角形寻路网格。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	[缺图4]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上面步骤中用到了体素化算法，区域划分算法，多边形轮廓构建算法，轮廓分割成凸多边形的算法，最后是我们前面介绍过的Delaunay三角剖分算法。&lt;/p&gt;

&lt;p&gt;参考文献：&lt;/p&gt;

&lt;p&gt;《Triangulation by Ear Clipping》David Eberly&lt;/p&gt;

&lt;p&gt;《Delaunay三角剖分的几种算法综述》吴莉莉&lt;/p&gt;

&lt;p&gt;《多边形寻路算法简单介绍》 liweizhaolili&lt;/p&gt;

&lt;p&gt;《The Core Build Process》http://critterai.org/projects/cainav/doc/html/e72bd1ee-04b0-4bbb-a21d-d8d7ecaa11af.htm&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第六章，网络层(六) - 网络同步解决方案</title>
   <link href="http://www.luzexi.com/2019/07/14/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-%E7%BD%91%E7%BB%9C%E5%B1%826"/>
   <updated>2019-07-14T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/07/14/Unity3D高级编程之进阶主程-网络层6</id>
   <content type="html">&lt;p&gt;当前网络游戏中网络同步方案有三种，即状态同步，实时广播同步，帧同步。三种方式并不互相排斥，它们可以混合使用。很多时候我们在开发的时候，为了能都让游戏显得更加逼真，会选择多种的同步方案一起使用。例如魔兽世界这种开放世界的多人在线RPG游戏，起初它们就使用了状态同步和位移信息同步两种方案，绝地求生、和平精英等战地竞技类游戏，也同样使用了状态同步与实时广播同步方案，而传奇世界、热血传奇等传奇类游戏因为有严格的寻路同步机制所以就只使用了状态同步，而王者荣耀、英雄联盟等一批5v5地图类竞技游戏则更多使用了帧同步方案。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;同步方案的目标是在针对多人游戏中如何用更少的信息同步量来逼真的’模拟‘其他玩家的一举一动，让我们在玩游戏的时候能知道并看到其他玩家的位置、动作、及状态。这里的关键词是’模拟‘，我们本地设备中获取的信息由于网络因素的关系通常都是延后的，如何通过这些延迟的信息来模拟真实角色的位移、动作、特效是整个方案的关键。&lt;/p&gt;

&lt;p&gt;在同步的解决方案中不仅涉及到信息同步还涉及到同步的范围，如果我们将每个玩家每次的变化信息向游戏内的所有玩家都广播一遍，那么我们需要同步的数据量太大，这不仅客户端承载不了这样大量的渲染压力和信息通信压力，服务器也同样无法承受如此巨大的数据传输压力(会占满服务器网络带宽或让网络费用过大)。因此同步方案对游戏来说不仅仅只是用来逼真模拟其他玩家的一个解决方案，它还需要解决和优化网络传输数据带来的压力和部分渲染压力。下面我们就来讲解下三大网络同步解决方案的方法论。&lt;/p&gt;

&lt;h3 id=&quot;状态同步法&quot;&gt;状态同步法&lt;/h3&gt;

&lt;p&gt;为什么要状态同步？同步机制主要目的模拟，倘若我们每帧(以每秒30帧为标准)都将自己的信息同步给所有人，那么需要传输和广播的信息量就太大，因此我们需要尝试更节省流量的方式。游戏角色通常可以用状态形式来代表某一段时间的行为，因此如果我们使用状态信息作为同步信息广播给其他设备去模拟的话就会比较节省流量，这样当我们收到同步的广播信息后，就可以在一段时间内不需要其他信息就能模拟出这些角色的动作、位移、以及特效。&lt;/p&gt;

&lt;p&gt;为了能更逼真的模拟其他玩家的行为，我们把每个人的行为方式抽象成若干个状态，每个状态都有一套行为方式将，有时尽管3D模型不一样但所调用的程序是一样的。比如空闲状态，所有人都会站在某个位置循环播放站立动画，这时我们只要告诉玩家说我在某个位置进入了空闲状态，只要我们的状态不变，其他玩家就可以在不收到任何同步数据的情况下知道我就是一直在原地并循环播放着站立动画。当然这是最简单的状态，我们也可以有其他比较复杂的状态，比如包括攻击状态、追击状态、防御状态、奔跑状态、技能状态、寻路状态等。&lt;/p&gt;

&lt;p&gt;在状态同步里，角色身上每个状态就相当于一个具有固定逻辑的行为模式，这个固定行为模式就像个黑盒，只要给到需要的数据，就能表现出相同的行为，比如攻击状态，就会播放一个攻击动画并在某个时间点判定攻击效果，攻击动画完毕后结束攻击状态。比如打坐休息状态，就会循环播放一个打坐动画，并每隔一段时间恢复一次血量。又比如寻路状态，就会从某点到某点做A星的寻路计算，边播放行走动画边跟随路线向各个路线节点移动，最终到达到目地完成寻路状态。最复杂的应该属于技能状态，技能有很多种很多个，每种技能都有不一样的流程，同一种技能还有不通的动画和特效，一些复杂的技能更是需要配备复杂的逻辑，但有一点是可以肯定的也必须做到的，那就是向技能状态输入相同的数据应该展示出相同的表现，例如向技能状态中，输入火球技能ID，目标对象，施法速度，角色在收到数据后就需要展示火球技能的动画，在等待吟唱时间后向目标发射火球。&lt;/p&gt;

&lt;p&gt;这些状态都有一个共同的特点就是只要我们给予所需的相同的数据就能展现出相同画面的个体效果。现在我们要让这些状态连贯起来拼凑成一个拥有一系列动作的角色，当我们向这个角色发送各种各样的指令时，就是在告诉它你应该触发这个状态然后触发那状态，由于指令中包含了状态需要的数据，这些数据广播给每个需要看到的玩家，收到这些状态信息的设备就需要通过这些同步数据去模拟角色的行为，从而让画面看起来像是很多玩家在操控自己的角色。&lt;/p&gt;

&lt;p&gt;在状态同步中，我们说的通俗点，服务器端扮演了幕后操纵木偶人的那个大老板，而客户端里渲染的对象就是那个木偶人，服务器端发出指令说ID为5的木偶人开始攻击，客户端就执行它的指令，找到那个ID为5的木偶人并开始进入攻击状态的相关逻辑。当动画播放到一半服务器端又发来指令说，被攻击的那个ID的怪兽受伤了并受到500点伤害，这时客户端就会在指定的怪物头上冒出500点的伤害值并且让怪物进入受伤状态播放受伤动画。&lt;/p&gt;

&lt;p&gt;现在当攻击动画播放完毕时，服务器又发来指令说继续攻击，客户端又根据这个指令找到ID为5的木偶人将它从站立状态切换到了攻击状态，不过这次攻击状态快结束时服务器发来指令说，这个怪物受到600点伤害并且死了，于是客户端根据这个指令找到这个怪物并在头上冒出600点伤害的数字，然后让怪物进入死亡状态播放死亡动画。木偶人也在播放完攻击动画后，进入了空闲状态循环播放了站立动画。&lt;/p&gt;

&lt;p&gt;服务器扮演着发送指令操控木偶人的角色，这个木偶人也包括玩家自己的角色，即当玩家操控’我‘自己的在游戏中的角色时，也同样遵循经过服务器的同意并发送指令给玩家的过程，当玩家收到同步信息指令时当前的角色才会进行状态的切换和模拟。&lt;/p&gt;

&lt;p&gt;不过也未必要一定要经过服务器同意才做模拟渲染，为了让玩家能够在网络环境糟糕的时候也能够看起来比较顺畅，我们在制作网络同步逻辑时，让玩家可以随意操控自己的角色，并不受限于服务器的延迟指令，但是在稍后的服务器校验时再对玩家进行矫正，最明显的例子就是我们在玩传奇类游戏中时的状态同步，在网络环境不太顺畅的情况下我们任然能操作自己的角色不停的移动，但在过一段时间后，当客户端接收到服务器发来的正确的数据后，客户端对角色进行了位置和状态的矫正。&lt;/p&gt;

&lt;p&gt;状态同步的是根据角色拥有不同的状态，每个状态在某一段时间下的行为可以根据数据被预测，除非状态被改变，否则相同的状态数据得到的是相同的个体状态结果，这样角色实体的行为就可以通过状态切换来模拟表现画面。&lt;/p&gt;

&lt;h3 id=&quot;实时广播同步法&quot;&gt;实时广播同步法&lt;/h3&gt;

&lt;p&gt;在一些FPS类型的竞技游戏中，人物的行动速度和旋转速度在不断的变化而且频次比较高，如果想要模拟不同玩家在游戏场景中的位置与旋转角度就要实时更新这些信息，这时状态同步就不能满足这样需求，由于我们移动的速度和旋转的变化太快频次太高，因此无法做到拆分同步状态来模拟。不过状态同步依然可以用于除了移动和旋转意外的同步方式，因为除了移动和旋转，其他信息都没有这样快速、多样的变化，并且角色很多数据仍旧遵守状态的规则，对于这些数据我们可以继续用状态来划分，因此状态同步常常与实时广播同步同时存在。&lt;/p&gt;

&lt;p&gt;实时广播同步方案的主要特点是，位置和旋转信息由客户端决定。客户端将自身的位置、旋转信息发给服务器端，再由服务器端分发给其他玩家，当其他玩家收到位置、旋转信息后根据收到的位置和旋转信息预测其当前的位置、速度、加速度，以及旋转速度，旋转加速度并进行模拟和展示。&lt;/p&gt;

&lt;p&gt;在这种竞技性比较强，移动速度比较快的游戏中，通常都需要玩家不停的改变移动速度和旋转角度来体现其控制角色的灵活性。比较常见为枪战类游戏CS，玩家不停在变化自己的位移速度和旋转角速度以适应战斗的需要。除了FPS类型的游戏，赛车类游戏也是类似的情况，在跑跑卡丁车游戏中，玩家要在高速移动下，不停的调整自己的方向和速度，让自己能够躲过众多障碍，同时在急弯处要旋转自己的车进行漂移等。魔兽世界也会使用实时广播同步法，这种开放世界下的RPG游戏，需要不停的改变自己的速度与旋转角度来让战斗显得更加丰富和灵活，不过魔兽世界在实时广播之上加了些验证机制让客户端不能为所欲为的决定自己的位置。&lt;/p&gt;

&lt;p&gt;为了能更加逼真的同步模拟这种变化频率很高的人物移动和旋转，我们不得不让客户端来决定其位置和旋转角度，这将牺牲一些数据的安全性来让画面模拟的更加真实流畅。&lt;/p&gt;

&lt;p&gt;每个玩家设备上的客户端会在1秒内向服务端发送15-30次左右自身的移动和旋转数据，为的就是让其他玩家在收到广播数据时能更加顺畅的模拟玩家在游戏中的移动旋转的表现，也只有这样才能让其他的游戏客户端不停的更新玩家的位置、移动速度和旋转角度。不过只是单纯的更新位置和旋转数据，会导致玩家在屏幕中不停的闪跳，因此我们用速度的方式表示它们的移动方式会让角色模拟运动得流畅些。当我们收到广播的玩家实时数据时，先计算速度和预测速度，以及加速度，让模拟的对象按速度和加速度的形式在屏幕中运动，而不是只更新位置，这让角色在画面中模拟行走的位置和方向时更加流畅。&lt;/p&gt;

&lt;p&gt;实时广播同步的算法和公式并不复杂，首先要取得已经收到的该玩家的位置信息前5个除以间隔时间，就能得到一个平均的速度，再取这样5个一组的3-5组，就能得到一个平均的加速度，根据这个速度和加速，就能让角色在屏幕中模拟出相对准确的跑动位置、速度和方向。不只是速度和加速度，我们还需要角色当前的面向的角度，以及旋转的角速度，在角度的同步上也可以按照这种速度和加速度的方式去预测，取最近5个角度的值得到平均旋转速度，再取5个一组的3-5组这样的数据计算得到旋转加速度。&lt;/p&gt;

&lt;p&gt;虽然我们用前面的数据计算当前的位置、速度、加速度、旋转角度、旋转角速度，但还是会有偏差，由于网络延迟大且不稳定的关系，很容易造成位移的偏差，所以我们依然需要定时的矫正。矫正会比较生硬，比如由于网络宽带关系，我们很久没有收到实时广播数据了，一下子收到了很多广播数据，这时由于位置相差太远就会一下子将角色置于最后定位的位置，这种看起来矫正会比较生硬。我们可以在生硬的基础上加入一些当前数据的预测，让矫正不是直接飞到那个位置而是加速移动过去，速度取决于延迟的时间大小。这也正是为什么我们在玩CF穿越火线，跑跑卡丁车这类游戏时，假如对方的网络比较卡，就会看到对方角色不停的一闪而过，因为预测数据和矫正数据偏离的太多了，客户端在不断的矫正角色的位置和速度。&lt;/p&gt;

&lt;h3 id=&quot;帧同步&quot;&gt;帧同步&lt;/h3&gt;

&lt;p&gt;状态同步既能控制数据计算的安全性，也能保证所有客户端的同步性，不过在位置和角度变化很快的竞技游戏中，状态同步无法承受这样又多又快的位置和旋转变化，所以就加入了实时广播同步的解决方案。实时广播同步解决方案放弃了玩家的位置和旋转角度的强校验，使得各个客户端能更加顺利和准确的模拟其他玩家的位置和旋转角度。&lt;/p&gt;

&lt;p&gt;更加严格的同步要求既能做到移动与旋转的准确定位也同时能同步角色状态还能有比较强的同步校验，这时实时广播同步已不能满足需求，实时广播同步解决方案虽然能预测模拟玩家的位置与速度，但不能做到强校验，无法保证数据的正确性就容易被专空子作弊。由于每个玩家的手机和电脑端的设备好坏都不一样，网络环境也不同，一台好的机子和手机，在同一时间段能位移的距离可能也不一样，在差异性巨大的设备和网络通信之间做到精准的同步比较困难，前面我们说的状态同步是基于状态可拆分的模式，把角色分为几种状态，每个状态都做自己的事，当玩家改变状态时发送数据给周围的人让他们各自去模拟改变后的状态信息，但是状态同步的问题是当我们需要频次高且精确的同步时它就无能为力了。&lt;/p&gt;

&lt;h6 id=&quot;帧同步解决方案就很好的解决了状态同步和实时广播同步解决不了的问题&quot;&gt;帧同步解决方案就很好的解决了状态同步和实时广播同步解决不了的问题。&lt;/h6&gt;

&lt;p&gt;在同步性和安全性要求很高的游戏中，例如王者荣耀，拳皇类格斗游戏，游戏中的每一帧都是非常关键的，一两帧的计算就有可能决定双方的胜负，所以不能有分毫之差，对于这种类型的游戏，同步的要求特别高而且精确计算的要求也很高，帧同步的解决方案正好契合这种类型游戏。&lt;/p&gt;

&lt;p&gt;与状态同步和实时广播同步法不同的是，帧同步的逻辑不再由客户端本身的逻辑帧Update来决定，而是转由从网络收到帧数据包来驱动执行逻辑更新，这也是帧同步最大的特点。其所有逻辑更新都放在了收到帧数据包时的操作中，包括人物角色的移动，攻击，释放技能等，每收到一个服务器发过来的帧数据包，就会更新一帧或更新前面因延迟累积的帧数。&lt;/p&gt;

&lt;p&gt;帧同步的服务器需要向每个客户端每秒发送15-30个帧数据包，每隔0.033-0.05秒发送一个，即使没有任何信息也会发送空的帧数据，因为客户端要根据这些帧数据包来‘演算’游戏逻辑。因此帧数据的集合被认为是一条时间线，平时我们用秒来计算时间，现在我们帧来代替，例如某个动作做5帧而不是5秒，这颗子弹向前滑行10帧而不是XX秒，其实质是用整数的方式来计量时间线上的位置而不再使用浮点数因为这样更准确。&lt;/p&gt;

&lt;p&gt;为什么要说‘演算’呢，一个比较容易理解的比喻是原来在客户端的Update里角色每帧移动xx米的逻辑，转移到了从网络收到的帧数据包的时刻，每收到一个帧数据包角色就调用一次移动逻辑(当然不只是移动逻辑，也不只会收到一个帧数据包，确切的说应该是更新逻辑)，这样使得不同的游戏设备在拥有不同的帧率的情况下，执行相同数量的逻辑帧的同时也执行了相同次数的逻辑指令。指令存储在帧数据里，不同设备收到的帧数据一致，执行顺序一致，执行结果也将一致(这里有精度问题导致结果不一样将在本节后面讨论)。&lt;/p&gt;

&lt;p&gt;帧数据中主要存储的就是指令以及指令相关的参数，一个帧数据可能有很多个指令分别指向不同的角色。当玩家通过屏幕或摇杆操作时将操作指令发给服务器，服务器在随后广播的帧数据中带就有我们上传的指令数据，除了有指令数据外的帧数据，其他没有任何指令的帧数据其内容是空的，空帧也需要被传达到每个客户端，因为这关系到逻辑的更新。&lt;/p&gt;

&lt;p&gt;我们客户端的执行步骤为，随着客户端不断收到从服务器端广播的帧数据，每帧都执行一次更新逻辑，执行到某一帧其带有指令数据就执行该帧内的所有指令，同时也更新逻辑。比如帧数据中指令为某角色以每帧1米速度向前移动，那么客户端就开始启动移动状态执行该指令，在接下来收到的帧数据中客户端每每执行逻辑更新都会执行每帧1米的逻辑，比如后面总共收到20帧的网络空数据帧，那么就执行了20次每帧1米的行走逻辑，直到玩家再次操作停止了移动指令，并把该指令发送给服务器端，服务器端再以帧数据的形式广播给所有玩家，任何玩家设备收到这个带有停止指令的帧数据时执行停止指令停止了移动。&lt;/p&gt;

&lt;p&gt;上述描述的客户端执行帧数据的逻辑步骤中，会有渲染与逻辑的差异。其原因是渲染时我们通常会一直在10-60帧范围内变化，而帧数据的频率则是固定的每秒15帧，这导致了帧数据的逻辑计算和渲染的差异。&lt;/p&gt;

&lt;p&gt;假如我们移动中帧数据在逻辑更新时不断的计算当前的位置以此来作为渲染帧中位置的依据，则我们将看到角色在一跳一跳一顿一顿的逐级向前’跳动‘。为了能更加平滑的模拟帧数据中的移动内容，我们在渲染时也要进行预测和模拟。移动时我们知道每个数据帧中角色的位置，一帧代表多少秒，这样至少2个帧数据就能知道当前的速度，至少3个帧数据就预测加速度，这和我们前面介绍的实时广播同步在模拟角色速度与位移时的方法一样，用最近3-5个点位数据来预测当前的速度和加速度以及旋转角度和角速度。当网络原因帧数据延迟的很厉害时，我们就应该能从收发的数据中得知网络有较大的延迟，例如距离最近一个帧数据收到已经是5秒前的时间了，那么我们就知道网络造成了严重的堵塞，现在的预测和模拟已经不再准确了应该停止才对，等待帧数据的到来再重新矫正位置和速度。&lt;/p&gt;

&lt;p&gt;现在的逻辑运算在网络收发的数据帧中执行，这就相当于服务器控制了所有玩家设备上的播放帧的速率和帧数长度，让所有玩家拥有相同的帧数据，执行相同的指令数量以及相同的指令顺序，由于执行逻辑的时间点(我们前面说过时间已经不再被用秒计算了而是用帧的数量计算)，执行的逻辑的次序和次数相同，从而使得所有收到帧数据的客户端做出的表现也是相同，这就是帧同步的最大特点。它由服务器发送的帧数据来完成所有客户端的同步执行操作，在每个客户端设备中所使用来‘演算’的算法一致，最终在所有设备中执行的结果一致，反应到屏幕画面上表现的出来的行为也是一致的。&lt;/p&gt;

&lt;h3 id=&quot;同步快进&quot;&gt;同步快进&lt;/h3&gt;

&lt;p&gt;现实中网络环境不太稳定特别是手机设备，不是所有客户端的网络都是流畅的，通常的网络环境都是时好时坏的，客户端在接收帧数据时经常都是波动的，时而会有一堆帧数据涌过来，或者又忽然完全收不到了帧数据，时常会有在两个极端之间的状况。因此如何预测和模拟延迟部分的表现，以及快速同步落后的很多帧的客户端画面成了客户端解决同步问题的关键。&lt;/p&gt;

&lt;p&gt;当网络造成延迟，帧数据一下子收到很多，为了能同步帧数据的逻辑，我们可以使用最简单的方式，瞬间执行全部堆积在队列里的帧数据，这样我们一下子就能到达逻辑数据中的最后一帧，接着继续正常接收和模拟画面。但是这样一次性执行所有的很多帧数据的方式会有问题，如果堆积的数据帧太多，会导致因执行逻辑更新太多而让游戏卡住很久，画面会因此停止不动一段时间，游戏体验会比较差。解决这个问题我们可以在执行帧逻辑时每次执行N帧(N大于10)来使画面快速推进，这样一来玩家又能看到动态的画面，又能快速的跟上最后的同步帧数据，同步完所有的帧数据后，再把执行速度恢复到正常，从而继续接收数据帧来做正常的同步工作。&lt;/p&gt;

&lt;p&gt;如果落后的太多太多，比如我们掉线后重连时相当于落后了整个数据帧，如果是一场20分钟的比赛相当于落后了20&lt;em&gt;60&lt;/em&gt;15=18000帧，这时快进的方法也不管用了，因为执行的帧数落后太多导致按普通快进的节奏得要快进很久才能跟上大部队，如果快进的速度太快则执行的帧数上升也会导致画面卡顿感太重。无论如何执行全部的帧逻辑消耗的CPU太多时间过久，如果是手机设备有可能会因消耗过大而发热发烫。&lt;/p&gt;

&lt;p&gt;这时我们可以用内存快照的方式做快进操作，快照在很多领域都有这样的概念，特别是硬盘快照，就是将硬盘里的数据全部复制一份作为备份，当下次要构建一个一模一样的新机器时就不用这么费力了，直接从快照中复制一份就可以完成装机工作。内存快照也是同样的理念，只是与硬盘快照不同的是内存快照做的内存备份，意思是把内存中关于战斗的所有数据包括玩家，怪物，可破坏的障碍等等都备份一份在文件上存储在本地或服务器中，当客户端需要所有帧数据时其实需要的是最后一帧数据计算后的内存数据，这时客户端就可以直接使用该快照数据获得内存数据的结果，以此为依据渲染画面。由于这一帧的快照离最后需要同步的帧数最近，中间可能跨越了几千或上万的帧数，从该帧数据开始快进到最近的数据帧，相对于从头开始快进来说要快了许多也节省了许多CPU的消耗。&lt;/p&gt;

&lt;p&gt;上面讲的都是模拟帧数据的画面表现，我们在发送操作指令时也需要注意一些问题，如果发送的指令过于频繁也会造成网络数据的灾难，比如玩家控制角色不断释放技能或者不停的旋转奔跑，就会让客户端以渲染帧的速度每帧大量的向服务器发送指令数据像机关枪一样扫射式的发射，这种方式就会造成帧数据混乱，一帧数据中当前玩家的指令应该最多只有1个，每个玩家的指令在1个帧的数据中只能有一个，只有这样才能不造成逻辑的混乱。&lt;/p&gt;

&lt;p&gt;为了在不让发送过多的指令混乱帧数据，我们可以选择把需要发送的指令存起来，等到收到一个从服务端来的网络帧数据时再发送，如果有很多指令则不断替换未发送的指令直到收到网络帧数据才发送最后一个替换的指令。这也符合多端帧同步的规则，其实是我们规定了每帧只能有一个操作，因为我们需要遵循’不能在同一帧有多个操作’的规则。这个规则确保了我们在逻辑计算时不能在同一帧中既前进又后退，也不能在同一帧中既释放技能又取消技能，确保了游戏逻辑的简单化。&lt;/p&gt;

&lt;p&gt;在实际操作中摇杆的在旋转和位移的变化以及技能的释放这些操作在用户端是非常快的，如果客户端等到有网路数据帧接收到时再发送当前的指令，那么用户操作的指令会被自己的指令覆盖好几次。实际确实是这样，很多手速非常快的玩家受到了网络数据帧频率的限制，为了解决这个问题很多游戏会提前模拟玩家的操作，客户端模拟玩家可以根据自己的操作自由行走甚至攻击或释放技能，而后再由网络接收到的数据来矫正位置与角度，或者先把指令序列存储起来，等到网络帧来的时候再将指令序列发送出去，这样的话可能是另一种网络延迟体验。&lt;/p&gt;

&lt;h3 id=&quot;精度问题&quot;&gt;精度问题&lt;/h3&gt;

&lt;p&gt;帧同步的核心战斗的计算逻辑放在了玩家各自设备的客户端中进行，我们前面说了由于所有设备执行的帧数一致，执行的指令和时间点一致，执行的算法一致，就能得出相同的结果。理论上确实是这样，但是这里会出现一个问题，由于不同设备上的浮点数精度损失结果不同，导致同样的浮点数公式在不同设备上计算出来的结果有细微的差别，经过多次并长时间的计算后这种误差会扩大到不可接受的地步。&lt;/p&gt;

&lt;p&gt;浮点数在各不同设备上的计算结果有细微的差异，随着计算量的增多差异会变得越来越大，即使我们逻辑业务的执行次数、时间、算法都一致，最终计算出来的结果还是会由于浮点数计算结果不同而不同。因此浮点数的精确计算也是帧同步在各设备同步问题上的一大关键，其根源是因为帧同步方案把计算过程交给不同设备而导致的问题。&lt;/p&gt;

&lt;p&gt;其实也并不是什么难题，我们有很多用浮点数精度问题的解决方案，定点数就是其中之一。所谓定点数，就是把整数和小数部分分开存储，小数部分用整数来计算，整数部分继续用整数，这样计算起来就不会有误差了。&lt;/p&gt;

&lt;p&gt;通常的浮点数在计算机中的表示法为 V = (-1)^s x (1.x) x 2^(E-f) 也就是说浮点数的表达其实是模糊的，它用了一个数的指数来表示当前的数。而定点数则不同，它把整数部分和小数部分拆分开来并都用整数的形式表示，这样计算和表达可以用整数的方式。整数的计算是确定的没有误差的，这样就不会存在不同设备上的误差，缺点是变量占用的内存空间多了一倍，计算的量也多了一倍，同时计算范围缩小了。&lt;/p&gt;

&lt;p&gt;用定点数来替换浮点数计算就能保证在各设备上的计算结果一致性，C#自带的decimal类型就是定点数，它在金融和会计领域的使用比较多，在游戏项目中使用的多，因为也并不顺手，比如无法和浮点数随意的互相转换，在计算前也依然需要进行封装，又比如无法控制末尾小数点，使得精度还是无法根据项目需求来控制，同时它占用128位数据来存储，游戏中很少需要这么多的位数，这会大量增加内存的负担。&lt;/p&gt;

&lt;p&gt;因此大部分项目都会自己去实现定点数的重新封装，把整数和小数拆开来都用32位整数表示封装在某个类中，再写一些关于定点数之间以及定点数与其他类型数字的数学计算库。数学库内的函数没有我们想象的那么多，其实就是把定点数与其他类型数字的加减乘除重写一下，如果涉及到更多的图形学运算的，则加入一些图形学的基本运算公式，比如线段交叉、点积、叉乘等，参数用我们封装的定点数代替。&lt;/p&gt;

&lt;p&gt;最最最快速简单，性价比最高的的方式，其实是将所有浮点数改为整数并乘以1000或者10000表达完整的数，以整数的方式来计算结果就不会有问题。把所有需要计算的数字都以这种方式存储，只有在需要在UI界面上展示小数点的时候才除回来变成浮点数再进行展示，但也仅仅是展示的用途不涉及逻辑运算。这样即控制了精度的一致性，也不用这么麻烦去实现定点数的封装。只是这样做，原本能表达的精度范围就减少了，整数部分缩小到了2000万或200万以内，小数部分只保留了3位或4位小数点精度，不过仍然能在部分游戏中使用，因为在很多帧同步游戏中，200万的数字都已经是足够大了，所以有时他们无需劳心费神的去封装一个定点数以及定点数数学库，而且封装后还难以与表现层有很好的过渡。直接乘一下就能达到一样的效果，大大降低了研发成本。不过两者各有优势利弊，不一定说哪种方式一定好或者坏，按照项目的需求做不同的决策是应该的也是必要的。​&lt;/p&gt;

&lt;p&gt;我们这里也了解下同步锁的机制。在更加严格的同步类游戏中，比如星际争霸1中，如果有玩家网络环境不好，希望能够等待该玩家的进度，就会使用同步锁的机制。同步锁的机制，要求每个客户端每隔一段时间都发送一个锁帧数据，类似‘心跳’数据包，服务器端在帧数据中嵌入心跳包，用这样的方式告诉其他玩家该玩家仍然在线并正常游戏中，如果客户端在接受帧数据时超过50帧没有收到某个玩家的锁帧数据的话则停止播放网络帧数据，等待该玩家跟上大部队后，再所有客户端同时从最近的一次锁帧数据点开始，以该点为最后一数据帧，一起继续各自演算。&lt;/p&gt;

&lt;p&gt;参考文献：&lt;/p&gt;

&lt;p&gt;《漫谈游戏帧同步》作者：布尔君de二次方&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>思路探讨(二十五) 困境中的中产</title>
   <link href="http://www.luzexi.com/2019/07/06/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A825"/>
   <updated>2019-07-06T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/07/06/思路探讨25</id>
   <content type="html">&lt;p&gt;还是聊聊关于钱的事吧，人生哲学聊多了也没什么立竿见影的效果，能理解的瞬间就能懂，理解不了也只能等那灵光一现的时候。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;最近股市很动荡，中国经济很动荡，L型的经济走势一直没有走完，还不断得被破坏和骚扰。我猜大部分人都在痛苦得挣扎，社会经济似乎走上了萧条的环境，周遭的环境和人们的积极性越来查差，埋怨的越来越多，人们开始怀疑中国的是否能崛起，其实我也一样，不过我知道未来总是不确定的，无论崛起与否我们自身都需要保持积极和乐观，即使有余钱的少部分人，手里拿着钱也是不知所措的，投哪都是亏惨的结局，所以现在这个环境，没有赢家。&lt;/p&gt;

&lt;p&gt;想尽快突围，只有一条路，打铁还需自身硬，乘着大环境不好，我们有理由不去追逐名利，那就沉下心来多多自我修行，多多学习，多多看书，多多磨炼。&lt;/p&gt;

&lt;p&gt;我为什么要弄明白经济运作规律，就是想在未来的人生道路上走的更有方向性，并不一定说要变富有，但至少能少走点邪魔歪道。&lt;/p&gt;

&lt;p&gt;其实最惨的就是中产阶级了，中产阶级即想往上冲，又背负着最沉重的债务，真的难。&lt;/p&gt;

&lt;p&gt;在经济不景气的时候大部分人都要面临债务危机。由于在经济繁盛的时候人们的心里装的都是怎么赚到更多的钱，而对大量的债务风险视而不见，当萧条和不景气来临时，就发现当前的债务过大，利息过多，原来在经济繁盛时很容易偿还和导手的资金，变得很艰难，因此债务压力变得越来越沉重。一不小心，几代人的血与泪以及奋斗积攒的财富，在一瞬间里全部消散了。&lt;/p&gt;

&lt;p&gt;这也是为什么中产阶级最难的原因，背负的债务与收入严重不匹配。在萧条期，最先倒下的就是中产，最容易倒下的也是中产，最快倒下的是中产。&lt;/p&gt;

&lt;p&gt;什么原因导致中产总是最先倒下，难道是中产的人笨吗？又为什么偏偏是中产阶级呢，就不能是其他层阶级或者富人阶级吗？&lt;/p&gt;

&lt;h6 id=&quot;中产有许多自己的阶级特征大部分的这些特征都说明了这个阶级没有自主权不够灵活&quot;&gt;中产有许多自己的阶级特征，大部分的这些特征都说明了，这个阶级没有自主权，不够灵活。&lt;/h6&gt;

&lt;p&gt;不够灵活体现在这几个方面：&lt;/p&gt;

&lt;h3 id=&quot;1资产量不大却大部分集中在单一投资方向上&quot;&gt;1，资产量不大，却大部分集中在单一投资方向上。&lt;/h3&gt;

&lt;p&gt;中产阶级的资产大部分比较集中房产上，与房地产行业捆绑紧密。并且是某一个市的一处房子上。&lt;/p&gt;

&lt;p&gt;或者有的中产买些股票，基本都是把大量资金集中在某只股票上。&lt;/p&gt;

&lt;p&gt;资产的升降直接与某个标的绑定的很牢固。只要这个标的一旦崩溃，就可能直接破产。&lt;/p&gt;

&lt;h3 id=&quot;2债务量与自身收入不成比例&quot;&gt;2，债务量与自身收入不成比例。&lt;/h3&gt;

&lt;p&gt;中产阶级创业的最多，超负荷负债买房的也最多。&lt;/p&gt;

&lt;p&gt;职场并不是所有中产阶级都能呆到退休的地方，这一点大部分人都没有意识到问题的严重性，年级大了想找份工作都是难事，年轻人的竞争力更强更有活力。只有职场上的赢家才能继续呆到退休，赢家很多种，技能上的，情商智商上的，还有运气上的。&lt;/p&gt;

&lt;p&gt;所以大部分中产阶级都需要通过自主创业的形式来获得更加稳固持久的收入来源，但创业哪有这么容易，基本都是赔钱，很多中产选择借钱维持，利滚利，加上，继续入不敷出，就会陷入一个更加大的泥潭，不止财务上出现大的问题，精神上也会出现大的问题。&lt;/p&gt;

&lt;p&gt;所以债务导致的破产，在中产阶级数不胜数。&lt;/p&gt;

&lt;h3 id=&quot;3财富迁移难度大&quot;&gt;3，财富迁移难度大。&lt;/h3&gt;

&lt;p&gt;中产阶级虽然有财富，但毕竟是不是富有人家，无法轻易的迈过一些高门槛的迁移动作。&lt;/p&gt;

&lt;p&gt;比如从二三线城市，甚至从三四线城市，向一线城市迁移就很难，这些其他线城市的房价卖掉后，在一线城市也很难买到几平米，更不用说由于迁移引发的一系列的家庭和生活的问题，挡住了大部分家庭的决心。&lt;/p&gt;

&lt;p&gt;其实不管从心里和现实的角度看，迁到一线城市这种动作，都是需要付出沉重代价的。沉重的代价背后，是否值得，对于每个不同家庭情况来说，都是不一样的。&lt;/p&gt;

&lt;p&gt;又比如移民到拥有更好的经济环境的国家去，这种大级别的迁向更好环境的动作非常难,门槛也非常高。迁移到比中国环境更加弱的国家去是毫无意义的，因为经常听到有人迁移到东欧，以及西欧部分不发达国家，甚至东南亚地区，根本就是自找苦吃。&lt;/p&gt;

&lt;p&gt;唯有迁到世界第一的美国，才是所有迁移选择中最值得的，迁移到美国的门槛比较高，要么技术很牛逼，要么钱很多，500w以上是正常的迁移及投资费用，对于囊中羞涩的中产来说希望渺茫。&lt;/p&gt;

&lt;h3 id=&quot;4急功近利的思想居多&quot;&gt;4，急功近利的思想居多。&lt;/h3&gt;

&lt;p&gt;严重缺钱的中产，其实比其他各层阶级都缺钱，这种缺钱的氛围时常影响着中产家庭的整个人生，这很容易滋生暴富思想。&lt;/p&gt;

&lt;p&gt;很多中产的人总是希望通过借用杠杆来达到一夜暴富。坚信，深信，只要机会一来，自己就能暴富。&lt;/p&gt;

&lt;p&gt;所以中产也是最好骗的，错的，假的，危险的机会，中产们很容易受其诱惑，一旦试图抓住这些机会，他们总想着暴富，不顾一切的投入，全部资产还不够，甚至还借用杠杆，试图一夜暴富。最终，醒过来时，一切已晚，财富早已化为乌有，消散而去。&lt;/p&gt;

&lt;p&gt;其实不能说中产素质不够，辨识不了真假和风险的高低，因为即使博士毕业在这样一个氛围的圈子里也同样会被蒙蔽了双眼，学历越高的人对自己做出的选择坚信不疑的程度越高。&lt;/p&gt;

&lt;h6 id=&quot;那么中产就没有办法了吗&quot;&gt;那么中产就没有办法了吗？&lt;/h6&gt;

&lt;h6 id=&quot;这个世界能突出重围的人毕竟是少数无论在哪个圈子哪个层级都是一样一样的因为人性不变金字塔就不会变中产更是普遍持有其特点的人群&quot;&gt;这个世界能突出重围的人毕竟是少数，无论在哪个圈子，哪个层级，都是一样一样的，因为人性不变，金字塔就不会变，中产更是普遍持有其特点的人群。&lt;/h6&gt;

&lt;h6 id=&quot;因为能看清经济发展规律并降低财富抬升期望的人毕竟是少数&quot;&gt;因为能看清经济发展规律并降低财富抬升期望的人毕竟是少数。&lt;/h6&gt;

&lt;h6 id=&quot;能持续不断分散投资多维度考察和思考投资思路和角度的人毕竟是少数&quot;&gt;能持续不断分散投资，多维度考察和思考投资思路和角度的人毕竟是少数。&lt;/h6&gt;

&lt;h6 id=&quot;能沉下心来排除杂音专心做好事情以及专心做自己的人毕竟是少数&quot;&gt;能沉下心来，排除杂音，专心做好事情，以及专心做自己的人毕竟是少数。&lt;/h6&gt;

&lt;h6 id=&quot;能推开暴富机会诱惑的不嫉妒那几十万分之一概率中赢得财富的人毕竟是少数&quot;&gt;能推开暴富机会诱惑的，不嫉妒那几十万分之一概率中赢得财富的人毕竟是少数。&lt;/h6&gt;

&lt;h6 id=&quot;能持之以恒不为外界纷扰所动的人毕竟是少数&quot;&gt;能持之以恒，不为外界纷扰所动的人毕竟是少数。&lt;/h6&gt;

&lt;h6 id=&quot;如果我们能做到这些少数人才能做到的我相信量变造成质变会在多年后发生在我们身上&quot;&gt;如果我们能做到这些少数人才能做到的，我相信量变造成质变会在多年后发生在我们身上。&lt;/h6&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(二十六) 我们真的聪明吗</title>
   <link href="http://www.luzexi.com/2019/06/24/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A826"/>
   <updated>2019-06-24T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/06/24/思路探讨26</id>
   <content type="html">&lt;h3 id=&quot;更多时候我们不是败给了外界而是败给了自己&quot;&gt;更多时候，我们不是败给了外界，而是败给了自己。&lt;/h3&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;h6 id=&quot;我们都是认为自己是聪明的人没有人会认为自己是那个笨蛋但聪明真的用在了刀口上吗不是的更多时候聪明被用在刀背上而愚蠢却常常用在刀口上&quot;&gt;我们都是认为自己是聪明的人，没有人会认为自己是那个笨蛋。但聪明真的用在了刀口上吗，不是的，更多时候，聪明被用在刀背上，而愚蠢却常常用在刀口上。&lt;/h6&gt;

&lt;p&gt;“骄傲使人落后”这句话一点都没说错，人很容易没有警惕心理，在没有了谨小慎微的态度后，人总是不自觉的陷入当前环境的假想中，认为当前状况是永远可持续的。&lt;/p&gt;

&lt;p&gt;举个例子，在繁荣时期，人人都有工作的时间，社会上到处都是工作，赚钱也很容易，投个股票，买个基金，放贷给个人，投个P2P，坐等收钱就行了。&lt;/p&gt;

&lt;p&gt;人会不自觉的陷入我不缺工作，我很聪明，我很能赚钱，我很厉害的心理状态和自我认识。&lt;/p&gt;

&lt;p&gt;所以在繁荣时期，人们都会觉得自己很聪明，很能赚钱，我们会认为是因为我们的聪明所以工作很容易找到，赚钱很容易，却从来不认为是繁荣的环境造就了我们。&lt;/p&gt;

&lt;h6 id=&quot;但在萧条时期就不同了我们会很难找到工作赚钱也很难亏钱很容易原来在繁荣时期浪费的时间浪费的钱在萧条时期就开始了对我们的大规模的惩罚这对人自身的打击不是一点点能彻底醒悟的人还是不多能冷静下来一步步脚踏实地积累和沉淀的更少骄傲没有这么容易败下阵来&quot;&gt;但在萧条时期就不同了，我们会很难找到工作，赚钱也很难，亏钱很容易，原来在繁荣时期浪费的时间，浪费的钱，在萧条时期就开始了对我们的大规模的惩罚。这对人自身的打击不是一点点，能彻底醒悟的人还是不多，能冷静下来一步步脚踏实地积累和沉淀的更少，骄傲没有这么容易败下阵来。&lt;/h6&gt;

&lt;p&gt;那么有人说，我在繁荣时期不去凑这个热闹，自己一步步脚踏实地的积累和沉淀，走自己的路，让别人去赚它自己的钱，不就可以了么。&lt;/p&gt;

&lt;p&gt;其实并没有这么容易，还是同样的话，人很难不受当前环境所影响，在大家都大把大把地赚钱，大把大把的挥霍的时候，我们是很难不受他们影响而不去追随他们的，嫉妒心和贪念的放大致使我们无法控制自己，在贪婪的时刻疯狂的去追求一些虚无缥缈的价值。&lt;/p&gt;

&lt;p&gt;其实退一步说，即使我们能忍住不去嫉妒、不去贪婪，我们周围的亲朋好友肯定没有我们这么好的定力，他们可能会不断得劝我们，骚扰我们，甚至指责我们的无能，指责我们在别人都疯狂的享受盛宴的时候我们却让这么大好的机会错过。&lt;/p&gt;

&lt;p&gt;像这样的假象在生活当中其实有很多。我们年轻的时候，会有股很强烈的意识，认为我们的时间和精力会一直像现在这么多，所以我们在年轻时，时常会大把大把的浪费我们的时间和精力在一些根本毫无意义的事情上，而对于那些需要我们沉下心来学习和积累的事情却认为时间很多以后也能去做。&lt;/p&gt;

&lt;p&gt;随着时间的流逝，精力的下降，年龄的增大，量变成了质变，突然会意识到，我们已经不再年轻，已经不再拥有这么多精力，而原本应该学习和积累的才干却一点都没有做到，原来大把大把的消磨时光和浪费的精力，已经一去不复返了。&lt;/p&gt;

&lt;p&gt;对于青春貌美的年轻人也是同样，几乎所有的年轻人都在这个年轻的姿态下，总是会不自觉的认为我会永远这样年轻貌美，进而无节制的用当下的年轻貌美的优势来享受当下的快乐，等过几年发现年轻貌美一去不复返时，却什么都没有留下。&lt;/p&gt;

&lt;h6 id=&quot;就因为我们并没有意识到当下的我们其实是暂时的我们的聪明和貌美只是环境造就的而非我们自己本身的努力所以总是表现出一副傲人的姿态&quot;&gt;就因为我们并没有意识到当下的我们其实是暂时的，我们的聪明和貌美只是环境造就的，而非我们自己本身的努力，所以总是表现出一副傲人的姿态。&lt;/h6&gt;

&lt;p&gt;我们真的聪明吗，确实不笨，但总是聪明反被聪明误。&lt;/p&gt;

&lt;h6 id=&quot;除了在好环境中无法感知自身的不足外在辛苦的环境也也会无法感知自身的进步&quot;&gt;除了在好环境中无法感知自身的不足外，在辛苦的环境也，也会无法感知自身的进步。&lt;/h6&gt;

&lt;p&gt;人总是无法在当前情景中察觉到自己的位置和状态，就像古诗说的，不识庐山真面目，只缘身在此山中。&lt;/p&gt;

&lt;p&gt;我们在辛苦和充实的日子里，经常会感到乏味、枯燥、苦楚，因为辛苦的日子里天天做着重复的事情，天天面对的自己无法克服的障碍，天天面对从来没有面对过的困难，天天跟自己较劲，天天突破自己的极限，以及天天突破自己的三观和认知。而我们却很难察觉这种辛苦和枯燥对我们的帮助有多大。就因为无法察觉导致我们时常想要摆脱辛苦的状态，总想着早早放弃。&lt;/p&gt;

&lt;p&gt;有时候也要冷静下来，细心想想，这些辛苦的日子里，我到底学到了什么，及时的让自己感受到进步。如果能在辛苦的时候察觉到自己的进步，这种及时的良好的反馈，能让自己不受精神的阻扰，勇往直前。&lt;/p&gt;

&lt;h6 id=&quot;苦中作乐其实是最好的状态但关键还是我们要能感受到乐的存在如果感受不到乐而只感受到苦那么即使是在一个很好的很难得的环境下可能我们也很难坚持下去&quot;&gt;苦中作乐，其实是最好的状态，但关键还是我们要能感受到’乐‘的存在，如果感受不到’乐‘而只感受到’苦‘，那么即使是在一个很好的、很难得的环境下，可能我们也很难坚持下去。&lt;/h6&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第十章，地图与寻路(一) A星算法及优化</title>
   <link href="http://www.luzexi.com/2019/06/22/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-%E5%9C%B0%E5%9B%BE%E4%B8%8E%E5%AF%BB%E8%B7%AF1"/>
   <updated>2019-06-22T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/06/22/Unity3D高级编程之进阶主程-地图与寻路1</id>
   <content type="html">&lt;p&gt;寻路是游戏项目中的常用功能，寻路算法用的最多的就是A星算法，其他也有比如 Dijkstra算法 与 Floyd 算法，它们两个在时间和空间上的复杂度都太高因此在游戏中用的比较少，Dijkstra算法时间复杂度为O(N^2)，空间复杂度也是O(N^2)，Floyd的时间复杂度更高有O(N^3)，空间复杂度也在O(N^2)，这种复杂度的寻路算法在小范围使用还能接受，如果应用在游戏中稍微大一点的场景中寻路范围变大了就会消耗掉很多CPU的计算量，甚至会有长时间被寻路计算阻塞的情况，频率稍微高一点的寻路需求也会让CPU无法承受。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;A星算法其实并不是最短路径算法，它找到的路径并不是最短的，它的目标是能以最快的速度找到通往目的地的路，算法的时间平均复杂度为O(NLogN)，最差的情况是从起点出发把所有的格子都走了一遍，最后才找到目的地。&lt;/p&gt;

&lt;h6 id=&quot;用一句话概括a星用最贪婪的方法最快的寻找到通往目的地的路径&quot;&gt;用一句话概括A星：用最贪婪的方法最快的寻找到通往目的地的路径。&lt;/h6&gt;

&lt;p&gt;它是如何用贪婪法的呢，我们拿方格来描述A星算法，这样描述很容易理解。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;		[][][][][]
		[][][][][]
		[][s][][][]
		[][][][][]
		[][][][][e]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上图为5X5的方格，其中s为start起点(3,2)，e为end终点(5,5)。&lt;/p&gt;

&lt;h6 id=&quot;用最贪婪的方法去找目的地先将四周的点加入到列表中在列表中找到与目的地e距离最短的那个就是离目的地e最接近的把这个点推出来继续用这个点向前探索直到找到目的地&quot;&gt;用最贪婪的方法去找目的地：先将四周的点加入到列表中，在列表中找到与目的地e距离最短的那个，就是离目的地e最接近的，把这个点推出来继续用这个点向前探索，直到找到目的地。&lt;/h6&gt;

&lt;h6 id=&quot;简单来说从s点开始取出它周围的4个点上下左右计算下它们与终点e的距离哪个离e的距离最短就推出那个点并取它周围4个元素继续进行同样的操作直到找到目的地e&quot;&gt;简单来说从s点开始取出它周围的4个点，上，下，左，右，计算下它们与终点e的距离，哪个离e的距离最短，就推出那个点并取它周围4个元素继续进行同样的操作，直到找到目的地e。&lt;/h6&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;		[-][-][-][-][-]
		[-][1][2][3][-]
		[1][s][1][2][3]
		[-][1][2][3][4]
		[-][-][-][4][e]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;如上图中，s开始取到周围的4个点标记为1，计算距离，并推入到一个队列，把这个队列按到e点距离值从小到大排序一下，取出离e点距离最短的点，再从该元素周围取出4个元素标记为2，推入到队列，并对它们计算与e的最短距离，再排序一下，取出离e点距离最近的那个标记为3，依次重复这种操作，直到找到e目的地。所有被标记过的都不能再被重复标记。&lt;/p&gt;

&lt;p&gt;我们来看看稍微复杂点的A星寻路例子：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;		[-][-][-][-][-]
		[s][+][+][-][-]
		[-][+][e][-][-]
		[-][+][-][-][-]
		[-][-][-][-][-]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;从s出发到e，图中‘+’为障碍物，用贪婪的A星寻路会怎么找到e呢，如下图：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;		[1][-][-][-][-]
		[s][+][+][-][10]
		[1][+][e][10][9]
		[2][+][+][+][8]
		[3][4][5][6][7]
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;1先取s周围的点标记1&quot;&gt;1.先取s周围的点标记1&lt;/h6&gt;

&lt;h6 id=&quot;2取标记里最近的且没被取过的点即下面这个1点取它周围点标记为2&quot;&gt;2.取标记里最近的且没被取过的点，即下面这个1点，取它周围点标记为2&lt;/h6&gt;

&lt;h6 id=&quot;3取标记里最近的且没被取过的点即点3也可以是上面的1但我们定个规则一样的距离以下面为标准取它周围的点标记为点4&quot;&gt;3.取标记里最近的且没被取过的点，即点3也可以是上面的1，但我们定个规则一样的距离以下面为标准，取它周围的点标记为点4&lt;/h6&gt;

&lt;h6 id=&quot;4离e点最近的还是4继续取它周围的标记为5&quot;&gt;4.离e点最近的还是4，继续取它周围的标记为5&lt;/h6&gt;

&lt;h6 id=&quot;5离e点最近的还是5继续取周围点标记为6&quot;&gt;5.离e点最近的还是5，继续取周围点标记为6&lt;/h6&gt;

&lt;h6 id=&quot;6离e点最近的还是6取它周围的点标记为7&quot;&gt;6.离e点最近的还是6，取它周围的点标记为7&lt;/h6&gt;

&lt;h6 id=&quot;7离e点最近的还是7取它最近的点标记为8&quot;&gt;7.离e点最近的还是7，取它最近的点标记为8&lt;/h6&gt;

&lt;h6 id=&quot;8离e点最近的是8取它周围的点标记为9&quot;&gt;8.离e点最近的是8，取它周围的点标记为9&lt;/h6&gt;

&lt;h6 id=&quot;9离e点最近的是9取它周围的点标记为10&quot;&gt;9.离e点最近的是9，取它周围的点标记为10&lt;/h6&gt;

&lt;h6 id=&quot;10离e点最近的为左边的10取它周围的点时发现到达目的地e结束&quot;&gt;10.离e点最近的为左边的10，取它周围的点时发现到达目的地e，结束。&lt;/h6&gt;

&lt;p&gt;上述过程明晰的阐述了A星算法贪婪的全过程，即只选择当前的最优选择，因为只关注当前的最优解，就忽视了对全局的最优解，走‘弯路’是常有的事。体现在代码上为如下伪代码：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;		function find_path(s,e)
		{
			open = new List(); //没有被取到过的点
			close = new List(); //已经被取过的点

			open.add(s); //从s点开始
			close.add(s);//将s加入close队列

			for(!open.IsEmpty()) //重复遍历直到没有点可以取
			{
				p = open.pop(); //把最近的点推出来

				if(p == e)
				{
					//找到终点
					break;
				}

				p1 = p.left(); //取左边的点
				p2 = p.right(); //取右边的点
				p3 = p.top(); //取上边的点
				p4 = p.down(); //取下边的点

				plist.add(p1);
				plist.add(p2);
				plist.add(p3);
				plist.add(p4);

				for(int i = 0 ; i&amp;lt;plist.Count ; ++i)
				{
					pp = plist[i];
					if(null != pp &amp;amp;&amp;amp; pp.IsNotInClose())
					{
						pp.f = dis(pp,e);
						if(pp.IsNotInOpen())
						{
							pp.SetOpen();//设置为已经在open中
							open.Add(pp);//加入队列
						}
					}
				}

				//p点已经被取过了
				close.add(p);

				open.sort(); //排序一下
			}
		}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述伪代码完整的诠释了A星寻路的全过程。我们设置了两个队列，一个是open队列，存储的是被选中过的点周围的点并且这些点没有被取到过，另一个是close队列，它存储的是已经被遍历过的点。最初先把起点放入open队列中然后开始循环，把open中最头上的数据推出来，并判断它周围的4个点是否已经‘踩’过，如果没有被‘踩’过就放入open队列成为可选的节点标志，把当前被推出来的点标记为被踩过的点，最后把整个open排序下继续循环。&lt;/p&gt;

&lt;p&gt;当我们面对场景地图很大的时候，或者使用A星算法的寻路频率很高的时候，普通的A星算法就会消耗大量的CPU，设备的性能就会急剧下降，这样看来普通的A星性能依然无法过关，我们需要改进它让它变得更高效。接下来我们讲讲A星寻路在遇到性能瓶颈时的优化方案。&lt;/p&gt;

&lt;h3 id=&quot;一长距离导航&quot;&gt;一、长距离导航&lt;/h3&gt;

&lt;p&gt;当我们需要寻路的两点距离很大，中间有很多障碍物时，A星的算法就会遇到瓶颈。我们前面展示了A星算法的伪代码来描述算法的步骤，我们从代码中可知open队列排序瓶颈，它会不断加入的可行走点使得排序速度越来越慢，这是最终导致CPU计算量过大画面无法动弹的主要原因。&lt;/p&gt;

&lt;p&gt;也就是说，当两点的距离很长时，open队列有可能被塞入了太多的节点而导致排序变慢，而open队列不断被塞入节点和排序是不可避免的，那么当寻路距离太大时我们该怎么办？&lt;/p&gt;

&lt;p&gt;我们可以把路径寻路的计算量大部分转化为离线下计算，其实很多时候我们可以把中间很长一段距离的计算过程放到脱机工具中去计算。路径不是非得实时计算出来才好，我们可以把一些常用的路径，在离线下算好放在数据文件中，当游戏开启时将已经计算完毕的常用点对点路径放在内存里，当角色需要寻路到某个节点时，我们可以从已经计算好的点对点数据中寻找起始点和目的地离我们最近的数据，如果我们要寻路的起点和终点这两个节点恰好是在我们计算好的数据附近，就可以将这段路径取出来直接使用，再拼上两点到真正起点和终点的实时寻路计算路径就成了一个完整的从起点到目的地的路径。这样我们节省了中间很长一段路径的计算量不再需要重新计算一遍，留给我们需要计算的仅仅是起点到伪起点和伪终点到终点的实时路径部分。&lt;/p&gt;

&lt;p&gt;我们来举例说明这种情况的过程，假如地图中 A点 到 B点 的路径，我们已经算好并存放在了内存里，当我们在A点附近的C点要寻路到B点附近的D点时，即C到D，中间有A到B的相似路径。我们从内存中得知C点的附近有A，并且D点的附近有B，A和B是的路径已经计算完毕，那么我们就可以先计算从C点寻路到A点的路径，再调出A到B的路径直接拼在后面，再计算B点到目的D点的路径将它们拼在路径在后面就有了C到D的完整了路径。以这种方式来规避一些计算量比较大寻路计算量的消耗，这种方式在大型世界的RPG游戏里特别常用，我们通常称它们为导航点，只要角色到了这个导航点就能直接取出路径直达目的地，而不再需要大量的寻路计算消耗。&lt;/p&gt;

&lt;p&gt;我们说实时计算中的寻路道路不能太长，太长的道路计算就会耗尽CPU让画面阻塞，离线计算的导航点也是一样，即使是离线计算也不能一口气把最南端的地图到最北端的地图的寻路路径计算完毕，这样的计算效率太差，因为路径越长情况越多，加入open列表的节点成指数级增长。&lt;/p&gt;

&lt;p&gt;因此我们也需在长路径上设置众多导航点以便加快离线计算，有了众多导航点就能在长路径寻路前先做导航点寻路，再取出各路径上导航点之间的数据形成长距离路径，比如大地图上有5座城市里，每座城市都有4个出口点，这个4个出口点就可以做成离线的导航点，每座城市之间的导航点都做了离线的寻路计算并存储了路径在数据文件上，我们在寻路时可以先寻找到最近的一个导航点，再从最近的一个导航出发寻找到目的地附近导航点的导航路径，我们可能找到，导航点 a -&amp;gt; c -&amp;gt; e 的导航点路径，直接取出 a -&amp;gt; c 和 c -&amp;gt; e 的路径，再拼凑上实时计算的角色到 a导航点的路径以及 e导航点到目的地的路径，就完成了一条完整的长距离路径链。&lt;/p&gt;

&lt;h3 id=&quot;二a星的排序算法优化&quot;&gt;二、A星的排序算法优化&lt;/h3&gt;

&lt;p&gt;前面我们多次提到open队列的问题，它在A星寻路中起到关键作用，由于每次插入open队列的点后，open就不再是有序的队列了，所以每次去拿最小值时都需要重新排序。排序的时间消耗随着队列长度的增大而增大，我们的A星大一部分消耗都在open队列排序上，所以对open排序做优化是比较重要的。&lt;/p&gt;

&lt;p&gt;是不是可以不排序，其实可以不排序只查找并插入，先使用查找算法找到应该插入的位置再插入元素，可以让队列在不用排序的状态下做到有序。&lt;/p&gt;

&lt;p&gt;通常我们使用最小堆的数据结构来做插入操作，由于每次只需知道最小预期值的节点，因此最小堆数据结构非常适合A星寻路的open排序。我们前面介绍过堆排序以及最大最小堆的基础知识，这里稍微简单阐述一下，最小堆的数据结构是完美二叉树结构，每个父节点都比子节点小，因此根节点肯定是最小的那个元素，每次插入或删除时都会重新寻找最小预期值的那个节点放在根结点上。它的插入和删除算法的时间复杂度为O(logN)，整体插入操作的复杂度为 O(logN + N)。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;function find_path(s,e)
{
	open = new MinHeap(); //最小堆
	close = new List(); //已经被取过的点
	plist = new List();

	open.add(s); //从s点开始
	close.add(s); //将s加入到close队列

	for(!open.IsEmpty()) //重复遍历直到没有点可以取
	{
		p = open.pop(); //把最近的点推出来

		if(p == e)
		{
			//找到终点
			break;
		}

		p1 = p.left(); //取左边的点
		p2 = p.right(); //取右边的点
		p3 = p.top(); //取上边的点
		p4 = p.down(); //取下边的点

		plist.Clear();
		plist.add(p1);
		plist.add(p2);
		plist.add(p3);
		plist.add(p4);

		for(int i = 0 ; i&amp;lt;plist.Count ; ++i)
		{
			pp = plist[i];
			if( null != pp &amp;amp;&amp;amp; pp.IsNotInClose())
			{
				pp.f = dis(pp,e); // 期望值为到终点的最短距离
				if(pp.IsNotInOpen())
				{
					pp.SetOpen(); //设置已经在open中
					open.Add(pp); //加入最小堆
				}
			}
		}

		//p点已经被取过了
		close.add(p);
	}
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述代码与前面不同的是open队列改为了最小堆，我们不再需要在每次循环结束时重新排序而是在节点插入最小堆时进行做队列的调整。&lt;/p&gt;

&lt;p&gt;除了最小堆排序外我们也可以用二分查找代替最小堆，因为open队列在插入元素前一定是有序的，因此我们可以先用二分查找算法来找到插入的位置。每次插入时都使用二分查找算法查找插入点，再将元素插入进队列，那么每次的插入复杂度为O(logN + N)。无论最小堆或二分查找都比快排一次的时间复杂度O(NlogN)要好很多。&lt;/p&gt;

&lt;h3 id=&quot;三优化期望值的计算方法&quot;&gt;三、优化期望值的计算方法&lt;/h3&gt;

&lt;p&gt;前面讲解A星算法时所举的例子中节点的期望值都是以当前点与终点e之间的距离来作为期望值，期望值越小越接近终点，这种方法简单但也不科学，这导致A星在障碍物比较多的复杂地图中寻找路径时会要绕比较大的弯路，也导致了在寻路过程中open队列中加入了比预期更多的节点使得open队列的排序变慢。&lt;/p&gt;

&lt;p&gt;我们需要优化一下这个计算期望值的策略，我们选用一个更科学的方法&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	F 期望值= G 当前最近步数 + H 预测当前点到终点的步数
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;F 为我们需要计算期望值，G为起点s到当前点p的最少步数，H为当前点p到终点e的最短距离，将这两个值加起来就是 F 期望值。其中 G 是已经计算好并放入节点p中的值，因为q是被open队列推出来的，所以它的最少步数肯定是计算完毕的，该值就是我们在前面计算过程中起点s到当前点的最少步数。我们可以把每步计算好的步数都放入节点中，以待需要计算时使用。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;function find_path(s,e)
{
	open = new MinHeap(); //最小堆
	close = new List(); //已经被取过的点

	open.add(s); //从s点开始
	close.add(s); //将s加入到close队列

	for(!open.IsEmpty()) //重复遍历直到没有点可以取
	{
		p = open.pop(); //把最近的点推出来

		if(p == e)
		{
			//找到终点
			break;
		}

		p1 = p.left(); //取左边的点
		p2 = p.right(); //取右边的点
		p3 = p.top(); //取上边的点
		p4 = p.down(); //取下边的点

		plist.add(p1);
		plist.add(p2);
		plist.add(p3);
		plist.add(p4);

		for(int i = 0 ; i&amp;lt;plist.Count ; ++i)
		{
			pp = plist[i];
			if( pp.IsNotInClose() &amp;amp;&amp;amp; p.g + 1 + dis(pp,e) &amp;lt; pp.f)
			{
				pp.g = p.g + 1;
				pp.f = pp.g + dis(pp,e);
				if(pp.IsNotInOpen())
				{
					pp.SetOpen();//设置为已经在open中
					open.Add(pp);//加入最小堆
				}
				else
				{
					open.Update(pp);//更新最小堆中的节点
				}
			}
		}

		//p点已经被取过了
		close.add(p);
	}
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述代码与之前的相比我们又加入了新的期望值的计算方式，对于期望值有更新的节点在堆中进行更新。新的期望值计算方式的改变相当于原来我们只关注点到终点的距离大小，现在变为关注起点s经过当前点p路径再到终点e的总距离，虽然仍然是贪婪的简单预测算法，但比起原来只关注当前点p与终点e的距离更加科学化，这能让我们更快的找到更好更近的点位去接近终点。&lt;/p&gt;

&lt;p&gt;我们改善了期望值的计算方式就能更快的接近终点是件很神奇的事，那么究竟改善期望值算法有怎样的意义呢？其实期望值的计算方法代表了在寻路过程中的我们探索的规则，如果我们的计算公式只关注于离终点距离最近的点位，那么在寻路过程中的选择点位的顺序就会偏向于与终点更近的点，无论能不能最终到达终点，只要它越靠近终点就行，但通常地图中很多点位很靠近终点但却无法达到终点。而如果期望值计算公式关注的是从起点到终点整段距离最小的点位上，那么在寻路过程中在选择点位上时也就会偏向整条路径最短的方向上去靠，这也间接加快了寻路的速度，即找到最快到达终点的点位。&lt;/p&gt;

&lt;h3 id=&quot;四a星寻路细节优化&quot;&gt;四、A星寻路细节优化&lt;/h3&gt;

&lt;p&gt;通常游戏中对A星寻路算法使用的频次比较高，这使得在多次频繁寻路中对A星算法中每个运算，每行代码的运算细节都会有比较重大的考验。&lt;/p&gt;

&lt;p&gt;比如我们在查看一个节点是否为被取过的节点，即是否为Close时，很多人都会在Close队列中寻找该节点是否存在，这个操作明显就没有考虑到性能的消耗，要在Close列表中找节点，就相当于遍历一遍所有已经找过的节点，Close里的节点越多越浪费CPU，而且是不只一次浪费每个循环都会浪费一次，性能消耗巨大。因此我们通常的做法是把节点作为一个实例，在实例中添加IsClose的变量，来判断是否被取过，或者说是否Close。但这种方法还是不够好，IsClose变量在寻路前是需要被初始化的，因为我们必须在每次寻路都要将前面寻路过的痕迹抹去才能开始全新的寻路过程。这就是又一个被很多人忽视的初始化的性能消耗，每次在A星寻路开始前必须将IsClose的变量初始化为false，就需要我们遍历整个数据结构来初始化每个实例中的变量。&lt;/p&gt;

&lt;p&gt;倘若我们在每次寻路前都要遍历整个数据结构的话，在短路径的寻路上A星的优势就荡然无存了，因为在初始化部分的性能消耗就已经将A星节省下来的性能消耗完全覆盖掉了。可以这么说如果初始化的需要遍历整个数据结构，那么优化A星算法的意义就会减少很多，因此我们需要用更好的方式来判断IsClose功能，最好是无需初始化的。&lt;/p&gt;

&lt;p&gt;我们可以改变一下判断方式，在寻路类中设置一个属性变量或者专门为寻路服务的静态变量也可以，暂名为FindIndex，让每个寻路节点中也存有一个变量FindIndex，每次调用寻路方法时先对FindIndex做加1操作，这样全局的FindIndex肯定比节点实例中的FindIndex要大。在判断IsClose时，当节点中的FindIndex与寻路类中FindIndex相等时则说明已经被当次寻路算法取出过，而如果它们两者不一样则说明这个节点没有被取出过。当节点被取出时，节点里的FindIndex应该设置为当前寻路类中的全局FindIndex值，以表明该节点已经被这次寻路算法计算过。&lt;/p&gt;

&lt;p&gt;用整数比较方式来代替布尔型变量的初始化，省去了巨量的初始化操作。不只是IsClose部分，其他需要判断初始化布尔型的逻辑都可以用此方法来避免初始化的开销。&lt;/p&gt;

&lt;h6 id=&quot;在a星算法这种经常用的算法中一个小小的性能消耗就能放大很多倍因此我们需要特别注意调用的函数的复杂度公式的复杂度以及运算的优化尽量做到能不调用函数的不调用函数能简化公式的尽量简化公式能用位运算符号代替加减乘除的尽量用位运算代替以节省a星算法的性能开销&quot;&gt;在A星算法这种经常用的算法中，一个小小的性能消耗就能放大很多倍，因此我们需要特别注意调用的函数的复杂度，公式的复杂度，以及运算的优化，尽量做到能不调用函数的不调用函数，能简化公式的尽量简化公式，能用&amp;amp;|&amp;lt;&amp;gt;位运算符号代替加减乘除的尽量用位运算代替，以节省A星算法的性能开销。&lt;/h6&gt;

&lt;h6 id=&quot;五a星规则优化jumppointsearch&quot;&gt;五、A星规则优化，JumpPointSearch&lt;/h6&gt;

&lt;p&gt;???&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第六章，网络层(五) - 剖析数据协议原理</title>
   <link href="http://www.luzexi.com/2019/06/16/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-%E7%BD%91%E7%BB%9C%E5%B1%825"/>
   <updated>2019-06-16T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/06/16/Unity3D高级编程之进阶主程-网络层5</id>
   <content type="html">&lt;h6 id=&quot;协议包的格式json-msgpack-protobuf-以及自定义格式&quot;&gt;协议包的格式，json, msgpack, protobuf 以及自定义格式&lt;/h6&gt;

&lt;p&gt;项目的网路层在建设中，除了选择传输协议TCP，UDP，以及应用层协议HTTP方式外，还需要选择在传输过程中的业务层协议格式。前面我们分析了TCP，UDP，HTTP的原理与应用，这里我们来了解下在传输层和应用层之上的业务层中，网络数据传输格式的选择以及它们的利弊。我们将在这里剖析JSON，MessagePack，Protobuf的原理，包括它们都是由什么组成的，怎么序列化的，以及怎么反序列化，通过对原理和底层的剖析使我们对网络数据协议的理解更加透彻清晰。&lt;/p&gt;

&lt;p&gt;我们从最常见的JSON格式开始，一步步深入了解业务层协议的规则与背后的原理，一步步剖析复杂的数据格式与底层实现。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;h6 id=&quot;json&quot;&gt;JSON&lt;/h6&gt;

&lt;p&gt;JSON原本是JavaScript 对象表示法（JavaScript Object Notation），后来慢慢在被大家所接受普及开来成为一种协议数据的格式。它是存储和交换文本信息的语法，类似于 XML 但又比 XML 更小、更快，更易解析。&lt;/p&gt;

&lt;p&gt;JSON 本身是轻量级的文本数据交换格式由字符串组成，它独立于语言且具有自我描述性，这些特性导致它非常容易被人理解。与同是纯文本类型格式的XML相比较，JSON不需要结束标签，JSON更短，JSON解析和读写的速度更快，在JavaScript中能够使用内建的 JavaScript eval() 方法进行解析，JSON还可以使用数组，且不使用保留字（&amp;amp;，&amp;lt;，&amp;gt;，’，”）。&lt;/p&gt;

&lt;p&gt;我们来看看 JSON 的语法规则，JSON 数据的书写格式是：名称/值对。名称/值对包括字段名称（在双引号中），后面写一个冒号，然后是值：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&quot;firstName&quot; : &quot;John&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;JSON数据由逗号分隔，它的值可以是数字、字符串、真假逻辑值、数组、对象，我们来看看它们在文本中的具体格式：&lt;/p&gt;

&lt;p&gt;数字（整数或浮点数）&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
  &quot;number&quot; : 1,
  &quot;number2&quot; : 11.5
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;字符串（在双引号中）&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
  &quot;str1&quot; : &quot;1&quot;,
  &quot;str2&quot; : &quot;11&quot;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;逻辑值（true 或 false）&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
  &quot;logic1&quot; : true,
  &quot;logic2&quot; : false
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;数组（在方括号中）&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
   &quot;array1&quot; : [1,2,3],
   &quot;array2&quot; : [{&quot;str1&quot;,1},{&quot;str2&quot;,2},{33,44}]
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;对象（在花括号中）&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
  &quot;obj1&quot; : {1, &quot;str1&quot;, true},
  &quot;obj2&quot; : {&quot;str2&quot;, 2, false},
  &quot;obj3&quot; : null
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;其中对象在花括号中书写，其对象可以包含多个名称/值对：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{ &quot;firstName&quot;:&quot;John&quot; , &quot;lastName&quot;:&quot;Doe&quot; }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;数组在方括号中书写，数组可包含多个对象：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{
    &quot;employees&quot;: [
        { &quot;firstName&quot;:&quot;John&quot; , &quot;lastName&quot;:&quot;Doe&quot; },
        { &quot;firstName&quot;:&quot;Anna&quot; , &quot;lastName&quot;:&quot;Smith&quot; },
        { &quot;firstName&quot;:&quot;Peter&quot; , &quot;lastName&quot;:&quot;Jones&quot; }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;JSON 文件的文件类型通常是 “xxx.json” 用来扩展名用来说明是json格式的文本文件。在HTTP协议中还定义了Json格式的MINE类型以方便终端逻辑识别，JSON 文本的 MIME 类型是 “application/json” (MIME (Multipurpose Internet Mail Extensions) 是描述消息内容类型的因特网标准。其他MIME 消息包含文本、图像、音频、视频以及其他应用程序专用的数据。&lt;/p&gt;

&lt;p&gt;我们在平时的编程中 JSON 解析器也比较多，例如simpleJson，MiniJson，DataContractJsonSerializer，JArray，JObject等等，都是非常通用高效的插件，也可以自己’造轮子‘做一个JSON解析器，做时也要多考虑下效率和性能方面的问题。&lt;/p&gt;

&lt;h6 id=&quot;自定义二进制流协议格式&quot;&gt;自定义二进制流协议格式&lt;/h6&gt;

&lt;p&gt;大部分的网络协议都具有一定的通用性，JSON是最典型的案例，其他的包括 XML，MessagePack，Protobuf都是相对通用的，但我们所要说的自定义二进制流协议则不是，理论上说它完全不通用，其原因是它被设计出来就不需要顾及通用性。&lt;/p&gt;

&lt;p&gt;我们在存储一串数据的时候，无论这串数据里包含了哪些数据以及哪些数据类型，当我们拿着这串数据解析的时候我们应该首先知道数据如何解析，这是定义协议格式的目标。简单说就是，当我们收到一串数据的时候，我们用什么样的规则知道这串数据里的内容的，这就是协议规则的目标。JSON就制定了这么一个规则，这个规则以字符串 KEY-VALUE 简单配对的形式，以及一些辅助的符号‘{’,’}’,’[’,’]’组合而成，这个规则比较通用且易于理解，这使得任何人拿到JSON数据都能一眼知道里面有什么数据。&lt;/p&gt;

&lt;p&gt;自定义二进制协议格式则不具有通用性，并不是任何人拿到数据都能知道里面装的是什么的，有且只有两端协定的双方才知道该如何解析收到的数据，对于破解自定义二进制流的内容也只有靠猜因为协议格式只有制定时的双方才知道(虽然猜的难度也不是很大，很多外挂都靠经验猜测数据内容)。&lt;/p&gt;

&lt;p&gt;一个自定义二进制流协议格式，分成三部分：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;数据大小|协议编号|具体数据
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;用代码结构可以表示为:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;class Mssage
{
  uint Size;
  uint CommandID;
  byte[] Data;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;数据大小、协议编号、具体数据，这三者构成了一个完整的协议内容，当然很多时候command id 可以放入具体的数据中去。&lt;/p&gt;

&lt;p&gt;现在假设我们客户端有这样一个数据结构需要传输到服务端去：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;class TestMsg
{
  int test1;
  float test2;
  bool test3;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;服务端拿到数据时，其实是完全不知道当前拿到的数据是什么，也不知道数据是否完整，有可能只拿到一半的数据，或者一部分的数据。因此首先我们要确定的是，我们收到的数据包它的完整的大小有多大，只有知道完整的包体大小才能确定我当前收到的数据在大小上是否完整，我们是要等待继续接受后面的数据，还是现在就可以进行解析操作了。&lt;/p&gt;

&lt;p&gt;为了确定包的完整性，我们必须先向二进制流中读取4个byte，组合成一个无符号整数，整数总共32位，也就是说我们的数据包的大小最大可以为2的32次减1个byte，这个整数让我们知道了接下来数据的大小。例如我现在接收到了20个byte后，读取了前4个byte，组成一个整数后这个整数为24，说明后面16个byte是一个不完整的包体，我应该继续等待后续的数据到来。&lt;/p&gt;

&lt;p&gt;其次我们要确定的是收到的数据包是属于哪个协议格式。于是我们再读取4个byte大小的数据，组成一个无符号整数CommandID，用来确定协议号。假如这个无符号整数的协议号为1002，就代表是接下来的数据是编号为1002的协议的数据格式。假设我们上面这个TestMsg类就是协议号1002的数据体，那么接下来连着这个协议号的所有数据直到包体大小的末尾都是这个TestMsg的数据，我们可以提取后解析为该类实例。&lt;/p&gt;

&lt;p&gt;在解析这个具体数据的时候，我们需要根据生成这个数据的顺序来解析，写入数据的顺序和读取数据的顺序是一致的。假设在生成这个二进制流数据时，我们的顺序是，先推入test1变量，再推入test2变量，再推入test3变量。其中test1变量为4个byte的整数，test2变量为4个byte的浮点数，test3变量为1个byte的布尔值，于是就有了如下byte数组结构：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;xxxx|xxxx|x
&lt;/code&gt;&lt;/pre&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;这样一个形状的二进制流，每个‘x’为一个byte，前两次4个byte组成一个int和float数据，最后1个byte组成布尔数据，‘&lt;/td&gt;
      &lt;td&gt;’只是为了解释说明用的分隔符不存在于数据内，这个数据是由9个byte组成，其中前4个byte为test1，中间4个byte为test2，后面1个btye为test3。&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;在向网络传输的中整个数据包TestMsg的格式为如下：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;13|1002|test1|test2|test3
&lt;/code&gt;&lt;/pre&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;上述格式中13为接下来的数据包大小，1002位协议编号，test1&lt;/td&gt;
      &lt;td&gt;test2&lt;/td&gt;
      &lt;td&gt;test3为具体数据。我们在解析的时候也需要按照生成时的顺序来解析，先读取前4个byte组成一个整数赋值给test1，接着再读取4个byte组成一个浮点数赋值给test2，接着再读取1个byte赋值给test3，完成数据解析。&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;对于数组形式的数据则要在原来的基础上多增加一个长度标志，比如 int[]类型数据，在生成时先推入代表长度的无符号整数数据，再连续推入所有数组内容，在解析的时候做同样的反向操作，先读取4个byte的长度标志，再对连续读取N个具体数据，N为提取的长度。我们举例int[]为3个整数数组则二进制为如下效果：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;xxxx|xxxx|xxxx|xxxx
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;前4个byte为长度数据，接着3次4个byte为数组内的整数数据。&lt;/p&gt;

&lt;p&gt;自定义二进制流协议格式为最不通用的格式，但可以成为最节省流量的协议方式，因为每个数据都可以用最小的方式进行定义，比如协议号不需要4个byte，2个byte大小代表2的16次-1也就是65535就够用了，长度有可能也不需要4个byte，只要2个甚至1个byte就够用了，有些数据不需要4个byte组成int整数，只需要2个byte数组ushort就够用了，甚至有些可以组合起来使用，比如协议结构中有4个bool，可以拼成一个byte来传递，这些都可以完全由我们来控制包体的大小不受到任何规则的限制，这也是自定义二进制协议格式最吸引人的地方。&lt;/p&gt;

&lt;p&gt;自定义二进制流协议格式最大的缺点是不通用和难更新，当我们需要更换一个协议格式的时候，旧的协议格式就无法解析了，特别是当新的协议解析旧的协议时就会报错。不过我们也可以做些补救这种问题的措施，为了能让旧的协议格式还能继续使用，我们在每个数据头部都加入一个2个byte的整数代表版本号，由版本号来决定该读取哪个版本的协议，这样旧的协议也照样可以兼容新的协议，只是处理起来的时候需要注意些初始化问题，那些旧协议没有的而新协议有的数据则要尽可能的初始化成默认值以免造成逻辑报错。&lt;/p&gt;

&lt;h6 id=&quot;messagepack&quot;&gt;MessagePack&lt;/h6&gt;

&lt;p&gt;MessagePack 是一个介乎于JSON和自定义二进制流之间的协议格式，他的理念是 ‘It’s like JSON. but fast and small.’ 。&lt;/p&gt;

&lt;p&gt;与JSON相同的是MessagePack也有采用Key-Value形式的Map映射类型，不同的是MessagePack用byte形式存储data部分的数据，包括整数、浮点数、布尔值等，并且在Map映射类型外加入了更多独立类型（非KEY-VALUE形式）的数据类型，其中也包括了自定义二进制流的数据类型。&lt;/p&gt;

&lt;p&gt;Map映射类型在MessagePack中也是比较常用的数据类型，它是比较通用的存储形式类型，也因为通用性被很多程序员所喜爱。在使用过JSON的程序员知道JSON易懂且易用，MessagePack使用起来能和JSON用起来一样，并且数据大小比JSON小，解析速度又比JSON快，这也是作者所说的 “It’s like JSON. but fast and small.”。&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;非Map类型的数据部分其实和自定义二进制流的存储方式差不多，只是把自定义二进制数据流中的‘数据大小&lt;/td&gt;
      &lt;td&gt;数据’的形式改为了‘类型&lt;/td&gt;
      &lt;td&gt;数据’，比如我们存储一个4个byte的32位的整数：&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;pre&gt;&lt;code&gt;+--------+--------+--------+--------+--------+
|  0xd2  |ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|ZZZZZZZZ|
+--------+--------+--------+--------+--------+
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;第一个byte的值0xd2代表32位整数类型，它表示后面4个byte组合起来是整数类型的数据。再比如32位的浮点数的存储格式：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;+--------+--------+--------+--------+--------+
|  0xca  |XXXXXXXX|XXXXXXXX|XXXXXXXX|XXXXXXXX|
+--------+--------+--------+--------+--------+
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;第一个byte的值0xca代表32位浮点数类型，它表示后面4个byte组合起来是浮点数类型的数据。以此类推，nil，bool，8位无符号整数，16位无符号整数，32位无符号整数，64位无符号整数，8位有符号整数，16位有符号整数，32位有符号整数，64位有符号整数等，以及32位浮点数，64位浮点数，都用这种类似的方式表示。&lt;/p&gt;

&lt;p&gt;其实用MessagePack并不是冲着这些单独的数据类型去的，因为这些单独的数据类型完全可以用自定义二进制流代替，我们关心的是它的Map类型数据的格式定义。我们先来看看，MessagePack的Map类型的存储机制和Json有什么区别，它为什么就比JSON快，为什么就比JSON小，它是如何存储和解析的。&lt;/p&gt;

&lt;p&gt;在Map之前我们看看数组类型的格式：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;+--------+--------+--------+~~~~~~~~~~~~~~~~~+
|  0xdc  |YYYYYYYY|YYYYYYYY|    N objects    |
+--------+--------+--------+~~~~~~~~~~~~~~~~~+
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;第一个byte的值0xdc代表是个总共可以存储16位长度的数组，也就是它的最大存放个数为2的16次-1个元素的数组，后面2个byte组合起来成为一个无符号的整数代表后面有多少个元素，接着后面N就是相同类型的元素的数据。&lt;/p&gt;

&lt;p&gt;假设说这N个元素是32位整数类型的数据，那么上述的数组类型具体格式就是如下：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;+--------+--------+--------+~~~~~~~~~~~~~~~~~+
|  0xdc  |00000000|00000011|  0xd2|00001001|0xd2|00001101|...(3 objects)
+--------+--------+--------+~~~~~~~~~~~~~~~~~+
&lt;/code&gt;&lt;/pre&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;我们看这个数组中指定了数组类型，0xdc就代表数组，以及后面2个byte拼起来表示数组元素的个数为11，接下来的数据就是单个元素的数据，即有11个整数数据组成的数组，每个数据都以‘类型&lt;/td&gt;
      &lt;td&gt;数据’格式的存储。其实Map类型就是数组类型的变种，我们在数组类型基础上每个元素，多加了个KEY字符串就成了Map类型的数据格式，我们来看下Map的具体格式：&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;pre&gt;&lt;code&gt;+--------+--------+--------+~~~~~~~~~~~~~~~~~+
|  0xde  |YYYYYYYY|YYYYYYYY|   N*2 objects   |
+--------+--------+--------+~~~~~~~~~~~~~~~~~+
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;第一个byte的值0xde代表的是最大个数为16位(即2的16次减1个)的map类型数据，接着2个byte组合起来表示有多少个元素，最后部分N乘2个元素为数据元素，以每2个元素为一个Key-Value组合，第一元素一定是字符串Key，第二个元素为任意的单独数据类型。&lt;/p&gt;

&lt;p&gt;我们用官方的例子来分析下，例如一个JSON类型的数据为：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;{&quot;compact&quot;:true, &quot;schema&quot;:0}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这个数据在MessagePack中是以Map类型数据存在的其格式为：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;82|A7|'c'|'o'|'m'|'p'|'a'|'c'|'t'|C3|A6|'s'|'c'|'h'|'e'|'m'|'a'|00|
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;数据中头部的‘82’这个数据，前半个byte的值即8代表是个最多拥有15个元素的map类型数据，后半个byte的值即2，代表总共有2个元素。接着第二个数据‘A7’，‘A’为前半个byte的值代表接下去是个31个字符以内的字符串，后半个byte值为7代表这个字符串拥有7个字符。接着7个元素都是字符元素及Key位置的字符串。接着‘C3’是Key-Value的Value类型数据部分，这个Value是一个bool型的ture值。接着‘A6’开始为第二个Key-Value数据组合，其中A为前半个byte代表是接下去是个31个元素以内的字符串，后半个byte为6代表这个字符串有6个长度大小。接着6个元素都是字符作为Key数据。最后的‘00’，前面0为前半个byte，表示类型为7位以内的整数，接着的0位后半个byte，代表数据为0。这样整个数据分析下来，MessagePack数据与Json的{“compact”:true, “schema”:0}数据对应上。&lt;/p&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;MessagePack整个Map就是以这种“类型&lt;/td&gt;
      &lt;td&gt;数据”或者”类型&lt;/td&gt;
      &lt;td&gt;大小&lt;/td&gt;
      &lt;td&gt;数据”的方式存储。由于存储的方式是顺序，所以在解析的时候不需要排序，不需要解析符号和类型，数据的类型直接可以用byte来表示，能用byte存储绝不用字符串形式存储，如能减少byte使用个数的尽量减少byte的使用个数，如能合并的尽量合并为一个byte。因此MessagePack对于JSON来说，减少了大量的解析，同时也减少了大量的数据占用空间，使得MessagePack能比起JSON来更快并且更小，就像它自己所说的那样 ‘It’s like JSON. but fast and small.’。&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h3 id=&quot;protocol-buffer&quot;&gt;Protocol Buffer&lt;/h3&gt;

&lt;p&gt;虽然Proto3在Proto2之上又做了更多的改进，但我们这里仍以Proto2为基准来讲解Protocal buffer的内在机制。MessagePack在JSON之上做了很多数据空间和序列化以及反序列化上的优化，其实可以看做是把JSON和自定义二进制的混合的做法，既吸收了JSON这种Key-Value(键值对)简单易懂通用性的优点，又吸收了自定义二进制流格式序列化与反序列化性能高和存储空间小的特点。不过话说回来，MessagePack的Map形式数据存储格式毕竟是Key-Value形式的，其Key值仍然使用了字符串，还是逃脱不了字符串string占用太多存储空间的弊端。&lt;/p&gt;

&lt;p&gt;Google Protocol Buffer 的出现就弥补了MessagePack的这个缺点，但是Google Protocol Buffer也有自身不可忽视的缺点，我们来看究竟Google Protocol Buffer是怎么的一种数据协议。&lt;/p&gt;

&lt;p&gt;Google Protocol Buffer( 简称 Protobuf) 是 Google 公司内部的混合语言数据标准，它们用于 RPC 系统和持续数据存储系统。Protobuf 是一种轻便高效的结构化数据存储格式，可以用于结构化数据串行化，或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域，是语言无关、平台无关、可扩展的序列化结构数据格式。常有人推崇说 Protocol Buffer 比JSON、MessagePack要好，那么它究竟好在哪里呢？我们就来分析下。&lt;/p&gt;

&lt;p&gt;我们选择数据协议的目的主要关注的点是，它是否能更简单易上手，序列化与反序列化数据性能是否高效，存储空间占用是否更小，更改协议后的兼容性是否能更强。对于这些特点，Protocol Buffer 是否能都做到，下面我们就来的对它剖析一番。&lt;/p&gt;

&lt;h6 id=&quot;protocol-buffer消息定义&quot;&gt;Protocol Buffer消息定义&lt;/h6&gt;

&lt;p&gt;Protobuf 的消息定义需要创建一个文件然后把消息结构写进入，然后再通过Protobuf生成工具将定义好的消息文件生成为指定语言的程序文件，我们在编程时可以通过调用这些生成的程序去序列化和反序列化Protobuf。&lt;/p&gt;

&lt;p&gt;我们先来创建一个扩展名为.proto的文件，假设文件名为 MyMessage.proto，并将以下内容存入该文件中。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;message LoginReqMessage {
  required int64 acct_id = 1;
  required string passwd = 2;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述消息定义是一个简单的登陆消息定义，我们来说明下里面的结构。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;message是消息定义的关键字，等同于C#中的struct/class。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;LoginReqMessage为消息的名字，等同于结构体名或类名。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;required前缀表示该字段为必要字段。即在序列化和反序列化之前该字段必须已经被赋值。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;与required相似的功能还存在另外两个类似的关键字，optional和repeated。optional表示该字段为可选字段，即在序列化和反序列化前可以不进行赋值。相比于optional，repeated主要用于表示数组字段，它代表数组。(required和optional字段已经在Protobuf3中取消，所有未定义类型都是optional)&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;int64和string分别表示64位长整型和字符串型的消息字段。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;其实在Protobuf中存在一张类型对照表，这张对照表用于Protobuf中的数据类型与其他编程语言(C#/Java/C++)中所用类型的一一对应。该对照表中还将给出在不同的数据场景下，使用哪种数据类型更为高效。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;acct_id 和 passwd 分别表示消息字段名，等同于C#中的域变量名。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;标签数字 1 和 2 表示不同字段序列化后在二进制数据中的布局位置。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;LoginReqMessage结构的实例数据在序列化时，acct_id先被推入数据流中再是passwd，passwd 字段在序列化后的数据一定位于 acct_id 之后。我们需要注意数字标签的值代表二进制流中的位置，该值在同一message中不能重复。&lt;/p&gt;

&lt;p&gt;另外Protocol Buffer有个优化规则我们需要在定制消息的注意，它在标签值为 1 到 15 的字段上序列化时会对其进行优化，即标签值和类型信息仅占有一个byte，标签范围在 16 到 2047 的则占有两个byte，而Protocol Buffer可以支持的字段数量则为2的29次方减1个即536870911个数据变量。鉴于此优化规则，我们在设计消息结构时，可以尽可能考虑让repeated类型的字段标签位于1到15之间，这样便可以有效的节省序列化后的字节大小。&lt;/p&gt;

&lt;h6 id=&quot;多层嵌套protocol-buffer&quot;&gt;多层嵌套Protocol Buffer&lt;/h6&gt;

&lt;p&gt;除了定义单个消息，我们也可以在同一个.proto文件中定义多个message，这样便可以实现多层嵌套消息的定义，我们来看看具体案例：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;    message Person {
      required string name = 1;
      required int32 id = 2;
      optional string email = 3;

      enum PhoneType {
        MOBILE = 0;
        HOME = 1;
        WORK = 2;
      }

      message PhoneNumber {
        required string number = 1;
        optional PhoneType type = 2 [default = HOME];
      }

      repeated PhoneNumber phones = 4;
      repeated float weight_recent_months = 100 [packed = true];
    }

    message AddressBook {
      repeated Person people = 1;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们在proto文件中定义了三个消息结构和一个枚举结构，其中AddressBook消息的定义中包含了Person消息类型作为其字段变量，Person又包含了PhoneNumber消息类型作为字段变量，这与我们平时编程的数据结构嵌套方式很相似。除了这些数据结构被集中定义在一个proto文件中以外，它们也可以被分开来定义在各自.proto文件中。&lt;/p&gt;

&lt;p&gt;由于Protocol Buffer提供了另外一个关键字，‘import’关键字，它相当于 C++ 的Include，这样我们在编写Proto结构时便可以将很多通用的message定义在同一个.proto文件中，而每个模块功能的消息体定义可以自己管理分别定义在自己独立的proto文件中或者以其他更清晰的方式分开定义，最后我们可以通过import关键字以动态导入的方式将需要的结构体文件导入进来，如：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;    message Person {
      required string name = 1;
      required int32 id = 2;
      optional string email = 3;

      enum PhoneType {
        MOBILE = 0;
        HOME = 1;
        WORK = 2;
      }

      message PhoneNumber {
        required string number = 1;
        optional PhoneType type = 2 [default = HOME];
      }

      repeated PhoneNumber phones = 4;
      repeated float weight_recent_months = 100 [packed = true];
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;例如上述我们先写好一个常用的数据结构体消息，将它放入Person.proto文件中。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;    import &quot;myproject/Person.proto&quot;

    message AddressBook {
      repeated Person people = 1;
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;然后再我们写我们自己模块里的结构消息，定义好自己的需要的数据类型字段，再将Person这个proto文件里的所有消息结构都导入进来。通过‘import’我们可以轻松而且清晰的表达项目中的数据分块与分层。&lt;/p&gt;

&lt;h6 id=&quot;限定符-requiredoptionalrepeated-的规则&quot;&gt;限定符 required、optional、repeated 的规则&lt;/h6&gt;

&lt;p&gt;在Protobuf2中有这3个限定符，并且在每个消息中必须至少有一个required类型的字段，保证数据中一定有至少一个数据。&lt;/p&gt;

&lt;p&gt;required限定符表示该字段为必要字段。即在序列化和反序列化之前数据中该字段必须已经被赋值。而每个消息中可以包含0个或多个optional类型的字段。optional限定符表示该字段为可选字段，即在序列化和反序列化前可以不进行赋值，如果没有赋值则表示该数据为空。repeated限定符则表示的字段可以包含0个或多个重复的数据，即数组类型符号。注意 repeated 代表是重复的数据，等价于我们常使用的数组和列表，并且可以不赋值，如果不赋值则表示0个数组数据。&lt;/p&gt;

&lt;h5 id=&quot;protocol-buffer-原理-序列化和反序列化&quot;&gt;Protocol Buffer 原理-序列化和反序列化&lt;/h5&gt;

&lt;p&gt;Protocol Buffer 是怎么识别和存储数据的是理解它序列化和反序列的关键。JSON 和 MessagePack 都使用了字符串Key键值作为映射到程序变量的连接桥梁，用变量的字符串名字去查看对应的Key键值是否存在，这样避免不了因Key键值字符串太多的空间浪费。&lt;/p&gt;

&lt;p&gt;Protocol Buffer 则用数字编号来作为Key键值与变量映射的连接桥梁，每个变量都必须有个不重复的标签号(即数字编号)，用Protobuf结构中的变量字段后面跟着的数字编号来映射到数据中的数字编号，进而读取数据。Protocol Buffer为每个结构变量都定义了一个标签号(即数字编号)，这个数字编号就代表了程序变量与指定编号数据的映射关系。&lt;/p&gt;

&lt;p&gt;其实有了这个规则还不够，因为程序在读取的时候，是不知道某个变量到底对应哪个标签号的，比如上面的Person的 name 变量，在程序里的 name 变量并不知道到底自己该读取哪个编号的数据的，除非在程序里写死说 name 变量就读取编号为1的数据。Protobuf 就是使用了这种简单粗暴的方法，‘在程序里写死’的这种方式让事情变得更简单。‘在程序里写死’这种粗暴的方式最讲究周边工具了，因为‘在程序里写死’本身是件危险的事，然而如果这个程序是我们通过工具生成的话就会好很多，它相当于我们使用了一些规则并配置了一些数据让生成的程序符合我们的预期并可以随时通过配置来改变它们。Protobuf 的周边工具就为很多种语言定制了生成序列化和反序列化程序代码的工具，我们可以视 .proto 文件为配置文件，Protobuf 根据 .proto 配置文件来生成序列化程序文件。我们只需要通过提供.proto文件就能生成不同语言的程序代码，其代码中的变量的读取与存储编号就是通过周边工具的方式‘写死’在程序中，我们所说的这些代码都是通过周边工具生成的，而我们只需要关心.proto文件中的结构就可以了。&lt;/p&gt;

&lt;p&gt;简要总结下，当 Protobuf 生成的用于序列化和反序列化的代码在读数据的时候，通过 .proto 文件中的内容把变量名与数字编号‘写死’绑定在代码中，一旦读取到某个编号的数据时，就把该编号的数据解析给指定变量，例如前面我们提过的Protobuf数据结构案例中，当程序读取到编号为1的数据时，就会把数据写入 name 变量中去，当 name 变量需要写入到数据文件时都是先将编号1这个数字写入进去，而编写这些操作的代码由Protobuf工具完成我们无需担心。&lt;/p&gt;

&lt;h6 id=&quot;我们来看个具体的例子我们使用上面提到的-addressbook-数据结构来序列化一个-protocol-buffer-数据&quot;&gt;我们来看个具体的例子，我们使用上面提到的 AddressBook 数据结构来序列化一个 Protocol Buffer 数据。&lt;/h6&gt;

&lt;p&gt;我们将数据序列化代码为：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;AddressBook address_book;
Person person = address_book.add_people();
person.set_id(1);
person.set_name(&quot;Jack&quot;);
person.set_email(&quot;Jack@qq.com&quot;);
Person.PhoneNumber phone_number = person-&amp;gt;add_phones();
phone_number.set_number(&quot;123456&quot;);
phone_number.set_type(Person.HOME);
phone_number = person.add_phones();
phone_number.set_number(&quot;234567&quot;);
phone_number.set_type(Person.MOBILE);

person-&amp;gt;add_weight_recent_months(50);
person-&amp;gt;add_weight_recent_months(52);
person-&amp;gt;add_weight_recent_months(54);

//将数据写入数据流中
address_book-&amp;gt;WriteStream(stream);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述代码生成出来的二进制数据流如下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;0a    // (1 &amp;lt;&amp;lt; 3) + 2 = 0a，1为people的标签号,2为嵌入结构对应的repeated类型号
3c    // 0x3c = 60，表示接下来60个字节为Person的数据

// 下面进入到 repeated Person 数组的数据结构
0a    // (1 &amp;lt;&amp;lt; 3) + 2 = 0a，Person的第一个字段name的标签号为1，2为string(字符串)对应的类型号
04    // name字段的字符串长度为4
4a 61 63 6b    // &quot;Jack&quot; 的ascii编码

10    // (2 &amp;lt;&amp;lt; 3) + 0 = 10，字段id的标签号为2，0为int32对应的类型号
01    // id的整型数据为1

1a    // (3 &amp;lt;&amp;lt; 3) + 2 = 1a，字段email的标签号为3，2为string对应的类型号
0b    // 0x0b = 11 email字段的字符串长度为11
4a 61 63 6b 40 71 71 2e 63 6f 6d        // &quot;Jack@qq.com&quot;

    //第1个PhoneNumber，嵌套message
    22    // (4 &amp;lt;&amp;lt; 3) + 2 = 22，phones字段，标签号为4，2为嵌套结构对应的类型号
    0a    // 0a = 10，接下来10个字节为PhoneNumber的数据
    0a    // (1 &amp;lt;&amp;lt; 3) + 2 =  0a, PhoneNumber的number，标签号为1，2为string对应的类型号
    06    // number字段的字符串长度为6
    31 32 33 34 35 36    // &quot;123456&quot;
    10   // (2 &amp;lt;&amp;lt; 3) + 0 = 10，PhoneType type字段，0为enum对应的类型号
    01   // HOME，enum被视为整数

    // 第2个PhoneNumber，嵌套message
    22 0a 0a 06 32 33 34 35 36 37 10 00  //信息解读同上，最后的00为MOBILE

a2 06   // 1010 0010 0000 0110 varint方式，weight_recent_months的key
        //  010 0010  000 0110 → 000 0110 0100 010 little-endian存储
        // (100 &amp;lt;&amp;lt; 3) + 2 = a2 06，100为weight_recent_months的标签号
        //  2为 packed repeated field的类型号
0c    // 0c = 12，后面12个字节为float的数据，每4个字节一个数据
00 00 48 42 // float 50
00 00 50 42 // float 52
00 00 58 42 // float 54
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述二进制数据是一个紧凑的byte数组，我们在剖析时将它拆解开来，这样会更加清晰。第一行的0a由 (1 « 3) + 2 = 0a 生成，其中1为people的标签号,2为嵌入结构对应的repeated类型号。紧跟其后的第二行中3c 表示 0x3c = 60，代表接下来有60个字节大小的数据是 Person 的数据。于是接下来下面进入到 repeated Person 数组的数据结构内容。其遇到的第一个数据 0a 由 (1 « 3) + 2 = 0a 生成而来，1代表Person的第一个字段name的标签号为1，2代表string(字符串)对应的类型号为2。后面的 04 表示为name字段的字符串长度为4，其后紧跟着就是 name 字段字符串的具体数据了，4a 61 63 6b 即为 “Jack” 的ascii编码。后面的数据 10 由 (2 « 3) + 0 = 10 生成而来，其中2代表字段id的标签号为2，0代表int32对应的类型号为2。接着就是int32的数据内容，即 01 表示数据结构中字段变量id的整型数据为1。接下来由是一串email字符串内容，先标志标签号+类型号，再紧跟一个数据大小，再是字符串的具体数据。&lt;/p&gt;

&lt;p&gt;再后面就是 PhoneNumber 的数组类型，先是22 表示标签号与类型号， (4 « 3) + 2 = 22，4代表phones字段标签号为4，2代表嵌套结构对应的类型号为2。接着 0a = 10，表示接下来10个字节为PhoneNumber的数据。接着 0a 由 (1 « 3) + 2 =  0a 生成而来，1代表PhoneNumber的number标签号为1，2代表string对应的类型号为2。接着是字符串长度 06 表示number字段的字符串长度为6。接着 31 32 33 34 35 36 为字符串 “123456” 的ascii编码。后面是一个 enum 类型，10 由 (2 « 3) + 0 = 10 生成而来，2代表PhoneType type字段的标签号，0为enum对应的类型号。接着是enum的具体数据，01 表示枚举值HOME，所有的enum被视为整数。第二个PhoneNumber也是同样的数据格式。&lt;/p&gt;

&lt;p&gt;最后这些数据为浮点数数组weight_recent_months字段，a2 06 由(100 « 3) + 2 = a2 06 生成而来，100为weight_recent_months的标签号，2为 packed repeated field的类型号。后面的 0c 是 0c = 12的意思，表示后面的12个字节为float的数组数据，每4个字节一个数据。于是接下来三个数据，每个数据4个字节都直接表示浮点数，00 00 48 42 表示float 50，00 00 50 42 表示 float 52，00 00 58 42 表示 float 54。&lt;/p&gt;

&lt;p&gt;这个例子来自《Protocol Buffers：阅读一个二进制文件》整个二进制数据分析下来都是遵循了简单的规则，即，标签号 + 类型号，其头部的标识和数据大小标识，作为可选标识放入具体数据，即如下格式：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;标签号 + 类型号|数据大小|具体数据
&lt;/code&gt;&lt;/pre&gt;

&lt;table&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;如果具体数据中再嵌套不同种类的数据，也同样遵循 ‘标签号 + 类型号&lt;/td&gt;
      &lt;td&gt;数据大小&lt;/td&gt;
      &lt;td&gt;具体数据’ 这样的规则。&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;h6 id=&quot;前面我们剖析了下序列化的数据内容接下来我们看看反序列化的过程&quot;&gt;前面我们剖析了下序列化的数据内容，接下来我们看看反序列化的过程。&lt;/h6&gt;

&lt;p&gt;二进制数据流中反序列化为程序对象数据过程中，标签号与变量的映射关系是由程序‘写死’在代码中的，我们仍然拿上面的protobuf结构来举例，我们重点看看其中 Person 结构的反序列过程：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;public void MergeFrom(pb::CodedInputStream input) {
  uint tag;
  while ((tag = input.ReadTag()) != 0) {
    switch(tag) {
      default:
        _unknownFields = pb::UnknownFieldSet.MergeFieldFrom(_unknownFields, input);
        break;
      case 1: {
        name = input.ReadString();
        break;
      }
      case 2: {
        id = input.ReadInt32();
        break;
      }
      case 3: {
        email = input.ReadString();
        break;
      }
      case 4: {
        phones_.AddEntriesFrom(input, _repeated_phones_codec);
        break;
      }
      case 100: {
        
        weight_recent_months_.AddEntriesFrom(input, _repeated_weight_recent_months_codec);
        break;
      }
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述Protocol Buffer生成的代码我们了解到，所有的对象变量都通过.proto文件中的标签号来识别数据是否与该变量有映射关系，当拿到具体数据时，先用标签号来判定映射到的是哪个变量名，再针对该变量的类型读取数据并赋值。&lt;/p&gt;

&lt;h6 id=&quot;protocol-buffer-更改数据结构后的兼容问题&quot;&gt;Protocol Buffer 更改数据结构后的兼容问题&lt;/h6&gt;

&lt;p&gt;在实际的开发中会存在这样一种应用场景，即消息格式因为某些需求的变化而不得不进行必要的修改，但是有些仍然使用原有消息格式的应用程序由于各种原因玩家暂时不能愿意升级客户端程序，这便要求我们在更新消息格式时要遵守一定的规则，从而可以保证基于新老消息格式的新老客户端程序中都能够顺利运行。我们应该注意的规则如下：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;不要修改已经存在字段的标签号，即.proto文件中结构消息变量字段后面的编号数字不应该被轻易改变，这保证旧数据协议能够继续从数据中读取指定标签号的正确数据。如果我们更改了标签号，则新老数据不能在新旧客户端中被兼容。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;任何新添加的字段必须使用optional和repeated限定符，这保证在旧数据无法加入新数据字段的情况下，新的协议数据还能够在旧数据协议之下顺利解析。如果我们不使用optional或repeated标签则无法保证新老程序在互相传递消息时的消息兼容性。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;在原有的消息中，不能移除已经存在的required字段，虽然optional和repeated类型的字段可以被移除，但是他们之前使用的标签号必须被保留，不能被新的字段重用。因为旧协议在执行时还是会在旧的标签号中加入自己的数据，新协议如果使用了旧的标签号，就会导致新旧协议数据解析错误的问题。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;int32、uint32、int64、uint64和bool等类型之间是兼容的，sint32和sint64是兼容的，string和bytes是兼容的，fixed32和sfixed32，以及fixed64和sfixed64之间是兼容的，这意味着如果想修改原有字段的类型时，为了保证兼容性，只能将其修改为与其原有类型兼容的类型，否则就将打破新老消息格式的兼容性。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h6 id=&quot;protocol-buffer-的优点&quot;&gt;Protocol Buffer 的优点&lt;/h6&gt;

&lt;p&gt;Protobuf 全程使用二进制流形式，用整数代替了Key键值来映射变量，比 XML、Json、MessagePack它们更小也更快。&lt;/p&gt;

&lt;p&gt;我们可以定义随意的创建自己的.proto文件在里面编写自己的数据结构，然后使用Protobuf代码生成工具生成的protobuf代码，用于读写我们需要序列化和反序列化的protobuf数据结构。我们甚至可以在无需重新部署程序的情况下更新我们的数据结构，只需使用 Protobuf 对数据结构进行一次重新描述，就可利用各种不同语言或从各种不同数据流中对我们的protobuf数据轻松读写。&lt;/p&gt;

&lt;p&gt;使用 Protobuf 也无需学习复杂的文档对象模型，因为Protobuf 的编程模式比较友好简单易学，同时它拥有良好的文档和示例，对于喜欢简单易用的工具的人来说，Protobuf 比其他的技术更加有吸引力。&lt;/p&gt;

&lt;p&gt;Protobuf 语义也更清晰，无需类似 XML，JSON 解析器的东西，简化了解析的操作，减少了解析的消耗。&lt;/p&gt;

&lt;p&gt;Protobuf 数据使用二进制形式，把原来在JSON,XML里用字符串存储的数字换成用byte存储，大量减少了浪费的存储空间。与MessagePack相比，Protobuf减少了Key的存储空间，让原本用字符串来表达Key的方式换成了用整数表达方式，不但减少了存储空间也加快了反序列化的速度。&lt;/p&gt;

&lt;h6 id=&quot;protocol-buffer-的不足&quot;&gt;Protocol Buffer 的不足&lt;/h6&gt;

&lt;p&gt;Protbuf 与 XML、Json类型的数据格式相比也有不足之处，它功能简单无法用来表示复杂的数据概念。XML和Json已经成为多种行业标准的编写工具，明文的表达方式让数据格式显得更加友好，Protobuf 只是运用在数据传输与存储上，在各领域的通用性上还差很多。由于 XML和Json 具有某种程度上的自解释性，它可以被人直接读取和编辑，Protobuf 却不行，它以二进制的方式存储，除非你有 .proto 定义，否则你没法直接读出 Protobuf 的任何内容。&lt;/p&gt;

&lt;p&gt;参考文献：&lt;/p&gt;

&lt;p&gt;《Protocol Buffers：阅读一个二进制文件》&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第六章，网络层(四) - 封装HTTP</title>
   <link href="http://www.luzexi.com/2019/06/02/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-%E7%BD%91%E7%BB%9C%E5%B1%824"/>
   <updated>2019-06-02T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/06/02/Unity3D高级编程之进阶主程-网络层4</id>
   <content type="html">&lt;h3 id=&quot;http协议原理&quot;&gt;HTTP协议原理&lt;/h3&gt;

&lt;p&gt;HTTP俗称短连接，由于其平均连接的时间较短，不受前端所控制，所以在游戏圈内通常被冠以‘短’字开头。&lt;/p&gt;

&lt;p&gt;在Unity3D中编写短连接可以用.NET库来编写，也可以用Unity3D的内置API的WWW来写，差别不是很大。WWW对.Net做了封装，其功能已经完全够用在游戏开发上了，即使有情况不够用再用.NET补充也是相对容易的事。然而作者在编写书本时，WWW已经在2018及以后的版本中都被废弃了，取代它的是UnityWebRequest，Unity3D对网络请求重新做了封装。&lt;/p&gt;

&lt;p&gt;WWW与.NET两者的区别主要还是WWW把.NET库封装后再加了层协程的封装，这会使我们开发者在使用时更加的便捷，而.NET库则直接用了线程，开发者需要关注主线程与子线程的资源抢占情况。总的来说WWW经过封装后使用更佳便捷，使用.NET编写HTTP则需要我们自己关注更多细节。&lt;/p&gt;

&lt;h6 id=&quot;不管协程还是线程其实两边都需要用到缓存和队列这样的缓存机制倘若没有缓存机制当网络请求量放大时就会出现混乱以及数据丢失的情况因此缓存机制在网络层是不可或缺的&quot;&gt;不管协程还是线程，其实两边都需要用到缓存和队列这样的缓存机制，倘若没有缓存机制当网络请求量放大时就会出现混乱以及数据丢失的情况，因此缓存机制在网络层是不可或缺的。&lt;/h6&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;我们所说的HTTP，最形象的描述就是网页(Web)形式的请求与回调，它被运用最多的就是网页(Web)请求上。HTTP是一种 请求/响应式 的协议。也就是说，请求通常是由像浏览器这样的用户代理User-
Agent发起的，当用户接收到服务器的响应数据后，通过这些数据处理相应的逻辑再反应到画面上，其特点为每个请求都是一一对应的，一个请求有且最多只能得到一个响应。&lt;/p&gt;

&lt;p&gt;我们来看看浏览器是如何用HTTP工作的，当我们要展示一个网页内容时，浏览器首先向服务器发送一个HTTP请求并且带上参数，目的是从服务器获取页面的HTML内容，获得HTML美容后再解析其中的资源信息接着发送获取这些资源信息的请求，例如获取可执行脚本、CSS样式资源的请求，以及一些其它页面资源（如图片和视频等）。最终浏览器会将这些资源通过HTML语言规则的形式整合到一起，从而展示一个完整的网页。除了这些浏览器执行的脚本JavaScript也可以在之后的阶段不断发起更多的请求来获取更多信息和资源，不断的叠加、更新到当前的网页内容上。&lt;/p&gt;

&lt;p&gt;我们常常会在游戏项目中把这套 请求/响应式 协议搬到了游戏中运用，其最重要的原因是HTTP简单易用，程序员们上手的成本低，功能扩展难度也低，因此被广大互联网程序员所喜爱。&lt;/p&gt;

&lt;p&gt;HTTP是应用层上的协议，它需要底层的传输层协议是可靠的，因此 HTTP 依赖于面向连接的TCP协议进行消息传递。HTTP是应用层上的协议就是因为它本身并没有检测是否连接，数据是否传输准确，是否有数据到达等的机制，这些都依赖于传输层的协议TCP协议，由TCP协议来做这些传输层的事情，而HTTP只是在TCP之上制定了自己的规则。&lt;/p&gt;

&lt;h3 id=&quot;我们来看看-http-在tcp之上制定了哪些规则&quot;&gt;我们来看看 HTTP 在TCP之上制定了哪些规则：&lt;/h3&gt;

&lt;h6 id=&quot;http制定了自己的协议格式协议分为head和body两块&quot;&gt;HTTP制定了自己的协议格式，协议分为HEAD和BODY两块&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;    GET /root1/module?name1=value1&amp;amp;name2=value2 HTTP/1.1
    Host: localhost:8080
    Accept-Language: fr

    //这里 HEAD 和 BODY 用空行隔开

    body content
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;如上协议所示其中 GET 为向服务器请求的参数方式，HTTP有2种参数请求方式，分别是GET 和 POST。GET请求方式把参数值以 Key-Value 的键值对形式放在地址中传输给服务器，它的格式是&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    ?Key1=Value1&amp;amp;Key2=Value2&amp;amp;...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;以问号’?’开头作为Get参数格式的开始，接下去每个Key=Value之间用与’&amp;amp;‘符号作为分隔，所有的表达式都是以字符串形式存在。如上文中&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    GET /root1/module.php?name1=value1&amp;amp;name2=value2 HTTP/1.1
    Host: localhost:8080
    Accept-Language: fr
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;就是个很好例子，表达了访问的客户端要访问 Host地址为 localhost:8080 的服务器，采用GET形式传参，访问子地址为 /root1/module 并且参数为 ?name1=value1&amp;amp;name2=value2，采用HTTP1.1的协议逻辑，最后 Accept-Language: fr 客户端支持法语。&lt;/p&gt;

&lt;p&gt;POST则是把参数值放在协议数据包中，存放也同样以Key=Value的字符串形式存在，不一样的地方为POST可以使用二进制作为参数值并且存放在Body上，body content是主要存储请求内容的，POST内容都放在这里，而GET则必须以字符串形式明文显性的展示在地址上。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    POST /root1/module.php HTTP/1.1
    Host: localhost:8080

    name1=value1&amp;amp;name2=value2
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;如上述POST例子所展示的那样，其实两者的实质是一样的，都是以Key1=Value1&amp;amp;Key2=Value2的形式作为请求内容，并且请求的参数都会被写入请求包体中，只是Get必须暴露在地址上而已。&lt;/p&gt;

&lt;p&gt;服务器在收到客户端请求并处理相应逻辑后发送响应数据，其数据格式为：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    HTTP/1.1 200
    Date:Mon,31Dec200104:25:57GMT
    Server:Apache/1.3.14(Unix)
    Content-type:text/html
    Last-modified:Tue,17Apr200106:46:28GMT
    Content-length:xxx
    
    //这里HEADS 和 BODY用空行隔开
    body content
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;例子是服务器端响应客户端请求的处理后的应答数据，里面包含了HTTP的版本，Date日期，Server服务器类型，Content-type内容类型，Content-length内容长度，body content内容主体，其中200为响应后代表请求成功的错误码，其他常用的错误码可以简单理解为 404 找不到请求页，500 服务器程序报错，400 访问请求参数错误，403 被拒绝访问。&lt;/p&gt;

&lt;h6 id=&quot;http101120的简述&quot;&gt;HTTP1.0，1.1，2.0的简述&lt;/h6&gt;

&lt;p&gt;上文的例子中 HTTP/1.1为HTTP协议的版本号，我们现在常用的协议为1.1，也有少部分仍然在使用HTTP1.0。我们常用的HTTP1.1兼容了1.0，并且在1.0之上改进了诸多内容，比如同一个地址不同host，增加了cache特性，增加Chunked transfer-coding标志切割数据块等。而2.0由于和1.1差别比较大，它并不能兼容1.0和1.1，因此HTTP的世界就像被分成了两块，HTTP2.0被运用在HTTPS上，而HTTP1.1和1.0则运行在原有的HTTP上。&lt;/p&gt;

&lt;p&gt;腾讯高级工程师杨良聪在他的一篇文章中对HTTP不同版本的问题做了很好的解释。在HTTP1.0中，一个请求就独占一条TCP连接，要并行的获取多个资源就需要建立多条连接。HTTP1.1则引入了持久连接和管线化，允许在应答回来之前就按顺序的发送多个请求，但是服务器端按照请求的顺序发送应答。在同一个TCP连接上，及时后面的请求先处理完，也必须等待前面的应答发送完毕后，才能发送后面的应答，这就是HTTP1.1的队首阻塞问题。HTTP1.0，1.1都是用了文本形式的协议，也就是所有数据都是以字符串形式存在，这样做的导致协议传输和解析效率不高。不过对于这种文本形式的协议体，我们可以通过压缩来减少数据传输量，但对于协议头我们无法压缩，这使得对于那些Body内容比较少的应答数据来说，很可能协议头的传输成为了首要的性能瓶颈。&lt;/p&gt;

&lt;p&gt;HTTP2.0则引入了Sream概念，这使得一个TCP连接可以被多个Stream共享，每个Stream上都可以跑单独的请求与应答，从而实现了TCP连接的复用，即单个TCP连接上可以并行传输多个请求与应答数据。另外HTTP2.0不再使用文本协议而采用了二进制协议这使得协议更加紧凑，协议头也可以做到压缩后再传输，减少了数据传输带来的开销。不仅如此，HTTP2.0还可以支持Server端的主动Push，进一步减少了交互流程，以及支持流量控制，并引入了流的优先级和依赖关系，这使得我们能够对流量和资源进行较为细致的控制。&lt;/p&gt;

&lt;h6 id=&quot;http无状态连接&quot;&gt;HTTP无状态连接&lt;/h6&gt;

&lt;p&gt;HTTP的无状态是指对于事务处理没有记忆能力，前后两次的请求并没有任何相关性，可以是不同的连接，也可以是不同的客户端，服务器在处理HTTP请求时，只关注当下这个连接请求时的可获取到的数据，不会去关心也没有记忆去关联上一次请求的参数数据。&lt;/p&gt;

&lt;p&gt;HTTP的访问请求一般都会有软硬件做负载均衡来决定访问哪台物理服务器，进而两次同样地址的访问请求，有可能处理这两次请求的服务器是不同的。而无状态很好的匹配了这种近乎随机的访问方式，也就是说HTTP客户端可以任意选择一个部署在不同区域的服务器进行访问，得到的结果是相同的。&lt;/p&gt;

&lt;h6 id=&quot;http每次请求访问结束都有可能断开连接&quot;&gt;HTTP每次请求访问结束都有可能断开连接&lt;/h6&gt;

&lt;p&gt;在HTTP1.0和1.1中，HTTP是依据什么来断开连接的呢？答案是content-length。content-length为 heads 上的标记，表示 body 内容长度。带有content-length 标签的请求，其body内容长度是可知的，客户端在接收服务器回应过来的数据body内容时，就可以依据这个长度来接受数据。在接受完毕后这个请求就完毕了，客户端将主动调用close进入四次挥手断开连接。假如没有content-length 标记，那么body内容长度是不可知的，客户端会一直接受数据，直到服务端主动断开。&lt;/p&gt;

&lt;p&gt;HTTP1.1在这个断开规则之上又扩展了一种新规则，即增加了Transfer-encoding标记。如果Transfer-encoding为chunked，则表示body是流式输出，body会被分成多个块，每块的开始会标识出当前块的长度，此时body不需要通过content-length长度来指定了。如果HEADS上带有Transfer-encoding:chunked 就表示body被分成很多块，每块的长度也是可知的，当客户端根据长度接受完毕数据后再主动断开连接。假如说Transfer-encoding 和 content-length 这两个标记都没有，那么就只能一直接受数据直到服务器主动断开连接。&lt;/p&gt;

&lt;p&gt;那么可不可以使用HTTP协议又不断开连接的方式？还有HEAD里的keep-alive标识，keep-alive标识会让客户端与服务器的连接保持状态，直到服务器发现空闲时间结束而断开连接，在结束时间内我们仍然能发送数据。也在就是说，可以减少多次的与服务器3次握手建立连接的消耗，以及多次与服务器4次握手断开连接的消耗，提高了连接效率。&lt;/p&gt;

&lt;p&gt;另外一面在服务器端上，Nginx的 keepalive_timeout，和Apache的 KeepAliveTimeout 上都能设置 Keep-alive 的空闲时间大小，当httpd守护进程发送完一个响应后，理应马上主动关闭相应的TCP连接，但设置 keepalive_timeout后，httpd守护进程会说：”再等等吧，看看客户端还有没有请求过来”，这一等，便是 keepalive_timeout时间。如果守护进程在这个等待的时间里，一直没有收到客户端发过来HTTP请求，则关闭这个HTTP连接。不过也不一定说使用 keep-alive 标识能提高效率，有时也会反而降低了效率，比如经常会没有数据需要发送，导致长时间的Tcp连接保持导致系统资源无效占用，浪费系统资源，巨量的保持连接状态就会浪费大量的连接资源。&lt;/p&gt;

&lt;p&gt;倘若我们客户端使用keep-alive做连续发送，则需要在连续发送数据时使用同一个HTTP连接实例。并且在发送完毕后要记录空闲时间，以便再次发送时，可以判断是否继续使用该连接，因为通常服务器端主动断开连接后并没有被客户端及时的得知，所以自行判断是否有可能已经被服务器端断开连接为好。还有一个问题是，如果网络环境不好导致发送请求无法到达时，则要尽可能的自己记录和判断，哪些数据是需要重发的。这几个问题增加了HTTP作为keep-alive来保持连接的操作难度，将本来简单便捷的HTTP，变得使用更加困难，因此这也是keep-alive并不常使用在游戏项目中。&lt;/p&gt;

&lt;h6 id=&quot;在unity3d中的http封装&quot;&gt;在Unity3D中的HTTP封装&lt;/h6&gt;

&lt;p&gt;Unity3D的2018版本中将原本经常用的WWW类废弃了，取而代之的是UnityWebRequest。其实API改了但功能都是一样的，我们主要还是围绕HTTP的原理来写程序，也只有这样才能真正写出程序的精髓。&lt;/p&gt;

&lt;p&gt;UnityWebRequest里有几个接口对我们来说比较重要，一个是&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    Post(string uri, WWWForm postData)接口
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;用来创建一个带有地址和Post数据的UnityWebRequest实例，一个是&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    SendWebRequest()
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;用来开始发送请求和迭代请求的接口，另一个是&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    SetRequestHeader(string name, string value)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;调用它可以设置HTTP的标签头，其中Unity3D官方说下面这些HEAD标记在UnityWebRequest中并不支持：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    These headers cannot be set with custom values on any platform: accept-charset, access-control-request-headers, access-control-request-method, connection, date, dnt, expect, host, keep-alive, origin, referer, te, trailer, transfer-encoding, upgrade, via.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们来看看这些标签都是代表什么功能：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    accept-charset: 用于告诉服务器，客户机采用的编码格式

    access-control-request-headers: 在预检请求中，用于通知服务器在真正的请求中会采用哪些请求首部

    access-control-request-method: 在预检请求中，用于通知服务器在真正的请求中会采用哪种 HTTP 方法

    connection: 处理完这次请求后是否断开连接还是继续保持连接

    date: 当前时间值

    dnt:  (Do Not Track) 表明了用户对于网站追踪的偏好。

    expect: 是一个请求消息头，包含一个期望条件，表示服务器只有在满足此期望条件的情况下才能妥善地处理请求。服务器开始检查请求消息头，可能会返回一个状态码为 100 (Continue) 的回复来告知客户端继续发送消息体，也可能会返回一个状态码为417 (Expectation Failed) 的回复来告知对方要求不能得到满足。

    host: 请求头指明了服务器的域名(对于虚拟主机来说)，以及服务器监听的TCP端口号。

    keep-alive: 允许消息发送者暗示连接的状态，还可以用来设置超时时长和最大请求数。

    origin: 指示了此次请求发起者来自于哪个站点。

    referer: 表示当前页面是通过此来源页面里的链接进入的，与origin相似。

    te: 指定用户代理希望使用的传输编码类型

    trailer: 允许发送方在分块发送的消息后面添加额外的元信息，这些元信息可能是随着消息主体的发送动态生成的，比如消息的完整性校验，消息的数字签名，或者消息经过处理之后的最终状态等。

    transfer-encoding: 指明了将 entity 安全传递给用户所采用的编码形式。transfer-encoding是一个逐跳传输消息首部，即仅应用于两个节点之间的消息传递，而不是所请求的资源本身。一个多节点连接中的每一段都可以应用不同的Transfer-Encoding 值。

    upgrade: 升级为其他协议

    via: 代理服务器相关的信息
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;以上这些由HEAD扩展的HTTP功能都不能进行自主的选择，其中包括了我们比较关心的标识 connection 和 keep-alive 保持连接的功能，这同时说明我们无法用UnityWebRequest来实现一次连接发送多次数据的需求。另外我们比较关心的content-length也不能被自定义设置，它会由API本身来自动设置。与原来WWWW的API不同的是，UnityWebRequest更加简洁方便，但也失去了一些自定义的功能。接下来我们就使用UnityWebRequest来封装HTTP网络层。&lt;/p&gt;

&lt;p&gt;我们先设计一个类，假如建个名称为HTTPRequest的类，它最重要的功能是向指定服务器发送请求并接受响应数据，每次请求服务器都调用这个类的方法来处理一个请求的操作。我们可以通过 HTTPRequest 这个类把地址、参数、回调句柄传进去，然后等待服务器响应数据，当收到响应数据时就调用相应游戏逻辑做处理从而反应到画面上。&lt;/p&gt;

&lt;p&gt;HTTP需要的接口相对较少，其中POST(以POST方式发送数据)、GET(以GET方式发送数据)、HEAD(修改某个HEAD标签值)、Start(开始发送请求) 这几个接口比较重要，这些我们都可以在 UnityWebRequest 中对应的是函数句柄。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;POST: UnityWebRequest UnityWebRequest.Post(string uri, WWWForm postData) (静态函数)

GET: UnityWebRequest UnityWebRequest.Get(string uri) (静态函数)

HEAD: UnityWebRequest.SetRequestHeader(string name, string value) (非静态函数)

Start: UnityWebRequest.SendWebRequest() (非静态函数)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;用这四个API实现HTTP基本的发送与接收足以。&lt;/p&gt;

&lt;p&gt;在使用 UnityWebRequest 发送请求时可以用协程也可以在逻辑更新中进行判断收发过程。只是根据我个人经验在协程中不太可控，每次都起一个协程来发送请求数据也有点浪费，在一些特殊需求下协程结束时随着函数调用结束而结束的，而我们时常会有需要暂停、而后继续操作等无法满足。所以一般都会把HTTP的收发判断移到脚本更新函数Update里去，这样做更容易掌控也更容易理解。&lt;/p&gt;

&lt;h6 id=&quot;下面我们建立一个类来封装-unitywebrequest-的收发过程&quot;&gt;下面我们建立一个类来封装 UnityWebRequest 的收发过程：&lt;/h6&gt;

&lt;p&gt;1.建立实例开始连接和发送请求，设置好这次请求回应的回调句柄。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;void StartRequest(string url, Callback _callback)
{
    this.web_request = UnityWebRequest.Get(url);
    this.Callback =  _callback;
    this.web_request.SendWebRequest();
}

or POST

void StartRequest(string url, WWWForm wwwform, Callback _callback)
{
    this.web_request = UnityWebRequest.POST(url, wwwform);
    this.Callback =  _callback;
    this.web_request.SendWebRequest();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上述代码中我们实例化了一个HTTP请求，并且把地址和参数内容传给了实例，然后设置数据响应时的回调句柄，最后发送请求。&lt;/p&gt;

&lt;p&gt;2.判断是否完成或者说发送请求是否完毕,并且调用回调函数&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;void Update()
{
    if(web_request != null)
    {
        if(web_request.isDone)
        {
            ProcessResponse(web_request);
            web_request.Dispose();
            web_request = null;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这里我们在脚本更新函数 Update 中不断去判断当前的请求实例是否完成并有响应数据，当收到响应数据时则调用处理函数对数据进行逻辑处理。&lt;/p&gt;

&lt;p&gt;3.处理数据，收到响应数据后先判断是否有错误存在，没有错误再对数据进一步处理。客户端与服务器之间的数据传输都需要有协商好的协议，通常HTTP上使用Json格式的比较多，我们在收到响应数据后对数据进行解析然后变为了具体实例，再传给相应的函数句柄进行调用，所以数据格式协议也是比较重要的关键，关于数据协议我们会在下面的章节中详细的讨论。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;void ProcessResponse(UnityWebRequest _WebRequest)
{
    if(_WebRequest.error != null)
    {
        NetworkErrorReport(_www.error);
        return;
    }

    NetData net_data = ParseJson(_WebRequest.downloadHandler.text)

    CallbackResponse(net_data);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;以上是用UnityWebRequest做HTTP请求的基本步骤。在具体项目中看似简单的连接、发送、接收过程有不少事情需要我们扩展，特别是游戏逻辑中大量多次频繁发送HTTP请求导致的效率问题需要我们更多的关注。&lt;/p&gt;

&lt;h6 id=&quot;多次请求时连续发送http请求引起的问题&quot;&gt;多次请求时连续发送HTTP请求引起的问题&lt;/h6&gt;

&lt;p&gt;多次或者连续请求HTTP在具体项目中比较常见，例如客户端向服务器请求角色信息，并同时请求军团信息，并同时请求每日任务信息，收到所有数据后将数据呈现在主界面上。像这样的连续多次的发出HTTP请求，会同时触发多个线程向服务器做请求操作，每个请求都包括建立连接、发送数据、接受数据、关闭连接这几个步骤，多个请求导致多个线程最后得到服务器响应的返回数据时，却不知道哪个在先哪个在后，例如军团信息有可能先得到响应，然后再是每日任务信息得到了响应，再是角色信息得到了响应，因为是多个线程发起的多个连接，服务端接收到的数据也会因为受到网络的干扰而导致顺序不正确，即使服务器端接收的请求数据顺序刚好没有被打乱顺序，在处理和回调的数据时也是不可知的，这导致回调的顺序不可知，所以多连接导致不能确定响应的顺序与请求的顺序是否一致。&lt;/p&gt;

&lt;p&gt;当接收响应数据的顺序无法确定时，我们还用顺序接收数据的方式处理就会发生问题，例如前面我们说的在发送，军团，任务，角色数据请求后，希望能够在三者都到齐的情况下执行某个程序逻辑，倘若得到的数据是顺序的话，我们在收到角色数据后就能确定所有数据都已经到齐，到时我们再执行逻辑程序也会是正确的。&lt;/p&gt;

&lt;p&gt;那么怎么在多开请求连接的情况下保证顺序呢？&lt;/p&gt;

&lt;h6 id=&quot;解决方案1多个连接同时发送请求数据等待所有数据到齐后再调用执行逻辑&quot;&gt;解决方案1：多个连接同时发送请求数据，等待所有数据到齐后再调用执行逻辑。&lt;/h6&gt;

&lt;p&gt;每个HTTP(也可以认为是UnityWebRequest)请求都会开一个线程来向服务器请求数据，多个HTTP请求同时开启，相当于多个线程同时工作这样能提高网络利用率，但同时也有数据响应顺序混乱的隐患。多个请求后等待数据全部到齐后再处理逻辑，是一个比较普遍的解决多请求数据响应的方式。不过我们仍然要假设多次请求之间没有且不需要逻辑顺序，逻辑只需要它们收集齐数据后再做执行操作，只有这样我们才可以使用同时发起多次HTTP请求后等待所有请求结束后再做逻辑处理。&lt;/p&gt;

&lt;p&gt;多次请求的响应数据后等待处理的解决方案其本质是‘如何判断我们需要的多条数据是否已经都到达’。为了能更快的、更高效的得到HTTP请求数据，在同一时间同时向服务器发起多个HTTP请求，并且等待所有请求都得到响应后再执行逻辑程序。&lt;/p&gt;

&lt;p&gt;这种方式我们可以用伪代码及注释来表述得更清楚些：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;class Multiple_Request
{
    void StartRequest( request_list, call_back )
    {
        lst_req = new List&amp;lt;UnityWebRequest&amp;gt;();
        for( url,wwwform in request_list )
        {
            //开启多个HTTP请求
            UnityWebRequest req = UnityWebRequest.Post(url, wwwform);
            req.SendWebRequest();

            //记录请求实例
            lst_req.Add(req);
        }

        //记录回调函数
        Callback = call_back;
    }

    void Update()
    {
        if(lst_req == null || lst_req.Count &amp;lt;= 0) return;

        for( UnityWebRequest req in lst_req)
        {
            //判断是否完成该请求，只要有一个没完成就继续等待
            if(!req.isDone)
            {
                return;
            }
        }

        //当全部请求都完成时，执行回调
        Callback(lst_req);

        //请求结束并清空
        lst_req.Clear();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述伪代码中同时开启多个HTTP连接来向服务器请求数据，提高了网络传输效率和服务器端的CPU利用率，请求彼此之间没有顺序关系，只有这样服务器端的执行顺序才无序担心。&lt;/p&gt;

&lt;p&gt;如果请求之间是有顺序要求的，服务器端执行顺序无法保障则容易出现逻辑顺序混乱的问题。例如我们同时发起多个如下这样的请求，先购买物品、再出售物品、最后使用物品，这三个请求同时一起被发起，由于网络波动原因服务器接受到的请求顺序有可能是，使用物品，出售物品，再购买物品，当顺序不同时就有可能存在逻辑问题，这与我们原本设想的逻辑存在偏差，实例物品可能先被使用或出售，而不是被先购买，导致没有物品可被使用或出售，逻辑错位混乱是必然的。&lt;/p&gt;

&lt;p&gt;我们在同时发起多个HTTP请求连接时，虽然提高了网络效率和服务器的CPU利用率，却无法保证预期的逻辑顺序，这会使得一些业务遭遇问题。因此这种解决方案在需要逻辑顺序严格执行的业务上无法正常运作，它只能运用在不需要逻辑顺并且需要同时发起多个请求的业务上。&lt;/p&gt;

&lt;h6 id=&quot;解决方案2逐个发起请求保证顺序&quot;&gt;解决方案2：逐个发起请求，保证顺序&lt;/h6&gt;

&lt;p&gt;为了解决顺序问题，我们可以逐个发起请求，这种方式比较普遍。在网页端、安卓原生端、苹果原生端都会使用这种方式来做请求，即每次只处理一个请求，当这个请求结束后再发起下一个请求，每个请求对应一个功能模块，结束当前功能模块后再发起下一个模块的请求逻辑。&lt;/p&gt;

&lt;p&gt;我们再用前面所描述的例子来描述当前的解决方案，我们对HTTP做请求角色信息、再请求军团信息，再请求每日任务信息，这次我们不再同时发起请求，而是每次只做一件事，在收到角色信息数据后，再发起请求军团信息，收到军团信息后，再发起请求任务信息，收到任务信息后再显示在界面，每个请求收到响应数据后再处理当前请求的业务逻辑，当某个请求得到响应后都处理各自的事情。&lt;/p&gt;

&lt;p&gt;也就是说在我们的例子中，请求角色信息的得到响应后处理角色信息相关的逻辑，请求任务信息的得到响应后处理任务信息相关的逻辑，以此类推由于客户端按顺序发送，结束时每个数据也都可以独立运行，结束后再继续下一个请求，并且所有请求响应的逻辑都可以程序来实时的自定义。可以理解为如下伪代码为：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;void on_button_click()
{
    //请求角色数据
    function_request_roleinfo( callback_function1 ) 
}

void callback_function1( data )
{
    //记录角色信息
    role_info.Add(data);

    //请求军团信息
    function_request_groupinfo( callback_function2 )
}

void callback_function2( data )
{
    //记录军团信息
    group_info.Add( data );

    //请求任务信息
    function_request_task( callback_funciton3 );
}

void callback_function3( data )
{
    //记录任务信息
    task_info.Add( data);

    //展示UI
    show_ui();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述伪代码描述了发送请求的逻辑顺序，这种逐个发送请求的方式，保证了逻辑顺序，同时也满足了对多样化功能的扩展，同一个请求可以在不同逻辑处拥有自己的自定义的处理逻辑。&lt;/p&gt;

&lt;p&gt;我们说的单个连接的方式确实保证了顺序，但也降低了网络连接效率，多个请求时需要逐个发起TPC连接，每个连接都需要等待上一个连接请求完毕后才能开始下一个请求，并且在每次收到消息后都会发起4次TCP挥手来断开连接，在每次发起请求时又必须进行3次握手来建立连接，这种方式消耗了网络资源降低了收发效率。&lt;/p&gt;

&lt;p&gt;除了上面我们说的这种自定义式的逐个发送请求外，另外还有一种逐个发送请求的方式，它为了保证请求的顺序进行，使用了发送队列和接收队列来确保发送与接收的顺序。其中为发送所做的队列在请求和响应中起到了缓冲的作用，在连续使用HTTP请求和连续收到HTTP响应时，能够做到依次处理相应的逻辑。&lt;/p&gt;

&lt;p&gt;这种方式全程只有1个线程在做请求处理，所以并不需要线程锁之类的操作。发送时向队列推送请求实例，在逻辑更新函数Update上判断是否有请求在队列里，有的话推出一个请求做HTTP连接操作，同时将当前请求的信息暂时存放在内存中，当得到服务器响应时调用请求信息中的回调函数处理回调数据。&lt;/p&gt;

&lt;p&gt;我们也用伪代码来描述这种发送和接收队列的过程:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;//将请求推入队列
void RequestHttp(Request req)
{
    ListRequest.Push(req);
}

//逻辑更新
void Update()
{
    //是否完成HTTP
    if( IsHttpFinished() )
    {
        //开始新的请求，推出一个请求数据，存起来，并开始发送HTTP连接
        Request  req = ListRequest.Pop();
        mCurrentRequest = req;
        StartHttpRequest(req);
    }
    else
    {
        //是否收到响应
        if(HttpIsDone())
        {
            //根据响应数据处理逻辑
            ProcessResponse(data);

            //完成HTTP
            FinishHttp();
        }
    }
}

//根据数据处理逻辑
void ProcessResponse(Response data)
{
    if(mCurrentRequest != null
        &amp;amp;&amp;amp; mCurrentRequest.Callback != null)
    {
        //回调句柄，根据数据处理相关逻辑
        mCurrentRequest.Callback(data);
        mCurrentRequest = null;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;以上过程阐述了在发送HTTP请求时的推入队列操作和在收到请求后的句柄响应操作，队列使我们能够逐个请求以及逐个响应。&lt;/p&gt;

&lt;h6 id=&quot;解决方案3多连接与逐个发送的混合使用&quot;&gt;解决方案3：多连接与逐个发送的混合使用。&lt;/h6&gt;

&lt;p&gt;多连接提高网络请求效率但没有一致的响应顺序，逐个发送有一致的响应顺序网络请求效率则不够好。两者之间其实没有排斥关系，我么可以混合使用，这样既提高了网络效率又保证了顺序。&lt;/p&gt;

&lt;p&gt;我们来看看该如何混合使用它们，我们仍然使用前面解决方案1中的类 Multiple_Request ，原本的 Multiple_Request 类可以传入多个请求数据，并等待全部数据响应结束后再执行回调，接着我们在回调逻辑中也可以用 Multiple_Request 来执行下一组请求。我们用伪代码描述一下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;//界面按钮
void on_click()
{
    //新建一个请求对象
    request1 = new request();
    request1.url = url1;
    request1.wwwform = new wwwform();
    request1.wwwform.AddField(&quot;name1&quot;,&quot;value1&quot;);

    //加入请求列表
    lst_request.Add(request1);

    //兴建一个请求对象
    request2 = new request();
    request2.url = url2;
    request2.wwwform = new wwwform();
    request2.wwwform.AddField(&quot;name21&quot;,&quot;value21&quot;);

    //加入请求列表
    lst_request.Add(request2);

    //新建一个请求对象
    request2 = new request();
    request2.url = url3;
    request2.wwwform = new wwwform();
    request2.wwwform.AddField(&quot;name22&quot;,&quot;value22&quot;);

    //加入请求列表
    lst_request.Add(request3);

    //新建多请求实例
    multiple_request req = new multiple_request();

    //开始多个请求对象开启HTTP请求
    req.StartRequest(lst_request, callback_function1);
}

void callback_function1(lst_request)
{
    //做些逻辑
    do_some_function(lst_request);

    //新建一个请求对象
    request1 = new request();
    request1.url = url1;
    request1.wwwform = new wwwform();
    request1.wwwform.AddField(&quot;name1&quot;,&quot;value1&quot;);

    //加入请求列表
    lst_request.Add(request1);

    //兴建一个请求对象
    request2 = new request();
    request2.url = url2;
    request2.wwwform = new wwwform();
    request2.wwwform.AddField(&quot;name21&quot;,&quot;value21&quot;);

    //新建多请求实例
    multiple_request req = new multiple_request();

    //开始多个请求对象开启HTTP请求
    req.StartRequest(lst_request, callback_function2);
}

void callback_function2(lst_request)
{
    //将信息展示到UI
    show_ui(lst_request);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述伪代码描述了如何多请求与逐个请求的并行使用，将多个请求看成一个请求的变体，将多个请求看成一个完整的功能块，就有了多请求与逐个请求的混合使用过程。在现实项目中，我们可以为每个网络功能新建一个类，即继承 Multiple_Request 类，并且将所有参数写进子类中后一并发送，并且可以多次使用在多个业务逻辑中。我们用伪代码描述这种情况:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;//新建个父类继承 multiple_request 专门用来做某个功能，这个功能里需要请求多条数据
class do_something : multiple_request
{
    void StartRequest( int val1 , int val2, int val3, call_back )
    {
        //新建一个请求对象
        request1 = new request();
        request1.url = url1;
        request1.wwwform = new wwwform();
        request1.wwwform.AddField(&quot;name1&quot;,&quot;value1&quot;);

        //加入请求列表
        request_list.Add(request1);

        //兴建一个请求对象
        request2 = new request();
        request2.url = url2;
        request2.wwwform = new wwwform();
        request2.wwwform.AddField(&quot;name21&quot;,&quot;value21&quot;);

        //加入请求列表
        request_list.Add(request2);

        //新建一个请求对象
        request2 = new request();
        request2.url = url3;
        request2.wwwform = new wwwform();
        request2.wwwform.AddField(&quot;name22&quot;,&quot;value22&quot;);

        base.StartRequest(request_list, call_back);
    }
}

//ui上的按钮
void on_click()
{
    //实例化功能对象
    do_something = new do_something();

    //开始请求
    do_something.StartRequest(1,2,3, call_back_function1);
}

//回调1
void call_back_function1(lst_request)
{
    //做点事
    do_something(lst_requset);

    //实例化功能2的对象
    do_something2 = new do_something2();

    //开始请求
    do_something2.StartRequest(3,2, call_back_function2);
}

//回调2
void call_back_function2(lst_request)
{
    //展示UI
    show_ui(lst_request);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述伪代码描述的，在实际项目中可以用子类继承父类功能的方法复用了请求的步骤，使得需要某个功能时使用起来更加便捷。&lt;/p&gt;

&lt;h6 id=&quot;解决方案4合并请求并逐个发送合并后的请求包&quot;&gt;解决方案4：合并请求，并逐个发送合并后的请求包。&lt;/h6&gt;

&lt;p&gt;多个请求与逐一发送混合的解决方案，确实既提高了发送效率，又保证了顺序，但任然有几个缺点。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    缺点1，多开连接导致连接数增多，频繁的建立连接与断开连接导致网络效率仍然不够高。

    缺点2，虽然自定义空间很大但必须手动建立新功能类，逻辑变得庞大后分层不容易清晰。

    缺点3，多功能间的顺序必须手动排列顺序。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;客户端程序员在使用时更希望的是多次请求能够被同时发出，同时收到的响应顺序也要按原来请求的顺序响应，并且要可以满足自定义回调句柄满足多样化的扩展需求。&lt;/p&gt;

&lt;p&gt;如此一来合并请求就成了更适合的解决方案，即合并多个请求的数据为一个请求，当发起请求时一次性打包多个请求，只用一个连接就能全部发送并响应所有数据，这样一来发送这个合并数据包可以做到与发起多个连接同样的效果，并且响应也可以做到顺序一致性。&lt;/p&gt;

&lt;p&gt;合并请求的方式做HTTP，减少了连接数量也提高了网络效率，同时还保证了请求顺序与响应顺序的准确性。&lt;/p&gt;

&lt;p&gt;那么如何实现呢？这次我们需要服务端程序员同学一些配合，服务端同学需要将原本多个地址，每个地址对应的功能块的模式，改为同一个地址，并且改为使用ID(我们暂且称它为Commoand ID) 字段来对应不同的功能块。&lt;/p&gt;

&lt;p&gt;这里我们以Json数据协议为例来说明这个过程(因为Json格式比较好理解)，HTTP请求数据使用Json格式，服务器响应数据也是Json格式(通常都是这种做法，并且这在互联网非常普遍)。客户端发送Json格式的请求数据给服务器，服务器收到后解析Json格式数据提取数据后处理逻辑，接着将要返回的数据反序列化为Json格式给客户端。&lt;/p&gt;

&lt;p&gt;现在为了合并Json数据，我们在对多个请求发起时，将多个Json请求格式推入到一个大的Json数据中，同时在合并时为每个合入进去的Json数据添加一个序列号，代表合入前的顺序，请看如下Json合并过程：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;//请求1
{
    &quot;request-order&quot; : 1,
    &quot;command&quot; : 1001,
    &quot;data1&quot; : &quot;i am text&quot;,
    &quot;data2&quot; : &quot;i am num&quot;,
}

//请求2
{
    &quot;request-order&quot; : 2,
    &quot;command&quot; : 2011,
    &quot;book&quot; : &quot;i am text&quot;,
    &quot;chat&quot; : &quot;i am num&quot;,
    &quot;level&quot; : 1，
}

//请求3
{
    &quot;request-order&quot; : 3,
    &quot;command&quot; : 3105,
    &quot;image&quot; : &quot;i am text&quot;,
    &quot;doc&quot; : &quot;i am num&quot;,
}

//将1，2，3合并后的请求数据为
{
    &quot;data&quot;:
    [
        {
            &quot;request-order&quot; : 1,
            &quot;command&quot; : 1001,
            &quot;data1&quot; : &quot;i am text&quot;,
            &quot;data2&quot; : &quot;i am num&quot;,
        }
        ,
        {
            &quot;request-order&quot; : 2,
            &quot;command&quot; : 2011,
            &quot;book&quot; : &quot;i am text&quot;,
            &quot;chat&quot; : &quot;i am num&quot;,
            &quot;level&quot; : 1;
        }
        ,
        {
            &quot;request-order&quot; : 3,
            &quot;command&quot; : 3105,
            &quot;image&quot; : &quot;i am text&quot;,
            &quot;doc&quot; : &quot;i am num&quot;,
        }
    ],
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述中合并时多个Json被推入到Json数组中，接着将它们一并发送给服务器端，服务器端程序接受到数据后对data字段中的数据进行提取、解析、处理，并且处理顺序按照request-order从小到的顺序来做，每个数据块都有一个command字段，用这个字段来判断使用哪个程序功能模块来处理逻辑，接着将数据传给该功能模块处理。&lt;/p&gt;

&lt;p&gt;在每个功能模块处理完毕后，将所有响应数据都推入同一个Json实例中，同时附上与请求数据中相同的request-order，以便客户端识别与请求数据对应的响应数据，最后把整个响应数据一并发送给客户端。当客户端收到数据时，通过request-order就能知道响应数据的顺序，以及是哪个请求发起后响应的数据，于是客户端在按照request-order从小到大的顺序排序后进行解析和回调，得到了与请求顺序一致的响应顺序。&lt;/p&gt;

&lt;p&gt;我们来看看客户端收到服务器端的合并响应数据是怎样的：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;//响应数据1
{
    &quot;response-order&quot; : 1,
    &quot;command&quot; : 1001,
    &quot;data1&quot; : &quot;response text&quot;,
    &quot;data2&quot; : &quot;response num&quot;,
    &quot;error_code&quot; : &quot;0&quot;, 
}

//响应数据2
{
    &quot;response-order&quot; : 2,
    &quot;command&quot; : 2011,
    &quot;data1&quot; : &quot;response text&quot;,
    &quot;data2&quot; : &quot;response num&quot;,
    &quot;error_code&quot; : &quot;1&quot;,
}

//响应数据3
{
    &quot;response-order&quot; : 3
    &quot;command&quot; : 3105,
    &quot;data1&quot; : &quot;response text&quot;,
    &quot;data2&quot; : &quot;response num&quot;,
    &quot;error_code&quot; : &quot;2&quot;,
}

//合并后的响应数据
{
    &quot;error_code&quot; : 0,
    &quot;data&quot;:
    [
        {
            &quot;response-order&quot; : 1,
            &quot;command&quot; : 1001,
            &quot;data1&quot; : &quot;response text&quot;,
            &quot;data2&quot; : &quot;response num&quot;,
            &quot;error_code&quot; : &quot;0&quot;, 
        }
        ,
        {
            &quot;response-order&quot; : 2,
            &quot;command&quot; : 2011,
            &quot;data1&quot; : &quot;response text&quot;,
            &quot;data2&quot; : &quot;response num&quot;,
            &quot;error_code&quot; : &quot;1&quot;,
        }
        ,
        {
            &quot;response-order&quot; : 3
            &quot;command&quot; : 3105,
            &quot;data1&quot; : &quot;response text&quot;,
            &quot;data2&quot; : &quot;response num&quot;,
            &quot;error_code&quot; : &quot;2&quot;,
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;如上所描述的，在得到合并后的响应数据后，客户端要先提取data数据中的所有数据，以respose-order为基准从小到大进行排序，从头上最小序号的开始处理。&lt;/p&gt;

&lt;p&gt;到现在合并请求的部分我们描述了单个连接多个请求的方式，如果多个合并请求一同发送也会遇到大麻烦，同样会遇到顺序问题，因此我们依然需要用队列来规避这个问题。用了队列的方式保证多个请求的有序性，与前面用队列逐个发送的方式相比，我们除了用队列暂时存储请求数据外还在队列之上进行操作请求合并，这样能更有效更方便的做到合并以及有序发送。&lt;/p&gt;

&lt;p&gt;我们来看看队列与合并数据包相结合的方式用伪代码描述:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;//将请求推入队列
void RequestHttp(Request req)
{
    ListRequest.Push(req);
}

//逻辑更新
void Update()
{
    //是否完成HTTP
    if( IsHttpFinished() )
    {
        //推出多个请求进行合并
        lst_combine_req.Clear();
        for(int i = 0 ; i&amp;lt;10 ; i++)
        {
            Request  req = ListRequest.Pop();
            lst_combine_req.Add(req);
        }

        //合并多个请求
        combine_request = CombineRequest(lst_combine_req);
        StartHttpRequest(req);
    }
    else
    {
        //是否收到响应
        if(HttpIsDone())
        {
            //按ID从小到大排序
            sort(combine_request.lst_response);

            //循环处理每个响应逻辑
            for(int i = 0 ; i&amp;lt;combine_request.lst_response.count ; i++)
            {
                //根据响应数据处理逻辑
                data = combine_request.lst_response[i];
                ProcessResponse(data);
            }

            //完成HTTP
            FinishHttp();
        }
    }
}

//根据数据处理逻辑
void ProcessResponse(Response data)
{
    if(mCurrentRequest != null
        &amp;amp;&amp;amp; mCurrentRequest.Callback != null)
    {
        //回调句柄
        mCurrentRequest.Callback(data);
        mCurrentRequest = null;
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;来看看从按钮点击事件开始的具体逻辑的操作：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;//按钮事件
void on_click()
{
    //请求玩家数据
    request_roleinfo( call_back_function1 );

    //请求军团数据
    request_groupinfo( call_back_function2 );

    //请求任务数据
    request_taskinfo( call_back_function3 );
}

//先回调的是这个句柄
void call_back_function1( data )
{
    //保存玩家数据
    save_roleinfo(data);
}

//再回调的是这个句柄
void call_back_function2( data )
{
    //保存军团数据
    save_groupinfo(data);
}

//最后收到任务数据时，表明前面的数据都已经收到了，就可以做一些逻辑处理
void call_back_function3( data )
{
    //保存任务数据
    save_taskinfo(data);

    do_something();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述伪代码中描述的在具体业务逻辑中，我们客户端在编写网络请求时可以随意的调用发送请求而不再需要关心顺序和发送效率的问题，大大提高了程序员的网络逻辑编程效率。&lt;/p&gt;

&lt;p&gt;我们对多个HTTP请求进行了合并数据的操作后减少请求的次数，对速度的提升很有大的效果，但这里还有一些细节问题需要我们关注，比如多少个开始进行合并，或者多少时间内合并一次。&lt;/p&gt;

&lt;p&gt;为了让HTTP最大效率的得到提升，必须每次请求得到响应后就应该立即进行下一次HTTP请求，如果有等待合并间隔，反而减低了网络效率。不过每次合并数量需要做一些限制，如果有100个请求同时被发起，我们不能统统合并了，这也是为了保证发送数据大小和回调数据大小合适，数据包越大丢包重发的概率也就越大，因此我们必须限制一次性合并的个数，我们可以选择每次最多合并10个请求数据包，以保证减少连接次数减少的情况下，发送和接收的数据包不会过大。&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第五章，3D模型与动画(四) - 3D模型的变与换4</title>
   <link href="http://www.luzexi.com/2019/05/18/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-3D%E6%A8%A1%E5%9E%8B%E4%B8%8E%E5%8A%A8%E7%94%BB7"/>
   <updated>2019-05-18T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/05/18/Unity3D高级编程之进阶主程-3D模型与动画7</id>
   <content type="html">&lt;p&gt;前面讲解了些骨骼动画的基础知识，我们在基础知识上理解了骨骼动画的播放原理。在了解了骨骼原理之后，我们又对人物模型动画的换皮换装的原理了剖析一下，由于有了骨骼动画的基础知识支撑，我们在对换装换皮的方法和技巧上理解起来就更加的清晰了。&lt;/p&gt;

&lt;p&gt;其实看一遍是不够的，我们可能需要要看很多遍，而且最好边想边看边实践，这样才能把学习效果放大，如果可以的话最好能与别人分享自己学到的知识。要不怎么说理论这东西用处不够大呢，如果只有理论那么这就是纸上谈兵，解决不了实际问题，反过来也是一样，只有实践则无法彻底了解原理和机制，我们就无法精进，总是觉得有什么东西被蒙在鼓里，运用起来不能得心应手。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;h6 id=&quot;有原理又有实践最好我们能多思考多举一反三要彻底理解一个知识点不容易在这条学习的道路上我们不能放弃即使停下来也要想着什么时候能重新开始人无完人大家都一样所有人都要经历失败放弃再重新捡起来的过程谁能更快的重新开始就成了关键失败是常见的屡次失败也是常有的就看谁能在一次次失败后还能继续坚持着&quot;&gt;有原理又有实践，最好我们能多思考多举一反三。要彻底理解一个知识点不容易，在这条学习的道路上我们不能放弃，即使停下来也要想着什么时候能重新开始。人无完人大家都一样，所有人都要经历失败，放弃，再重新捡起来的过程，谁能更快的重新开始就成了关键，失败是常见的，屡次失败也是常有的，就看谁能在一次次失败后还能继续坚持着。&lt;/h6&gt;

&lt;p&gt;本篇我们要讲在骨骼动画原理之上讲些更高级的技巧，这些技巧都是基于上篇和上上篇的内容之上的。我们用最简洁的语言回顾了一下基础的知识。&lt;/p&gt;

&lt;p&gt;网格主要由顶点，三角形索引数组，uv这三个基础数据组成，除了这三个也可以有顶点的法线和颜色的歌数据。其中uv用于贴图，法线用于展示凹凸效果，顶点颜色则有其他多种用途。&lt;/p&gt;

&lt;p&gt;蒙皮骨骼动画，也就是Unity3D中的SkinnedMeshRenderer，除了有这些网格数据外，又多了骨骼点和骨骼权重数据。骨骼点，是以父子或兄弟的关系连接的节点，它在Unity3D里的表现形式相当于许多GameObject相互挂载并放在根节点下，除了这些看得见的GameObject节点外，骨骼点还需要旋转矩阵bindPoses，它主要是为了当父节点旋转移位时能更快的计算得到自身位移和旋转的变化矩阵，每个矩阵都是其父节点矩阵相乘所得到的结果。最后每个顶点都有自己的顶点数据，在由骨骼动画的网格顶点数据之上又多了些数据来表达被哪些骨骼的影响权重值，这就是顶点的骨骼权重数据。&lt;/p&gt;

&lt;h3 id=&quot;捏脸&quot;&gt;捏脸&lt;/h3&gt;

&lt;p&gt;捏脸在网络游戏中泛指对虚拟角色样貌进行DIY的数据操作。捏脸看起来像是很复杂的技巧，在我们剖析一下后就你会觉得它并没有想象中的那么困难。&lt;/p&gt;

&lt;h6 id=&quot;首先捏脸最重要的部分就是换部位&quot;&gt;首先，捏脸最重要的部分就是换部位。&lt;/h6&gt;

&lt;p&gt;角色身上可以替换的部位有，不同形状的头，不同形状的上身，不同形状的腿，不同形状的脚，不同形状的手，其实还可以细分到更多，比如嘴，耳朵，胸，头发等，这些部件都可以从整体模型中拆分出来，单独成立一个模型，然后再选出来后拼装到整体模型上去。拆分出不同部位的模型，有了多个相同部位不同形状的模型后，我们就有了很多个模型部件可以替换，在捏脸时就可以选择不同的形状的部件。&lt;/p&gt;

&lt;p&gt;替换的过程就是上篇我们讲到的换装的过程，我们可以再来简单回顾下。&lt;/p&gt;

&lt;p&gt;我们必须所有模型都使用同一套骨骼，把骨骼以SkinnedMeshRenderer组件的方式实例化出来，我们暂时称它为‘根节点’，并挂上动画组件和动画文件，当播放动画时就可以看到骨骼会跟随每帧动画数据而变动。但此时还没有任何模型展示，我们把选中的部件模型也以SkinnedMeshRenderer组件的形式实例化出来，并挂载在‘根节点’下。&lt;/p&gt;

&lt;p&gt;现在挂载在‘根节点’下的部件模型只是静止的不会动的模型，虽然其自身有顶点的骨骼权重数据，但没有骨骼点的数据是无法计算出骨骼变化后的模型变化的。因此我们再把‘根节点’里的骨骼点数据赋值给这些模型部件，让他们能在每帧渲染前根据骨骼点的变化结合自身的骨骼权重数据计算出自身的网格变化情况。做完这些操作后，我们就算成功合成了一个由自己选择的人体部件并带骨骼动画的角色模型实例。&lt;/p&gt;

&lt;p&gt;当需要更换人体部件时，所需要的操作与合成一个角色模型的步骤一样，只是在这之上有了些小的变化，因为只替换某个部件，所以‘根节点’与其他没有更换的部件不需要被销毁，是可以重复利用的，只需要删除替换的部件实例。&lt;/p&gt;

&lt;p&gt;合成完模型看看这个角色，这么多部件都使用了SkinnedMeshRenderer，每个SkinnedMeshRenderer都有一定计算和drawcall的消耗，怎么办？合并。&lt;/p&gt;

&lt;p&gt;一种简单的办法就是仍然使用多个材质球进行渲染，在合并Mesh时使用子网格(SubMesh)模式，相当于只减少了SkinnedMeshRenderer组件的数量，并没有减少其他的消耗。另一种办法稍微复杂点，不使用子网格(SubMesh)模型，而是将所有模型合并成一个Mesh网格，使用同一个材质球。不过我们还是得保证有相同Shader的材质球进行合并，不相同的Shader的材质球不合并的原则，以保证角色渲染效果不变。&lt;/p&gt;

&lt;p&gt;把这么多材质球合并成一个的困难之处在于，贴图怎么办，uv怎么办？贴图我们需要采用实时合并贴图的方式。合并材质球实质上是为了降低Drawcall，我们的办法实质上就是内存换CPU的方式，每次合成角色、更换部件时都重新合成一遍贴图，同时把uv设置在合并贴图后的某个范围内，因为uv的相对位置是不变的，所以只要整体移动到某个范围内就可以正常显示。&lt;/p&gt;

&lt;p&gt;这样模型的更换与合并，让角色捏脸系统有了基础的功能，而材质球、贴图的合并，优化了性能效果让这个系统更加完美。&lt;/p&gt;

&lt;h6 id=&quot;其次是更换贴图&quot;&gt;其次是更换贴图&lt;/h6&gt;

&lt;p&gt;不同颜色的头发，不同颜色的手套，不同颜色相同形状的衣服，不同贴图相同形状的眼睛等，这些可以简单的使用更换贴图来达到目标的动作，就直接更换材质球里的贴图就可以了，不需要太复杂的操作，如果是采用贴图合并的方式来做的合并，那么就再重新合并一次贴图，如果更换的贴图大小不一样uv也需要重新计算一次。&lt;/p&gt;

&lt;h6 id=&quot;再者是骨骼移动旋转缩放&quot;&gt;再者是骨骼移动、旋转、缩放&lt;/h6&gt;

&lt;p&gt;除了更换部件、更换颜色的操作外，捏脸还有一个重要的功能，就是用户可以自由随意的DIY去塑造模型。例如把鼻子抬高点，把嘴巴拉宽点，把腰压细一点，把腿拉长一点等。&lt;/p&gt;

&lt;p&gt;由于模型的网格(Mesh)是根据骨骼点来变化的，每个组成网格的顶点都有自己的骨骼权重数据，所以只要骨骼点移动了，它们也跟着移动，骨骼点旋转了，它们也跟着旋转，骨骼点缩放了，它们也跟着缩放。于是我们可以利用这个特性来做一些操作，来让‘捏泥人’更加容易，最后只要记录骨骼移动和旋转或缩放的数据就可以了，在重现时再次将数据重新导入到骨骼，就能呈现出原来玩家捏脸时的样子。&lt;/p&gt;

&lt;p&gt;不过问题也来了，骨骼点是随着动画一起动的，动画数据里的关键帧决定了骨骼点的变化，我们实时改变骨骼点位置是无法达到效果的，因为动画数据会强行恢复骨骼点，致使我们的操作变得无效。我们既要整个模型网格仍然依照原来的动画数据去变动，又要用某个骨骼点去影响某些网格怎么办？额外增加一些骨骼点，这些骨骼点是专门为用户可操作服务的骨骼点，并且这些骨骼点不加入到动画数据中。也就是说动画animation中的数据不会有这些特别骨骼点的存在，这也使得在动画播放时这些骨骼点是不会动的。&lt;/p&gt;

&lt;p&gt;然后为了能让网格随着操作这些骨骼点儿发生变化，在顶点的骨骼权重数据里给这些骨骼点一些权重，这个权重能达到玩家操作效果就可以了，其他都由动画去决定变化，SkinnedMeshRenderer会在每帧根据骨骼点的变化计算出所有顶点的位置，也就是网格的变化形状。&lt;/p&gt;

&lt;p&gt;这样操作下来，我们就达到了先前说的，既要整个模型网格仍然依照原来的动画数据去变动，又可以让用户自定义操作骨骼点去影响网格变化。&lt;/p&gt;

&lt;h6 id=&quot;用两个不同网格顶点的线性插值做脸部动画&quot;&gt;用两个不同网格顶点的线性插值做脸部动画&lt;/h6&gt;

&lt;p&gt;只操作骨骼点来改变模型的捏脸效果还是不够的，因为毕竟骨骼点数量不能太多，顶点的骨骼权重数据也是有限的，特别是脸部表情的网格变化比较复杂，它有50多块肌肉组成网格顶点变化又多又快，此时无法通过增加大量的骨骼点来达到脸部模型复杂变化的效果。于是只能再另寻它方，这次我们回到了最基础的网格变化，由于蒙皮网格在每帧都从原始的网格加上骨骼点的变化数据来计算现在网格的形状的，那么改变原始网格的顶点数据也同样可以改变网格在动画时的模型变化。&lt;/p&gt;

&lt;p&gt;还记得前面我们提到的最初在没有蒙皮骨骼时动画师们所用的办法么，用两个模型网格顶点的线性插值来做为网格变化的形状，即变形目标动画(morph target animation)。我们可以制作几个极端的脸部表情模样，用网格顶点插值形式对原始网格数据里的顶点进行变化，每个插值都带来顶点变化形成一个网格形状，从而形成不同脸部表情的动画。&lt;/p&gt;

&lt;h6 id=&quot;其实我们一直在围绕着基础知识做技术研究基础知识和原理是核心当我们实践时巩固了对基础知识和原理的理解理论与实践相结合并且不停地交替学习逐渐得我们就能得心应手运用自如甚至还能到无剑胜有剑的境界&quot;&gt;其实我们一直在围绕着基础知识做技术研究，基础知识和原理是核心，当我们实践时巩固了对基础知识和原理的理解，理论与实践相结合，并且不停地交替学习，逐渐得我们就能得心应手，运用自如甚至还能到无剑胜有剑的境界。&lt;/h6&gt;

&lt;h3 id=&quot;动画优化&quot;&gt;动画优化&lt;/h3&gt;

&lt;p&gt;我们前面讲了关于蒙皮动画太消耗CPU的问题，所有蒙皮网格的变化都是由CPU计算得到的。&lt;/p&gt;

&lt;p&gt;在Unity3D中有一个 CPU Skinning 的选项，开启后将使用多线程 + SIMD 对蒙皮计算做加速处理，由于每个顶点的变化都是独立于骨骼点之上的，相邻的顶点并不互相影响，因此可以使用多线程将一个模型的网格顶点拆分成多个部分顶点进行计算，多线程的使用将提高蒙皮计算的速度。&lt;/p&gt;

&lt;p&gt;而这里的SIMD即Single Instruction Multiple Data，是指单指令多数据流，它能够复制多个操作数，并把它们打包在大型寄存器的一组指令。我们以加法指令为例，通常我们使用的单指令单数据（SISD）在CPU对加法指令译码后，执行部件必须先访问内存取得第一个操作数，之后第二次访问内存取得第二个操作数，随后才能进行求和运算。而在SIMD型的CPU中，指令译码后几个执行部件同时访问内存，一次性获得所有操作数进行运算。SIMD的这个特点使它特别适合于数据密集型运算，在我们游戏开发中SIMD特别适合矩阵运算，我们的蒙皮计算就是大量使用矩阵运算的地方。&lt;/p&gt;

&lt;p&gt;但 CPU SKinning 并没有减少运算CPU的运算量只是加速了运算速度提高了运算效率，我们在游戏使用了大量的蒙皮动画来达到丰富效果的目的，而通常所有项目都会极致得用尽动画功能，让游戏看起来很生动，丰富，饱满，火热。这使得设备在游戏项目中承受了巨大压力，如果效果再好性能消耗太大，只有高端机才能承受渲染压力的游戏，就无法对普罗大众产生吸引力，也就无法开启吸引力效应，因此对每个项目来说动画的优化是重中之重。&lt;/p&gt;

&lt;p&gt;下面我们就来说说3D模型动画的优化方法和解决方案。&lt;/p&gt;

&lt;h6 id=&quot;用着色器代替动画&quot;&gt;用着色器代替动画&lt;/h6&gt;

&lt;p&gt;蒙皮动画说到实质处，就是网格顶点的变化，根据骨骼点与权重数据计算网格变化，它到底是前人发明的一种每帧改变网格的方法，最终的目标都是怎么让网格每帧发生变化，并且这种形状的变化是我们所期望看到的。&lt;/p&gt;

&lt;p&gt;其实无论用什么方法只要达到“变化是我们所期望看到的”这个目标都是可行的。想到这里，除了cpu中改变顶点坐标位置，我们还有另一种途径来改变顶点坐标位置，它就是着色器(Shader)中的顶点着色器，它也可以改变网格顶点的位置。于是我们可以用顶点着色器，加上一个适合的顶点变化算法就等于得到一个随着时间变化的模型动画。&lt;/p&gt;

&lt;p&gt;用着色器制造的动画这种方式已经很久远了，它也在许多项目中用的比较频繁，最常见就是随风摆动的草，会飘动的旗子，飘动的头发，左右摇摆的树，河流的波浪等等。这些算法不在这里一一讲解，它们大部分这些算法都利用了，游戏时间，噪声算法(noise)，数学公式(sin、cos等)来表达顶点的偏移量。&lt;/p&gt;

&lt;p&gt;Shader着色器随风飘动的草的例子&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    ？？？
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;除了顶点动画，我们还可以利用uv来做动画，比如不断流淌的水流就属于uv位移动画，又如火焰效果可以根据不断更换uv范围达到序列帧动画效果的uv序列帧动画，还如不停旋转的面片动画，就可以用uv旋转来代替面片旋转，把CPU的消耗转入到GPU消耗，使一部分计算更为高效。uv动画的具体算法也不再这里讲解。&lt;/p&gt;

&lt;p&gt;Shader着色器序列帧动画的例子&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    ？？？
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;用着色器代替动画实质上，就是用GPU消耗来分担CPU的计算量，因为部分计算在GPU中会更高效，这让两个芯片能更好的发挥其作用，而不是让某一个闲着没事干(GPU很闲或者CPU很闲)，另一个则忙的要死。用着色器动画就能充分利用GPU的计算优势，为CPU分担不少计算量。但用前面说的顶点动画，uv动画的算法来代替动画方式毕竟是有限的复杂度，当动画复杂到没有固定算法规律可寻时，就需要某求其他途径了，因此着色器动画的对模型动画的可优化范围是有限的。&lt;/p&gt;

&lt;h6 id=&quot;离线bake每帧的模型网格然后用更换网格的方式绘制每一帧用内存换cpu&quot;&gt;离线Bake每帧的模型网格，然后用更换网格的方式绘制每一帧，用内存换CPU&lt;/h6&gt;

&lt;p&gt;上面介绍了用着色器算法来动画，实质上它将CPU的消耗转移到了GPU消耗从而使得动画性能得以优化，不过这样做的动画的复杂度是有限的，很多复杂的动画无法用算法来表达。除了将计算量消耗从CPU转移到GPU，我们还有其他方法来优化，这次我们不打算用算法了，我们来场无剑胜有剑的战斗‘没有算法就是最大的算法’。&lt;/p&gt;

&lt;p&gt;动画的实质是，每帧显示的内容不一样，而每帧显示的内容不一样，就需要每帧都计算出一个不一样的形状。那么我们能不能不计算呢？可以的。我们可以每帧都准备一个模型，每帧都展示一个已经准备好的不一样的模型，这样就有了每帧都有不同形状的模型渲染形成动画。&lt;/p&gt;

&lt;p&gt;例如这一个5秒的蒙皮动画，每秒30帧，总共需要150个画面，我们需要最多准备150个模型来依次在每帧中播放。内存和硬盘的代价很大，原本一个模型只要一个模型网格就够了，现在要准备150个网格，内存的代价是巨大的。这就是内存换CPU的想法，到底值不值得这么做呢？&lt;/p&gt;

&lt;p&gt;假设这个场景只有2-3个模型在播放这个动画，那么为了这2-3个模型动画，我们就需要额外准备150个模型来播放动画，本来只要一个模型+骨骼就可以办到的事情，我们却要用150个模型来代替，加载这150个模型也是需要时间的，更何况内存额外加大了150倍，确实不值得。那么我们又假设，这个模型同时播放的这个动画的数量非常多，例如20个以上，这20个模型都需要计算机每帧通过模型+骨骼的方式计算出一个模型的变化形状，而且要重复计算100次，这时我们再用150个模型来代替这每帧持续的CPU消耗就非常值得了。&lt;/p&gt;

&lt;p&gt;具体步骤为：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    ？？？
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;用这种方式计算机不再需要大量计算相同模型网格的变化而只是在读取这150个模型时内存消耗以及加载的消耗换来的是持续的高效的动画效果这样的方式用内存换cpu就非常值得&quot;&gt;用这种方式计算机不再需要大量计算相同模型网格的变化，而只是在读取这150个模型时内存消耗以及加载的消耗，换来的是持续的高效的动画效果，这样的方式用内存换CPU就非常值得。&lt;/h6&gt;

&lt;h6 id=&quot;将每帧的网格偏移数据导出到图片在shader中让gpu通过图片里的数据来偏移顶点&quot;&gt;将每帧的网格偏移数据导出到图片，在Shader中让GPU通过图片里的数据来偏移顶点。&lt;/h6&gt;

&lt;p&gt;前面说的离线制作每帧的模型网格，再在每帧中渲染不同的网格来实时渲染动画的效果，这种方式确实在当需要大量渲染相同动画的场景中起到了很大的优化作用，它用内存节省了骨骼计算蒙皮的CPU消耗。不过这种方式毕竟是还是会有大量的Drawcall存在，每个模型至少有一个Drawcall，100个模型就会有100个Drawcall，GPU的压力依然没有减少。那么有没种方法合并绘制相同模型以及动画的Drawcall呢？利用GPU Instancing 这个GPU特性。&lt;/p&gt;

&lt;p&gt;什么是GPU Instancing？这是GPU显卡一个特性，大部分图形API都提供的一种技术，其表象是假如我们绘制1000个物体，它只将模型数据以及1000个坐标提交给显卡，这1000个物体不同的位置，状态，颜色等等他们将整合成一个per instance attribute的Buffer给GPU，在显卡上区别绘制。这样做因为只需要提交一次，这大大减少提交次数，这种技术对于绘制大量的相同模型的物体由于有硬件实现，效率高，更灵活，也避免了合批而造成内存浪费。这样一来我们可以根据 GPU Instancing 来实现骨骼动画的多实例渲染。&lt;/p&gt;

&lt;p&gt;GPU Instancing 最重要的一个特点是只提交一次就可以绘制1000个物体，把原本要提交1000次的流程，简化成了只需要提交1次，1000个Drawcall瞬间降为了1个。当然没那么简单，它是有条件的，首先条件是模型的着色器(Shader)要支持GPU Instancing，其次是这1000个模型他们位置、角度可以不一样，但都使用同一个网格数据。其实 GPU Instancing 的条件有点苛刻，Shader要相同，材质球要相同，网格要相同(就是传入的网格不能有变形的，就是不能有动画)。&lt;/p&gt;

&lt;p&gt;对于我们来说，如果用GPU Instancing 来优化渲染就不能再使用 SkinnedMeshRender 和 Animator或Animation来计算蒙皮了，那怎么办呢？&lt;/p&gt;

&lt;p&gt;前面我们提高离线制作模型的方法来准备150个模型，每帧都渲染一个从而省去了计算骨骼蒙皮的CPU消耗。现在我们把150个模型的网格顶顶啊数据换成贴图形式的数据，把这150个模型的网格顶点数据都一股脑放入一张贴图里去，让这张图总共有150行，每行都写入了一个的完整的网格顶点数据，让每个像素数据的 RGB 都分别代表 xyz 的顶点坐标，这样的话如果一个网格顶点总共有3000个顶点，那么每行就有3000个像素，总共150行，这张贴图就是 3000 * 150 大小。&lt;/p&gt;

&lt;p&gt;将动画的所有帧下的模型网格顶点数据制作成一张贴图后，我们要让这张图在着色器中起作用，这相当于是将所有动画帧下的网格的顶点数据传入着色器(Shader)，着色器根据传入的图中的每个像素去偏移顶点，每帧都换一行从而形成了每帧都不一样的模型动画。我们用着色器去改变顶点坐标，就相当于让GPU去改变网格省去了CPU的提交渲染的消耗。就这样我们把贴图当做动画顶点数据交给着色器去渲染，每次渲染时所有模型都使用的是同一个模型，同一个材质球，同一个Shader着色器，符合了开启GPU Instancing的条件，即我们可以只要提交一次模型数据，就能渲染1000个模型并且有动画，瞬间降低1000个Drawcall到个位数字。具体 GPU Instancing 的工作原理我们将在渲染章节中讲解。&lt;/p&gt;

&lt;p&gt;至此我们在实时渲染时不再需要计算骨骼了，同时也不需要SkinnedMeshRenderer了，只需要普通的MeshRenderer来渲染模型就可以了，动画里的顶点变化交给了着色器去做，将CPU的消耗从线上转移到了线下，同时利用 GPU Instancing 的特性更高效的渲染了图形。&lt;/p&gt;

&lt;p&gt;这里用于 GPU Instancing  做动画的着色器并不复杂，只是比普通的顶点着色器在传入参数时多了个变量‘顶点索引’，根据这个’顶点索引‘变量来计算得到传入的贴图中的uv位置从而在RGB中得到顶点坐标。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;v2f vert(appdata v, uint v_index : SV_VertexID)
{
        UNITY_SETUP_INSTANCE_ID(v); // gpu instance

        // 根据时间获得数据的y轴位置
        float f = _Time.y / _AnimLength;
        fmod(f, 1.0);

        // 计算 uv位置
        float animMap_x = (v_index + 0.5) * _AnimWidth;
        float animMap_y = f;

        //获得 顶点坐标
        float4 pos = tex2Dlod(_AnimTexture, float4(animMap_x, animMap_y, 0, 0));

        v2f o;
        //计算模型贴图uv
        o.uv = TRANSFORM_TEX(v.uv, _MainTex);
        //计算顶点位置
        o.vertex = UnityObjectToClipPos(pos);
        return o;
}

fixed4 frag (v2f i) : SV_Target
{
        // 根据uv上色
        fixed4 col = tex2D(_MainTex, i.uv);
        return col;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code&gt;    代码来源于github
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述Shader代码就是对模型动画 GPU Instancing 的实现，它先计算得到当前需要展示哪一行帧数据，然后计算数据行中当前顶点的数据位置，即数据贴图中的uv，根据计算获得的数据uv位置提取顶点坐标，之后就是顶点着色器在传统意义上的贴图uv位置计算与顶点投影计算，在片元着色器上很简单只是根据uv位置从贴图中提取颜色而已。&lt;/p&gt;

&lt;p&gt;顶点着色器中，用时间和动画长度计算出y的位置也就是数据在贴图中的行位置，再用顶点索引计算出数据在贴图中的列位置，从而得到动画数据贴图中属于自己位置的像素，取出这个像素信息就等于得到了这个顶点的坐标了，与世界坐标轴转换后即可使用。Shader中没有复杂的公式，就是从图片中取出值来作为坐标去传递。&lt;/p&gt;

&lt;h6 id=&quot;上面我们用gpu-instancing-的gpu-特性将数据从&quot;&gt;上面我们用GPU Instancing 的GPU 特性，将数据从&lt;/h6&gt;
&lt;p&gt;CPU端转移到了GPU端并达到了降低 Drawcall 的目的。但只是这样还不够，如果只是使用普通的材质球嵌入Meshrender的方式，就会使得每个人物的动画都是一样的，不会错开来，同一时间很多模型做相同的动作。如果这时使用不同的材质球实例来达到不同的动画时差后又会增加了drawcall，为了不增加drawcall，我们就需要用Unity3D的 GPU Instance 接口向着色器传入动画开始位置的数据。即用 Graphics.DrawMeshInstanced 将数据传入需要 Instancing 的 Mesh，在调用前，我们将准备好的所有模型的坐标数据、顶点贴图数据、动画状态数据等传入材质球中，从而实现不同的动画的时差。&lt;/p&gt;

&lt;p&gt;举例传入不同的模型动画数据:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    ？？？
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;除了各模型在动画播放时的差异数据精度问题也值得关注如果要求模型动画有好的表现就必须提高图片的精度因为每个像素rgb的颜色就代表了顶点坐标信息如果要求gpu支持float类型的贴图就需要open-gl-es-30以上级别的设备虽然现在的手机设备的open-gl-es-30也算比较普遍但还是有小部分低端手机设备无法实现是否考虑降低动画精度或者根据高中低端机的切换动画模块值得考虑另外如果我们使用-gpu-instancing-优化模型动画在融合动画方面则仍然束手无策这使得动画表现会比较生硬&quot;&gt;除了各模型在动画播放时的差异数据，精度问题也值得关注，如果要求模型动画有好的表现，就必须提高图片的精度，因为每个像素RGB的颜色就代表了顶点坐标信息，如果要求GPU支持float类型的贴图，就需要Open GL ES 3.0以上级别的设备，虽然现在的手机设备的Open GL ES 3.0也算比较普遍，但还是有小部分低端手机设备无法实现，是否考虑降低动画精度，或者根据高中低端机的切换动画模块值得考虑。另外如果我们使用 GPU Instancing 优化模型动画，在融合动画方面则仍然束手无策，这使得动画表现会比较生硬。&lt;/h6&gt;

&lt;h6 id=&quot;离线制作lod动画与lod网格&quot;&gt;离线制作LOD动画与LOD网格&lt;/h6&gt;

&lt;p&gt;前面我们说了使用 GPU Instancing 来优化同一个物体在场景中渲染，这使得Drawcall降低了很多，但仍然无法避免网格面片数太多的问题，我们仍然要为100个模型在场景中的渲染付出很多渲染巨大数量面片的代价。这对于一个100个由1万个三角面组成模型来说，在场景中渲染时巨大的负担。&lt;/p&gt;

&lt;p&gt;LOD就是一个很好的解决面片渲染负担过重的方案，我们可以根据不同的机型来加载’高、中、低‘不同等级的资源，从而实现降低面片渲染的负担。无论我们使用传统的骨骼动画实时计算网格顶点的方式，还是通过 GPU Instancing 来提高动画性能，LOD都可以做到让性能再次提升。&lt;/p&gt;

&lt;p&gt;我们再一次回到基础知识的原点，传统骨骼动画计算的网格的变化是由，骨骼点与顶点的权重数据计算得到的。也就是说，顶点数量越多骨骼数量越多有效权重数据越多，CPU消耗的也就越多，CPU的消耗与这三者任何一个都成正比。反过来也是一样，顶点越少骨骼数越少有效权重数据越少，CPU的消耗的就越少。&lt;/p&gt;

&lt;p&gt;不过我们仍然要注意的是，顶点数少了模型就不那么精细了，而骨骼数少了动画就补那么丰富了，而有效权重数据少了网格变化就不那么细腻了。因此我们需要对高、中、低设备进行判断，对于低阶设备则使用低档资源以保证性能优先画面流畅，对于高阶设备则使用高档资源以丰富画面。&lt;/p&gt;

&lt;p&gt;当然高、中、低资源都需要我们离线制作，完成后在场景使用前选择并加载进来，如果使用传统的骨骼动画，则我们需要准备3套模型3套骨骼动画，如果使用 GPU Instancing 则使用需要3套模型3套动画数据贴图。&lt;/p&gt;

&lt;p&gt;很多情况下场景内的静态物体都可以使用LOD来实时切换模型精细度等级，那么实时切换动画的高、中、低资源是否可取。传统的实时LOD(Level of Detail)用远近的视觉差来优化性能开销，用内存来换取CPU。LOD的视觉差是利用离摄像机太远的东西精细度无法分辨，这使得替换成更粗糙的模型，在距离远的情况下效果差别很小。&lt;/p&gt;

&lt;p&gt;在实时切换动画LOD时，我们可以这么做：&lt;/p&gt;

&lt;h6 id=&quot;第一每套模型都分别有高中低三套模型与骨骼动画在渲染时判断摄像机与物体的距离来决定使用哪一套模型&quot;&gt;第一，每套模型都分别有高、中、低三套模型与骨骼动画，在渲染时判断摄像机与物体的距离来决定使用哪一套模型。&lt;/h6&gt;

&lt;h6 id=&quot;第二当切换模型时把被切换的资源隐藏将新的资源展示出来并初始化初始化时要把当前的动画状态还原到新的lod动画模型上以保证动画无缝切换&quot;&gt;第二，当切换模型时，把被切换的资源隐藏，将新的资源展示出来并初始化，初始化时要把当前的动画状态还原到新的LOD动画模型上，以保证动画无缝切换。&lt;/h6&gt;

&lt;h6 id=&quot;第三当前动画状态一致还不够还需要还原动画融合的表现以使得切换前后表现一致这可能是一个大的麻烦我们需要先将动画切换到前置的动画再对后置动画进行融合&quot;&gt;第三，当前动画状态一致还不够，还需要还原动画融合的表现，以使得切换前后表现一致，这可能是一个大的麻烦，我们需要先将动画切换到前置的动画再对后置动画进行融合。&lt;/h6&gt;

&lt;p&gt;这样看来LOD也可以用的很极致，LOD不只为了静态模型服务的，也同样可以为动画模型服务，虽然我们并没有用LOD降低任何Drawcall，但它仍然能降低了很大的CPU开销，和降低Drawcall相比也有着异曲同工之妙。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第五章，3D模型与动画(四) - 3D模型的变与换3</title>
   <link href="http://www.luzexi.com/2019/05/12/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-3D%E6%A8%A1%E5%9E%8B%E4%B8%8E%E5%8A%A8%E7%94%BB6"/>
   <updated>2019-05-12T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/05/12/Unity3D高级编程之进阶主程-3D模型与动画6</id>
   <content type="html">&lt;p&gt;上篇我们了解了简化模型的算法，以及普通网格和蒙皮网格的区别，其中蒙皮网格是专门用来播放动画的，它除了普通网格所需要的顶点，三角形，uv数据外，还需要骨骼数据，以及每个顶点的骨骼权重数据。在骨骼动画中骨骼数据是主要由骨骼点组成的，每个骨骼点之间的关系，它们都是父子关系，或者是平行的兄弟节点的关系，这些骨骼点可以在 Unity3D 中对应到模型上的 GameObject 点。在这种骨骼数据结构下，当父节的骨骼点位移，旋转以及缩放时，子节点的骨骼也同时相对于父节点有位移和旋转操作。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;那么骨骼点是怎么影响到顶点的呢，又是如何判断影响哪些顶点的呢？其实骨骼动画中，需要顶点的骨骼权重数据来决定顶点受哪些骨骼影响，每个顶点都可以受到骨骼点的影响，在Unity3D中每个顶点最多被4根骨骼影响，这些数据被存储在一个BoneWeight实例里，实例中描述了当前这个顶点对分别哪4个骨骼影响，它们分别有多少权重。因为是固定4个权重且每个顶点都会有，所以顶点数组有多长，顶点权重数据BoneWeight数组就有多长。当骨骼点移动时，引擎就会使用这些顶点权重值来计算顶点的旋转、偏移量和缩放程度。&lt;/p&gt;

&lt;h6 id=&quot;简单来说就是用顶点上的骨骼权重数据确定了该点受哪些骨骼点影响影响的程度有多少&quot;&gt;简单来说就是，用顶点上的骨骼权重数据确定了该点受哪些骨骼点影响，影响的程度有多少。&lt;/h6&gt;

&lt;p&gt;我们在制作蒙皮动画时通常分三步：第一步是用工具3DMax，Maya等3D模型软件在几何模型上建立一系列的骨骼点(bones)，并计算好几何模型的每个顶点受这些骨骼点的影响的权重值(BoneWeight)。第二步则是动画师通过3D模型软件制作一系列的动画，这些动画都是通过骨骼点的偏移、旋转、缩放来完成的，每一帧都有可能有变化，包括关键帧与关键帧之间会补间一些非关键帧的动画，制作完毕后导出引擎专有的动画文件格式，我们在Unity3D中以FBX格式文件专有格式文件。接着第三步则是在Unity3D中导入并播放动画，在动画播放时在动画数据中已经存储了动画师制作的骨骼点位每帧变化的情况，动画序列帧会根据每帧的动画数据持续改变一系列骨骼点，骨骼点的改变又导致了几何模型网格上的顶点产生相应的变化。&lt;/p&gt;

&lt;p&gt;通常我们使用的都是关键帧动画，就是Unity3D里的Animation动画文件，在某个时间点上对需要改变的骨骼做关键帧，而并不是在每帧上都做关键帧的操作。使用关键帧作为骨骼的旋转位移点的好处是不需要每帧去设置骨骼点的位置变化，在关键帧与关键帧之间的骨骼位置，可以由Animation动画组件做平滑插值计算相应数据，这样可以大量减少数据大小，相当于关键帧之间做了‘补间动画’，‘补间动画’的目的就是对需要改变的骨骼做平滑的位移、旋转、缩放的插值计算，从而实时地得到相应的结果减少数据使用量。&lt;/p&gt;

&lt;h6 id=&quot;补间动画在每帧都对骨骼动画做了位置缩放旋转的改变然后蒙皮网格组件skinnedmeshrender在每帧必须重新计算骨骼与网格的关系最终补间动画每帧改变一些列骨骼点骨骼点被skinnedmeshrender重新计算得到模型网格变化从而每帧就呈现出不同的网格变化最后有了3d模型网格动画&quot;&gt;’补间动画‘在每帧都对骨骼动画做了位置、缩放、旋转的改变，然后蒙皮网格组件(SkinnedMeshRender)在每帧必须重新计算骨骼与网格的关系。最终‘补间动画’每帧改变一些列骨骼点，骨骼点被(SkinnedMeshRender)重新计算得到模型网格变化，从而每帧就呈现出不同的网格变化，最后有了3D模型网格动画。&lt;/h6&gt;

&lt;p&gt;整个骨骼动画的计算流程清晰了，我们来用Unity3D的API来整理下，可以让我们理解的更加透彻。举例代码和解释如下，重点在注释的解释：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;//新建个动画组件和蒙皮组件
gameObject.AddComponent&amp;lt;Animation&amp;gt;();
gameObject.AddComponent&amp;lt;SkinnedMeshRenderer&amp;gt;();
SkinnedMeshRenderer rend = GetComponent&amp;lt;SkinnedMeshRenderer&amp;gt;();
Animation anim = GetComponent&amp;lt;Animation&amp;gt;();

//新建个网格组件，并编入4个顶点形成一个矩形形状的网格
Mesh mesh = new Mesh();
mesh.vertices = new Vector3[] {new Vector3(-1, 0, 0), new Vector3(1, 0, 0), new Vector3(-1, 5, 0), new Vector3(1, 5, 0)};
mesh.uv = new Vector2[] {new Vector2(0, 0), new Vector2(1, 0), new Vector2(0, 1), new Vector2(1, 1)};
mesh.triangles = new int[] {0, 1, 2, 1, 3, 2};
mesh.RecalculateNormals();

//新建个漫反射的材质球
rend.material = new Material(Shader.Find(&quot;Diffuse&quot;));

//为每个顶点定制相应的骨骼权重
BoneWeight[] weights = new BoneWeight[4];
weights[0].boneIndex0 = 0;
weights[0].weight0 = 1;
weights[1].boneIndex0 = 0;
weights[1].weight0 = 1;
weights[2].boneIndex0 = 1;
weights[2].weight0 = 1;
weights[3].boneIndex0 = 1;
weights[3].weight0 = 1;

//把骨骼权重赋值给网格组件
mesh.boneWeights = weights;

//创建新的骨骼点，设置骨骼点的位置，父节点，和位移旋转矩阵
Transform[] bones = new Transform[2];
Matrix4x4[] bindPoses = new Matrix4x4[2];

bones[0] = new GameObject(&quot;Lower&quot;).transform;
bones[0].parent = transform;
bones[0].localRotation = Quaternion.identity;
bones[0].localPosition = Vector3.zero;
bindPoses[0] = bones[0].worldToLocalMatrix * transform.localToWorldMatrix;

bones[1] = new GameObject(&quot;Upper&quot;).transform;
bones[1].parent = transform;
bones[1].localRotation = Quaternion.identity;
bones[1].localPosition = new Vector3(0, 5, 0);
bindPoses[1] = bones[1].worldToLocalMatrix * transform.localToWorldMatrix;

mesh.bindposes = bindPoses;

//把骨骼点和网格赋值给蒙皮组件
rend.bones = bones;
rend.sharedMesh = mesh;

//定制几个关键帧
AnimationCurve curve = new AnimationCurve();
curve.keys = new Keyframe[] {new Keyframe(0, 0, 0, 0), new Keyframe(1, 3, 0, 0), new Keyframe(2, 0.0F, 0, 0)};

//创建帧动画
AnimationClip clip = new AnimationClip();
clip.SetCurve(&quot;Lower&quot;, typeof(Transform), &quot;m_LocalPosition.z&quot;, curve);

//把帧动画赋值给动画组件，并播放动画
anim.AddClip(clip, &quot;test&quot;);
anim.Play(&quot;test&quot;);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;以上的Unity3D代码就呈现了，几何模型数据，蒙皮动画数据，从无到有的过程。先添加动画组件和渲染组件，再自行创建一个Mesh实例，放入4个顶点成为一个矩形网格，添加uv和索引数组，计算法线数据，创建一个新的材质球并存入渲染组件。然后是对动画数据的创建，创建并设置骨骼权重数据，创建骨骼并设置空间矩阵。最后创建动画数据，包括关键帧数据，动画曲线，改变的节点名称，再加入到动画组件中去。&lt;/p&gt;

&lt;p&gt;代码的大部分内容都简单易懂，除了bindPoses这个骨骼点的矩阵。我们前面提到骨骼点数据中，除了坐标还有矩阵，其实并没有什么节点之间的连线一说，都是我们为了便于理解而想象出来的，子节点在父节点的空间内，父节点的改变能带动子节点的改变，为了能更好的计算出在变动后子节点的位置，节点上的4x4矩阵就很好的发挥了重要作用。由于4x4矩阵能完整的表达点位的偏移、缩放、旋转的操作，也能以通过连续右乘法计算出从根节点到父节点再到子节点上的具体方位，因此4x4矩阵是骨骼点必要的数据，它表达了相对空间的偏移量。即&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	骨骼节点变化矩阵 =  根节点矩阵 * 父父父节点矩阵1 * 父父节点 * 父节点矩阵 * 骨骼节点矩阵
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们再返回去看在蒙皮动画第一步中权重的计算决定了蒙皮算法的效果，如果想要几何模型发生自然、高质量的形变，必须得有一种高效准确的权重计算方法。这里简单讲一下蒙皮的计算方式，以了解下其中的计算原理。&lt;/p&gt;

&lt;p&gt;线性混合蒙皮（Linear Blending Skinning，LBS)是最最常用的蒙皮计算方式，由于它的计算速度优势使得其成为商业应用中最主要的方法之一。什么是线性混合蒙皮计算方式呢？简单说就是&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	骨骼点变化坐标 = (骨骼点变化矩阵*(顶点坐标 - 骨骼点原坐标) + 骨骼点变化后坐标)

	当前顶点位置 =  骨骼点1变化坐标 * 骨骼权重1 + 骨骼点2变化坐标 * 骨骼权重2 ....
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;如果直接使用这种线性混合计算蒙皮的方式效果有点粗糙，为了更好的效果。[Jacobson et al. 2011]提出了一种有界双调和权重（Bounded Biharmonic Weights，BBW）的计算方法，该权重能使得几何模型发生平滑变形，这个算法后来就成为了我们现在最常使用的骨骼蒙皮动画的计算方式。&lt;/p&gt;

&lt;p&gt;总结他的意思就是说，既然网格数据的变化计算量大，线性混合计算的速度又是最快的，我们可以在线性计算的基础上加以改进。在线性蒙皮混合计算公式中，初始位位置无法改变，骨骼点的变化也无法改变，所以权重计算骨骼点的变化的量决定了最终效果是否好的关键。于是他就提出了，有界双调和权重的计算方法，其数学表达式如下：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/5/bone-bbw.jpg&quot; alt=&quot;有界双调和权重的计算方法&quot; /&gt;&lt;/p&gt;

&lt;p&gt;没看懂没关系其实我也是不懂，这方面也不是我的强项，故意点点头继续看下一个知识点，至少我们知道了它能更平滑的表达骨骼点影响顶点的变化。&lt;/p&gt;

&lt;h6 id=&quot;前面说了这么多我们来总结下骨骼点结构是父子关系的层级结构每个骨骼点都由坐标和空间矩阵数据组成每次计算都可以通过矩阵连乘得到顶点最终的坐标计算通过多个骨骼点的权重和偏移量来决定这样看来由于蒙皮动画是每帧都通过骨骼点来计算网格的变化的如果骨骼点越多网格越复杂顶点或者面数很多那么消耗的cpu就很多因为网格里的顶点都需要通过蒙皮算法来算出顶点的变化一般情况下这些都是靠cpu来计算的因此在制作模型动画的时候特别要注意同屏里有多少蒙皮动画在播放以及每个蒙皮动画中骨骼的数量有多少网格的面数有多复杂如果太多太复杂就会巨量的消耗cpu&quot;&gt;前面说了这么多，我们来总结下，骨骼点结构是父子关系的层级结构，每个骨骼点都由坐标和空间矩阵数据组成，每次计算都可以通过矩阵连乘得到，顶点最终的坐标计算通过多个骨骼点的权重和偏移量来决定。这样看来，由于蒙皮动画是每帧都通过骨骼点来计算网格的变化的，如果骨骼点越多，网格越复杂(顶点或者面数很多)那么消耗的CPU就很多，因为网格里的顶点都需要通过蒙皮算法来算出顶点的变化，一般情况下这些都是靠CPU来计算的。因此在制作模型动画的时候，特别要注意，同屏里有多少蒙皮动画在播放，以及每个蒙皮动画中，骨骼的数量有多少，网格的面数有多复杂，如果太多太复杂就会巨量的消耗CPU。&lt;/h6&gt;

&lt;h3 id=&quot;人物3d模型动画换皮换装&quot;&gt;人物3D模型动画换皮换装&lt;/h3&gt;

&lt;p&gt;有了上面的这些3D模型和骨骼动画的知识，我们在3D模型动画换装这种常见的游戏功能的编码设计上就显得简单的多了。&lt;/p&gt;

&lt;h6 id=&quot;首先为了达到模型动画的动态拼接我们必须一个人物的所有动画和部件都只使用同一套骨骼&quot;&gt;首先，为了达到模型动画的动态拼接，我们必须一个人物的所有动画和部件都只使用同一套骨骼。&lt;/h6&gt;

&lt;p&gt;骨骼点的移动影响网格顶点，更换了模型的部分网格可以，但骨骼点是不能更换的否则骨骼点对顶点的权重影响就不对了动画就乱了套了，因此如果要一个人物不断更换局部模型后还能有一样的动画效果，那么骨骼点必须是同一套。&lt;/p&gt;

&lt;h6 id=&quot;其次把骨骼和模型部件拆分开来骨骼文件只有骨骼数据每个部件的模型文件只包含了它自己的模型的顶点数据同时它也必须包含了顶点上的骨骼权重数据&quot;&gt;其次，把骨骼和模型部件拆分开来，骨骼文件只有骨骼数据，每个部件的模型文件只包含了它自己的模型的顶点数据，同时它也必须包含了顶点上的骨骼权重数据。&lt;/h6&gt;

&lt;p&gt;用Unity3D的术语来说就是，把一个人物模型拆分成有很多个Fbx，其中一个Fbx只有骨骼数据，其他Fbx是每个部件的模型数据，它们都带有已经计算好的骨骼权重数据。这样更便于更换和拼凑模型，每套部件都可以玩家自己拼凑，而骨骼点不变，以及每个部件上模型的顶点数据也始终映射到这一套骨骼上。每次在更换部件时，只要把原来的部分删除，更换成新的部件即可，其他数据依然有效，这便是拆分骨骼与模型部件的好处。&lt;/p&gt;

&lt;h6 id=&quot;然后把骨骼数据和模型都实时的动态拼接起来&quot;&gt;然后，把骨骼数据和模型都实时的动态拼接起来。&lt;/h6&gt;

&lt;p&gt;将骨骼Fbx模型数据实例化后成为了一个SkinnedMeshRenderer，这样基础的骨骼数据就包含在这个实例里，接着再把需要展示的各个部件Fbx模型实例化出来，它们拥有自己的SkinnedMeshRenderer，最后将骨骼信息从前面骨骼SkinnedMeshRenderer里取出来赋值给他们，包括所有骨骼节点以及变换矩阵。&lt;/p&gt;

&lt;p&gt;这样每个部件都进行了SkinnedMeshRenderer实例化，SkinnedMeshRenderer可以渲染出自己的模型效果，并且每个部件自己的SkinnedMeshRenderer都有骨骼数据，由于原本每个部件模型上也都一直存有骨骼的权重数据，这使得每个模型部件针对骨骼动画是有效的。&lt;/p&gt;

&lt;h6 id=&quot;接着在骨骼的skinnedmeshrenderer上挂上animator来播放动画文件动画文件里的数据改变的是骨骼点当动画播放时骨骼点会针对动画关键帧进行位移由于部件模型的骨骼数据都是从骨骼的skinnedmeshrenderer上映射过来的所以当骨骼点动起来时就能带动众多的模型网格上的顶点一起动起来&quot;&gt;接着在骨骼的SkinnedMeshRenderer上挂上Animator来播放动画文件，动画文件里的数据改变的是骨骼点，当动画播放时骨骼点会针对动画关键帧进行位移，由于部件模型的骨骼数据都是从骨骼的SkinnedMeshRenderer上映射过来的，所以当骨骼点动起来时就能带动众多的模型网格上的顶点一起动起来。&lt;/h6&gt;

&lt;p&gt;当骨骼动画的SkinnedMeshRenderer上的动画文件开始播放时，每个部件模型上的顶点也会随着骨骼点的变动而不断的计算出网格模型的变动情况，进而在渲染上体现出部件模型的动画效果。这是由于动画播放时顶点偏移是由顶点上的骨骼权重数据决定的，如果骨骼权重数据没有问题，对应的骨骼点也没有被替换或删除，那么这就表明模型在动画表现上所对应数据的对应关系都是正确，因此他们所展现出来的动画效果也是正确的。&lt;/p&gt;

&lt;h6 id=&quot;最后当我们需要更换人物上的某个部件模型时只需要把原有的部件模型实例删除再实例化出那个我们需要的部件模型并把骨骼数据赋值给它就完成了操作更换的操作很简单从表现上看就是更换了人物的某个部件脸或腿或手或腰或脚&quot;&gt;最后，当我们需要更换人物上的某个部件模型时，只需要把原有的部件模型实例删除，再实例化出那个我们需要的部件模型，并把骨骼数据赋值给它就完成了操作。更换的操作很简单，从表现上看就是更换了人物的某个部件，脸，或腿，或手，或腰，或脚。&lt;/h6&gt;

&lt;p&gt;这种方式虽然是最简单的更换部件的方式，但它由个缺点就是需要的Drawcall比较多，由于人物拆分成了5个部件，头，手，身体，腿，脚，这样我们就需要6个SkinnedMeshRenderer来支撑渲染，除了5个部件模型外还需要1个为骨骼动画的SkinnedMeshRenderer，其他5个为部件模型的SkinnedMeshRenderer，这从效率上看上去很不友好，也就是说一个人物要至少5个Drawcall来支撑。骨骼动画已经很消耗性能了，还需要5个材质球去消耗5个drawcall，加重了性能消耗，一旦人物在场景中过多就会拖慢帧率。&lt;/p&gt;

&lt;p&gt;我们有更好的办法，我们希望一个人物动画只使用一个drawcall，那么我们就需要把这5个部件合并成一个模型，他们都使用同一个材质球，模型合并好办，使用Unity3D的Mesh.CombineMeshes就可以实现。&lt;/p&gt;

&lt;p&gt;那么贴图怎么办？也同样合并。在每次初始化拼接一个人物模型时，或者更换人物的部件模型时，将5张贴图动态的合并成一张，并在合并贴图的同时需要改变每个模型部件的uv，将他们的uv偏移到这张合并整图的某个范围内。&lt;/p&gt;

&lt;p&gt;这样一来，每个人物模型只需要消耗1个drawcall，减轻了gpu的负担。从CPU消耗来看，拼接的操作只存在于人物初始化，和更换部件模型时才会有消耗，因此合并贴图和模型的实际消耗cpu的量并不多。不过所有这些合并的前提是，模型部件都可以使用同一个材质球。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(二十四) 要勇敢的相信自己</title>
   <link href="http://www.luzexi.com/2019/05/01/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A824"/>
   <updated>2019-05-01T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/05/01/思路探讨24</id>
   <content type="html">&lt;p&gt;你是很厉害的人物，就像是无所不能的一样，但凡你接触到的都能成功搞定。&lt;/p&gt;

&lt;p&gt;我很确信的知道所有看我文章的读者都是很聪明的人，我认为你们就应该这么认为自己。&lt;/p&gt;

&lt;p&gt;我为什么要这么认为？&lt;/p&gt;

&lt;p&gt;因为我知道人与人之间的区别就是思想上的区别。你在想什么，怎么想的，决定了你的现在和你的未来。&lt;/p&gt;

&lt;p&gt;就比如现在我就是这么认为，你们是很聪明的，很棒的，很有能力的一群人，我认为读我的文章的人，都是这个世界上很厉害的人，无论你们来自哪个地方，都那个地方的佼佼者。&lt;/p&gt;

&lt;p&gt;这也同时映射出我是很棒的，是聪明的，是厉害的人。&lt;/p&gt;

&lt;p&gt;我不是反夸自己，因为我相信自己是个厉害的人物，是个聪明的人，是个很棒的人，不论现在是不是，未来一定是。在变成未来那个很厉害的人之前，所有阻碍和困哪，都不值得去太在意。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;h6 id=&quot;我只关注那个未来画面里的我要成为的那个厉害的人我只关注我是如何变成那个人的这个过程只关注我要如何可以更快更好的变成未来那个人的方法只关注我有没有按照变成未来那个人的方向去行动&quot;&gt;我只关注那个未来画面里的我要成为的那个厉害的人，我只关注我是如何变成那个人的这个过程，只关注我要如何可以更快更好的变成未来那个人的方法，只关注我有没有按照变成未来那个人的方向去行动。&lt;/h6&gt;

&lt;h6 id=&quot;除了上面这些其他的一切都不值得我在意因为我很坚信我能成为我心中的那个很棒的人钱没了就没了再去赚下次小心点贵重物丢了就丢了再买一个下次小心点事情失败了就失败了再来一次下次小心点事情变遭了尽力修复下次小心点&quot;&gt;除了上面这些，其他的一切都不值得我在意，因为我很坚信我能成为我心中的那个很棒的人。钱没了就没了，再去赚，下次小心点，贵重物丢了就丢了，再买一个，下次小心点，事情失败了就失败了，再来一次，下次小心点，事情变遭了，尽力修复，下次小心点。&lt;/h6&gt;

&lt;p&gt;你看，我并没有把注意力聚焦在那些阻碍我发展的事情上，而是把所有的注意力都集中在了我相信的那个事情上。其他所有的阻碍、困难、失败，都被我轻描淡写的掠过了，而我则在这些苦难后还在继续向前走。&lt;/p&gt;

&lt;p&gt;这就不就是，我们生活中一直幻想着的那个，聪明的，冷静的，有远见的，不斤斤计较的，充满智慧的人么。我们每天都在期盼有一个这样厉害的家伙成为自己的合作伙伴。&lt;/p&gt;

&lt;p&gt;成为这样的人其实很简单，只要你想，就能成。&lt;/p&gt;

&lt;h6 id=&quot;是的只要你把注意力集中在你想的那个东西上其他事物都是次要的如果我们把注意集中在未来的美好事物上我们就能在当下成为一个聪明智慧的人并且在未来成为画面里那个很棒的人&quot;&gt;是的，只要你把注意力集中在你想的那个东西上，其他事物都是次要的。如果我们把注意集中在未来的美好事物上，我们就能在当下成为一个聪明智慧的人，并且在未来成为画面里那个很棒的人。&lt;/h6&gt;

&lt;p&gt;不知道你有没有和我有同样的体会：回忆过去的时候，发现，很多很多事情都是因为我们不相信，所以才没有成，也有很多很多事情只是因为我们相信我们能成才成就的。我们能否做成一件事，全靠我们有多少的相信（即我相信的力度），我‘有点’相信，这个事情成功的概率就有是‘有点’，如果我‘非常’相信，那么这个事情的成功率就有‘非常’高。&lt;/p&gt;

&lt;p&gt;我相信我今天存进银行的钱，明天就能取出来，那么明天确实能取出来。&lt;/p&gt;

&lt;p&gt;如果我不相信我今天存进银行的钱能取出来，那么明天真的有可能取不出来。（如果有一天社会动荡到你完全不相信存进银行的钱是安全的时候，明天银行就会因挤兑而倒闭）&lt;/p&gt;

&lt;p&gt;我不是神棍，而是告诉大家，让人相信某件事是很难的，让人不相信某件事业也同样很难。就比如我如何让你不相信，存进银行的钱，明天取不出来，这件事，很难。因为现在的社会环境让你非常相信，存进银行的钱，明天就能取出来，我是很难改变你的想法的。就像我无法让你相信，你以后会竞选总理并且获胜一样，很难让你有这种相信的想法。但我能让你相信，我的想法是对，并且对你很有用，这个相对容易点，但也不是针对每个人都有效。&lt;/p&gt;

&lt;h3 id=&quot;让一个人相信一件事或者让一个人不相信一件事都非常难概括可以说要改变一个人的想法非常难&quot;&gt;让一个人相信一件事，或者让一个人不相信一件事都非常难，概括可以说，要改变一个人的想法非常难。&lt;/h3&gt;

&lt;p&gt;我想了很久，关于人与人之间的区别到底在哪里的问题。&lt;/p&gt;

&lt;p&gt;是钱么？&lt;/p&gt;

&lt;p&gt;如果是钱，那么为什么有钱的人也同样迷茫，也同样冲动，也同样不知所措，同样犯与没钱的人一样的错误，也同样无知。&lt;/p&gt;

&lt;p&gt;是知识么？&lt;/p&gt;

&lt;p&gt;如果是知识，那么为什么博士以及博士后不是在掌控世界，理论上说他们的知识量是最大的，为什么依旧与普通人一样，迷茫，冲动，不知所措，也同样表现的无知。&lt;/p&gt;

&lt;p&gt;其实都不是。人与人之间的区别就是思维，和，想法。你怎么想的，决定了你怎么做，你想的是什么，决定了你最终会成为什么样人。&lt;/p&gt;

&lt;p&gt;如果你每天想着，我基因不好，我老是身体不好，我总是生病，我比较笨，我不会运动，我很穷，我被穷困所限制，老是有人和我作对，我的人生是苦难的，我有才但始终没有伯乐，我每次都被别人抛弃，我在变老，我怕我会破产，我总是迟到，我总是经不住诱惑，别人老是来占我便宜。等等等等。&lt;/p&gt;

&lt;p&gt;那么你就会成为你所想的，你的基因一定不好，你的身体肯定会坏，你肯定总是生病，你肯定比较笨，你肯定不擅长运动，你肯定富不起来，你的人生肯定是苦难的，你肯定遇不到伯乐，你肯定每次都被抛弃，你肯定老的很快，你肯定破产，你肯定迟到…..&lt;/p&gt;

&lt;p&gt;你想什么，就是你希望得到的，事情就会往这个方向发展，最终就会实现你所想。&lt;/p&gt;

&lt;p&gt;反之，如果我们的想法能更加积极点，乐观点，美好点，光明点，那么事情就会向更加积极，乐观，美好，光明的方向去走。&lt;/p&gt;

&lt;p&gt;如果我每天都认为，我的人生是完美的，我拥有很好的基因，我身体很棒，我免疫力很好，我很会运动，我总是很幸运，我总能遇到伯乐，我总是能和大家搞好关系，我永远都是那么年轻，我会越来越富有，我总是很准时的赴约，我总是很理智，别人都很喜欢跟我合作，我的知识会越来越渊博，我很智慧，我总是很受人照顾，我会很厉害。等等等等。&lt;/p&gt;

&lt;p&gt;那么事情就会真的向我想象的方向发展，我的基因很完美，我很健康，我很理智，我总是很受欢迎，我总是能遇到伯乐，我变得越来越富有，我很幸运，我看上去一直很年轻。&lt;/p&gt;

&lt;h6 id=&quot;难道说真的有什么外在因素在决定这些事情的发生吗并没有原因在人的内心我怎么想决定了事情怎么走&quot;&gt;难道说真的有什么外在因素在决定这些事情的发生吗？并没有，原因在人的内心：我怎么想，决定了事情怎么走。&lt;/h6&gt;

&lt;h3 id=&quot;人与人之间最大的区别就在这里可以用很简单的一句话概括你相信什么信则有不信则无&quot;&gt;人与人之间最大的区别就在这里，可以用很简单的一句话概括：你相信什么？信则有，不信则无。&lt;/h3&gt;

&lt;h3 id=&quot;你所相信的就是你能得到的同样的你所不相信的就是你得不到的&quot;&gt;你所相信的，就是你能得到的。同样的，你所不相信的，就是你得不到的。&lt;/h3&gt;

&lt;h3 id=&quot;无论你遭遇什么境况都要勇敢的相信自己我是健康的人我是幸运的人我是厉害的人我是受欢迎的人我是幸福的人&quot;&gt;无论你遭遇什么境况，都要勇敢的相信自己，我是健康的人，我是幸运的人，我是厉害的人，我是受欢迎的人，我是幸福的人。&lt;/h3&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第五章，3D模型与动画(四) - 3D模型的变与换2</title>
   <link href="http://www.luzexi.com/2019/04/27/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-3D%E6%A8%A1%E5%9E%8B%E4%B8%8E%E5%8A%A8%E7%94%BB5"/>
   <updated>2019-04-27T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/04/27/Unity3D高级编程之进阶主程-3D模型与动画5</id>
   <content type="html">&lt;p&gt;上一篇介绍了一些3D模型的渲染基础知识，并且简单分析了用这些基础知识在3D模型的切割、拉伸、扭曲当中的运用技巧。&lt;/p&gt;

&lt;h6 id=&quot;技术原理是重要的核心点如果我们理解了技术原理就能将技术表象解释清楚在实际项目中才能将这种技术运用自如&quot;&gt;技术原理是重要的核心点，如果我们理解了技术原理，就能将技术表象解释清楚，在实际项目中才能将这种技术运用自如。&lt;/h6&gt;

&lt;h6 id=&quot;现实中也并没有这么容易理解和运用自如之间还是有一段很大的差距在实际项目过程中理解只是理解只是能解释这种表象是如何发生的运用自如则是熟知了原理的优缺点不再对这种技术有任何的偏见能做什么不能做什么做什么有优势有什么有劣势有了更加深入的理解当没有了这么多的偏见运用起来才能得心应手&quot;&gt;现实中也并没有这么容易，理解和运用自如之间，还是有一段很大的差距。在实际项目过程中，理解只是理解，只是能解释这种表象是如何发生的，运用自如，则是熟知了原理的优缺点，不再对这种技术有任何的偏见，能做什么不能做什么，做什么有优势，有什么有劣势，有了更加深入的理解，当没有了这么多的偏见，运用起来才能得心应手。&lt;/h6&gt;

&lt;h6 id=&quot;理论上说我们每个人都应该把所有精力先放在理解原理上然后再去做技术实现不就轻松了吗理论上是这样的但人类头脑的进化毕竟还没到那么高级的程度还不能轻松的把抽象的事物短时间内在脑中有条不紊的整理清楚这还没把人的情绪影响算进去人性毕竟是弱点多于优点的虽然表面上能处理一些看上去相对复杂的事物但从学习知识的角度看人类的大脑还是不够高级说实话应该说是有点低级&quot;&gt;理论上说，我们每个人都应该把所有精力先放在理解原理上，然后再去做技术实现不就轻松了吗。理论上是这样的，但人类头脑的进化毕竟还没到那么高级的程度，还不能轻松的把抽象的事物短时间内在脑中有条不紊的整理清楚，这还没把人的情绪影响算进去，人性毕竟是弱点多于优点的，虽然表面上能处理一些看上去相对复杂的事物，但从学习知识的角度看，人类的大脑还是不够高级，说实话应该说是有点低级。&lt;/h6&gt;

&lt;h6 id=&quot;因此对于这么低级的大脑我们只能通过反复的学习然后实践再学习再实践很多遍后才能对原理的理解推进那么一点点我们也只能依靠长时间的漫长时间的磨练从有兴趣到放弃再重新拿起后又放下反反复复但始终不离弃才能最终到达运用自如的境界其实人之间没有聪明与不聪明之分有的人善于思考善于举一反三是因为前面很长一段时间他都一直在练习这种多思考和举一反三的方式当我们看到他时他的思维方式已经成型这些并不是先天就有而是通过后天努力得来天才这东西不管它是否真的存在我们都应该忽略它转而更加专注于自己的努力上&quot;&gt;因此对于这么低级的大脑，我们只能通过反复的学习然后实践，再学习再实践，很多遍后才能对原理的理解推进那么一点点，我们也只能依靠长时间的、漫长时间的磨练，从有兴趣到放弃，再重新拿起后又放下，反反复复但始终不离弃，才能最终到达运用自如的境界。其实人之间没有聪明与不聪明之分，有的人善于思考，善于举一反三是因为前面很长一段时间他都一直在练习这种多思考和举一反三的方式，当我们看到他时他的思维方式已经成型，这些并不是先天就有，而是通过后天努力得来。天才这东西，不管它是否真的存在，我们都应该忽略它，转而更加专注于自己的努力上。&lt;/h6&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;这篇我们来讲一讲模型简化和蒙皮动画。&lt;/p&gt;

&lt;h3 id=&quot;简化模型&quot;&gt;简化模型&lt;/h3&gt;

&lt;p&gt;经常在项目中看到有模型有几万个面的情况，模型面数多的好处是能表现的更加精细画面更细腻，坏处却是加重了渲染压力，渲染的面数越多压力越重，帧率就会越低。所以一般都会对场景中的单个3D模型进行限制，或者对整体场景面数进行限制，前面我们在美术资源规范中讲述了规范的方法这里不做重述。&lt;/p&gt;

&lt;p&gt;画面质量和性能需要权衡，通常都是要求模型降低面数而画面质量不变，LOD(Level Of Detail)经常可以在场景模型的质量与性能平衡中发挥巨大的作用，它的原理是随着镜头的靠近模型物体精细度会逐级更换更细腻的模型。&lt;/p&gt;

&lt;p&gt;简化模型是LOD比较常用的方法，我们可以手动用3D模型软件简化每个模型，也可以用程序的方式简化模型。在实际的项目中，手动简化更加平滑但费时间，很多时候时间成本过大而选用程序工具去简化，因为用程序简化更加快速，缺点是不平滑。我们可以根据项目的需求，规模，画质等要求来权衡是否使用程序工具去简化模型，或者更加灵活点，一部分不太需要细致的模型使用程序工具简化，而另一部分比较需要细节化的模型使用手动简化，这样即照顾到了画质，又照顾到了工期。&lt;/p&gt;

&lt;p&gt;这里我们来具体讲述下简化模型的算法。虽然市面上简化模型的插件和工具很多，但如果我们对原理有了更深入了解，在实际的项目中运用这些工具会更加自如且得心应手。&lt;/p&gt;

&lt;p&gt;模型网格是由点，线，面组成。面由点和线组成，减面相当于减点和线，单纯的减去点和线容易引起模型变化不受控制，收缩线上的两顶点成为一个顶点则更加靠谱些。[Garland et al. 1997]提出了一种基于二次项误差作为度量代价的边收缩算法，其计算速度快并且简化质量较高。&lt;/p&gt;

&lt;p&gt;该方法是去选择一条合适的边进行收缩时，定义一个边的收缩都是有代价的，每个顶点也有自己的代价。为了计算代价，对于网格中的每个顶点v，我们先定义一个4×4的对称误差矩阵Q，那么顶点v=[x y z 1]的代价为其二次项形式Δ(v)=vQ。&lt;/p&gt;

&lt;p&gt;这样同时也定义了边收缩的代价公式，假设对于一条收缩边(v1,v2)，其收缩后v1，v2顶点收缩为v3，我们定义顶点v3的误差矩阵Q3为Q3=Q1 + Q2，也就说是v1，v2的这条边的收缩为v3后代价为Δ(v3) = v3(Q1 + Q2)，以此类推每条边都有一个代价。&lt;/p&gt;

&lt;p&gt;有了上面的代价公式，下面的网格简化算法就容易理解多了：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	1，对所有的初始顶点都计算它们各自的Q矩阵.

	2，选择所有有效的边（这里取的是两点有连线的边，也可以将两点有连线且距离小于某个阈值的边归为有效边）

	3，对每一条有效边(v1,v2)，计算最优收缩目标v3.误差(Q1+Q2)是收缩这条边的代价（cost）

	4，将所有的边按照cost的权值都放在队列中从小到大进行排序。

	5，每次移除队列顶部的代价（cost）最小的边，也就是收缩最小代价的边，删除v1，v2，并用v3替换。

	6，重复1-5步骤，直到顶点数少于某个设定的值，或者所有cost代价大于某个值，则停止收缩算法。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;似乎有点难理解，其实整个算法并不复杂，关键这里有两个核心问题需要解决，一个是每个顶点的初始Q矩阵如何计算，另一个是v1，v2收缩为v3时的坐标位置该怎么计算。在原始网格模型中，每个顶点可以认为是其周围三角片所在平面的交集，也就是这些平面的交点就是顶点位置，因此我们定义顶点的误差为顶点到这些平面的距离平方和。&lt;/p&gt;

&lt;p&gt;由此定义我们可以计算出每个顶点的初始误差矩阵Q：Δ(v)为顶点误差值 = vQ = 0，这里的初始顶点的误差值为0，是因为它最初与相交平面的距离平方和为0，即没有误差，也就是说，Q为v的逆矩阵，于是初始顶点的误差矩阵Q就是v的逆矩阵。&lt;/p&gt;

&lt;p&gt;至于v1，v2收缩为v3时如何选择最优的坐标，简单的方法就是取v1，v2，和中点(v1+v2)/2的三个中收缩代价最小的一个为最优选择，另一种策略则是数值计算顶点v3位置使得Δ(v3)最小，由于Δ的表达式是一个二次项形式，因此令一阶导数为0。&lt;/p&gt;

&lt;p&gt;按照这个算法步骤，不停的收缩最小代价的边，直到顶点数量小于某个值时停止，最终将得到一个简化的模型。&lt;/p&gt;

&lt;h3 id=&quot;蒙皮骨骼动画原理&quot;&gt;蒙皮骨骼动画原理&lt;/h3&gt;

&lt;p&gt;场景中有了3D模型又会有3D模型动画，那么3D模型和3D模型动画之间到底多了哪些数据，这些数据又是怎么起作用的呢，我们来分析下。为了能更直观的了解模型与模型动画的不同，我们以 Unity3D 的 MeshRenderer 和 SkinnedMeshRenderer 这两个组件作为切入点来讲解。&lt;/p&gt;

&lt;p&gt;在Unity3D中，MeshRenderer 与 SkinnedMeshRenderer 这两个组件分别用于渲染 3D模型 和 3D模型动画，他们两个的模型数据都存储在 MeshFilter 中，因此他们都依赖于 MeshFilter 组件。其中 MeshRenderer 只负责渲染模型，我们也可以称它为普通网格渲染组件，它从MeshFilter中提取网格数据顶点数据，而蒙皮网格(SkinnedMeshRenderer)虽然也渲染模型，也从MeshFilter中提取模型网格顶点数据，但蒙皮网格被引擎程序员编写出来主要是为了渲染动画服务的，所以蒙皮网格除了3D模型数据外还有骨骼数据以及顶点权重数据。&lt;/p&gt;

&lt;p&gt;我们前面说过3D模型渲染的数据传递过程，这里 MeshRender 也同样遵循这种规则，即先从 MeshFilter 中取得网格顶点数据、uv数据、颜色数据、法线数据等，结合自己身上的材质球都发送给GPU，其中包含了许多OpenGL的状态设置，在指令最后是一个Drawcall调用告诉GPU按照传送的这些数据渲染(见渲染管线与图形学章节中详解讲解内容)。&lt;/p&gt;

&lt;p&gt;蒙皮网格(SkinnedMeshRenderer)在渲染时也遵循了和 MeshRender 一样的渲染步骤，如果蒙皮网格上没存储任何骨骼数据，那么它和普通网格MeshRender的作用没有任何区别，渲染的都是没有动画的3D模型。&lt;/p&gt;

&lt;p&gt;有很多人并没有理解骨骼动画的原理，所以在实际项目中对3D模型骨骼动画的运用有很多误区，这里我们有必要阐述一下骨骼动画的原理，以及在Unity3D的SkinnedMeshRenderer上骨骼动画是如何组装和组成的。通过对骨骼动画的原理解剖和对 SkinnedMeshRenderer 的解剖，我们能彻底的明白骨骼动画的计算和渲染其实并不复杂，揭开这层薄薄的面纱后是一片平坦的开阔地。&lt;/p&gt;

&lt;p&gt;我们知道3D模型要做动作，首先是模型网格上的点、线、面要动起来，只有点、线、面动起来了，每帧渲染的时候才能在帧渲染时渲染不同的网格形状，从而才有看起来会动的画面。那么怎么让点，线，面动起来呢？对我们来说有两种方法，一种是用一个算法来改变顶点位置，我们通常叫它为顶点动画，另一种是用骨骼的方式去影响网格顶点，我们叫它为骨骼动画。这两种动画方式都是通过在每一帧里偏移模型网格上的各个顶点，让模型变形从而形成动画的效果的。当每一帧模型网格的形状不一样，播放时就形成了动画，两种方法虽然方式不同，但遵循都是同一个原理。&lt;/p&gt;

&lt;p&gt;起初3D模型动画只有刚性层级式动画(rigid hierarchical animation)，它将整个模型拆分成多个部位，然后按照层级节点的方式安装上去，如下示意图。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	[根结点]
	--[躯干节点]
	----[右上臂]
	------[右上前臂]
	--------[右手]
	----[左上臂]
	------[左前臂]
	--------[左手]
	----[头]
	--[右大腿]
	----[右小腿]
	------[右脚]
	--[左大腿]
	----[左小腿]
	------[左脚]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这样模型以层级的方式布置在节点上，这样当父节点移动、旋转、缩放时子节点也随之而动。这种方式的动画问题很多，其中比较严重的是关节连接位置常产生‘裂缝’，因为它们并不是一个模型衔接而成而是由多个模型拼凑起来的，我们可以想象在节点旋转时模型与模型之间的衔接重合部分产生的问题，无论怎么摆放部件的位置在节点做动画时都会有这样或那样的‘裂缝’出现，因此当时的动画比较不自然。&lt;/p&gt;

&lt;p&gt;除了刚性层级式动画(rigid hierarchical animation)，最初也是用过改变每个顶点的动画，意思为把每个顶点都记录下来每帧都告诉引擎怎么改变顶点位置，这种方式太过暴力也很少使用，但另一个变种叫变形目标动画(morph target animation)常使用在脸部动画技术，它将动画做成几个固定的极端姿势的模型，然后在两个模型之间的每个顶点做线性插值，脸部的动作大约需要50组肌肉驱动，这种复杂细微程度的动画用2个网格顶点之间的线性插值表现再适合不过了。更多时候会使用程序员们编写的顶点走向算法去改变每个顶点的位置，这与今天我们用着色器(Shader)在顶点函数中去改变顶点位置成为动画效果类似，这种方式的动画效果在很多游戏中都有在使用，当然它也已经成为优化骨骼动画性能开销的必要手段，比如草、树在风中的左右摆动，丝带或国旗在空中自然飘动等。&lt;/p&gt;

&lt;p&gt;蒙皮骨骼动画简单易用，它的出现让3D模型的动画效果就变得越来越丰富多彩。骨骼动画数据主要由一些骨骼点和权重数据组成，游戏角色中通常骨骼动画的骨骼数量都不会超过100个，这个数量与动画制作速度有一定关系但更多的是跟性能有关。通过对这些骨骼点操作，在上3DMAX，Maya这样的好用的动画编辑工具，我们能够创造出许许多多丰富多彩的动画效果。骨骼数据是怎么起作用的，下面我们来分析下。&lt;/p&gt;

&lt;p&gt;首先，骨骼动画由骨骼点组成，骨骼点我们可以认为是带有相对空间坐标点的数据实体，每个模型骨骼动画中可以有许多个骨骼点但根节点只有一个，我们在现代手机游戏中每个人物的骨骼动画的数量一般都会在30个左右，PC单机游戏中会更多点到达75个左右。骨骼数量越多，表现出来的动画就会越细腻越有动感，但同时也消耗掉更多的运算量。&lt;/p&gt;

&lt;p&gt;其次，骨骼点是树形结构，一个骨骼可以有很多个子骨骼，子节点存在于父节点的相对空间下，每个子骨骼都与父节点拥有相同的功能，由于子节点在父节点的空间下，因此当父节点移动、旋转、缩放时子节点也随着父节点的一起移动、旋转、缩放，他们的相对位置、相对角度、相对比例不变。这与Unity3D中的 GameObject 的节点相似，父子节点有着相对位置的关系，因此骨骼点在Unity3D中的存在形式是以 Transform 形式存在的，这样我们可以直观的从带有骨骼的模型中看到骨骼点的父子挂载结构。我们在Unity3D的 SkinnedMeshRenderer 组件中就有 bones 这个变量用于存储所有骨骼点，骨骼点的存储形式在 SkinnedMeshRenderer 中就是 Transform 数组形式存在，这可以从 bones 这个变量就是 Transform[] 数组类型上得知。&lt;/p&gt;

&lt;p&gt;另外，一个骨骼点可以影响周围一定范围内的顶点，单一一个顶点也可以受到多个骨骼的影响。其实除了骨骼数据，模型中每个顶点都有对它顶点本身影响的最多4个骨骼的权重值，在Unity3D中对这4个骨骼权重数据做了存储，它们存放在 BoneWeight 这个Struct结构中，每个 SkinMeshRender 类都有一个 boneWeights 数组变量来记录所有顶点的骨骼权重值，对于那些没有骨骼动画的网格就没有这些数据。&lt;/p&gt;

&lt;p&gt;每个顶点都需要有一个BoneWeight 结构实例以确保每个顶点都知道被哪些骨骼点影响，在 BoneWeight 中变量 boneIndex0，boneIndex1，boneIndex2，boneIndex3分别代表被影响的骨骼点的索引值，而weight0，weight1，weight2，weight3则是分别代表被0、1、2、3索引的骨骼点所影响的权重值，权重最大为1，最小为0，所有权重分量之和为1。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;[缺Unity3D的Quality Setting图]
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;如上图所示，在Unity3D中的 Quality setting 图形质量设置中，我们可以看到关于Blend Weights 参数，就是关于一个顶点能被多少骨骼影响的参数选项。其中选项中有，1 Bone，2 Bones，4 Bones，表达的意思为一个顶点能被1个骨骼影响，或者被2个骨骼影响，或者被4个骨骼影响，能被影响的骨骼数越多，CPU消耗在骨骼计算蒙皮的时间越长，消耗量越大。&lt;/p&gt;

&lt;h3 id=&quot;未完篇幅有限下篇继续&quot;&gt;未完，篇幅有限，下篇继续&lt;/h3&gt;
</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第五章，3D模型与动画(四) - 3D模型的变与换1</title>
   <link href="http://www.luzexi.com/2019/04/21/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-3D%E6%A8%A1%E5%9E%8B%E4%B8%8E%E5%8A%A8%E7%94%BB4"/>
   <updated>2019-04-21T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/04/21/Unity3D高级编程之进阶主程-3D模型与动画4</id>
   <content type="html">&lt;p&gt;模型是游戏3D场景中的基础单位，它们变化繁多，这些3D模型除了骨骼动画、顶点动画外，很多时候为了画面效果我们还需要：切割，简化，变形，捏脸，飘动等等。那么它们是怎么实现的呢？我们今天就来讲讲这蕴含其中的技术与原理。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;首先我们需要了解下3D模型的基础知识，基础知识很重要，花里胡哨的技巧在基础知识面前都是万变不离其宗的。&lt;/p&gt;

&lt;p&gt;在模型的世界里，众多的顶点勾画出了一个完整的3D模型，顶点之间的连线组成了三角形或多边形，大部分情况下我们的模型还是以三角形为主，任意多边形网格也都能转换成三角形网格。三角形以其简单性而吸引人，相对于一般多边形网格许多操作对三角网格会更容易些，除了细分着色器和几何着色器这两个不常用的着色器可以处理多边形外，许多重要阶段的顶点处理方式都是以三角形为单位进行处理的。&lt;/p&gt;

&lt;p&gt;这么多的顶点是怎么表达三角形网格的呢。通常使用索引三角形网格的表达方式，索引三角形网格它有两个列表，一个是顶点列表，里面存储了网格所有的顶点，另一个是索引列表，里面存储了所有成形三角形的索引，列表从头部到尾部依次排开，每三个索引指向三个顶点，这三个顶点代表一个三角形。除了顶点其实我们还需要其他信息，包括纹理映射坐标uv、表面法向量和切向量，顶点颜色值等等附加数据，这些数据都需要自己建立一个与顶点列表同样大小的列表来存储以便在顶点传入时提取相对应的数据传入到GPU处理。&lt;/p&gt;

&lt;p&gt;三个索引指向了三个顶点组成了一个三角形，所以索引的顺序也很重要，我们必须考虑面的“正向”和“反向”从而决定我们是否要渲染它们。因此我们用顺时针方向列出顶点，以确保我们能顺利计算出面的朝向。&lt;/p&gt;

&lt;p&gt;使用索引的方式表示三角形也并不代表图形卡中一定需要传入索引，10年前大多数图形卡都不直接支持索引而是通过传入三个一组的顶点来代表三角形，当时一个网格中有很多三角形且他们都有各自的领边共享的话，传入图形卡的数据会有很大的顶点冗余，一个顶点可能被当做多个三角形的顶点传入很多次。但现在不同了，我们所用的设备几乎都支持索引的方式渲染网格，我们只需要传入模型的顶点和构成三角面的索引就可以渲染出整个模型网格。&lt;/p&gt;

&lt;p&gt;无论怎么样CPU与GPU之间的数据传输速率还是有限的，因此为了节省数据传输的消耗，通常图形卡都会有三种方式去做优化，第一种就是缓存命中，就像CPU的高速缓存那样，图形卡也做了缓存操作，当传入的顶点数据命中，即已经在缓存中的数据，则不必再传入数据，可以直接从显存中取，如果没有命中则需要传入顶点数据并临时保存于缓存中。其次是三角带方式，以共享边的方式把所有三角形排开每个顶点加上共享边则可以成为一个三角形，这种方式省去了索引表和也节省了顶点传入数据量，但很多复杂的多边形网格需要被拆分成共享边形式的数据并且需要在传入引擎做些预计算，灵活度相对比较低。另外一种三角扇，灵活度则更低，它以一个顶点为中心点，其他相邻的两个顶点与中心点的连线形成三角形，在复杂的网格上也需要拆分数据并且也同样需要对网格做预计算，这种方式与三角带的方式所需要传入的顶点数据差不多却更为不灵活，实际项目中很少使用。&lt;/p&gt;

&lt;p&gt;因此顶点索引是主流的三角形表达方式，其他方式都是为了优化索引这个数组而设计出来的，应用的范围相对小一点。&lt;/p&gt;

&lt;p&gt;顶点索引在程序中的表现为，把所有顶点放进一个数组里，再用另一个整数数组作为索引来表达三角形的组成（整数代表顶点数组里的index下标）。在索引数组里，每3个索引组成一个三角形，当4个顶点的数组表达了一个矩形网格时，这个矩形网格相当于两个三角形组成的面片，索引的数组大小就是6个，其中前3个索引表达第一个三角形，后3个索引表达了另一个三角形。&lt;/p&gt;

&lt;p&gt;我们来看看这个矩形网格数据：(0,0,0), (0,1,0), (1,1,0), (1,0,0) 这个4个顶点构成了正方形。索引数组中的数据为，0,1,2,2,3,0 ，其中前三个0,1,2构成一个三角形，后三个2,3,0构成另一个三角形，每三个索引单元描述三角形的三个顶点。更复杂的网格数据与上述格式一样，在顶点数组中存储了所有顶点坐标的数据，在索引数组中每3个索引指向3个顶点构成了一个三角形网格，所有三角形网格描绘了整个网格上具体的面片。&lt;/p&gt;

&lt;p&gt;我们再来完整的叙述一遍网格数据从制作到渲染的过程。首先通常3D模型由3D美术同学制作出来并导出成 Unity3D 能够识别的格式即 FBX 文件，里面已经包含了顶点和索引数据，我们在程序中将 FBX 实例化成 Unity3D 的 GameObject 后他们身上附带的 MeshFilter 组件存储了网格的顶点数据和索引数据(我们也可以通过自己创建顶点数组和索引数据，以手动的方式输入顶点数据与索引数据，就如我们上面描述矩形网格那样)。有了 MeshFilter 帮助我们存储顶点和索引数据，就可以通过 MeshRender 或 SkinMeshRender 来渲染模型，这些顶点数据通常都会和材质球结合在渲染时一起送入图形卡，其中与我们想象的不一样的是在送入时并不会有索引数据的送入，而是由三个顶点一组组成的三角形顶点送入图形卡。接着又图形卡处理我们送入的数据后渲染到帧缓存并输出到屏幕。&lt;/p&gt;

&lt;p&gt;除了顶点和索引描述了模型的轮廓，我们还需要其他数据来渲染模型，包括贴图，uv，颜色，法线等。下面我们就来讲一讲这些常见的数据是如何作用在模型渲染上的。&lt;/p&gt;

&lt;p&gt;那么贴图是怎么渲染上去的呢？&lt;/p&gt;

&lt;p&gt;如果把3D空间中的三角形当做一个2D的面片来看待就会好理解一些，一个2D的三角形面片为了把图片贴上去，就需要在图片上也指定三个点，当贴图上的三个点形成的三角形与顶点三角形比例一致时，这部分三角形贴图贴到对应的顶点上后就会有顶点与图片的拟合，如果贴图上的三角形与顶点上的三角形比例不对称也可以贴，只是我们看起来会感觉拉伸或扭曲。这三个在贴图上的点坐标就叫uv，它们由2个浮点数组成，这两个浮点数的范围是0到1，0表示贴图的左上角起始位置，1表示贴图的最大偏移位置也就是右下角，一听uv二个字母我们就应该要知道是说的是图片上的坐标。&lt;/p&gt;

&lt;p&gt;用这种三角形贴图的方式贴到3D模型的每个三角形上，就可以如期的绘制出有’皮‘的3D模型了。这样一来我们在绘制3D模型时，除了顶点和索引数组外就又多了另一个数组叫uv数组，这个uv数组是用于存储uv坐标而存在，由于已经有了索引来表达三角形的三个顶点，所以uv数组就不再需要索引来表达了，只需要按照顶点的索引形成的三角形来定制uv的顺序就可以了。&lt;/p&gt;

&lt;p&gt;我们还是用简单的矩形网格数据举例来说明：&lt;/p&gt;

&lt;p&gt;[(0,0,0), (0,1,0), (1,1,0), (1,0,0)] 这个4个顶点构成了正方形。&lt;/p&gt;

&lt;p&gt;[0,1,2,2,3,0] 组成了顶点索引，它表达了2个三角形的形成。&lt;/p&gt;

&lt;p&gt;[(0,0), (0,1), (1,1), (1,1), (1,0), (0,0)] 组成了uv数组，表达了两个三角形上贴图的绘制范围。&lt;/p&gt;

&lt;p&gt;如果有一个带着材质球的贴图传入图形卡，这面这些数据将会在画面上显示这个正方形的图片。我们可不能小看这些基础知识，所有花里胡哨的技巧都是建立在基础知识之上的，这些基础知识在平时的实践中有很大很大的用途。下面就来介绍些我们在具体实践中的一些技巧，看看这些基础知识是如何灵活的运用在这些技巧上的。&lt;/p&gt;

&lt;h3 id=&quot;顶点在拆分模型时的表现&quot;&gt;顶点在拆分模型时的表现&lt;/h3&gt;

&lt;p&gt;模型切割也即模型的分裂，在游戏中是比较常见的手法。我们来说说如何用直线切割的方式切割模型，就如’切水果游戏‘那样，横向或纵向的直线切割。&lt;/p&gt;

&lt;p&gt;我们知道在Unity3D中一个3D模型是由一个渲染实例构成，也就是说一个Render组件（MeshRender或者SkinnedMeshRender,这里统一称为Render组件）只能渲染一个模型。那么要把一个Render渲染的模型切割成2个，就相当于把这个渲染组件，变成两个Render渲染组件，从而渲染两个不同的模型。&lt;/p&gt;

&lt;p&gt;有了这个大的方向就会容易多，我们可以把原来的Render渲染组件中的顶点数组、顶点索引数组，uv数组，都提取出来，并将它们分成两部分，一部分是切割后的左半部分，另一部分是切割后的右半部分。再把这两部分，分别放入新建的两个新的Render渲染组件实例中去，就得到了切割后的模型。在切割后对这两个切割后的模型加入碰撞体和物理运动组件（或者说重力引擎Rigidbody），可以让画面表现的更真实，像是一个有重量的球体被切割后倒地分成了两半那样。&lt;/p&gt;

&lt;p&gt;这其中核心的关键是如何拆分成两部分，我们要知道一个顶点在左半边还是右半边，以及切割中在哪里生成新的切割顶点，如何将这新的切割顶点与原来的顶点集合缝合。&lt;/p&gt;

&lt;p&gt;我们首先面临的是怎么区分点在左半边还是右半边，其实我们不用区分是左边还是右边，只要区分顶点是否是在平面的同一侧即可，方法可以通过矢量的点积值判断是否在平面同一侧，点与平面的法线的点积(Dot)值可以来判断是否为相同一侧。一般来说我们在切割时应该会知道切割平面，比如像切水果游戏那样在屏幕上划一下代表切割平面，滑动时我们可以知道滑动起点和终点从而知道切割平面的法线，以及从摄像机碰面出发的平面方向，因此可以利用这两个数据得到点积需要的数据。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;public float PointDotClipplane(Vector3 point)
{
	return Vector3.Dot((point - touchEnd), planeNormal);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述函数中用指尖的结束的点位、平面法线、模型顶点这三个数据来计算点积，得出的结果如果大于0则为一侧，小于0则为零一侧。当然这三个数据在使用前都必须转换到同一个坐标系中才正确。&lt;/p&gt;

&lt;p&gt;其次我们需要计算三角形三条线是否与切割平面相交，从而来判定是否有新交点。我们前面计算过点在切割平面左边还是右边的结果，如果一条线段的两个顶点在点积时的结果方向不一致就说明线段与切割平面有相交，这样我们就能很快判定出哪些线段需要计算交点。如果线段有相交则需要计算交点，看起来复杂其实不需要害怕，平面与线段的交点其实也在线段上，因此我们只要计算出一个交点在从起点向终点推进的比例就能得出交点的结果，即t为比例时最终结果x坐标为 x = (begin.x - end.x) * t + begin.x，另外的y和z也是同理。所以关键点到了比例值t怎么求，它有几种方法，其中一种是起点到平面的垂直距离与起点到终点的垂直距离的比例就是t，另外还可以在xyz轴方向任意轴上计算起点到切割线的距离，以及终点到切割线的距离，他们相加的值为整条线与切割线的垂直距离，起点到切割线的距离除以整条线的距离得到的值就是t。&lt;/p&gt;

&lt;p&gt;切割后中空部分需要缝合，由于缝合面上所有点是在同一平面，所以缝合时只需要缝合新生成出来的交点部分就可以了，因为其他部分还是保留原来的样子。&lt;/p&gt;

&lt;p&gt;缝合的算法有好几种，主要的目的是将多新生成出来的点，有规则的组成新的三角形进而形成一整个切割面。其中一种相对简单的算法步骤为，选一个点用这个点与其他所有需要缝合的点形成的线段算出夹角值，用夹角的大小进行排序，排序后的结果也就是顺时针或者逆时针的点位，然后按顺序先将靠最前面的前三个点形成一个三角形，后面的点位与它之前的两个点形成新的三角形，也就是第4个点与前两个点也就是第三个点和第二个点形成三角形，第5个点与第4个和第3个点形成新的三角形，类似于我们前面介绍的扇形三角形，这样依次类推缝合切割面。这种不筛选顶点的缝合算法在凸多边形的情况下没什么问题，但在复杂的凹多边形情况下就会出现问题，因此我们仍然要寻找出所有corner点，即角上的顶点，用角上的顶点来做为起点缝合三角形会表现的更加合适。详细请参考资料(github.com/hugoscurti/mesh-cutter)&lt;/p&gt;

&lt;h3 id=&quot;网格变形&quot;&gt;网格变形&lt;/h3&gt;

&lt;p&gt;扭曲模型的操作相当于将3D模型变形，例如将模型凹陷进去，或拉长凸出来，甚至对部分区域放大或者缩小等。&lt;/p&gt;

&lt;p&gt;有了上面介绍过的3D模型的基础知识，这里在编写模型的变形时就显得更加容易。模型网格由三角形组成的，三角形是由点组成的，要变形就得移动顶点的位置，不只一个顶点的位置，而是一片顶点的位置。&lt;/p&gt;

&lt;p&gt;扭曲和变形在实际项目中也有很多应用，爆炸后的地面凹陷，拉某个球时球有个先被拉伸再恢复的过程，以及用3D模型来表现制陶工艺，也有些角色扮演类游戏中，操控的摇杆就是用可拉伸的泡泡糖网格表现的，这些都是对模型内某个范围或者某些顶点的进行位移后表现出来的。&lt;/p&gt;

&lt;p&gt;顶点的点位的移动相对比较简单，就是取出顶点数组，修改坐标，再放回去而已。难点一是准确找出需要修改的顶点，二是不同的点修改的值不同，找出修改顶点偏移量。&lt;/p&gt;

&lt;p&gt;我们这里拿爆炸凹陷，球体拉伸反弹，制陶工艺这三个技巧来简单分析下。&lt;/p&gt;

&lt;h6 id=&quot;爆炸凹陷&quot;&gt;爆炸凹陷&lt;/h6&gt;

&lt;p&gt;首先找出爆照范围的这块地面网格，并取出这块地面网格上所有顶点数据，对所有顶点求出在爆炸范围球体内的顶点，这些顶点就是需要修改的顶点。&lt;/p&gt;

&lt;p&gt;其次来做顶点凹陷，凹陷算法的目的是把顶点位置修改到爆炸球体的球表面上，这个算法相当于，如何把一个点对应到一个球体的面上。&lt;/p&gt;

&lt;p&gt;由于球面坐标可以用经纬度定位，经转化后公式为&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;x = cos(a)cos(b)

y = cos(a)sin(b)

z = sin(a)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;其中a，b为经纬度，这样我们就能从原始顶点到球中心点计算出方向矢量，从而计算出经纬度，最后得到该顶点修改后的球面位置，计算出来后修改点位置再装入渲染的实例中，凹陷变形就完成了，顶点索引和uv都不需要任何变化。&lt;/p&gt;

&lt;h6 id=&quot;球体拉伸与反弹恢复&quot;&gt;球体拉伸与反弹恢复&lt;/h6&gt;

&lt;p&gt;在拉伸球体时，计算所有顶点与要拉的那个点位的距离，距离越大顶点需要的偏移量越小，这个比例肯定不能是个正比关系，一定是一个衰减的曲线，假设距离是d，拉伸的距离为f，结果为res，那么最简单的衰减公式为 res =  f/(d * d)，用这个公式对每个顶点进行计算，得到一个需要移动的数据res，这个数据是根据距离大小而衰减的，与拉伸点离得越近移动量越大，反之越小。修改完成所有顶点的坐标后，再推入渲染实例中去，就得到了球体拉伸的效果。&lt;/p&gt;

&lt;p&gt;球体的拉伸后的放开恢复时，这里我们假定球体的屁股是被固定的。恢复和拉伸有点相似，拉的最远的反弹的快，也就是与原有的位置距离最大的点反弹的速度最大，也就是说，我们需要记录原来没被拉伸时的顶点的坐标位置，他们与原来的位置相减就是反弹速度的基础变量。&lt;/p&gt;

&lt;p&gt;反弹力度肯定也不是正比关系，肯定也是类似衰减公式的增强公式，或者说是弧线比例，最简单的公式就做个平方，res = d * d，或者为了更平滑点，找个更好的曲线公式res = (d / max) * (d - 2) * k。&lt;/p&gt;

&lt;p&gt;反弹恢复的力度，在不断得计算过程中，会由于顶点与原有的点位的距离缩小而减小，后又由于反弹过度而不断放大，有一个来回反弹的过程，最后恢复到平静不再移动的状态，用这个公式能就很好的体现出来，因为它与原顶点距离有关，力度在不断得衰减，最后形成稳定态。&lt;/p&gt;

&lt;h6 id=&quot;最后再说说制陶工艺的模拟&quot;&gt;最后再说说制陶工艺的模拟&lt;/h6&gt;

&lt;p&gt;一个罐体模型在转盘上不停的转，当用手（鼠标或者触摸屏）去触摸它的时，在触摸的点会形成凹陷，或者拉伸，人们通过在这样不断的凹陷和拉伸过程中制作出了一个完整的陶瓷的模样，这就是制陶工艺的过程。&lt;/p&gt;

&lt;p&gt;在一个叫做《釉彩》的手机App中有具体的表现，里面你可以用一个很丑的泥罐，通过来回、上下、左右的手指滑动制作出一个你喜欢的陶瓷品，制作出来的陶瓷品可以让别人定做，也可以通过里面的超市直接购买。&lt;/p&gt;

&lt;p&gt;它的原理非常简单，就是在当你触碰时，根据你的手指滑动的方向，把范围内的顶点向手指滑动方向偏移，并且有一个衰减范围，离手指最近的点越近拉伸的距离越大，离得越远拉伸的距离越小。顶点选取的范围的判定可以认为是在一个矩形中的范围，比如认定的手指滑动的矩形范围，从而构建出一个相应的立方体范围，进而选出在立方体内的顶点，再进行衰减式的位移，最终构建出，可上下，左右，对陶瓷罐的拉伸变形操作。&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>思路探讨(二十三) 997成就世界</title>
   <link href="http://www.luzexi.com/2019/04/13/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A823"/>
   <updated>2019-04-13T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/04/13/思路探讨23</id>
   <content type="html">&lt;p&gt;实际上还是有人不知道996是什么的。996，每天早上9点上班，晚上9点下班，周6加班，一周工作6天。&lt;/p&gt;

&lt;p&gt;似乎所有人都很痛恨这种模式，当所有人都在喊996痛苦的时候。你，就不应该喊了，你是普通人吗，你不是的。普通人每天过着凑合的日子，每天上着凑合的班，做着凑合的工作，什么都凑合。加班对他们来说是种恶毒的迫害，是对他们劳动力的剥削，是对他们娱乐生活的不尊重，所以他们痛恨。他们认为资本家是万恶的，是拥有无限权利的，是可以掌控世界的，是可以无所不能的，所以他们拒绝资本家的‘迫害’。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;那么真的是他们想象的那样吗？是真的为了迫害，是真的为了剥削，是真的不尊重，是真的万恶，是真的拥有无限权利和无所不能，才996的吗？不是的。&lt;/p&gt;

&lt;p&gt;资本家也是人，有一天你也会成为资本家，其实你我都有可能是资本家。资本家是人，也会痛苦，也会被迫害，也会遭遇不幸，也同样受到压迫，只是他们穿的西服比我们贵，但这并不舒服，不如我们的睡袍和卫衣，吃的200块钱一餐的美食，同样有可能用的是地沟油，也不如我们的汉堡包、薯条、鸡块来的可口。&lt;/p&gt;

&lt;p&gt;世界真的是公平的，不会因为你钱多而让你的痛苦少一点，也不会因为你钱少而给你少一点的机会。&lt;/p&gt;

&lt;h3 id=&quot;我不赞成996但我心中不止996成年人世界里辛苦点又算的了什么为了生存为了更好的生存为了追求极致的生存皮肉上痛苦在成年人的世界里已经是最轻量级的痛苦了&quot;&gt;我不赞成996，但我心中不止996，成年人世界里辛苦点又算的了什么，为了生存，为了更好的生存，为了追求极致的生存，皮肉上痛苦在成年人的世界里已经是最轻量级的痛苦了。&lt;/h3&gt;

&lt;p&gt;普通人依赖环境，没有环境他们无法生存。普通人，任人摆布，他们无法自控只有听从于他人。普通人，委曲求全，没有突出的能力到哪都是求稳定，稳定就要牺牲自我牺牲尊严。普通人，听从命运，你愿意听从你那稀烂的命运吗？！&lt;/p&gt;

&lt;p&gt;世界是残酷的，也是公平的。残酷在，人人都要为生存而斗争，不是你死就是我活。公平在，只有勤奋努力好学的人才配的上成功，机会永远都是给那些积累很多年并且时时刻刻保持勤奋努力好学的人的。&lt;/p&gt;

&lt;p&gt;当面临淘汰时，也同样公平，也同样残酷，那些凑合的人最先被淘汰，然后那些普通人大部分被淘汰，留下的普通人只是幸运儿，下次还会这么幸运么，你死我活的斗争一直没有停歇。&lt;/p&gt;

&lt;h6 id=&quot;996不能赢得世界但997能相当于全年无休为什么&quot;&gt;996不能赢得世界，但997能(相当于全年无休)。为什么？&lt;/h6&gt;

&lt;p&gt;因为996给自己留了余地，说一周来每天这么辛苦最后一天让自己休息一下。实际上，只要停下来，启动就难了。给了一个停下来的理由，启动的理由却又要再次寻找了。难以维系。&lt;/p&gt;

&lt;p&gt;我们之间有聪明与不聪明的区别吗？没有，（不要以为自己比别人聪明，当这么认为的时候我们就已经输了一半了）。从整个人生来看，到死之前，我们比的都是谁能坚持到最后，比的是都是耐力，是毅力，是勇气。谁能勇敢的坚持到最后谁就能赢得世界，聪不聪明只是人的自以为是的幻想而已，持久战才是人生真正的赛点。&lt;/p&gt;

&lt;p&gt;那么‘坚持’真就这么容易吗？不是的。‘坚持‘超难，难到怀疑人生。&lt;/p&gt;

&lt;h6 id=&quot;为什么997就能成就世界因为全年无休让你不得不转变观念&quot;&gt;为什么997就能成就世界？因为全年无休让你不得不转变观念。&lt;/h6&gt;

&lt;h6 id=&quot;当我们明白自己无法逃脱全年无休的状态时我们已经彻底放弃了所有试图休息的谋划转而关注如何提高专注力如何用更少的精力做好当下的事情如何加强体力如何加强免疫力以及如何平衡工作学习和娱乐三者之间的关系&quot;&gt;当我们明白自己无法逃脱全年无休的状态时，我们已经彻底放弃了所有试图休息的谋划，转而关注如何提高专注力，如何用更少的精力做好当下的事情，如何加强体力，如何加强免疫力，以及如何平衡工作、学习和娱乐三者之间的关系。&lt;/h6&gt;

&lt;p&gt;我们训练了更多提高专注力的方法，从而提高了专注力，训练了更多做好事情的方法、知识和技巧，从而提高了效率，加强了每天的健身锻炼，从而让自己每天元气满满，免疫力爆棚，抵御外来病毒侵扰，让高效状态保持的更久更有力。&lt;/p&gt;

&lt;p&gt;我们开始主动关注了每天工作的时间，学习的时间，娱乐的时间，不断调整着三者的时间平衡，我们没有时间娱乐，但人又不能没有娱乐活动，所以我们只能在工作中学习，在学习中娱乐，在娱乐中工作，边工作边学习边娱乐，三者混为一体，工作即学习，学习即娱乐，娱乐为了更好的工作，保持平衡和高效，不断主动的调整，因为它们并不是一个固定时间比，每个人每个时间段每个环境下都不一样，需要主动及时调整。&lt;/p&gt;

&lt;h6 id=&quot;997让我们做时间的朋友&quot;&gt;997让我们做时间的朋友。&lt;/h6&gt;

&lt;p&gt;全年无休的情况下我们不得不做时间的朋友，只有让时间成为我们最重要的朋友，我们才能减轻无休的痛苦。做时间的朋友最大的好处便是复利，时间就是复利的要素，每天积累一点，一年下来你就比别人多跑一点，5年下来，你就比别人多跑好几个整圈，10年下来你就比别人多跑好几个级别的比赛。&lt;/p&gt;

&lt;p&gt;每天都问下自己，今天和时间这个朋友合作的如何，今天有把时间好好分配吗，能对得起这位默默无闻支持你永不抛弃永不放弃你的朋友吗，如果有明天继续，如果没有明天继续努力。&lt;/p&gt;

&lt;h6 id=&quot;997让我们转念转念之间997反而成就了我们让时间成为了我们最好的朋友让复利的积累引领我们走向了世界的巅峰&quot;&gt;997让我们转念，转念之间，997反而成就了我们，让时间成为了我们最好的朋友，让复利的积累引领我们走向了世界的巅峰。&lt;/h6&gt;
</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第八章 AI(3) 非典型性AI</title>
   <link href="http://www.luzexi.com/2019/04/07/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-AI3"/>
   <updated>2019-04-07T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/04/07/Unity3D高级编程之进阶主程-AI3</id>
   <content type="html">&lt;p&gt;前面讲了状态机和行为树在AI中的运用，在实际开发过程中，特别是一些在游戏行业做过5-10年左右的很多人似乎似乎已经习惯了用这些固定的工具去写AI逻辑，一想到游戏AI就固定思维的认为就用状态机去写或者说就用行为树去写。&lt;/p&gt;

&lt;p&gt;思维跳不出这些‘工具’，我们就成不了大师。大师是什么，是能将‘剑’运用到最高境界的人，手里‘无剑’而心中有剑是他们的至高境界。万事万物都是同一个道理，我们的‘技术’就像剑术，在练习过程中都会经过几个阶段，具体有哪些阶段其实每个人都不一样，但大多数人都可以总结为这三大阶段，即‘识剑’是第一个阶段，认识剑是怎么样的，也就是认识‘技术’是个怎样的东西用在哪里。而‘用剑’是另一个阶段，如何用剑或者说如何运用这项‘技术’是大多数人停留的阶段。它是一个怎么用剑怎么用好剑的过程，这是一个非常漫长枯燥的过程，以至于很多人做着做着就放弃了，随着工作和学习时间的增长，已经没有了提升没有了成就感，不再有兴趣专研和练习，慢慢地挫折感和自卑彻底压垮了人的意志。如果你有幸突破’用剑‘的阶段就会进入’无剑‘的阶段，手里没有剑而心中有剑在剑术的造诣上达到了至高的境界，无剑胜有剑在这个阶段体现的淋漓尽致，你可能不再在一线编程，或者你编写的程序相对比较少但是是最核心的了，即使这样你的心里也清清楚楚的知道一线的程序员们的编程习惯和编程方向，你用自己所能去帮助他们编写出更好更健壮的程序，帮助的手法也不再是手把手亲自教导，因为这样效率太差，普及率太低，因此此时你可能不再亲自出马，而是利用制度、规则、流程来提高所有人的技术高度，从而支撑部门做出更好的产品。&lt;/p&gt;

&lt;p&gt;我们接触的面要尽量的广，接受和包容的东西要尽量的多，才能体验到更多的东西。游戏开发也是，世界上不是只有RPG角色扮演类一种游戏类型，还有SLG策略类游戏，还有休闲娱乐类游戏，还有体育竞技类游戏，还有教育类游戏等等等。只有状态机只有行为树是无法为项目开发出最好最适合的AI的，我们应该尽量包容非典型性AI。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;下面我们来了解下游戏项目中非典型性的AI。&lt;/p&gt;

&lt;h3 id=&quot;1庄家式ai&quot;&gt;1，庄家式AI&lt;/h3&gt;

&lt;p&gt;顾名思义，是庄家统治游戏的一种玩法，玩法中AI就是全局的统治者，它决定了整个游戏的高潮和低谷，AI如何操作成为了至关重要的关键点，玩家是否喜欢这款游戏很大程度上取决于AI的策略。&lt;/p&gt;

&lt;p&gt;这种类型游戏大部分以彩票或赌局的形式存在，比如老虎机，百家乐，21点，以及部分以彩票为形式的游戏。&lt;/p&gt;

&lt;p&gt;AI策略的目的是要让玩家玩的好又不能赢太多而且最终是要输给AI的。玩家喜欢继续玩但从总体上来看却又是输的，让庄家就是游戏运行商有的赚，但又不能太狠，让游戏细水长流。&lt;/p&gt;

&lt;p&gt;这种AI常以输赢的概率为基本控制手段，如果现阶段以玩家欢乐为主，那么胜的概率变得大一点，玩家会认为幸运女神眷顾他了，一段时间后为了让玩家遇到点困难，让玩家有挑战性，这时AI慢慢降低了赢的概率，玩家缓慢觉得赢的次数开始减少，有些玩家会以为幸运女神走了，需要靠自己的‘本事’玩下去了，他们不断得发现和寻找输赢的规律，以及出牌和翻牌的概率，直到输得所剩无几，然后AI开始了缓慢提高胜率的操作，让玩家又看到了希望，找到了自信，认为自己终于不费苦心找到了规律和破解的秘密，于是又开始了新一轮有输有赢但输多赢少的旅程。如此这般不断得循环往复，AI以一种庄家的形式控制着整个游戏过程。&lt;/p&gt;

&lt;h6 id=&quot;从技术上来看这种控制概率的过程就是加法和减法的过程其核心是给出一个概率后如何让它真的起到概率的作用而且这种概率能让人很舒适的接受比如当有30概率赢时能否做到真正的随机值在30的概率上徘徊比如10局中有2-4局能中玩家可以得到想要的那个点位或者想要的牌什么的而不是10局中0-5局能赢这样输赢实际结果波动到了不好的体验&quot;&gt;从技术上来看，这种控制概率的过程，就是加法和减法的过程。其核心是给出一个概率后，如何让它真的起到概率的作用，而且这种概率能让人很舒适的接受，比如当有30%概率赢时能否做到真正的随机值在30%的概率上徘徊，比如10局中有2-4局能中玩家可以得到想要的那个点位或者想要的牌什么的，而不是10局中0-5局能赢，这样输赢实际结果波动到了不好的体验。&lt;/h6&gt;

&lt;h3 id=&quot;2可演算式ai&quot;&gt;2，可演算式AI&lt;/h3&gt;

&lt;p&gt;可演算式的AI在策略类游戏中非常常见，在页游里的大部分的自动对战，以及现在卡牌手游中的大部分自动战斗都是可演算式的AI，是可以根据两边的阵容数据和一个随机种子来演算出整场战斗的每个细节的。例如，两个军队的各5个英雄，互相间攻击并释放技能，服务器需要在一瞬间把所有需要在客户端演示的内容都计算出来，并以数据的形式发送给客户端，客户端根据演算的数据进行展示人物的动作，位移，技能释放等，客户端并不需要计算任何内容，所有内容都在服务器里已经计算好了，以数据形式代表了整个过程。&lt;/p&gt;

&lt;p&gt;可演算式AI的特点是逻辑一定是确定性的，不能是模糊的，或者会随机改变的，或者随时间变化而变化的结果，同样的数据第一次计算和多次计算的结果必须是相同的，才能最终体现出可演算的这个特征。其次，可演算式AI大都是根据时间轴来演化游戏的进程，在那里‘时间轴’的概念在可演算式AI中是比较常见的。&lt;/p&gt;

&lt;p&gt;什么是时间轴演算路径？我们用卡牌对战算法来举几个例子。&lt;/p&gt;

&lt;p&gt;首先最简单的时间轴演算路径，例如，先由敏捷度最高的英雄进攻，等待英雄进攻完毕后再由其次高敏捷度的英雄进攻，依次进行下去直到所有的英雄进攻完毕再重新一轮进攻。这种相对比较简单，可以把进攻看成一轮一轮的回合，每个回合都相当于是一次for循环，每次for循环前先对敏捷度进行排序，再在for循环中依次计算进攻了谁受到了多少伤害，以及是否死亡。最后把进攻的数据和伤害的数据用队列的形式存储在数据中发送给客户端，客户端受到数据后再进行演示。也可以不发送具体数据，而是只是发送随机数的种子，通过种子来产生与服务器一样的随机数，最后再通过使用同一套算法来达到校验的目的，即如果前端的计算结果和后端的计算结果一致则认为客户端的演算正确，这样就减少了很多数据传输的压力。&lt;/p&gt;

&lt;p&gt;稍微复杂点的时间轴逻辑，例如游戏里的卡牌角色不再由敏捷度来决定进攻的先后次序了，而是由每个英雄的进攻间隔冷却时间来决定进攻次序，对战开始时开始计算每个英雄的进攻冷却时间是否结束，谁的冷却时间先结束谁就最先得到进攻权，进攻完毕后再等待下一个英雄的冷却时间，以此类推。从技术上讲，冷却时间不需要等待，把所有英雄加入一个队列，排序一下就可以得到谁的剩余冷却时间最短，就立马可以开始计算进攻细节，完毕后根据冷却时间插入到队列中去，任然还是一个有序的剩余冷却时间队列，一直这样计算下去直到演算结束一方胜利或失败。这种演算方式就有了更多了时间轴的概念。&lt;/p&gt;

&lt;p&gt;我们再来看看更复杂的时间轴，前面所说的进攻逻辑都是在其他英雄都停止的状态下进行的，现在进攻时不再需要其他英雄等待了，一旦冷却时间结束就可以立刻进攻。也就是说，每个英雄都可以在其他英雄还在进攻时进行进攻操作，只要他的冷却时间到位，并且他的进攻是需要花费固定时间的，等到进攻完毕后再判断对方和自己是否死亡。这种方式，不只是多了一个进攻消耗时间这么简单了，还有死亡时间的判断。在还没有死亡判定前，此英雄虽然是即将死了但任然可以被进攻。从技术上来看，计算的量从单一的剩余时间量排序，增加了进攻消耗时间排序插入，以及死亡判定排序，我们既要在冷却时间结束时计算进攻，还要在选择进攻对象时计算该英雄是否已经判定死亡，最后在进攻完毕后计算当前的时间并插入到排序队列中。这样我们就需要将每个关键时间点都计算出来，并依次演算，由于前面的关键时间点有可能影响后面的角色AI的动作，导致我们不能演算太多的时间段，只能一步步来做AI演算。&lt;/p&gt;

&lt;p&gt;再复杂点，英雄在进攻一半时可以被别的英雄打死，也可以被其他英雄的技能打断，也包括了随时被回复血量蓝量，增加状态等等，让战斗更加逼真、更具有实时性。加强了实时性的要求，更加考验可演算式AI的复杂度，由于太多的需要计算的时间内容，所以我们必须有一个能有效管理时间轴的方法。从技术上来看，我们必须定义‘时间节点’这个概念了。&lt;/p&gt;

&lt;h6 id=&quot;时间节点就是一个事件在整个过程的时间轴上的发生时的位置&quot;&gt;‘时间节点’就是一个事件在整个过程的时间轴上的发生时的位置。&lt;/h6&gt;

&lt;p&gt;有了时间节点这个概念，我们可以用时间节点为计算标准，把所有人物的下一个事件的时间节点计算出来，比如，移动到达敌方位置，释放技能，回到原地，冷却时间结束，每个人只计算最近的一个时间节点，并把计算出来的结果放进队列中，然后就可以找出时间节点里离我们最近的一次事件的发生节点，也就是时间差最少的节点，执行它。&lt;/p&gt;

&lt;p&gt;由于每个时间点都有可能引起其他时间点的变化，例如这个做东把对方打死了，对方的时间节点就消失了，又例如打断了别人技能释放，对方就回到了冷却队列，时间节点就需要重新计算了，又例如加速了友军的攻击速度，因此所有友军正在攻击或打算攻击的时间节点都要重新计算了等等等等，这些关键点的时间节点被前面的时间节点打断则需要重新计算。因此每次在执行完一个时间节点后，都要对有可能产生变化的人物的时间节点进行重新计算，并重新加入队列。这样经过重新计算后，我们可以再次找出离我们最近的一个时间节点了。如此往复的重复这个计算过程，直到没有任何时间节点可计算和执行，最终决出这张战斗的胜利或失败。&lt;/p&gt;

&lt;h6 id=&quot;时间轴贯穿了整个ai过程人物之间的打斗移动释放法术冷却时间等待ai每次只计算一个时间节点因为只有最近的那个时间节点是一定不会被其他节点影响的这样既照顾到了可演算的根本原则也照顾到了游戏的实时性让战斗更加精彩最后我们可以把每个计算的结果都记录下来这样可以随时在客户端进行演示整个过程的事件发生的时间都将准确无误呈现出来如果觉得这样传输数据太浪费网络资源则可以使用我前面提到的传输随机种子然后通过两边一致的算法进行各自演算&quot;&gt;时间轴贯穿了整个AI过程，人物之间的打斗，移动，释放法术，冷却时间等待，AI每次只计算一个时间节点，因为只有最近的那个时间节点是一定不会被其他节点影响的，这样既照顾到了可演算的根本原则，也照顾到了游戏的实时性，让战斗更加精彩。最后我们可以把每个计算的结果都记录下来，这样可以随时在客户端进行演示，整个过程的事件发生的时间都将准确无误呈现出来。如果觉得这样传输数据太浪费网络资源，则可以使用我前面提到的，传输随机种子，然后通过两边一致的算法进行各自演算。&lt;/h6&gt;

&lt;h3 id=&quot;3博弈式ai&quot;&gt;3，博弈式AI&lt;/h3&gt;

&lt;p&gt;游戏项目中大部分AI的目的都是以娱乐玩家为主，并没有要真正打败玩家，最多也是要与玩家达到一种平衡。而博弈式AI则不同，它的目标就是为了打败玩家，它是为了赢得比赛而生的。&lt;/p&gt;

&lt;h6 id=&quot;博弈ai最大的特点是搜索通过搜索将所有下一步可能发生的以及下几步可能发生的事情都记录在内存中以此来确定电脑该怎么进行下一步动作进而获得最大的效益&quot;&gt;博弈AI最大的特点是搜索。通过搜索将所有下一步可能发生的，以及下几步可能发生的事情都记录在内存中，以此来确定电脑该怎么进行下一步动作，进而获得最大的效益。&lt;/h6&gt;

&lt;p&gt;有人不理解为什么是搜索，在AI选择下一步的动作前，下一步要发生的情况有很多，我们需要选择最佳的那个。于是把所有下一步的情况都列出来，再把下一步的后几步的情况也列在下一步的下面，计算的步数太多CPU计算量太大的因此裁剪了后面的步数，假设我们只计算和预测五步的结果，我们需要找到第五步的最好结果，再反推到第一步来确定第一步到第五步是怎么走的。&lt;/p&gt;

&lt;p&gt;既然我们要从第一步走到第五步，假设第一步有10多种情况，第一步到第五步都有10多种情况，每一步之间都有一个结果就相当于一点到另一个点的路径一样，从第一步到第五步就可以视为路径的一种，那么从第一个点到第五个点的最短路径，就是第一步到第五步的最佳结果。把视角一转换后发现事情变得的简单了，用最短路径算法就可以解决搜索问题。&lt;/p&gt;

&lt;p&gt;不过事情远没有这么简单，这里只是举了个简单的例子，情况会复杂到每一步有几百种情况，普通的搜索方式计算机可能完全无法承受。为了能让计算机更快更有效的计算出结果，通常在搜索中我们都需要引入枝剪和优化，设定一些固定技巧，以及拒绝计算一些明显比较差的选择，以此来改进搜索算法的效率。&lt;/p&gt;

&lt;h6 id=&quot;除了搜索我们还需要对每一步的结果进行估值估值表示了当前局势的好坏程度通常用一个0到100的浮点数来表示估值估值的准确性是衡量ai的一个关键点只有正确的计算出局势的估值ai才能知道当前选择的行动方向它的效益是多少是否达到了最大效益的目的&quot;&gt;除了搜索我们还需要对每一步的结果进行估值。估值表示了当前局势的好坏程度，通常用一个0到100的浮点数来表示估值，估值的准确性是衡量AI的一个关键点，只有正确的计算出局势的估值，AI才能知道当前选择的行动方向它的效益是多少，是否达到了最大效益的目的。&lt;/h6&gt;

&lt;p&gt;我们拿五子棋来说，棋盘上的每一个点都是对手下一步的可能性，通常不会在空白处下一个完全不着边的棋，所以搜索范围缩减到了当前棋盘上有棋子的范围周围的空位。AI对所有这些空位录入到程序中并搜索下一步最佳摆放位置，当AI把棋子放入该位置上后，对方做出的反应后AI由此引发了后几步的对弈可能。我们假设AI只搜索和计算后5步的预测，也就是说，计算出来的当前落子的估值，就是所有棋盘中可落子的后5步内最优估值。&lt;/p&gt;

&lt;h6 id=&quot;最后通过数据录入和训练增强ai的博弈能力人类的手法很高明欺骗性很强很容易就能找到ai的盲点所以录入人类的想法也是博弈ai的关键点&quot;&gt;最后通过数据录入和训练增强AI的博弈能力。人类的手法很高明，欺骗性很强，很容易就能找到AI的盲点，所以录入人类的想法也是博弈AI的关键点。&lt;/h6&gt;

&lt;p&gt;96年97年的人机国际象棋大战就是录入了大量的棋局来告诉AI人类的一些固定走法，让AI在对弈中识别人类的套路。通过数据录入以达到对固定手法的判断，这样做的局限性比较大，无法应付快速更新换代的技巧和想法。为了做出更强大的博弈式AI，我们不得开始让AI拥有自学的能力，通过模仿人脑的神经系统网络，再用数据训练，让机器学习更加有效。它们原理就是数据训练+估值判定，给AI不断地喂数据的同时，告诉AI这个数据是对的还是错的，或者说估值能达到多少，通过不断的数据训练（俗称喂数据）的方式，让AI的神经网络系统像人脑那样形成链路，这样就拥有了丰富的‘经验’，进而在对弈中不断学习，每次对弈对AI来说既时竞赛同时也是学习。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(二十二) 人为什么做不出最正确的选择</title>
   <link href="http://www.luzexi.com/2019/03/31/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A822"/>
   <updated>2019-03-31T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/03/31/思路探讨22</id>
   <content type="html">&lt;p&gt;人这一生真的能在每个节点都做出最好的决策吗，肯定不能。这个比较理想化的美梦，大多数人都知道是不可能的。&lt;/p&gt;

&lt;p&gt;但人真的能在模棱两可的分岔路面前做出最正确的决策吗？我认为是不可能的。（需要明确一点是，模棱两可的选择是指两种或者几种选择都是朝着光明的方向去的，而不是一黑一白或者两种都是黑。）&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;为什么做不出最正确的选择？&lt;/p&gt;

&lt;p&gt;因为最好的决策通常都是事后分析才会知道，在当下是无法知道事情到底会如何发展，只有当事情变成了历史，我们再回过头来看这件事时才能真正找到最好的决策方向。&lt;/p&gt;

&lt;p&gt;比如，何时买股票，何时卖股票，何时买房，何时卖房，选择什么专业，该不该和这个人结婚，该不该下海去创业，该不该选择这个人合作，该不该送孩子出国，该报考哪所学校，以及该不该跳槽，应该去哪座城市工作和生活等等等。所有这些都会等你做出决策，并执行多年后，再回过头来梳理时，才能真正明白到底我做对了还是错了。&lt;/p&gt;

&lt;p&gt;为什么是要等事情成为了历史才能知道当时该如何决策，我就不能直接洞悉事情的发展方向做出相应的决策？&lt;/p&gt;

&lt;p&gt;不能。因为世界是变化的。&lt;/p&gt;

&lt;p&gt;我们所处的这个世界里每分每秒都在变化，在这个变化的世界里，我们永远无法知道事情会如何发展，永远无法知道别人到底怎么想（你以为你聪明到能知道所有人的想法时就已经代表你错了），他们到底会怎么做，有多少人会这么想并有多少人会那样做，这所有与之有关的人的行为会给这样事情产生多少影响，又有多少人会因为别人的行为而改变自己的行为方式，你认为就凭你聪明的脑袋能计算的过来么，不可能。&lt;/p&gt;

&lt;p&gt;更何况还有那些与事情有关联的人的关联人，以及与之相关联事情的事情，太多太多，甚至我们都无法知道究竟有多少与之相关联的事情在同时发生，何况在同一时刻这所有关联的人和事都在不断得产生着变化。&lt;/p&gt;

&lt;h6 id=&quot;我们不得不敬畏这个世界我们永远无法估量到底有多少事情在影响着我们当下关心的这件事情&quot;&gt;我们不得不敬畏这个世界，我们永远无法估量，到底有多少事情在影响着我们当下关心的这件事情。&lt;/h6&gt;

&lt;h6 id=&quot;在这个变化的世界里想做出最正确的选择是不太可能的正确的选择只有在回顾历史时才存在而我们只能就自己的理解做出自己认为最正确的选择&quot;&gt;在这个变化的世界里，想做出最正确的选择，是不太可能的，正确的选择只有在回顾历史时才存在。而我们只能就自己的理解做出自己认为最正确的选择。&lt;/h6&gt;

&lt;p&gt;那么做出一个正确的选择真的那么重要吗？不是的。为什么？&lt;/p&gt;

&lt;p&gt;我们退一步说，即使从历史的角度看我们做出了正确的选择，就一定能成功吗？不是。&lt;/p&gt;

&lt;p&gt;首先，即使我们很幸运做出的选择，从历史角度看是最正确的选择，我们也不一定能执行到位。&lt;/p&gt;

&lt;p&gt;实际上，大部分时候连我们自己都不太相信自己做出的决策是对是错，总是在摇摆不定，虽然在表面上坚定的支持这种选择，但在心里还是持有一定的怀疑的态度。这种怀疑的态度，导致我们无法完全百分百的执行我们做出的决策。&lt;/p&gt;

&lt;p&gt;知行合一的难度就在这里，我们到底相信多少我们所知道的。由于人类思维的灵活性以及人性的弱点，我们很难百分百相信我们所知道的一切，永远都在怀疑是否有更好的我们所不知道的存在，所以很难做到相信即行动。&lt;/p&gt;

&lt;p&gt;其次，即使我们很幸运做出了正确的选择，并且很坚决的执行了决策，但成功并不是一个决策而导致的结果，而是由几百几千个决策共同组成的决策群，和，所有为之努力的执行方式的集合组合而成的结果。&lt;/p&gt;

&lt;p&gt;成功的难度就在这里，一个决策并不能决定一个人或一件事的成功与否，成功是由一群决策方向和一群行动方向组合而成，并经过多年累月得到的最终结果。&lt;/p&gt;

&lt;h6 id=&quot;抢到门票就能赢吗不是的门票只是一个赛跑的起步优势点并不能决定整场比赛的输赢别忘了拼命抢到的门票也有可能是一场灾难的开始&quot;&gt;抢到门票就能赢吗？不是的，门票只是一个赛跑的起步优势点并不能决定整场比赛的输赢，别忘了拼命抢到的门票也有可能是一场灾难的开始。&lt;/h6&gt;

&lt;h6 id=&quot;做出一个正确的选择并没有那么重要重要的是做出选择后的所有其他的选择以及接下来的所有行动我们共勉&quot;&gt;做出‘一个’正确的选择并没有那么重要。重要的是做出选择后的所有其他的选择，以及接下来的所有行动。我们共勉。&lt;/h6&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第八章 AI(2)-用行为树构建AI</title>
   <link href="http://www.luzexi.com/2019/03/28/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-AI2"/>
   <updated>2019-03-28T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/03/28/Unity3D高级编程之进阶主程-AI2</id>
   <content type="html">&lt;p&gt;前面讲了用状态机来构建游戏中的怪物AI，由于它易于理解，容易被人类接受，扩展性和耦合性都很好，所以状态机的用途很广泛。但是状态机也有很大的缺点，就是不能处理和模仿太复杂的智能行为，比如模拟一个人类奔赴一场大型战役中，对战场中的各种繁多的突发事件做出符合人类模式的处理，这种方式状态机就无能为力，由于突发情况太多，组合起来的突发情况更多，做出反应的方式也变化多端，用人类思维逻辑去编写每个状态，状态的数量就会如指数级攀升无法应付，不止人类大脑无法应付这么复杂的逻辑编写，这样复杂的逻辑也很难有效维护。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;行为树很好的弥补了状态机的缺点，它简化了逻辑拼凑的方式，让脑容量有限的人类能更容易编写和控制机器人的智能行为。这也是AI架构的关键点，能够让人更加简单容易的制作复杂的人工智能的行为，可以说是‘化繁为简’。&lt;/p&gt;

&lt;p&gt;‘化繁为简’是我们的大部分编程时的重点，尤其在AI设计上，人工智能的行为方式大都是由人类头脑指定的行为方式，是人类头脑所预期的行为。制作出人类所预期的AI行为逻辑，并且制作的方式和过程又在人类大脑的承受范围内的是我们所预期的。状态机的方式制作AI理论上可以实现任何AI，但复杂到一定程度人脑无法承受，其实就是低效的或者说低阶的AI。行为树的优势就在这里，能够通过一些简单的操作制作出达到人类所预期的足够复杂的机器人行为方式。&lt;/p&gt;

&lt;p&gt;行为树的本质是树状节点，每个节点可以选择某种类型的功能节点，也可以选择某个叶子节点即没有子节点功能，功能节点以各种逻辑顺序来选择继续访问下面的子节点或者直接停止并返回结果给父节点，子节点的结果将给父节点作为参考以便继续运行相关逻辑，因此行为树本身就是一种树形的父子节点之间的逻辑结构，我们可以理解为节点逻辑，通过扩展节点的功能来实现复杂的行为逻辑。&lt;/p&gt;

&lt;p&gt;下面我们来详细介绍行为树的组成。&lt;/p&gt;

&lt;p&gt;行为树(Behavior Tree)具有如下的特性，它有4大类型的节点：&lt;/p&gt;

&lt;h6 id=&quot;1composite-node-复合节点&quot;&gt;1，Composite Node 复合节点&lt;/h6&gt;

&lt;p&gt;复合节点可详细的分为3种：&lt;/p&gt;

&lt;p&gt;一种是Selector Node，即选择节点，它的节点规则是当执行本类型节点时，它将从头到尾迭代执行自己的子节点，如果遇到一个子节点执行后返回True则停止迭代，本节点向自己的上层父节点也返回True，否则所有子节点都返回False，那么本节点才向自己的父节点返回False。&lt;/p&gt;

&lt;p&gt;另一种是Sequence Node，即顺序节点，它的节点规则是当执行本类型节点时，它将从头到尾依次迭代执行自己的子节点，如果其中一个子节点执行后返回False那么就立即停止迭代，同时本节点向自己的父节点也返回False，相反如果所有子节点都返回True，则本节点就向自己的父节点返回True。&lt;/p&gt;

&lt;p&gt;还有一种是Parallel Node，即并发节点，它的节点规则是并发执行它的所有子节点。并发节点又分为三种策略，这与它们向父节点返回的值和并行节点所采取的具体策略有关即如下：&lt;/p&gt;

&lt;p&gt;Parallel Selector Node，即并行选择节点，它的节点规则为执行完所有子节点，如果有一个子节点返回False则自己向父节点返回False，只有当所有子节点全返回True时自己才向父节点返回True。&lt;/p&gt;

&lt;p&gt;Parallel Sequence Node，即并行顺序节点，它的节点规则为执行完所有子节点，如果有一个子节点返回True则自己向父节点返回True，否则只有当全部子节点返回False时自己才向父节点返回False。&lt;/p&gt;

&lt;p&gt;Parallel Hybird Node，即并行混合节点，它的节点规则为执行完所有子节点，按指定数量的节点返回True或False后才决定返回结果。&lt;/p&gt;

&lt;p&gt;并行节点提供了并发性，由于它可以在线程或协程级别提供并发操作，所以在性能上面有能够充分利用CPU提高性能。通常情况下，Parallel Node下会并行挂多个Action子树，或者挂多个条件节点以提供实时性。并行节点增加性能和方便性的同时，也增加了维护的复杂度。&lt;/p&gt;

&lt;p&gt;除此以外为了进一步提高AI的复杂度和随机性，Selector 和 Sequence可以进一步提供非线性迭代的加权随机变种。比如，Weight Random Selector 即随机权重选择节点，每次执行不同的起点，提供了每次执行不同的First True Child Node的可能。而Weight Random Sequence 即随机权重顺序节点，则每次执行顺序不同，以提供了不同的随机迭代顺序，能使AI避免总出现可预期的结果让结果更加的随机化。&lt;/p&gt;

&lt;h6 id=&quot;2decorator-node-修饰节点&quot;&gt;2，Decorator Node 修饰节点&lt;/h6&gt;

&lt;p&gt;Decorator Node，即修饰节点，修饰节点的功能为将它的子节点执行后返回的结果值做额外修饰处理后再返回给它的父节点。&lt;/p&gt;

&lt;p&gt;这里举几个修饰节点的例子便于读书者们理解，下面这些修饰节点都可以自定义创造出来，节点功能可以为五花八门，它们共同点为修饰子节点的结果，或者通过子节点的结果来运行逻辑。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	反向修饰 Decorator Not，它的节点功能为将结果反置后返回上级处理，即当子节点为True时返回给自己父节点的则为False，反之子节点返回False时自己返回给父节点为True。

	直到失败修饰 Decorator FailureUntil，它的节点功能为子节点运行在指定的次数到达前一直向上级返回失败，指定次数后一直向上级返回成功。

	总是失败修饰 Decorator Fail，它的节点功能为无论子节点返回的结果是否为True都向上级返回False。

	计数修饰 Decorator Counter，它的节点功能为只运行子节点n次，运行计数超过n次后不再运行。

	时间修饰 Decorator Time，它的节点功能为在指定时间内运行子节点后都返回True，超出这个时间范围无论子节点返回什么结果都向上级返回False。		

	甚至Decorator Nothing 它的功能就是什么都不干，用来提前占个位置为后面的功能预留。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;除了以上这些我们还可以创造出来更多种类型的节点，例如用来调试的日志(log)节点，告知开发者当前节点的位置及相关信息，或者循环修饰节点，循环执行子节点n次等等，我们可以根据项目的需求来增加必要的修饰节点逻辑。&lt;/p&gt;

&lt;h6 id=&quot;3condition-node-条件节点&quot;&gt;3，Condition Node 条件节点&lt;/h6&gt;

&lt;p&gt;Condition Node相对比较简单，条件满足则返回Ture，否则返回False。&lt;/p&gt;

&lt;p&gt;各式各样的条件节点，都继承基础条件节点并且返回Ture或False。比较常用的条件节点例如，大于，小于，等于，与，或，判断True或False。由这些条件节点可以与变量组合成，判断血量的条件，距离判断的条件，状态判断的条件，时间间隔判断条件等，可以用于行为树AI中。&lt;/p&gt;

&lt;h6 id=&quot;4action-node-行为节点&quot;&gt;4，Action Node 行为节点&lt;/h6&gt;

&lt;p&gt;Action Node，通常都是最后的叶子节点，它完成具体的一次(或一小步)行为并视需求返回值。行为节点可以是执行一次得到的结果，也可以视为分步执行很多次的行为。例如向前行走这个行为可以一直被执行直到走出某个范围。&lt;/p&gt;

&lt;p&gt;我们可以通过扩展行为节点让AI行为变得更为丰富多彩，行为节点也是自主定义角色行为的关键，它通常涉及角色行为的具体行为。常用的行为节点的例如，行走到目标地点的行为节点，追击目标的行为节点，使用物品的行为节点，撤退的行为节点，攻击目标的行为节点，防御动作的行为节点，释放某技能的行为节点等等。这些行为节点都可以根据我们项目的需要从基础的行为节点扩展而来的，行为节点是最丰富的节点库，大部分时间我们程序员也一直在修改和扩容行为节点以向AI行为提供更多丰富的可编辑行为内容。&lt;/p&gt;

&lt;h6 id=&quot;在行为树中任何节点被执行后必须向其上层的父节点报告执行结果成功true或失败false或running正在运行还在执行并未执行完毕例如行走到某目的地角色行走正在途中这简单的成功或失败或运行中的汇报原则被很巧妙地用于控制整棵树的决策方向&quot;&gt;在行为树中任何节点被执行后，必须向其上层的父节点报告执行结果：成功True或失败False或Running正在运行(还在执行并未执行完毕，例如行走到某目的地，角色行走正在途中)，这简单的成功或失败或运行中的汇报原则被很巧妙地用于控制整棵树的决策方向。&lt;/h6&gt;

&lt;p&gt;整棵行为树中，只有条件节点和行为节点才能成为叶子节点，也只有叶子节点才是需要特别定制的节点，而复合节点和修饰节点均用于控制行为树中的决策走向，所以有些资料中也统称条件节点和行为节点为Behavior Node 即表现节点，而复合节点和修饰节点为Decider Node 即决策节点。&lt;/p&gt;

&lt;p&gt;行为树能够支撑起对复杂逻辑的AI的原因就是由于我们可以使用这些简单的节点去搭建一个庞大的AI行为树(也可以说是一个AI模型)。我们程序员很容易的扩展节点来丰富AI节点库，无论是复合节点、修饰节点、条件节点、还是行为节点在扩展时都是相对容易的且有良好的耦合性的，这使得我们在壮大AI行为时节点的功能性扩展变得很容易，而拼装这些节点形成一个完整的AI行为就如同是一个搭积木的过程，人脑能搭建多复杂的积木就能搭建起多复杂的行为树AI。&lt;/p&gt;

&lt;p&gt;决策树也是与行为树相似的AI解决方案。决策树和行为树一样都是树形结构叶都是由节点构成。在决策树中决策的构成是由树形结构的头部开始，从上往下一路判断下去决策该往哪走，最后一定会执行到叶子节点，进而确定当次行为的动作。在决策树中只有叶子节点才决定了如何行动，而且在决定后的行动中无法中途退出，必须等到当次行为执行完毕或者被打断后才能开始下一次决策。从理念上讲，决策树就是为了制定决策，而行为树是为了控制行为，它们是两个不同的理念。行为树更加注重变化，而决策树则更加注重选择。因此行为树可以定制比决策树更加复杂的AI逻辑，而实现的难易程度上比较行为树并没有增加多少。&lt;/p&gt;

&lt;h6 id=&quot;行为树比状态机更容易编写复杂的ai逻辑很大程度上是得益于单个节点的易扩展性状态机的每个状态的扩展难度相对比较难从人们拼装节点组合的角度看ai树的可搭建更加丰富多彩的ai行为而且它的搭建行为需要的能力也都是在人脑的可及范围内而状态机对于稍微复杂点的ai行为搭建则非常费力不仅需要更多的硬编码支撑而且状态间的连线也容易让人陷入困惑&quot;&gt;行为树比状态机更容易编写复杂的AI逻辑很大程度上是得益于单个节点的易扩展性，状态机的每个状态的扩展难度相对比较难。从人们拼装节点组合的角度看，AI树的可搭建更加丰富多彩的AI行为，而且它的搭建行为需要的能力也都是在人脑的可及范围内，而状态机对于稍微复杂点的AI行为搭建则非常费力，不仅需要更多的硬编码支撑而且状态间的连线也容易让人陷入困惑。&lt;/h6&gt;

&lt;p&gt;不过再厉害的人脑在搭建行为树AI时也有极限的搭建复杂度，无论你对搭建行为树这项技能有多么熟练多么精通都有到一个极限的状态。当搭建的复杂度超出人类大脑的时候我们就会陷入混乱，或者说无法完成更加复杂的AI行为。例如如果想完全模拟人类的思维判断逻辑，对不同的人，对不同的事，对不同的景色，对不同的物体，不同的路面，以及以上混合组合的不同组合作出应对策略，就完全超出了人类大脑搭建的极限。像这种特别复杂，完全不能由人类大脑通过搭建的方式形成的AI，我们就需要引入‘机器学习’技术，它不再由人来制定行为方式而是通过对案例的学习，从而获得更多更复杂的环境应对方式。&lt;/p&gt;

&lt;p&gt;参考:&lt;/p&gt;

&lt;p&gt;《使用行为树(Behavior Tree)实现游戏AI》by AKara 2010&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(二十一) 生存之战</title>
   <link href="http://www.luzexi.com/2019/03/17/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A821"/>
   <updated>2019-03-17T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/03/17/思路探讨21</id>
   <content type="html">&lt;p&gt;22周岁毕业，在杭州工作了4年，26岁带着不甘的心独自去了上海，27岁结了婚，28岁有了第一个孩子，29岁第二个孩子降临，今年要32周岁了。从毕业到现在过去了10个年头，有时偶尔回头望去觉得是精彩的，不过精彩的并不是结果，而是这10年里拼搏的过程，那种奋力前行用尽全力突破自我的精神和逆流而上明知山有虎偏向虎山行的勇气。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;这种勇气和精神，来自于我的内心，不甘心成为一个平庸的人的意识形态，我不愿意自己每天妥协过着看不到尽头的无奈日子，我希望自己改变自己，改变成为自己心中的那个非常强大的人。&lt;/p&gt;

&lt;h6 id=&quot;还没有孩子前我只为我自己奋斗那时我觉得这样的动力已经很足够了但我还是too-young-too-naive在有了孩子后特别是有第二个孩子之后我彻底清醒的认识到我过去的行动和能力都是小儿科级别的&quot;&gt;还没有孩子前我只为我自己奋斗，那时我觉得这样的动力已经很足够了，但我还是too young too naive，在有了孩子后，特别是有第二个孩子之后，我彻底清醒的认识到我过去的行动和能力都是小儿科级别的。&lt;/h6&gt;

&lt;p&gt;特别是最近我读了《富兰克林自传》、《居里夫人自传》、《稻盛和夫自传》、《韩信传》、《林肯传》这些名人传记后，生存的严肃性再次深刻的、深切的、深不见底的印在我记忆的肌肉里。&lt;/p&gt;

&lt;h6 id=&quot;他们都是严肃生活的典范他们专心专业努力奋力认真得经营着自己包括身体知识思想家庭他们并不埋怨环境而是改变自己他们知道环境无法改变只有改变自己因此在恶劣的环境下他们任然保持自己前进的节奏虽然偶尔被环境所打断甚至被迫害但他们依然能够不断调整自己回到最初的状态尽管时常也陷入巨大的瓶颈但多大的苦难和瓶颈都没能挡得住他们突破自己的步伐&quot;&gt;他们都是严肃生活的典范，他们专心，专业，努力，奋力，认真得经营着自己，包括身体、知识、思想、家庭。他们并不埋怨环境，而是改变自己，他们知道环境无法改变，只有改变自己。因此在恶劣的环境下他们任然保持自己前进的节奏，虽然偶尔被环境所打断，甚至被迫害，但他们依然能够不断调整自己回到最初的状态，尽管时常也陷入巨大的瓶颈，但多大的苦难和瓶颈都没能挡得住他们突破自己的步伐。&lt;/h6&gt;

&lt;p&gt;对比我自己，我还是太稚嫩，严肃的不够严肃，认真得不够认真，对生活没有一个清醒的彻底的完全的认识，时常有’休息一下也没事‘、’一天不努力也可以‘、’明天再学也不迟‘的行为和想法，导致原本制定的计划，或者良好的生活习惯，或者良好的学习习惯被一点点侵蚀，最终导致自律能力彻底丧失，彻底沦为浪费时间浪费生命的大懒虫。&lt;/p&gt;

&lt;p&gt;我再次回忆我这年轻的岁月里，浪费了有多少的时日时，时常感叹如果我能早点知道该有多好。&lt;/p&gt;

&lt;h6 id=&quot;不过幸好每次有这种早点知道该有多好的感叹时我明白为时还不晚因为有很多智者都提醒过我任何时候只要你意识到正确道路的方向时为时都不晚虽然这些智者我都没见过他们都存在于书中广播中视频中但却时常能够起到提醒我的作用&quot;&gt;不过幸好，每次有这种’早点知道该有多好‘的感叹时，我明白为时还不晚，因为有很多智者都提醒过我，任何时候只要你意识到正确道路的方向时为时都不晚。虽然这些智者我都没见过，他们都存在于书中，广播中，视频中，但却时常能够起到提醒我的作用。&lt;/h6&gt;

&lt;p&gt;正确的道路确实难走，所以人才会跟随人性弱点去选择舒适的但错误的道路。&lt;/p&gt;

&lt;h6 id=&quot;生存之战就是谁能长时间的长期的长久的更长时间的更长期的更长久的走在正确道路上的无形的战争&quot;&gt;生存之战就是谁能长时间的、长期的、长久的、更长时间的、更长期的、更长久的走在正确道路上的无形的战争。&lt;/h6&gt;

&lt;p&gt;每个人在战争中都走的是自己的路并平行于他人，从来都不跟别人碰撞，但每一步都有好有坏，而每个步都有他们自己来决策。&lt;/p&gt;

&lt;p&gt;让人感到意外又不十分惊奇的是，很多人并不知道他们已经决策了自己的路，他们甚至以为生活的决策是有个缓冲期的，会给到具体的问题和正确的答案。&lt;/p&gt;

&lt;h6 id=&quot;生存之战的残酷性就在这里它的决策之路很少有具体的标志性的事件或者缓冲期甚至连问题和答案都没有所有的人生道路的决策都是由生活中的一言一行一举一动拼凑而成的&quot;&gt;生存之战的残酷性就在这里，它的决策之路很少有具体的标志性的事件或者缓冲期，甚至连问题和答案都没有，所有的人生道路的决策都是由生活中的一言一行，一举一动拼凑而成的。&lt;/h6&gt;

&lt;p&gt;你是怎么生活的，你就是怎么决策着你的道路，容不得我们半点侥幸。根本不会出现，例如有个决策能一下子完全彻底的改变你的人生道路的东西出来，所有你走出来的道路都是由你生活的点滴组合而成，所有导致事件发生的都是由你先前的生活中一举一动的积累导致的量变引起的质变。&lt;/p&gt;

&lt;p&gt;人们口中时常说的残酷是由于人多或者需求多而供应少导致的，我觉得生存之战的残酷比它这种残酷要残酷一百倍。&lt;/p&gt;

&lt;p&gt;生存之战的残酷是没有硝烟的，是看不见摸不着的，但却时时刻刻在战斗，更残酷的是，并不是战斗一年两年就结束的，而是10年，20年，30年，40年，50年，一辈子。直接说一辈子太突兀，所以用前面的数字来做铺垫会让人好受些。&lt;/p&gt;

&lt;p&gt;一辈子持久战的残酷性，我想是我们这个和平年代下最最残酷的残酷战争了吧。&lt;/p&gt;

&lt;h6 id=&quot;我希望能用这篇文章来告知同僚们提高警惕并且奋斗这是场最长的持久战记得身体是第一关键因素谁在持久战中身体先垮掉谁就第一个败下阵因此每天运动保持身体免疫系统正常工作是第一要素有了身体还不够还要有知识有技能有多维度的思维方式有育人的技巧有沟通能力有经验等等等要我们去征服&quot;&gt;我希望能用这篇文章来告知同僚们，提高警惕并且奋斗，这是场最长的持久战，记得身体是第一关键因素，谁在持久战中身体先垮掉谁就第一个败下阵，因此每天运动保持身体免疫系统正常工作是第一要素。有了身体还不够，还要有知识、有技能、有多维度的思维方式、有育人的技巧、有沟通能力、有经验等等等，要我们去征服。&lt;/h6&gt;

&lt;h6 id=&quot;面对生存之战我们需要严肃生活并且在严肃的生活中别放弃别太多沮丧哭完了我们继续上&quot;&gt;面对生存之战，我们需要严肃生活，并且在严肃的生活中，别放弃，别太多沮丧，哭完了我们继续上！&lt;/h6&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第八章 AI(1)-状态机构架机器人行为</title>
   <link href="http://www.luzexi.com/2019/03/10/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-AI1"/>
   <updated>2019-03-10T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/03/10/Unity3D高级编程之进阶主程-AI1</id>
   <content type="html">&lt;p&gt;AI机器人在游戏中非常普遍，它们常以模仿人类的行为在游戏中活动。游戏中的怪物的自动行为比较普遍，这种简单的人工智能方式可以用几种不同的方式进行编写，这里我们介绍两种方式，一种为容易被人类思维接受的状态机，另一种是为机器人策略型思考方式编写的行为树。其它还有很多方式来编写AI，比如与行为树差不多的决策树，以及相对比较复杂的神经网络，以及现在比较流行的机器学习，由于作者接触的只是皮毛在此不多做介绍，它们的复杂度也超过了项目的周期，研发和维护成本暂时比较高，所以在游戏项目中的AI方面应用还不太多，大家也一直在往这方面靠拢，因为神经网络和机器学习是人工智能的趋势。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;h3 id=&quot;ai-状态机&quot;&gt;AI-状态机&lt;/h3&gt;

&lt;p&gt;状态机虽然是个比较简单的概念，但在实际编程中花样也是繁多的，不过最终都是围绕着状态的概念来做的变化。状态机比较符合人类思考的方式，我们喜欢把事物的行为以状态形式进行拆分。以时间状态为基础的可以有，当前状态、前置状态、下一个状态、状态变化条件，其中每个状态都有自己定义，它们可以以行为方式拆分成，比如行走状态，休息状态，躺下状态，攻击状态，防守状态，三连击状态，俯冲状态，平移状态等。&lt;/p&gt;

&lt;p&gt;人们习惯把人或动物的某些连贯的行为定义为状态，所以状态其实不只是一个动作，它可以是好几个动作，或者好几段位移，也就是好几段执行程序。这种好几个动作和好几段位移一起组成的组合，在程序中用状态来表示比较符合人类思考逻辑。每种状态下不只是一个动作或一种位移，而是由很多个动作多段位移组合而成。在游戏里，智能机器人由于都是人设计出来的，行为方式也需要符合人类的逻辑，因此用状态机的方式制作AI比较符合人类思维方式，也最容易被接受。&lt;/p&gt;

&lt;p&gt;从面向程序的角度来看，状态可以从父类构建开始，并且向不同的方向继承并衍生，最后把所有的状态类集中到控制状态的状态控制器中。下面我们就以图文的方式构建一个 AI 状态机：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	我们新建一个 AIStateBase 为状态基础类，基础类中有一个识别状态类的变量，可以是整数也可以是枚举，和三个必要的接口包括，更新函数 Update() ，进入状态事件引发的事件函数 OnEnter()，退出状态事件引发的事件函数 OnExit()。这三个函数概括了状态的出、入、以及自我循环更新三个动作，状态机定义下这三个动作也是比较重要的接口。
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;class AIStateBase
{
	private int STATE;

	public abstract OnEnter();
	public abstract OnExit();
	public abstract Update();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;接下来，我们开始扩展状态机的功能，以跑步动作状态为例，在进入跑步动作状态时，表现为，机器人有跑步动画，并且向前移动。&lt;/p&gt;

&lt;p&gt;新建一个跑步状态类，继承父类 AIStateBase 并且把跑步状态类命名为 AIRunState。对 AIRunState 进行编写，实现进入状态函数即 OnEnter 函数中编写机器人播放跑步的动画并让动画不断循环播放，然后在更新函数 Update 中开启不断向前移动的位置变化。当不再需要移动时，也就是退出跑步状态事件中，OnExit 里调用停止播放动画，也可以不停止播放，因为下一个状态肯定会播放其他动画，不如让他们通过插值过度下使得动画看起来会更顺滑。&lt;/p&gt;

&lt;p&gt;下面是 AIRunState 的伪代码&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;class AIRunState : AIStateBase
{
	public AIRunState()
	{
		STATE = STATE.AIRunState;
	}

	public override OnEnter()
	{
		PlayAnimation(&quot;run&quot;,loop);
	}

	public override OnExit()
	{
		StopAnimation(&quot;run&quot;);
	}

	public override Update()
	{
		if(target != null)
		{
			//如果有目标则向目标移动
			MoveTo(target);
		}
		else
		{
			//如果没有目标则向指定方向移动
			Move(dir);
		}
	}
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;实现了跑步状态，可以扩展其他的和跑步状态相似的状态，它们可以完全按照跑步状态的方式实现，比如后退，侧移，漫步，打坐，跳跃，攻击等，它们都是只用到播放自身动画和持续位移组合的方式完成状态，这些对复杂状态来说，算是比较基本的状态。&lt;/p&gt;

&lt;p&gt;现在我们来实现稍微复杂点的状态‘追击’状态。追击中有两个动作，一个是追，一个是攻击，我们首先锁定目标然后寻找追击的路径。当怪物进入‘追击’状态时，会锁定目标，然后不断向目标跑去，当跑到攻击范围内时再进行攻击。&lt;/p&gt;

&lt;p&gt;我们定义一个‘追击’状态类，AIMoveAttackState 类同样继承 AIStateBase。当‘追击’状态开始时，进入 OnEnter 函数时先锁定目标，把目标保存下来，并且寻找追击目标的路径 Path Find，人物寻路的解决方案我们在地图与寻路章节中详细介绍。接下来就是‘追击状态’的更新函数，每帧都会调用更新函数 Update，在Update中，检查目标是否已在攻击范围内，如果在范围内则立刻播放攻击动画，并且调用目标的攻击接口攻击目标，如果不在范围内则检查锁定目标是否超出检测范围，如果在范围内则重新寻找路径，并且根据路径来进行位移，否则不再追击。整个追击状态，在追击中都是在 Update 函数中不断判断和位移的。&lt;/p&gt;

&lt;p&gt;下面是 AIMoveAttackState 的伪代码&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;class AIMoveAttackState : AIStateBase
{
	public AIMoveAttackState()
	{
		STATE = STATE.AIMoveAttackState;
	}

	public void SetTarget(Actor target)
	{
		currentTarget = target;
	}

	public override OnEnter()
	{
		//寻路后播放动画
		path = Findpath(target);
		PlayAnimation(&quot;run&quot;,loop);
	}

	public override OnExit()
	{
		//nothing can do
	}

	public override Update()
	{
		dis = Vector3.Distance(myself, target);

		if(dis &amp;gt; 2 &amp;amp;&amp;amp; dis &amp;lt; 10)
		{
			//在监视范围内的进行追击
			Move(path);
		}
		else if(dis &amp;lt;= 2)
		{
			//攻击范围内的进行攻击
			Attack(target);
		}
		else
		{
			//监视范围外的停止状态
			Stop();
		}
	}
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;通常在攻击 Attack 中，可以根据人物数据表的数据做一些动画和攻击时间点的变化，也可以根据技能编辑器的技能数据在某个时间点做某个动作或者释放某个特效，这样更加方便量产更多的角色在游戏里的动作，这将后面的章节中介绍技能编辑器。&lt;/p&gt;

&lt;p&gt;追击状态以追踪和攻击为主要行为。与追击状态类似的，也比较常用的还有‘巡逻状态’，当AI怪物进入这个‘巡逻状态’时，怪物会在一个范围内到处行走以查看敌人是否在周围，如果在视野范围内查看到敌人，则退出巡逻状态，进而进入追击状态。&lt;/p&gt;

&lt;p&gt;我们来看看编写‘巡逻状态’的步骤。当进入‘巡逻状态’时，状态进入 OnEnter 函数，在 OnEnter 里找到最近的一个巡逻点，然后在更新函数 Update 中持续循环走向最近的巡逻点，当走到第一个最近的巡逻点后继续按照巡逻点的布置顺序继续往下一个巡逻点走，如此不断循环往复。同时更新每次移动时，都检查一下敌人是否在范围内，如果有敌人在检查范围内，当前‘巡逻状态’就结束，OnExit 被调用，转而进入‘追击状态’。&lt;/p&gt;

&lt;p&gt;巡逻状态的伪代码如下:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;class AIPatrolState : AIStateBase
{
	public AIPatrolState()
	{
		STATE = STATE.AIPatrolState;
	}

	public override OnEnter()
	{
		// 获取下个巡视点
		point = GetNextPatrolPoint();
		//寻路
		path = FindPath(point);
		// 向巡逻点移动
		MoveByPath(path);
	}

	public override OnExit()
	{
		//nothing can do
	}

	public override Update()
	{
		//检测敌人的范围
		float distance = 5;
		//获取距离5以内的敌人
		target = GetNearestEnemy(distance);

		if(null != target)
		{
			//有敌人进行追击
			ChangeToMoveAttackState();
		}
		else
		{
			if(MoveFinish())
			{
				// 寻找下一个巡逻点
				point = GetNextPatrolPoint();
				//寻路
				path = FindPath(point);
				// 向巡逻点移动
				MoveByPath(path);
			}
			//更新移动
			Move();
		}
	}
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;至于‘追击状态’如何返回‘巡逻状态’的条件可以设置为，当追击太远时重新转入’巡逻状态‘，比如在进入’追击状态‘ OnEnter 函数里记录起始追击位置，每次移动时都判断下，与起始位置的距离是否太远，如果太远则退出当前的’追击状态‘转而进入’巡逻状态‘。&lt;/p&gt;

&lt;p&gt;这些状态的设计熟练了以后是会变得相对简单，但状态不能太多，这会导致难以维护。因此通常我们会引入更多通用性比较强的技能系统或者说Action系统，即一系列可编辑可调试的动作集合不需要设计师写代码，通过对时间线和节点的编辑让设计师能够轻松的完成技能或一系列动作的设计，我们将在后面的编辑器章节中详细介绍。&lt;/p&gt;

&lt;p&gt;无论多少复杂的行为都能在一个状态中体现出来，以状态形式的编写好处就在这里。现在有了众多复杂的状态还不够，我们还需要有控制这些状态的状态控制器。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;class StateControl
{
	AIStateBase[] mStates;
	AIStateBase mCurrentState;

	public void Init()
	{
		//初始化
		mStates = new AIStateBase[STATE.Max];
		mStates[STATE.AIIdleState] = new AIIdleState(StateControl);
		mStates[STATE.AIRunState] = new AIRunState(StateControl);
		mStates[STATE.AIMoveAttackState] = new AIMoveAttackState(StateControl);
		mStates[STATE.AIPatrolState] = new AIPatrolState(StateControl);

		mCurrentState = null;
	}

	public void ChangeState(STATE state)
	{
		// 切换状态
		if(mCurrentState != null)
		{
			mCurrentState.OnExit();
		}
		mCurrentState = mStates[state];
		mCurrentState.OnEnter();
	}

	void Update()
	{
		// 实时更新
		if(mCurrentState != null)
		{
			mCurrentState.Update();
		}
	}
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;状态控制器是存储所有状态的状态管理类，我们可以暂时命名为 StateControl类，所有状态都在 StateControl 里有一份实例，而且状态控制器记录了当前的状态，由于所有状态都是继承与 AIStateBase，因此这个当前状态的变量可以是基类 AIStateBase 以支持所有类型的状态类。又由于所有的状态类的对外接口都是统一，因此只需要基类就能操作所有状态，而且状态的转换也完全可以由各自状态的本身来决定。状态控制器只是起到了对状态实例管理和转换的作用，因此通常状态控制器没有太多了的逻辑，大部分AI逻辑都放在了每个状态类里面，由每个从 AIStateBase 父类继承而来的AI子类来决定如何行动，这样大大降低了逻辑的耦合，让代码逻辑更加清晰、更容易维护、更容易扩展。&lt;/p&gt;

&lt;h6 id=&quot;用状态机编写ai有诸多优点诸如可维护性强可扩展性强逻辑耦合清晰符合人类思维逻辑易上手但缺点也很大由于每个状态都必须由设计人亲自制定因此在每个状态时编写时要考虑到所有情况每种情况都要有相应的处理方式这样就导致当需要设计的ai行为过于复杂的时候编写的逻辑复杂度和工期长度也呈现指数级的增长到最后有可能无法承受太复杂的ai行为逻辑比如人类在战场中的随机应变能力对于各种各样的爆炸攻击冲锋防御的应变能力需要表现出各种不一样的行为方式时就会在状态机里出现很多-if-的情况这时仍然用状态机来编写ai就会很容易陷入超出人类逻辑的复杂度&quot;&gt;用状态机编写AI有诸多优点，诸如可维护性强，可扩展性强，逻辑耦合清晰，符合人类思维逻辑易上手。但缺点也很大，由于每个状态都必须由设计人亲自制定，因此在每个状态时编写时要考虑到所有情况，每种情况都要有相应的处理方式，这样就导致当需要设计的AI行为过于复杂的时候，编写的逻辑复杂度和工期长度也呈现指数级的增长，到最后有可能无法承受太复杂的AI行为逻辑，比如人类在战场中的随机应变能力，对于各种各样的爆炸，攻击，冲锋，防御的应变能力需要表现出各种不一样的行为方式时就会在状态机里出现很多 if 的情况，这时仍然用状态机来编写AI就会很容易陷入超出人类逻辑的复杂度。&lt;/h6&gt;

</content>
 </entry>
 
 <entry>
   <title>YOU CAN LIVE YOUR DREAM!</title>
   <link href="http://www.luzexi.com/2019/02/19/you-can-live-your-dream"/>
   <updated>2019-02-19T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/02/19/you-can-live-your-dream</id>
   <content type="html">&lt;p&gt;I don’t know what that dream is that you have.&lt;/p&gt;

&lt;p&gt;I don’t care how disappointing it might have been, as you’ve been working towards that dream.&lt;/p&gt;

&lt;p&gt;That the dream that you’re holding in your mind … that it’s possible.&lt;/p&gt;

&lt;p&gt;That some of you already know that its hard… it’s not easy. Its hard changing your life.&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;That in the process of working on your dreams you are going to incur a lot of disappointment … a lot of failure … a lot of pain … there are moments when you’re going to doubt yourself … and say “ God why? why is this happening to me? I’m just trying to take care of my children and my mother I’m not trying to steal or rob from anybody. How did this have to happen to me ?”&lt;/p&gt;

&lt;p&gt;For those of you that have experienced some hardships, don’t give up on your dream.&lt;/p&gt;

&lt;p&gt;There are rough times that are gonna come but they have not come to stay, they have come to pass.&lt;/p&gt;

&lt;p&gt;Greatness is not this wonderful esoteric, elusive, god like feature that only the special among us will ever taste … it’s something that truly exists in all of us.&lt;/p&gt;

&lt;p&gt;It’s very important for you to believe, that you are the one.&lt;/p&gt;

&lt;p&gt;Most people, they raise a family, they earn a living and then they die.&lt;/p&gt;

&lt;p&gt;They stop growing… they stop working on themselves, they stop stretching, they stop pushing themselves.&lt;/p&gt;

&lt;p&gt;And a lot of people like to complain but they don’t want to do anything about their situation, and most people don’t work on their dreams.&lt;/p&gt;

&lt;p&gt;why? One is because of fear, the fear of failure… “what if things don’t work out?” And the fear of success… “what if they do and I can’t handle it?”. These are not risk takers.&lt;/p&gt;

&lt;p&gt;You spend so much time with other people, you spend so much time trying to get people to like you, you know other people more than you know yourself, you study them… you know about them, you wanna hang out with them, you wanna be just like them.&lt;/p&gt;

&lt;p&gt;And you know what you’ve invested so much time into being like them you don’t know who you are, I challenge you to spend time by yourself.&lt;/p&gt;

&lt;p&gt;It’s necessary that you get the losers out of your life, if you want to live your dream.&lt;/p&gt;

&lt;p&gt;For people who are running towards their dreams, life has a special kind of meaning.&lt;/p&gt;

&lt;p&gt;When you become the right person, what you do is you start separating yourself from other people, you begin to have a certain uniqueness.&lt;/p&gt;

&lt;p&gt;As long as you follow other people and as long as you’re being a copy cat you will never ever be the best copy cat in the world. But you will be the best you can be.&lt;/p&gt;

&lt;p&gt;I challenge you to find your value. That everybody won’t see it, that everybody won’t join you, that everybody won’t have the vision, it’s necessary to know that. That you are an uncommon breed.&lt;/p&gt;

&lt;p&gt;It’s necessary that you align yourself with people and attract people into your business who are hungry.&lt;/p&gt;

&lt;p&gt;People who are unstoppable and unreasonable, people who are refusing to live life just as it is and who want more.&lt;/p&gt;

&lt;p&gt;The people that are living their dream are finding winners to attach themselves to. The people that are living their dreams, are the people that know that if it’s gonna happen it’s up to them.&lt;/p&gt;

&lt;p&gt;If you wanna be more successful, if you wanna do and have stuff that you have never had before, I’m asking you to invest in you.&lt;/p&gt;

&lt;p&gt;Someone’s opinion of you does not have to become your reality … that you don’t have to go through life being a victim. And even if you have to face disappointment, you have to know within yourself, I can do this even if no one else sees this in me I must see it for myself.&lt;/p&gt;

&lt;p&gt;Because you’re still talking about your dream … you’re still talking about your goal …you’ve not done anything … just take the first step … you can touch millions of peoples ‘ lives … and the world will never be the same again … because you came this way.&lt;/p&gt;

&lt;p&gt;Don’t let nobody steal your dreams. If you face a rejection or a no, if you hold a meeting and nobody shows up, if someone says ” you can count on me ” and they don’t come through.&lt;/p&gt;

&lt;p&gt;What if we have that kind of attitude, a cause repossessed, nobody believes in you … you’ve lost again, and again, and again, the lights are cut off … but you’re still looking at your dream, renewing it every day and saying to yourself it’s not over… UNTIL I WIN !&lt;/p&gt;

&lt;p&gt;YOU CAN LIVE YOUR DREAM!&lt;/p&gt;

&lt;h3 id=&quot;copy-from-google&quot;&gt;Copy from google&lt;/h3&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第一章，C#要点技术(六) 搜索算法</title>
   <link href="http://www.luzexi.com/2019/02/17/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-CSharp%E8%A6%81%E7%82%B9%E6%8A%80%E6%9C%AF6"/>
   <updated>2019-02-17T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/02/17/Unity3D高级编程之进阶主程-CSharp要点技术6</id>
   <content type="html">&lt;h3 id=&quot;搜索算法&quot;&gt;搜索算法&lt;/h3&gt;

&lt;p&gt;广度优先搜索和深度优先搜索是最常见的搜索算法，如果不进行些枝剪的话效率是比较差的，直接使用不加修饰的广度和深度算法，会消耗比较多的CPU以至于整体效率比较差。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;其实搜索算法不只广度和深度优先算法，他们只是明面上看上去比较直接的搜索算法。搜索算法的目的就是找东西，找出各种类型的东西，动态规划，图论，也能帮助我们很好的找到东西。好的搜索算法，大部分需要有数据结构的支撑，在数据结构里记录了信息的特征，前人的搜索的记录，和当前的内容环境，用于枝剪和优化。&lt;/p&gt;

&lt;p&gt;搜索的目的一般有，一组元素中找出某元素，一组元素中找出某个特征的所有元素，一个2D或3D空间中找出某元素，2D或3D空间中找出某一个特征的所有元素，以及一堆相互连接的结构中找出两点的最短路径等等等。&lt;/p&gt;

&lt;h6 id=&quot;二分查找算法&quot;&gt;二分查找算法&lt;/h6&gt;

&lt;p&gt;二分查找算法是搜索中用的最多的，也是最简单易用的，效率也比较好的搜索算法。不过它有个前提条件，即查找所用的数组必须是有序的数组。也就是说，在使用二分算法查找前，必须对数组进行排序。而且在每次更改，插入，删除后都要进行排序以保证数组的有序状态。&lt;/p&gt;

&lt;p&gt;二分查找算法的步骤是：&lt;/p&gt;

&lt;p&gt;1，将数组分为三块，分别是前半部分区域，中间位元素，后半部分区域。&lt;/p&gt;

&lt;p&gt;2，将要查找的值和数组的中间位进行比较，若小于中间位则在前半部分查找，若大于中间位则在后半部分查找，如果等于中值时直接返回。&lt;/p&gt;

&lt;p&gt;3，继续在选择的查找范围中查找，跳入1步骤，依次进行递归过程，将当前选择的范围继续拆分成前半部分，后半部分，和中间位三部分，直到范围缩小到最小，如果还是没有找到匹配的元素，就说明元素并不在数组里面。&lt;/p&gt;

&lt;p&gt;二分查找的平均时间复杂度为O(logN)，是一个效率比较好的查找算法，这也是它常被使用到的原因，不过前提是数组是有序的，可能这对于一些情况下的搜索问题的门槛会比较高，它们可能需要不停得插入或删除元素，这种情况下可以使用二分先查找到插入的位置，再做插入操作，但是插入的操作本身就是O(N)的平均时间复杂度，导致查找无论多快都还是抵不过插入带来的消耗，删除也是同样的道理，数组的拷贝操作已经成了算法中的瓶颈。我们来看看其他搜索算法是怎么做的。&lt;/p&gt;

&lt;h6 id=&quot;二叉树二叉查找树平衡二叉树红黑树b树&quot;&gt;二叉树、二叉查找树、平衡二叉树、红黑树、B树&lt;/h6&gt;

&lt;p&gt;二叉树及其衍生的所有算法都是以父节点有且只有至多两个子节点为规则。二叉查找树就是在二叉树之上建立的查找树，主要目标是快速查找，它在构造时的特点为，左边的子节点一定比父节点小，右边的子节点一定比父节点大。算法的方式与二分查找算法有点类似，不一样在于二分查找树构建出来的树形结构，由于原始数据的排列不同有可能导致深度很大的二叉树犹如直线连接的节点，因此它也被称为是个不稳定的查找方式，搜索的速度由原始数据的排列方式决定，排列的顺序不好则速度就不佳，因此二叉树的稳定性并不高。&lt;/p&gt;

&lt;p&gt;平衡二叉树很好的解决了查找二叉树的二叉树不平衡问题，它的规则是父节点的左右两棵树的深度差的绝对值不能超过1，所有节点都遵循这个规则包括节点下的左右两棵子树叶同样遵循这个规则。二叉树的深度问题解决了，在查找时的效率就更加稳定，如果能一直保持O(logN)的时间复杂度那将是很好的算法效率。&lt;/p&gt;

&lt;p&gt;红黑树就是实现平衡二叉树的算法，都是在插入和删除节点操作时通过特定的操作保持二叉查找树的平衡，从而使二叉查找时获得较高的查找效率。它虽然是复杂的，但它的最坏情况运行时间也是非常良好的，并且在实践中是高效的，它能够在O(logN)时间复杂度内做查找，插入和删除操作，这里的N为二叉树中元素的数目。&lt;/p&gt;

&lt;p&gt;红黑树的数据结构特点是各节点上多了一个颜色值，颜色为红色或黑色。通过对任何一条从根到叶子的路径上各个节点着色方式的限制，红黑树确保没有一条路径会比其他路径长出两倍，因而是接近平衡的。具体红黑树是怎样的算法和程序步骤不在这里介绍，有很多书本和资料可供参考。&lt;/p&gt;

&lt;p&gt;红黑树虽然高效稳定，但实际项目中运用的稍微少了点，一方面它通常会封装在自定义的底层容器算法中，例如我们通常会重新封装Map、Dictionary容器，把红黑树放入容器中使得写业务逻辑的同学能够不需要关心底层的算法的情况下高效的使用容器。另一方面它的算法复杂度高，一般程序员需要费点心思去研究，因此通常也不会方在明显的位置上使用，这也是红黑树一般都会放入容器里使用的原因之一，它也更适合容器类外壳。还有一方面，查找可以用“快速排序”+“二分查找”代替，效率稍差一些但简单实用，使用优化后的快速排序效率在查找效率上会优于红黑树，如果逻辑中查找的次数远远大于插入与删除的次数，则可以考虑用“快速排序”+“二分查找”代替这种方式进行替代。&lt;/p&gt;

&lt;p&gt;B树大家听的也比较多，其实它主要是为磁盘存储设备而设计的一种平衡查找树。它与红黑树主要不同在于，B树是建立在查找树之上的多叉树，它的一个节点上有多个值且父节点可以拥有2个以上的节点，同时还必须保持平衡树的层次结构，即树的深度值不得超过logN的深度(N为节点个数)。这种数据结构犹如在红黑树之上建立多叉的方式，其比较方式由单一值比较改为了多值比较。在B树结构里一个节点里的信息是个数组，它们是有序的，因此可用二分查找法查找数据，若没有找到准确值，则继续往下搜索子节点的数据。B树在游戏项目中里很少用到，不过它在文件信息存储结构上能发挥巨大作用，这也是它被开发出来的原因之一，其他包括B+和B*树都是从B树衍生而来。&lt;/p&gt;

&lt;h6 id=&quot;四叉树搜索算法&quot;&gt;四叉树搜索算法&lt;/h6&gt;

&lt;p&gt;四叉树搜索算法，有点像二叉树的多维度版本，有点类似一维数组上数组分成4块进行查找，但也仅限于此。四叉树的最重要的理念是空间划分，把一个二维空间划分成多个块来存放，它的数据结构也与二叉树一样只是扩展了子节点的个数，每个点有四个子节点，这四个子节点代表父节点的四个象限区块，四个子节点加起来代表整个完整的父节点，以此类推下去，每个节点可以有子节点，一旦有就得四个一起有，除非是末尾的叶子节点，这就相当于是个完美二叉树的变形即完美四叉树，我们可以用一个数组来完美表示完美四叉树的结构情况。&lt;/p&gt;

&lt;p&gt;在实际运用中，四叉树主要在2D平面空间上的搜索用的比较多，虽然其他领域也有用到，但平面上空间划分更适合它。我们可以把一个2D矩形平面想象成一个分成相等四个大小的矩形，共有4个子节点共四块矩形方块，其中子节点中即这4块中每块又再次分成相等大小的四块，一直分直到分到定义的最小块为止，最小块大小可自定义，这样的划分将一个大的平面矩形划分成都能用四叉树表示的结构，每一块都有自己的父节点和子节点。&lt;/p&gt;

&lt;p&gt;用这样的数据结构表示一个平面里的所有方块内容好处是能牢牢掌控每个小平面内的内容变化。当有元素加入这个平面时，即加入到了这个四叉树的数据结构中去时，会先计算它的坐标在树中的索引，这就相当于在四叉树中搜索某个节点那样，从最大的四块的x、y大小范围开始依次往下推导，一直推导到最小区块的节点上，就能得到该坐标所在的节点。&lt;/p&gt;

&lt;p&gt;形成四叉树数据结构后，就可以根据这个数据结构进行查找节点上的元素了。比如在某个位置上可以查找到与它在相同块内的所有其他元素，即给出某个坐标后就能根据四叉树依次推下去最终找到最小块的那个节点，这样就可以获取该块上的所有其他元素的信息。我们可以想象用这种四叉树的方式把地图分成3级，每1级四块矩形图上都存有相关元素的信息，这样就可以快速从1、2、3级地图上得到某块方块内的所有元素的信息。&lt;/p&gt;

&lt;p&gt;四叉树在游戏项目中的用法包括，二维平面上的有效碰撞检测搜索范围，地形的有效展示范围，在地图上找某块方块上的人或事，以及包括二维平面的寻路网格构建等。&lt;/p&gt;

&lt;h6 id=&quot;八叉树搜索算法&quot;&gt;八叉树搜索算法&lt;/h6&gt;

&lt;p&gt;八叉树与四叉树类似，但在理念上更关注三维空间上的划分。八叉树把一个立方体从纵向和横向各切一刀，分割成八个相同大小的立方体。每个小立方体也就相当于子节点，也可以再分割成八个相同大小的更小的立方体，用树形结构表示每个大大小小的立方体形成了八叉树。经过树形结构构造八叉树就很容易用在3D空间中的场景管理，可以很快地获取3D场景中的某范围物体，或测试与其它物体是否有碰撞以及是否在可视范围内。八叉树在实际游戏中用到的包括，渲染中的渲染裁切，和物理引擎中的碰撞检测。&lt;/p&gt;

&lt;h6 id=&quot;优秀的算法都是能找到事物特征并且利用好事物特征的算法不同种类的算法有其自身的理念混用或合用几个算法也是常有的事理解和知晓是第一步灵活运用真的不是一朝一夕的事&quot;&gt;优秀的算法都是能找到事物特征并且利用好事物特征的算法，不同种类的算法有其自身的理念，混用或合用几个算法也是常有的事，理解和知晓是第一步，灵活运用真的不是一朝一夕的事。&lt;/h6&gt;

&lt;p&gt;参考资料&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;《算法导论》
&lt;/code&gt;&lt;/pre&gt;
</content>
 </entry>
 
 <entry>
   <title>思路探讨(二十) 如何将你的家庭与普通人拉开差距</title>
   <link href="http://www.luzexi.com/2019/02/04/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A820"/>
   <updated>2019-02-04T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/02/04/思路探讨20</id>
   <content type="html">&lt;h6 id=&quot;其实上大部分人都知道怎么拉开差距但知易行难啊知道和执行是两码事&quot;&gt;其实上大部分人都知道怎么拉开差距，但知易行难啊，知道和执行是两码事。&lt;/h6&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;办法是，努力学习，努力工作，努力做人，努力保持健康。不可能是偷懒，投机，作弊，暴力，反社会。&lt;/p&gt;

&lt;p&gt;这属于正常思路的范畴。可是大部分人都不信啊，为什么不信，效果太慢，太枯燥乏味，需要5-10年甚至20年。于是他们一定要寻找另外一条路，从此踏上了寻找捷径的旅途。&lt;/p&gt;

&lt;p&gt;他们相信世界上有这么一条捷径，他们一定能找到，比别人用的时间少花的精力少就能夺得世界，不能夺得世界也能拥有自己一块领土，颐养天年。他们对这种想法的意志非常坚定。&lt;/p&gt;

&lt;p&gt;可是真的有么？弯弯绕，最后都要回到原地，回来后发现还是要按部就班得走，这时才发现差距已经拉开了。&lt;/p&gt;

&lt;p&gt;不可否认，有些人还真的是运气好到爆炸，真的被他找到一条路，通往世界的宝藏中心，财富和权利的交集处。&lt;/p&gt;

&lt;p&gt;但故事只是个开头，后面故事怎么发展，不能下定论，虽然我的观点是99%最后都灰飞烟灭了，但世界不会因为我怎么想而怎么走。&lt;/p&gt;

&lt;p&gt;无论如何，这个概率也是几十亿到几百亿分之一以下的概率，基本可以忽略不计，但人就是这样，永远认为自己会是那个幸运儿。&lt;/p&gt;

&lt;h6 id=&quot;啊哈看到没有只要按正常人的正常思路走就能击败大部分人与普通人拉开差距&quot;&gt;啊哈，看到没有，只要按正常人的正常思路走，就能击败大部分人，与普通人拉开差距。&lt;/h6&gt;

&lt;p&gt;为什么？因为普通人耐不住寂寞，受不了枯燥，行动不下去。&lt;/p&gt;

&lt;p&gt;那么真的有人能耐得住寂寞，受的了枯燥，沉着冷静吗？&lt;/p&gt;

&lt;p&gt;当然没有，别骗自己了，我们都是人，都是恒温动物，都有血有肉，都有感情，贪嗔痴慢疑一样不能少。&lt;/p&gt;

&lt;p&gt;每个人都是一样的，拥有完整的人性的弱点，谁都躲不过。&lt;/p&gt;

&lt;p&gt;一个拥有完整人性弱点的人，是不可能完完全全、彻彻底底反人性的。&lt;/p&gt;

&lt;p&gt;那什么样的人能够完完全全、彻彻底底反人性？疯子，神经病。无论什么原因病态彻底扰乱了人性的特点。&lt;/p&gt;

&lt;p&gt;我肯定不想成为疯子，我也意识到人性的弱点无法改变，但我又想抗拒人性弱点，怎么办？&lt;/p&gt;

&lt;h6 id=&quot;1在枯燥中寻找乐趣不惜一切方式方法围绕目标消除枯燥即使会暂时的退步也是以退为进的一种战略战术&quot;&gt;1，在枯燥中寻找乐趣。不惜一切方式、方法，围绕目标消除枯燥，即使会暂时的退步，也是以退为进的一种战略战术。&lt;/h6&gt;

&lt;h6 id=&quot;2在痛苦中寻找欢乐痛苦是必然的疲惫也是必然的要在痛苦中找到欢乐在疲惫中找到精神支柱就不会像表面上看起来那么苦了虽然表面上还是苦的累的但当事人心里却是甜的是兴奋的&quot;&gt;2，在痛苦中寻找欢乐。痛苦是必然的，疲惫也是必然的，要在痛苦中找到欢乐，在疲惫中找到精神支柱。就不会像表面上看起来那么苦了，虽然表面上还是苦的累的，但当事人心里却是甜的是兴奋的。&lt;/h6&gt;

&lt;h6 id=&quot;3迷茫时给自己打精神麻醉剂告诉自己是金子总会发光的不要忘了事业财富并不是直线向上的而是一个波动的曲线无论有多强大的心生命中充斥着大部分的都是失败因此迷茫是必然的而且大部分时候都在迷茫真正看清前方道路的时间没几分钟不一会又会再次陷入迷茫不要在迷茫上浪费太多的时间告诉自己是金子总会发光的不要犹豫了先冲上去拨开云雾&quot;&gt;3，迷茫时给自己打精神麻醉剂，告诉自己是金子总会发光的。不要忘了事业，财富，并不是直线向上的，而是一个波动的曲线。无论有多强大的心，生命中充斥着大部分的，都是失败。因此迷茫是必然的，而且大部分时候都在迷茫，真正看清前方道路的时间没几分钟，不一会又会再次陷入迷茫。不要在迷茫上浪费太多的时间，告诉自己是金子总会发光的，不要犹豫了，先冲上去拨开云雾。&lt;/h6&gt;
</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第一章，C#要点技术(五) 排序算法</title>
   <link href="http://www.luzexi.com/2019/02/03/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-CSharp%E8%A6%81%E7%82%B9%E6%8A%80%E6%9C%AF5"/>
   <updated>2019-02-03T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/02/03/Unity3D高级编程之进阶主程-CSharp要点技术5</id>
   <content type="html">&lt;p&gt;年纪越大写程序写的越多，时间越长就越觉得算法的重要性，基础能力决定你到底能走多远。我们不是写一两年程序就完事了，从毕业算起，我们可能要写20-30年的程序，这段漫长的长跑路程中，最终比的不是谁熟悉API比较多，也不是谁用插件用的有多熟练，更不是比谁更熟悉某软件，而是比谁的基础能力强，比谁的算法效率高，比谁对底层原理更加熟知于心，比谁能够解决更复杂的系统和需求。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;这其中算法能力比较重要，在程序员生涯中算法能力是基础能力的一种，很多时候程序的好坏，一方面看的是写程序的经验，另一方面看的是对计算机原理的理解程度，还有一方面看的是对算法的理解和运用熟练度。&lt;/p&gt;

&lt;p&gt;算法能力不仅仅代表的是表面的算法熟知度，也是一种追求卓越的精神高度，即对所有经过自己手的程序效率负责的精神高度。在平时工作中某一处的算法有可能运用的很好，其他地方却依然用了很烂的算法或者算法运用的不太妥当，其对于整体程序效率来说效率依然很糟糕。因此在平时的编程习惯中，做到时刻关注算法效率是区分中、高水平的一个大的关键点。&lt;/p&gt;

&lt;p&gt;在平时的编程工作中，排序和搜索算法最为常用。毫不夸张的说一个项目中有90%的算法都是排序和搜索算法，如果说我们把这90%的算法提高到一个很高效的程度，那么剩下的10%算法处理起来压力会小很多。我们这节就来讲讲排序的各种算法，以及运用到具体项目中去时的优劣程度。&lt;/p&gt;

&lt;h3 id=&quot;快速排序算法&quot;&gt;快速排序算法&lt;/h3&gt;

&lt;p&gt;快速排序，是种最坏情况为O(n^2)的排序算法，虽然这个最坏情况比较差但快速排序通常是用于排序的最佳的实用选择，这是因为它的平均性能比较好，其排序期望运行时间为 O(nlogn) 且 O(nlogn)记号中隐含的常数因子很小，另外还不消耗额外的内存空间，在嵌入式虚存环境中也能很好工作，因此广受人们欢迎是最常用最好用用的最多的排序算法。&lt;/p&gt;

&lt;p&gt;快速排序算法的排序步骤很简单：&lt;/p&gt;

&lt;p&gt;1.从序列中选一个元素作为基准元素&lt;/p&gt;

&lt;p&gt;2.把所有比基准元素小的元素移到基准元素的左边，把基准元素大的移到右边。&lt;/p&gt;

&lt;p&gt;3.对分开来的二个一大一小的区块进行递归再筛选，对两个区块同样进行1、2的两个步骤处理。&lt;/p&gt;

&lt;p&gt;简单来说就是选取一个区域里的数字，把这个区域按这个数字分成两半，一半小一半大，然后继续对这两半做同样的操作，直到所有筛选都完成就完成了排序。&lt;/p&gt;

&lt;p&gt;排序算法最差的情况是，每次都选到一个最小的或最大的数字，这样每次筛选大小时都要充分移动，这种概率比较低。快速排序是最常用的排序方法，所以我们要着重优化此算法，对大量使用的算法和程序我们需要格外的重视。&lt;/p&gt;

&lt;h3 id=&quot;优化&quot;&gt;优化：&lt;/h3&gt;

&lt;h6 id=&quot;1随机选择中轴数&quot;&gt;1.随机选择中轴数&lt;/h6&gt;

&lt;p&gt;在快速排序中为了选择哪个元素作为中位数是比较关键的，这影响了算法排序效率，如果选中的数字不是中间的数字，而是一个比较偏小或者比较偏大的数字，那么排序的速度就会大大降低。如果选中的刚好是最大的或者最小的数字则更加糟糕，左边或右边完全没有数字可以排，相当于一次完整的遍历只排序了一个元素。&lt;/p&gt;

&lt;p&gt;如果我们去找到区间的那个准确的中位数会增加更多消耗，所以我们只能减少得到最坏情况的概率，我们可以随机一个列表上的元素来作为基准元素，随机是为了减小选到最大和最小值的概率，但随机也时常会选到坏的基准元素，实际上随机数并没有对排序提供多大的帮助。&lt;/p&gt;

&lt;h6 id=&quot;2三数取中&quot;&gt;2.三数取中&lt;/h6&gt;

&lt;p&gt;为了让选择的中轴数更加接近中位数，可以先选择头、中点、尾，三个数字先来次排序，把最小的放在头，中间的放在中，最大的放在尾，用三个数字去提高有效接近中位数的中轴元素。&lt;/p&gt;

&lt;p&gt;每次每个区间的头、中、尾的排序前都做这个操作，也就是说每次排序前，中轴数数都不可能是最小的，起码是区间里第2小的或者第2大的，这样选出来的中轴数靠近中位数的概率就很大了。&lt;/p&gt;

&lt;p&gt;那么是否可以把三个数扩大到4个或M个数，其实过多数字的选择就相当于多出了个一个排序算法，减慢的二分排序的效果，实际效果不如3个数字来的快。虽然可以用随机选取3个数字的方式来替代做选择，但实际上随机选择并没有什么帮助，况且伪随机数的计算和冲突的解决也是需要消耗cpu的，因此最终还是使用三数取中的做法是选择接近中位数的比较有效的办法。&lt;/p&gt;

&lt;h6 id=&quot;3小区间使用插入排序&quot;&gt;3，小区间使用插入排序&lt;/h6&gt;

&lt;p&gt;排序算法都有各自的使用量级，当量级不同时则排序效率可能不一样，插入排序就依赖于序列的有序性和排序元素数量，即排序的效率由排序列表的有序程度决定，也与排序的元素数量多少有关，如果序列的排序刚好是反序的则排序时效率最差，反之如果是有序序列则效率最快。&lt;/p&gt;

&lt;p&gt;插入排序有一个特点，排序序列越长效率越差，对于短序列的排序效果则很好，高效排序序列长度在8左右。于是我们可以用这个特点来改善我们在快速排序中的效率，即在排序中，当切分的区块小于等于8个时，就采用插入排序来替代快速排序，因为8个以下的元素排序时，插入排序能发挥出更好的效率，我们就混合它与快速排序的逻辑关系，这样使得排序效率更高，其他时候任然采用快速排序算法。&lt;/p&gt;

&lt;h6 id=&quot;4缩小分割范围与中轴数相同的合并在一起&quot;&gt;4，缩小分割范围，与中轴数相同的合并在一起&lt;/h6&gt;

&lt;p&gt;除了选择更加靠近中位数的数字作为中轴数，以及小范围时使用更快的排序方式外，我们还可以通过缩小排序范围来提高排序效率。&lt;/p&gt;

&lt;p&gt;我们可以把与中轴数相同的数可以合并到中轴数左右的位置，这样使得分割后的两边范围缩小，范围越小排序的速度就越快，刨去了更多不需要排序的元素。具体操作步骤为，每次在分割比较中，当元素与中轴数相等时则直接移动到中轴数身边，移动完毕后划分范围从中轴数变为最边上的相同元素的位置，用这种方式来缩小范围，让后续的排序减少排序元素。&lt;/p&gt;

&lt;h6 id=&quot;快速排序是最常用也是使用范围最广的排序算法铭记于心是有必要的&quot;&gt;快速排序是最常用也是使用范围最广的排序算法，铭记于心是有必要的。&lt;/h6&gt;

&lt;h6 id=&quot;除了快速排序堆排序也是相对比较常用的特别是堆排序的优先级队列&quot;&gt;除了快速排序，堆排序也是相对比较常用的，特别是堆排序的优先级队列&lt;/h6&gt;

&lt;p&gt;堆排序本身结构是由完全二叉树这样的结构支撑的，普通的堆排序比快速排序更低效一点，但堆排序中的最大最小堆的优先级队列异常用有，即只关注最大或最小值，在不断增加和删除根节点元素的情况下获取最大或最小值。优先级队列在排序完成后，数据堆就成了一个头顶着一个最大值或最小值的数据结构，这种数据结构更有利于获取根节点的最大最小值节点，在后面程序逻辑中当需要插入新元素、修改旧元素、以及推出最大最小值时效率比较高。优先级队列在实时获取最大最小值时高效的特点，导致它在寻路系统的A星算法中特别有用，因此最大最小堆排序常常用于A星算法。&lt;/p&gt;

&lt;p&gt;由于堆排序是一种完全二叉树结构，所以这种结构可以用一维数组表示，这样则会让效率更加高一些因为内存是连续的。一维数组在表示二叉树时，通常都是以完全二叉树形式表示每个节点与其子节点，每个节点都一一对应数组上的索引规则，即如果 i 为节点索引时，则i&lt;em&gt;2 和 i&lt;/em&gt;2+1 就是它的两个子节点，而索引 i 的父节点位置可以用 i/2 来表示，数组中任何节点都以遵循这种规则。以此类推到子节点，即 (i&lt;em&gt;2)&lt;/em&gt;2 和 (i&lt;em&gt;2)&lt;/em&gt;2+1 就是 i&lt;em&gt;2 这个索引的两个字节点，所有子节点自身的索引直接除以2就是父节点的索引，即 i&lt;/em&gt;2 和 i*2+1 的除以2后取整就是他们的父节点索引 i 。&lt;/p&gt;

&lt;p&gt;最大最小堆的优先级队列的操作分为，插入元素，返回最大或最小值，返回并删除最大最小值，查找并修改某个元素，其中关键的算法在于插入新元素和删除最大最小元素。其基本思想是利用完全二叉树的特性将新元素放入二叉树的叶子节点上，然后比较它与父节点的大小，如果它比父节点大(最小堆的情况)则结束，否则就与父节点交换一下，继续比较直到没有父节点或者父节点比它小。删除根节点则反过来，把叶子节点放入根节点，然后找到这个新根节点的实际位置，即比较它与两个子节点大小，如果比它们小(最小堆的情况)则结束，否则取最小值(最小堆的情况)替换节点位置，然后再继续向下比较和替换，直到停止或者替换到叶子节点时再没有子节点可比较就算完成操作。&lt;/p&gt;

&lt;p&gt;我们来看两幅图就能很好的理解插入与删除的步骤：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;缺图1

缺图2
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;图1为插入时的堆排序步骤，不断与父节点比较并交换，直到到根节点或者无法交换。图2为删除根节点的情况，把根节点与叶子节点交换后，叶子节点再从根部不断比较并下移到应有的位置。&lt;/p&gt;

&lt;h3 id=&quot;其他排序算法简要概括&quot;&gt;其他排序算法简要概括&lt;/h3&gt;

&lt;p&gt;其他排序虽然使用频率没有快速排序来的多，但也会在很多特定的系统上出现。&lt;/p&gt;

&lt;p&gt;桶排序，把所有的元素按一定大小范围分成N个组，对每个组进行快速排序，最终得到有序的数组，并且得到N个桶的记录，虽然第一次排序的速度不怎么样，但这N个桶的信息记录下来后对于后面的程序逻辑有非常大的帮助。比如我们需要进行模糊排序或模糊搜索，这种桶信息就会有很大帮助。&lt;/p&gt;

&lt;p&gt;基数排序，是针对元素的特性来实时的‘分配式排序’，利用数字的特性按个位数，十位数，百位数的性质放入0-9的桶中不用排序，几次合并后就有了序数组，利用元素特性排序的速度比任何其他排序方式都要快速。这种算法思路教会我们在运用算法时，可以从元素的特性着手，找到它的特点就有可能找到更合适更快的算法。&lt;/p&gt;

&lt;h6 id=&quot;基本的常用的几种排序算法是我们必须了解的面对比较复杂难解决的难题我们就需要更为广阔的思路算法在实际运用中并不是固定的适合的才是最好的我们应该随着问题环境的变化而变化找到最佳的突破口&quot;&gt;基本的、常用的几种排序算法是我们必须了解的，面对比较复杂难解决的难题，我们就需要更为广阔的思路，算法在实际运用中并不是固定的，适合的才是最好的，我们应该随着问题环境的变化而变化，找到最佳的突破口。&lt;/h6&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第一章，C#要点技术(四) 委托、事件、装箱、拆箱</title>
   <link href="http://www.luzexi.com/2019/01/26/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-CSharp%E8%A6%81%E7%82%B9%E6%8A%80%E6%9C%AF4"/>
   <updated>2019-01-26T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/01/26/Unity3D高级编程之进阶主程-CSharp要点技术4</id>
   <content type="html">&lt;h3 id=&quot;委托delegate与事件event的实质&quot;&gt;委托(delegate)与事件(Event)的实质&lt;/h3&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;使用过C或C++的同学都对指针很清楚，指针是个需要谨慎对待的东西，它不仅仅可以指向变量的地址，还可以指向函数的地址，本质上它是指向内存的地址。&lt;/p&gt;

&lt;p&gt;在C#中万物皆是类，大部分时间里都没有指针的身影，最多也只是引用，因为指针被封装在内部函数当中。不过回调函数却依然存在，于是C#多了一个委托(delegate)的概念，所有函数指针都以委托的方式来完成的。委托可以被视为一个更高级的函数指针，它不仅仅能把地址指向另一个函数，而且还能传递参数、获得返回值等多个信息。系统还为委托对象自动生成了同步、异步的调用方式，开发人员使用 BeginInvoke、EndInvoke 方法就可以避开 Thread类 从而直接使用多线程调用。&lt;/p&gt;

&lt;h6 id=&quot;那么究竟委托delegate在c中是如何实现的呢我们来一探究竟&quot;&gt;那么究竟委托(delegate)在C#中是如何实现的呢？我们来一探究竟。&lt;/h6&gt;

&lt;p&gt;首先不要错误的认为委托是一个语言的基本类型，我们在创建委托delegate时其实就是创建了一个delegate类实例，这个delegate委托类继承了System.MulticastDelegate类，类实例里有，BeginInvoke、EndInvoke、Invoke三个函数，分别表示，异步开始调用，结束异步调用，以及直接调用。&lt;/p&gt;

&lt;p&gt;不过我们不能直接写个类来继承 System.MulticastDelegate类，因为它不允许被继承在明文上，它的父类Delegate类也同样有这个规则，官方文档中写的就是这么个规则：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	MulticastDelegate is a special class. Compilers and other tools can derive from this class, but you cannot derive from it explicitly. The same is true of the Delegate class.

	MulticastDelegate是一个特殊的类，编译器或其他工具可以从它这里继承，但你却不能直接继承它。Delegate类也有同样的规则。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Delegate类中有个变量是用来存储函数地址的，当变量操作 =(等号) 时，把函数地址赋值给变量存起来。不过这个存储函数地址的变量是个可变数组，你可以认为是个链表，每次直接赋值时会换一个链表。&lt;/p&gt;

&lt;p&gt;Delegate委托类还重写了 +=，-= 这两个操作符，其实就是对应 MulticastDelegate 的 Combine 和 Remove 方法，当对函数操作 += 和 -= 时，相当于把函数地址推入了链表尾部，或者移出了链表。&lt;/p&gt;

&lt;p&gt;当委托被调用时，委托实例会把所有链表里的函数依次按顺序用传进来的参数调用一遍。官方文档中写的就是如上述所说：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;A MulticastDelegate has a linked list of delegates, called an invocation list, consisting of one or more elements. When a multicast delegate is invoked, the delegates in the invocation list are called synchronously in the order in which they appear. If an error occurs during execution of the list then an exception is thrown.

MulticastDelegate 中有一个已经连接好delegate的列表，被称为调用列表，它由一个或者更多的元素组成。当一个multicast delegate被启动调用时，所有在调用列表里的 delegate 都会被按照它们出现的顺序调用。如果一个错误在执行列表期间遇到就会立马抛出异常并停止调用。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;看到这里我们彻底明白了原来 delegate 关键字其实只是个修饰用词，背后是由C#编译器来重写的代码，我们可以认为是编译时把delegate这一句换成了Delegate，从而变成了一个class它继承System.MulticastDelegate类。&lt;/p&gt;

&lt;h6 id=&quot;那么什么是event它和delegate又有什么关系&quot;&gt;那么什么是event，它和delegate又有什么关系？&lt;/h6&gt;

&lt;p&gt;event 很简单，它在委托delegate上，又做了一次封装，这次封装的意义是，限制用户直接操作delegate委托实例中变量的权限。&lt;/p&gt;

&lt;p&gt;封装后，用户不再能够直接用赋值(即使用 = 等号操作符)操作来改变委托变量了，只能通过注册或者注销委托的方法来增减委托函数的数量。也就是说被 event 声明的委托不再提供 ‘=’ 的操作符，但仍然有 ‘+=’ 和 ‘-=’ 的操作符可供操作。&lt;/p&gt;

&lt;p&gt;为什么要限制呢？因为在平时的编程中，由于项目太过庞大，经手的人员数量太多，导致我们常常无法得知其他人在编写的代码是什么有什么意图，这样公开的delegate委托会直接暴露在外，随时会被‘=’赋值而清空了前面累积起来的委托链表，委托的操作权限范围太大导致问题会比较严重。申明 event 后，编译器内部重新封装了委托，让暴露在外面的委托不再担心随时被清空和重置的危险。因为经过 event 封装后不再提供赋值操作来清空前面的累加，只能一个个注册或者一个个注销委托(或者说函数地址)，这样就保证了谁注册就必须谁负责销毁的目的，更好的维护了delegate的秩序。&lt;/p&gt;

&lt;h3 id=&quot;装箱和拆箱&quot;&gt;装箱和拆箱&lt;/h3&gt;

&lt;p&gt;什么是装箱和拆箱。它其实很简单，把值类型实例转换为引用类型实例，就是装箱。相反，把引用类型实例转换为值类型实例，就是拆箱。&lt;/p&gt;

&lt;p&gt;针对这个解释继而又发出了疑问，什么是值类型，什么是引用类型。值类型的变量会直接存储数据，如byte，short，int，long，float，double，decimal，char，bool 和 struct 统称为值类型，而引用类型的变量持有的是数据的引用，其真实数据存储在数据堆中，如所有的class实例的变量，string 和 class统称为引用类型。当声明一个类时，只在堆栈(堆或栈)中分配一小片内存用于容纳一个地址，而此时并没有为其分配堆上的内存空间，因此它是空的为null，直到使用 new 创建一个类的实例时，分配了一个堆上的空间，并把堆上空间的地址保存给这个引用变量，这时这个引用变量才有真正指向内存空间。&lt;/p&gt;

&lt;p&gt;我们可以解释的再通俗点，举个例子来说明：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;int a = 5;

object obj = a;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;就是装箱，因为a 是值类型是直接有数据的变量，obj为引用类型是指针与内存是拆分开来的，把 a 赋值给 b 实际上就是 b 为自己创建了一个指针并指向了a的数据空间，然后继续上面代码:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;a = (int)obj;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;就是拆箱，相当于把 obj 指向的内存空间复制一份交给了a，因为 a 是值类型，它不允许指向某个内存空间只能靠复制数据来传递数据。&lt;/p&gt;

&lt;h6 id=&quot;为何需要装箱&quot;&gt;为何需要装箱。&lt;/h6&gt;

&lt;p&gt;值类型是在声明时就初始化了，因为它一旦声明就有了自己的空间因此它不可能为null，也不能为null。 而引用类型在分配内存后，它其实只是一个空壳子，可以认为是指针，初始化后不指向任何空间，因此默认为null。&lt;/p&gt;

&lt;p&gt;值类型有，所有整数，浮点数，bool，以及 Struct 申明的结构，这里要注意 Struct 部分，它是经常我们犯错误的地方，常常很多人会把它当作类来使用是很错误的行为。因为它是值类型，在复制操作时是通过直接拷贝数据完成操作的，所以常常会有a、b同是结构的实例，a赋值给了b，当 b 更改了数据后发现 a 的数据却没有同步的疑问出现，事实上根本就是两个数据空间，当 a 赋值给 b 时其实并不是引用拷贝，而是整个数据空间拷贝，相当于有了a、b为两个不同西瓜，只是长得差不多而已。&lt;/p&gt;

&lt;p&gt;引用类型包括，类，接口，委托(委托也是类)，数组以及内置的object与string。前面说了delegate也是类，类都是引用类型，虽然有点问题也不妨碍它是一个比较好记的口号。虽然 int 等值类型也都是类，只不过它们是特殊的类，是值类型的类，因为在C#里万物皆是类。&lt;/p&gt;

&lt;p&gt;话锋一转这里稍微阐述下堆和栈内存，因为很多人都错误的认识了堆栈内存，为什么要分栈内存和堆内存，用简短的语言阐述下：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	栈是本着先进后出的数据结构(LIFO)原则的存储机制，它是一段连续的内存，所以对栈数据的定位比较快速, 而堆则是随机分配的空间, 处理的数据比较多, 无论如何, 至少要两次定位。堆内存的创建和删除节点的时间复杂度是O(logn)。栈创建和删除的时间复杂度则是O(1)，栈速度更快。

	那么既然栈速度这么快，全部用栈不就好了。这又涉及到生命周期问题，由于栈中的生命周期是必须确定的，销毁时必须按次序销毁，从最后分配的块部分开始销毁，创建后什么时候销毁必须是一个定量，所以在分配和销毁时不灵活，基本都用于函数调用和递归调用中，这些生命周期比较确定的地方。相反堆内存可以存放生命周期不确定的内存块，满足当需要删除时再删除的需求，所以堆内存相对于全局类型的内存块更适合，分配和销毁更灵活。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;很多人把值类型与引用类型归类为栈和堆内存分配的区别是错误的，栈内存主要为确定性生命周期的内存服务，堆内存则更多的是无序的随时可以释放的内存。因此值类型可以在堆内也可以在栈内，引用类型的指针部分也一样可以在栈和堆内，区别在于引用类型指向的内存块都在堆内，一般这些内存块都在委托堆内，这样便于内存回收和控制，我们平时说的GC就会做些回收和整理的事。也有非委托堆内存不归委托堆管理的部分，是需要自行管理的，比如C++写了个接口生成一个内存块，将指针返回给了C#程序，这个非委托堆内存需要我们自行管理，C#也可以自己生成非委托堆内存块。&lt;/p&gt;

&lt;p&gt;大部分时候只有当程序、逻辑或接口需要更加通用的时候才会需要装箱。比如调用一个含类型为object的参数的方法，该object可支持任意为型，以便通用。当你需要将一个值类型(如Int32)传入时，就需要装箱。又比如一个非泛型的容器为了保证通用，而将元素类型定义为object，当值类型数据加入容器时需要装箱。&lt;/p&gt;

&lt;p&gt;我们来看看装箱的内部操作：&lt;/p&gt;

&lt;p&gt;装箱： 根据相应的值类型在堆中分配一个值类型内存块，再将数据拷贝给它。按三步进行。&lt;/p&gt;

&lt;p&gt;第一步：在堆内存中新分配一个内存块(大小为值类型实例大小加上一个方法表指针和一个SyncBlockIndex)。&lt;/p&gt;

&lt;p&gt;第二步：将值类型的实例字段拷贝到新分配的内存块中。&lt;/p&gt;

&lt;p&gt;第三步：返回内存堆中新分配对象的地址。这个地址就是一个指向对象的引用了。&lt;/p&gt;

&lt;p&gt;拆箱则更为简单点，先检查对象实例，确保它是给定值类型的一个装箱值，再将该值从实例复制到值类型变量的内存块中。&lt;/p&gt;

&lt;h6 id=&quot;装箱拆箱对执行效率有哪些影响如何优化&quot;&gt;装箱、拆箱对执行效率有哪些影响，如何优化。&lt;/h6&gt;

&lt;p&gt;由于装箱、拆箱时生成的是全新的对象，不断得分配和销毁内存会不但大量消耗CPU，也同时增加了内存碎片，降低了性能。 那该如何做呢？&lt;/p&gt;

&lt;p&gt;最需要我们做的就是减少装箱、拆箱的操作，在我们编程规范中要牢记这种比较浪费CPU的操作，在平时编程要特别注意。&lt;/p&gt;

&lt;p&gt;整数、浮点数、布尔等数值型变量的变化手段很少，变不出什么花样来，主要靠加强规范减少装拆箱的情况来提高性能。Struct 有点不一样，它既是值类型，又可以像类一样继承，用途多转换的途径多可变的花样多，稍不留神花样就变成了麻烦，所以这里讲讲 Struct 变化后的优化方法。&lt;/p&gt;

&lt;p&gt;1、Struct 通过重载函数来避免拆箱、装箱。&lt;/p&gt;

&lt;p&gt;比如常用的ToString()，GetType()方法，如果 Struct 没有写重载ToString()和GetType()的方法，就会在 Struct 实例调用它们时先装箱再调用，导致内存块重新分配性能损耗，所以对于那些需要调用的引用方法，必须重载。&lt;/p&gt;

&lt;p&gt;2、通过泛型来避免拆箱、装箱。&lt;/p&gt;

&lt;p&gt;不要忘了 Struct 也是可以继承的，在不同的、相似的、父子关系的 Struct 之间可以用泛型来传递参数，这样就不用装箱后再传递了。&lt;/p&gt;

&lt;p&gt;比如B,C继承A，就可以有这个泛型方法 void Test&lt;T&gt;(T t) where T:A，以避免使用object引用类型形式传递参数。&lt;/T&gt;&lt;/p&gt;

&lt;p&gt;3、通过继承统一的接口提前拆箱、装箱，避免多次重复拆箱、装箱。&lt;/p&gt;

&lt;p&gt;很多时候拆装箱不可避免，那么我们就让多种 Struct 继承某个统一的接口，不同的 Struct 就可以有相同的接口。把 Struct 传递到其他方法里去时就相当于提前进行了装箱操作，在方法中得到的是引用类型的值，并且有它需要的接口，避免了在方法中重复多次的拆装箱操作。&lt;/p&gt;

&lt;p&gt;比如 Struct A 和 Struct B 都继承接口 I，我们调用的方法是 void Test(I i)。当调用Test方法时传进去的 Struct A 或 Struct B 的实例都相当于提前做了装箱操作，Test里拿到的参数后就不用再担心内部再次装箱拆箱问题了。&lt;/p&gt;

&lt;p&gt;最后我依然要提醒大家 struct 值类型数据结构如果没有理解它的原理用起来可能会引起很多麻烦，切记盲目认为使用结构体会让性能提升，在没有完全彻底理解之前就冒然大量使用可能会对你的程序性能带来重创。&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第一章，C#要点技术(三) 浮点数的精度问题</title>
   <link href="http://www.luzexi.com/2019/01/19/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-CSharp%E8%A6%81%E7%82%B9%E6%8A%80%E6%9C%AF3"/>
   <updated>2019-01-19T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/01/19/Unity3D高级编程之进阶主程-CSharp要点技术3</id>
   <content type="html">&lt;h3 id=&quot;浮点数的精度问题&quot;&gt;浮点数的精度问题&lt;/h3&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;很多同学在平时的项目中会用double类型来替代float来提高精度，错误的认为double可以解决精度问题。其实平常我在编程中极少使用double类型，浮点数计算在我们大多数项目中并没有使用到特别的科学计算部分，所以float基本都够用，其实double也同样有精度问题，无论怎么样都是无法避免精度导致的在逻辑中的不一致的问题。&lt;/p&gt;

&lt;p&gt;我们不妨比较下float 与 double 来来看看它们有什么不同。float和double所占用的比特位数不同会导致精度不同，float是32个位占用4字节而double是64个位占用了8个字节，因此它们在计算时也会引起计算效率的不同。&lt;/p&gt;

&lt;p&gt;实际工作中我们很多时候想试图通过使用double替换float来解决精度问题，最后基本都会以失败告终。因此我们要认清精度这个问题的根源，才能真正解决问题，我们先来看下浮点数在内存中到底是如何存储的。&lt;/p&gt;

&lt;p&gt;计算机只认识 0 和 1，无论什么形式的数字都是一样的，无论是整数还是小数在计算机中都以二进制的方式储存在内存中的，那么浮点数是以怎样的方式来存储的呢我们来看下。根据 IEEE 754 标准，任意一个二进制浮点数 F 均可表示为：F = (-1 ^ s) * (1.M) * (2 ^ e)。&lt;/p&gt;

&lt;p&gt;从公式中可以看出，它被分为了3个部分，符号部分即 s部分 和 尾数部分即 M部分 和 阶码部分即 e部分，s为sign符号位0或1，M为尾数指具体的小数用二进制表示，它完整的表示为1.xxx中的1后面的尾数部分因为称它为尾数，e是比例因子的指数是浮点数的指数。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;(float1.png)

(float2.png)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上图所示的是32位和64位的浮点数存储结构，即float和double的存储结构。不论是32位浮点数还是64位浮点数，它们都由S、E、M三部分组成并以相同的公式来计算得到最终值。&lt;/p&gt;

&lt;p&gt;其中E的阶符采用隐含方式，即采用移码方法来表示正负指数。移码方法对两个指数大小的比较和对阶操作比较方便，因为阶码的值大时其指向的数值也是大的，这样更容易计算和辨认。移码（又叫增码）是符号位取反的补码，例如float的8位价码，应将指数e加上一个固定的偏移值127（01111111），即 e 加上 127才是存储在二进制中的数据。&lt;/p&gt;

&lt;p&gt;尾数M则更为简单点，它只表示1后面的小数部分，而且是二进制直译的那种，然后再根据阶码来平移小数点，最后根据小数点的左右部分得出整数部分和小数部分的数据。&lt;/p&gt;

&lt;p&gt;以9.625 为例，转换为二进制为 1001.101 可以表达为 1.001101×(2^3)，因此M尾数部分为 00110100000000000000000 去掉了小数点左侧的 1，并用 0 在右侧补齐。&lt;/p&gt;

&lt;p&gt;其中表示9.625时的 1001.101 的小数部分为什么是“101”，因为整数部分采用 “除2取余法”来得到从10进制转换到2进制的数字，而小数部分则采用 “乘2取整法”得到二进制。因此这里的小数部分 0.625 乘 2 为 1.25 取整得到 1，继续 0.25 乘以 2 为 0.5 取整得到 0，再继续 0.5 乘以 2 为 1 取整为 1，后面都是 0 不再计算，因此得到 0.101 这个小数点后面的二进制。&lt;/p&gt;

&lt;p&gt;再以 198903.19 这个数字为例，先形象直接的转成二进制的数值为，整数部分采用 “除2取余法”，小数部分采用 “乘2取整法”，并把整数和小数部分用点号隔开的到&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	110000100011110111.0011000010100011（截取 16 位小数）
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;以1为首平移小数点后得到 1.100001000111101110011000010100011 * (2 ^ 17)（平移17位）即&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	198903.19(10) = (-1 ^ 0) * 1.100001000111101110011000010100011 * (2 ^ 17)。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;从结果可以看出，小数部分 0.19 转为二进制后，小数位数超过 16 位（已经手算到小数点后 32 位都还没算完，其实这个位数是无穷尽的），因此这里导致浮点数有诸多精度的问题，它很多时候无法准确的表示数字，甚至非常不准确。&lt;/p&gt;

&lt;h6 id=&quot;浮点数的精度问题可不只是小数点的精度问题随着数值越来越大即使是整数也开始会有相同的问题因为浮点数本身是一个-1m--2--e-公式形式得到的数字当数字放大时m的尾数的存储位数没有变化能表达的位数有限自然越来越难以准确表达特别是数字的末尾部分越来越难以准确表达&quot;&gt;浮点数的精度问题可不只是小数点的精度问题，随着数值越来越大，即使是整数也开始会有相同的问题，因为浮点数本身是一个 1.M * (2 ^ e) 公式形式得到的数字，当数字放大时，M的尾数的存储位数没有变化，能表达的位数有限，自然越来越难以准确表达，特别是数字的末尾部分越来越难以准确表达。&lt;/h6&gt;

&lt;p&gt;人们总是觉得精度问题看起来好像很难碰到，但其实并非如此，实际开发中常常碰到而且确实很头疼，如果没有这部分的知识结构很容易在查看问题时陷入无尽的问号。那么我们来看看哪些情况会碰到这类问题。&lt;/p&gt;

&lt;h6 id=&quot;1数值比较不相等&quot;&gt;1.数值比较不相等&lt;/h6&gt;

&lt;p&gt;我们在写程序时经常会遇到到阀值触发某逻辑的情景，比如某个变量，需要从0开始加，每次加某个小于0.01的数，加到刚好0.23时做某事，到0.34时做另外一件事，到0.56时再做另一件。&lt;/p&gt;

&lt;p&gt;这种精确定位的问题，就会遇到麻烦。因为浮点数在加减乘数时无法完全准确定位到某个值，就会在出现，要么比0.23小，要么比0.23大，永远不会刚刚与0.23相等的时候，这时我们不得不放弃 ‘==’ 这个等于号而选择‘&amp;gt;’大于号或者‘&amp;lt;’小于号来解决这种问题的出现。&lt;/p&gt;

&lt;p&gt;如果一定要用等于来做比较，则需要有一个微小的浮动区间，即 ABS(X-Y) &amp;lt; 0.00001 时认为 X 和 Y 是相等的。&lt;/p&gt;

&lt;h6 id=&quot;2数值计算不确定&quot;&gt;2.数值计算不确定&lt;/h6&gt;

&lt;p&gt;比如 x = 1f，y = 2f，z = 1f /5555f * 11110f，如果 x / y &amp;lt;= 0.5f 时做某事，那么理论上说 x / z 也能通过这个if，因为在我们看来z 就等于 2 和 y是一样，但实际上未必是这样的。浮点数在计算时由于位数的限制无法得到精确的数值而是一个被截断的数值，因此 z 的计算结果有可能是0.4999999999991，当x / z 时，结果有可能得到大于0.5。&lt;/p&gt;

&lt;p&gt;这让我们很头疼，在实际编码中，我们经常会遇到这样的情况，在外圈的if判断成立，理论上同样的结果只是公式不同，它们在内圈的if判断却可能不成立，使得程序就出现异常行为，因为看起来应该是得到同样的数值，但结果却不一样。&lt;/p&gt;

&lt;h6 id=&quot;3不同设备计算结果不同&quot;&gt;3.不同设备计算结果不同&lt;/h6&gt;

&lt;p&gt;不同平台上的浮点数计算也有所偏差，由于不同设备上CPU的计算方式不同，导致相同的公式在不同的设备上计算出来的结果有略微的偏差。&lt;/p&gt;

&lt;p&gt;面对这些精度上的问题我们该怎么办，下面就来看看我们有哪些解决方案。&lt;/p&gt;

&lt;h6 id=&quot;我们可以简单点只计算一次认定这个值为准确值只用这个变量结果做判断也省去了多次计算浪费的cpu&quot;&gt;我们可以简单点，只计算一次，认定这个值为准确值，只用这个变量结果做判断，也省去了多次计算浪费的CPU。&lt;/h6&gt;

&lt;p&gt;由于多次计算相同的结果可能造成精度问题，不如只计算一次，只用一次计算的结果把它当做唯一确定性结果，而不使用多次计算得到的结果。排除了多次结果不同导致的问题。&lt;/p&gt;

&lt;p&gt;由于多次看似相等的计算其实得到的结果有可能不同，使得问题变得更复杂，比如上面所说的，1f / 2f 的结果，用 1f / (1f / 5555f * 11110f ) 来表示得到的结果不一样导致问题变得不可控。不如只使用一次计算，不再进行多次计算，认定这次的结果的数值为准确数值，只用这个浮点数值当做判断的标准。&lt;/p&gt;

&lt;p&gt;我在编程中也常用这种方法，多次计算同样的结果也浪费了不少CPU，这种方法简单实用，当前其他方法由于复杂度高，项目需要快速推进，当前遗留的不是很重要的问题的话，可以推延到整体架构演变时再一步步细化。&lt;/p&gt;

&lt;p&gt;当然这种方法使用范围比较小，可能不适用你所在的项目我们可以继续看看其他的解决方案。&lt;/p&gt;

&lt;h6 id=&quot;我们可以改用int或long型来替代浮点数&quot;&gt;我们可以改用int或long型来替代浮点数。&lt;/h6&gt;

&lt;p&gt;浮点数和整数的计算方式都是一样的，只是小数点部分不同而已，那么完全可以把浮点数乘以10的幂次得到更准确的精度部分的数字，把自己需要的精度提上来用整数表示。&lt;/p&gt;

&lt;p&gt;比如保留3位精度，所有浮点数都乘以1万来存储(因为第四位不是很准确了)，1.5变成了15000的整数，9.9变成了99000整数存储。&lt;/p&gt;

&lt;p&gt;这样整数 15000 乘以 99000 得到的结果，与，整数30000 除以 2 再乘以 99000 得到的结果是完完全全相等的。&lt;/p&gt;

&lt;p&gt;再复杂点 原来 2.5 / 3.1 * 5.1 与 0.8064 * 5.1，两者都约等于 4.1126，用整数替代，2500 / 31 * 51 与 80 * 51，等于 4080，把4080看作 4.08 虽然精度出现问题，但是前两者结果不一致，而后两者结果完全相同，使用整数来代替小数使得一致性得到了保证。&lt;/p&gt;

&lt;p&gt;如果你觉得用整数做计算精度问题比较大，我们可以再扩大数值上10的幂次，来看看扩大后如果是 250000 / 31 * 51 就等于 411290，是不是发现精度提高了。但问题又来了，乘以10的幂次来提高精度时，当浮点数值比较大时就会超出了整数的最大上限2 ^ 32 - 1或者2 ^ 64 - 1 的问题。&lt;/p&gt;

&lt;p&gt;如果你觉得精度可以接受，并且数值计算的范围肯定会被确定在32位或64位整数范围内，则可以用这种int和long的方式来代替浮点数。&lt;/p&gt;

&lt;h6 id=&quot;用定点数保持一致性并缩小精度问题&quot;&gt;用定点数保持一致性并缩小精度问题&lt;/h6&gt;

&lt;p&gt;浮点数在计算机中的表示方法是用 V = (-1)^s x (1.M) x 2^(e) 这样的公式表示的，也就是说浮点数的表达其实是模糊的，它用了另一个数乘以2的幂次来表示当前的数。&lt;/p&gt;

&lt;p&gt;定点数则不同，它把整数部分和小数部分拆分开来，都用整数的形式表示，这样一来计算和表达都使用整数的方式。由于整数的计算是确定的，这样就不会存在误差，缺点是由于拆分了整数和小数，两个部分都要占用空间，所以受到存储位数的限制，占用字节多了通常使用64位的long型整数结构来存储定点数，计算的范围也会相对缩小些。&lt;/p&gt;

&lt;p&gt;与浮点数不同的，用定点数来做计算就能保证在各设备上的计算结果一致性，C# 有种整数类型叫 decimal 它并非基础类型，是基础类型的补充类型，是C# 额外造出来的一种类型，可以认为是造了一个类作为数字实例并重载了操作符，它拥有更高的精度却比float范围还要小。它的内部实现就是定点数的实现方式，我们完全可以把它看作定点数来操作。&lt;/p&gt;

&lt;p&gt;C# 的 decimal 类型数值有几个特点需要我们重点关注一下，它占用128位的存储空间即一个decimal变量占用16个字节，相当于4个int整数大小，或2个long型长整数大小，比double还要大1倍。它的数值范围在 ±1.0 × 10^28 到 ±7.9 × 10^28之间，这么大的的占用空间却比float的取值范围还小。decimal 精度比较大，精度范围在 28 个有效位，另外任何与它一起计算的数值都必须先转化为 decimal 类型否则就会编译报错，数值不会隐式的自动转换成 decimal。&lt;/p&gt;

&lt;p&gt;看起来好用的 decimal 却不是大部分游戏开发者的首选选择。使用 C# 自带的 decimal 定点数在使用时存在诸多问题，最大的问题是无法和浮点数随意的互相转换，因此在计算上也会需要进行一定的封装，要么提前对float处理，要么在 decimal 基础上封装一层外壳(类)以适应所有数值的计算。精度过大导致CPU 计算消耗量大，128位的变量28位的精度范围，计算起来实在是比较大的负荷，如果大量用于程序内的逻辑计算则CPU就会不堪重负。内存也是同样的，大量使用时会使得堆栈内存直线飙升，这也间接的增大了CPU的消耗。因此它只适用于财务和金融领域的软件，对于游戏和其他普通应用来说实在不太合适，其根源是不需要这么高的精度浪费了诸多设备资源。&lt;/p&gt;

&lt;p&gt;实际上大部分项目都是自己实现定点数的，具体实现如前面所说的那样：把整数和小数拆开来存储，用两个int整数分别表示整数部分和小数部分，或者用long长整型存储(前32位存储整数，后32位存储浮点数)，long型存储会更好因为这样就确保定点数的内存就是连续的。这样无论整数还是小数部分都用整数表示，并封装在类中，继而我们需要重载(override)所有的基本计算和比较符号，包括+、-、*、/、==、!=、&amp;gt;、&amp;lt;、&amp;gt;=、&amp;lt;=、这些符号都需要重载，重载范围包括 float浮点数、double双精度、int整数、long长整数等。除了以上这些，为了能更好的融合定点数与外部数据的逻辑计算，我们还需要为此写一些额外的定点库，包括定点数坐标类，定点数Quaternion类等用于定点数的扩展。&lt;/p&gt;

&lt;p&gt;看起来比较困难其实并不复杂，只要耐下心来写都会发现不是难事，把定点数与其他类型数字的加减乘除重写一下，如果涉及到更多的数学运算，则再建立一个定点数的数学库，存放一些数学运算的函数，再用写好的定点数类去写些用于扩展的逻辑类仅此而已，都是只要花点时间就能搞定的事，github上也有很多定点数开源的代码，大可以下载下来参考着写，或者把它从头到尾看一遍，把它改成适合自己项目的。&lt;/p&gt;

&lt;h6 id=&quot;最耗的办法用字符串替代浮点数&quot;&gt;最耗的办法，用字符串替代浮点数。&lt;/h6&gt;

&lt;p&gt;如果想要精确度很高很高，那么就可以用字符串代替浮点数来计算结果。但它的缺点是CPU和内存的消耗特别大，如果只是少量使用高精度计算还是可以的。&lt;/p&gt;

&lt;p&gt;我记得以前在大学里做ACM题目时，就有这种方式来检验程序员的逻辑能力和考虑问题的全面性的题目，题目很简单A * B 或 A - B 或 A + B 或 A / B 输出结果，精度要求在小数点后100位。我们把中小学算术的笔算方式写入到程序里去，把字符串转化为整数，并用整数计算当前位置，接着再用字符串形式存储数字，这样的计算方式就完全不需要担心越界问题，还能自由的控制精度。&lt;/p&gt;

&lt;p&gt;缺点是很消耗CPU和内存，比如123456.78912345 * 456789.2345678，这种类型的计算使用字符串代替浮点数，计算一次相当于计算好几万次的普通浮点数计算量。所以如果程序中对精度要求很高，且计算的次数不大，这种方式可以放在考虑范围内。&lt;/p&gt;

&lt;h6 id=&quot;最差的办法提高期望值&quot;&gt;最差的办法，提高期望值。&lt;/h6&gt;

&lt;p&gt;如果 1.55f / 2f 有可能等于 0.7749999 而无法达到 0.775 的目标值时，我们不妨在计算前多加个0.01，使得 1.56f / 2f，这样就大概率保证超出 0.775 的结果目标了。&lt;/p&gt;

&lt;h6 id=&quot;怎么想的为什么能这么做因为我们做了假设&quot;&gt;怎么想的？为什么能这么做？因为我们做了假设。&lt;/h6&gt;

&lt;p&gt;我们脑袋中假设结果是模糊的，这个结果范围就是有可能是 0.7751，也有可能是 0.7749。正常情况下，由计算机的寄存器和CPU决定到底会得到什么结果，但我们不想由它来决定。我们的期望是，宁愿高一点点，跨过这道门槛，也不要少一点点，被门槛拦在外面。&lt;/p&gt;

&lt;p&gt;如果正常浮点数的精度不能给我们安全感，那么我们就自己给自己安全感。提高自己的数值一点点，从而降低门槛跨过去的门槛，差不多的就差一点点的都当做是跨过门槛的。于是就有了 (X + 1) / Y 或者 (X + 0.001) * Y 的写法来度过’精度危机’。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(十九) 机会</title>
   <link href="http://www.luzexi.com/2019/01/12/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A819"/>
   <updated>2019-01-12T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/01/12/思路探讨19</id>
   <content type="html">&lt;p&gt;谈了很久的人生和哲学，灌了很多人生鸡汤，多了也有负效果，因为没有付诸于行动效果等于没有，天天空想哲学问题时间长了也等于在浪费时间。&lt;/p&gt;

&lt;p&gt;以前有段时间很不明白为什么有人说鸡汤多了副作用也大，明明是好的文章，讲得道理也很有意义，对自身的激励也很好，为什么就会有副作用呢。&lt;/p&gt;

&lt;p&gt;后来才渐渐明白，看多了没有在实际中体现出其具体价值也属于浪费时间，浪费生命，纸上谈兵。天天畅想着未来，嘴上说着坚持，脑袋里想着高科技，手里却什么都不干，干不了，不敢干，其实也没什么用，境界再高，也是假境界，说的再牛，也是打肿脸充胖子，迟早是要露馅的。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;接下去日子的文章中，尽量会在专业知识，实际技能，技巧上多做分析和研究。这能在实际生活和工作中，立刻见效。&lt;/p&gt;

&lt;p&gt;眼界和视野虽然有用，但毕竟要很长时间才能体现出来的东西，而且分摊到每件事情的处理上都是很细微的东西。过分关注眼界和视野就会忘记实际生活中需要面对的困难，没有对生活工作小事的解决手法，技巧，知识体系，就会陷入另一种烦恼：看起来很厉害，实际却没什么用。&lt;/p&gt;

&lt;p&gt;视野越大，眼界越宽，长远方向越正确，做当下的事时就越觉得吃力，因为预期太高，看到了未来的美好，却要收拾现在的烂摊子，而且要收拾的时间要按‘年’计算，反差太大，期望容易落空，最终很容易导致：有视野，有眼界，却是个废物。&lt;/p&gt;

&lt;p&gt;回主题：&lt;/p&gt;

&lt;p&gt;从宏观的角度看世界，这个世界有非常多的机会可以升迁，致富。在全世界范围内每个月都差不多有1次在全世界发生的，每个月都有可能在不同的国家发生不同的机会，只要参与进去就有可能致富。&lt;/p&gt;

&lt;p&gt;即使在一个国家内，比如我们国家中国，我认为每年至少有一次这样的机会，比如，前年的千年计划雄安新区，以及粤港澳大湾区计划，去年的海南岛自由港规划，以及今年的5G发展规划，只要你参与其中，我想有很大的可能是可以达到快速致富的效果的。&lt;/p&gt;

&lt;p&gt;人说只要你抓住一次机会，就能翻身了。但我们真的能抓的住吗？真的能赢得吗？输了会如何？&lt;/p&gt;

&lt;p&gt;我们且不说输了会如何。如果说机会这么容易抓住，我们就不用工作了，天天等着就行了，机会来了参与下，就有可能赢得全世界。那岂不是太容易了，照这个理论，每个人都可以这么做，等着，然后时间到了进去参与下就可以了，然后就财务自由了。&lt;/p&gt;

&lt;p&gt;不可能，因为实际情况中，首先你无法辨认是否是‘机会’，辨认‘机会’需要庞大的知识库和信息量，包括人工脑袋的分析以及收集情报。即使最专业的人士在机会辨认率面前也仅有51%概率，比抛筛子的概率多了1%而已。因为其中还涉及到信息的真假，逻辑的判断与分析对错问题。普通人就更不用说了，有30%已经大师级很厉害了。&lt;/p&gt;

&lt;p&gt;其次，参与的时间点也能决定是否能抓住机会，5年前早就爆发了新能源汽车的概念，现在还是不温不火，到底什么时候是个头，新能源汽车最终能否全面进入家庭和社会，到现在还是个未知数。即使是现在回头看已经确定的智能手机的机会，在6年前的那时，电池续航能力，彩屏像素和手机系统都未完善的当时，能否最终全面被社会所接受，是否能全面替换旧式的好用牢固的黑白手机都是个未知数，如果不是苹果一家公司坚持突破新技术，新创意，诺基亚手机可能到现在还是全球销量第一，在当时就能那么确定苹果能突破这个瓶颈吗，现在看来当然会回答‘是’，但是在当时，谁知道呢。&lt;/p&gt;

&lt;p&gt;事情都是随着时间的变化而变化的，我们永远不会知道事情如何变化，什么时间点是最好的参与时间点。如果一旦参与，剩余的就是等待，时间也是金钱，假如5年后亏损退出，损失的不只是金钱，更宝贵的是时间成本和机会成本。&lt;/p&gt;

&lt;p&gt;最后是输赢概率背后的损失比。如果一个机会，输的时候要全部损失，那么一辈子也就只能输一次，一旦输了，就什么都没有了。这种拿命赌的游戏，根本不值得参与，因为人生最宝贵的是时间和精力，只要有时间有精力，慢慢积累，总能积累到很大的时候，但如果一次机会要全部输掉，就相当于损失掉了以前所有的时间和精力，完全不值得参与。&lt;/p&gt;

&lt;p&gt;你看，其实机会没那么好抓，而且很容易受重伤，一旦受重伤就很难恢复。&lt;/p&gt;

&lt;p&gt;为什么那么难，我也一直在想，人类这个世界其实已经存在了好几千年，人类群体生存的规则早就是很完善了，不可能随意的让你一个人破坏或者跳过的。一个存在几千年的人类社会，就像一道围城一圈的又厚又高的城墙，岂是我们一个人敲几下就能破个洞的，全力撞过去都是纹丝不动的，即使撞到头破血流它都不会有任何反应。&lt;/p&gt;

&lt;p&gt;这道墙、这个人类的世界本身就是由人性构成的，人性的弱点也好，优点也好，大大小小的人性特点，以及一个个不同个性的个体，构成了整个社会和世界。&lt;/p&gt;

&lt;p&gt;如果能从这个角度思考，我们就能发现，不要去试图改变世界是对的，因为世界由人性构成，人性无法改变。我们能做的就只是利用人性，弱点也好，优点也好，让我们能在这个世界里，活的更好点。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(十八) 所有不确定都是机会</title>
   <link href="http://www.luzexi.com/2019/01/05/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A818"/>
   <updated>2019-01-05T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/01/05/思路探讨18</id>
   <content type="html">&lt;p&gt;以前不明白为什么，不确定是机会，随着自己经历的丰富，渐渐明白了。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;每次回头看那些当时不确定的事物进展到后来，事情明朗化后都没有那么可怕，可以说当时的自己完全被不确定和未知所蒙蔽。&lt;/p&gt;

&lt;p&gt;是我无知，是我笨吗，每次回头看都这样怀疑自己，后来才慢慢发现，其实每个人都和我一样，对那些自己没有经历过的事物都由于缺乏经验而感到迷茫或害怕。人还是需要对同一种事物进行多次练习的，熟能生巧才能沉着冷静的应对。&lt;/p&gt;

&lt;p&gt;回头看我自己所经历的不确定事物，包括，改变红利方向转变导致父母失意，父母离异，中考失利，高考失利，计算机专业遇冷，端游遭遇冷却期，页游遭遇白热化竞争，手游不确定期，手游进入白热化竞争，整个游戏行业被冰封，以及职业生涯中，公司起步艰难，经营困难，公司倒闭，团队解散等一系列危机与困难导致的不确定。&lt;/p&gt;

&lt;p&gt;回头看看，当时的危机导致的不确定也并不会让天塌下来，但由于都是我生命中前所未有的，从来没有遇到过的，所以以往每次的不确定时期当时都感到非常迷茫和害怕。&lt;/p&gt;

&lt;h6 id=&quot;人在迷茫和害怕中最容易做出错误的决定这就是所有不确定都是机会的最根本原因由于大部分人对不确定的恐惧而导致做出了错误的决定最终使得只有少数人得利的最终结局这种事屡试不爽人性脆弱的一面无法改变&quot;&gt;人在迷茫和害怕中，最容易做出错误的决定。这就是，’所有不确定都是机会‘的最根本原因：由于，大部分人对不确定的恐惧而导致做出了错误的决定，最终使得只有少数人得利的最终结局。这种事屡试不爽，人性脆弱的一面无法改变。&lt;/h6&gt;

&lt;p&gt;我们当下就是出于一种不确定的时期，我观察到不仅仅是一个行业处于低谷，很多行业都在低谷和正奔向低谷的状态，房地产，媒体娱乐，金融大部分行业都处于低谷或者正在迈向低谷，甚至有人喊出中国崩盘的论调。&lt;/p&gt;

&lt;p&gt;面对当下的不确定性，我们不必惊慌，中国会起来吗，不知道。但积极的面对工作，学习，生活，总是不会错的。&lt;/p&gt;

&lt;p&gt;当所有人都在担心害怕的时候，不确定带来的机会就来了，他们像热锅上的蚂蚁乱窜，事情还没有确定下来，自己被自己吓个半死，而我们由于故意减少担心和害怕，专心做好自己的事，就等于比别人更早的走出萧条的阴霾，更好的活出自己，更好的锻炼了自我，更早的走在时代的前列。&lt;/p&gt;

&lt;h6 id=&quot;机会就在这里人性的反向一面平静替代恐惧勤劳替代懒惰知足替代贪婪强壮替代瘦弱坚定替代迷茫&quot;&gt;机会就在这里，人性的反向一面：平静替代恐惧，勤劳替代懒惰，知足替代贪婪，强壮替代瘦弱，坚定替代迷茫。&lt;/h6&gt;

&lt;h6 id=&quot;题外话&quot;&gt;题外话：&lt;/h6&gt;

&lt;p&gt;什么是机会？怎样才能抓住机会？&lt;/p&gt;

&lt;p&gt;难道坚持就能抓住机会吗？不是的。有可能放弃现在的，重新开始才是机会。&lt;/p&gt;

&lt;p&gt;由于情况永远是不同的，没有永恒不变的统一答案。&lt;/p&gt;

&lt;p&gt;很多人接受不了这个理念：“答案是变化的”。说，为什么会有变化的答案？&lt;/p&gt;

&lt;p&gt;由于我们的思维总是固定在某个角度上，其实跟应试教育也没什么关系，世界上所有的人类都是一样的更加倾向于固定思维，人毕竟是动物，狭隘的观念是比较舒适的观念，谁都不想折磨自己不是么，所以我们很多时候都用固定的答案在处理问题，要么对，要么错，要么A，要么B，要么C，要么D。其实这个世界上还有一种答案是不固定，可以错，也可以对。&lt;/p&gt;

&lt;p&gt;这个答案可以在今天的早上7点05分时，是对的，到了晚上的7点05分时就不对了。&lt;/p&gt;

&lt;p&gt;拿股票的涨跌来说，最容易说的清楚。很多人都有固定思维，比如，回调思维，大部分人认为涨高了回调下，然后会再涨。于是他在回调时倾家荡产的买入，但是事情并没有像他想象的那样发展，在回调结束K线走平时，这家公司发出了业绩亏损的报告，股价随即一泻千里，这时此人一辈子积累的财富，瞬间灰飞烟灭。还有人有抄底思维，认为跌太多，就会回升，其实回头看他抄的底，其实是半山腰，下面还有万丈深渊在等待。&lt;/p&gt;

&lt;p&gt;（买短线股票最厉害的思维模式要属变化思维模式，错了就撤，对了就攻。不过我不想引导大家到这种短线思维误区。因为这种短线股票思维，在短线里是对的，在长线里却是错的。这也应验了我们前面说的，答案在某个范围或时期是对的，在另外一个范围和时期是错的。）&lt;/p&gt;

&lt;p&gt;用这种固定思维模式的方法来处理人生大事，即使这次被你侥幸赌赢，也有下次赌输的时候吧，只要赌输一次，就是万丈深渊吧。&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>思路探讨(十七) 2018形势和心态</title>
   <link href="http://www.luzexi.com/2019/01/01/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A817"/>
   <updated>2019-01-01T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2019/01/01/思路探讨17</id>
   <content type="html">&lt;p&gt;2018开年感觉还是不错的，虽然没有红红火火，但平淡中带点小火花还是有的，现在看来当时还是偏乐观的。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;最典型的例子就是房地产了，年初三四线的销售楼盘还是精神劲十足的在叫卖，年末却感觉到整个房地产要崩了。&lt;/p&gt;

&lt;p&gt;时隔只有不到1年，却反差如此巨大，看来我们都对形式错误的估算了。&lt;/p&gt;

&lt;p&gt;‘贸易战’什么的其实只是借口罢了，从形式上看，大的趋势一直在做回调，从2016开始，整体经济都在不断下滑中，到了2018年已经是节点了，如果不是贸易战这个借口，可能大家还沉浸在‘还可以’这个幻觉中，‘贸易战’给了借口，该展现的真实面目都浮出了水面。美国也好不到哪里去，只是在全球资金的掩护下，皮比较厚，要多磨几下而已。&lt;/p&gt;

&lt;p&gt;很多人还一直在崇拜美国经济厉害，政策厉害，其实美国也快撑不住了，如果不是撑不住，他们有必要收紧贸易吗，有必要出减税这招吗？收紧贸易会伤到自己，如果经济真的向好为什么要害自己，美国的智囊团也不傻，减税这招明显是在经济不好的情况下使用效果会更好，如果他们经济向好，为什么还要用减税来加热经济，最后经济过热这不是自讨苦吃么。&lt;/p&gt;

&lt;p&gt;所以，形势其实很明显，大家都不好过，大家都在收缩，只是2018年由于量变导致质变，整体急速下滑。&lt;/p&gt;

&lt;p&gt;其实我们一直都低估了趋势的力量，如果说我们确实重视了它，那我敢肯定的是，我们还不够重视。如果我们知道了顺势而为的重要性，我想我们以前还不够深刻的理解它的真正含义。&lt;/p&gt;

&lt;h6 id=&quot;2018对我个人而言是很神奇的一年&quot;&gt;2018对我个人而言是很神奇的一年。&lt;/h6&gt;

&lt;p&gt;我能很明显的感觉到我自己能完全控制自己了，不知是因为心智又被打开还是怎么的，我开始了我自己的理论，我开始故意不去将别人的理论掺杂进来，和故意不去评论别人对我的看法，而专注自己的理论体系，更加专注做自己挑战自己。&lt;/p&gt;

&lt;p&gt;我开始明白为什么一些很明显很简单的道理，大部分人一辈子都无法参透，因为他们不信，他们认为肯定有更好的方法和更短的路，于是他们不信，找了一辈子最后发现无法回头，或回头的时间太长，劣势太大，只能继续下去，希望下辈子自己早点明白。其实会去找路还是好的，最后还可能折腾回来，大部分人还是像咸鱼思想，坐等天上掉馅饼。&lt;/p&gt;

&lt;p&gt;我也开始明白了，为什么说人到中年时‘无奈’占据了大半壁江山，因为人到中年，大部分事情都由于前十几和几十年的积累导致事情到了现在这个地步而难以改变，如果想要改变也同样需要花去十几和几十年的时间，但这么多时间对于他们来说太长了，他们需要立刻解决，那是不可能的事情，只能选择‘无奈’接受。&lt;/p&gt;

&lt;p&gt;我也开始彻底明白，积极向上的重要性，任何事情没有积极的态度，是无法完成的，如果面对的是比较棘手的，甚至比较糟糕的情况，那么积极向上态度的持续的时间和程度更是重中之重，核心中的核心。没有了积极的态度，万事皆悲，任何事的发展都会向下跌落深渊。&lt;/p&gt;

&lt;p&gt;我也开始深刻理解，什么是‘活在当下’，以及‘活在当下’的重要性，从而引出了，不积跬步无以至千里的行动模式，每日都努力，日日皆重要，天天是机会的‘活在当下’的自我理论体系。&lt;/p&gt;

&lt;h6 id=&quot;我每天都在努力的多积累一点期望在长远的未来能更好一点&quot;&gt;我每天都在努力的多积累一点，期望在长远的未来，能更好一点。&lt;/h6&gt;

&lt;h6 id=&quot;要是说我学习和观察经济形势是我开阔视野的工具那么每天的努力工作和努力学习是我在现实中对长远展望的积累&quot;&gt;要是说我学习和观察经济形势是我开阔视野的工具，那么每天的努力工作和努力学习是我在现实中对长远展望的积累。&lt;/h6&gt;

&lt;h6 id=&quot;2018年最后1天的最后一句话现在的所作所为将是10年后的自己生活的模样10年后的自己也会为现在自己努力的每一天而感到高兴&quot;&gt;2018年最后1天的最后一句话：现在的所作所为，将是10年后的自己生活的模样，10年后的自己也会为现在自己努力的每一天而感到高兴。&lt;/h6&gt;
</content>
 </entry>
 
 <entry>
   <title>思路探讨(十六) 感官动物</title>
   <link href="http://www.luzexi.com/2018/12/26/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A816"/>
   <updated>2018-12-26T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/12/26/思路探讨16</id>
   <content type="html">&lt;p&gt;最近半年的写思路文章的经历让自己学到很多东西。工作非常的繁忙，纵使我降低了书写的频率，还是很难抽出时间来专心写作，只能靠每天的碎片时间来思考这周的主题，并在印象笔记里记录我的思考过程和结论。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;我的印象笔记里记录了很多很多我的思考过程，包括看书时，看文章时，放空时得到的思路和感悟，不过大部分都无法发表出来，因为过于零碎，没有形成整体的思路和整体的思路框架，无法从各角度论证思路的有效性，所以记录下平时的想法有时候也无济于事，这是最糟糕的也最让我难受的。&lt;/p&gt;

&lt;p&gt;本想着平时利用每天零碎的时间可以记录些东西，在发表文章时可以用上，但并没有起到决定性的作用，最终还是要靠积累。这种积累不是说一两天就能搞定的，而是靠长期，几年，十几年，几十年的积累才能建立起巨量的经验和知识，以淡定自若的应对各种情况。于是我更加注重平时每日的积累过程，比如每日看书3页，每日学英语半小时，每日健身半小时，每日听书10分钟，每日设计产品半小时等等，其实总体加起来也不超过2个小时，在一天的除去睡觉后的16个小时里，只占了12.5%的时间，但每日坚持做一遍却是非常厉害的也非常困难的事情，这样积跬步至千里的感觉是非常棒的做法，大家一起来加油。&lt;/p&gt;

&lt;h3 id=&quot;感官动物&quot;&gt;感官动物&lt;/h3&gt;

&lt;p&gt;感官动物其实是人类最大的弱点之一，很容易被表面化的事物所迷惑，最终导致犯错。&lt;/p&gt;

&lt;p&gt;这种弱点很容易被利用，比如美女，帅哥，美景，绚丽的颜色，缤纷的烟火，很容易迷惑我们，让我们沉醉。相声，歌曲，柔和的声音，激情的演讲，也同样会迷惑我们，让我们的意志放松警惕。&lt;/p&gt;

&lt;p&gt;其实这种感官上的刺激，在很多情况下都是对我们人本身的发展是有害的，因为过于留恋于表面化的和感官上的事物，导致无法透析深层次的根源，也容易陷入感官陷阱，比如沉迷于美色，沉迷于电影电视剧，沉迷于喝酒，沉迷于刷微博，沉迷于刷微视频，沉迷于购物，沉迷于相声笑话，沉迷于虚荣心，这种留恋于表面化的行为其实就完全是在浪费自己的生命，因为沉迷它们是相对容易的，只要你‘看’，‘听’，‘感受’就可以了，不需要用脑袋思考。&lt;/p&gt;

&lt;p&gt;我发现这些表面化的事物不断吸引我注意力的事物，同时也浪费了我大量的时间。我们人的注意力是有限，而我又想把有限的注意力放到自己想要做的事情上去。好难。偶尔享受下感官盛宴放松一下不小心就会被吸引去忘记了时间。放松一下却常常过了头。&lt;/p&gt;

&lt;p&gt;这种人性的弱点是无法抹除的，既然这样，我们为何不好好利用它。比如在打造产品上，从视觉，听觉和感觉上大大加强感官的效果，让自己的产品更加吸引人，在视觉和听觉盛宴上下更大的功夫去满足人性弱点，让产品，看着漂亮的，听着舒服的，摆弄起来顺畅，这样才能利用人性的弱点来创造吸引人的产品。&lt;/p&gt;

&lt;p&gt;不只是产品。其实我们自己人本身也是产品的一种，如果你想让自己更加吸引世界关注，我想你可能得从这三个方面来改造自己，视觉，听觉，感觉。比如每日健身让自己身材更加棒，人看起来更加精神，加上衣着干净整洁视觉上的效果会更好。比如在讲话时更加注重逻辑清晰条理清晰语气更加缓和，让人听着更加舒适并且容易理解。又比如在学习和补充知识上花更多的时间，让别人能感受到你是个爱学习的人，并且知识渊博经验丰富，让人对你有种靠谱的感觉。从三方面打造自己，不断完善自己，年复一年的改造改进翻新，终有一天你会成为世界的焦点。&lt;/p&gt;

&lt;h6 id=&quot;感官动物的实质是留恋于表象而我们却要反过来不留恋于他人的表象而更加注重自己的内在知识和经验并且把自己的优秀的表象展现给别人看因为人只看我们的表象看不到我们的内在&quot;&gt;感官动物的实质是留恋于表象，而我们却要反过来，不留恋于他人的表象，而更加注重自己的内在知识和经验，并且把自己的优秀的表象展现给别人看，因为人只看我们的表象看不到我们的内在。&lt;/h6&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(十五) 中产阶层的天花板</title>
   <link href="http://www.luzexi.com/2018/12/15/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A815"/>
   <updated>2018-12-15T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/12/15/思路探讨15</id>
   <content type="html">&lt;p&gt;empty&lt;/p&gt;

&lt;!-- 
很多人应该跟我一样，曾有段时间一直怀疑社会阶层是否真的存在，或者幻想着它是以何种形式存在的。

社会阶层确实存在是事实不可否认的，它到底以何种形式存在，是很多人包括我自己所迷惑的。

大部分人还是把阶层的概念停留在’钱‘上，这种不假思索的观念只能说只对了一小部分。

===

###### 阶层的划分不只是’钱‘，因为大部分界限的划分是由无形资产构成的，比如知识，技能，观念，成就，荣誉，权利，职业，人际圈。

这些无形资产和’金钱‘是相互关联的，有了知识，有了技能，有了良好的观念，有了一定的成就，有了权利，有了好的职业，就有了源源不断的’金钱‘，有了高等的人际圈，就有了更多的交流思路开阔了眼界。

源源不断的’金钱‘和开阔的视野又支撑了，我们得到更好的教育资源，得到更多的成就感，得到更多的自信，从而对目标更加坚定，对职业计划更加长远，对成功更加有信心。

很多人的误区就在于，认为有’金钱‘就一定能得到知识，技能，观念，成就，荣誉，权利，职业，人际圈。不是的，如果世界上可以只用’钱‘就能解决任何问题，那么我们也不用这么痛苦了。

我们人生的困难就在于，看起来似乎很重要的’钱‘，只是各种因素的中的一个，并不是最重要的，而且很多时候有可能它是最不重要的，也很多时候可能它只是个结果的表象，并不意味着任何意义，只是它在我们生活中充当着生存的基本底线。

###### 对’金钱‘的过分关注，是中产阶层的天花板之一。

学校中接受学习和教育时，对考试成绩的过分关注，是同一个道理。当我对’考试成绩‘过于关注，就会导致自己迷失在成绩上，任何一次低于预期的成绩都会让我感到愧疚，忧郁，苦闷，自责。无论我的成绩排在第几名，我对成绩的过分关注，都导致我在知识深度和宽度这个层级上无法越过当前这个台阶，达到更高的层级。

中产的天花板里还包括：只关注短期效果，不执行长期目标；以及不愿主动跳出自己的这口井，去看看世界；不想尝试，和不愿冒险；等

阶层的天花板毕竟是坚硬无比的，无论什么时候，无论哪个年代，突破阶层都是非常艰难的事情，这需要的已经不仅仅是一个人的努力了，更需要几代人的共同努力。

###### 抛开阶层不说，人与人之间都是一样的，都是人类，并且所有人的烦恼都是相同的，我相信我所烦恼的事情，习见平主席也有同样的烦恼，我很确信这一点。如果我们能从这个角度来看我们自己的人生的话，实际上已经解决一个根本性的大问题，“自信问题”，你我都是人，我并不惧怕你，也不羡慕你，我相信我十年如一日的努力能让我受益无穷，我也希望你能像我一样天天努力。

题外话：

阶层的突破还有假突破和真突破的表象，这可能需要几代人时间的适应和努力。

当你面对你所拥有的一切，平静，安详时，就属于真突破，因为对当前阶层的生活习惯，思维习惯，行为习惯，习以为常。而不是拿一些东西来向自己心中认为的下层的阶层炫耀。由于整个人生几十年都浸泡在当前阶层中而，习惯了这一切，任何发生在当前阶层的事物，并不觉得有什么特别。
 --&gt;
</content>
 </entry>
 
 <entry>
   <title>思路探讨(十四) 要行动，不要焦虑</title>
   <link href="http://www.luzexi.com/2018/12/08/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A814"/>
   <updated>2018-12-08T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/12/08/思路探讨14</id>
   <content type="html">&lt;p&gt;人生大多数遇到的难题都不是能单靠几个小时或者几天就能解决的。很多人不愿意接受这个事实，总是试图靠短时间内的突击行动来试图解决长期的问题，那是不可能的事，而且大概率会让事情变得越来越糟糕，身体坏了，健康没了，事情没成，问题没解决，一切变得愈发糟糕。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;焦虑的根源其实是希望立刻解决问题而不得，或者想短时间内解决自己面临的问题而不行。&lt;/p&gt;

&lt;p&gt;焦虑最大的坏处是扰乱我们的节奏，让我们迷失方向，思想更加混沌不堪。焦虑虽然可以使我们居安思危，但比起坏处，这个好处有点弱。&lt;/p&gt;

&lt;p&gt;“我要”的欲望越多，焦虑就会越多。有些人选择逃避欲望，焦虑也相对少了，但生活品质就随之下降了，没有了钱，没有了房，没有了车，生活在底层又没有欲望，人生就少了很多意义。&lt;/p&gt;

&lt;p&gt;更多的人选择“追逐欲望”，但他们想要的太多，积累的太少，不给自己时间去坚持，失去了自己的节奏，被”欲望“所控制，被焦虑所恐吓。&lt;/p&gt;

&lt;h6 id=&quot;相反的追逐欲望的人群中也有另一种人他们追逐欲望而不奢望短时间得到他们懂得积累深信不积跬步无以至千里的道理他们拥有自己的节奏任何扰乱他们节奏的人和事都一概闭屏他们就是那些保持长期积极的心态长期坚持努力奋斗长期斗志昂扬的人&quot;&gt;相反的。”追逐欲望“的人群中，也有另一种人，他们追逐欲望而不奢望短时间得到，他们懂得积累，深信不积跬步无以至千里的道理，他们拥有自己的节奏，任何扰乱他们节奏的人和事都一概闭屏，他们就是那些保持长期积极的心态，长期坚持努力奋斗，长期斗志昂扬的人。&lt;/h6&gt;

&lt;p&gt;积累多的人之所以稳健，是因为对他来说，”我要“的欲望可以用”我有“的来满足。即使”我有“的暂时不够满足，他也能借助已经拥有的足够的努力和勤奋（或者已经熟练的技能和技巧或方法），只需假以时日，定能如愿以偿。一旦得偿所愿，不仅”我有“的更多，”我要“的也更容易获得，如此形成良性循环。&lt;/p&gt;

&lt;h6 id=&quot;其中保持长期的足够的努力和足够的勤奋最为关键&quot;&gt;其中保持长期的，足够的努力，和足够的勤奋，最为关键。&lt;/h6&gt;

&lt;p&gt;从某种意义上理解，“逆境造就成功”，“磨难令人成熟”之类的话纯属胡说八道。显然，在顺境中更容易成功，而且很多磨难本身没有不要—这更可能是失败者对他们自己一生都未曾有机会体验的成功及成功者意淫式的猜想而已。失败者很难有机会了解到成功的真相，因为人最容易受自身经验的限制，而不曾有哪怕一点点成功经验的人无从摆脱自身局限。&lt;/p&gt;

&lt;h6 id=&quot;从长期来看如果我们的心态是要精进我们自身的能力而不是要达成某个绩效目标我们更有可能长期坚持下去并保持积极性&quot;&gt;从长期来看，如果我们的心态是要精进我们自身的能力而不是要达成某个绩效目标，我们更有可能长期坚持下去并保持积极性。&lt;/h6&gt;

&lt;p&gt;比如：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;专注于提升你的编程技术和你的能力来创造有意义的影响，而不是专注于想要被公司晋升为主管工程师&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;训练自己成为一个更好的选手或运动员，而不是为了赢得某项运动，不论是跑步，打球还是瑜伽。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;加入一家创业公司，不是为了发财暴富，而是因为你对将要面对的问题充满热情，并且激动地想要从这个旅程中学习。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;你将会意识到基于绩效的长期目标依赖于很多你无法控制的外界因素（你的经理是否会提拔你，你是否比你的竞争对手强或是你的创业公司是否成功）。&lt;/p&gt;

&lt;p&gt;你让环境因素在你的成功中扮演如此重要的角色，当你遇到障碍时，你很难保持积极性。相反如果你专注于自我学习和能力提升，你更有可能克服困难获得实际上的成功。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(十三) 三十而立四十不惑</title>
   <link href="http://www.luzexi.com/2018/12/01/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A813"/>
   <updated>2018-12-01T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/12/01/思路探讨13</id>
   <content type="html">&lt;h6 id=&quot;三十立什么&quot;&gt;三十立什么？&lt;/h6&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;三十岁的人，应该能依靠自己的本领独立承担起自己应承受的责任，并已经确定自己的人生目标与发展方向。简单说，三十岁的人应该能坦然地面对一切了。&lt;/p&gt;

&lt;p&gt;三十岁后对社会和自己都有比较明确的认识和理解，在这基础上的一种自觉的或者是说是一种自我人格独立的意识。&lt;/p&gt;

&lt;p&gt;三十岁后确立了我们自己的品格和修养，包括：思想的修养，道德的涵养，能力的培养。&lt;/p&gt;

&lt;p&gt;三十岁后不再把自己的需求寄托在父母的资助上，寄托在别人的帮助上，有了自己完全独立的生存和发展的能力。&lt;/p&gt;

&lt;p&gt;三十岁后确立了自己所从事的事业，应该有比较固定的职业了。从事每项事业都要有本领，按现代话讲就是有一技之长。这是为生的必备，随着现代科技的发展，对每个人的各方面要求都会越来越高。&lt;/p&gt;

&lt;p&gt;三十岁后陆续组建了自己的家庭，对家庭和团体的观念开始关注，甚至会开始上升到国家的大团体，为团体尽心做出自己的贡献的意识越来越强烈，同时也越来越清楚的意识到，没有团体就没有自己的事实。&lt;/p&gt;

&lt;p&gt;三十而立是对年轻人的起码要求，不过现实和我们所期望的有很大的距离，这也是三十岁后的10年内要用勤劳和智慧要解决的问题。&lt;/p&gt;

&lt;h6 id=&quot;四十而不惑明白了什么&quot;&gt;四十而不惑，明白了什么？&lt;/h6&gt;

&lt;p&gt;“四十而不惑”，到了四十岁，才不怀疑。人到了四十岁，咀嚼了事态的冷暖，感怀了岁月的无情，往事悠悠，四十年弹指一挥间，在经历了许多疑惑、彷徨、振奋、欣喜之后，是沉思，是恍然大悟……少了激情，多了沉稳；少了冲动，多了冷静；少了烦恼，多了理智；少了放任，多了责任；少了盲从，多了自我……四十岁的人是十分理智和清醒的，对外，他明白了社会；对内，他明白了自己；对自己，他明白了责任。&lt;/p&gt;

&lt;p&gt;对外，他明白了社会。人是生活在社会之中，离不开社会，社会越进步，人们对社会的依赖度就会越高。四十岁的人，不再年轻，缺少了对社会那种幻想和迫切投身于社会中的激情，生活的经历多少磨去了些激情，多了沉稳，挫折让他明白了，人和社会的关系就是个体和群体的关系，个体是群体中的一分子，个体左右不了群体，那就必须正视它，个人的理想和愿望必须建立在现实社会的基础之上，否则就是空想。他内心会有一种淡定的力量去应对外界。&lt;/p&gt;

&lt;p&gt;对内，他明白了自己。所以，所谓成长是内心在历练中的逐渐强大，所谓人生的归位是把外在的东西变成内心的能量。有理想，而不是妄想；有愿望，而不奢望；有作为，而不妄为。有理想是应该的，但关键在打理，去实践；有愿望也可以，关键在于切合实际；有作为，关键在适度。四十岁的人，是最讲实际的年龄段的人，人生走过一半，机会和机遇少了，不应该再犯过错而走迂回的路了。&lt;/p&gt;

&lt;p&gt;对自己，他明白了责任。四十岁的人最大特点是明白了自己的责任。他必须承担社会责任、家庭责任和抚养子女责任。首先，他必须做好自己的工作，这不单是生存的需要，也是为社会所做的贡献，是必须尽到的社会责任，只有每个社会成员的辛勤工作，才换来整个社会的进步；其次，四十岁的人，其父母年龄都大了，做为四十岁的人，必须承担起赡养老人的家庭责任，尽心尽力尽早去尽孝；还有，四十岁的人，子女都处在学习阶段，培养教育好自己的子女，即是父母的责任，也是为社会延续和发展所应尽的社会责任。&lt;/p&gt;

&lt;p&gt;四十岁的人，是人生辉煌时期，也是为社会和家庭贡献最大的时期。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(十二) 思维突破</title>
   <link href="http://www.luzexi.com/2018/11/22/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A812"/>
   <updated>2018-11-22T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/11/22/思路探讨12</id>
   <content type="html">&lt;p&gt;人很难突破自己的思维，因为我们自己很难知道自己的思维是否固化，也很难从其他人口中了解到。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;我们都认为他人对我们的评价是片面的不合理的评价，所以即使收到批评或指正的信息时也很难有效认识到自己的问题。&lt;/p&gt;

&lt;p&gt;即使认识到自己的问题，也无法改正。有趣的人类，明知道错误，想改也改不过来。&lt;/p&gt;

&lt;p&gt;这种问题通常是长年累月积累的结果，习惯成了自然。所有习惯性的动作，思考方式，语调，语句，都进了肌肉记忆里，在肌肉记忆里根本不需要思考，一到某个相似情景的点，自然而然的触发。形成良好的肌肉记忆很难，想去掉恶劣的肌肉记忆也同样很难，这把双刃剑弄得我很受伤。&lt;/p&gt;

&lt;p&gt;另一方面，思维的突破需要自己主动折腾（受虐）。这种受虐是精神上的，通常我们去接触一些自己不喜欢的事物，去接触一些自己从来没有接触过的人和事，能形成有效的突破。&lt;/p&gt;

&lt;p&gt;未知的事物，令我们恐惧的事物，我们讨厌的事物，这些对我们来说，难以理解的事物，都是绝佳的突破点。&lt;/p&gt;

&lt;p&gt;越害怕，越恐惧，越不知道，越讨厌的事物，越是思维固化的关键点，通常这些事物的真实情况完全和我们理解的不一样。&lt;/p&gt;

&lt;p&gt;让自己主动陷入这样的“困境”，大部分人都会望而怯步，人都是向着舒服的一个方向走的，自己怎么舒服怎么走，让自己主动去陷入“困境”这种事，人类很难接受。&lt;/p&gt;

&lt;p&gt;不过我们要明白，主动陷入“困境”，比被动陷入“困境”要好的多，被动承受“困境”会把人摧毁，而主动挑战“困境”却能成就自己。看起来差不多的“困境”情景，其实在人内心的状态是完全两个样子的。&lt;/p&gt;

&lt;p&gt;不只是主动折腾，还要主动吃亏，舍小取大。&lt;/p&gt;

&lt;p&gt;大多数人也很难想明白，自己付出的努力为什么要和别人共享结果，我赚到的钱为什么要和别人分享，我建立的公司为什么要分给别人股份，我做出来的项目为什么要和别人分成。这种主动丢掉当前的小利益而寻求更长远的利益，以及宏观意义上的大格局，少数几个人才能做得到，通常这种人都是人中龙凤，短时间是体现不出价值来的，只有从长时间来看才能懂得其精华所在。人毕竟是短视的动物，能理解舍小取大的就不多，就更别说共赢和分享了。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(十一) 活在当下</title>
   <link href="http://www.luzexi.com/2018/11/18/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A811"/>
   <updated>2018-11-18T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/11/18/思路探讨11</id>
   <content type="html">&lt;p&gt;人在不同的阶层，不同的位置思考的内容不同，所以屁股决定脑袋，你坐什么位置站在什么阶层决定了你担忧什么内容，想的是什么事。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;不仅仅是职位，更是人生的位置，20-25岁的人担心的事情和25-30岁的不同，25-30岁的人担心的事情和30-35岁的人不同，30-35岁的人担心的事和35-40岁的人不同，以此类推，每隔5年都会有一些大的不同点，隔10年的人思考的问题大不相同了。&lt;/p&gt;

&lt;p&gt;人生每个位置都要经历那个阶段的痛苦与欢乐。并不一定说，25岁的人去担心35岁该担心的事有多厉害，不是的，那没什么用，你并不在35岁那个点上，其实大部分都是你自己在杞人忧天，因为实际情况是你好好过完25岁到35岁这个阶段，在25岁担心35岁的那个问题已经迎刃而解了。&lt;/p&gt;

&lt;p&gt;就好像我们没必要去担心总统总理该担心的事情一样，他们在那里思考的制度，权利，经济，民生问题，我们就完全不需要去担心和忧愁，因为我们并不在那个位置，担心完全就是浪费时间和精力。&lt;/p&gt;

&lt;p&gt;为什么说浪费时间和精力呢，因为我们根本不在那个位置上，无法体会到当事人的环境，压力，困难。我们所创造出来的点子或想法都是很空的，无法跟实际匹配。当我们实实在在的坐到那个位置上时才能真正切切的体会到，以前坐在这个位置上的人的感受。&lt;/p&gt;

&lt;p&gt;所以做好当下是关键的关键，下一个阶段到来时，自然会根据上一个阶段的情况发生变化，上一个阶段优秀或良好，这个阶段就会更好一点，如果上一个阶段比较糟糕，那么这个阶段如果不努力可能会更糟。&lt;/p&gt;

&lt;h6 id=&quot;活在当下就是告诉我们这个道理它并不是说在当下要及时享乐而是把更多的精力关注在当下的发展和努力上未来下一个阶段就会变得更加美好&quot;&gt;活在当下，就是告诉我们这个道理，它并不是说在当下要及时享乐，而是，把更多的精力关注在当下的发展和努力上，未来下一个阶段就会变得更加美好。&lt;/h6&gt;

&lt;h6 id=&quot;以前还讲究策略什么好坏啦什么有前景啦什么蓝海红海啦什么的现在已经不管了有兴趣就上好好干一天天积累没兴趣即使是天上掉馅饼我也不去捡什么馅饼还是陷阱的不再考虑范围内&quot;&gt;以前还讲究策略，什么好坏啦，什么有前景啦，什么蓝海红海啦什么的，现在已经不管了，有兴趣就上，好好干，一天天积累，没兴趣即使是天上掉馅饼我也不去捡，什么馅饼还是陷阱的，不再考虑范围内。&lt;/h6&gt;

&lt;h6 id=&quot;由于下一个阶段的状况是当下这个阶段的好坏来影响的我为什么要天天忧心下一个阶段发生的事不如做好当下这个阶段的事情到下一个阶段来临时问题自然会迎刃而解&quot;&gt;由于下一个阶段的状况，是当下这个阶段的好坏来影响的，我为什么要天天忧心下一个阶段发生的事，不如做好当下这个阶段的事情，到下一个阶段来临时问题自然会迎刃而解。&lt;/h6&gt;
</content>
 </entry>
 
 <entry>
   <title>思路探讨(十) 成与败，得与失</title>
   <link href="http://www.luzexi.com/2018/11/11/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A810"/>
   <updated>2018-11-11T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/11/11/思路探讨10</id>
   <content type="html">&lt;h3 id=&quot;成与败得与失&quot;&gt;成与败，得与失&lt;/h3&gt;

&lt;p&gt;人生海海，都是起起落落的，失意时给别人捧捧场，得意时听听别人掌声，这样很好也很正常，激励了自己也鼓励了别人。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;失败和成功都是一个陷阱的开始，失败的人容易陷入更多的困惑，成功的人容易陷入自我陶醉与傲慢，最后都将发生更多的失败和更大的挫折。&lt;/p&gt;

&lt;h6 id=&quot;这是人性弱点的体现失落中难以站起来高兴时却想要飞起来&quot;&gt;这是人性弱点的体现，失落中难以站起来，高兴时却想要飞起来。&lt;/h6&gt;

&lt;p&gt;所以，我们要反人性操作，失败时不要太在意，相信努力最终会成功，黎明终会到来，而成功时不要太骄傲，要明白如果没有大趋势的推动着整个事件发生，我们是不可能成功的，敬畏大自然大世界大趋势。&lt;/p&gt;

&lt;p&gt;实际生活中失败的困惑最常发生，我们每天都发生着不同级别的失败。&lt;/p&gt;

&lt;p&gt;在失败面前不要太在意，因为，从整个人生70年寿命角度来看，当下所发生的事情都是一时的得与失，结果并不是我们唯一追求的东西，努力拼搏奋斗的过程才是。&lt;/p&gt;

&lt;p&gt;面对失败，就是不要在意它，就是要从中提取总结后忘记失败这件事，继续向前进。&lt;/p&gt;

&lt;p&gt;要明白，杀不死我的，终将使我更加强大。秉持这个理念时就会明白，失败的好处，大大的有。&lt;/p&gt;

&lt;h6 id=&quot;偶尔回头看看这些拼搏的过程和痕迹觉得精彩才是真的精彩至于得与失太在意就太短视了太不在意就太无知了我们追求的应该是常反思得与失又常故意忘记得与失对困境无谓无惧对荣耀又不留恋从而形成大视野长远见短目标立刻做的行为习惯和思维逻辑&quot;&gt;偶尔回头看看，这些拼搏的过程和痕迹，觉得精彩才是真的精彩。至于得与失，太在意，就太短视了，太不在意，就太无知了，我们追求的应该是常反思得与失，又常故意忘记得与失，对困境无谓无惧，对荣耀又不留恋，从而形成大视野，长远见，短目标，立刻做的行为习惯和思维逻辑。&lt;/h6&gt;

&lt;p&gt;题外话。&lt;/p&gt;

&lt;p&gt;积极乐观的心态带给我光明的世界，主动出击掌握主动权，以攻为守。主动进攻虽然代价大，但被动的代价可能更大，取舍之间不如奋勇向前。&lt;/p&gt;

&lt;p&gt;我曾经以为我懂得越多就能赚到越多钱，就能坐拥金字塔顶尖。错了，认知度和财富和权力是极难匹配，财富靠的是年复年的积累，而不是爆发，权力也是，爆发其实就是运气，无其他原因。&lt;/p&gt;

&lt;p&gt;那些一直梦想着暴富的人，通常一生都没有任何建树，郁郁寡欢，认为自己不得志，上天不给我恩赐，其实上天凭什么就给我恩赐，世界上60亿人，没有理由给我恩赐，我又懒又黑暗，还见不得别人成功，一辈子在底层被压迫那是理所当然，顺理成章的事。&lt;/p&gt;

&lt;p&gt;现实中很多人认为因为自己聪明才赚到了钱，殊不知没有大趋势刚好在那个点推动了他，他什么都不是。如果没有认清这个现实问题，后面将很大概率遭遇危机。不敬畏世界和大自然，总会吃亏的.&lt;/p&gt;

&lt;h6 id=&quot;成功并不是一个人的事而是一个看不见的集体日积月累的综合性事件&quot;&gt;成功并不是一个人的事，而是一个看不见的”集体”日积月累的综合性事件。&lt;/h6&gt;

&lt;h6 id=&quot;说到底这个世界是辩证的世界没有绝对的对也没有绝对的错在不同环境下就会产生不同的效果或结果&quot;&gt;说到底，这个世界是辩证的世界，没有绝对的对，也没有绝对的错，在不同环境下就会产生不同的效果或结果。&lt;/h6&gt;

&lt;p&gt;我要做的是把自己变成，辩证的人，从不同角度，不同世界观，不同级别的视野中看当下的问题。&lt;/p&gt;

&lt;p&gt;随着时间的推移，演变出不同的情况，用不同的方法应对，做最合适的选择，虽然外在的变化多但心中要保持一颗纯洁的信仰，牢牢记住，是金子总会发光的，光明总会战胜黑暗的，良币总会驱逐劣币的，黎明总会到来的，努力总会有回报。&lt;/p&gt;

&lt;p&gt;说的这么复杂，人似乎要记住很多东西才能有效的行走江湖似的？&lt;/p&gt;

&lt;p&gt;不，人毕竟是人，有效记忆是非常少的，就如一个人只能跟周围3-5个人合作一样，一旦加大数量，精力和脑袋就完全无法承受。这也是为什么我要强调做减法的原因。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>思路探讨(九) 实业，和，金融业</title>
   <link href="http://www.luzexi.com/2018/11/03/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A89"/>
   <updated>2018-11-03T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/11/03/思路探讨9</id>
   <content type="html">&lt;h3 id=&quot;实业和金融业&quot;&gt;实业，和，金融业&lt;/h3&gt;

&lt;p&gt;金融业虽然整体金钱的量大，但赚钱的比例最少，翻个几倍已经不得了要上天了，上涨百分之几就有几十亿甚至几百亿的升值幅度。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;因为它的量大，所以看起来好赚钱，其实不是的。金融业本身就是，靠着，金钱堆积起来的，量大是正常的事，我们砸进去的钱只是茫茫大海中的一滴水，完全跟整体赚多少钱没有关系。&lt;/p&gt;

&lt;p&gt;而实业潜力多的多，翻个十几倍都是平常的，几十倍，几百倍也不是不可能，最多翻到几千倍的也是有的。&lt;/p&gt;

&lt;p&gt;说到底还是，实业兴家，金融败家。&lt;/p&gt;

&lt;p&gt;打工赚钱也是条很好的路子，用技能夺得世界也未尝不可，收入从几千到几万，到几十万，再到几百万，根据你的努力程度和良好心态也是顺理成章的事情，这中间其实也翻了几百倍了，比起金融也是多出很多的倍的翻翻。&lt;/p&gt;

&lt;p&gt;说到我自己其实这几年的认知都错误的将金融排在了第一位。现在明白过来也不迟。起码我知道了一些，这个世界的运作规律，掌握了规律后，用行之有效的方法就能有好的成绩。&lt;/p&gt;

&lt;p&gt;这些年交了些看起来无用的‘学费’，但其实我自己知道，自己领悟到的东西巨大，完全突破了自己以前陈旧的思想，也大幅度转变了自己的思维模式，并且，在整个过程中也了解了自己很多，其实是人性的丑与恶。&lt;/p&gt;

&lt;p&gt;如果再让我重来一次，我依然会选择再折腾一次，学到的东西绝不是能用钱来衡量的，至少我知道了：&lt;/p&gt;

&lt;p&gt;1，知识改变命运，奋斗成就人生，科技成就世界。&lt;/p&gt;

&lt;p&gt;2，人都是随着时间而成长的，要跟随时间去经历，去磨练，去学习，尽量不要停下来。&lt;/p&gt;

&lt;p&gt;3，没有我，世界照样转得很好，有了我，世界该怎么转还是怎么转。不会因为我的到来而改变什么，也不会因为我的离开而缺少什么。&lt;/p&gt;

&lt;p&gt;4，我对世界来说就是个蚂蚁，不起眼，但是我的想法对于我个人来说非常重要，我想什么，怎么想决定了我，怎么做怎么走，对自己的前途命运非常重要。&lt;/p&gt;

&lt;p&gt;5，世界上，没有绝对的苦，也没有绝对的甜。苦中有甜，甜中有苦。苦中做乐是最佳状态，我们要在苦中找到甜。&lt;/p&gt;

&lt;p&gt;6，虽然这个世界永远都有，劣币驱逐良币的过程，但要明白，要相信，良币会获得最终胜利，光明最终会战胜黑暗，努力的人最终会比懒惰人更幸福。&lt;/p&gt;

&lt;p&gt;7，对于个人来说，最重要的是突破自我，挑战自我，唯有突破和挑战才能得到进阶，最终成就自我。&lt;/p&gt;

&lt;p&gt;8，真理都是很简单的一句话，简单的道理里，蕴含着复杂的思考过程和诸多的体验经历。&lt;/p&gt;

&lt;p&gt;9，漫长的人生长跑路上，健康和免疫力第一，努力，拼搏，学习第二，两者又缺一不可，两手都要抓两手都要硬。&lt;/p&gt;

&lt;p&gt;10，自律使我自由。&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>思路探讨(八) 谁在掌控世界</title>
   <link href="http://www.luzexi.com/2018/10/20/%E6%80%9D%E8%B7%AF%E6%8E%A2%E8%AE%A88"/>
   <updated>2018-10-20T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/10/20/思路探讨8</id>
   <content type="html">&lt;h2 id=&quot;一直尝试了解世界总是被世界的真相震撼real-world-是真的残酷但我们同样有希望逆袭不要怕我们能赢&quot;&gt;一直尝试了解世界，总是被世界的真相震撼，Real world 是真的残酷，但我们同样有希望逆袭，不要怕，我们能赢。&lt;/h2&gt;

&lt;h6 id=&quot;为什么整个世界是由40岁50岁的人在掌控的我们该如何思考&quot;&gt;为什么整个世界是由40岁，50岁的人在掌控的，我们该如何思考?&lt;/h6&gt;

&lt;p&gt;现实生活中，40，50岁的人占据了大部分的重要的位置，各种管理岗位，各种Top position，大量的人脉，以及大量的财富，甚至各类尖端技术都是由他们掌控的。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;40岁，50岁的人，在他们成年后经历了20，30十年的磨练，不管在技能上，生活经验上，社会经验上都已经基本到达了最巅峰的状态。&lt;/p&gt;

&lt;p&gt;在交际圈里，他们的朋友也是40，50岁的人为主，所以朋友圈里，基本都是各色大佬，有什么需要了解打个电话基本都能打听到。&lt;/p&gt;

&lt;p&gt;他们已经不再惧怕大部分的事物，以及困难。他们知道如何面对未知，如何处理恐惧。甚至情绪的自我调节也已经达到了一个相当熟练的程度。&lt;/p&gt;

&lt;p&gt;与20，30岁的人比较，他们精力相当，经验却更丰富，从根源上就更胜一筹。&lt;/p&gt;

&lt;p&gt;而与60，70岁的人比较，他们具备更好的体力和精力，经验也没有差很多，因为从60岁开始，学习速度由于体力和精力的下降而变得缓慢。&lt;/p&gt;

&lt;p&gt;因此从整个人类年龄的宏观分布来看，40，50岁的人占据了绝对的优势。他们统治世界是无可厚非的。&lt;/p&gt;

&lt;p&gt;20多岁的时候我知道这件事时，比较不满和愤慨，总是觉得凭什么20多岁的人就不能拥有世界。&lt;/p&gt;

&lt;p&gt;现在30多岁了，更多的是慌张，想着自己40岁的时候，如何能在这个统治者的世界里立足一个位置。&lt;/p&gt;

&lt;p&gt;越接近40岁越感受到来自世界的真正压力。我真切的体会到这是个 Real world 容不得半点玩笑。&lt;/p&gt;

&lt;p&gt;难道40，50岁的人就没有人压着他们吗，还有60，70岁的人压着他们，60，70岁的人已经奋斗了大半身，掌握了大部分的财富和权利。他们已经不需要亲自出面了，派30，40，50岁的人出去干活就可以了。&lt;/p&gt;

&lt;p&gt;他们已经完全成了幕后的操纵者。从表面上很难看到有60，70岁人的身影，因为他们已经完全懂得了如何低调如何归隐，以及为什么一定要低调一定要归隐的道理。&lt;/p&gt;

&lt;h3 id=&quot;世界无法改变我们要改变的是自己&quot;&gt;世界无法改变，我们要改变的是自己&lt;/h3&gt;

&lt;p&gt;世界是无法改变的，40，50岁的人统治世界，60，70岁的操纵世界，20，30岁的了解世界，这种布局是不可能改变的。&lt;/p&gt;

&lt;p&gt;同时，我们也会随着时间的推移，被推入到40，50岁或者60，70岁的人群中去。&lt;/p&gt;

&lt;p&gt;我们幻想下，到那个时候，当你站在40，50岁的圈子中时，是在底层，还是在中层，还是在上层呢？&lt;/p&gt;

&lt;p&gt;到那个时候，你是否有足够的能力统治这个世界，是否有足够的大的眼界，是否有足够大的视野，是否有足够强壮的身体。&lt;/p&gt;

&lt;p&gt;这是我们需要在40，50岁前做好的准备。是否锻炼了足够多的能力，经历了足够多的困境，完善了足够多的知识体系，储备了足够多的经验。&lt;/p&gt;

&lt;p&gt;准备工作的充分程度决定了，当我们被推入40，50岁的世界时，是依然被世界统治，还是统治世界。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>白领投资探讨(七) 实干方法</title>
   <link href="http://www.luzexi.com/2018/10/14/%E7%99%BD%E9%A2%86%E6%8A%95%E8%B5%84%E6%8E%A2%E8%AE%A87"/>
   <updated>2018-10-14T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/10/14/白领投资探讨7</id>
   <content type="html">&lt;p&gt;金融冰冻三尺，没什么可聊的，我们来聊聊学习和工作的方法论。&lt;/p&gt;

&lt;p&gt;我如何看待日常的学习，工作中的方法。&lt;/p&gt;

&lt;p&gt;有效利用每天有限的时间，不强行超出13个小时工作时间，并保持每天12个小时高强度工作，坚持每天运动健身，要在有限的时间范围内做出最大的效率。&lt;/p&gt;

&lt;h3 id=&quot;时间对于每个人都是一样的区别就是在于如何在每天的时间里将学习和工作效率最大化&quot;&gt;时间对于每个人都是一样的，区别就是在于如何在每天的时间里，将学习和工作效率最大化。&lt;/h3&gt;

&lt;p&gt;每天比别人多学习到一点，每天的积累，一个月就能比别人多学到30点，一季度就是90点，一年是365点，3年是1095点，5年就是1825点，10年就是3650点。&lt;/p&gt;

&lt;p&gt;5年的时间，能将你与普通人的差距完完全全拉开一个档次，10年的时间，你将成为完全不同层级的人。&lt;/p&gt;

&lt;p&gt;积跬步至千里，坚持奋斗坚持努力的力量会让震撼。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;h3 id=&quot;毛泽东同志曾强调我们不但要提出任务而且要解决完成任务的方法问题我们的任务是过河但是没有桥或没有船就不能过不解决桥或船的问题过河就是一句空话不解决方法问题任务也只是瞎说一顿可见方法十分重要方法得当事半功倍方法失当事倍功半&quot;&gt;毛泽东同志曾强调，“我们不但要提出任务，而且要解决完成任务的方法问题。我们的任务是过河，但是没有桥或没有船就不能过。不解决桥或船的问题，过河就是一句空话。不解决方法问题，任务也只是瞎说一顿。”可见方法十分重要，方法得当事半功倍，方法失当事倍功半。&lt;/h3&gt;

&lt;h2 id=&quot;一文经我手无差错事交我办请放心&quot;&gt;一，文经我手无差错、事交我办请放心&lt;/h2&gt;

&lt;p&gt;不要差不多，盯住最完美。别人对你的工作期望是100分，我们就要努力盯住如何给出200分的目标来做。&lt;/p&gt;

&lt;p&gt;超出别人对你的期望，达到最完美的状态。&lt;/p&gt;

&lt;h2 id=&quot;二长计划短安排立即做&quot;&gt;二，长计划、短安排、立即做&lt;/h2&gt;

&lt;p&gt;长计划，就是说要着眼明天、着眼未来、着眼长远。凡事预则立，不预则废。&lt;/p&gt;

&lt;p&gt;一个对人生和工作有计划的人，他就能胸怀大局、放眼长远，不为一时一地的不利所困。&lt;/p&gt;

&lt;p&gt;俗话说得好，愚者赚今朝，智者赚明天。切忌贪一时之功、图一时之名，而要脚踏实地、从长计议。&lt;/p&gt;

&lt;p&gt;长计划还不够，没有短期安排，就如迷路的蚂蚁，到处乱撞。&lt;/p&gt;

&lt;p&gt;每天给自己制定一个小的目标，制定1星期，1个月的目标。这样不仅可以知道每天要做些什么、做了些什么，还可以对工作进行有效控制，让每一个小目标、短安排的成绩，都成为成功路上的阶梯和里程碑。&lt;/p&gt;

&lt;p&gt;不管是长计划还是短安排，都要立即行动、马上就办，将学习和工作落到实处，否则都只是一句空话。&lt;/p&gt;

&lt;p&gt;只有做到了长计划、短安排，才能真正实现有序、有效；也只有把“立即做”当成自己的座右铭，并形成习惯，才能不断进步。&lt;/p&gt;

&lt;h2 id=&quot;三周周总结月月反思&quot;&gt;三，周周总结，月月反思&lt;/h2&gt;

&lt;p&gt;每过一段时间就及时“回头看”，检查审视一下自己的工作，确保任务不拖延、事情不遗漏，效率不低下。&lt;/p&gt;

&lt;p&gt;要保持稳健，高速，健康的发展。太快不行，身体吃不消，不可持续，太慢不行，跟不上潮流，被时代落在后面，坐吃山空。&lt;/p&gt;

&lt;p&gt;所以要稳健，要有自己的节奏，节奏时快时慢，要学会调整，让状态尽量稳定，偏离时尽自己最大努力拉回原点。&lt;/p&gt;

&lt;p&gt;“回头看”让人了解自己，了解过去的自己，了解现在的自己，计划未来的自己。&lt;/p&gt;

&lt;p&gt;了解到了自己的缺陷和优势，才能够做到有效突破。&lt;/p&gt;

&lt;h2 id=&quot;四学习工作化工作学习化&quot;&gt;四，学习工作化，工作学习化&lt;/h2&gt;

&lt;p&gt;事实反复告诉我们，学习力的高低是人与人之间拉开距离的重要因素。一个人只要做到坚持学习、善于学习、快速地学习，就一定会有所成就。&lt;/p&gt;

&lt;p&gt;大家应该把学习作为一种精神追求、一种工作状态、一种生活方式，下得苦功夫，求得真学问。&lt;/p&gt;

&lt;p&gt;要树立“不学习无以立”的意识，坚持向书本学习、向实践学习，边学边用，边用边学。&lt;/p&gt;

&lt;p&gt;在学习与工作的良性互动中不断增强本领，超越自我。&lt;/p&gt;

&lt;p&gt;工作就是学习，学习就是工作，要融会贯通。&lt;/p&gt;

&lt;h2 id=&quot;五信息要对称善于沟通&quot;&gt;五，信息要对称，善于沟通&lt;/h2&gt;

&lt;p&gt;团体的力量比你想象的强大，但溃散的团体不如个体。沟通是团结整个力量最好方法。&lt;/p&gt;

&lt;p&gt;及时有效的沟通，才能达成协调一致的意见、形成步调统一的行动。&lt;/p&gt;

&lt;p&gt;对自己所从事的工作，要了解上面的要求、左右的情况、下面的进展，就要增强主动沟通的意识，确保上情准确下达、下情及时上传，着力构建上下贯通、左右衔接、内外一体、立体交叉的运转体系，实现各方面工作无缝对接，形成“整体一盘棋，同唱一台戏”的良好格局。&lt;/p&gt;

&lt;h2 id=&quot;六跳出自身看自身立足自己看自己&quot;&gt;六，跳出自身看自身，立足自己看自己&lt;/h2&gt;

&lt;p&gt;人生是一个不断认识自我、完善自我的过程，一个人不可能用自己的眼睛完全看清自己，不识庐山真面目，只缘身在此山中。&lt;/p&gt;

&lt;p&gt;要学会登高望远，放开视野去比较，在一览众山小中看清自己的位置、自己的渺小，看到别人看不到的；&lt;/p&gt;

&lt;p&gt;要学会用宏观的视野看自身，不以自我为中心，用旁观者的心态，高出事物的一两个层次来审视自己；&lt;/p&gt;

&lt;p&gt;要学会以人为镜，见有才能的人要敢于和他比较，照出自己的差距和不足，明确方向和目标。&lt;/p&gt;

&lt;h2 id=&quot;七始终保持适度的紧张感&quot;&gt;七，始终保持适度的紧张感&lt;/h2&gt;

&lt;p&gt;对每一个人来说，压力太大会崩溃，但没有一定的压力，不保持适度的紧张感，对身体、对生命、对工作都是负能量。&lt;/p&gt;

&lt;p&gt;人无压力轻飘飘，有压力不一定是坏事，适度的紧张感对于一个单位、一个团队、一个组织、一个人的健康等方方面面都有好处。&lt;/p&gt;

&lt;p&gt;它能使我们不忘“初心”，远离职业倦怠，激发工作热情，始终让思维和行动保持在平均水准以上，甚至可以迸发出超出想象的能力。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>白领投资探讨(六) 为什么不要做逆向投资，和，为什么坚决要做逆向投资的探讨</title>
   <link href="http://www.luzexi.com/2018/09/23/%E7%99%BD%E9%A2%86%E6%8A%95%E8%B5%84%E6%8E%A2%E8%AE%A86"/>
   <updated>2018-09-23T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/09/23/白领投资探讨6</id>
   <content type="html">&lt;h3 id=&quot;为什么不要做逆向投资和为什么坚决要做逆向投资的探讨&quot;&gt;为什么不要做逆向投资，和，为什么坚决要做逆向投资的探讨&lt;/h3&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;一直没明白这个世界，也一直不断地在找这个世界的方向。这个世界，这么多的变化的，这么多的故事，这个多的人，我们要学习的太多太多。&lt;/p&gt;

&lt;p&gt;很久前就知道这个世界是变化的道理，不过这个道理始终都没有落在心里，直到今天才有点醒悟。&lt;/p&gt;

&lt;p&gt;我们不知道明年会怎样，明天会如何，甚至都无法确定下一秒会发生什么，一切都在变。&lt;/p&gt;

&lt;p&gt;我记得有一天工作到中午忘了吃饭，肚子很饿，于是穿上衣服去吃饭，然而这么一件大概率确定的事，却在出门后快进餐厅那一瞬间因为跟同事聊了几句，就跟着同事去了他的办公室，一个下午没有吃上饭。&lt;/p&gt;

&lt;p&gt;如果世界上有一个确定的事情，那就是，“这个世界是永恒变化的”这个道理。&lt;/p&gt;

&lt;h6 id=&quot;这就是我们今天要探讨的话题的基础在变化下投资&quot;&gt;这就是我们今天要探讨的话题的基础，在“变化下投资”。&lt;/h6&gt;

&lt;h3 id=&quot;1为什么不要做逆向投资&quot;&gt;1，为什么不要做逆向投资。&lt;/h3&gt;

&lt;p&gt;最常见的是在逆向投资中容易迷失自己。不要忘了，我们是时代下的蚂蚁，时代的浪潮推我们去哪，我们就得去哪，谁都逃不过，越挣扎越痛苦。&lt;/p&gt;

&lt;p&gt;这就是趋势，最大浪潮的趋势，最宏观的浪潮趋势，拍动着整个国家，或者整个地区，甚至是整个全球的去向。我们这种蚂蚁级别的小颗粒，做再大的挣扎都无济于事。&lt;/p&gt;

&lt;p&gt;因此逆向投资最容易受伤，它是在对抗时代，拒绝被推动，最后反而被时代撕裂。&lt;/p&gt;

&lt;p&gt;在逆向投资中，富豪变穷光蛋的例子遍地是，眼看他起高楼，眼看他宴宾客，眼看他楼坍塌，亿万富豪破产不是梦。&lt;/p&gt;

&lt;p&gt;不要试图对抗时代的趋势，越挣扎越痛苦，我们要做的就是做好自己，其他的交给时代和浪潮去选择。&lt;/p&gt;

&lt;h3 id=&quot;2又为什么要坚持做逆向投资&quot;&gt;2，又为什么要坚持做逆向投资。&lt;/h3&gt;

&lt;p&gt;先把教条拿出来，巴菲特说过：别人贪婪我恐惧，别人恐惧我贪婪。&lt;/p&gt;

&lt;p&gt;要反着做，是在这个社会生存的更好的基本要素。当所有人都悲观绝望卖出时，你就应该与众不同拿出积极乐观的态度坚决买入。&lt;/p&gt;

&lt;p&gt;大的盈利，大的利润基本上都是在逆向投资中获得的，因为低谷越低，高峰越高，上下幅度足够大时，利润能高的吓人。&lt;/p&gt;

&lt;p&gt;另外一点，是赔率，如果1%的概率1块钱，能赢10000块，为何不做，投100次也才100块，却能获得10000块的收入。&lt;/p&gt;

&lt;p&gt;足够大的赔率，是投资的最佳机会。这种机会，也就只会在逆势中才会出现。当发生恐慌，踩踏，挤兑，所有人都撤退时，这种绝佳的赔率机会才会浮出水面。&lt;/p&gt;

&lt;p&gt;等大多数人清醒过来，好的赔率早就没了。&lt;/p&gt;

&lt;p&gt;最后一点，要学会乐观面对世界。倒下了要学会站起来，只有站起来，才能继续往前进，躺在那里，只有死路一条。&lt;/p&gt;

&lt;h3 id=&quot;3如何理解两个方法&quot;&gt;3，如何理解两个方法。&lt;/h3&gt;

&lt;p&gt;其实这两个方法不矛盾，完全可以一起进行，同时操作。&lt;/p&gt;

&lt;p&gt;比如，在还没出现大面积资金撤退，并且还没出现撤退完毕迹象的情况下，坚决不做逆向投资。&lt;/p&gt;

&lt;p&gt;又比如，在大面积资金撤退完毕，并出现明显踩踏，恐慌现象后，以3年为期，把打算投资的资金分为36份，每个月投一份，而且这一份资金中，进行分散投资，降低踩雷概率，以防一次性亏损过大。&lt;/p&gt;

&lt;p&gt;再比如，不对没有实际业绩支撑的公司进行投资，在逆势中，这种公司雷暴概率最大，资金链断裂最快。&lt;/p&gt;

&lt;p&gt;还比如，坚决分批买入，那些自己熟悉的行业，自己擅长的行业。行业有起，有伏，有最冰冷时，也会有最火热时，自己擅长的行业，自己最清楚，什么时候最冷，什么时候最热。&lt;/p&gt;

&lt;p&gt;题外话：&lt;/p&gt;

&lt;h6 id=&quot;还记得跟一位友人聊天他说每次在人生的十字路口都知道哪条路是正确的但他每次都不选择那条路他说因为他知道那条路实在太难走了&quot;&gt;还记得跟一位友人聊天，他说：每次在人生的十字路口，都知道哪条路是正确的，但他每次都不选择那条路，他说因为他知道那条路实在太难走了。&lt;/h6&gt;

&lt;h6 id=&quot;正确的路当然难走即使选择了正确的路也不是每个人都能坚持走完的大多数人都在中途放弃了有些甚至一开始就没有选择这条难走的路&quot;&gt;正确的路当然难走，即使选择了正确的路，也不是每个人都能坚持走完的，大多数人都在中途放弃了，有些甚至一开始就没有选择这条难走的路。&lt;/h6&gt;

&lt;h6 id=&quot;所以这个社会的层级才是金字塔形状而不是扁平形状少数人赢得了胜利统治了世界&quot;&gt;所以这个社会的层级才是金字塔形状，而不是扁平形状。少数人赢得了胜利，统治了世界。&lt;/h6&gt;

</content>
 </entry>
 
 <entry>
   <title>白领投资探讨(五) 金融知识到底带给我了什么？</title>
   <link href="http://www.luzexi.com/2018/09/08/%E7%99%BD%E9%A2%86%E6%8A%95%E8%B5%84%E6%8E%A2%E8%AE%A85"/>
   <updated>2018-09-08T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/09/08/白领投资探讨5</id>
   <content type="html">&lt;h3 id=&quot;金融知识到底带给我了什么&quot;&gt;金融知识到底带给我了什么？&lt;/h3&gt;

&lt;p&gt;刚开始学习金融知识时候，对各类信息都非常关注，小到商品的的价格波动起伏，大到全球的政治格局。&lt;/p&gt;

&lt;p&gt;一开始不懂，认为牛人肯定是对信息把握和预测能力很强，包括对政策的洞察力等等。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;后来明白了点，价格是无序波动的，是无法预测的，价格的根源是交易，交易由两个人类相互作用，一个乐观的买入，一个悲观的卖出，而人类无序紊乱的情绪导致了整个波动的无需的。越短的周期内的价格波动的表象越是无法捕捉到规律，这就是几亿人情绪共同作用导致的。倘若是交易由机器发起，就会变得有规律可循，因为机器是无情绪化的。&lt;/p&gt;

&lt;p&gt;再后来又明白了点，价值才真正的王道，人的价值，知识的价值，市场的价值，技术的价值，那些无法用金钱换来的才是真正意义上的价值体现。&lt;/p&gt;

&lt;p&gt;不过价值可并不等于金钱，在一段很长时间里，价值在金钱上是无法体现出来的。就像那些死后才出名的画家，诗人，政治家，科学家一样，在生前，在还没有等到价值体现的那一刻，他们就去世了，一辈子都没能等到价值体现的那一刻。&lt;/p&gt;

&lt;p&gt;再后来，终于懂了这个世界上没有大神，即使巴菲特也不是绝对的，大家都是被时代的浪潮推着走的人，时代推我们到哪我们就在哪。&lt;/p&gt;

&lt;p&gt;在这个过程中，淘汰了许多人，留下来的活下来的得到了最终的胜利。于是就有人说这些人是大神，殊不知这个世界上有幸存者偏差的存在，所有有利信息都指向胜者的优势，忽略了失败者的能力。其实换个环境换个趋势，那些所谓的优胜者，有可能分分钟被秒杀。说白了就是运气好。&lt;/p&gt;

&lt;h3 id=&quot;那么难道说这个世界就是靠运气的可以说是也可以说完全不是&quot;&gt;那么难道说这个世界就是靠运气的？可以说是，也可以说完全不是！&lt;/h3&gt;

&lt;h3 id=&quot;因为人可以制造运气&quot;&gt;因为人！可！以！制！造！运！气！&lt;/h3&gt;

&lt;p&gt;努力的人创造价值，坚持努力的人创造更多的价值，十年如一日坚持努力的人创造巨量的价值，巨量的价值吸引了很多人来关注，关注的人越来越多，量变积累导致了质变，巨量的价值给我们带来了机会和运气。&lt;/p&gt;

&lt;p&gt;价值是人类社会的基础，没有价值的人，没有价值的事，是不会有人愿意去关心的，没有人关心的事才没有价值和意义。&lt;/p&gt;

&lt;p&gt;想要制造运气，就要坚持努力，特别是十年如一日的坚持，将带给你颠覆性的变化。&lt;/p&gt;

&lt;h3 id=&quot;胜者败者都很努力为什么还是分出了胜负&quot;&gt;胜者，败者，都很努力，为什么还是分出了胜负？&lt;/h3&gt;

&lt;p&gt;这个问题本身就有问题，因为这个问题太关注当前的胜负结果。&lt;/p&gt;

&lt;p&gt;胜，和，负，只是我们在整个坚持努力的过程中的一个结果。如果我们太关注胜负，很容易被胜负心理吞没，无法自拔。&lt;/p&gt;

&lt;p&gt;试问大家，谁就能够保证，一辈子赢？首富都有倾家荡产流落街头的例子，何况是其他人。&lt;/p&gt;

&lt;p&gt;“眼看他起高楼，眼看他宴宾客，眼看他楼坍塌”，这种事时时刻刻都在发生，万事万物一直在变，只有努力价值从而创造运气不会变。&lt;/p&gt;

&lt;h3 id=&quot;我们在生活中事业中学习中最怕不是失败而是害怕失败&quot;&gt;我们在生活中，事业中，学习中，最怕不是失败，而是害怕失败。&lt;/h3&gt;

&lt;h3 id=&quot;失败不可怕最怕的是害怕失败&quot;&gt;失败不可怕，最怕的是害怕失败。&lt;/h3&gt;

&lt;p&gt;当遭遇重大挫折或是迷茫后，有些人一蹶不振，从此得过且过混日子，再也抬不起头。这样的人是大多数，这样的公司也是绝大多数。&lt;/p&gt;

&lt;p&gt;但总有那么些人和公司，他们会在痛苦中思索，反省，成长，蜕变，直到进化出第二曲线，重回新一阶段的成长。&lt;/p&gt;

&lt;p&gt;经历过挫折的打磨，可能他们的成长不如过去锋利、迅猛，但更加沉稳坚定。&lt;/p&gt;

&lt;h3 id=&quot;在黑暗中保持乐观在狂热中保持理性最难&quot;&gt;在黑暗中保持乐观，在狂热中保持理性最难。&lt;/h3&gt;

&lt;p&gt;在失业下，没有收入来源时，甚至饥寒交迫时，你还能保持每天规律的作息，坚持运动，坚持学习，沉稳的心态，积极主动的态度吗？大多数人就像只发疯的苍蝇一般到处乱串，只有少数人冷静的对待，保持平稳心态，排除悲观情绪，积极乐观，主动出击。&lt;/p&gt;

&lt;p&gt;最黑暗的时候，最需要鼓起勇气面对，积极乐观向上，而大多数人即使明白这个道理也做不到，所以只有少部分人突出重围，从下层突围到中层，或者从中层突围到上层，甚至从上层突围到顶层。&lt;/p&gt;

&lt;p&gt;反过来说，如果现在社会安定，每个人都很有钱，工作机会很多，工资都很高，股价房价涨涨涨，并没有多少需要担心的事，大家只需要掏出钱来买买买，尽情享受躺着赚钱就行的时候，我们是否还能做到居安思危，让未来更加有保障吗？可以说，大多数人都沉浸在享受中，而并没有对未来有可能发生的灾难提高警惕。&lt;/p&gt;

&lt;p&gt;对狂热的不理性，才会有这么多倾家荡产的悲剧发生。&lt;/p&gt;

&lt;h3 id=&quot;大多数人还是逃不过遭难的只有少部分人可以&quot;&gt;大多数人，还是逃不过遭难的，只有少部分人可以。&lt;/h3&gt;

&lt;h3 id=&quot;大多数人都会沦为平庸平凡的一生而只有少部分人才真正为自己创造了运气成为新一代的领导者&quot;&gt;大多数人都会沦为平庸平凡的一生，而只有少部分人才真正为自己创造了“运气”，成为新一代的领导者。&lt;/h3&gt;

&lt;h3 id=&quot;这些少部分人就是那些自律努力积极勇敢的家伙们&quot;&gt;这些少部分人，就是那些，自律，努力，积极，勇敢的家伙们。&lt;/h3&gt;

</content>
 </entry>
 
 <entry>
   <title>中国，顶住！！</title>
   <link href="http://www.luzexi.com/2018/08/17/%E4%B8%AD%E5%9B%BD%E9%A1%B6%E4%BD%8F"/>
   <updated>2018-08-17T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/08/17/中国顶住</id>
   <content type="html">&lt;h6 id=&quot;在美国主导的加息和减税的政策下美元强势回流全球汇率大幅度波动很多新兴国家混乱不堪陷入经济危机金融危机中国家经济陷入倒退&quot;&gt;在美国主导的加息和减税的政策下，美元强势回流，全球汇率大幅度波动，很多新兴国家混乱不堪，陷入经济危机，金融危机中，国家经济陷入倒退。&lt;/h6&gt;

&lt;h6 id=&quot;中国也没能幸免今年股汇商品三杀经济数据糟糕&quot;&gt;中国也没能幸免，今年股，汇，商品，三杀，经济数据糟糕。&lt;/h6&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;确实中国存在很多问题，但美国难道问题不大吗，美国也存在诸多问题，甚至比中国还要多还要大。只是现在它的霸主地位，很多问题被掩盖了。&lt;/p&gt;

&lt;p&gt;而现在在中国，太多人崇洋媚外，特别是这个时候，中国经济最困难的时候，出汉奸的概率很大。&lt;/p&gt;

&lt;p&gt;说实话，美国确实强大，他的霸主地位无人能撼动，这是由历史原因造成的，但也不至于去投靠，连尊严都不要吧。&lt;/p&gt;

&lt;p&gt;我们可以学习，可以借鉴，但不可以放弃尊严选择去无脑膜拜。中国现在是世界经济第2大国，难道这还不足以说明中国的强大吗？！&lt;/p&gt;

&lt;p&gt;中国只需要一个机会就能颠覆美国。只要美国犯一个致命错误，比如次贷危机，内乱内战，或者陷入全面对外战争那种就可以。但这个机会一直没有出现，可能需要等上4，5十年。&lt;/p&gt;

&lt;p&gt;在两国都没有陷入危机的情况下，就会始终保持这种被压制的局面。打破平衡，需要大事件。&lt;/p&gt;

&lt;p&gt;现在最严重的问题是，境内外势力正在合力搞垮中国。那些近几年没赚到钱或者亏钱的人都站在欧美势力上了，汉奸会越来越多。&lt;/p&gt;

&lt;p&gt;真的要当心啊，每个新兴国家，在没达到霸主地位前，任然在发展中的情况下，一直都会有这种问题，不断有崇洋媚外的人冒出来，不顾国家利益的鼓动人民投靠他国。那些无法冷静的，没有独立思考的人民，就会像热锅上的蚂蚁一样乱了阵脚。&lt;/p&gt;

&lt;p&gt;我们任然是新兴国家，被美国收割是正常的，这场风暴是美国一手主导的，目的就是收割新兴国家。但太多人投靠美国，导致信心挫败。&lt;/p&gt;

&lt;p&gt;这几年中国肯定艰难痛苦，反转只能靠政府自己，人民群众是靠不住的。&lt;/p&gt;

&lt;h6 id=&quot;人民只会关心自己的利益而不顾国家的利益幸好国家是一党专政制度否则后果不堪设想政变大规模示威游行甚至军变内战都有可能&quot;&gt;人民只会关心自己的利益，而不顾国家的利益，幸好国家是一党专政制度，否则后果不堪设想：政变，大规模示威游行，甚至军变，内战都有可能。&lt;/h6&gt;

&lt;h6 id=&quot;美国就希望看到中国内乱内战这样他的目的就达到了一旦内乱经济地位全球地位就不保错失了这个地位未来再重返将难上加难&quot;&gt;美国就希望看到中国内乱，内战，这样他的目的就达到了。一旦内乱，经济地位，全球地位就不保，错失了这个地位，未来再重返，将难上加难。&lt;/h6&gt;

&lt;h3 id=&quot;中国千万要顶住&quot;&gt;中国千万要顶住！！！！&lt;/h3&gt;
</content>
 </entry>
 
 <entry>
   <title>白领投资探讨(四) 流动性探讨</title>
   <link href="http://www.luzexi.com/2018/08/13/%E7%99%BD%E9%A2%86%E6%8A%95%E8%B5%84%E6%8E%A2%E8%AE%A84"/>
   <updated>2018-08-13T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/08/13/白领投资探讨4</id>
   <content type="html">&lt;p&gt;#白领投资探讨(四)&lt;/p&gt;

&lt;p&gt;流动性探讨&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;从交易流动性谈起，在股票和期货市场上做交易，交易量就是流动性，如果没有流动性，就相当于买卖的人少，从而导致少数人决定大多数人价格的情况。&lt;/p&gt;

&lt;p&gt;打个比方一个市值100亿的企业股票，每天的交易量为100万，今天涨5%，那么就是说，这100万导致了100亿的资金躺着赚了5个亿。一个区区100万，却能影响100亿的资金，是不是很不正常。流动性缺失导致的，因为没人交易所以才会使得100万影响100亿。今天会因为这100万的交易涨5%，明天也会因为这100万的交易量而跌10%，甚至20%，这种可能性很大。&lt;/p&gt;

&lt;p&gt;流动性可以比喻为人流，交易的标的可以比作为一座城市，一座城市如果没有人流那就是个空城，如果有人在一座人烟稀少的城市做生意会如何，一个面包卖1块钱也会可能没人要。即使因为空城导致的供给短缺价格上涨，有人卖100块一个面包，那又怎样，每个月就那么1-2个人来买你这个100块的面包，你永远赚不到大钱，而且随时有亏本的风险，因为这1-2个人随时可能离开，你随时会因为没人来买而亏本破产。在这种城市里做生意是十分危险的，即便你垄断了货物，也有可能破产亏本，因为趋势是空城越来越空，人越来越少，交易越来越少。&lt;/p&gt;

&lt;p&gt;因此流动性差得地方价格特别容易有特别大的起伏，今年1块钱一个，明年100块钱一个，但买卖交易的人永远都只会是那么一两个。大的资金是绝对不会去那种地方的，因为即使在1块钱买进仓库的面包，在价格涨到100块的时候卖出，也卖不了几个，没人来接盘，大量的面包堆积在仓库卖不出去。&lt;/p&gt;

&lt;h3 id=&quot;聪明的富人远离流动性差的投资产品是有道理的他们知道倘若没人做他们的对手盘没人愿意接盘怎么做都是死永远赚不到钱而且随时有把所有钱都亏完的风险他们何必要冒这么大的风险做这种愚蠢的事只有没有钱的穷人眼里只看到价格的穷人才会冲进去交易厮杀最终的结果是没人能赢因为交易本身就是个负和游戏买卖双方在交易完成时就要交一笔费用交易税&quot;&gt;聪明的富人远离流动性差的投资产品是有道理的，他们知道倘若没人做他们的对手盘，没人愿意接盘怎么做都是死永远赚不到钱，而且随时有把所有钱都亏完的风险，他们何必要冒这么大的风险做这种愚蠢的事。只有没有钱的穷人，眼里只看到价格的穷人，才会冲进去交易厮杀，最终的结果是没人能赢，因为交易本身就是个负和游戏，买卖双方在交易完成时就要交一笔费用，交易税。&lt;/h3&gt;

&lt;p&gt;那么流动性大，到底有什么好处呢？&lt;/p&gt;

&lt;h3 id=&quot;1当参与者众多的时候能平摊风险要明白交易是人创造出来人的思维具有多样有人看多就有人空否则怎么会有成交就因为有人卖有人买才能成交卖的人看空买的人看多人越多多样性越复杂随时随地有看空的转为看多看多的转为看空多空一直在随着时间的推移不断得变化人越多这种多样性就越多变化也就越复杂&quot;&gt;1，当参与者众多的时候，能平摊风险。要明白，交易是人创造出来，人的思维具有多样，有人看多就有人空，否则，怎么会有成交，就因为有人卖有人买才能成交。卖的人看空，买的人看多，人越多，多样性越复杂，随时随地有看空的转为看多，看多的转为看空，多空一直在随着时间的推移不断得变化，人越多这种多样性就越多，变化也就越复杂。&lt;/h3&gt;

&lt;p&gt;所以我们说股价是不可控的，无论你精通多少数学方法，AI计算，宏观经济，企业财报都无法知道股价的走势。这不仅仅是在流动性大的地方不可控，在流动性少的地方也一样不可控，因为交易本身就是由人创造出来的，人这种生物本身就是不可控的，虽然我们因为这种不可控导致获得了很多好处，比如发明新的理论，发现新的大陆等。&lt;/p&gt;

&lt;p&gt;那么越多的多样性怎么就能平摊风险了呢？就因为多样性才能平摊风险，不至于预期一致性，比如一致看空无人看多的情况，一个几百亿市值的公司，一夜市值归零，就是一致看空导致的结果，所有人都不看好，没人看好的结果就是踩踏，挤兑，巨幅下跌。&lt;/p&gt;

&lt;p&gt;但是如果有足够多的多样性会如何？即使再大的坏消息，依然有人看多，那些人认为坏消息固然不好，但是跌的太过，完全超出了坏消息的影响幅度，或者有人认为利空消息是有人恶意编造，或者有人认为利空消息有可能会转折，甚至会有人认为利空消息并不一定是坏消息也有利于管理层认清现实在今后的工作中更加有效对待现有的漏洞。&lt;/p&gt;

&lt;p&gt;这些多样性导致大量的买入操作，从而让更多人相信跌的太多，或者有希望将利空消息转为利好消息，从而随着时间的推移，看空转看好的人越来越多，致使不至于导致非常糟糕的一致性预期，踩踏，挤兑等。&lt;/p&gt;

&lt;p&gt;这就是流动性的好处，让更多的多样性来平摊风险，而不至于让预期一致性发生而导致一瞬间破产。&lt;/p&gt;

&lt;h3 id=&quot;2吸引更多资金参与有更多接盘可能的选择流动性好的地方因为交易的频繁导致交易量大比如1天几十亿资金的交易量待在里面的1个亿体量的基金会很放心随时都可以在几天之内把自己手上的股票抛完也可以在几天之内买进几亿的股票而不让股票大幅上升而增加自己的持仓成本&quot;&gt;2，吸引更多资金参与，有更多接盘可能的选择。流动性好的地方，因为交易的频繁，导致交易量大，比如1天几十亿资金的交易量，待在里面的1个亿体量的基金会很放心，随时都可以在几天之内把自己手上的股票抛完，也可以在几天之内买进几亿的股票而不让股票大幅上升而增加自己的持仓成本。&lt;/h3&gt;

&lt;p&gt;所以更多的资金愿意参与进来，因为大家知道，自己随时都能套现走人，而不用顾忌被埋，他们更多的专注是企业的内在，企业的核心资产，核心技术，技术壁垒，管理层人员，行业供给等。&lt;/p&gt;

&lt;p&gt;当资金认为这个企业有美好未来时候，它可以毫不犹豫的买入而不影响股价上涨带来的持仓成本抬高，相反，当他们认为美好未来不再，可以随时卖出而不影响股价下跌导致成交价下跌，因为接盘的人随时都有。&lt;/p&gt;

&lt;p&gt;流动性大导致，资金持有人可以不用考虑自己想卖的时候没人要。作为一个投资持有者，无论是你持有股票，还是期货，还是房产，都是一样的，最怕的不是卖的价格太低，而是卖不出去，没人愿意接盘，再低的价格都没人要的情况，因为那时候就相当于你手里持有的是一张废纸。&lt;/p&gt;

&lt;h3 id=&quot;简单的来说流动性差的地方更容易破产一瞬间破产的概率较大流动性大的地方虽然不能保证价格上升但可以保证不会一夜破产并且会因为多样性多而得到有更多机会&quot;&gt;简单的来说，流动性差的地方更容易破产，一瞬间破产的概率较大，流动性大的地方虽然不能保证价格上升但可以保证不会一夜破产，并且会因为多样性多而得到有更多机会。&lt;/h3&gt;
</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第六章，网络层(三) - 实现UDP</title>
   <link href="http://www.luzexi.com/2018/08/07/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-%E7%BD%91%E7%BB%9C%E5%B1%823"/>
   <updated>2018-08-07T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/08/07/Unity3D高级编程之进阶主程-网络层3</id>
   <content type="html">&lt;h3 id=&quot;实现udp&quot;&gt;实现UDP&lt;/h3&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;在前面介绍了如何实现TCP socket，下面要介绍下UDP的实现方式。其实两者都是长链接的方式，很多地方都有相识之处，比如两者都需要连接和断开事件支撑，都需要做发送和接收队列缓存，都需要定义数据包协议格式，都需要加密和校验。&lt;/p&gt;

&lt;p&gt;我们先来说说TCP有而UDP没有的部分，再看看需要我们在UDP上实现的有哪些功能。和TCP相比，UDP基本就是TCP的阉割版本，很多TCP本身自带有的功能UDP都没有，虽然它们最大的区别是数据传输的方式，但在接口和逻辑层上很难感觉到它们的区别，不过至少我们得知道它们传输的不同方式。相同的地方包括，大都以异步发送和接收的方式进行，以及都需要理解多线程同步的操作机制，还有发送和接收都需要缓冲队列，并且发送数据时都需要合并数据包(其实TCP可以不用，因为它本身就有确认和重发机制)。TCP有而UDP没有的地方包括，UDP不会自己校验重发，由于是数据报的发送模式丢包概率大，数据包接收顺序不确定，而且UDP本身没有连接状态导致没有连接断开这个机制，也没有连接确认机制。&lt;/p&gt;

&lt;p&gt;前面说了这么多关于TCP和UDP在程序上的区别就是要大家明白，直接使用UDP来做网络发送和接收会遇到诸多问题，可以说如果直接使用而不加修饰，使得不确定性太大，导致基本用不了UDP。因此这节我们来说说应该怎么封装才能让UDP顺利的使用在我们的项目上。&lt;/p&gt;

&lt;h6 id=&quot;连接确认机制&quot;&gt;连接确认机制&lt;/h6&gt;

&lt;p&gt;TCP有连接的三次握手协议，相当于在连接过程中跟服务器端协商后敲定我们已经建立了连接这个一致预期，而UDP是无状态链接，它并没有三次握手的协议，所以可以说UDP的连接其实是一厢情愿的，客户端并不知道是否真正连接成功了，如果由于网络异常原因没有连接上，就会导致收发失败。其实怎么确认UDP连接上了服务器，这才是我们想要得到的反馈，我们需要准确的得知UDP连接是否已经成功连接上服务器，因此这个连接确认机制必不可少。&lt;/p&gt;

&lt;p&gt;发送和接收如果建立在连接确认的基础上则会更加牢靠，因此我们必须先确认知道我们是否连接成功。能够判断连接是否成功是整个实现UDP的第一步，只有这样才能顺利得进行下面的数据包收发操作。&lt;/p&gt;

&lt;h6 id=&quot;我们应该怎么确认连接成功&quot;&gt;我们应该怎么确认连接成功&lt;/h6&gt;

&lt;p&gt;很简单我们可以模仿TCP的确认连接机制，我们来看看TCP连接的三次握手在数据包上是怎么做的。&lt;/p&gt;

&lt;p&gt;1.首先客户端向服务器端发送一个数据包，里面包含了Seq=0的变量，表示当前发送数据包的序列号为0，也就是第一个数据包。&lt;/p&gt;

&lt;p&gt;2.务器端收到客户端的数据包后，发现Seq=0，说明是第一个包是用来确认连接的，于是给客户端也发送了一个数据包，包含了Seq=0，和Ack=1，表示服务器端已经收到客户端的连接确认包了，并且回应包Ack序列标记为1。&lt;/p&gt;

&lt;p&gt;3.当客户端收到服务器端给的回应数据包后，知道了服务器端已经知道我们想要并对方已经建立连接，于是向服务器端发送了一个数据包，里面包含了Seq=1，Ack=1，表示确认数据包已经收到，连接已经确认，开始发送数据。&lt;/p&gt;

&lt;p&gt;以上就是TCP的三次握手来确认连接的流程在数据包中的体现。&lt;/p&gt;

&lt;p&gt;在UDP下它自身并没有三次握手机制，为了建立更好的确认连接机制，我们可以模仿TCP三次握手的形式来确认连接。不过第3次握手稍微有点多余，我们可以省去最后一次握手的数据包，改为2次握手。步骤如下：&lt;/p&gt;

&lt;p&gt;首先在UDP打开连接后，在确认连接前不进行任何的其他类型的数据发送和接收，我们将这种发送数据包以确认连接成功与否的数据包称为握手包。&lt;/p&gt;

&lt;p&gt;在打开连接后，客户端先向服务器端发送一个握手数据包，代表客户端向服务器端请求连接确认信号的数据包，包内的数据仅仅是一个序列号Seq=0，或者不是序列号也行，它可以是一个特殊的字段。只要当服务器端收到这个握手数据包后能够识别该数据包为连接确认的握手包，也就是实现了第一次握手。&lt;/p&gt;

&lt;p&gt;服务器端在收到第一次握手数据包后，需要向客户端反馈一个握手数据包，里面同样带有客户端能识别的连接确认信号。当客户端接收握手数据包时说明发出去给服务器端的连接确认数据包有了反馈，并且收到了服务器握手数据包的反馈，也就是说第二次握手成功。在接收到了这个第二次握手连接确认数据包时，双方都可以认为是连接已经成功建立。&lt;/p&gt;

&lt;p&gt;整个UDP确认连接的握手过程，就相当于客户端和服务器端的一次交流，相互认识一下并且示意双方后面的交流即将开始。&lt;/p&gt;

&lt;p&gt;实现UDP连接确认具体步骤（伪代码）：&lt;/p&gt;

&lt;p&gt;1.首先使用API建立UDP连接：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
SvrEndPoint = new IPEndPoint(IPAddress.Parse(host), port);
UdpClient = new UdpClient(host, port);
UdpClient.Connect(SvrEndPoint);

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述代码为使用C#的UDP接口对指定的IP和端口打开链接。&lt;/p&gt;

&lt;p&gt;2.启动接收数据线程：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
UdpClient.BeginReceive(ReceiveCallback, this);
void ReceiveCallback(IAsyncResult ar)
{
    Byte[] data = (mIPEndPoint == null) ?
        UdpClient.Receive(ref mIPEndPoint) :
        UdpClient.EndReceive(ar, ref mIPEndPoint);

    if (null != data)
        OnData(data);

    if (mUdpClient != null)
    {
        // try to receive again.
        mUdpClient.BeginReceive(ReceiveCallback, this);
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;UPD是无状态连接，打开连接就相当于只是一厢情愿的自我意识，因此可以立刻开始接收数据的接口并开启线程。&lt;/p&gt;

&lt;p&gt;3.发送连接确认数据包，并屏蔽其他发送和接收功能。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
SendConnectRequest();
StopSendNormalPackage();
StopReceiveNormalPackage();

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这三个函数是自定义的，第一个表示发送握手包，可以认为是为握手包特意定制的数据装载函数，第二个和第三个只是把普通的接收数据包的开关给关了，在连接还没确认前不接收任何其他形式的数据包，实际上接收数据包仍在继续，只是不加入到数据处理队列且也不处理数据的具体句柄。&lt;/p&gt;

&lt;p&gt;4.等待接收连接确认数据包：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
Void OnData(byte[] data)
{
     If( !IsConnected )
    {
        If( IsConnectResponse(data) )
        {
            OnEvent( Event.ConnectSuccess );
            IsConnected = true;
        }
        Return;
    }
    ProcessNormalData(data);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这是接收到数据的处理过程，先判断是否当前是否已经处于连接状态，再判断该数据包是否为握手数据包，如果确认是握手数据包的话，那意味着服务器收到了握手数据包并且做出了回应，连接可以确认了。因此在这里发出了连接确认事件，并且标记连接为确认状态。&lt;/p&gt;

&lt;p&gt;5.连接握手数据包收到后，说明确认连接已经成功建立，于是就可以开启正常发送和接收数据包的功能。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
Void ProcessNormalData(data)
{
    If( !IsConnected ) return;

    DealNetworkData(data);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;经过与服务器端数据包的来回，UDP完成了2次握手的确认机制，已经可以认定为连接已经成功建立，这里是正常数据包处理过程，包括识别，装载，推入队列，检测是否丢失，是否需要重发等。&lt;/p&gt;

&lt;h6 id=&quot;检测连接是否依旧存在&quot;&gt;检测连接是否依旧存在&lt;/h6&gt;

&lt;p&gt;UDP自己不能判断连接是否断开，因为它是无状态的连接，打开即完成连接关闭即完成断开，因此需要我们自己来做断线检测，检测的方式也与我们前面说的如何主动检测TCP连接状态的心跳包类似，因为这种方式是花费最小的代价并且能够及时准确的确认连接的方式。因此UDP检测连接的判定机制，也可以用数据包来回的形式，不过这次不像握手数据包那样只是单一一个数据包，而是持续的心跳包的形式来做持续的判断连接状态。&lt;/p&gt;

&lt;p&gt;首先，我们要与服务器端有个协定，每隔X秒（比如5秒）发送一个心跳数据包给服务器端，这个客户端发送的心跳数据包里包含了一些客户端信息，包括ID，角色状态，设备信息等，包体不能太大，否则会就加重了宽带负担。&lt;/p&gt;

&lt;p&gt;当服务器端收到心跳数据包时，也立刻回复一个心跳数据回应包，里面包含了，服务器端当前时间，服务器端当前状态等信息。客户端收到此数据包时，说明连接尚在，也能同时同步服务器端的时间和一些基础的信息。&lt;/p&gt;

&lt;p&gt;如果客户端很久没有收到心跳数据回应包时，就表明，连接已经断开了，比如30秒没收到心跳包，可以判断连接已经断开。服务器端也是一样操作，当没有收到心跳包很久，就表明客户端的连接已经断开。这时客户端就可以开启相应的重连程序，或重连提示以及步骤。&lt;/p&gt;

&lt;p&gt;步骤可以分为如下4步：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    1.每隔X秒向服务器端发送心跳数据包。

    2.服务器端收到心跳数据包后回复心跳响应数据包。

    3.如果客户端和服务器端都很久没有收到心跳数据包，比如30秒，则判定连接断开。

    4.当判定为连接断开，则主动断开连接并发起断开连接事件，通知客户端提示用户，或者重新创建连接，服务器端则是处理与之相关的数据处理操作。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这里不只是客户端主动发送数据包给服务器，也可以反过来服务器主动发送数据包给客户端的形式检测连接是否断开。&lt;/p&gt;

&lt;h6 id=&quot;数据包校验与重发机制&quot;&gt;数据包校验与重发机制&lt;/h6&gt;

&lt;p&gt;我们前面说UDP相当于是TCP的阉割版，其中UDP最关键的阉割部分就是校验和重发机制。没有校验和重发机制，意味着发送端无法知道数据包的发送是否到达，丢失了也无法重新发送的机制来补充丢失的数据包，因此我们需要自己编写增加对数据的校验和重发机制来确保数据的可靠性。&lt;/p&gt;

&lt;p&gt;TCP已经有校验和重发机制，我们可以模仿它的校验和重发机制，把它搬到UDP上，并在此基础上加以改进，这样我们即有了UDP的速度，又有了TCP的可靠性。&lt;/p&gt;

&lt;p&gt;我们先来看看TCP是如何做数据包的校验和重发的：&lt;/p&gt;

&lt;p&gt;先解释几个英文名词Seq和Ack。Seq即Sequence Number，为源端（source）的发送序列号；Ack即Acknowledgment Number，为目的端（destination）的接收确认序列号。&lt;/p&gt;

&lt;p&gt;1.首先A端向B端发送数据包，TCP的包头中里面包含了字段Seq(sequence number)序列号值为1(即已经发送的数据包的累计大小)，比如这次A向B发送的数据包大小为264，则size大小为264。&lt;/p&gt;

&lt;p&gt;2.B端收到传过来的数据包后，知道了当前连接的这个数据包的序号为1，也就是连接后第一个数据包。于是向A端发送了一个确认包，确认包中包含了的Ack=264(接收到的数据包的累计大小)，告诉A端我B端已经收到了数据包，现在累计接收大小为264的数据包。&lt;/p&gt;

&lt;p&gt;3.A端收到B端发来的确认数据包时，会检测累计发送大小和累计接收大小是否一致，如果发现累计接收大小与累积发送大小不一致则认为传输错误启动重传机制，退回到最后一次正确的的数据包位置进行重传，以保证可靠性。&lt;/p&gt;

&lt;p&gt;4.如果大小比较一致，则认为B端准确收到了数据包，A端可以继续发送其他数据包，当再次发送时里面包含的Seq序列号更改为了265，意思为累计发送数据大小，假如这次发送的数据包大小为100，则size字段填写100，B端接收到后再向A端发送确认包，确认包中包含了Ack=365，意思是已经累计收到数据包大小为365。当A端收到确认包时会先对比一致性，如果累计大小错误，请启动重传机制，确保可靠性。以此类推&lt;/p&gt;

&lt;p&gt;5.如果是由B端向A端发送数据也是同样的步骤。B端向A端发送数据也是同样的方法和步骤。数据包中包含了B端的Seq(已经发送的数据包大小)，比如累计发送了1，seq=1，这次数据包大小为585，A端接收到B端的数据包后，向B端发送确认包，包中包含了B端发过来的Ack  =586，B端收到确认包后，就知道了累计接收的数据包大小已经到了585，也就是说当前的数据包已经发送成功。如果累积发送和累积接收数据不一致则启动重发机制，从最近的正确确认点开始发送数据。&lt;/p&gt;

&lt;p&gt;序列号确认机制是TCP可靠性传输的保障，除了累积接收大小和累积发送大小的比较外，若在规定的时间内收到确认数据包就表明该报文发送成功，可以发送下一个报文，如果超过时间则启动重传（TCP Retransmitssion）。&lt;/p&gt;

&lt;p&gt;我们可以借鉴TCP的方法，UDP也可以用此方法来做检测和重传。不过TCP接收和发送累积大小的检测方式使得重传量比较大，一旦会导致失败重传整个数据，重传内容太多。因此它不能准确快速的定位重传的数据包，由于中间数据包的丢包，导致已经到达的数据也需要重传，这种类型的可靠性保证的依赖于消耗大量的带宽消耗。&lt;/p&gt;

&lt;p&gt;因此我们在实现UDP的检测和重传时，我们可以进行如下的改进。&lt;/p&gt;

&lt;p&gt;1.A端向B端发送数据包，数据包中包含Seq=1(表示数据包的发送序列)，发送后将此数据包推入到已经发送但还没有确认的队列里。&lt;/p&gt;

&lt;p&gt;如果B端接收到Seq=1的数据包，就回应客户端一个确认包，包中Ack=1，表示Seq=1的包已经确认收到。&lt;/p&gt;

&lt;p&gt;如果B端没有接收到数据，客户端X秒后发现仍然没有收到Seq为1的确认包，判定为Seq=1的数据包传输失败，从已经发送但未确认的数据包队列中取出Seq=1的数据包，重新发送。&lt;/p&gt;

&lt;p&gt;2.例如A端向B端发送了10个数据包，分别是Seq=1，2，3，4，5，6，7，8，9，10，服务器收到的序列是，1，3，4，5，7，8，9，10，其中有2，6，没有收到数据包。&lt;/p&gt;

&lt;p&gt;A端在等待确认包超时后，对2，6进行重传。在B端接收到数据包后，处理数据包时，如果数据包顺序有跳跃的现象就表明数据包丢失，就等待A端重传，这时就在断开的序列处停止处理数据包，等待重传数据包的到来。&lt;/p&gt;

&lt;p&gt;3.B端也可以做加快重传确认时间的处理。A端向B端发送5个数据包，分别是Seq=1，2，3，4，5，B端收到的包的序列是1，3，4，5，当收到3时，发现2被跳过1次，当收到4时发现2被跳过2次，立刻向A端发送确认包要求启动2的重传，这样就加快了丢包重传的确认速度。&lt;/p&gt;

&lt;h6 id=&quot;丢包问题分析&quot;&gt;丢包问题分析&lt;/h6&gt;

&lt;p&gt;UDP丢包多是很正常现象，这是UDP牺牲质量而提高速度的代价。UDP丢包的原因很多，我们这里做个分析。&lt;/p&gt;

&lt;p&gt;1.接收端处理时间过长导致丢包：&lt;/p&gt;

&lt;p&gt;当调用异步接收数据方法接收到数据后，处理数据会花去一些时间，处理完后再次调用接收方法，在这二次调用间隔里发过来的包可能会丢失。&lt;/p&gt;

&lt;p&gt;要解决接收方丢包的问题其实很简单，首先保证程序执行后马上开始监听（如果数据包不确定什么时候发过来的话），其次要在收到一个数据包后最短的时间内重新回到监听状态，其间要尽量避免复杂的操作。&lt;/p&gt;

&lt;p&gt;对于这种情况可以修改接收端，将包接收后存入一个缓冲区，然后迅速返回继续开启接收线程。或者使用前面提到的双队列机制，来缩短锁队列的时间从而解放了处理包的时间和接收数据包的线程之间的冲突，让两个线程能迅速回到自己的‘岗位’上做自己的事。&lt;/p&gt;

&lt;p&gt;2.发送的包巨大导致丢包概率加大：&lt;/p&gt;

&lt;p&gt;虽然Send方法会帮我们做大包切割成小包发送的事情，但包太大也不行。例如超过50K的一个udp包，不切割直接通过send方法发送也会导致这个包丢失。&lt;/p&gt;

&lt;p&gt;发送的数据包较大是个危险的行为，如果超过接收者缓存将大概率导致丢包，一般当包超过MTU大小的数倍就会增大丢包的概率。&lt;/p&gt;

&lt;p&gt;什么是MTU，即Maximum Transmission Unit的缩写，意思是网络上传送的最大数据包。大部分网络设备的MTU都是1500，如果本机的MTU比网关的MTU大，大的数据包就会被拆开来传送，这样会产生很多数据包碎片增加丢包率，从而增加重发概率导致降低网络速度。&lt;/p&gt;

&lt;p&gt;报文过大的问题可以通过控制报文大小来解决，使得每个报文的长度小于MTU。以太网的MTU通常是1500 bytes，其他一些诸如拨号连接的网络MTU值为1280 bytes，如果使用speaking这样很难得到MTU的网络，那么最好将报文长度控制在1280 bytes以下，这些都是经验之谈。&lt;/p&gt;

&lt;p&gt;3.发送的包频率太快：&lt;/p&gt;

&lt;p&gt;虽然每个包的大小都小于MTU大小但是频率太快，例如40多个MTU大小的包连续发送中间不休眠，也有可能导致丢包。&lt;/p&gt;

&lt;p&gt;这种情况可以通过建立Socket接收缓冲队列解决，以及通过建立发送缓冲队列来解决，并且在发送频率过快的时候考虑线程Sleep休眠一下作为时间间隔。&lt;/p&gt;

&lt;p&gt;这里有些人会不理解发送速度过快为什么会产生丢包，原因是UDP的发送数据是不会造成线程阻塞的，也就是说UDP的发送不会像TCP中的发送数据那样直到数据完全发送才会返回回调用函数，UDP并不保证当执行下一条语句时前面的数据是否被发送，它的发送接口是异步的。&lt;/p&gt;

&lt;p&gt;如果要发送的数据过多或者过大，那么在缓冲区满的那个瞬间要发送的报文就很有可能被丢失，一般一秒钟几个数据包不算什么，但是一秒钟成百上千的数据包就不好办了。&lt;/p&gt;

&lt;p&gt;以上是UDP的实现细节，同时阐述了UDP的实现难点和注意点。&lt;/p&gt;

&lt;p&gt;参考文献：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    1， 基于TCp的数据包传输过程
&lt;/code&gt;&lt;/pre&gt;
</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第六章，网络层(二) - 实现TCP</title>
   <link href="http://www.luzexi.com/2018/08/07/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-%E7%BD%91%E7%BB%9C%E5%B1%822"/>
   <updated>2018-08-07T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/08/07/Unity3D高级编程之进阶主程-网络层2</id>
   <content type="html">&lt;h3 id=&quot;程序实现tcp长连接&quot;&gt;程序实现TCP长连接。&lt;/h3&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;前面讲了很多关于协议的介绍和一些底层的知识，这节我们从原理出发在程序上实现协议的通信。我们先从整体出发，列一下实现TCP连接需要的考虑哪些方面：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	1， 建立连接

	2， 断线检测

	3， 网络协议

	4， 发送和接收队列缓冲

	5， 发送数据合并

	6， 线程死锁策略
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上面6个方面是程序在建立和实现连接的必备要素，我们下文将依次讲解如何来对此作出程序上的实现。&lt;/p&gt;

&lt;p&gt;TCP本身已经具备有数据包可靠性确认，以及丢包重发机制，数据包的大小也没有做限时，从TCP那里我们可以把免费得到的这些功能。所以在实现TCP的连接中不需要我们再做包括：包体的校验、包体的拆分，以及重发数据包，省掉了很大一部分麻烦。我们只需要做的就是建立连接，发送，以及接收，三个步骤，以及这三个步骤引起的一系列问题的对策。不过如果不对发送数据进行合并，就会有很多小的数据分批发送，导致发送效率降低。所以我们下文中会提到关于发送合并的问题。&lt;/p&gt;

&lt;h6 id=&quot;介绍下tcp的api库&quot;&gt;介绍下TCP的API库&lt;/h6&gt;

&lt;p&gt;c#的.net库提供了 TCP 的 Socket连接API，我们来看看c#的.net库中的这些API的用法。&lt;/p&gt;

&lt;p&gt;一般情况下我们不会去用阻塞方式连接和接收，因为我们不会让你的游戏卡住不动来等待连接，因为这有可能导致崩溃，所以连接，接收，断开都是异步的线程操作。同步阻塞的操作可能会在周边工具中会用到，比如编辑器的工具，回放工具，GM的工具等，但其他大部分时候都会更加平滑的异步操作作为网络连接和收发操作。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	BeginConnect，开始连接

	BeginReceive，开始接收信息

	BeginSend，开始发送数据

	BeginDisconnect，开始断开

	Disconnect(Boolean)，立刻断开连接
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上锁接口中前四个都是异步的，调用后会开启一个线程来工作，最后一个为同步阻塞方式的断开连接接口。最后一个阻塞式大都在游戏退出时调用，但问题是APP没有退出事件，因此一般Disconnect都会用在Unity3D Eitor下或者windows版本上调用，以保证在开发时强制退出后编辑器不会奔溃。&lt;/p&gt;

&lt;h6 id=&quot;线程锁&quot;&gt;线程锁&lt;/h6&gt;

&lt;p&gt;实际项目中网络模块中所有的操作都会以线程级的形式对待，而Unity3D的渲染和逻辑都是在主线程上运作的，这里就涉及到了主线程和子线程对资源抢占冲突导致我们需要做线程锁的问题。&lt;/p&gt;

&lt;p&gt;当主线程与子线程一起工作到某时间点都需要某个内存块或者资源时，就会同时去读取或者写入资源，这就会造成资源读写混乱的情况。因此在所有线程上，在调用有冲突的资源时，都需要做锁的操作，以防止线程们在读取或写入操作时对资源错误的争夺。&lt;/p&gt;

&lt;p&gt;我们拿网络接收数据的线程来举例，接收线程在接收到网络数据后将数据推入到队列里，在push操作上就需要做锁的操作：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
lock(obj)
{
	mQueue.Push(data);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;另一边当主线程在读取网络数据时，当需要推出一个数据时，在推出元素操作上也需要加一个锁的操作。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
Lock(obj)
{
	data = mQueue.Pop();
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;由于两边的线程都需要对队列进行操作，所以每次对线程共享的资源进行操作时，都需要先进行锁确认的操作，以避免线程争夺资源而造成混乱。我们拿在海上航行的运输船来做比喻，每个线程都是一条船，每条船都在各自做着自己的事，直到两条船都要进港口卸货，港口几条卸货的通道，它们不得不排队等待前面的船只卸完才能轮到自己，每次它们(可以认为是线程)都会去查看是否有空出来的航线，如果有则先按下标记说我先占了，其他船只在查看时则会看到已被占用的标记则继续等待。&lt;/p&gt;

&lt;h6 id=&quot;缓冲队列&quot;&gt;缓冲队列&lt;/h6&gt;

&lt;p&gt;在网络收发时，数据会远远不断进行发送和接收，很多时候程序还没处理好当前的数据包，就已经有许许多多的数据包从服务器已经传送到达了客户端。发送数据也是一样，会瞬间积累很多的需要发送的数据包，这些数据包如果没有保存好则无法进行重发甚至丢失，所以我们需要用一个队列来进行存储和缓冲，它就被称为缓冲队列。&lt;/p&gt;

&lt;p&gt;一般我们会让负责接收的子线程把接收好的网络数据包放入接收缓冲队列，再由主线程通过Update轮训去检查接收队列里是否有数据，有的话则一个个取出来处理，没有的话继续轮训等待。&lt;/p&gt;

&lt;p&gt;下面伪代码表达了，主线程每帧检查一下是否有收到信息，检测到就立刻处理，没有的话下一帧继续轮训检测。如果没有信息接收来时，子线程上会阻塞等待直到有消息接收到才会调用接收消息接口，将数据包解析后推入接收队列。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
/////////////////////////////////////////////////////////
//接收线程等待接收数据并推入队列
try
{
	socket.BeginReceive( Receive_Callback );
}
catch (Exception e)
{
    Log(LogerType.ERROR, e.StackTrace);
    DisConnect();
}

void Receive_Callback(IAsyncResult  _result)
{
	PushNetworkData(_result); //将数据推入队列
	Receive();	//继续接收数据消息
}

///////////////////////////////////////////////////////////////
//主线程处理数据队列里的数据
void Update()
{
	While( (data = PopNetworkData() )!= null )
	{
		DealNetworkData(data);
	}
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上面的伪代码中，首先有子线程的等待接收数据，接收到数据以后就立即将数据推入队列。另一面，主线程一直在轮询是否有已经接收到的网络数据，如果有就立即逐个处理全部数据。&lt;/p&gt;

&lt;p&gt;其中接收数据时会有些细节在里面，因为数据包并不会按照我们希望的大小发送，所以它或多或少的都会拆分一些数据或者粘粘了其他的数据，导致我们需要识别是否是一个完整的数据包，然后再从中解析出正确的数据包。这部分的数据包格式定义我们把它挪到了后面的章节中，在网络协议格式章节中进行详细的讲解。&lt;/p&gt;

&lt;h6 id=&quot;双队列结构&quot;&gt;双队列结构&lt;/h6&gt;

&lt;p&gt;前面提到的缓冲队列是在多线程编程中常用的手段之一，不过它的效率还不够高，因为多个线程的锁的效率影响会被锁点卡住导致其他线程无法继续工作。双队列数据结构就能很好解决这个问题，它能增强多线程中队列的读写效率。&lt;/p&gt;

&lt;h6 id=&quot;双队列是一种高效的内存数据结构在多线程编程中能保证生产者线程的写入和消费者的读出尽量做到最低的影响避免了共享队列的锁开销&quot;&gt;双队列是一种高效的内存数据结构，在多线程编程中能保证生产者线程的写入和消费者的读出尽量做到最低的影响，避免了共享队列的锁开销。&lt;/h6&gt;

&lt;p&gt;在大多数多线程工作时都需要对缓冲队列读写，其中接收数据的网络线程会将数据写入队列，而处理数据的主线程则会读取队列头部并删除(即我们所说的弹出)，两者都会读写队列导致资源争夺，因此通常会增加锁机制来规范它们的行为。但是锁机制导致线程会常常处于等待状态，因为占用的线程需要处理些复杂的逻辑导致其他线程需要暂停很久才能继续工作，因此加入了线程锁的机制后，仍然没能很好的解决两个线程顺畅操作一个队列的问题。实际项目中处理响应数据逻辑的主线程需要花很久的时间去处理网络数据使之反应到画面上，这时接收数据的线程因为接收队列被主线程锁住而不能继续自己的工作去接收数据，所以子线程只能等待资源使用完毕后才能使用资源，当这个接收到数据所需要处理的逻辑很多很复杂时，那么子线程就要等很少时间，大大降低了线程了效率。&lt;/p&gt;

&lt;p&gt;用双队列的形式就能让线程处理队列时解放出来，让线程的效率大大增加，使得各线程能够各自处理调用各自的队列处理而不用因为资源锁而等待。双队列与普通的缓冲队列在接收数据包部分的逻辑操作都是一样的，即接收数据线程接收到数据时直接推入接收数据的队列，不一样的地方在当处理数据的线程轮询时，先将接收数据的队列拷贝到处理数据的队列中并清空接收数据的队列，然后主线再对拷贝后的数据队列进行处理，这时子线程无需等待主线程的逻辑处理时间就能够顺利的继续接收数据。这样就解放了两个线程各自工作的冲突时间，即两个队列分别理解为了接收数据队列和处理数据队列，当主线程需要处理数据时，先把接收到的数据队列中的数据置换为处理数据队列上，最后各自继续处理自己的工作因为队列已经分开。&lt;/p&gt;

&lt;p&gt;我把最关键部分提取出来用伪代码描述，如下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
/////////////////////////////////////////////
//子线程中，接收数据线程
void Receive_CallBack(Data _result)
{
	Pushdata(_result);
}

/////////////////////////////////////////
//处理数据的主线程
void SwitchQueue()
{
	lock(obj)
	{
		Swap(receiveQueue, produceQueue);
	}
}

void Update()
{
	SwitchQueue();
	while( (data = PopQueue()) != null )
	{
		Deal_with_network_data(data);
	}
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述伪代码中，首先是对子线程的接收部分描述，当接收到数据包时与普通的接收一样只需将数据推送到接收队列中即可，当主线程需要处理数据时，先切换队列防止对队列占用过多时间，切换完毕后，再对队列中的全部数据进行处理。&lt;/p&gt;

&lt;p&gt;这样一来两个线程在锁上的时间变短了，原本要在处理期间全程上锁导致其他线程无法使用，现在只在切换那一瞬间锁上资源即可，其他时间各线程都能顺畅得各自做自己的工作，这样大大提高了多线程的工作效率。&lt;/p&gt;

&lt;h6 id=&quot;发送数据&quot;&gt;发送数据&lt;/h6&gt;

&lt;p&gt;我们前面说的都是接收时的队列，发送数据时也需要队列来做缓冲。当发送的数据包会很多时，也有可能很短时间内会积累过多数据包导致发送池溢出。如果发送时大多数的数据包都是很小很小的数据包，如果每个数据包都发送一次等待接受后再发送就会导致发送效率过低，发送太慢导致延迟过大。而如果一下子把全部数据都发送的话，发送的数据可能会太大，导致发送效率很差，因为数据包越大越容易发送失败或丢包，TCP就会全盘否定这次发送的内容，并将整个包都重新发送一次，效率极其糟糕。&lt;/p&gt;

&lt;p&gt;因此我们需要自己建立发送缓冲来保证发送的有序和高效，发送队列以及对发送数据的合并就是很好的策略。其具体步骤如下：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	1，每次当你调用发送接口时先把数据包推入发送队列，发送程序就开始轮训是否有需要发送的信息在队列里，有的话就发送，没有的话就继续轮训等待。

	2，发送时合并队列里的一部分数据包，这样可以一次性发送多个数据包以提高效率。

	3，对这种合并操作做个限制，如果因为合并而导致数据包太大，也会导致效率差。发送过程中，只要丢失一个数据就要全盘重新发送，数据包很大的话，发送本来就很缓慢的情况下，又重新整体重新发送，就会使得发送效率大大降低，合并的数据的大小限制在窗口大小的范围内(2的16次字节内)。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们既要合并数据包，又不能让数据包太大，这样才能保证发送的效率比较高。比如我们做个合并后的数据包大小限制不得超过10K。这样每个数据包大小都处于10K以下的大小，除非单个包大于10K就让他单独发送，且每次发送包含了多个数据包，这样发送效率就有了一定的保证。&lt;/p&gt;

&lt;h6 id=&quot;协议数据定义标准&quot;&gt;协议数据定义标准&lt;/h6&gt;

&lt;p&gt;在网络数据传输中协议是比较重要的一个关键点，它是客户端与服务器交流的语言。&lt;/p&gt;

&lt;p&gt;协议简单来说就是客户端和服务器端商讨后达成一个对数据格式的协定，是客户端与服务器进行交流的语言，假如两边都用Json格式的协议来传输数据，两边都能用根据协议的格式来知道对方传达了什么信息，以及我的信息如何传达给对方。这样两边在发送和收到数据时，都能够按照一定的规则识别数据了。&lt;/p&gt;

&lt;p&gt;我们在实现TCP的程序里需要对协议进行商讨，下面讲一下在制定协议过程中的几个关键点：&lt;/p&gt;

&lt;h6 id=&quot;1选择客户端和服务器都能接受的格式&quot;&gt;1.选择客户端和服务器都能接受的格式。&lt;/h6&gt;

&lt;p&gt;并不是所有的格式都适合，我们最好选择前后端都能接受的协议格式是最重要的，因为合作最重要，个人力量和一个协议格式的力量与团体来说都是渺小的。在团队都理解和一致的情况下，再对协议进行精进，选择更好更高效的协议。&lt;/p&gt;

&lt;h6 id=&quot;2数据包体大小最小化&quot;&gt;2.数据包体大小最小化。&lt;/h6&gt;

&lt;p&gt;为了尽可能的减少包体的大小，我们应该选择一些能节省包大小空间的格式，比如google protocol buffer 或者其他变种，具体还是要看团队和项目的情况。也可以对已经确定的协议格式，对其协议包的主体部分使用压缩算法，我不建议只加入压缩算法而不改变协议本身，因为这样会导致对压缩算法过度依赖进而省略了协议本身的浪费空间，比如你用了压缩算法后发现xml或json格式的协议也还过得去就不再更改协议本身了，这样就会导致后期数据量大时数据包变得很大很沉重，传输效率降低。但很多老旧的项目和一些为了加快速度而不去更换更好的改协议的项目情况也时常发生，它们只启用压缩算法而不改变协议本身很多时候也是无奈之举。&lt;/p&gt;

&lt;h6 id=&quot;3要有一定的校验能力&quot;&gt;3.要有一定的校验能力。&lt;/h6&gt;

&lt;p&gt;当数据包体不完整时或者本身包体后面连接着另外的数据包时(即粘包情况)，我们要能识别。很多时候我们在传输数据的时候，收到的并不是一个完整的包体，或者因为网络关系，收到了错误的，甚至被攥改过的数据，我们要有能力去校验他们。因此在数据包完整性上，我们要能有校验能力和识别完整包体范围的能力。&lt;/p&gt;

&lt;h6 id=&quot;我们这里主要聊一下网络数据包的校验能力各种包体的协议格式会在后面章节中详细讲解&quot;&gt;我们这里主要聊一下网络数据包的校验能力，各种包体的协议格式会在后面章节中详细讲解。&lt;/h6&gt;

&lt;p&gt;在接收数据的时候，有时候会是一个不完整的包或者一个包后面跟着另一段不完整的包，我们怎么识别哪里是头部数据，并且数据块是哪些？为了解决这些问题就有了数据格式的意义，通常两端通信的协议数据格式，分为包头和数据块两部分组成，这和我们前面介绍的TCP和UDP的包头数据一样，我们自己定义的格式也需要包头用来作为我们业务层的协议格式。&lt;/p&gt;

&lt;p&gt;通常头部数据由4-8个字节组成，里面通常包含了数据包大小，加密方式，广播方式等数据位。其中比较重要的是数据包大小，一般数据大小为4个字节，这4个字节代表的是数据块得大小size，有了这个数据后面数据块得大小就能知道了。因为每次拿到网络数据的时候我们先取头部规定好的几个字节，这样就知道了后面数据块的大小，接着再读取size大小的数据块，这样就拿到了数据信息，如果接收到的数据块大小部满足size大小，则需要继续等待。&lt;/p&gt;

&lt;p&gt;比如再做的复杂点，把数据块的标示也融入头重，每个标示为一个2字节的正整数，为了确定调用的是哪个逻辑具柄的，我们可以把数据包分成，头、固定标识信息、数据块，三个部分。头部存储包体大小、加密位、广播方式等信息，标识信息则存储例如句柄编号、序列号、特殊命令编号、校验码等的标识信息；数据块则存储具体的数据信息。&lt;/p&gt;

&lt;p&gt;TCP本身有做一些校验的工作，为了防止数据被人为攥改、以及逻辑本身的错误检测，我们有时也需要做额外的校验工作。通常的校验方法有几种：&lt;/p&gt;

&lt;h6 id=&quot;md5校验&quot;&gt;MD5校验。&lt;/h6&gt;

&lt;p&gt;这种校验方式比较直接，将数据块整个用MD5散列函数生成一个校验字符串，将校验字符串保存在数据包中。当服务器收到数据包时，也对整个数据块做同样的MD5操作，将数据块用MD5散列函数生成一个校验字符串，与数据包中的校验字符串进行比较，如果一致，则认为校验通过，否则就认为被人为修改过。&lt;/p&gt;

&lt;p&gt;算法可以用下面的代码表示：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;bool CheckData(byte[] data, string data_md5)
{
	string str_md5 = MD5(data);

	if(str_md5 == data_md5)
	{
		return true;
	}

	return false;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述代码中data和data_md5来自数据包中是由发送方计算的值，收到数据后对数据块进行md5操作并与传过来的data_md5字符串进行比较，如果相同则认为校验一致。&lt;/p&gt;

&lt;h6 id=&quot;奇偶校验&quot;&gt;奇偶校验。&lt;/h6&gt;

&lt;p&gt;奇偶校验与MD5有点类似，只是所用的函数方法不同。对每个数据进行异或赋值成一个变量，将这个变量保存在数据包中。当服务器收到数据包时，也对整个数据块做同样的操作，将数据快中的数据进行异或操作并转换成一个变量，然后将这个变量值与数据包中的校验值进行比较，如果数据一致则认为校验正确，否则则认为数据被人攥改过。&lt;/p&gt;

&lt;p&gt;这种方式校验相对于MD5比较简单快速，但重复性也比较高，这个校验算法如下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;unsigned uCRC=0;//校验初始值
for(int i=0;i&amp;lt;DataLenth;i++)
{
	uCRC^=Data[i];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述代码中，每个Data中的数据都会与前面操作过的数据进行异或，最终得出一个值就是校验值，客户端与服务器都做同样的操作，如果得出的值时相等的则认为是正确的数据。&lt;/p&gt;

&lt;h6 id=&quot;crc循环冗余校验&quot;&gt;CRC循环冗余校验。&lt;/h6&gt;

&lt;p&gt;循环冗余校验是利用除法及余数的原理来进行错误检测的.将接收到的数据组进行除法运算，如果能除尽则说明数据校验正确，如果未除尽，则表明数据被认为攥改过。&lt;/p&gt;

&lt;p&gt;该算法步骤如下：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	1，	前后端约定一个除数。

	2，	将数据块用除数取余。

	3，	将余数保存在数据包中。

	4，	服务器收到数据后，将余数和数据块相加，并进行取余操作。

	5，	余数为0则认为校验正确，否则则认为数据被攥改过。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在数据数组中，对每4个字节的数据合并后除余，得到一个1个字节以内的余数，每4个字节得到1个字节的余数，最终得到一组余数数组。校验时反向操作，先取4个字节的数据组成一个正整数加上对应的余数，再除余操作，如果不为零则校验失败，如果全部为零则校验成功。&lt;/p&gt;

&lt;h6 id=&quot;4加密&quot;&gt;4.加密。&lt;/h6&gt;

&lt;p&gt;为了保证网络数据包不被篡改和查看，导致外挂破坏整个游戏平衡，我们需要对发送的网络数据包中的主体部分进行加密。加密算法很多，包括RSA，公钥私钥，以及非对称加密等，其中最简单也是最快的加密方式就是对数据做异或处理，由于数据两次异或处理就能使得数据回到原形，所以算法中常使用异或的操作来做加密。通常做法是发送时对数据做异或处理一次，收到时再做一次异或处理，这样就能简单快速加密解密数据。&lt;/p&gt;

&lt;p&gt;前面说的这种方式密钥Key是同一个，通常大多数加密都使用秘钥的概念，秘钥的Key常常会暴露在外界导致一些不怀好意的人会在客户端上破解并查看秘钥从而知道网络数据协议的格式进而可以进行一些捣乱，所以前后端同使用一个秘钥Key会比较危险，于是非对称加密是加密会比较安全，这样前后端两边的密钥Key不同且各自保存，即使当前端密钥泄漏了也可以随时替换。但仍然无法避免前端的秘钥暴露在外面被人破解，于是如何隐藏这个秘钥键值成了重要关键，很多人写入代码中编译进程序里，如果是C#代码由于它是先翻译为IL语言的，因此很容易用IL翻译的方式反向得出代码内容，也有人把秘钥用c或c++编译放入额外的so文件中，这种确实加大了破解的难度但也不是没有办法破解。所以我们仍然需要不断加大破解的门槛，比如把秘钥分为几段分别用几种方式隐藏在项目文件中，用多种加密方式对秘钥加密，让关键秘钥在获取前再做加密等等，我们应该详尽办法用各种手段达到通常人能想到的解密思路，加大了破解的门槛最好能让黑客望而生畏。&lt;/p&gt;

&lt;p&gt;最后还是要关注下加密导致的性能损耗问题，如果加密的性能损耗过大那就得不偿失了，所以我们仍然希望加密的过程是快速的，在不损耗大量CPU前提下，不影响项目性能的情况下对协议数据做最大化的加密工作。&lt;/p&gt;

&lt;h6 id=&quot;断线检测&quot;&gt;断线检测&lt;/h6&gt;

&lt;p&gt;TCP本身就是强连接，所以自身就有断线的检测机制，但是它本身的检测机制还不够好，时常会因为网络问题导致断线的判断不够及时，所以我们在编写TCP长连接的程序时需要加强断线检测机制，让断线判断变的更加准确及时。&lt;/p&gt;

&lt;p&gt;为了能有效检测TCP连接是否正常，我们需要服务器和客户端共同达成一个协议来检测连接，我们把这个共同达成的协议取名叫心跳包协议。在心跳包协议中，每几秒服务器向客户端发送一个心跳包，包内包含了服务器时间、服务器状态等少量信息，然后由接收到这个心跳协议的客户端做反馈，发送给服务器一个心跳回应包，包内也包含客户端的少量信息例如客户端状态、用户信息等。两边的终端上的逻辑可以就此达成共识，认为当收到心跳信息时认为连接时存在的，当服务器30秒没有收到任何反馈心跳包的信息则认为客户端已经断线，这时主动断开客户端的连接。客户端这边也是同样的协定，当客户端30秒内没有接收到任何数据包时则认为网络已经断开，客户端最好主动退出游戏重新登陆重新连接服务器。&lt;/p&gt;

&lt;p&gt;通常当网络异常时，客户端和服务器都很难断定连接是否依然存在，因此需要用这种机制来加以判定，例如在ios中APP可以随时切出屏幕并不关闭游戏，或者直接关闭应用不给服务器任何解释，服务器自然收不到断开连接的请求。面对各种异常的情况，我们制定的心跳包和心跳回应来判定是否仍处于连接的状态，倘若没有收到心跳包和心跳回应包，就表示连接存在问题了，有可能已经断开。为了规避一些时候网络的波动，我们可以设置一个有效判断断开连接的时间间隔，比如，10秒内没有收到心跳包和心跳回应包，就表示连接已经断开，这时服务器和客户端主动断开连接，客户端可以根据游戏的逻辑先退出游戏再重新登陆寻求再次与服务器连接。&lt;/p&gt;

&lt;p&gt;心跳协议在TCP之上，加强了断线检测的准确性，能更有效快速的检测到断线问题。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第六章，网络层(一) - TCP还是UDP</title>
   <link href="http://www.luzexi.com/2018/08/06/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-%E7%BD%91%E7%BB%9C%E5%B1%821"/>
   <updated>2018-08-06T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/08/06/Unity3D高级编程之进阶主程-网络层1</id>
   <content type="html">&lt;h3 id=&quot;tcp和udp的简介&quot;&gt;TCP和UDP的简介&lt;/h3&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;我们日常使用的网络协议通常只有TCP和UDP两种，但事实上还有其他类型的协议，它们使用的范围比较特殊，TCP和UDP是比较通用网络协议使用范围比较广，下面我们来着重介绍TCP和UDP。&lt;/p&gt;

&lt;h6 id=&quot;tcptransmission-control-protocol传输控制协议是面向连接的协议也就是说在收发数据前必须和对方建立可靠的连接&quot;&gt;TCP（Transmission Control Protocol，传输控制协议）是面向连接的协议，也就是说在收发数据前，必须和对方建立可靠的连接。&lt;/h6&gt;

&lt;p&gt;一个TCP连接必须要经过三次“对话”才能建立起来，这里我们描述下这三次对话的形象过程：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    主机A向主机B发出连接请求数据包：“我想给你发数据，可以吗？”，这是第一次对话；

    主机B向主机A发送同意连接并要求同步的数据包（同步就是两台主机一个在发送，一个在接收的协调工作）：“可以，你什么时候发？”，这是第二次对话；

    主机A再发出一个数据包确认主机B的要求同步：“我现在就发，你接着吧！”，这是第三次对话。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;三次“对话”的目的是使数据包的发送和接收同步，经过三次“对话”之后，主机A才向主机B正式发送数据。&lt;/p&gt;

&lt;p&gt;TCP建立连接要进行3次握手,而断开连接却要进行4次，我们来看看它是怎样的一个过程：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    1，当主机A完成数据传输后,将控制位FIN置1，提出停止TCP连接的请求

    2，主机B收到FIN位置上的1信息后对其作出响应，确认这一方向上的TCP连接将关闭，将ACK置1

    3，B主机再提出反方向的关闭请求，并将控制位FIN置1，发送给A主机，并关闭连接

    4，主机A对主机B的请求进行确认，将ACK置1，并关闭连接，至此双方向的关闭结束.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;由TCP的三次握手和四次断开可以看出，TCP使用面向连接的通信方式，大大提高了数据通信的可靠性，使发送数据端和接收端在数据正式传输前就有了交互，为数据正式传输打下了可靠的基础。&lt;/p&gt;

&lt;p&gt;名词解释：
        ACK  TCP报头的控制位之一,对数据进行确认.确认由目的端发出,用它来告诉发送端这个序列号之前的数据段都收到了。比如,确认号为X,则表示前X-1个数据段都收到了,只有当ACK=1时,确认号才有效,当ACK=0时,确认号无效,这时会要求重传数据,保证数据的完整性.&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    SYN  同步序列号,TCP建立连接时将这个位置1
    
    FIN  发送端完成发送任务位,当TCP完成数据传输需要断开时,提出断开连接的一方将这位置1
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们来看看TCP的包头结构：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    源端口(source port) 16位

    目标端口(target port) 16位

    序列号(SYN) 32位

    回应序号(ACK) 32位

    TCP头长度(head size) 4位

    reserved 6位

    控制代码 6位

    窗口大小(size) 16位

    偏移量 16位

    校验和 16位

    选项  32位(可选)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这样我们把它们需要的空间位数都加起来得出了TCP包头的最小长度总共为：192-32位，最后32位的选项位可没有，所以最小长度为 160/8=20 字节。&lt;/p&gt;

&lt;p&gt;上面描述的只是包头，也就是所有TCP数据包收到时的头部数据格式，头部数据后面跟着的才是真正的数据，后面具体跟着多少空间大小的数据由窗口大小(size)位置上的数据决定，也就是单个数据包最大能承受2^16-1=65535字节的容量。&lt;/p&gt;

&lt;p&gt;这里有个有趣的概念，即TCP通过滑动窗口的概念来进行流量控制。由于在发送端发送数据的速度很快而接收端接收速度却很慢的时就很难保证数据不丢失，所以需要进行流量控制， 协调好通信双方的工作节奏。所谓滑动窗口概念，可以理解成接收端所能提供的缓冲区大小是有限的且是变化的。TCP利用一个滑动的窗口值来告诉发送端对它所发送的数据能提供多大的缓 冲区，以此来协调控制两边的传送节奏和速率。由于窗口只有16个比特的大小，所以接收端TCP 能最大提供65535个字节的缓冲。&lt;/p&gt;

&lt;h6 id=&quot;udpuser-data-protocol用户数据报协议&quot;&gt;UDP（User Data Protocol，用户数据报协议）&lt;/h6&gt;

&lt;p&gt;下面我们来介绍下UDP，它最大的特点可以分6部分：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    （1）UDP是一个非连接的协议，传输数据之前源端和终端不建立连接，当它想传送时就简单地去抓取来自应用程序的数据，并尽可能快地把它扔到网络上。在发送端，UDP传送数据的速度仅仅是受应用程序生成数据的速度、计算机的能力和传输带宽的限制；在接收端，UDP把每个消息段放在队列中，应用程序每次从队列中读一个消息段。

    （2）由于传输数据不建立连接，因此也就不需要维护连接状态，包括收发状态等，因此一台服务机可同时向多个客户机传输相同的消息。

    （3）UDP信息包的包头很短，只有8个字节相对于TCP的20个字节包头信息，UDP的包头开销很小。

    （4）吞吐量不受拥挤控制算法的调节，只受应用软件生成数据的速率、传输带宽、源端和终端主机性能的限制。

    （5）UDP会尽最大努力去传输和接受数据且没有限制但并不保证可靠的数据交付，主机也不需要维持复杂的链接状态表（这里面有许多参数）。

    （6）UDP是面向报文的。发送方的UDP对应用程序传过来的报文，在添加包头后就向下交付给IP层。既不拆分，也不合并，而只是保留这些报文的边界，因此应用程序需要自己限制合适的报文大小，以免报文太大丢失率高。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们经常使用“ping”命令来测试两台主机之间TCP/IP通信是否正常，其实“ping”命令的原理就是向对方主机发送UDP数据包，然后对方主机确认收到数据包，如果数据包到达的消息及时反馈回来，那么网络就是通的，并且可以通过返回回来的数据包计算响应时间。&lt;/p&gt;

&lt;p&gt;其中UDP的包头结构为：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    源端口 16位

    目的端口 16位

    长度 16位

    校验和 16位
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;包头总共64位8个字节就足以。与TCP同样包头只是数据的头部，其真正数据是跟随在包头后面，具体长度由长度这个字段来决定，最大为2^16-1=65535字节的容量。&lt;/p&gt;

&lt;h3 id=&quot;用tcp还是用udp&quot;&gt;用TCP，还是用UDP？&lt;/h3&gt;

&lt;p&gt;前面介绍了TCP和UDP，我们来看看TCP与UDP的它们的不同之处：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    1，TCP是基于连接的，UDP则是无连接；
    
    2，对系统资源开销，TCP开销较多，UDP开销少；
    
    3，TCP包头大各类状态多程序结构稍显复杂，UDP包头小没有状态程序结构较简单；

    4，TCP为流模式，UDP为数据报模式，相当于TCP是自来水那样需要管子以便把书举不断流入盆中，而UDP则不需要管子连接两端只要像机关枪一样不停扫射到目的地就可以。

    5，TCP保证数据正确性，UDP可能丢包，TCP保证数据顺序，UDP不保证。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;从原理上，TCP的优势有，简单直接的长连接，可靠的信息传输，能确保数据到达有确认机制，并且数据到达后是有序的，数据包的大小没有限制，不需要自己切分数据包，TCP的底层程序本身就已经帮助我们做了数据包切分。UDP是基于数据包构建，这意味着在某些方面需要我们完全颠覆在TCP下的观念。&lt;/p&gt;

&lt;p&gt;由于UDP只使用一个socket进行通信，不像TCP需要为每一个客户端建立一个socket连接，这虽然是UDP还不错的地方，但是大多数情况下我们需要的仅仅是一些连接的概念罢了，一些基本的数据包先后次序功能，以及传输时的可靠性。可惜的是这些功能UDP都没有办法简单的提供给你，而我们使用TCP却都可以免费得到。这也是人们为什么经常推荐TCP的原因之一，在用TCP的时候你可以不考虑这些问题。&lt;/p&gt;

&lt;p&gt;UDP没有提供所有的解决方法，但这也正是UDP有巨大潜力的地方，它的吞吐量可以巨大但需要我们去控制它丢失率和可靠性。在某种意义上来说，TCP对UDP就好比是自动档汽车和手动挡汽车的区别，驾驶者需要掌握的技巧和关注度比自动档要多很多，但在效率上的潜力却是巨大的。&lt;/p&gt;

&lt;p&gt;容易造成巨大的延迟问题是TCP的性质决定的，在发生丢包的时候，会产生巨大的延迟，因为TCP首先会去检测哪些包发生了丢失，然后重发所有丢失的包，直到他们都被接收到。虽然UDP也是有延迟的，但是由于它是在UDP的基础之上建立的通信协议，所以可以通过多种方式来减少延迟，不像TCP，所有的东西都要依赖于TCP协议本身而无法被更改。&lt;/p&gt;

&lt;p&gt;那么为什么魔兽世界采用TCP呢？首先我们需要解释这个问题。这个问题其实是“为什么魔兽世界有的时候1000毫秒以上的延迟还能够运行？”魔兽世界以及其他的一些游戏是怎么处理延迟问题的呢？方法也很简单，他们能够隐藏掉延迟带来的影响。&lt;/p&gt;

&lt;p&gt;我们看一下魔兽世界的战斗就会发现，玩家的攻击指令发送给服务器的操作，一些类似发起攻击动作和释放技能特效就能够在没有收到服务器确认的情况下就直接执行，比如展现冰冻技能的效果就可以在服务器没有返回数据前在客户端就做出来。客户端直接开始进行计算而不等待服务端确认是一种典型的隐藏延迟的技术。这也意味着，我们到底是使用TCP还是UDP取决于我们能否隐藏延迟。&lt;/p&gt;

&lt;p&gt;那么到底是用UDP还是TCP呢？提供如下参考：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    如果是由客户端间歇性的发起无状态的查询，并且偶尔发生延迟是可以容忍，那么使用HTTP/HTTPS吧。

    如果客户端和服务器都可以独立发包，但是偶尔发生延迟可以容忍（比如：在线的纸牌游戏，许多MMO类的游戏），那么使用TCP长连接吧。

    如果客户端和服务器都可以独立发包，而且无法忍受延迟（比如：大多数的多人动作类游戏，以及少部分MMO类游戏），那么使用UDP吧。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;为了保证作者理解的正确性，部分参考来自知乎问答。&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第五章，3D模型与动画(三) - 状态机</title>
   <link href="http://www.luzexi.com/2018/08/06/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-3D%E6%A8%A1%E5%9E%8B%E4%B8%8E%E5%8A%A8%E7%94%BB3"/>
   <updated>2018-08-06T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/08/06/Unity3D高级编程之进阶主程-3D模型与动画3</id>
   <content type="html">&lt;h2 id=&quot;如何用状态机模拟人物行为动作&quot;&gt;如何用状态机模拟人物行为动作。&lt;/h2&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;什么是状态机？状态机有两种，一种是有限状态机，一种是无限状态机。有限状态机运用的地方比较多，无限状态机使用的情况比较少，在编译原理中可能会用到，在游戏算法中很难见到，这节我们主要来讲讲有限状态机的使用。&lt;/p&gt;

&lt;p&gt;有限状态机可以简单描述为，实例本身有很多种状态，实例从一种状态切换到另一种状态的动作就是状态机转换，而转换是有条件的，这个转换条件就是状态机之间的连线。&lt;/p&gt;

&lt;p&gt;打个比方，人有三个状态：健康，感冒，康复中。触发的条件有淋雨（t1），吃药（t2），打针（t3），休息（t4）。状态机的连接图可以是这样：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	健康-（t4休息）-&amp;gt;健康；

	健康-（t1淋雨）-&amp;gt;感冒；

	感冒-（t3打针）-&amp;gt;健康；

	感冒-（t2吃药）-&amp;gt;康复中；

	康复中-（t4休息）-&amp;gt;健康
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;状态在不同的条件下跳转到不同状态中去，每个状态要转移到其他状态都必须有他们之间的连线条件，而且不一定状态与状态之间有连线，因为有可能是不允许转换的，例如‘健康’就不允许转换到‘康复’。&lt;/p&gt;

&lt;p&gt;状态机可归纳为4个要素，即现态、条件、动作、次态。这样的归纳主要是出于对状态机的内在因果关系的考虑。“现态”和“条件”是因，“动作”和“次态”是果。详解如下：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;现态：是指当前所处的状态。

条件：又称为“事件”，当一个条件被满足，将会触发一个动作，或者执行一次状态的迁移。

动作：条件满足后执行的动作。动作执行完毕后，可以迁移到新的状态，也可以仍旧保持原状态。动作不是必需的，当条件满足后，也可以不执行任何动作，直接迁移到新状态。

次态：条件满足后要迁往的新状态。“次态”是相对于“现态”而言的，“次态”一旦被激活，就转变成新的“现态”了。
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;游戏中人物行为动作中使用状态机&quot;&gt;游戏中人物行为动作中使用状态机&lt;/h3&gt;

&lt;p&gt;游戏项目中状态机的关键是事件机制和控制状态的控制类。状态机的数量和作用都会因系统的不同而不同，触发条件也各异的，唯有事件机制和控制类是状态机的不变的功能。&lt;/p&gt;

&lt;p&gt;事件机制使得状态在切换时，在进入状态和退出状态时触发了进入事件和退出事件，这个是状态启动运作和停止运作的关键点。&lt;/p&gt;

&lt;p&gt;当状态在满足转换条件时，在即将退出状态前向当前状态发起退出事件，告诉当前状态机你将停止运行，停止运行前需要处理什么逻辑请赶快处理，等待退出逻辑处理完毕后，再向新状态发起进入事件，告诉新状态你将要开始运作，运作前的有什么逻辑或者准备工作请尽快处理。这样每次状态的切换都能合理的告诉当前状态和将要切换的状态进行事件的调用处理。&lt;/p&gt;

&lt;h6 id=&quot;状态机在游戏项目中哪些地方会使用到呢&quot;&gt;状态机在游戏项目中哪些地方会使用到呢？&lt;/h6&gt;

&lt;p&gt;所有能够构成独立状态的系统或者功能都能使用状态机来表现，我们来举例看看：&lt;/p&gt;

&lt;p&gt;1，场景切换。&lt;/p&gt;

&lt;p&gt;场景是独立的，而且只能有一个场景展示在游戏中，因此场景的切换可以用状态机来表示。例如当前为登录场景，点击登录后切换到游戏场景，这时需要把登录场景的UI销毁，UI的销毁工作是登录场景状态在退出时触发的退出事件中做的事。同样的，在之后进入到游戏场景状态时，要先把游戏场景的UI创建出来，这个操作是游戏场景状态在触发启动事件时要做的事。&lt;/p&gt;

&lt;p&gt;2，人物行为状态切换。&lt;/p&gt;

&lt;p&gt;人物一般只能有一个动作状态，比如攻击状态，比如防守状态，比如死亡状态，又比如人物跑步状态，这些行为都只能用单独的一个状态来表示，但也有人物边跑步边吃东西的时候，这时我们也会有几种方式去实现，比如我们把人物跑步且吃东西另外创建一个新的状态来运行，也可以把跑步状态里加一个吃东西的参数，让跑步这个状态机来运行不一样的跑步动作。&lt;/p&gt;

&lt;p&gt;3，宝箱，机关等具有多动画的元素都可以构成独立的状态。&lt;/p&gt;

&lt;p&gt;可以把宝箱或机关的每个动画都看成一个状态，比如打开状态的宝箱和关闭状态的宝箱，以及打开时的机关状态以及关闭时的机关状态。&lt;/p&gt;

&lt;p&gt;4，	AI。&lt;/p&gt;

&lt;p&gt;用状态机来做AI是比较常见到的方式，每个AI状态都可以看成一个独立运行的状态。比如AI状态中的激怒状态，一般来说怪物在此状态中会不断向周围的敌人发起攻击，如果20秒后恢复到平静状态后就不再攻击则认为AI状态由激怒状态转换到了空闲状态，又比如AI巡逻状态，在某个点周围或者按一定路线进行走动，如果5米内发现敌人就会激活条件转换到AI激怒状态。&lt;/p&gt;

&lt;p&gt;以上都是状态机在游戏项目各个逻辑模块中的运用，下面我们重点来介绍下人物行为的状态机结构，我们先按常见的需求把人物行为动作划分一下：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	1，	休息状态。原地不动，并且重复做一个休息的动画。

	2，	攻击状态。播放攻击动画，并且对目标或前方进行攻击。

	3，	技能状态。技能稍微复杂点，因为每个技能都不一样，所以技能状态里面的逻辑可以由不同的技能类来实现。比如建个技能基类class SkillBase，里面有几个统一的接口，然后子类对基类进行继承后，细化技能的细节。

	4，	防御状态。播放防御动画，当受到攻击时，不切换受伤状态。

	5，	受伤状态。播放受伤动画，完毕后自动进入休息状态。

	6，	行走与跑步状态。播放行走或跑步动画，并根据操作输入移动方向。

	7，	跳跃状态。播放跳跃动画，并上下移动人物进行跳跃，下落时底部受到碰撞就进入休息状态。

	8，	死亡状态。播放死亡动画，并且不再受到任何指令而转入任何状态。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这里我们对每个状态编写一些伪代码来描述状态机在人物行为中的运作，如下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
Class BaseState
{
	Public virtual void OnEnter(){}
	Public virtual void OnExit(){}
	Public virtual void Update(){}
}

Class IdleState
{
	Public override void OnEnter()
	{
		role.playAnimation(“idle”,loop);
	}
}

Class HurtState
{
	Public override void OnEnter()
	{
		role.playAnimation(“hurt”,Once);
	}
	
	Public override void Update()
	{
		If(!Role.IsPlayingAnimation(“hurt”))
		{
			HurtFinish();
		}
	}

	Public override void OnExit()
	{
		GotoIdleState();
	}

}

Class AttackState
{
	Public override void OnEnter()
	{
		Role.playAnimation(“attack”,once);
	}

	Public override void Update()
	{
		If(!Role.IsPlayingAnimation(“attack”))
		{
			AttackStateFinish();
		}
	}

	Public override void OnExit()
	{
		GotoIdleState();
	}
}

Class MoveState
{
	Public override void OnEnter()
	{
		Role.playAnimation(“walk”,loop);
	}

	Public override void Update()
	{
		Move();
	}

	Public override void OnExit()
	{
		GotoIdleState();
	}
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述代码表达了对状态机中的状态的描述，其中空闲状态，在没有收到任何指令时只是循环播放Idle动画其他什么都不做。攻击状态，进入状态时播放攻击动画，并在动画结束后返回到Idle状态并等待指令。移动状态在进入时播放walk动画并移动，当移动行为结束时就退出，在退出时进入空闲状态。&lt;/p&gt;

&lt;p&gt;状态机中除了事件接口对状态起了关键的作用外，控制状态的控制类也是关键，它其实就是状态的管理类，用于管理状态的入口和出口。&lt;/p&gt;

&lt;p&gt;我们也同样用伪代码的形式来描述状态管理类是如何运作的，以下是伪代码：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
class RoleStateController
{
	private IdleState idleState;
	private MoveState moveState;
	private AttackState attackState;
	private HurtState hurtState;

	private BaseState currentState;
	
	public void OnHurt()
	{
		ReduceHP();
		If(currentState != hurtState)
		{
			ChangeToHurtState();
		}
	}

	public void InputAttack()
	{
		If(currentState  == hurtState) return;	//受伤状态下不可攻击
		If(currentState == attackState) return;	//攻击状态还没结束时不可重新开始攻击
		ChangeToAttackState();
	}

	Public void InputMove(){}
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在状态机控制类中，存储了各个状态，并且提供了输入的接口，这种输入的事件是由状态机外部提供的，比如发起攻击，比如受到伤害，比如向前向后移动等等，在外部需要状态机触发状态改变时，发起了对状态机的输入事件。这就是状态机控制类需要做的事，简单来说就是存储状态，并提供输入事件接口。&lt;/p&gt;

&lt;p&gt;更大规模的状态机可能还需要稍微改进下，比如业务逻辑都会转移到状态内去做，状态之间的转换则更为简单。如果状态很多则需要用数组的形式来存储，因为那样才能时内存连续提高CPU内存读取效率。&lt;/p&gt;

&lt;p&gt;有了状态机的控制类就有了对状态机的管理，就可以对各种逻辑的控制，比如硬值，受伤中不可行走和攻击，攻击中不可移动等，这些当状态在切换时的条件限制，是阻挡还是通过，该切换什么状态，都可以由控制类来决定的，它是状态机系统的大脑。&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>被封纪念帖</title>
   <link href="http://www.luzexi.com/2018/08/03/%E8%A2%AB%E5%B0%81%E7%BA%AA%E5%BF%B5%E5%B8%96"/>
   <updated>2018-08-03T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/08/03/被封纪念帖</id>
   <content type="html">&lt;p&gt;www.luzexi.com 博客 被关小黑屋了，原因是自己架了梯子，还写文章教别人怎么架梯子。被冠以双重罪，直接封服务器了，不仅仅是域名，服务器也不给用了。&lt;/p&gt;

&lt;p&gt;这是不给程序员活路么，不学习外来技术怎么振兴中华。&lt;/p&gt;

&lt;p&gt;但是我想通了，生活在中国就要按照中国的规矩办事，政治觉悟要有。&lt;/p&gt;

&lt;p&gt;被封让我彻底放弃了阿里云，也放弃了国外的服务器，转而用国内，也同时放弃了自建某些服务器，感觉也轻松了很多，并且又多了一个域名，luzexi.cn 因为原来的域名不知道什么时候恢复，暂时用这个吧。&lt;/p&gt;

&lt;p&gt;我想明白了在中国还是需要按中国的套路走，如果你移民在国外也一样，要拼劲全力融入当地的风俗民习，都不容易。&lt;/p&gt;

&lt;p&gt;所以那些在国外的说国内不好的，或者在国内说国外不好的，都是井底之蛙，过于偏见纠结问题的表面。&lt;/p&gt;

&lt;p&gt;做好自己，在规矩下办事，在规则下取胜才是关键，不要去秀下限，而是要努力去突破上限。&lt;/p&gt;

&lt;p&gt;放个连接吧，总不至于白写。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/luzexi/blog/blob/master/luzexi.com/hyde-master/_posts/2018-08-01-%E8%A2%AB%E5%B0%81%E7%9A%84%E5%B8%96%E5%AD%90.md&quot;&gt;被封的说明书&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>git同步多个repository仓库</title>
   <link href="http://www.luzexi.com/2018/08/03/git%E5%90%8C%E6%AD%A5%E5%A4%9A%E4%B8%AArepository%E4%BB%93%E5%BA%93"/>
   <updated>2018-08-03T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/08/03/git同步多个repository仓库</id>
   <content type="html">&lt;p&gt;由于国内服务器访问GitHub奇慢，所以把仓库复制一份放在了国内。但是苦于要同步两边，所以想有没有办法，同步多个git仓库，是否有办法，只维护一份就可以了。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;网上搜了一下，大都说的是&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	git remote add 

	和 git remote set-url --add ，

	以及 git push origin --all 的用法。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;但是自己用了一下，完全用不了。总是拒绝push，说需要先pull，但pull了又说需要merge，我感觉这个坑巨大，于是想找其他法子。自己想了想最土的办法或许可以。试验了下，果然可行。步骤如下：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	1， 两个文件夹，分别装两个仓库，或者多个文件夹装多个仓库，每个不同的仓库一个文件夹。

	2， 把其中一个作为主要维护的仓库，维护完毕后，执行以下步骤。

	3， 写一个shell程序。首先把所有内容全部都复制粘贴到其他仓库去，直接替换掉旧的文件。然后每个仓库一个个的执行

		git add .

		git commit -m 'sync'

		git push origin master

		每个仓库都执行完毕后，同步完成。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;也就是，用shell程序把人工手动需要做的事情，让程序去完成，省时省力，一键搞定。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;h6 id=&quot;举例代码为同步三个仓库具体的-shell-如下&quot;&gt;举例代码为同步三个仓库，具体的 shell 如下：&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	cp -r ../* /Users/luzexi/Desktop/work/gitee/blog/
	cd /Users/luzexi/Desktop/work/gitee/blog/
	git add .
	git commit -m 'sync'
	git push origin master

	cp -r ../* /Users/luzexi/Desktop/work/github/blog/
	cd /Users/luzexi/Desktop/work/github/blog/
	git add .
	git commit -m 'sync'
	git push origin master

	cp -r ../* /Users/luzexi/Desktop/work/gitlab/blog/
	cd /Users/luzexi/Desktop/work/gitlab/blog/
	git add .
	git commit -m 'sync'
	git push origin master
&lt;/code&gt;&lt;/pre&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第五章，3D模型与动画(二) - 合并3D模型</title>
   <link href="http://www.luzexi.com/2018/08/03/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-3D%E6%A8%A1%E5%9E%8B%E4%B8%8E%E5%8A%A8%E7%94%BB2"/>
   <updated>2018-08-03T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/08/03/Unity3D高级编程之进阶主程-3D模型与动画2</id>
   <content type="html">&lt;h3 id=&quot;animation-和-animator的选择&quot;&gt;Animation 和 Animator的选择。&lt;/h3&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;首先说明Unity3D引擎已经不再对Animation动画系统进行维护。但不维护也并不是说一定不能用，很多旧的项目任然在用，只是在老版本中使用。新动画系统 Mecanim 中有了新的动画组件 Animator，为什么要用新系统 Mecanim 呢？&lt;/p&gt;

&lt;p&gt;原因如下几个方面：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Mecanim 系统使用多线程计算，比Animation的单线程性能要高出一点。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Unity3D本身就自带对 Mecanim 系统的优化选项“Optimize GameObject”。开启该选项，Animator.Update和MeshSkinning.Update的CPU占用均会一定程度的降低。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Animator的功能更加多，Retargeting功能让不同角色使用同一套动画资源，比如游戏中的角色的空闲动画，就可以使用同一个动画文件省去了动画资源内存的开销。Animator状态机的接入，让动画可以在不同的条件下可以自动的切换。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Animation因为Unity3D引擎不再维护了，大多数人选择Animator这是正确的做法。其实就我而言我更喜欢Animation，但现实是也不得不抛弃它去投奔新‘主子’(Animator)，因为不再维护意味着从长远来看它会越来越糟糕。&lt;/p&gt;

&lt;h3 id=&quot;unity3d-3d模型中submesh的意义&quot;&gt;Unity3D 3D模型中SubMesh的意义&lt;/h3&gt;

&lt;p&gt;在模型中可以有很多网格，一个模型可以由很多个网格构成。因此在Unity3D中一个Mesh网格的构成可以由多个子Mesh组成也就是SubMesh，即一个Mesh里可以有多个SubMesh。&lt;/p&gt;

&lt;p&gt;引擎在渲染的时候，每个SubMesh都需要对应一个Material材质球来匹配做渲染，说白了一个SubMesh本身就是普通的模型有很多个三角形构成它也需要材质球支持以达成渲染。在美术人员制作3D模型过程中，可以将SubMesh拆分成独立的Mesh，也可以并成多个子模型即SubMesh。&lt;/p&gt;

&lt;p&gt;这里可能大家有个疑问，为什么美术人员在制作3D模型时不把网格都编成一个而要制作成多个SubMesh？这是有原因：&lt;/p&gt;

&lt;p&gt;一种情况是，3D模型制作人员在制作模型的时候，希望一个模型中一部分Mesh用一种材质球来表现效果，另一部分Mesh则用另一种材质球来表现效果，这时就需要将模型拆分开来。因为一个Mesh只能对应一个材质球做渲染，一个材质球只能表现一种效果，当他们需要表现两种完全不同的效果时就需要拆分。&lt;/p&gt;

&lt;p&gt;第二种情况是，模型中的某部分的贴图，在众多模型中共同使用的频率比较高，为了不重复制作以及减少重复劳动，那么就会让原本可以整体的模型单独拆分出来一部分公共材质的部分让它们都使用同一个材质球。&lt;/p&gt;

&lt;p&gt;第三种情况是，在制作动画时，由于动画过于复杂导致如果使用同一个模型去表现的话，骨骼数量就会成倍增加。为了能更好的表现动画，也为了能更节省骨骼的使用量，拆分出一部分模型让他们单独成为模型动画的一部分。&lt;/p&gt;

&lt;p&gt;以上三种情况都是我们在制作模型过程中需要着重考虑的问题，通常情况下都会用拆分模型的方式来解决这些问题。&lt;/p&gt;

&lt;p&gt;其实SubMesh有诸多好处，与没有SubMesh的Mesh相比，拥有多个SubMesh一样可以有动画，另外它还能针对不同部分的Mesh选择有个性化的材质球来表现效果，从功能上来看比单个Mesh要灵活的多。但它也有些许缺点，由于每个SubMesh都多出了材质球，导致SubMesh越多，增加的Drawcall也越多。Mesh中存在多个SubMesh，在动作和拆分材质球渲染上确实有很好的优势，但无法与其他Mesh合并，导致优化的一个重要环节被阻断。&lt;/p&gt;

&lt;p&gt;SubMesh虽然功能很强大，但对性能的开销也需要注意，需要我们慎重使用。有时我们也可以选择用完全拆分Mesh为其他Mesh的形式来代替SubMesh，这样在合并Mesh时就有更多的选择了。下面我们就来深入浅出的聊聊合并模型的方法和途径。&lt;/p&gt;

&lt;h3 id=&quot;动态合并3d模型&quot;&gt;动态合并3D模型。&lt;/h3&gt;

&lt;p&gt;我们制作的场景中的3D的物体很多，每个3D物体都需要有一个材质球支持，导致每个模型都会产生一个Drawcall（渲染管线的调用），众多的3D模型会产生很多Drawcall，CPU在等待渲染GPU在忙于处理Drawcall，使得帧率下降画面卡顿感强烈。&lt;/p&gt;

&lt;p&gt;实际中的项目都会遇到这样的问题，场景中要摆放的3D物体很多，包括人物，建筑，路标，景观，树木，石头，碎块，花朵等。这些3D物体都有自己的材质球，相同模型的物体使用相同的材质球，不一样的物体使用不同的材质球，有时不一样的物体也有相同的材质球。如果不做任何优化处理就会产生很多Drawcall，导致帧率下降。于是我们就会想这么多的材质球引起这么多Drawcall，是否能合并合并成一个，这就是合并3D模型发挥作用的时候。&lt;/p&gt;

&lt;p&gt;合并3D模型主要的目的就是为了减少Drawcall，它是通过减少材质球的提交数量来完成优化手段的，说的简单点就是把拥有相同材质球的模型合并起来成为一个模型和一个材质球，从而减少向GPU提交的Drawcall数量。&lt;/p&gt;

&lt;p&gt;Unity3D引擎在合并模型从而优化Drawcall上有自己的功能，即 动态批处理 和 静态批处理 两种，它们的前提条件都是模型物体必须是相同材质球的模型，除了这个必要条件外还有其他条件也需要符合。下面我们就来介绍下Unity3D中动态批处理和静态批处理：&lt;/p&gt;

&lt;h6 id=&quot;动态批处理&quot;&gt;动态批处理&lt;/h6&gt;

&lt;p&gt;动态批处理即意味着随时都在做的模型合并批量处理，当我们把 Dynamic Batch 动态批处理开启时，Unity3D可以自动批处理场景中某些物体成为同一个Drawcall，如果是他们使用的是同一个材质球并且满足一些条件的话动态批处理会自动完成的，我们不需要增加额外的操作。&lt;/p&gt;

&lt;p&gt;其中需要满足的动态批处理的条件是，&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;1，动态批处理的物体的顶点数目要在一定范围之内，动态批处理只能应用在少于900个顶点的Mesh中。

	如果你的Shader使用顶点坐标，法线，单独的UV，那么只能动态批处理300个顶点内的网格，

	如果你的Shader使用顶点坐标，法线，UV0，UV1和切线，则只能有180个顶点了。

2，两个物体的缩放比例一定是相同，假如两个物体不在同一个缩放单位上，它们将不会进行动态批处理（例如物体A的缩放比例是(1,1,1)，物体B的缩放比例是是(1,1,2)，他们的缩放比例不同则不会被合并处理，除非A的缩放比例改为(1,1,2)，或者B的缩放比例改为(1,1,1)）

3，使用相同的材质球的模型才会被合并，使用不同的材质球是不会被动态批处理的，即使他们模型是同一个或者看起来像是同一个。

4，多管线(Pipeline)Shader会中断动态批处理。

	很多Unity3D里的Shader支持多个灯光的前置渲染增加了多个渲染通道，这些多个通道的材质球是无法用于动态批处理渲染的。

	Legacy Deferred(灯光前置通道)传统延迟渲染路径已经被动态处理关闭，因为它必须绘制物体两次。

	所有多个pass的Shader增加了渲染管道，不会被动态批处理。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;动态批处理的条件是很苛刻的，在项目中很多模型是不符合动态批处理的。另外动态批处理要消耗CPU转换所有物体的顶点到世界空间的操作，所以它唯一的优势是如果它的工作能让Drawcall变少。&lt;/p&gt;

&lt;p&gt;最后我们需要理解一味的减少Drawcall不是万能，它的资源需求取决于很多因素，主要被图形API使用。例如一个控制台或流行的API像Apple Metal这样的，Drawcall的开销会普遍很低，因此动态批处理时常在优化方面的优势并不是很大。&lt;/p&gt;

&lt;h6 id=&quot;静态批处理&quot;&gt;静态批处理&lt;/h6&gt;

&lt;p&gt;静态批处理允许引擎在离线的情况下去做模型合并的批处理以降低Drawcall，无论模型多大只要使用同一个材质球都会被静态批处理优化。他通常比动态批处理有用（因为它不需要实时转换顶点来消耗CPU），但也消耗了更多的内存。&lt;/p&gt;

&lt;p&gt;为了让静态批处理起作用，我们需要将物体置为静态不同的，即我们需要去确认指定的物体是否是静态的不能动，不能移动、不能旋转或者缩放。因此我们需要给这物体在面板上标记一个静态的标记以确定性的告诉Unity3D引擎，此物体是不能动不能缩放的，可以对该物体做静态批处理的预处理。&lt;/p&gt;

&lt;p&gt;使用静态批处理需要增加额外的内存来存储合并的模型。在静态批处理下如果一些物体在静态批处理前共用一个模型，那么Unity3D会复制每个物体的模型以用来合并，在Editor里或者在实时运行状态下都会做这个操作。这可能不总是有益的，因为这样做会带来大量的内存增加，因此有时我们需要减少对物体的静态处理来减少内存的使用量，虽然这样做会牺牲了渲染性能，不过我觉得内存换CPU是值得的，但是如果100兆的内存来换1%的CPu效率任然是不划算的，所以我们还是应该谨慎。&lt;/p&gt;

&lt;p&gt;静态批处理的具体做法是，将所有静态物体放入世界空间，并且把他们以材质球为分类标准分别合并起来，并构建一个大的顶点集合和索引缓存，所有可见的同类物体就会被同一批的Drawcall处理，这就会让一系列的Drawcall减少从而实现优化的效果。&lt;/p&gt;

&lt;p&gt;技术上来说静态批处理并没有节省3D API Drawcall数量，但他节省了他们之间的状态改变导致的消耗。在大多数平台上，批处理被限制在6万4千个顶点和6万4千个索引(OpenGLES上为48k，macOS上为32k)，所以倘若我们超过这个数量需要取消一些静态批处理对象。&lt;/p&gt;

&lt;p&gt;现在我们知道动态批处理 和 静态批处理是什么了，我们来做个简单总结：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	1，	动态批处理条件是，使用同一材质球，顶点数量不超过900个，有法线的不超过300个顶点，有两个UV的不超过150个顶点，缩放大小要一致，Shader不能有多通道。

	2，	静态批处理条件是，必须是点上静态标记的物体，不能动，不能旋转，不能缩放，不能有动画。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;动态批处理的规则是极其严格的，在具体的场景中能用到的模型是相对简单的，它对顶点限制太紧，而且缩放比例还要相同，渲染管道也只能有一个。&lt;/p&gt;

&lt;p&gt;静态批处理的使用范围更广一些，但要求物体是静态不能移动，旋转，缩放。这个限制太固定，用到的地方只有完全不动的场景中的固定物体。&lt;/p&gt;

&lt;p&gt;动态批处理限制太大，静态批处理又不满足我们的需求，所以有时我们也只能自己手动合并模型来替代Unity3D的批处理。也只有用自己程序合并的模型才能体现自定义动态批处理的用途。比如构建场景后的动态建筑，动态小件合并，人物模型更换装备，发型，首饰，衣裤等导致多个模型挂载的需要合并模型来优化渲染。&lt;/p&gt;

&lt;h3 id=&quot;自己来编写合并3d模型的程序&quot;&gt;自己来编写合并3D模型的程序&lt;/h3&gt;

&lt;p&gt;为了编写自己的合并3D模型程序需要调用些Unity3D的API，我们来了解下Unity3D的几个类和接口：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	Mesh类有个CombineMeshes的接口提供了合并3D模型的入口。

	MeshFilter类，是承载Mesh数据的类。

	MeshRenderer类，是绘制Mesh网格的类。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在使用这几个类前我们首先需要弄明白几个概念：&lt;/p&gt;

&lt;p&gt;1，	SubMesh的意义。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	前文用专门的一节来解释它的意义。这里简单阐述下，SubMesh是Mesh里拆出来的子模型，SubMesh需要额外多个的材质球，而普通的Mesh只有一个材质球。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;2，	MeshFilter 和 MeshRenderer中的 mesh 和 shareMesh ，material 和 shareMaterial 的区别。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	mesh 和 material 都是实例型的变量，对 mesh 和 material 进行任何操作都会额外复制一份后再进行重新赋值，即使只是get操作也同样会发生复制效果。也就是说对 mesh 和 material 进行操作后就会变成另一个实例，虽然看上去一样，但其实已经是同的实例了。

	sharedMesh 和 sharedMaterial 与前面两个变量不同，他们是共享型的。多个3D模型可以共用同一个指定的 sharedMesh 和 sharedMaterial，当你修改sharedMesh或sharedMaterial里面的参数时，多个同是指向同一个 sharedMesh 和 sharedMaterial的模型就会同时改变效果。也就是说 sharedMesh 和 sharedMaterial 被改变后，所有使用sharedMesh 和 sharedMaterial资源的3D模型会有同一个表现效果。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;3，	materials 和 sharedMaterials 的区别。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	与前面 material 和 sharedMaterial 同样的区别， materials 是实例型，sharedMaterials 是共享型，只不过现在他们变成了数组形式。

	materials 只要对它进行任何操作都会复制一份一模一样的来替换，sharedMaterials 操作后所有指向这个材质球的模型都会改变效果。而 materials 和 material，与 sharedMaterials 和 sharedMaterial 的区别是，materials和sharedMaterials可以针对不同的subMesh，而material和sharedMaterial只针对主Mesh。也就是说 material 和 sharedMaterial 等于 materials[0] 和 sharedMaterials[0]。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;4，	Mesh，MeshFilter，MeshRenderer的关系。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	Mesh是数据资源，它可以有自己的资源文件，比如XXX.FBX。Mesh里存储了，顶点，uv，顶点颜色，三角形，切线，法线，骨骼，骨骼权重等提供渲染必要的数据。

	MeshFilter是一个承载Mesh数据的类，Mesh被实例化后存储在MeshFilter，MeshFilter有两种类型即实例型和共享型的变量，mesh和sharedMesh，对mesh的操作将生成新的mesh实例，而对sharedMesh操作将改变与其他模型共同拥有的那个指定的Mesh数据实例。

	MeshRenderer具有渲染功能，它会提取MeshFilter中的Mesh数据，结合自身的materials或者sharedMaterials进行渲染。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;5，	CombineInstance即合并数据实例类。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	合并时我们需要为每个需要合并的 Mesh 创建一个CombineInstance实例并往里面放入，mesh，subMesh的索引，lightmap的缩放和偏移，以及realtimeLightmap的缩放和偏移(如果有的话)，和世界坐标矩阵。CombineInstance承载了所有需要合并的数据，通过将CombineInstance数组传入到合并接口，即通过Mesh.CombineMeshes接口进行合并。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;理清以上概念后，我们在编写合并3D模型程序时难度会降低很多。下面来看下合并3D模型的具体步骤：&lt;/p&gt;

&lt;p&gt;1，建立合并数据数组&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
CombineInstance[] combine = new CombineInstance[mMeshFilter.Count];

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;2，填入合并数据&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
for(int i = 0 ; i&amp;lt; mMeshFilter.Count ; i++)
{
    combine[i].mesh = mMeshFilter[i].sharedMesh;
    combine[i].transform = mMeshFilter.transform.localToWorldMatrix;
    combine[i].subMeshIndex = i; //标识Material的索引位置，可以为0，1，2等
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;3，合并所有Mesh为单独一个&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
new_meshFilter.sharedMesh.CombineMeshes(combine);

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;或者，合并后保留SubMesh&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
		new_meshFilter.sharedMesh.CombineMeshes(combine,false);

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;4，CombineMeshes接口定义为&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
public void CombineMeshes(CombineInstance[] combine, bool mergeSubMeshes = true, bool useMatrices = true, bool hasLightmapData = false);

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;完整代码为：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
CombineInstance[] combine = new CombineInstance[mMeshFilter.Count];

for(int i = 0 ; i&amp;lt; mMeshFilter.Count ; i++)
{
    combine[i].mesh = mMeshFilter[i].sharedMesh;
    combine[i].transform = mMeshFilter.transform.localToWorldMatrix;
    combine[i].subMeshIndex = i;//标识Material的索引位置，可以为0，1，2等
}

new_meshFilter.sharedMesh.CombineMeshes(combine);

&lt;/code&gt;&lt;/pre&gt;
</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第五章，3D模型与动画(一) - 美术资源的规范</title>
   <link href="http://www.luzexi.com/2018/08/03/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-3D%E6%A8%A1%E5%9E%8B%E4%B8%8E%E5%8A%A8%E7%94%BB1"/>
   <updated>2018-08-03T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/08/03/Unity3D高级编程之进阶主程-3D模型与动画1</id>
   <content type="html">&lt;h3 id=&quot;3d模型大小面数贴图大小骨骼数量在游戏中的规范&quot;&gt;3D模型大小，面数，贴图大小，骨骼数量在游戏中的规范。&lt;/h3&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;资源的规范在项目中是比较重要的，看到过有很多项目都没有重视资源规范，他们奢求高的运行效率，却不懂资源的规范是运行效率的前提。&lt;/p&gt;

&lt;p&gt;有的游戏项目，一个人物模型就有几万个面，一个建筑就有几十万个面，不堪入目。贴图也有很糟糕的，到处都是不规则的贴图蕾丝1024和2048大小的贴图到处都是，骨骼数量有的甚至到了几百。在这样恶劣的资源环境情况下，项目的运行效率怎么可能高。&lt;/p&gt;

&lt;p&gt;资源的优劣直接导致了项目的性能好坏，模型太大，面数太多，贴图太大，会导致包体过大，骨骼数量太多导致CPU在动画上消耗过多。资源过大过重，还会导致CPU消耗在资源加载上面的时间过多过长，导致画面帧率下降，卡顿严重等问题。假如这个游戏项目完成打包后，在这么劣质的性能品质下发布出去，基本上投资人的钱就打水漂了。&lt;/p&gt;

&lt;p&gt;对于美术资源来说，模型面数不是越多越好，越细越好，而是在一定数量的限制下，我们尽最大努力做到最大限度的美化。同样贴图也不是越大越好，精度越高越好，而是在一定大小的限制下，做到最大限度的不失真。资源规范能有效的限制美术人员在制作美术资源时无度的扩张。&lt;/p&gt;

&lt;h6 id=&quot;如何规范3d美术资源&quot;&gt;如何规范3D美术资源？&lt;/h6&gt;

&lt;p&gt;模型的大小一般都会按正常的尺寸进行制作，通常都规定1个单位空间就是1米，也有例外的时候，不过也是按这个放大缩小的，比如1个单位空间为1厘米，或10米等。&lt;/p&gt;

&lt;h6 id=&quot;如何确定美术资源规范的大小&quot;&gt;如何确定美术资源规范的大小。&lt;/h6&gt;

&lt;h3 id=&quot;1-根据运用的场景而不同&quot;&gt;1，	根据运用的场景而不同。&lt;/h3&gt;

&lt;p&gt;像汤姆猫这种强调单一主角的场景，主要资源全部投入到主角一个人的，对主角进行精细化的雕琢是很有必要的。&lt;/p&gt;

&lt;p&gt;这种场景的人物模型做到1万面也不为过，主角的骨骼也可以做得很精细，骨骼数量可以达到50-100个，贴图大小也可以在512 x 512左右，如果需要 1024 x 1024 的贴图我觉得问题也不是很大，只是如果需求中有换装时，需要考虑下包体大小。&lt;/p&gt;

&lt;p&gt;第三人称视角的RPG游戏，由于视角与角色的距离有限，同时看到的场景范围有限，可以使用动态场景地图加载的方式控制内存使用量，主要是同屏单位数量会决定模型面数。如果人模模型面数控制在3000-4000面，骨骼数量控制在30个左右，至于建筑模型面数因为大小差异太多无法统一，不过我们可以用整体面数统计法来规范资源。贴图可以控制在 256 x 256 及以下，比如主贴图为 256 x 256，副贴图为 128 x 128。&lt;/p&gt;

&lt;p&gt;总的来说美术资源规范就是限制模型的面数，限制贴图的大小，限制一个材质球的贴图张数，限制一个人物身上的材质球数量，限制骨骼数量，具体数字是多少需要因项目而定，不过这里可以给一个大多数项目使用的标准，3000面左右的模型，不超过256大小的贴图，一个材质球内至多3张贴图，一个人物不超过2个材质球使用量，30个左右的骨骼数量。&lt;/p&gt;

&lt;p&gt;如果做的是《塞尔达传说》，《魔兽世界》这种超大型的游戏场景，可以从高空俯瞰整个场景的这种情况，渲染压力比较大，除了制定美术规范外，我们也需要借用用其他方法比如LOD，它能把渲染压力和渲染质量平衡的很好。后面章节中会具体介绍LOD在优化中的运用。&lt;/p&gt;

&lt;h3 id=&quot;2-使用反推计算来得出规范&quot;&gt;2，	使用反推计算来得出规范。&lt;/h3&gt;

&lt;p&gt;对于一些模型物体大小差异比较大的，无法统一模型与贴图面数的，我们可以用全场景总面数来控制。&lt;/p&gt;

&lt;p&gt;我们可以用这种方式来举个例子，假如我们在场景中同屏面熟控制在6万面左右，然后再开始部署，地形模型总共3万面左右，数量多的小件模型在100面以下平均50面，中型建筑在500-1500面平均下1000面好了，大型建筑在2000-5000面以下平均3000面。这样的情况下，除了地形模型剩下的还有3万面，小件模型可以放100件也就是5千面，中型建筑可以放10件1万面，大型建筑5件，1万面，总共3万面差不多满足了6万面左右的需求。&lt;/p&gt;

&lt;p&gt;贴图大小也可以同样按照这种方法进行规范，假如我们设定总体内存中贴图的大小不得超过100MB，倒推出去平均有多少种小件模型在场景内，平均有多少种中型模型在场景内，平均有多少种大型建筑在场景内。如果小件平均有30种，中型模型有20种，大型模型有10种，那么小件每个贴图不得超过0.3MB，中型模型每个贴图不得超过1MB，大型模型每个贴图不得超过2MB。&lt;/p&gt;

&lt;p&gt;除了占用内存的大小，也可以用总张数和size大小来进行规范。例如我们设定场景的总体贴图大小设置为不得超过 20张 1024 x 1024，那么在小件平均30种，中型模型平均20种，大型模型平均10种的情况下，我们就可以规定为小件贴图大小在128 x 128以下，中型模型贴图大小在256 x 256以下，大型模型贴图大小在 512 x 512 以下。&lt;/p&gt;

&lt;p&gt;用反推的计算方法来计算和规范，一整个地形场景的模型和贴图的规范就会相对容易些，对于整体的内存和计算量的把控会加强很多。&lt;/p&gt;

&lt;h3 id=&quot;3-规范的自动检测&quot;&gt;3，	规范的自动检测&lt;/h3&gt;

&lt;p&gt;无论什么方法，都敌不过实际测试。如果可以在项目前加入实际的压力测试环节，或者在项目进行中加入渲染压力测试的环境，会更有利于对美术资源的规范，专门有人做渲染压力的实际测试是有必要的。&lt;/p&gt;

&lt;p&gt;专门派遣一个人来完成这件事情，做好前期的计划和测试是对项目负责的做法，是对项目有更好前程的安全保障。但仍然有许多项目和公司，由于人才缺乏成本高企等问题下，没有派人去做各种渲染测试，只是一味的求快求速度是有问题的。不过现实仍然打败了大多数人和项目，更多时候也是无奈的选择，有时我们只能边做边测，遇到具体问题寻找自己的答案。&lt;/p&gt;

&lt;p&gt;只是人工去寻找美术资源规范问题仍然会有很多遗漏，不能形成系统化的流程与规范，导致大家都是有一枪打一枪，发现就修一个，无法确定是否有遗漏，以及完全也不知道什么时候有人一不小心又提交了不符合的资源。&lt;/p&gt;

&lt;p&gt;因此我们要建立自动规范的检测程序，这些测试程序应该设定为每个2-3小时运行一次，运行后提醒我们有多少资源存在不规范的情况，分别是哪些资源罗列出来，甚至可以细化到是最近一次谁提交的。&lt;/p&gt;

&lt;p&gt;这里列举一个我在前一个能够分享的项目的检测例子:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-C#&quot;&gt;
[MenuItem(&quot;校验工具/角色、模型、地形Prefab&quot;)]
static public void ModelPrefabValidate()
{
	//写入csv日志
    StreamWriter sw = new StreamWriter(&quot;模型Prefab检测报告.csv&quot;, false, System.Text.Encoding.UTF8);

    string[] allAssets = AssetDatabase.GetAllAssetPaths();
	foreach (string s in allAssets)
    {
    	if (UIAssetPost.IsInPath(s, ModelFbxAssetPost.Character_Prefab_path)
    		)
    	{
			GameObject obj = AssetDatabase.LoadAssetAtPath(s, typeof(GameObject)) as GameObject;

            //--检查Fbx，网格，设置
        	MeshFilter[] meshes = obj.GetComponentsInChildren&amp;lt;MeshFilter&amp;gt;();
        	if (meshes != null)
        	{
            	int vertexCount_sum = 0;

            	for(int i = 0 ; i &amp;lt; meshes.Length ; i++)
            	{
            		Mesh mesh = meshes[i].sharedMesh;
            		SkinnedMeshRenderer smr = meshes[i].GetComponent&amp;lt;SkinnedMeshRenderer&amp;gt;();
            		// BatchRenderer br = meshes[i].GetComponent&amp;lt;BatchRenderer&amp;gt;();

            		if(mesh == null)
            		{
            			str_record = string.Format(&quot;丢失Mesh ,{0} ,{1}&quot;, s, meshes[i].name);
            		}
            		else
            		{
	            		ModelImporter model_importer = null;
	            		string path_obj = null;
	            		UnityEngine.Object obj_fbx = null;

            			//检查fbx路径
            			path_obj = AssetDatabase.GetAssetPath(mesh);
            			obj_fbx = AssetDatabase.LoadAssetAtPath(path_obj, typeof(GameObject));

            			//检查fbx设置
            			model_importer = AssetImporter.GetAtPath(path_obj) as ModelImporter;
            			if(!model_importer.optimizeMesh)
            			{
            				str_record = string.Format(&quot;Fbx设置中 optimizeMesh off 没开起来 ,{0} ,{1}&quot;, path_obj, obj_fbx.name);
            			}
            			if(model_importer.importMaterials)
            			{
            				str_record = string.Format(&quot;Fbx设置中 importMaterials on 被开起来了 ,{0} ,{1}&quot;, path_obj, obj_fbx.name);
            			}
            			if(!model_importer.weldVertices)
            			{
            				str_record = string.Format(&quot;Fbx设置中 weldVertices off 没开起来 ,{0} ,{1}&quot;, path_obj, obj_fbx.name);
            			}
            			if(model_importer.importTangents != ModelImporterTangents.None)
            			{
            				str_record = string.Format(&quot;Fbx设置中 importTangents on 被开起来了 ,{0} ,{1}&quot;, path_obj, obj_fbx.name);
            			}
            			if(model_importer.importNormals != ModelImporterNormals.Import)
            			{
            				str_record = string.Format(&quot;Fbx设置中 importNormals off 没开起来 ,{0} ,{1}&quot;, path_obj, obj_fbx.name);
            			}
            			if(smr != null
            				&amp;amp;&amp;amp; model_importer.isReadable)
            			{
            				str_record = string.Format(&quot;Fbx设置中 isReadable on 开起来了 SkinnedMeshRenderer 即动画不能开write ,{0} ,{1}&quot;, path_obj, obj_fbx.name);
            			}
            			if(!path_obj.Contains(&quot;_write&quot;)
            				&amp;amp;&amp;amp; model_importer.isReadable)
            			{
            				str_record = string.Format(&quot;Fbx设置中 isReadable on 开起来了 但文件名没有 _write 后缀,{0} ,{1}&quot;, path_obj, obj_fbx.name);
            			}
            			if(path_obj.Contains(&quot;_write&quot;)
            				&amp;amp;&amp;amp; !model_importer.isReadable)
            			{
            				str_record = string.Format(&quot;Fbx设置中 isReadable off 没开起来 但文件名有 _write 后缀,{0} ,{1}&quot;, path_obj, obj_fbx.name);
            			}
            			vertexCount_sum += mesh.vertexCount;
            		}
            	}

            	if(vertexCount_sum &amp;gt; MESH_VERTEX_MAX)
        		{
        			str_record = string.Format(&quot;网格顶点数大于 {0},{1} ,{2}&quot;, MESH_VERTEX_MAX, s, vertexCount_sum);
        		}
        	}

        	...

			//检测命名是否合法
			if (!UIAssetPost.IsFileNameLegal(s))
			{
				str_record = string.Format(&quot;文件命名不合法, {0}&quot;, s);
			}

			...
    	}
	}

	...
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;代码有点长我把它们省略了很多，并突出了重要的部分，我们可以举一反三，在检查的过程中，把资源在Unity3D中不符合规则的设置输出到文件中，告知大家有哪些资源文件有问题。把这个程序放在打包机上或者专门用于检测的流水线上，每1-2个小时运行一次，用微信或者企业微信的方式告知大家。这样就能做到检测的实时性和完整性，如果哪条规则疏漏了，就在程序里加上就可以了，检测到的资源问题分派给各个成员去处理，每个人做好自己范围内的资源管理，把资源检测警告清零就等于把项目中的资源完全规范化了。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第四章，UI(七) - UI优化(三)</title>
   <link href="http://www.luzexi.com/2018/07/30/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-UI9"/>
   <updated>2018-07-30T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/07/30/Unity3D高级编程之进阶主程-UI9</id>
   <content type="html">&lt;h6 id=&quot;前面介绍过优化ui的几种方法包括ui动静分离拆分过大的uiui预加载ui图集alpha分离ui字体拆分scroll-view-滚屏优化以及ugui图在改变颜色或alpha后导致对mesh重构的优化这篇我们来讲解ui展示与关闭的优化对象池的运用ui贴图设置的优化内存泄露排查与预防&quot;&gt;前面介绍过优化UI的几种方法，包括，UI动静分离，拆分过大的UI，UI预加载，UI图集Alpha分离，UI字体拆分，Scroll View 滚屏优化，以及UGUI图在改变颜色或Alpha后导致对Mesh重构的优化。这篇我们来讲解，UI展示与关闭的优化，对象池的运用，UI贴图设置的优化，内存泄露排查与预防。&lt;/h6&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;h3 id=&quot;-ui展示与关闭的优化&quot;&gt;⑧ UI展示与关闭的优化。&lt;/h3&gt;

&lt;p&gt;UI的展示与关闭动作最常见，需要查看界面时打开，结束了关闭。但打开和关闭会消耗一定的CPU，打开时需要实例化和初始化，关闭需要销毁GameObject。这些是CPU消耗在实际项目中的消耗量巨大。&lt;/p&gt;

&lt;p&gt;对于关闭和打开的CPU消耗的优化这里有几个策略可寻，&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	1.前面提过利用碎片时间的预加载，会让展示速度更加快。

	2.在关闭时隐藏节点，打开时再显示所有节点。

	3.移出屏幕。移出屏幕并不会让CPU消耗全部消失，但会减少GPU在这个界面上的消耗。当需要显示时再移入屏幕，有时候移入后进行初始化回到原来的状态也是必要的。

	4.打开关闭时，设置UI界面为其他的层级Layout，使得其排除在相机渲染之外，当需要展示时再设置回UI层级。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述中 2、3、4方法相同点是，都是用内存换CPU，关闭界面时不减少内存，只减少了CPU的消耗。不同点是，方法2 在关闭期间CPU消耗比方法3的更少，在打开时CPU消耗比方法3 的却更多。因为在显示所有节点的同时，UI网格需要重构，而移出屏幕则不需要重构网格。&lt;/p&gt;

&lt;p&gt;方法3 和方法4都使用了相同的原理。只是方法3 用坐标去做摄像机的渲染排除，而方法4 则用层级Layout去做摄像机的排除操作。方法3和4 在CPU消耗上会更少，不过也要注意它们在关闭的同时也需要关闭Update更新程序，以减少不必要的消耗。&lt;/p&gt;

&lt;h3 id=&quot;-对象池的运用&quot;&gt;⑨ 对象池的运用。&lt;/h3&gt;

&lt;p&gt;什么是对象池，以及为什么要用对象池？&lt;/p&gt;

&lt;p&gt;对象池，即对象的池子。对象池里寄存着一些废弃的对象，当计算机程序需要该种对象时，可以向对象池申请，让我们对废弃的对象再利用。&lt;/p&gt;

&lt;p&gt;如果对废物再利用就能省去了很多实例化时的CPU消耗。实例化消耗包括了，模型文件读取，贴图文件读取，GameObject实例化，程序逻辑初始化，内存销毁消耗等。&lt;/p&gt;

&lt;p&gt;对象池的规则是，当需要对象时向对象池申请对象，对象池从池子中拿出以前废弃的对象重新‘清洗’下(重置下)给出去，如果对象池也没有可用对象，则新建一个放入给出去，当对象用完后，把这些废弃的对象放入对象池以便再利用。&lt;/p&gt;

&lt;p&gt;对象池的方法，本质是用内存换CPU的策略。我们在UI界面中，时常会需要不断跳出不同的物体。这时实例化和销毁UI物体是逻辑中消耗最大的，物体被不断新建出来，又不断被销毁。CPU大部分浪费在了实例化和销毁上，渲染只占了很小一部分比重。这时运用对象池就能解决大部分浪费的问题，将要销毁的实例对象，放入对象池并移出屏幕或隐藏，当需要他们时再放出来重新初始化。&lt;/p&gt;

&lt;p&gt;对象池是个用内存换CPU的方法，它用内存付出代价来换取CPU的效率。不过使用的不恰当的话也会引起不少内存问题的，因此对象池最好是要用在重复利用率高的对象上。这里总结了几条对象池运用的经验：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;当程序中有重复实例化并不断销毁的对象时需要使用对象池进行优化。重复实例化和销毁操作会消耗大量CPU，在此类对象上使用对象池的优化效果极佳，相反如果在很少或较少做重复和销毁操作的对象上使用对象池，则会浪费内存，得不偿失。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;每个需要使用对象池的对象都需要继承对象池的基类对象，这样在初始化时可以针对不同对象做重载，区别对待不同类型的对象。让不同对象的初始化方法根据各自的情况分别处理。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;销毁操作时使用对象池接口进行销毁。在销毁物体时要使用对象池提供的销毁接口，让对象池来决定是真销毁，还是只是隐藏对象。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;场景结束时要及时销毁整个对象池，避免无意义的内存驻留。当场景结束后，在对象池内的物体，已经不再适合新的场景了，或者说面临的环境情况与旧场景不同时所以需要及时清理对象池，把内存空出来留给新场景使用。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;-ui贴图设置的优化&quot;&gt;⑩ UI贴图设置的优化。&lt;/h3&gt;

&lt;h6 id=&quot;为什么要关心ui贴图设置&quot;&gt;为什么要关心UI贴图设置？&lt;/h6&gt;

&lt;p&gt;首先我们得知道，Unity3D会重置全部贴图格式。可以理解为，无论你是JPG，PNG，PSD等，只要放在Unity3D中，Unity3D会读取图片内容，然后重新生成一个自己格式的图，在引擎中使用的是自己生成的图和格式。因此在Unity3D中使用图片其实不必关心用什么格式的图，只要你做好内容就可以了，比如JPG是没有alhpa通道的，通常做透贴都是PNG，这些图形或颜色内容上的东西是我们需要关心的，其他的交给Unity3D就可以。&lt;/p&gt;

&lt;p&gt;Unity3D中图片的设置也有很多讲究，因为关系到重新生成的图片的格式，最终将决定加载入引擎的是什么样格式的图片，所以我们不得不要研究下贴图的设置问题。&lt;/p&gt;

&lt;p&gt;这里以NGUI和UGUI为例分别讨论。NGUI的UI贴图使用传统的贴图方式，常使用 Editor GUI and Legacy GUI 方式，这种方式隐藏了一些设置参数，为了需要全面掌握所有对图片的功能才能做好优化工作，我们把Editor GUI and Legacy GUI 方式展开为 Advance 类型。&lt;/p&gt;

&lt;p&gt;Advance 里面需要注意的有：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;1.Alpha是否需要。如果需要透明通道，则要把透明通道点开，否则最好关闭。

2.是否需要进行2次方大小的大小纠正。Non Power of 2，对UI贴图来说基本上都是2次方大小的图集，使用对象大多是头像之类的Icon。

3.读写权限去除。常会默认勾选，导致内存量大增。此选项会使贴图在内存中存储两份，内存会有比不勾选大1倍。

4.Mipmap去除。Mipmap是对3D远近视觉的优化，Mipmap会在摄像头离物体远时因为不需要高清的图片而选择使用Mipmap生成的贴图小的模糊图像，从而减轻GPU压力。但是UI里没有远近之分，所以并不需要Mipmap这个选项，而且Mipmap会导致内存和磁盘空间加大，选择小尺寸图像会使得UI看起来模糊。

5.	压缩方式选择。

	压缩方式的选择，主要是为了降低内存消耗，降低CPU与GPU之间的带宽消耗，以及减少包体的大小，在清晰度足够的情况下，我们可以针对性的选择一些压缩方式来优化内存和包体。

	最高的色彩度是无压缩，其次是RGBA16色彩少了点且有透明通道，再次是RGB24没有alpha通道的全彩色，再RGB16色彩少了一半也没了透明通道，最后是算法级别的压缩，RGBA ECT2 8bits和RGBA PVRTC 4bits是带透明通道的压缩算法，RGB ECT2 4bits和RGB PVRTC 4bits，是不带透明通道的压缩算法。

	这样逐级下来，压缩的越来越厉害，同时画质就越来越差。前面有介绍过关于UI贴图Alpha分离的方法，这方法就是压缩的极致和平衡，既做到好画质又最大极限的压缩了图片。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;UI的选项的优化，我们可以通过写脚本的方式，把放入UI的贴图自动设置我们规定好的图片选项，辅助我们更改UI贴图设置，省去不少二次检查时间。例如以下这段，就是利用Unity3D的 Editor API 来自动设置UGUI的精灵图片。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;void Apply_ui_sprite()
{
	if(!UIAssetPost.IsInPath(assetImporter.assetPath, UI_Sprite_path))
	{
		return;
	}

	TextureImporter tex_importer = assetImporter as TextureImporter;

	if(tex_importer == null) return;

	tex_importer.textureType = TextureImporterType.Sprite;
	FileInfo file_info = new FileInfo(assetImporter.assetPath);
	string dir_name = file_info.Directory.Name;
	tex_importer.spritePackingTag = dir_name;
	tex_importer.alphaIsTransparency = true;
	tex_importer.mipmapEnabled = false;
	tex_importer.wrapMode = TextureWrapMode.Clamp;
	tex_importer.isReadable = false;

	SetCompress(tex_importer);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;要完全省去检查时间是不可能的，在实际项目中我们也不得不从头检查一遍所有贴图的设置情况，来确认是否是我们所期望的设置，不过工作量比以前少了很多，可靠性也增强了许多。&lt;/p&gt;

&lt;h3 id=&quot;-内存泄露&quot;&gt;⑪ 内存泄露。&lt;/h3&gt;

&lt;p&gt;内存泄露是个敏感的词汇，在各大项目中，都会对内存泄露进行检验，一旦涉及到内存泄露所有内存，大家都会格外重视。其实在整个项目各个地方都有可能，我把内存泄露放在UI章节里是因为UI逻辑占去了游戏逻辑中比较大的一部分，所以内存泄露在UI逻辑中也是重灾区。&lt;/p&gt;

&lt;h6 id=&quot;什么是内存泄露&quot;&gt;什么是内存泄露？&lt;/h6&gt;

&lt;p&gt;内存泄漏，简单来说就是由程序向系统申请内存，使用完毕后并没有将内存还给系统导致内存驻留或者浪费的过程。系统本身的内存是有限的，如果内存泄漏一直被调用，就会耗尽系统内存，最终导致奔溃。就像你一直向银行借钱不还一样，银行虽然一直容忍你的不道德行为但银行也是有底线的，最终会切断你的资金来源，一下子收回全部资金，到那时候你就崩溃了。计算机系统也是一样，他不会无限制的让程序申请到内存，当申请内存影响到系统运行时就会停止。&lt;/p&gt;

&lt;h6 id=&quot;为什么会内存泄露&quot;&gt;为什么会内存泄露？&lt;/h6&gt;

&lt;p&gt;游戏项目内存泄漏简单分两种，一种是程序上的内存泄漏，另一种是资源上的内存泄漏。虽然资源上的内存泄漏也跟程序有关，但跟程序上的自身内存块的内存泄漏相比，它主要是因为资源在使用后或不再使用时没有卸载的原因导致的。&lt;/p&gt;

&lt;p&gt;程序上的内存泄漏主要是因为Mono的垃圾回收机制并没有识别“垃圾”的原因造成的。为什么会没有识别呢，根源还是在编程时的疏忽，在编程时一些不好的习惯，错误的想法，不清晰的逻辑，导致申请的内存或指向内存的引用，没有有效的释放，导致垃圾回收机制没能识别出释放此块内存的理由。
而资源上的内存泄漏，主要是因为人为的申请资源使用完毕后并没有释放，导致资源内存长期驻留在内存里。&lt;/p&gt;

&lt;p&gt;程序上的内存泄漏，需要借助些工具，也可以从框架的角度建立有效的指针计数器来排查，可以说是属于预防型为主排查为辅。而资源上的内存泄漏，就完全是属于人为的过错或疏忽，关键是容易排查。资源内存泄漏，主要排查的内容就是，资源在不需要使用时，却仍然驻留在内存里的情况。&lt;/p&gt;

&lt;h6 id=&quot;什么是垃圾回收机制&quot;&gt;什么是垃圾回收机制？&lt;/h6&gt;

&lt;p&gt;Unity3D是使用基于Mono的C#作为脚本语言，它是基于Garbage Collection（简称GC）机制的内存托管语言。那么既然是内存托管，为什么还会存在内存泄漏呢？GC本身并不是万能的，GC能做的是通过一定的算法找到“垃圾”，并且自动将“垃圾”占用的内存回收，并且每次运行垃圾回收需会消耗一定量的CPU。&lt;/p&gt;

&lt;p&gt;找“垃圾”的算法有两种，一种是用引用计数的方式，另一种是跟踪收集的方式。&lt;/p&gt;

&lt;p&gt;引用计数，简单的说，就是当被分配的内存块地址赋值给引用时，增加计数1，相反当引用清除内存块地址时，减少计数1。当引用计数变为0时，表明没有人再需要此内存块了，所以可以把内存块归还给系统，此时这个内存块就是垃圾回收机制要找的“垃圾”。&lt;/p&gt;

&lt;p&gt;另一个是跟踪收集，简单的说就是遍历一遍引用内存块地址的根变量，以及与之相关联的变量，对内存资源没有引用的内存块进行标记，标记为“垃圾”，在回收时还给系统。&lt;/p&gt;

&lt;h6 id=&quot;为什么有了这么智能的垃圾回收机制还会有内存泄漏呢&quot;&gt;为什么有了这么智能的垃圾回收机制，还会有内存泄漏呢？&lt;/h6&gt;

&lt;p&gt;首先引用计数的方式它很难解决对象之间相互循环引用的问题，导致引用计数时无法被释放。现代计算机语言中已经很少使用这种方式去做了，但在逻辑组件上或业务框架上有很多，因为这样做简单方便，比如C++智能指针就是这种方式。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;比如 A类中有B类的实例变量，而B类中有A类的实例变量，现在有A,B两个实例，这时A的引用计数为2，B的引用计数也为2，当B变量被置NULL后，B的引用计数只减少了1，因为在A中还有一个，因此，只有当A的实例变量也被销毁时，B实例的引用计数才真正变为0。也就是说B类变量是否销毁的命运同时取决于A和B。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;其次跟踪收集并不是万能的，很多时候会有环状的引用链存在，以及包括在编码时错误操作的泄漏，这些编码的泄漏问题在实际编码过程中是非常隐蔽且难以查找的，不少的泄露问题需要人工去检查引用变量是否释放引用，工作量比较巨大且繁琐，特别是程序侧的内存泄漏尤其难找。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;比如常常会有 A类中有B，B类中有C，C类中有D，D类中有A。这种环装的链路，导致跟踪收集比较困难，当C类实体设置为NULL后，B中依然有C，B设置为NULL后，A中依然有B，进而导致B中依然有C。这种就像‘命运共同体’似的环状引用链，导致跟踪收集的垃圾回收机制在被调用时的效果并不明显。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;因此垃圾回收并不是万能的，即使有垃圾回收也一样会存在内存泄露问题。如果想避免内存泄露，至少在建立框架或架构时就应该对此有足够的考虑，对基础组件应该更加严谨，在这基础之上再对编程规范进行严格的把控，即使是这样在排查时也要保持足够的耐心和细心。&lt;/p&gt;

&lt;p&gt;资源侧的内存泄漏对游戏项目来说量级上比较大，大到几百MB甚至几个G，不过万幸的是相对程序侧来说资源侧的内存泄漏查找相对比较容易。下面介绍一些关于Unity3D内存运作，泄漏排查，预防泄漏的经验，来帮助大家在实际项目中针对内存泄漏理解，排查，和预防。&lt;/p&gt;

&lt;h6 id=&quot;unity3d内存是如何运作的&quot;&gt;Unity3D内存是如何运作的？&lt;/h6&gt;

&lt;p&gt;Unity3D中C#起初使用Mono做为虚拟机(VM，和Java一样都是虚拟机托管)运行在各大平台上，也就是说C#代码只要一份就够了(准确的来说应该是IL即中间语言是同一份的)，各大平台的Mono需要各自制作一份来应对各系统的执行接口，简单说也就是说Unity3D通过Mono来跨平台解析并运行C#代码，在Android系统上App的lib目录下存在的libmono.so文件，就是Mono在Android系统上的实现。&lt;/p&gt;

&lt;p&gt;C#代码通过Mono这个虚拟机解析和执行，当需要的内存自然由Mono来进行分配管理。只是Mono的堆内存大小在运行时是只会增加而不会减少的。可以将Mono内存堆理解为一个内存池，每次C#向Mono内存的申请堆内存，都会在池内进行分配，释放的时候也是归还给池里去，而不是释放归还给操作系统。假如某次分配，发现池里的内存不够了，则会对池进行扩建，即向操作系统申请更多的内存扩大池以满足该次以及后面更多的内存分配。需要注意的是，每次对池的扩建都是一次较大的内存分配，每次扩建都会将池扩大6-10MB左右。&lt;/p&gt;

&lt;p&gt;分配在Mono堆内存上的都是程序上需要的内存块，例如静态实例以及这些实例中的变量和数组、类定义数据、虚函数表，函数和临时变量更多得则使用栈来存取。Unity3D的资源则不同，当它被读取进来时是通过Unity3D的C++层，分配在Native堆内存上的那部分内存，与Mono堆内存是分开来管理的。&lt;/p&gt;

&lt;p&gt;Mono通过垃圾回收机制（Garbage Collect，简称GC）对内存进行回收。前面我们说了当Mono需要分配内存时，会先查看空闲内存是否足够，如果足够的话则直接在空闲内存中分配，否则Mono会扩容，在扩容之前Mono会进行一次垃圾回收(GC)以释放更多的空闲内存，如果GC之后仍然没有足够的空闲内存，这时Mono才会向操作系统申请内存扩充堆内存。&lt;/p&gt;

&lt;p&gt;除了空闲内存不足时Mono会自动调用GC外，我们也可以在代码中主动调用GC.Collect()来手动进行GC。不过问题是GC本身是个比较消耗CPU计算量的过程，不仅如此，由于GC会暂停那些需要Mono内存分配的线程（C#代码创建的线程和主线程），因此无论是否在主线程中调用GC都会导致游戏一定程度的卡顿，需要谨慎调用。&lt;/p&gt;

&lt;p&gt;由于各种原因Unity3D后来不再完全依靠Mono了，而另寻了一个解决方案那就是IL2CPP，Unity3D将C#翻译成IL中间语言后再翻译成C++以解决所有问题。那么翻译成C++语言内存就不托管了吗？不是的。内存依然托管，只是这次由C++编写VM来接管内存，不过这个VM只是内存托管而已，并不解析和执行任何代码，它只是个管理器。&lt;/p&gt;

&lt;p&gt;IL2CPP与Mono的区别在什么地方呢？区别在于Mono只将C#翻译为IL中间语言，并把中间语言交给VM去解析和执行，VM的工作既要解析又要执行，这样的话Mono要针对不同平台执行IL程序就需要为每个平台定制一个单独的VM。IL2CPP则是把C#代码翻译为IL中间语言后又再继续翻译为C++代码，对于不同平台来说每次翻译的C++代码必须针对当前平台的API做出些变化，也就是说IL2CPP在不同平台下需要对不同平台的接口进行改造。与Mono针对不同平台拥有不同的VM相比，IL2CPP只是在翻译时改造了不同平台的接口代码，显而易见IL2CPP对程序员来说维护的工作量减少了很多。不仅仅只是程序员维护的工作量少了，在IL2CPP翻译完成后的编译时，使用的是平台本身都各自拥有的C++编译器，用各自平台的C++编译器进行编译后就可以直接执行编译内容无需再通过VM，因此IL2CPP相对Mono的效率会更高一些。&lt;/p&gt;

&lt;h6 id=&quot;资源内存泄漏&quot;&gt;资源内存泄漏&lt;/h6&gt;

&lt;p&gt;资源内存泄漏就是Native内存泄漏，与程序内存泄漏不一样，资源内存泄漏都是因为加载后没有释放造成的，也有在逻辑中拷贝了一份资源但没有在使用完释放的情况。基本上都是疏忽大意造成的，除非完全不知道需要卸载。&lt;/p&gt;

&lt;p&gt;Unity3D的 MemoryProfiler 是个查内存泄漏的利器，他是由官方开发的专门用于Unity3D 5.x以上版本的内存快照工具。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://bitbucket.org/Unity-Technologies/memoryprofiler&quot;&gt;MemoryProfiler&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;它可以快照内存的信息，并可以以文件形式保存和加载，这样我们可以在不同的节点进行内存快照，再经过两者的对比找出内存泄漏的资源，定位泄漏的资源文件，再根据此文件从程序逻辑中寻找泄漏点。&lt;/p&gt;

&lt;p&gt;比较遗憾的是，MemoryProfiler并没有提供两次（或多次）内存快照的比较功能。所以更多的是需要人工去核实。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/4/ui-optimization1.png&quot; alt=&quot;MemoryProfiler&quot; /&gt;&lt;/p&gt;

&lt;p&gt;从图中可以看出整体上的内存占用规模，包括，音效，字体，Assetbundle，动画，模型，粒子，贴图，Shader等。也可以点击整个模块细致的检查，模块中的各个点位资源的信息。比如我选中的Texture模块中的一个贴图，就展示出此贴图的信息包括：名字、图案、材质球、以及关联了哪些脚本等。&lt;/p&gt;

&lt;p&gt;我们也可以借助Unity3D自带的Memory Profiler，这是个比较老的工具。它会记录CPU使用情况，精准定位CPU耗时节点，也可以记录Mono堆内存和资源内存的使用情况，并且详细记录下了内存中资源的详细情况。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/4/ui-optimization2.png&quot; alt=&quot;MemoryProfiler&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当我们检查到当前场景，不需要用到的资源时，这个资源就是泄漏的点。我们可以顺藤摸瓜根据Profiler提供的信息，在代码中寻找线索。寻找的过程还是很枯燥的，这是肯定的，但当我们寻找出一个资源泄漏点时，可以举一反三的找出更多的资源泄漏点。不过在Editor下编辑场景时Editor本身会加载些资源来达到可视化的目的，这导致在Editor下的使用Memory Profiler时不太准确因为前面你已经查看过这个资源，这个资源已经被加载到内存里了，所以最好在使用 Memory Profiler 前重启Unity3D查看和不编辑任何资源立刻调试。&lt;/p&gt;

&lt;h6 id=&quot;这里介绍两种寻找资源内存泄漏的技巧&quot;&gt;这里介绍两种寻找资源内存泄漏的技巧：&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	1） 通过资源名来识别。

		即在美术资源（如贴图、材质）命名的时候，就将其所属的游戏状态放在文件名中，如某贴图叫做bg.png，在房间中使用，则修改为Room_bg.png。

		这样在Profile工具里一坨内存资源里面，混入了一个Room大头的资源，可以很容易地识别出来，也方便利用程序来识别。

		这个方法但也不是万能的，因为在项目制作过程当中，一张图需要被用到各个场景中去，很可能也不只一两个，有时甚至四五个场景中都会用，只用前缀来代替使用场景的指定，很多时候也会造成另一种误区。

		甚至由于项目的复杂度扩展到一定程度，包括人员更替，在检查资源泄漏时，用前缀来判断使用场景点不太靠谱，因为你根本就不知道这张图在哪使用了。所以说技巧只能辅助你，并不是说一定能有效。

	2） 我们可以通过Unity提供的接口Resources.FindObjectsOfTypeAll()进行资源的Dump.

		可以根据需求Dump贴图、材质、模型或其他资源类型，只需要将Type作为参数传入即可。

		Dump成功之后我们将这些信息结果保存成一份文本文件，这样可以用对比工具对多次Dump之后的结果进行比较，找到新增的资源，那么这些资源就是潜在的泄漏对象，需要重点追查。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在平时项目中，我们找到这些泄漏的资源的方法，最直观的方法，就是在每次游戏状态切换的时候，做一次内存采样，并且将内存中的资源一一点开查看，判断它是否是当前游戏状态真正需要的。这种方法最大的问题，就是耗时耗力，资源数量太多眼睛容易看花看漏。&lt;/p&gt;

&lt;p&gt;现在市面上比较有名的Unity3D项目优化工具UWA的GOT，它会逐帧记录资源内存和Mono堆内存的使用情况，并且可以在快照之间进行相互比较，得出新增或减少的资源名称。有了内存快照之间的对比就可以大大加快了我们查找内存泄漏的问题。&lt;/p&gt;

&lt;p&gt;另外在Github上有一个在Editor下可以对内存快照进行比较的工具。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/PerfAssist/PA_ResourceTracker&quot;&gt;内存快照进行比较的工具&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;它是将Unity Memory Profiler改造后，加了快照比较，搜索，内存分配跟踪的功能，在原来Unity Memory Profiler的快照功能上提升了不少实用性。我们可以用这个工具来方便得快照内存以及比较内存的使用情况，借此来查找内存泄漏情况，确实是一个内存泄漏查找利器。&lt;/p&gt;

&lt;p&gt;排查还是后置的方法，在编写程序和架构，特别是基础组件(即内存管理器，资源管理器)时，我们应该强化生命周期的理念，无论是程序内存还是资源内存，都应该有它存在的生命周期，在生命周期结束后就应该及时被释放。具体我们将在“资源加载与释放”章节中详细讲解。&lt;/p&gt;

&lt;p&gt;参考文献：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	深入浅出再谈Unity内存泄漏 作者：Arthuryu
&lt;/code&gt;&lt;/pre&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第四章，UI(七) - UI优化(四)</title>
   <link href="http://www.luzexi.com/2018/07/30/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-UI10"/>
   <updated>2018-07-30T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/07/30/Unity3D高级编程之进阶主程-UI10</id>
   <content type="html">&lt;p&gt;这篇我们继续聊UI优化，这篇讲，区别针对高低端机型的优化，UI图集拼接的优化，GC的优化。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;h3 id=&quot;-区别针对高低端机型的优化&quot;&gt;⑫ 区别针对高低端机型的优化。&lt;/h3&gt;

&lt;p&gt;为什么要区分高低端机型？&lt;/p&gt;

&lt;p&gt;我们在做游戏项目时画质和流畅度是非常重要的，不过市面上的设备并不是统一的一种设备，纷繁复杂的设备市场，各种厂商都会推出不同型号不同性能的设备来满足大众的需求。&lt;/p&gt;

&lt;p&gt;一款游戏的画质和流畅度是决定游戏是否能畅销的关键，而画质和流畅度又需要靠设备来支撑，高低性能的设备会导致游戏产生不同的性能效果，这是最让人头疼的。&lt;/p&gt;

&lt;p&gt;辛苦开发的游戏，却不能在设备上顺畅的跑起来，导致画质糟糕，画面过慢，卡顿，甚至崩溃等问题，影响了游戏的可玩性，也影响了游戏在市场上的前景。&lt;/p&gt;

&lt;p&gt;为了让市场上的高低机型的设备都能让游戏流畅的跑起来，我们需要让游戏对高低机型区别对待。&lt;/p&gt;

&lt;p&gt;在高端机型中使用画质好的画面，而在低端机型中使用差一点的画质。因为低端机型内存低，CPU性能差，设备中部件之间配合并没那么好，所以如果还是运行高端的画质会比较不流畅。&lt;/p&gt;

&lt;p&gt;如何处理高低端机型的画质问题？&lt;/p&gt;

&lt;p&gt;高低端机型的区分对待，我们可以从几个方面入手：&lt;/p&gt;

&lt;p&gt;1， UI贴图质量区分对待。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    针对高低端不同的机型，分别使用不同的两套UI贴图，其中一套是针对高端机型的，无任何压缩，无缩小，
    
    另一套是针对低端机型的，对UI贴图进行了压缩，且缩小了UI贴图的大小。

    我们会在下面详细的讲如何在游戏中用NGUI和UGUI无缝的切换高低质量的UI画面。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;2， 特效使用情况区分对待。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    针对高低端不同的机型，分别使用不同的特效，或者非关键部位不使用特效。

    特效在项目中使用的最常见也最频繁，高质量的特效能直接导致低端机型的卡顿，因此在低端机型中更换低质量的特效，或者甚至在非关键部位不使用特效。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;3， 阴影使用情况区分对待。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    针对高低端不同的机型，分别使用不同质量的阴影，或者不使用阴影。

    场景中的模型物体越多，面数就越多，实时阴影的计算量与渲染量就越多。

    在低端机型上保持顺畅是第一，如果能不使用阴影那是最好的，直接省去了阴影的计算。

    但假如一定要使用，那我们也有办法减低阴影计算和渲染的消耗。

    下面是针对高低端机型对阴影处理的几个方法：

        方法1，用Unity3D提供的阴影渲染设置接口，QualitySettings.shadowResolution 设置渲染质量，QualitySettings.shadows 设置有无和模式，以及 QualitySettings.shadowProjection 投射质量，QualitySettings.shadowDistance 阴影显示距离，QualitySettings.shadowCascades 接受灯光的数量等。

        在高端机型上使用高质量阴影渲染设置，而在中端机型上使用中端阴影渲染设置，在低端机型上使用低端阴影渲染设置，甚至关闭阴影渲染设置。

        方法2，关闭传统的阴影渲染设置，使用简单的底部圆形黑色阴影面片代替。

        这样就省去了阴影计算的CPU消耗，而且还能看见阴影在底部。

        用一些简单的阴影面片替换了阴影计算的CPU消耗，用内存换了CPU。

        方法3，关闭部分模型的阴影计算。
        
        使用简单的圆形面片代替阴影在真实感上效果还是差了点，如果我们既需要实时的阴影效果，又不想消耗过多的CPU，怎么办？

        我们可以对部分模型使用简单的阴影面片代替，对另一部分比较重要的模型使用实时阴影渲染。

        这就需要我们将场景中的所有可被计算阴影的模型集中起来管理。

        我们将场景中的Render全部收集起来，把需要实时阴影计算的模型打开Render.receiveShadows选项，对另一些不需要实时阴影计算的模型关闭Render.receiveShadows选项，并选择使用简单阴影模型代替。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;4，	抗噪声，LOD，像素光数量使用情况区分对待。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    针对高低端不同的机型，分别使用不同质量的抗锯齿效果和不同级别的LOD效果，或者直接不使用抗锯齿和LOD效果。

    Unity3D渲染设置专门有针对抗噪声，LOD，像素光数量限制的选项，我们可以在程序里调用 QualitySettings.antiAliasing，QualitySettings.lodBias，QualitySettings.maximumLODLevel，QualitySettings.pixelLightCount等。

    可以使用这些接口在高中低端机上分别进行设置，让不同性能的机子拥有不同画质的设置。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;5，	整体贴图渲染质量区分对待。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    针对高低端不同的机型，分别使用不同的贴图渲染质量。

    Unity3D有对贴图渲染质量的设置，QualitySettings.masterTextureLimit，这个API默认是0，就是不对贴图渲染进行限制，假如设置为1，就是渲染1/2大小的画质，相当于压缩了所有要渲染的贴图的大小至原先的1/2大小，假如设置2，就是1/4画质，以此类推。

    对所有贴图进行渲染限制，是个大杀器，能直接让CPU和GPU消耗降下来，但画质也遭到了毁灭性的打击，我们需要它在低端机型上发挥作用，但也要谨慎使用。
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;那么怎么用程序区分机子是高低端呢&quot;&gt;那么怎么用程序区分机子是高低端呢？&lt;/h6&gt;

&lt;p&gt;区分机子是高低端的几个方法介绍下：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    1，	Apple，毕竟IOS的型号是有限的，我们可以把其中一些型号的机子归类为高端，其中一些型号的机子归类为中端，另一些型号的机子归类为低端。Unity3D中的API有可以用的接口，例如 UnityEngine.iOS.Device.generation == UnityEngine.iOS.DeviceGeneration.iPhone6，等以此类推就能实现区分Apple机子高低端的情况。

    2，	Android 等其他机型，由于机子型号太多，我们可以用内存大小，系统版本号，屏幕分辨率大小，平均帧率等因素判断是高端机还是低端机。

    比如，3G内存或以上为高端机，1G或以下肯定是低端，其他为中端。

    又比如，Android 7.0以上为高端，Android 5.0以下都是低端，其他为中端。

    如上描述通过一些简单的规则将高低端机型区分开来。

    3， 我们也可以通过平均帧率来判定高低端机型。

    在游戏中加入统计平均帧率的程序，将机型型号和平均帧率发送给服务器，由服务器记录各机型型号的平均帧率，再统计出来一份报表给客户端，客户端根据这份统计报表决定，哪些机型型号是高端设备，哪些是低端设备。

    最后再通过高低端设备画质不同的方法，来优化高低端设备的游戏流畅度。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;区分高低端的方法，我们可以三种一起用，因为Android中有一些机子是有固定名称或者编号的，我们可以确定他的高低端类型，有些是不确定型号的，就需要通过设备的硬件设置来确定高低端，哪些完全不能确定高低端的机子，就只能在统计中得到答案了。&lt;/p&gt;

&lt;p&gt;在高低端机型优化中，UI贴图质量的区别对待是个比较重要的手法，我们下面将对此方法进行详细的介绍。&lt;/p&gt;

&lt;p&gt;在开发游戏时免不了要针对不同机型的做不同的处理，让游戏在高画质和低画质之间切换，在判定高低端设备后，可以实时切换游戏品质让游戏更加流畅。这能给客户端在渠道发行后提高些许留存率。&lt;/p&gt;

&lt;p&gt;NGUI和UGUI切换方式有所不同，NGUI基于图集Atlas，UGUI基于Image Unity3D4.6.1后的Sprite 2D。&lt;/p&gt;

&lt;p&gt;UI高低画质切换在NGUI和UGUI里都是基于两套图和两套Prefab的。&lt;/p&gt;

&lt;p&gt;它们的共同特点是在所有的原生态的高画质(HD)Prefab都是用脚本工具去生成相应的标准画质(SD)Prefab。&lt;/p&gt;

&lt;p&gt;用编写的程序把高清UI和标清UI做成两个不同的Prefab，两个UI的功能是相同的，只是相对应的图集质量不同罢了。&lt;/p&gt;

&lt;p&gt;然后我们在高端机型中运行高清UI，在中低端机型中运行标清UI，使得高低端机型都能流畅的跑游戏，而且拥有相同的功能。&lt;/p&gt;

&lt;p&gt;这个生成高低画质Prefab的程序其运行的步骤是，先把所有UI用到的图集，材质球都复制一份到固定文件夹下，再复制一份Prefab存放在文件夹下面，再把Prefab里与图集有关的变量都指向标清SD材质球或者标清SD图集，或者也可以是标清SD单张图。&lt;/p&gt;

&lt;p&gt;我们来看看实时切换高清和标清的UI时的具体步骤：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;一套UI图复制成两套图。&lt;/p&gt;

    &lt;pre&gt;&lt;code&gt;一套高清一套标清，高清(HD) Prefab指向高清图，(标清SD)Prefab指向低清图。

这里NGUI和UGUI的方法不同。

NGUI需要制作两个Atlas prefab，再通过修改内核将Atlas实时更换掉，也可以复制并制作另一个SD UI Prefab只改变Atlas部分的指向标清画质的Atlas。

UGUI稍微复杂一点，但原理差不多，虽然不能实时改变图集来切换高清画质和标清画质，但也可以通过制作一个SD Prefab来达到高清和标清切换的目的。

步骤是，首先复制所有图片到SD文件夹，并加上前缀sd，这样好辨认，然后复制一个相同UI Prefab命名为SD UI Prefab，再把复制过来的SD UI Prefab里的图都换成SD里的图。这样高清和标清UI Prefab都有相同的逻辑，只是指向的图不同而已。
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;程序在选择SD还是HD时，只要需要关注Prefab名的不同。&lt;/p&gt;

    &lt;pre&gt;&lt;code&gt;Prefab名字在高清和标清之间只是前缀不一样，高清的前缀HD或者没有前缀，而标清的文件名前缀统一设置为SD，所以加载时很容易用前缀名区分开来。
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;最后，NGUI和UGUI制作SD Prefab的流程可以通过写一个脚本程序一键搞定，就不用再麻烦手动一个个复制一个个修改了。&lt;/p&gt;

    &lt;pre&gt;&lt;code&gt;在开发过程中，我们只要维护好HD UI Prefab就可以了，在打包前用脚本一键构建SD UI Prefab能节省不少时间，提高不少效率。

这样一来，在制作过程中，我们不用再关心SD的事情，完全可以只把注意力和精力集中在做好高清的UI上，关于标清SD的问题，脚本程序已经帮我们全部搞定。
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;最后总结：两套图一高一低，需要维护两套，使用脚本程序工具根据HD的Prefab生成SD的Prefab。&lt;/p&gt;

&lt;h3 id=&quot;-ui图集拼接的优化&quot;&gt;⑬ UI图集拼接的优化。&lt;/h3&gt;

&lt;p&gt;为什么要优化UI图集拼接？&lt;/p&gt;

&lt;p&gt;UI图集概念在Unity3D的UI中是必不可少的。UI图在整个项目中也占了举足轻重的作用。所以UI图集的大小，个数，也一定程度上决定了项目打包后的大小和运行效率。&lt;/p&gt;

&lt;p&gt;没有优化过UI图集的项目，会浪费很多空间，包括硬盘空间和内存空间，同时也会浪费CPU的工作效率。所以优化UI图集拼接也是很重要的。&lt;/p&gt;

&lt;p&gt;如何优化图集拼接？&lt;/p&gt;

&lt;p&gt;下面介绍几个方法：&lt;/p&gt;

&lt;p&gt;1，	充分利用图集空间。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    在我们大小图拼接在一起制作成图集时，尽量不要让图集空出太多碎片空间。

    碎片空间怎么来的呢，基本上由大图与大图拼接而来，因为大图需要大块的拼接空间，所以会有几张大图拼接在一起形成图集的情况，导致很多浪费的空白空间在图集内。

    我们要把大图拆分开来拼接，或者把大图分离出去不放入图集内，而使用单独的图片做渲染。

    在拼接时，大图穿插小图，让空间更充分的利用。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;2，	图集大小控制。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    假设我们图集的大小不加以控制，就会形成例如 2048x2048 甚至 4096x4096 的图。

    这会导致什么问题呢，在游戏加载UI时异常的卡顿，而且由于卡顿的时间过长内存消耗过快，导致糟糕的用户体验甚至崩溃。

    我们需要规范图集大小，例如我们通常规定图集大小标准在1024x1024，这样不仅在制作时要考虑让大小图充分利用空白空间，也让UI在加载时，只加载需要的图集，让加载速度更快。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;3，	图片的拼接归类。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    在没有图片拼接归类的情况下，通常会在加载UI时加载了一些不必要的图集，导致加速速度过慢，内存消耗过大的问题。

    比如背包界面的一部分图片，放在了大厅图集里，导致，在加载大厅UI时，也把背包界面的图集一并加载了进来，导致加速速度缓慢，内存飙升。

    我们要人为的规范他们，把图集分类，例如，通常我们分为，常用图集（里面包含了一些各个界面都会用到的常用图片），功能类图集（比如大厅界面图集，背包界面图集，任务界面图集等），链接类图集（链接两种界面的图集，比如只在大厅界面与背包界面都会用的，特别需要拆分出来单独成为一张图集）
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们优化图集拼接的最终目的是，减少图集大小，减少图集数量，减少一次性加载图集数量，让游戏运行的更稳，更快。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;h3 id=&quot;-gc的优化&quot;&gt;⑭ GC的优化。&lt;/h3&gt;

&lt;p&gt;什么是GC？为什么要优化GC？&lt;/p&gt;

&lt;p&gt;GC(garbage collection)就是垃圾回收机制。前面在内存泄漏章节中对垃圾回收机制做过详细的介绍，这里再简单介绍下。&lt;/p&gt;

&lt;p&gt;在游戏运行的时候，数据主要存储在内存中，当游戏的数据在不需要的时候，存储当前数据的内存就可以被回收以再次使用。&lt;/p&gt;

&lt;p&gt;内存垃圾是指当前废弃数据所占用的内存，垃圾回收（GC）是指将废弃的内存重新回收再次使用的过程。&lt;/p&gt;

&lt;p&gt;在进行垃圾回收（GC）时，会检查内存上的每个存储变量，然后对每个变量检查其引用是否处于激活状态，如果变量的引用不处于激活状态，则会被标记为可回收，被标记的变量会在接下去的程序中被移除，其所占的内存也会被回收到堆内存中。&lt;/p&gt;

&lt;p&gt;所以GC操作是一个相当耗时的操作，堆内存上变量或者引用越多则其检查的操作会更多，耗时也更长。&lt;/p&gt;

&lt;p&gt;引用变量的多少和层数，只是GC耗时的其中一个因素。其他关键的耗时因素，还有内存分配和申请系统内存的耗时，回收内存的耗时以及垃圾回收（GC）接口被调用的次数，都是非常关键的GC耗时因素。&lt;/p&gt;

&lt;p&gt;下面我们来看内存分配和申请系统内存是如何影响耗时的：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    1）Unity3D内部有两个内存管理池：堆内存和堆栈内存。堆栈内存(stack)主要用来存储较小的和短暂的数据，堆内存(heap)主要用来存储较大的和存储时间较长的数据。

    2）Unity3D中的变量只会在堆栈或者堆内存上进行内存分配，变量要么存储在堆栈内存上，要么处于堆内存上。

    3）只要变量处于激活状态，则其占用的内存会被标记为使用状态，则该部分的内存处于被分配的状态。

    4）一旦变量不再激活，则其所占用的内存不再需要，该部分内存可以被回收到内存池中被再次使用，这样的操作就是内存回收。处于堆栈上的内存回收及其快速，处于堆上的内存并不是及时回收的，此时其对应的内存依然会被标记为使用状态。

    5) 垃圾回收主要是指堆上的内存分配和回收，Unity3D中会定时对堆内存进行GC操作。

    6) Unity3D中堆内存只会增加，不会减少，也就是当堆内存不足时只会向系统申请更多内存，而不会空闲时还给系统，除非应用结束重新开始。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;由于Unity3D在堆内存不足时会向系统申请新的内存以扩充堆内存，这种向系统申请新内存的方式是比较耗时的。&lt;/p&gt;

&lt;p&gt;我们平时的游戏项目中，常常由于堆内存中的申请与回收导致大量的碎片内存，而当大块的内存需要使用时，这些碎片内存却无法用于大块内存需求，此时就会引起一个耗时的操作，就是向系统申请更多的内存，申请的次数越多越频繁，GC的耗时也就越多。&lt;/p&gt;

&lt;p&gt;垃圾回收（GC）接口被调用的次数也是关键因素。&lt;/p&gt;

&lt;p&gt;经常有游戏项目中，不断频繁的调用垃圾回收（GC）接口，每次调用都会重新检查所有内存变量是否被激活，并且标记需要回收的内存块并且在后面回收，这样就在逻辑中产生了很多不必要的检查，和并不集中的销毁导致的内存命中率下降，最终导致浪费了宝贵的CPU资源。&lt;/p&gt;

&lt;p&gt;GC的调用次数以及时机是非常关键的，GC操作会需要大量的时间来运行，如果堆内存上有大量的变量或者引用需要检查，则检查的操作会十分缓慢，这就会使得游戏运行缓慢。&lt;/p&gt;

&lt;p&gt;其次GC可能会在关键时候运行，例如在CPU处于游戏的性能运行关键时刻，此时任何一个额外的操作都可能会带来极大的影响，使得游戏帧率下降。&lt;/p&gt;

&lt;p&gt;另外一个GC带来的问题是堆内存的碎片化。&lt;/p&gt;

&lt;p&gt;当一个内存单元从堆内存上分配出来，其大小取决于其存储的变量的大小。&lt;/p&gt;

&lt;p&gt;当该内存被回收到堆内存上的时候，有可能使得堆内存被分割成碎片化的单元。&lt;/p&gt;

&lt;p&gt;也就是说堆内存总体可以使用的内存单元较大，但是单独的内存单元较小，在下次内存分配的时候不能找到合适大小的存储单元，这也会触发GC操作或者堆内存扩展操作。&lt;/p&gt;

&lt;p&gt;堆内存碎片会造成两个结果，一个是游戏占用的内存会越来越大，一个是GC会更加频繁地被触发。&lt;/p&gt;

&lt;h3 id=&quot;如何主动减少gc的消耗&quot;&gt;如何主动减少GC的消耗？&lt;/h3&gt;

&lt;p&gt;主要有三个操作会触发垃圾回收：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;　　 1） 在堆内存上进行内存分配操作而内存不够的时候都会触发垃圾回收来利用闲置的内存；

　　 2） GC会自动的触发，不同平台运行频率不一样；

　　 3） GC可以被强制执行。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;特别是在堆内存上进行内存分配时内存单元不足够的时候，GC会被频繁触发，这就意味着频繁在堆内存上进行内存分配和回收会触发频繁的GC操作。&lt;/p&gt;

&lt;p&gt;在Unity3D中，值类型变量都在堆栈上进行内存分配，而引用类型和其他类型的变量都在堆内存上分配。&lt;/p&gt;

&lt;p&gt;所以值类型的分配和释放,在其生命周期后会被立即收回，例如函数中的临时变量Int，其对应函数调用完后会立即回收。&lt;/p&gt;

&lt;p&gt;而引用类型的分配和释放，是在其生命周期后或被清除后，在GC的时候才回收，例如函数中的临时变量List&lt;Int&gt;，在函数调用结束后并不会立刻被回收，而是要等到下次GC时才被回收至堆内存。&lt;/Int&gt;&lt;/p&gt;

&lt;p&gt;大体上来说，我们可以通过三种方法来降低GC的影响：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;　　1）减少GC的运行次数；

　　2）减少单次GC的运行时间；

　　3）将GC的运行时间延迟，避免在关键时候触发，比如可以在场景加载的时候调用GC。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们可以采用三种策略：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;　　1）对游戏进行重构，减少堆内存的分配和引用的分配。更少的变量和引用会减少GC操作中的检测个数从而提高GC的运行效率。

　　2）降低堆内存分配和回收的频率，尤其是在关键时刻。也就是说更少的事件触发GC操作，同时也降低堆内存的碎片化。

　　3）我们可以试着测量GC和堆内存扩展的时间，使其按照可预测的顺序执行。当然这样操作的难度极大，但是这会大大降低GC的影响。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;减少内存垃圾的一些方法：&lt;/p&gt;

&lt;p&gt;1，	缓存变量，达到重复利用的目的，减少不必要的内存垃圾。&lt;/p&gt;

&lt;p&gt;比如，Update或OnTriggerEnter中使用 MeshRenderer meshrender = gameObject.GetComponent&lt;MeshRenderer&gt;()来处理模型渲染类，就会造成不必要的内存垃圾。&lt;/MeshRenderer&gt;&lt;/p&gt;

&lt;p&gt;我们可以在Start或Awake中先缓存起来，然后在Update或OnTriggerEnter中使用。这样我们已经缓存的变量内存一直存在在那，而不会被反复的销毁和分配。&lt;/p&gt;

&lt;p&gt;优化前&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c#&quot; data-lang=&quot;c#&quot;&gt;&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnTriggerEnter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Collider&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;MeshRenderer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;meshRenderer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gameObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MeshRenderer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;ExampleFunction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;meshRenderer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;优化后&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c#&quot; data-lang=&quot;c#&quot;&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MeshRenderer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mMeshRenderer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
 
&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;mMeshRenderer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gameObject&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GetComponent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MeshRenderer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 
&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;OnTriggerEnter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Collider&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;other&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;ExampleFunction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mMeshRenderer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;2， 减少逻辑调用。&lt;/p&gt;

&lt;p&gt;堆内存分配，最坏的情况就是在其反复调用的函数中进行堆内存分配，例如Update()和LateUpdate()函数这种每帧都调用的函数，这会造成大量的内存垃圾。&lt;/p&gt;

&lt;p&gt;我们要想方设法减少逻辑调用，可以利用时间因素，对比是否改变的情况等将Update和LateUpdate中的逻辑调用减少到最低程度。&lt;/p&gt;

&lt;p&gt;例如，用时间因素，决定是否调用逻辑的案例&lt;/p&gt;

&lt;p&gt;优化前，每一帧都在调用逻辑。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c#&quot; data-lang=&quot;c#&quot;&gt;&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;ExampleFunction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;优化后，调用逻辑的间隔延迟到1秒。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c#&quot; data-lang=&quot;c#&quot;&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timeSinceLastCalled&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt;  &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delay&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;timSinceLastCalled&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deltaTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timeSinceLastCalled&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
         &lt;span class=&quot;nf&quot;&gt;ExampleFunction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
         &lt;span class=&quot;n&quot;&gt;timeSinceLastCalled&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;例如，用对比情况，决定是否调用逻辑的案例&lt;/p&gt;

&lt;p&gt;优化前，每帧都在调用逻辑。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c#&quot; data-lang=&quot;c#&quot;&gt;&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;ExampleFunction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;优化后，只在坐标X改变的情况下才调用逻辑。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c#&quot; data-lang=&quot;c#&quot;&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;previousTransformPositionX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;previousTransformPositionX&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;ExampleFunction&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;    
        &lt;span class=&quot;n&quot;&gt;previousTransformPositionX&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;transform&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;通过这样细小的改变，我们可以使得代码运行的更快同时减少内存垃圾的产生。案例中，我们只使用了几个变量的代价，却节省了很多CPU消耗。&lt;/p&gt;

&lt;p&gt;3，	清除链表，而不是不停的生成新的链表。&lt;/p&gt;

&lt;p&gt;在堆内存上进行链表的分配的时候，如果该链表需要多次反复的分配，我们可以采用链表的clear函数来清空链表从而替代反复多次的创建分配链表。&lt;/p&gt;

&lt;p&gt;优化前，每帧都会分配一个链表的内存进行调用。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c#&quot; data-lang=&quot;c#&quot;&gt;&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;myList&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;ExampleFunction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;myList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;       
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;优化后，不再重复分配链表内存，而是将他清理后再使用。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c#&quot; data-lang=&quot;c#&quot;&gt;&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;List&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;myList&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;List&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;myList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Clear&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;nf&quot;&gt;ExampleFunction&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;myList&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;4， 对象池。&lt;/p&gt;

&lt;p&gt;用对象池技术保留废弃的内存变量，在当重复利用时不再需要重新分配内存而是利用对象池内的旧有的对象。&lt;/p&gt;

&lt;p&gt;因为即便我们在代码中尽可能地减少堆内存的分配行为，但是如果游戏有大量的对象需要产生和销毁，依然会造成频繁的GC。&lt;/p&gt;

&lt;p&gt;对象池技术可以通过重复使用对象来降低堆内存的分配和回收频率。&lt;/p&gt;

&lt;p&gt;对象池在游戏中广泛的使用，特别是在游戏中需要频繁的创建和销毁相同的游戏对象的时候，例如枪的子弹这种会频繁生成和销毁的对象。&lt;/p&gt;

&lt;p&gt;5，	字符串。&lt;/p&gt;

&lt;p&gt;在C#中，字符串是引用类型变量而不是值类型变量，即使看起来它是存储字符串的值的。&lt;/p&gt;

&lt;p&gt;这就意味着字符串会造成一定的内存垃圾，由于代码中经常使用字符串，所以我们需要对其格外小心。&lt;/p&gt;

&lt;p&gt;C#中的字符串是不可变更的，也就是说其内部的值在创建后是不可被变更的。&lt;/p&gt;

&lt;p&gt;每次在对字符串进行操作的时候（例如运用字符串的“加”操作），C#会新建一个字符串用来存储新的字符串，使得旧的字符串被废弃，这样就会造成内存垃圾。&lt;/p&gt;

&lt;p&gt;我们可以采用以下的一些方法来最小化字符串的影响：&lt;/p&gt;

&lt;p&gt;1）	减少不必要的字符串的创建，如果一个字符串被多次利用，我们可以创建并缓存该字符串。&lt;/p&gt;

&lt;p&gt;例如项目中常用到的，将文字字符串存储在数据表中，然后由程序去读取数据表，从而将所有常用的字符串存储在内存里。&lt;/p&gt;

&lt;p&gt;这样的话，成员们在调用字符串时就可以直接调用我们存储的字符串了，而不需要去新建一个字符串来操作。&lt;/p&gt;

&lt;p&gt;2）减少不必要的字符串操作。&lt;/p&gt;

&lt;p&gt;例如如果在Text组件中，有一部分字符串需要经常改变，但是其他部分不会，则我们可以将其分为两个部分的组件，对于不变的部分就设置为类似常量字符串即可。&lt;/p&gt;

&lt;p&gt;优化前，每帧都会重新创建一个字符串来设置时间文字。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c#&quot; data-lang=&quot;c#&quot;&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timerText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;timer&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deltaTime&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;timerText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Time:&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;优化后，不再操作字符串，而是赋值给文字组件显示，从而减少了内存垃圾。&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c#&quot; data-lang=&quot;c#&quot;&gt;&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timerHeaderText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Text&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timerValueText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;float&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;timerHeaderText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;TIME:&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
 
&lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Update&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;n&quot;&gt;timerValueText&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;text&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;3）如果我们需要实时的操作字符串，我们可以采用StringBuilderClass来代替，StringBuilder专为不需要进行内存分配而设计，从而减少字符串产生的内存垃圾。&lt;/p&gt;

&lt;p&gt;不过此类方法还是要选择性使用，因为在实际项目中，如果大量的使用StringBuilder，又会产生很多new的操作，导致内存垃圾的产生，同时原本工作量小的字符串操作，又会变成工作量大的StringBuilder函数调用。&lt;/p&gt;

&lt;p&gt;所以只能在小范围特定区域使用，比如，特别频繁的操作字符串的情况下，不断增加，改变字符串的地方。例如，游戏中有对字符串逐步显示的需求，像写文章一样一个个或者一片片的显示，而不是全部一下子显示的需求时，用StringBuilder就恰到好处。&lt;/p&gt;

&lt;p&gt;4）移除游戏中的Debug.Log()函数的代码.&lt;/p&gt;

&lt;p&gt;尽管该函数可能输出为空，对该函数的调用依然会执行，该函数会创建至少一个字符（空字符）的字符串。&lt;/p&gt;

&lt;p&gt;如果游戏中有大量的该函数的调用，这会造成内存垃圾的增加。&lt;/p&gt;

&lt;p&gt;6，	协程。&lt;/p&gt;

&lt;p&gt;调用 StartCoroutine()会产生少量的内存垃圾，因为Unity3D会生成实体来管理协程。任何在游戏关键时刻调用的协程都需要特别的注意，特别是包含延迟回调的协程。&lt;/p&gt;

&lt;p&gt;yield在协程中不会产生堆内存分配，但是如果yield带有参数返回，则会造成不必要的内存垃圾，例如：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    yield return 0;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;由于需要返回0，引发了装箱操作，所以会产生内存垃圾。这种情况下，为了避免内存垃圾，我们可以这样返回：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    yield return null;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;另外一种对协程的错误使用是每次返回的时候都new同一个变量，例如：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c#&quot; data-lang=&quot;c#&quot;&gt;&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isComplete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WaitForSeconds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;我们可以采用缓存来避免这样的内存垃圾产生：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c#&quot; data-lang=&quot;c#&quot;&gt;&lt;span class=&quot;n&quot;&gt;WaitForSeconds&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delay&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;WaiForSeconds&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;1f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;while&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(!&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;isComplete&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;delay&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;7，	Foreach循环。&lt;/p&gt;

&lt;p&gt;在Unity3D 5.5以前的版本中，在foreach的迭代中都会生成内存垃圾，主要来自于其后的装箱操作。&lt;/p&gt;

&lt;p&gt;每次在foreach迭代的时候，都会在堆内存上生产一个System.Object用来实现迭代循环操作。&lt;/p&gt;

&lt;p&gt;如果游戏工程不能升级到5.5以上，则可以用for或者while循环来解决这个问题。&lt;/p&gt;

&lt;p&gt;8，	函数引用。&lt;/p&gt;

&lt;p&gt;函数的引用，无论是指向匿名函数还是显式函数，在Unity3D中都是引用类型变量，这都会在堆内存上进行分配。&lt;/p&gt;

&lt;p&gt;特别是System.Action匿名函数在项目中使用的特别频繁，匿名函数调用完成后都会增加内存的使用和堆内存的分配。&lt;/p&gt;

&lt;p&gt;具体函数的引用和终止都取决于操作平台和编译器设置，但是如果想减少GC最好减少函数的引用，特别是匿名函数。&lt;/p&gt;

&lt;p&gt;9，	LINQ和常量表达式。&lt;/p&gt;

&lt;p&gt;由于LINQ和常量表达式以装箱的方式实现，所以在使用的时候最好进行性能测试。&lt;/p&gt;

&lt;p&gt;如果可以尽量使用其他方式代替LINQ，以家少LINQ对内存垃圾的增加。&lt;/p&gt;

&lt;p&gt;10，	主动调用GC操作。&lt;/p&gt;

&lt;p&gt;如果我们知道堆内存在被分配后并没有被使用，我们希望可以主动地调用GC操作，或者在GC操作并不影响游戏体验的时候（例如场景切换，或读进度条的时候），我们可以主动的调用GC操作System.GC.Collect()。通过主动的调用，我们可以主动驱使GC操作来回收堆内存。让体验不好的时间段放在察觉不到的地方，或者不会被明显察觉的地方。&lt;/p&gt;

&lt;p&gt;参考文献：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    1， Unity优化之GC——合理优化Unity的GC 作者：zblade

    2， Optimizing garbage collection in Unity games 作者：Unity3D
&lt;/code&gt;&lt;/pre&gt;
</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第四章，UI(七) - UI优化(二)</title>
   <link href="http://www.luzexi.com/2018/07/27/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-UI8"/>
   <updated>2018-07-27T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/07/27/Unity3D高级编程之进阶主程-UI8</id>
   <content type="html">&lt;p&gt;这篇我们来继续聊聊优化，UI图集Alpha分离，UI字体拆分，Scroll View 滚屏优化，以及UGUI图在改变颜色或Alpha后，导致对Mesh重构的优化。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;h3 id=&quot;-ui图集alpha分离&quot;&gt;④ UI图集Alpha分离。&lt;/h3&gt;

&lt;h6 id=&quot;为什么要对ui图集进行alpha分离&quot;&gt;为什么要对UI图集进行Alpha分离？&lt;/h6&gt;

&lt;p&gt;我们对UI图集的压缩是减少APP包体大小的一部分，这也是减少内存使用量的一个比较有效方法，内存减少的同时对CPU也会降低些消耗。UI图集的压缩好处很多，但同样也会引起些问题，当我们对图集进行压缩后，在屏幕上显示的效果却不尽如人意，模糊，锯齿，线条等劣质的画面出现。这是因为我们在使用压缩模式ECT或PVRTC时将透明通道也一并压缩进去了，导致了渲染的扭曲，因此我们需要把透明通道alpha分离出来单独压缩。这样就既可以压缩图集，达到缩小内存目的，图像显示又不会太失真。&lt;/p&gt;

&lt;h6 id=&quot;如何分离ui图集的alpha呢&quot;&gt;如何分离UI图集的Alpha呢？&lt;/h6&gt;

&lt;p&gt;我们这里主要是针对NGUI的方案，而UGUI由于是内部集成的，Alpha分离在Unity3D的UGUI中已经帮你完成了，因此我们仅仅针对NGUI来讲一讲如何分离alpha。&lt;/p&gt;

&lt;p&gt;首先，用TexturePacker在打图集时将原来打成2张的图集，改成打成一张RGB888的png和一张Alpha8的png。RGB888的PNG图没有alpha，而所有的alpha通道都在Alpha8的PNG里。也可以使用程序分离的方式，把原图中的颜色提取出来放入一张新的图片中，alpha部分提取出来放入另一张图片。&lt;/p&gt;

&lt;p&gt;然后，我们需要修改NGUI的原始shader，把原来的只绑定一张主图的shader改成需要绑定一张主图和一张Alpha图的shader。&lt;/p&gt;

&lt;p&gt;需要修改下面这4个Shader&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	Unlit – Transparent Colored.shader，

	Unlit – Transparent Colored 1.shader，

	Unlit – Transparent Colored 2.shader，

	Unlit – Transparent Colored 3.shader
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;修改的内容为，加入&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;_AlphaTex (&quot;Alpha (A)&quot;, 2D) = &quot;black&quot; {} 变量，用来可以绑定Alpha图
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;然后在 frag 函数中，有对 alpha 与主图 alpha 操作的内容，都替换成Alpha图中的alpha值。用Alpha图的Alpha，来替代原来主图承担的alpha部分，而主图仍然承担主要色彩内容。具体如下:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	 //fixed4 col = tex2D(_MainTex, i.texcoord) * i.color;
	 //return col;
	 fixed4 texcol = tex2D(_MainTex, i.texcoord);   
	 fixed4 result = texcol;  
	 result.a = tex2D(_AlphaTex,i.texcoord).r*i.color.a;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述代码中，注释掉的部分为原始的只用一张图承担颜色和透明通道的，新加入的方式为用_MainTex和_AlphaTex这两张图分别替代颜色和透明通道。&lt;/p&gt;

&lt;p&gt;最后，在以上都完成后，选中一个创建好的图集 prefab 会发现 Inspector 窗口下的预览窗口以及 Sprite 选择窗口中看到的 sprite 都是没有 alpha 通道显示，这是因为用于Editor下的展示模式仍然使用的原始的一张图使用两个通道的方式，因此我们也需要修改这些编辑器上的NGUI工具。&lt;/p&gt;

&lt;p&gt;修复这个问题我们的解决方案是在编辑器模式下动态生成一个rgba32的texture来替换它，rgb和alpha通道的值分别取两张我们现在拥有的图。&lt;/p&gt;

&lt;p&gt;其中需要修改的NGUI编辑类的有以下这几个文件:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	UIAtlas.cs，

	UIAtlasInspector.cs，

	SpriteSelector.cs，

	NGUITools.cs，

	UISpriteInspector.cs
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;修改以上类里，绘制图片时都启用新生成的图，就是上面所说的用RGB888和Alpha合成的临时图。&lt;/p&gt;

&lt;p&gt;其实修改的部分并不多，修改的方向和原理也简单，首先生成两张图一张只带颜色，一张只带Alpha通道，再是 Shader 的 alpha 来源修改为新的alpha图，最后是 Shader修改导致编辑器的显示问题，需要在编辑器部分生成临时的图来替换原来显示的图。&lt;/p&gt;

&lt;h3 id=&quot;-ui字体拆分&quot;&gt;⑤ UI字体拆分。&lt;/h3&gt;

&lt;p&gt;为什么要拆分UI字体？&lt;/p&gt;

&lt;p&gt;项目中字体其实占了很大的空间，如果有几个不同的字体一起展示在屏幕上，会消耗较大的内存。字体很多时候不可避免，但需要规范和整理，并且也需要优化。我们需要更快的性能效率，拆分字体会让加载字体的速度更快，让场景加载的速度更快。&lt;/p&gt;

&lt;p&gt;如何拆分UI字体？&lt;/p&gt;

&lt;p&gt;我们的解决方案是把字体中的常用字拆出来，另外生成一个字体文件，让字体文件变小，内存变少，最终使得加载变快。&lt;/p&gt;

&lt;p&gt;比如在登陆场景中，我们只需要几个数字和字母，所以我们大可以从字体中提取数字和26个字母成立一个新的字体在场景中应用，这样就省去了大的字体的加载。&lt;/p&gt;

&lt;p&gt;又比如，注册登陆后的取名字的场景，我们去掉部分使用频率比较少的字，只保留3000个常用字。将字体中常用的3000字拆出来，生成新的字体进而用到场景中去。这样就节省掉了很多无用的字形图片存放在字体里，从而节省了不少内存空间。&lt;/p&gt;

&lt;h3 id=&quot;-scroll-view-滚屏优化&quot;&gt;⑥ Scroll View 滚屏优化。&lt;/h3&gt;

&lt;p&gt;Scroll View 使用在类似背包的界面中非常常见，会有巨量的元素存在在窗口中进行渲染，所以在生成和滑动时，会消耗大量的CPU来重构Mesh，进而导致游戏运行缓慢，出现卡顿现象。这是由于我们前面在UGUI源码剖析中介绍过的元素属性上的改变将导致网格的重构，如果不断移动则每帧都需要重构，导致大量的CPU浪费。&lt;/p&gt;

&lt;p&gt;要优化这种情况，就必须对滚屏菜单组件进行改造，将原来策略中所有元素都必须一次性实例化的问题，改为只实例化需要显示的实例数量。然后在拖动滑动的期间，实时判断是否有有UI元素被移出画面，这样的元素可以重复利用，将他们填补到需要显示的位置的上去，再对该单位元素的属性重新设置，我们需要的元素信息，让它展现为在该位置需要显示的元素的样子。&lt;/p&gt;

&lt;p&gt;从表现上观察就如同下面所描述的那样:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	我们在窗口中实例化10排元素显示在那里滚动窗口中，其中5排是展示在中央的窗口上的，另外5排中的顶上2排因为超出了窗口被裁剪而无法看见，

	同样的下面3排也是因为超出了窗口被裁剪无法看见，在整个10排元素整体向上滑动期间，顶上2排变成了3排，底下3排变成了2排，

	其中最顶上的1排超过了重置的界线，就被移动到了底下去了，这样整体10排元素，变成了顶上2排，底下3排的局面，

	这样不断反复，不断在移动顶上或底下的1排元素，把他们移动到需要补充的位置上去。看起来像是，很顺畅地上下滚屏整个500个元素那样，实际上是对这5排元素在不断重复的利用而已。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Scroll View 自定义组件是大部分项目都必须的，大部分项目都会遇到这类问题也会去改善这个问题，有一个自己优化过的自定义组件，能很快很高效的解决这类问题。&lt;/p&gt;

&lt;h3 id=&quot;-ugui图在改变颜色或alpha后导致对mesh重构的优化&quot;&gt;⑦ UGUI图在改变颜色或Alpha后，导致对Mesh重构的优化。&lt;/h3&gt;

&lt;p&gt;这里再稍微解释下为什么在UGUI的图元素在改变颜色或Alpha后会导致Mesh的重构？UGUI的Mesh的合并机制是拥有相同的材质球的Mesh合并在一起才能达到最佳效果，一个材质球对应一个图集，只有相同图集内的图片才需要合并在一起。&lt;/p&gt;

&lt;p&gt;UGUI中当元素需要对颜色进行改变时，UGUI是通过改变顶点的颜色来实现颜色的变化的。改变当前元素的顶点颜色，然后需要将它们重新合并到整块的Mesh里去，因为不能直接从原来合并好的Mesh上找到当前的顶点位置，所以需要一次整体的合并重构Mesh。&lt;/p&gt;

&lt;p&gt;元素改变了 alpha 则会更糟糕，由于改变 alpha 的效果无法通过改变顶点的颜色来实现，于是就需要拆分出一个另外的材质球来进行渲染，通过对材质球的参数改变来实现 alpha 的效果。这样做不但重构了Mesh，还多出来个材质球，就相当于多一个Drawcall，效率消耗相当大。&lt;/p&gt;

&lt;p&gt;倘若在动画里，每一帧都对UGUI的颜色和Alpha进行改变，那么UGUI每一帧都会对Mesh进行重构一次，并且每帧都生成新的材质球来实现 alpha 的透明效果。这样做消耗了大量的CPU运算，通常使得UI界面在运行动画时效率特别低下，即使拆分动静分离也无济于事。&lt;/p&gt;

&lt;p&gt;如何对此做优化呢？我们不希望在UI颜色改变时，导致Mesh重构，这样动画中消耗掉太多CPU，那么我们就自己建一个材质球，提前告诉UGUI：我们使用自己的特殊的材质球进行渲染。当颜色动画对颜色和 alpha 更改时，我们直接对我们自定义的材质球进行颜色和 alpha 的改变。这样UGUI就不需要重构Mesh了，因为把渲染的工作交给了新的材质球，而不是通过 UGUI 设置顶点颜色和新材质球来达到效果。&lt;/p&gt;

&lt;p&gt;如何操作？&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	首先，我们需要把UGUI的Shader下载下来。

	然后，建立一个自己的材质球，并且材质球里使用下载下来的UGUI的Shader。

	再次，把这个材质球放入Image或RawImage的Material上去，与Image或RawImage绑定。

	接着，写个类比如class ImageColor继承MonoBehaviour，里面有个public 的颜色变量，比如public Color mColor，类里面只干一件事，在update里一直判断是否需要更改颜色，如果颜色被更改，就把颜色赋值给Material。

	最后，把动画文件中的颜色部分从更改Image或RawImage的颜色变为更改 ImageColor 的颜色变量。这样UGUI颜色动画在播放时，不会直接去改变 Image 或 RawImage 的颜色，改变的是我们创建的 ImageColor 的颜色。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;通过 ImageColor 来改变材质球属性，最后达到不重构Mesh的效果。切换元素的贴图时也一样可以做到不重构的效果，由于贴图更换会导致重构，为了达到不重构的目的可以给一个自定义材质球的并且更换材质球中的贴图。&lt;/p&gt;

&lt;p&gt;不过要注意下，因为启用了自定义的材质球，所以Drawcall就提高了，因为每个材质球都会单独增加一次Drawcall。并且当 alpha 不是1的时候，会与原有的UGUI产生的材质球的透贴形成不同的渲染排序，原因是当两张透贴放在一起渲染时，alpha混合会导致渲染排序混乱而前后不一致。所以使用时要小心谨慎，权衡利弊用在恰当的地方将发挥大的功效，用在不恰当的地方则事倍功半。&lt;/p&gt;

&lt;p&gt;这个半透明物体的排序问题，归根结底是无法写入深度数据问题，是3D渲染中无法彻底解决的问题。我们会在后面的渲染管线与图形学章节中详细介绍。这里解决半透明排序问题，可以通过改变自定义的 Shader 中的渲染次序(RenderQueue)来解决。&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第四章，UI(七) - UI优化(一)</title>
   <link href="http://www.luzexi.com/2018/07/27/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-UI7"/>
   <updated>2018-07-27T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/07/27/Unity3D高级编程之进阶主程-UI7</id>
   <content type="html">&lt;p&gt;这篇我们来聊聊，优化UI的几种方法，UI动静分离，拆分过大的UI，UI预加载。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;h3 id=&quot;-ui动静分离&quot;&gt;① UI动静分离。&lt;/h3&gt;

&lt;p&gt;什么是UI动静分离？&lt;/p&gt;

&lt;p&gt;动指的是元素移动，或放大缩小频率比较高的UI，静就是静止不动的，或者说动的比较少的UI。&lt;/p&gt;

&lt;p&gt;我们在做项目中，避免不了一些UI会动的而且是不停的动的UI元素，这些一直在动的UI元素就是UI性能的祸害。&lt;/p&gt;

&lt;p&gt;那么为什么要将他们分离开来呢？&lt;/p&gt;

&lt;p&gt;UGUI 和 NGUI一样，都是用模型构建UI画面的，在构建后都做了合并Mesh的有优化操作，不合并会导致无数drawcall进而导致GPU队列阻塞或消耗加大，游戏性能降低。&lt;/p&gt;

&lt;p&gt;合并操作是有极大益处的，但问题在于UI元素一动就需要重新合并，将那些原本不需要重新构建的内容也一并重构了，导致原来合并Mesh的好事变坏事。&lt;/p&gt;

&lt;p&gt;因此要将行动的UI元素和静态不动的UI元素分离开来，让合并的范围缩小，只合并那些会动的UI元素，因为他们重绘的频率比较高，而那些基本不动的UI元素就不让它们参与重新合并Mesh的操作了。&lt;/p&gt;

&lt;p&gt;那么如何分离他们呢？&lt;/p&gt;

&lt;p&gt;UGUI 和 NGUI都有自己的重绘合并节点，我们可以称它们为画板，UGUI是Canvas，NGUI是UIPanel。&lt;/p&gt;

&lt;p&gt;以画板为节点进行拆分。把会动的UI元素放入专门为它们准备的合并节点上，而将静止不动的UI留在原来的合并节点上。&lt;/p&gt;

&lt;p&gt;这样一来，当会动的UI元素来回移动缩放的时候，不再会重构静态部分的UI了。在实际项目中静态的UI元素占UI的数量比较多，而动态的UI元素只是小部分。动静分离后，减少了不少的CPU在重绘和合并时的消耗。&lt;/p&gt;

&lt;h3 id=&quot;-拆分过大的ui&quot;&gt;② 拆分过大的UI。&lt;/h3&gt;

&lt;h6 id=&quot;为什么要拆分过大的ui&quot;&gt;为什么要拆分过大的UI？&lt;/h6&gt;

&lt;p&gt;项目的制作过程是个比较长期的时间过程，在这个过程中UI的大小会随着项目时间的积累而不断扩大。&lt;/p&gt;

&lt;p&gt;很多时候我们总是莫名其妙的感觉，‘怎么这个UI界面，前段时间还好好的，现在打开会变得如何缓慢呢？！‘。&lt;/p&gt;

&lt;p&gt;随着项目的推进，UI经手的人越来越多，添加的功能也越来越多，有的甚至一个Prefab里，装着2-3个界面。它们在展示一个界面时时隐藏了其他的几个而已，最后导致UI过大，实例化，初始化时，消耗的CPU过大。我们需要想办法拆分这些，过大的UI界面。&lt;/p&gt;

&lt;h6 id=&quot;如何拆分&quot;&gt;如何拆分？&lt;/h6&gt;

&lt;p&gt;把隐藏的UI界面拆分出来，成为独立运作的界面，只在需要它们时才调用并实例化。其次，如果界面内容还是很多，我们可以把2次显示的内容拆出来。&lt;/p&gt;

&lt;p&gt;什么是2次内容？打个比方，一个界面打开时会显示一些内容(例如动画)，完毕后或者点击后才能看到另外的内容。这之后出现的内容视为2次显示内容，可以考虑拆分出来成为独立的界面，需要时再加载。&lt;/p&gt;

&lt;p&gt;注意权衡加载速度与内存，过大的UI固然加载缓慢内存消耗大，但拆分成小个体时，如果小个体频繁加载和销毁，也同样会消耗过多CPU。如果加载和销毁过于频繁，我们可以使用后面介绍的优化方法，把它们存起来不销毁。&lt;/p&gt;

&lt;h3 id=&quot;-ui预加载&quot;&gt;③ UI预加载。&lt;/h3&gt;

&lt;p&gt;为什么要进行UI的预加载？&lt;/p&gt;

&lt;p&gt;我们在UI实例化时，需要将Prefab实例化到场景中，这期间还会有Mesh的合并，组件的初始化，渲染初始化，图片的加载，界面逻辑的初始化等程序调用，消耗掉了很多CPU。这导致了在我们打开某个界面时，出现卡顿的现象，就是CPU消耗过重的表现。&lt;/p&gt;

&lt;p&gt;上面讲的拆分UI是一个方面，不过只能在一些冗余比较大的界面上做优化，而一些容易比较小，难以拆分的UI界面，就很难再用拆分的方法优化效果。甚至有的UI界面即使拆分后，任然会消耗很多CPU。因此我们使用UI预加载，在游戏开始前加载一些UI界面，让实例化的消耗在游戏前平均分摊在等待的时间线上。&lt;/p&gt;

&lt;h6 id=&quot;如何进行ui预加载&quot;&gt;如何进行UI预加载？&lt;/h6&gt;

&lt;p&gt;第一步，最直接的方法，在游戏开始前加载UI资源但不实例化，只是把资源加载到内存。这样当点击按钮后，弹出UI界面时就少了一点加载资源的时间，把CPU消耗重心放在了实例化和初始化上。&lt;/p&gt;

&lt;p&gt;第二步，在第一种方法的基础上，打开界面时CPU还是消耗太严重，那么就将UI实例化和初始化也提前到游戏开始前。只是实例化和初始化后，对UI界面进行了隐藏，当需要他出现时，再显示出来，而不再重新实例化，当关闭时，也同样只是隐藏而不是销毁。这样一来在打开和关闭时，只消耗了少量CPU在展示和隐藏上。&lt;/p&gt;

&lt;p&gt;现在项目大都使用 AssetBundle 来做资源，但也有部分使用 Unity3D 的本地打包机制，这些prefab在Unity3D中有Preload的功能，在平台设置里这个功能，可以把需要预加载的Prefab加入到列表中去。它会将这些Prefab在进入APP或者说打开应用展示LOGO界面时进行预加载。在APP初始化时，预加载了指定的Prefab，CPU消耗在启动页面上，对于使用Resources.Load接口的加载整体效果不错。&lt;/p&gt;

&lt;p&gt;最后，所有的预加载，都会出现另一个问题，CPU集中消耗带来的卡顿。预加载并没有削减CPU，CPU消耗的总量并没有发生变化。总体需要加载的图片数是不变的，实例化的元素数不变，以及初始化程序需要消耗的时间也不变，所有消耗总量是不变的。我们只是把它们这些消耗分离了或者说提前了，拆分到了各个时间碎片里去，让人感觉不到一瞬间有很大的CPU消耗。所以如果我们将这些预加载，集中在了某个位置，比如全部集中在游戏开始前，或者进度条的某个位置，也同样会有强烈的卡顿感，因为CPU在这个点进行了集中的消耗。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第四章，UI(四) - UGUI核心源码剖析</title>
   <link href="http://www.luzexi.com/2018/07/27/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-UI4"/>
   <updated>2018-07-27T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/07/27/Unity3D高级编程之进阶主程-UI4</id>
   <content type="html">&lt;p&gt;前面我们对NGUI和UGUI进行了比较，讲述了UGUI的组件使用详解以及一些内在运作机制，又对UGUI源码中输入事件模块源码进行了剖析。此篇我们接着上篇的源码剖析，讲解下UGUI组件部分的核心源码。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;h3 id=&quot;ugui核心源码剖析&quot;&gt;UGUI核心源码剖析&lt;/h3&gt;

&lt;p&gt;我们依然从文件夹结构下手，从最容易看懂的地方下手，寻找某块之间的划分，我们先来看下核心部分的文件结构，如下图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/4/ugui3.png&quot; alt=&quot;ugui-core&quot; /&gt;&lt;/p&gt;

&lt;p&gt;从图中可以看出，以文件夹为单位，拆分模块有，Culling(裁剪), Layout(布局), MaterialModifiers(材质球修改器), SpecializedCollections(收集), Utility(实用工具), VertexModifiers(顶点修改器)，我们下面就来分析这些模块.&lt;/p&gt;

&lt;h3 id=&quot;culling-裁剪模块&quot;&gt;Culling 裁剪模块&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/4/ugui4.png&quot; alt=&quot;ugui-core&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Culling 里是对模型裁剪的工具类，大都用在了 Mask 遮罩上，只有 Mask 才有裁剪的需求。&lt;/p&gt;

&lt;p&gt;里面四个文件，其中一个是静态类，一个是接口类。&lt;/p&gt;

&lt;p&gt;代码不多，但其中Clipping 类中有2个函数比较重要，常常被用在Mask的裁剪上，如下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
public static Rect FindCullAndClipWorldRect(List&amp;lt;RectMask2D&amp;gt; rectMaskParents, out bool validRect)
{
    if (rectMaskParents.Count == 0)
    {
        validRect = false;
        return new Rect();
    }

    var compoundRect = rectMaskParents[0].canvasRect;
    for (var i = 0; i &amp;lt; rectMaskParents.Count; ++i)
        compoundRect = RectIntersect(compoundRect, rectMaskParents[i].canvasRect);

    var cull = compoundRect.width &amp;lt;= 0 || compoundRect.height &amp;lt;= 0;
    if (cull)
    {
        validRect = false;
        return new Rect();
    }

    Vector3 point1 = new Vector3(compoundRect.x, compoundRect.y, 0.0f);
    Vector3 point2 = new Vector3(compoundRect.x + compoundRect.width, compoundRect.y + compoundRect.height, 0.0f);
    validRect = true;
    return new Rect(point1.x, point1.y, point2.x - point1.x, point2.y - point1.y);
}

private static Rect RectIntersect(Rect a, Rect b)
{
    float xMin = Mathf.Max(a.x, b.x);
    float xMax = Mathf.Min(a.x + a.width, b.x + b.width);
    float yMin = Mathf.Max(a.y, b.y);
    float yMax = Mathf.Min(a.y + a.height, b.y + b.height);
    if (xMax &amp;gt;= xMin &amp;amp;&amp;amp; yMax &amp;gt;= yMin)
        return new Rect(xMin, yMin, xMax - xMin, yMax - yMin);
    return new Rect(0f, 0f, 0f, 0f);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述中，Clipping 类的函数里，第一个函数 FindCullAndClipWorldRect 的意义是，将很多 RectMask2D 重叠部分，计算出它们的重叠部分的区域。第二个函数 RectIntersect 为第一函数提供了计算服务，其意义是计算两个矩阵的重叠部分。&lt;/p&gt;

&lt;p&gt;这两个函数都是静态函数，可以视为工具函数，直接调用就可以，不需要实例化。&lt;/p&gt;

&lt;h3 id=&quot;layout-布局模块&quot;&gt;Layout 布局模块&lt;/h3&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/4/ugui5.png&quot; alt=&quot;ugui-core&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们从文件夹结构可以看出，Layout 主要功能都是布局方面，包括横向布局，纵向布局，方格布局等等。总共12个文件，有9个带有 Layout 字样，它们都是处理布局的。&lt;/p&gt;

&lt;p&gt;除了处理布局内容以外，其余3个文件，CanvasScaler，AspectRatioFitter，ContentSizeFitter 则是调整自适应功能。&lt;/p&gt;

&lt;p&gt;从 ContentSizeFitter，AspectRatioFitter 都带有 Fitter 字样可以了解到，它们的功能都是处理自适应。其中 ContentSizeFitter 处理的是内容自适应的， 而 AspectRatioFitter 处理的是朝向自适应的，包括以长度为基准的，以宽度为基准的，以父节点为基准的，以外层父节点为基准的自适应，四种类型的自适应方式。&lt;/p&gt;

&lt;p&gt;另外 CanvasScaler 做的功能非常重要，它操作的是 Canvas 整个画布针对不同屏幕进行的自适应调整。&lt;/p&gt;

&lt;p&gt;我们着重来看看 CanvasScaler 里的代码，其CanvasScaler的核心函数:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
protected virtual void HandleScaleWithScreenSize()
{
    Vector2 screenSize = new Vector2(Screen.width, Screen.height);

    float scaleFactor = 0;
    switch (m_ScreenMatchMode)
    {
        case ScreenMatchMode.MatchWidthOrHeight:
        {
            // We take the log of the relative width and height before taking the average.
            // Then we transform it back in the original space.
            // the reason to transform in and out of logarithmic space is to have better behavior.
            // If one axis has twice resolution and the other has half, it should even out if widthOrHeight value is at 0.5.
            // In normal space the average would be (0.5 + 2) / 2 = 1.25
            // In logarithmic space the average is (-1 + 1) / 2 = 0
            float logWidth = Mathf.Log(screenSize.x / m_ReferenceResolution.x, kLogBase);
            float logHeight = Mathf.Log(screenSize.y / m_ReferenceResolution.y, kLogBase);
            float logWeightedAverage = Mathf.Lerp(logWidth, logHeight, m_MatchWidthOrHeight);
            scaleFactor = Mathf.Pow(kLogBase, logWeightedAverage);
            break;
        }
        case ScreenMatchMode.Expand:
        {
            scaleFactor = Mathf.Min(screenSize.x / m_ReferenceResolution.x, screenSize.y / m_ReferenceResolution.y);
            break;
        }
        case ScreenMatchMode.Shrink:
        {
            scaleFactor = Mathf.Max(screenSize.x / m_ReferenceResolution.x, screenSize.y / m_ReferenceResolution.y);
            break;
        }
    }

    SetScaleFactor(scaleFactor);
    SetReferencePixelsPerUnit(m_ReferencePixelsPerUnit);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;不同 ScreenMathMode 模式下 CanvasScaler 对屏幕的适应算法，包括优先匹配长或宽的，最小化固定拉伸的，以及最大化固定拉伸三种数学计算方式。其中代码中在优先匹配长或宽算法中，介绍了使用Log和Pow来计算缩放比例可以表现的更好。&lt;/p&gt;

&lt;h3 id=&quot;materialmodifiers-specializedcollections-utility&quot;&gt;MaterialModifiers, SpecializedCollections, Utility&lt;/h3&gt;

&lt;p&gt;材质球修改器，特殊收集器，实用工具，这三块相对代码量少却很重要，他们是其他模块所依赖的工具。&lt;/p&gt;

&lt;p&gt;文件夹结构如下图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/4/ugui6.png&quot; alt=&quot;ugui-core&quot; /&gt;&lt;/p&gt;

&lt;p&gt;IMaterialModifier 是一个接口类，为Mask 遮罩修改材质球所准备的，但所用方法都需要各自实现。&lt;/p&gt;

&lt;p&gt;IndexedSet 是一个容器，在很多核心代码上都有使用，它加速了移除元素的速度，以及加速了元素包含判断。&lt;/p&gt;

&lt;p&gt;ListPool是List容器对象池，ObjectPool是普通对象池，很多代码上都用到了它们，对象池让内存利用率更高。&lt;/p&gt;

&lt;p&gt;VertexHelper 特别重要，它是用来存储生成 Mesh 网格需要的所有数据，由于在Mesh生成的过程中顶点的生成频率非常高，因此 VertexHelper 存储了 Mesh 的所有相关数据的同时，用上面提到的ListPool和ObjectPool做为对象池来生成和销毁，使得数据高效得被重复利用，不过它并不负责计算和生成 Mesh，计算和生成由各自图形组件来完成，它只为它们提供计算后的数据存储服务。&lt;/p&gt;

&lt;h3 id=&quot;vertexmodifiers&quot;&gt;VertexModifiers&lt;/h3&gt;

&lt;p&gt;顶点修改器为效果制作提供了更多基础方法和规则。&lt;/p&gt;

&lt;p&gt;文件夹结构如下图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/4/ugui7.png&quot; alt=&quot;ugui-core&quot; /&gt;&lt;/p&gt;

&lt;p&gt;VertexModifiers 模块，主要用于修改图形网格，尤其是在UI元素网格生成完毕后对其进行二次修改。&lt;/p&gt;

&lt;p&gt;其中 BaseMeshEffect 是抽象基类，提供所有在修改UI元素网格时所需的变量和接口。&lt;/p&gt;

&lt;p&gt;IMeshModifier 是关键接口，在下面的渲染核心类 Graphic 中会获取所有拥有这个接口的组件，然后依次遍历并调用 ModifyMesh 接口来触发改变图像网格的效果。&lt;/p&gt;

&lt;p&gt;当前在源码中拥有的二次效果包括，Outline(包边框)，Shadow(阴影)，PositionAsUV1(位置UV) 都继承了 BaseMeshEffect 基类，并实现了关键接口 ModifyMesh。其中 Outline 继承自 Shadow， 他们的共同的关键代码，我们可以重点看一下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
protected void ApplyShadowZeroAlloc(List&amp;lt;UIVertex&amp;gt; verts, Color32 color, int start, int end, float x, float y)
{
    UIVertex vt;

    var neededCpacity = verts.Count * 2;
    if (verts.Capacity &amp;lt; neededCpacity)
        verts.Capacity = neededCpacity;

    for (int i = start; i &amp;lt; end; ++i)
    {
        vt = verts[i];
        verts.Add(vt);

        Vector3 v = vt.position;
        v.x += x;
        v.y += y;
        vt.position = v;
        var newColor = color;
        if (m_UseGraphicAlpha)
            newColor.a = (byte)((newColor.a * verts[i].color.a) / 255);
        vt.color = newColor;
        verts[i] = vt;
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;此函数作用是，在原有的Mesh顶点基础上，加入新的顶点，这些新的顶点复制了原来的顶点数据，修改颜色并向外扩充，使得原图形外渲染出外描边或者阴影。&lt;/p&gt;

&lt;h3 id=&quot;核心渲染类&quot;&gt;核心渲染类&lt;/h3&gt;

&lt;p&gt;现在我们来看看核心渲染类的奥秘所在。&lt;/p&gt;

&lt;p&gt;我们常用的组件 Image，RawImage，Mask，RectMask2D，Text，InputField 中，Image，RawImage，Text 都是继承了 MaskableGraphic ，而 MaskableGraphic 又继承自 Graphic 类，这里 Graphic 类相对比较重要，是基础类也存些核心算法。除了这几个类外 CanvasUpdateRegistry 是存储和管理所有可绘制元素的管理类也是个蛮重要的类，我们会在下面的内容中介绍。&lt;/p&gt;

&lt;h6 id=&quot;我们首先来看-graphic-核心部分它有两个地方比较重要这两个地方揭示了-graphic-的运作机制&quot;&gt;我们首先来看 Graphic 核心部分，它有两个地方比较重要，这两个地方揭示了 Graphic 的运作机制。&lt;/h6&gt;

&lt;p&gt;第一个如下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
public virtual void SetAllDirty()
{
    SetLayoutDirty();
    SetVerticesDirty();
    SetMaterialDirty();
}

public virtual void SetLayoutDirty()
{
    if (!IsActive())
        return;

    LayoutRebuilder.MarkLayoutForRebuild(rectTransform);

    if (m_OnDirtyLayoutCallback != null)
        m_OnDirtyLayoutCallback();
}

public virtual void SetVerticesDirty()
{
    if (!IsActive())
        return;

    m_VertsDirty = true;
    CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this);

    if (m_OnDirtyVertsCallback != null)
        m_OnDirtyVertsCallback();
}

public virtual void SetMaterialDirty()
{
    if (!IsActive())
        return;

    m_MaterialDirty = true;
    CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild(this);

    if (m_OnDirtyMaterialCallback != null)
        m_OnDirtyMaterialCallback();
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述代码中，SetAllDirty 设置并通知元素需要重新布局、重新构建网格、以及重新构建材质球。 它通知 LayoutRebuilder 布局管理类进行重新布局，在 LayoutRebuilder.MarkLayoutForRebuild 中它调用 CanvasUpdateRegistry.TryRegisterCanvasElementForLayoutRebuild 加入重构队伍，最终重构布局。&lt;/p&gt;

&lt;p&gt;SetLayoutDirty、SetVerticesDirty、SetMaterialDirty 都调用了 CanvasUpdateRegistry.RegisterCanvasElementForGraphicRebuild，它被调用时可以认为是通知它去重新重构Mesh，但它并没有立即重新构建，而是将需要重构的元件数据加入到IndexedSet&lt;ICanvasElement&gt;容器中，等待下次重构。注意，CanvasUpdateRegistry 只负责重构Mesh网格，并不负责渲染和合并。我们来看看 CanvasUpdateRegistry 的RegisterCanvasElementForGraphicRebuild 函数部分:&lt;/ICanvasElement&gt;&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
public static void RegisterCanvasElementForGraphicRebuild(ICanvasElement element)
{
    instance.InternalRegisterCanvasElementForGraphicRebuild(element);
}

public static bool TryRegisterCanvasElementForGraphicRebuild(ICanvasElement element)
{
    return instance.InternalRegisterCanvasElementForGraphicRebuild(element);
}

private bool InternalRegisterCanvasElementForGraphicRebuild(ICanvasElement element)
{
    if (m_PerformingGraphicUpdate)
    {
        Debug.LogError(string.Format(&quot;Trying to add {0} for graphic rebuild while we are already inside a graphic rebuild loop. This is not supported.&quot;, element));
        return false;
    }

    if (m_GraphicRebuildQueue.Contains(element))
        return false;

    m_GraphicRebuildQueue.Add(element);
    return true;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述代码中，InternalRegisterCanvasElementForGraphicRebuild 将元素放入重构队列中等待下一次重构。&lt;/p&gt;

&lt;p&gt;以及重构时的逻辑:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
private static readonly Comparison&amp;lt;ICanvasElement&amp;gt; s_SortLayoutFunction = SortLayoutList;
private void PerformUpdate()
{
    CleanInvalidItems();

    m_PerformingLayoutUpdate = true;

    //布局重构
    m_LayoutRebuildQueue.Sort(s_SortLayoutFunction);
    for (int i = 0; i &amp;lt;= (int)CanvasUpdate.PostLayout; i++)
    {
        for (int j = 0; j &amp;lt; m_LayoutRebuildQueue.Count; j++)
        {
            var rebuild = instance.m_LayoutRebuildQueue[j];
            try
            {
                if (ObjectValidForUpdate(rebuild))
                    rebuild.Rebuild((CanvasUpdate)i);
            }
            catch (Exception e)
            {
                Debug.LogException(e, rebuild.transform);
            }
        }
    }

    for (int i = 0; i &amp;lt; m_LayoutRebuildQueue.Count; ++i)
        m_LayoutRebuildQueue[i].LayoutComplete();

    instance.m_LayoutRebuildQueue.Clear();
    m_PerformingLayoutUpdate = false;

    // 裁剪
    // now layout is complete do culling...
    ClipperRegistry.instance.Cull();

    //元素重构
    m_PerformingGraphicUpdate = true;
    for (var i = (int)CanvasUpdate.PreRender; i &amp;lt; (int)CanvasUpdate.MaxUpdateValue; i++)
    {
        for (var k = 0; k &amp;lt; instance.m_GraphicRebuildQueue.Count; k++)
        {
            try
            {
                var element = instance.m_GraphicRebuildQueue[k];
                if (ObjectValidForUpdate(element))
                    element.Rebuild((CanvasUpdate)i);
            }
            catch (Exception e)
            {
                Debug.LogException(e, instance.m_GraphicRebuildQueue[k].transform);
            }
        }
    }

    for (int i = 0; i &amp;lt; m_GraphicRebuildQueue.Count; ++i)
        m_GraphicRebuildQueue[i].LayoutComplete();

    instance.m_GraphicRebuildQueue.Clear();
    m_PerformingGraphicUpdate = false;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述代码中，PerformUpdate 为 CanvasUpdateRegistry 在重构调用中的逻辑。先将需要重新布局的元素取出来一个个调用Rebuild 函数重构，再对布局后的元素进行裁剪，裁剪后对布局中每个需要重构的元素取出来调用 Rebuild 函数进行重构，最后做一些清理的事务。&lt;/p&gt;

&lt;h6 id=&quot;我们再来看看-graphic-另一个重要的函数即执行网格构建函数&quot;&gt;我们再来看看 Graphic 另一个重要的函数，即执行网格构建函数。&lt;/h6&gt;

&lt;p&gt;如下代码：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
private void DoMeshGeneration()
{
    if (rectTransform != null &amp;amp;&amp;amp; rectTransform.rect.width &amp;gt;= 0 &amp;amp;&amp;amp; rectTransform.rect.height &amp;gt;= 0)
        OnPopulateMesh(s_VertexHelper);
    else
        s_VertexHelper.Clear(); // clear the vertex helper so invalid graphics dont draw.

    var components = ListPool&amp;lt;Component&amp;gt;.Get();
    GetComponents(typeof(IMeshModifier), components);

    for (var i = 0; i &amp;lt; components.Count; i++)
        ((IMeshModifier)components[i]).ModifyMesh(s_VertexHelper);

    ListPool&amp;lt;Component&amp;gt;.Release(components);

    s_VertexHelper.FillMesh(workerMesh);
    canvasRenderer.SetMesh(workerMesh);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;此段代码是 Graphic 构建 Mesh 的部分，先调用OnPopulateMesh创建自己的Mesh网格，然后调用所有需要修改 Mesh 的修改者(IMeshModifier)也就是网格后处理组件(描边等效果组件)进行修改，最后放入 CanvasRenderer 。&lt;/p&gt;

&lt;p&gt;其中 CanvasRenderer 是每个绘制元素都必须有的组件，它是画布与渲染的连接组件，通过 CanvasRenderer 我们才能把网格绘制到 Canvas 画布上去。&lt;/p&gt;

&lt;p&gt;这里使用 VertexHelper 是为了节省内存和CPU消耗，它内部采用List容器对象池，将所有使用过的废弃的数据都存储在里pool池子的容器中，当需要时再拿旧的继续使用。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;public class VertexHelper : IDisposable
{
    private List&amp;lt;Vector3&amp;gt; m_Positions = ListPool&amp;lt;Vector3&amp;gt;.Get();
    private List&amp;lt;Color32&amp;gt; m_Colors = ListPool&amp;lt;Color32&amp;gt;.Get();
    private List&amp;lt;Vector2&amp;gt; m_Uv0S = ListPool&amp;lt;Vector2&amp;gt;.Get();
    private List&amp;lt;Vector2&amp;gt; m_Uv1S = ListPool&amp;lt;Vector2&amp;gt;.Get();
    private List&amp;lt;Vector3&amp;gt; m_Normals = ListPool&amp;lt;Vector3&amp;gt;.Get();
    private List&amp;lt;Vector4&amp;gt; m_Tangents = ListPool&amp;lt;Vector4&amp;gt;.Get();
    private List&amp;lt;int&amp;gt; m_Indicies = ListPool&amp;lt;int&amp;gt;.Get();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述代码为 VertexHelper 的定义部分。&lt;/p&gt;

&lt;p&gt;组件中，Image, RawImage, Text 都override重写了 OnPopulateMesh 函数。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    protected override void OnPopulateMesh(VertexHelper toFill)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;因为这些需要有自己自定义的网格样式，构建不同类型的画面。&lt;/p&gt;

&lt;p&gt;其实 CanvasRenderer 和 Canvas 才是合并Mesh网格的关键，但 CanvasRenderer 和 Canvas 并没有开源出来。并且从源码上看，他们是 C++ 编写的，从另外dll或so引进来。&lt;/p&gt;

&lt;p&gt;我试图通过查找反编译的代码查看相关内容，也没有找到，我们无法获得这部分的源码。但仔细一想，也差不多能想个大概。合并部分无非就是每次重构时获取 Canvas 下面所有的 CanvasRenderer 实例，将它们的 Mesh 合并起来，仅此而已。因此关键还是要看，如何减少重构次数，以及提高内存，CPU使用效率。&lt;/p&gt;

&lt;h6 id=&quot;除了-graphic类遮罩部分也是我们非常关心的问题我们继续看-mask-遮罩部分的核心部分&quot;&gt;除了 Graphic类，遮罩部分也是我们非常关心的问题，我们继续看 Mask 遮罩部分的核心部分：&lt;/h6&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
var maskMaterial = StencilMaterial.Add(baseMaterial, 1, StencilOp.Replace, CompareFunction.Always, m_ShowMaskGraphic ? ColorWriteMask.All : 0);
StencilMaterial.Remove(m_MaskMaterial);
m_MaskMaterial = maskMaterial;

var unmaskMaterial = StencilMaterial.Add(baseMaterial, 1, StencilOp.Zero, CompareFunction.Always, 0);
StencilMaterial.Remove(m_UnmaskMaterial);
m_UnmaskMaterial = unmaskMaterial;
graphic.canvasRenderer.popMaterialCount = 1;
graphic.canvasRenderer.SetPopMaterial(m_UnmaskMaterial, 0);

return m_MaskMaterial;

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;从上述代码中看出来，Mask 组件调用了模板材质球构建了一个自己的材质球，因此它使用了实时渲染中的模板方法来裁切不需要显示的部分，所有在 Mask 组件后面的物体都会进行裁切。我们可以说 Mask 是在 GPU 中做的裁切，使用的方法是着色器中的模板方法。&lt;/p&gt;

&lt;p&gt;不过 RectMask2D 并不和 Mask 一样。我们来看 RectMask2D 核心部分源码：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
public virtual void PerformClipping()
{
    // if the parents are changed
    // or something similar we
    // do a recalculate here
    if (m_ShouldRecalculateClipRects)
    {
        MaskUtilities.GetRectMasksForClip(this, m_Clippers);
        m_ShouldRecalculateClipRects = false;
    }

    // get the compound rects from
    // the clippers that are valid
    bool validRect = true;
    Rect clipRect = Clipping.FindCullAndClipWorldRect(m_Clippers, out validRect);
    if (clipRect != m_LastClipRectCanvasSpace)
    {
        for (int i = 0; i &amp;lt; m_ClipTargets.Count; ++i)
            m_ClipTargets[i].SetClipRect(clipRect, validRect);

        m_LastClipRectCanvasSpace = clipRect;
        m_LastClipRectValid = validRect;
    }

    for (int i = 0; i &amp;lt; m_ClipTargets.Count; ++i)
        m_ClipTargets[i].Cull(m_LastClipRectCanvasSpace, m_LastClipRectValid);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述源码中我们可以看到，RectMask2D 会先计算并设置裁切的范围，再对所有子节点调用裁切操作。其中:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    MaskUtilities.GetRectMasksForClip(this, m_Clippers);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;获取了所有有关联的 RectMask2D 遮罩范围，然后&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    Rect clipRect = Clipping.FindCullAndClipWorldRect(m_Clippers, out validRect);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;计算了需要裁切的部分，实际上是计算了不需要裁切的部分，其他部分都进行裁切。最后&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
        for (int i = 0; i &amp;lt; m_ClipTargets.Count; ++i)
            m_ClipTargets[i].SetClipRect(clipRect, validRect);

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;对所有需要裁切的UI元素，进行裁切操作。其中 SetClipRect 裁切操作的源码，如下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
public virtual void SetClipRect(Rect clipRect, bool validRect)
{
    if (validRect)
        canvasRenderer.EnableRectClipping(clipRect);
    else
        canvasRenderer.DisableRectClipping();
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;最后操作是在 CanvasRenderer 中进行的。前面说过 CanvasRenderer 是我们无法得知内容。不过我们可以想到这里面的内容，计算两个四边形的相交点，再组合成裁切后的内容。&lt;/p&gt;

&lt;h3 id=&quot;至此我们把-ugui-的源代码都剖析完毕了其实并没有高深的算法或者技术所有核心部分都围绕着如何构建mesh谁将重构以及如何裁切的问题上很多性能关键在于如何减少重构次数以及提高内存和cpu的使用效率&quot;&gt;至此我们把 UGUI 的源代码都剖析完毕了。其实并没有高深的算法或者技术。所有核心部分都围绕着，如何构建Mesh，谁将重构，以及如何裁切的问题上。很多性能关键在于，如何减少重构次数，以及提高内存和CPU的使用效率。&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://bitbucket.org/Unity-Technologies/ui/downloads/?tab=downloads&quot;&gt;UGUI源码地址&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/Unity-Technologies/UnityCsReference/tree/master/Runtime&quot;&gt;Unity3D的C#部分开源代码地址&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>重写Jekyll的Relate功能</title>
   <link href="http://www.luzexi.com/2018/07/26/jekyll-related-page"/>
   <updated>2018-07-26T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/07/26/jekyll-related-page</id>
   <content type="html">&lt;p&gt;Jekyll 的 ‘Relate-相关文章’的功能，写的真的不好用。完全表达不了相关文章的含义。&lt;/p&gt;

&lt;p&gt;于是打算修一下 Relate 部分的功能。&lt;/p&gt;

&lt;p&gt;网上查了很久，很多人在抱怨，但没人把写好的 Relate 放到网上。&lt;/p&gt;

&lt;p&gt;唯一一个Jekyll 的 Relate 插件都是渣的要命的那种，根本没法用。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;于是就有了重写这个功能的欲望，本想也简单，只是几个for循环而已。&lt;/p&gt;

&lt;p&gt;原以为是 Jekyll 是 Ruby 写的，网页上也是 Ruby，还复习了下 Ruby 写法。以前写过很久没用就会忘记。&lt;/p&gt;

&lt;p&gt;最后发现网页部分不是Ruby语法呢，而是 Liquid 的语法，一款用 ruby 写的‘模板引擎’。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/Shopify/liquid&quot;&gt;Liquid github&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;查了下他的用法，API还真的不多，用起来好难受。比如 for循环 就是个要命的点。还有变量申明和运算，和平常用的语言相差有点大的。&lt;/p&gt;

&lt;h3 id=&quot;关于relate功能我希望是有最新的文章链接之前的文章链接和之后的文章链接让读者能有更大的概率找到自己想要的文章&quot;&gt;关于Relate功能，我希望是，有最新的文章链接，之前的文章链接和之后的文章链接，让读者能有更大的概率找到自己想要的文章。&lt;/h3&gt;

&lt;p&gt;写完后的效果如图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/uploads/2018/07/jekyll-relate.png&quot; alt=&quot;relate功能图&quot; /&gt;&lt;/p&gt;

&lt;p&gt;源码如下:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/luzexi/jekyll-relate&quot;&gt;Jekyll Relate github&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第四章，UI(六) - 如何架构UI框架</title>
   <link href="http://www.luzexi.com/2018/07/26/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-UI6"/>
   <updated>2018-07-26T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/07/26/Unity3D高级编程之进阶主程-UI6</id>
   <content type="html">&lt;p&gt;回顾下，前面两章着重对 UGUI 的源码进行的剖析，包括事件系统的模块和底层渲染模块以及渲染组件。这篇我们来讲讲，如何在Unity3D游戏项目中架构UI框架。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;h3 id=&quot;快速架构一个简单易用的ui框架&quot;&gt;快速架构一个简单易用的UI框架&lt;/h3&gt;

&lt;p&gt;在前面架构章节中，我们讲述了架构的需要注意的特性，以及设计架构时所使用的抽象方法。我们在经历几个项目后，会总结所有经历过的这些项目的经验，这些经验很好的支撑了构建架构的基础。&lt;/p&gt;

&lt;h6 id=&quot;我们从宏观的角度看ui框架&quot;&gt;我们从宏观的角度看UI框架。&lt;/h6&gt;

&lt;p&gt;只有从宏观的角度看问题，才能看的更明白。我们项目中拥有众多UI界面，我们要统一管理所有UI，这样才能使得每个UI界面都能得到有效的调配。不仅如此，如果每个UI界面都是可扩充的那就太棒了。UI有一个很关键的系统是输入事件系统，UI内的每个按钮都需要有一个处理输入的句柄。所以我们需要写一个统计的管理类，以及每个UI都要有统一的基类，并且每个UI按钮元素都对应一个处理输入的句柄。另外对于UI来说，有通用UI，也有非通用UI，有常用UI和非常用UI之分。接下来我们把细节规划一下。&lt;/p&gt;

&lt;h3 id=&quot;ⅰ管理类&quot;&gt;Ⅰ.管理类。&lt;/h3&gt;

&lt;p&gt;整个UI是由N个界面构成的。这些UI界面有基本的功能，生成，展示，销毁，查找。如果说，我们分别对N个UI界面的这些功能进行编程，就会有大量的工作产生，而且维护起来的工作量也是巨大的。&lt;/p&gt;

&lt;p&gt;我们需要用一个单例实例来管理所有的UI界面，让他们能有统一的接口进行以上的活动，创建UI管理类是最好的选择，我们可以命名它为 UIManager，这个名字符合它代表的功能。&lt;/p&gt;

&lt;p&gt;那么 UIManager 具体里面要做些什么呢。它需要创建UI，需要查找现有的某个UI，以及需要销毁UI，以及一些UI的统一接口调用和调配工作。UIManager 承担了所有UI的管理工作，因此UI在生成出来后的实例都将存储在这里。不仅如此，一些UI常用变量也存储在里面，比如屏幕的适配标准大小，比如UI的Camera实例等等。&lt;/p&gt;

&lt;p&gt;这样一来，第一个方向确定了，那就是UIManager是UI界面的管理员，统筹管理UI问题。它包括了UI的众多统筹需求，比如下层UI切换到上层，比如加载方式变更，比如选择性预加载UI等，都需要在UIManager里编写。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
public class ScreenManager : CSingleton&amp;lt;ScreenManager&amp;gt;
{
    protected Transform _transform = null;
    private Dictionary&amp;lt;string, UIScreenBase&amp;gt; _DicScreens = new Dictionary&amp;lt;string, UIScreenBase&amp;gt;();

    // 关闭所有界面
    public void CloseAll()
    {
        ...
    }

    // 是否UI正打开
    public bool IsShow(string screenID)
    {
        ...
    }

    // 关闭界面
    public void CloseScreen(UIScreenBase screen)
    {
        ...
    }

    // 创建所有界面
    public T CreateMenu&amp;lt;T&amp;gt;() where T : UIScreenBase
    {
    	...
    }

    // 找出某个界面
    public T FindMenu&amp;lt;T&amp;gt;() where T : UIScreenBase
    {
        ...
    }

    ...
}

&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;ⅱ基类&quot;&gt;Ⅱ.基类。&lt;/h3&gt;

&lt;p&gt;项目中有很多界面，这N个界面他们有自己的共性，比如最基本的，他们都需要进行初始化，他们都需要有展示接口，他们都可以关闭，共性产生统一特征的接口，Init，Open和Close。继承基类又使得管理起来比较方便，在上面提到的 UIManager 里存储的UI实例时，可以统一使用基类的方式存储。我们可以把基类的名字称为 UIScreenBase，每个UI界面都继承自它，Screen一词很形象贴切的描述了屏幕上显示的界面。&lt;/p&gt;

&lt;p&gt;我们将所有UI都定义为基类的子类，对有需要做特殊处理的UI界面，可以重写Init，Open和Close。为了能更方便的知道UI的状态，我们也可以定义一个UI状态，比如OpenState为打开状态，CloseState为关闭状态，HidenState为隐藏状态，PreopenState为预加载状态，以状态的形式来判断UI现在的情况。&lt;/p&gt;

&lt;p&gt;到这里，我们的每个界面有了基类，自己成为了扩展界面功能的一个类实体，可以自主定义自己的功能性的接口，同时还会受到管理类的统一调配。做到了，既满足有序管理，又能满足自定义需求。看似简单的几行代码，里面蕴含着复杂的思考过程，抽象的意义就在于此。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
public abstract class UIScreenBase : MonoBehaviour
{
    protected bool mInitialized = false;
    protected UIState mState = UIState.None;
    public UIState State { get { return mState; } }

    public delegate void OnScreenHandlerEventHandler(UIScreenBase screen);
    public event OnScreenHandlerEventHandler onCloseScreen;

    // 初始化
	protected virtual void Init()
	{	
        mInitialized = true;
	}

	//打开
	public virtual void Open() {}

	//关闭
	public virtual void Close() {}
}

&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;ⅲ输入事件响应机制&quot;&gt;Ⅲ.输入事件响应机制。&lt;/h3&gt;

&lt;p&gt;UI中输入事件的响应机制比较重要，好的输入事件响应机制能提高更多的效率，让程序员编写逻辑的时候更加舒服。&lt;/p&gt;

&lt;p&gt;Unity3D的UGUI输入事件响应机制建立通常有2种，一种是继承型，一种是注册型。&lt;/p&gt;

&lt;p&gt;继承型是指事件先响应到基类，再由基类反应给父类，由父类做处理，这样UI既可以得到对输入事件的响应，也可以自行修改自己需要的逻辑。比如我们写了个处理事件的基类组件UIEventBase是父类能接受各种输入事件响应，UIEventButton是继承UIEventBase的子类，当输入事件传入时UIEventButton能做出响应，因为它继承了父类。&lt;/p&gt;

&lt;p&gt;绑定型是指在对输入事件响应之前，我们对UI元素绑定一个事件响应的组件。比如编写一个绑定型事件类 UIEvent，当某个UI元素需要输入事件回调时，对这个物体加绑一个 UIEvent，并且对 UIEvent 里需要的相关响应事件进行赋值或注册操作函数。当输入事件响应时，由 UIEvent 来区分输入的是什么类型的事件，再分别调用响应到具体函数。&lt;/p&gt;

&lt;p&gt;继承型和绑定型都有一个共同的特点，都需要与UI元素关联，区别是继承型融入在了各种组件内，而绑定型以独立的组件形式体现。&lt;/p&gt;

&lt;p&gt;继承型UI事件输入响应机制需要关联到组件内，UGUI和NGUI都已经有了自己的基础的组件，所以很难在这上面使用，而在另一些比较特殊的GUI系统内可以很好的适应。比如我曾经做过一个项目，我们构建的一套新的UI系统的完全独立于UGUI和NGUI的GUI系统之外，我们将输入事件处理注入到这个系统的各个组件内，达到了输入事件处理与组件融合的效果。&lt;/p&gt;

&lt;p&gt;绑定型的方式更适合在已经建立了GUI系统的基础上，对输入事件进行封装处理。通常在UGUI和NGUI上都会使用绑定型对输入事件处理进行封装。&lt;/p&gt;

&lt;p&gt;例如，在UI初始化中，对需要输入事件响应的，绑定一个事件处理类，比如命名为 UIEvent，然后对事件句柄进行赋值，例如，ui_event.onclick = OnClickLogin，OnClickLogin就是响应登录按钮的事件句柄。&lt;/p&gt;

&lt;p&gt;这样的赋值方式，让程序员写逻辑时看起来更加清爽，简洁，直观。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;/// &amp;lt;summary&amp;gt;
/// UI 事件
/// &amp;lt;/summary&amp;gt;
public class UI_Event : UnityEngine.EventSystems.EventTrigger
{
    protected const float CLICK_INTERVAL_TIME = 0.2f; //const click interval time
    protected const float CLICK_INTERVAL_POS = 2; //const click interval pos

    public delegate void PointerEventDelegate ( PointerEventData eventData , UI_Event ev);
    public delegate void BaseEventDelegate ( BaseEventData eventData , UI_Event ev);
    public delegate void AxisEventDelegate ( AxisEventData eventData , UI_Event ev);

    public Dictionary&amp;lt;string,object&amp;gt; mArg = new Dictionary&amp;lt;string,object&amp;gt;();

    public BaseEventDelegate onDeselect = null;
    public PointerEventDelegate onBeginDrag = null;
    public PointerEventDelegate onDrag = null;
    public PointerEventDelegate onEndDrag = null;
    public PointerEventDelegate onDrop = null;
    public AxisEventDelegate onMove = null;
    public PointerEventDelegate onClick = null;
    public PointerEventDelegate onDown = null;
    public PointerEventDelegate onEnter = null;
    public PointerEventDelegate onExit = null;
    public PointerEventDelegate onUp = null;
    public PointerEventDelegate onScroll = null;
    public BaseEventDelegate onSelect = null;
    public BaseEventDelegate onUpdateSelect = null;
    public BaseEventDelegate onCancel = null;
    public PointerEventDelegate onInitializePotentialDrag = null;
    public BaseEventDelegate onSubmit = null;

    private static PointerEventData mPointData = null;

    // 设置参数
    public void SetData(string key , object val)
    {
        mArg[key] = val;
    }

    // 获取参数
    public D GetData&amp;lt;D&amp;gt;(string key)
    {
        if(mArg.ContainsKey(key))
        {
            return (D)mArg[key];
        }
        return default(D);
    }

    ...

    public static UI_Event Get(GameObject go)
    {
        UI_Event listener = go.GetComponent&amp;lt;UI_Event&amp;gt;();
        if (listener == null) listener = go.AddComponent&amp;lt;UI_Event&amp;gt;();
        return listener;
    }

    public override void OnBeginDrag( PointerEventData eventData ) { ... }
    public override void OnDrag( PointerEventData eventData ) { ... }
    public override void OnEndDrag( PointerEventData eventData ) { ... }
    public override void OnDrop( PointerEventData eventData ) { ... }
    public override void OnMove( AxisEventData eventData ) { ... }

    public override void OnPointerClick(PointerEventData eventData)
    {
    	...
        if(onClick != null)
        {
            onClick(eventData , this);
        }
        ...
    }

    public override void OnPointerDown (PointerEventData eventData) { ... }
    public override void OnPointerEnter (PointerEventData eventData) { ... }
    public override void OnPointerExit (PointerEventData eventData) { ... }
    public override void OnPointerUp (PointerEventData eventData) { ... }
    public override void OnScroll( PointerEventData eventData ) { ... }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;如上代码，篇幅有限，我把事件部分最重要的部分摘了出来，组件的挂在，事件的调用，以及参数的设置。&lt;/p&gt;

&lt;p&gt;到这里我们有了统一管理UI的管理类，有了界面的基类，有了处理输入事件句柄的事件类，就能开始拓展UI了，大部分UI界面我们都能够处理，但很多原生的组件用起来不是很好，效率也特别的差，所以我们需要构建自己的高效的UI自定义组件。&lt;/p&gt;

&lt;h3 id=&quot;ⅳ自定义组件&quot;&gt;Ⅳ.自定义组件。&lt;/h3&gt;

&lt;p&gt;除了NGUI和UGUI本身的组件外，我们自己的自定义组件是必不可少的，特别是游戏项目，无论大小，都需要有自己的自定义组件，自定义组件不仅能让程序员在写逻辑时快速上手，满足项目的设计需求，而且也能起到对UI优化的作用，尤其在元素多的组件内。&lt;/p&gt;

&lt;p&gt;下面介绍项目中最常改造的组件:&lt;/p&gt;

&lt;h6 id=&quot;-ui动画组件&quot;&gt;① UI动画组件。&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	动画在UI中扮演重要的角色，这里主要说的是Animation的K线动画。

	如何让Animation在美术人员手里自如的制作，并且让程序员能方便调用是关键。

	UI动画组件里应该有什么呢？我们暂时命名为 UIAnimation 好了。

	首先它肯定要依赖 Unity3D 的 Animator 组件 [RequireComponent (typeof(Animator))]。

	其次它要有播放(Play)接口用来播放指定动画，Play的参数包括，动画名，播放完毕后的回调函数委托。

	再次他可以在无需程序调用的情况下自动播放，因此在 public 变量中需要 AutoPlay 这个参数，这样美术人员就可以在 Unity3D 界面上设置自动播放而无需程序调用了。

	最后美术人员需要在自动播放时选择指定的动画名和是否循环播放，以及循环播放间隔。

	这样就基本成形了，接下来要做的事就是我们对抽象的 UIAnimation 里完善以上的功能。
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;-按钮播放音效组件&quot;&gt;② 按钮播放音效组件。&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	在点击按钮时会需要播放音效，这是每个项目必要的组件。

	功能也挺简单，当输入事件触发Click事件时发出绑定的声音文件就可以了。

	不过很多项目用到的音效系统并不是Unity3D原生态的音效系统，需要自己为这些系统定制组件。
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;-ui跟随3d物体组件&quot;&gt;③ UI跟随3D物体组件。&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	项目中很多时候需要UI元素来跟随它们，比如游戏中的血条，又比如场景中建筑物头上的标志等等，因此UI跟随3D物体的组件非常必要。

	它的功能实现起来也挺简单的，不断地计算3D物体在屏幕中的位置，来确定UI位置，并且在前后位置不同时再进行更改以避免不必要的移动。
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;-无限滚动页面组件&quot;&gt;④ 无限滚动页面组件。&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	在滚动的菜单栏里，通常类似于游戏中的背包界面，如果有几百个UI元素同时生成，或同时滚动时，效率会非常低，因为UI在每帧都需要重新构建Mesh，每一次的滚动都会引起不小的CPU消耗。

	因此一个自定义的无限滚动页面组件来，替换原来的模式，让CPU花最小的代价来运行这个滚动页面是非常有必要的。

	那么这个无限滚动页面组件关键点在哪呢？设想下，这么多UI元素一起生成，一起移动，都是一件很费力的事，我们需要减少UI元素的数量。

	最好减少到与在屏幕上显示的数量差不多，利用看不见的UI元素，来补充能看见的元素，可以描述为一个把上下UI元素不可见时的再利用过程。

	我们就拿游戏里的背包界面来举例吧，500个物品在背包界面中时，实例化，初始化，滚动都会很费劲，我们可以减少UI元素在背包界面里的显示数量。

	当UI元素滚动时一部分元素被遮挡住时，不再需要他们显示了，这时我们就可以对这些元素进行再利用。

	当上面有一行元素被遮挡住，可以被再利用时，我们就把他们移动到下面去，让他们变成下面的背包物品元素。

	这样不断得滚动，在表现上跟真的有500个物品滚动过程一模一样。这样就可以大量地削减组件消耗的CPU，不管有多少物品在背包里面，也不会引起CPU的负担了。
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;-其他组件&quot;&gt;⑤ 其他组件。&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	其他组件，比如美术数字组件，让美术制定的数字展示得更好，又比如暴击数字是特殊的图片数字等。又比如计数组件，可以让数字滚动的更加漂亮，又比如在获得游戏币时数字会像动画一样跳动由慢到快。

	再比如，针对UGUI改变颜色动画时过于消耗CPU而设计的优化组件，让动画只改变组件的颜色值，由组件来改变UI元素的材质球颜色，这样能省去很多重构Mesh导致的CPU消耗。
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;编写自定义的ui组件的目标就是增加更多通用的组件减少重复劳动让程序员在编写ui界面时更加快捷高效同时也提升了ui的运行效率拥有属于自己的一套自定义套件对项目来说也是非常有价值和高效的一件事&quot;&gt;编写自定义的UI组件的目标就是，增加更多通用的组件，减少重复劳动，让程序员在编写UI界面时更加快捷高效，同时也提升了UI的运行效率。拥有属于自己的一套自定义套件，对项目来说也是非常有价值和高效的一件事。&lt;/h3&gt;
</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第四章，UI(三) - 剖析UGUI源码中的输入与事件模块</title>
   <link href="http://www.luzexi.com/2018/07/26/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-UI3"/>
   <updated>2018-07-26T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/07/26/Unity3D高级编程之进阶主程-UI3</id>
   <content type="html">&lt;p&gt;前面几节对 NGUI 和 UGUI 进行了比较，也阐述了一些UGUI的组件的用途和原理，不过这些都仅仅只是停留在系统的表面，对系统深层次的原理和实现方式我们并不了解，接下来我们就从 UGUI 的源码入手，逐步为揭开它的神秘面纱。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;在了解整个 UGUI 源码之前，我们在此篇来从输入事件下手，对 UGUI 源码中输入事件模块进行剖析。&lt;/p&gt;

&lt;h3 id=&quot;ugui源码剖析&quot;&gt;UGUI源码剖析&lt;/h3&gt;

&lt;p&gt;UGUI的源码是Unity3D官方公开的，这里我们来剖析的是 UGUI 在 Unity2017 中的公开源码。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/4/ugui1.png&quot; alt=&quot;ugui文件夹&quot; /&gt;&lt;/p&gt;

&lt;p&gt;上图为 UGUI 内核源码的文件夹结构图。它把UGUI分成了三块，输入事件，动画，核心渲染。&lt;/p&gt;

&lt;p&gt;其中动画部分相对比较简单，用了tween补间动画的形式，对颜色，位置，大小做了渐进的操作。tween的原理是在启动一个协程，在协程里对元素的属性渐进式的修改，除了修改属性数值，tween还有多种曲线可以选择，比如内番曲线，外翻曲线等，一个数值从起点到终点的过程可以由曲线来控制。举个例子，数字从 0 到 100 的变化，在3秒里完成，如果是线性的话，则在第2秒时的数值，应该是&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    (100 - 0) * (2f/3f) = 200f/3f = 66.666
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;而如果使用内番曲线就不是这个结果了，不过它们最终都会到达100，只是过程有点‘曲折’罢了，曲线也体现了动画的‘有趣’。&lt;/p&gt;

&lt;p&gt;下面我们重点来剖析下输入事件和核心渲染这两块。&lt;/p&gt;

&lt;h3 id=&quot;输入事件源码&quot;&gt;输入事件源码&lt;/h3&gt;

&lt;p&gt;输入事件源码的文件结构图如下：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/4/ugui2.png&quot; alt=&quot;ugui事件文件夹结构&quot; /&gt;&lt;/p&gt;

&lt;p&gt;图中，UGUI 把输入事件模块有四部分，事件数据模块，输入事件捕获模块，射线碰撞检测模块，事件逻辑处理及回调模块。我们把每部分的核心源码都拉出来分析一下。&lt;/p&gt;

&lt;h6 id=&quot;事件数据模块&quot;&gt;事件数据模块&lt;/h6&gt;

&lt;p&gt;事件数据模块部分对整个事件系统的作用来说，它主要定义并且存储了事件发生时的位置、和事件对应的物体，事件的位移大小，触发事件的输入类型，以及事件的设备信息等。事件数据模块在逻辑上没有做过多的内容，而主要为了获取数据，提供数据服务。&lt;/p&gt;

&lt;p&gt;它有三个类 PointerEventData、AxisEventData、BaseEventData，分别为点位事件数据类，滚轮事件数据类，事件基础数据类。PointerEventData和AxisEventData 继承自 BaseEventData，且 AxisEventData 的代码量非常少，因为它只需要提供滚轮的方向信息。即如下:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;namespace UnityEngine.EventSystems
{
    public class AxisEventData : BaseEventData
    {
        //移动方向
        public Vector2 moveVector { get; set; }
        public MoveDirection moveDir { get; set; }

        public AxisEventData(EventSystem eventSystem)
            : base(eventSystem)
        {
            moveVector = Vector2.zero;
            moveDir = MoveDirection.None;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;BaseEventData 定义了几个常用的接口，其子类 PointerEventData 是最常用的事件数据，我们来看看它是如何编写的，代码量并不多基本全是数据定义:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
public class PointerEventData : BaseEventData
{
    public GameObject pointerEnter { get; set; }

    // 接收OnPointerDown事件的物体
    private GameObject m_PointerPress;
    // 上一下接收OnPointerDown事件的物体
    public GameObject lastPress { get; private set; }
    // 接收按下事件的无法响应处理的物体
    public GameObject rawPointerPress { get; set; }
    // 接收OnDrag事件的物体
    public GameObject pointerDrag { get; set; }

    public RaycastResult pointerCurrentRaycast { get; set; }
    public RaycastResult pointerPressRaycast { get; set; }

    public List&amp;lt;GameObject&amp;gt; hovered = new List&amp;lt;GameObject&amp;gt;();

    public bool eligibleForClick { get; set; }

    public int pointerId { get; set; }

    // 鼠标或触摸时的点位
    public Vector2 position { get; set; }
    // 滚轮的移速
    public Vector2 delta { get; set; }
    // 按下时的点位
    public Vector2 pressPosition { get; set; }
    
    // 为双击服务的上次点击时间
    public float clickTime { get; set; }
    // 为双击服务的点击次数
    public int clickCount { get; set; }

    public Vector2 scrollDelta { get; set; }
    public bool useDragThreshold { get; set; }
    public bool dragging { get; set; }

    public InputButton button { get; set; }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述代码中为数据类的核心类 PointerEventData，它存储了大部分的事件系统逻辑需要的数据，包括按下时的位置，松开与按下的时间差，拖动的位移差，点击到的物体等等，承载了所有输入事件需要的数据。事件数据模块的意义所在便是存储数据并为逻辑部分做好准备。&lt;/p&gt;

&lt;h6 id=&quot;事件数据模块主要作用为在各种事件发生时为事件逻辑做好数据工作&quot;&gt;事件数据模块，主要作用为在各种事件发生时，为事件逻辑做好数据工作。&lt;/h6&gt;

&lt;h3 id=&quot;输入事件捕获模块源码&quot;&gt;输入事件捕获模块源码&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;缺图UGUI的时间捕获模块文件夹结构
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;输入事件捕获模块由四个类组成，BaseInputModule，PointerInputModule，StandaloneInputModule，TouchInputModule。&lt;/p&gt;

&lt;p&gt;BaseInputModule 是抽象(abstract)基类，提供必须的空接口和基本变量。&lt;/p&gt;

&lt;p&gt;PointerInputModule 继承了BaseInputModule，并且在他基础上扩展了关于点位的输入逻辑，也增加了输入的类型和状态。&lt;/p&gt;

&lt;p&gt;StandaloneInputModule 和 TouchInputModule 又继承了 PointerInputModule，它们从父类开始延展向不同的方向。&lt;/p&gt;

&lt;p&gt;StandaloneInputModule 向标准键盘鼠标输入方向拓展，而 TouchInputModule 向触控板输入方向拓展。&lt;/p&gt;

&lt;p&gt;下面我们来看看他们的核心部分的代码：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
/// &amp;lt;summary&amp;gt;
/// 处理所有的鼠标事件
/// &amp;lt;/summary&amp;gt;
protected void ProcessMouseEvent(int id)
{
    var mouseData = GetMousePointerEventData(id);
    var leftButtonData = mouseData.GetButtonState(PointerEventData.InputButton.Left).eventData;

    // Process the first mouse button fully
    // 处理鼠标左键相关的事件
    ProcessMousePress(leftButtonData);
    ProcessMove(leftButtonData.buttonData);
    ProcessDrag(leftButtonData.buttonData);

    // Now process right / middle clicks
    // 处理鼠标右键和中建的点击事件
    ProcessMousePress(mouseData.GetButtonState(PointerEventData.InputButton.Right).eventData);
    ProcessDrag(mouseData.GetButtonState(PointerEventData.InputButton.Right).eventData.buttonData);
    ProcessMousePress(mouseData.GetButtonState(PointerEventData.InputButton.Middle).eventData);
    ProcessDrag(mouseData.GetButtonState(PointerEventData.InputButton.Middle).eventData.buttonData);

    //滚轮事件处理
    if (!Mathf.Approximately(leftButtonData.buttonData.scrollDelta.sqrMagnitude, 0.0f))
    {
        var scrollHandler = ExecuteEvents.GetEventHandler&amp;lt;IScrollHandler&amp;gt;(leftButtonData.buttonData.pointerCurrentRaycast.gameObject);
        ExecuteEvents.ExecuteHierarchy(scrollHandler, leftButtonData.buttonData, ExecuteEvents.scrollHandler);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;以上代码为 StandaloneInputModule 的主函数 ProcessMouseEvent，它从鼠标键盘输入事件上扩展了输入的逻辑，处理了鼠标的按下，移动，滚轮，拖拽的操作事件。其中比较重要的函数为 ProcessMousePress、ProcessMove、ProcessDrag 这三个函数，我们来重点看下他们处理的内容。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
/// &amp;lt;summary&amp;gt;
/// Process the current mouse press.
/// 处理鼠标按下事件
/// &amp;lt;/summary&amp;gt;
protected void ProcessMousePress(MouseButtonEventData data)
{
    var pointerEvent = data.buttonData;
    var currentOverGo = pointerEvent.pointerCurrentRaycast.gameObject;

    // PointerDown notification
    // 按下通知
    if (data.PressedThisFrame())
    {
        pointerEvent.eligibleForClick = true;
        pointerEvent.delta = Vector2.zero;
        pointerEvent.dragging = false;
        pointerEvent.useDragThreshold = true;
        pointerEvent.pressPosition = pointerEvent.position;
        pointerEvent.pointerPressRaycast = pointerEvent.pointerCurrentRaycast;

        DeselectIfSelectionChanged(currentOverGo, pointerEvent);

        // 搜索元件中按下事件的句柄，并执行按下事件句柄
        var newPressed = ExecuteEvents.ExecuteHierarchy(currentOverGo, pointerEvent, ExecuteEvents.pointerDownHandler);

        // didnt find a press handler... search for a click handler
        // 搜索后找不到句柄，就设置一个自己的
        if (newPressed == null)
            newPressed = ExecuteEvents.GetEventHandler&amp;lt;IPointerClickHandler&amp;gt;(currentOverGo);

        // Debug.Log(&quot;Pressed: &quot; + newPressed);

        float time = Time.unscaledTime;

        if (newPressed == pointerEvent.lastPress)
        {
            var diffTime = time - pointerEvent.clickTime;
            if (diffTime &amp;lt; 0.3f)
                ++pointerEvent.clickCount;
            else
                pointerEvent.clickCount = 1;

            pointerEvent.clickTime = time;
        }
        else
        {
            pointerEvent.clickCount = 1;
        }

        pointerEvent.pointerPress = newPressed;
        pointerEvent.rawPointerPress = currentOverGo;

        pointerEvent.clickTime = time;

        // Save the drag handler as well
        // 保存拖拽信息
        pointerEvent.pointerDrag = ExecuteEvents.GetEventHandler&amp;lt;IDragHandler&amp;gt;(currentOverGo);

        // 执行拖拽启动事件句柄
        if (pointerEvent.pointerDrag != null)
            ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.initializePotentialDrag);
    }

    // PointerUp notification
    // 抬起通知
    if (data.ReleasedThisFrame())
    {
        //执行抬起事件的句柄
        // Debug.Log(&quot;Executing pressup on: &quot; + pointer.pointerPress);
        ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerUpHandler);

        // Debug.Log(&quot;KeyCode: &quot; + pointer.eventData.keyCode);

        var pointerUpHandler = ExecuteEvents.GetEventHandler&amp;lt;IPointerClickHandler&amp;gt;(currentOverGo);

        // 如果抬起时与按下时为同一个元素，那就是点击
        if (pointerEvent.pointerPress == pointerUpHandler &amp;amp;&amp;amp; pointerEvent.eligibleForClick)
        {
            ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerClickHandler);
        }
        // 否则也可能是拖拽的释放
        else if (pointerEvent.pointerDrag != null &amp;amp;&amp;amp; pointerEvent.dragging)
        {
            ExecuteEvents.ExecuteHierarchy(currentOverGo, pointerEvent, ExecuteEvents.dropHandler);
        }

        pointerEvent.eligibleForClick = false;
        pointerEvent.pointerPress = null;
        pointerEvent.rawPointerPress = null;

        // 如果正在拖拽则抬起事件等于拖拽结束事件
        if (pointerEvent.pointerDrag != null &amp;amp;&amp;amp; pointerEvent.dragging)
            ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.endDragHandler);

        pointerEvent.dragging = false;
        pointerEvent.pointerDrag = null;
        
        // 如果当前接收事件的物体和事件的刚开始的物体不一致，则对两个物体做进和出的事件处理
        if (currentOverGo != pointerEvent.pointerEnter)
        {
            HandlePointerExitAndEnter(pointerEvent, null);
            HandlePointerExitAndEnter(pointerEvent, currentOverGo);
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上面展示了 ProcessMousePress 处理鼠标按下事件的代码，虽然比较多但并不复杂，我在代码上做了详尽的注解。其实它不仅仅处理的是按下的操作，也同时处理鼠标抬起的操作，以及处理了拖拽启动和拖拽抬起与结束的事件。在调用处理相关句柄的前后，事件数据都会被保存在 pointerEvent 中，然后被传递给业务层中设置的输入事件句柄。&lt;/p&gt;

&lt;p&gt;我们再来看看 ProcessDrag 拖拽处理函数:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
protected virtual void ProcessDrag(PointerEventData pointerEvent)
{
    bool moving = pointerEvent.IsPointerMoving();

    // 如果已经在移动，且还没开始拖拽启动事件，则调用拖拽启动句柄，并设置拖拽中标记为true
    if (moving &amp;amp;&amp;amp; pointerEvent.pointerDrag != null
        &amp;amp;&amp;amp; !pointerEvent.dragging
        &amp;amp;&amp;amp; ShouldStartDrag(pointerEvent.pressPosition, pointerEvent.position, eventSystem.pixelDragThreshold, pointerEvent.useDragThreshold))
    {
        ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.beginDragHandler);
        pointerEvent.dragging = true;
    }

    // 拖拽时的句柄处理
    if (pointerEvent.dragging &amp;amp;&amp;amp; moving &amp;amp;&amp;amp; pointerEvent.pointerDrag != null)
    {
        // 如果按下的物体和拖拽的物体不是同一个则视为抬起拖拽，并清除前面按下时的标记
        if (pointerEvent.pointerPress != pointerEvent.pointerDrag)
        {
            ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerUpHandler);

            pointerEvent.eligibleForClick = false;
            pointerEvent.pointerPress = null;
            pointerEvent.rawPointerPress = null;
        }

        // 执行拖拽中句柄
        ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.dragHandler);
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上面展示了 ProcessDrag 拖拽句柄处理函数，与ProcessMousePress类似对拖拽事件逻辑做了判断，包括拖拽开始事件处理，判断结束拖拽事件，以及拖拽句柄的调用。&lt;/p&gt;

&lt;p&gt;ProcessMove 则相对简单点，每帧都会直接调用处理句柄。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
protected virtual void ProcessMove(PointerEventData pointerEvent)
{
    var targetGO = pointerEvent.pointerCurrentRaycast.gameObject;
    HandlePointerExitAndEnter(pointerEvent, targetGO);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;除了鼠标事件外，我们再来看看触屏事件的处理方式，即 TouchInputModule 的核心函数。如下:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
/// &amp;lt;summary&amp;gt;
/// Process all touch events.
/// 处理所有触屏事件
/// &amp;lt;/summary&amp;gt;
private void ProcessTouchEvents()
{
    for (int i = 0; i &amp;lt; Input.touchCount; ++i)
    {
        Touch input = Input.GetTouch(i);

        bool released;
        bool pressed;
        var pointer = GetTouchPointerEventData(input, out pressed, out released);

        ProcessTouchPress(pointer, pressed, released);

        if (!released)
        {
            ProcessMove(pointer);
            ProcessDrag(pointer);
        }
        else
            RemovePointerData(pointer);
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;从代码中我们看到 ProcessMove 和 ProcessDrag 与前面鼠标事件处理时一样的，只是按下的时间处理不同，而且它对每个触点都做了相同的操作处理。其实 ProcessTouchPress 和鼠标按下处理函数 ProcessMousePress 非常相似，可以说基本上一模一样，只是传入时的数据类型不同而已，由于篇幅有限这里不再重复展示长串代码。&lt;/p&gt;

&lt;p&gt;这里大量用到了 ExecuteEvents.ExecuteHierarchy，ExecuteEvents.Execute 之类的静态函数来执行句柄，它是怎么工作的呢，其实很简单:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
private static readonly List&amp;lt;Transform&amp;gt; s_InternalTransformList = new List&amp;lt;Transform&amp;gt;(30);

public static GameObject ExecuteHierarchy&amp;lt;T&amp;gt;(GameObject root, BaseEventData eventData, EventFunction&amp;lt;T&amp;gt; callbackFunction) where T : IEventSystemHandler
{
    // 获取物体的所有父节点，包括它自己
    GetEventChain(root, s_InternalTransformList);

    for (var i = 0; i &amp;lt; s_InternalTransformList.Count; i++)
    {
        var transform = s_InternalTransformList[i];
        // 对每个父节点包括自己依次执行句柄响应
        if (Execute(transform.gameObject, eventData, callbackFunction))
            return transform.gameObject;
    }
    return null;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;解释下上述代码，对所有父节点都调用句柄函数。也就是说，当前节点的事件会通知给它上面的父节点。&lt;/p&gt;

&lt;h6 id=&quot;到这里我们基本清楚事件处理的基本逻辑了下面我们来看看碰撞测试模块是如何运作的&quot;&gt;到这里我们基本清楚事件处理的基本逻辑了，下面我们来看看碰撞测试模块是如何运作的&lt;/h6&gt;

&lt;h6 id=&quot;射线碰撞检测模块源码&quot;&gt;射线碰撞检测模块源码&lt;/h6&gt;

&lt;p&gt;射线碰撞检测模块主要工作是从摄像机的屏幕位置上，做射线碰撞检测并获取碰撞结果，把结果返回给事件处理逻辑类，交由事件处理模块处理事件。&lt;/p&gt;

&lt;p&gt;射线碰撞检测模块主要为3个类，分别作用于 2D射线碰撞检测，3D射线碰撞检测，GraphicRaycaster图形射线碰撞测试。&lt;/p&gt;

&lt;p&gt;2D、3D射线碰撞测试相对比较简单，用射线的形式做碰撞测试，区别在2D碰撞结果里预留了2D的层级次序以便在后面的碰撞结果排序时，以这个层级次序为依据做排序，而3D的碰撞检测结果则是以距离大小为依据排序的。&lt;/p&gt;

&lt;p&gt;GraphicRaycaster 为UGUI元素点位检测的类，它被放在了 Core 渲染块里。它主要针对 ScreenSpaceOverlay 模式下输入点位做碰撞检测，因为这个模式下的检测并不依赖于射线碰撞，而是遍历所有可点击的UGUI元素来检测比较，从而判断是该响应哪个UI元素。因此 GraphicRaycaster 是比较特殊的。&lt;/p&gt;

&lt;p&gt;我们来着重看下 GraphicRaycaster 的核心源码如下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
/// &amp;lt;summary&amp;gt;
/// Perform a raycast into the screen and collect all graphics underneath it.
/// &amp;lt;/summary&amp;gt;
[NonSerialized] static readonly List&amp;lt;Graphic&amp;gt; s_SortedGraphics = new List&amp;lt;Graphic&amp;gt;();
private static void Raycast(Canvas canvas, Camera eventCamera, Vector2 pointerPosition, List&amp;lt;Graphic&amp;gt; results)
{
    // Debug.Log(&quot;ttt&quot; + pointerPoision + &quot;:::&quot; + camera);
    // Necessary for the event system
    var foundGraphics = GraphicRegistry.GetGraphicsForCanvas(canvas);
    for (int i = 0; i &amp;lt; foundGraphics.Count; ++i)
    {
        Graphic graphic = foundGraphics[i];

        // -1 means it hasn't been processed by the canvas, which means it isn't actually drawn
        if (graphic.depth == -1 || !graphic.raycastTarget)
            continue;

        if (!RectTransformUtility.RectangleContainsScreenPoint(graphic.rectTransform, pointerPosition, eventCamera))
            continue;

        if (graphic.Raycast(pointerPosition, eventCamera))
        {
            s_SortedGraphics.Add(graphic);
        }
    }

    s_SortedGraphics.Sort((g1, g2) =&amp;gt; g2.depth.CompareTo(g1.depth));
    //      StringBuilder cast = new StringBuilder();
    for (int i = 0; i &amp;lt; s_SortedGraphics.Count; ++i)
        results.Add(s_SortedGraphics[i]);
    //      Debug.Log (cast.ToString());

    s_SortedGraphics.Clear();
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述代码中，GraphicRaycaster 对每个可以点击的元素(raycastTarget是否为true，并且 depth 不为-1，为可点击元素)进行计算，判断点位是否落在该元素上。再通过 depth 变量排序，判断最先该落在哪个元素上，从而确定哪个元素响应输入事件。&lt;/p&gt;

&lt;p&gt;所有检测碰撞的结果数据结构为 RaycastResult 类，它承载了所有碰撞检测结果的依据，包括了距离，世界点位，屏幕点位，2D层级次序，碰撞物体等，为后面事件处理提供了数据上的依据。&lt;/p&gt;

&lt;h6 id=&quot;事件逻辑处理模块&quot;&gt;事件逻辑处理模块&lt;/h6&gt;

&lt;p&gt;事件主逻辑处理模块，主要的逻辑都集中在 EventSystem 类中，其余的类都是对它起辅助作用的。&lt;/p&gt;

&lt;p&gt;EventInterfaces，EventTrigger，EventTriggerType 定义了事件回调函数，ExecuteEvents 编写了所有执行事件的回调接口。&lt;/p&gt;

&lt;p&gt;EventSystem 主逻辑里只有300行代码基本上都在处理由射线碰撞检测后引起的各类事件。判断事件是否成立，成立则发起事件回调，不成立则继续轮询检查，等待事件的发生。&lt;/p&gt;

&lt;p&gt;EventSystem 是事件处理模块中唯一继承 MonoBehavior 并且有在 Update 帧循环中做轮询的。也就是说，所有UI事件的发生都是通过 EventSystem 轮询监测到的并且实施的。EventSystem 通过调用输入事件检测模块，检测碰撞模块，来形成自己主逻辑部分。因此可以说 EventSystem 是主逻辑类，是整个事件模块的入口。&lt;/p&gt;

&lt;h6 id=&quot;架构者在设计时将整个事件层各自的职能拆分的很清楚使得我们看源代码时也并没有那么难输入监测由输入事件捕捉模块完成碰撞检测由碰撞检测模块完成事件的数据类都有各自的定义eventsystem-主要作用是把这些模块拼装起来成为主逻辑块&quot;&gt;架构者在设计时将整个事件层各自的职能拆分的很清楚，使得我们看源代码时也并没有那么难。输入监测由输入事件捕捉模块完成，碰撞检测由碰撞检测模块完成，事件的数据类都有各自的定义，EventSystem 主要作用是把这些模块拼装起来成为主逻辑块。&lt;/h6&gt;

&lt;p&gt;&lt;a href=&quot;https://bitbucket.org/Unity-Technologies/ui/downloads/?tab=downloads&quot;&gt;UGUI源码地址&lt;/a&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第四章，UI(二) - UGUI的原理及组件使用详解</title>
   <link href="http://www.luzexi.com/2018/07/25/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-UI2"/>
   <updated>2018-07-25T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/07/25/Unity3D高级编程之进阶主程-UI2</id>
   <content type="html">&lt;p&gt;前文对NGUI和UGUI进行了比较，讲述了如何选择UI系统作为项目的UI框架。这篇我们来讲讲，UGUI的原理，以及UGUI的组件使用详解。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;h3 id=&quot;ugui的初级和高级使用详解&quot;&gt;UGUI的初级和高级使用详解&lt;/h3&gt;

&lt;p&gt;首先我们来介绍下ugui的运作机制。&lt;/p&gt;

&lt;p&gt;UGUI是在3D网格下建立起来的UI系统，它的每个可显示的元素都是以3D模型网格的形式来构建起来的。当UI被实例化时，UGUI首先要做的事就是构建网格。&lt;/p&gt;

&lt;p&gt;也就是说当Unity3D制作一个图元，或者一个按钮，或者一个背景时，都会先构建一个方形网格，再将图片放入网格中。可以理解为制造了一个3D模型，用一个网格绑定一个材质球，材质球里存放了要显示的图片。&lt;/p&gt;

&lt;p&gt;如果每个元素都会生成一个模型并且绑定一个材质球存入一张图片的话，UI上成千上百个元素就会拥有成千上百个材质球，以及成千上百张图。这样使得引擎在渲染时都需要读取成千上百张图，对每个材质球和网格都进行渲染，这会导致性能开销巨大，drawcall过高，可以简单的理解为一个材质球一个drawcall。(drawcall的原理我们将在后面的章节中介绍)&lt;/p&gt;

&lt;p&gt;UGUI当然做了优化，它将一部分相同类型的图片都集合起来合成一个张图，然后将拥有相同图片相同shader的材质球合并成一个材质球，并且把分散开的模型网格也一起合并了，这样就生成了几个大网格和几个材质球，以及少许整张的图集。节省了很多材质球，图片，网格的渲染，UI的效率更高了很多，游戏在进行时才会顺畅。这就是我们常常在UI制作中提到的图集的概念，它把很多张图片都放置在一张图集上，导致大量的图片和材质球不需要重复的绘制，只要改变模型顶点上的uv即可。&lt;/p&gt;

&lt;p&gt;UGUI也并不是所有的网格和材质球都合并成一个，只有把相同层级的元素，以及相同层级上的拥有相同的材质球参数的才合并在一起。合并成一个网格了就是一个静止的模型了，如果我们移动了任何元素，或者销毁了任何元素，或者改变了任何元素的材质球参数，UGUI则会销毁这个网格，重新构建一个新的。我们设想下，如果我们每时每刻都在移动一个元素的话，UGUI就会不停的拆分合并拆分合并，就会不停的消耗CPU，来使得画面保持应该有的样子。&lt;/p&gt;

&lt;p&gt;因为这些合并和拆分的操作会消耗很多CPU，UI系统要做的就是尽一切可能节省些CPU消耗，把尽量多的剩余CPU让给项目逻辑。UGUI在制作完成成品后性能优劣差距很多时候都会出现在这里，合并的最多的元素，拆分次数最少的UI，才能达到优秀的性能开销.&lt;/p&gt;

&lt;p&gt;下面我们来主要介绍下UGUI的核心组件。&lt;/p&gt;

&lt;h3 id=&quot;核心组件canvas&quot;&gt;核心组件Canvas&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;	Canvas，我们暂且叫它画布。Canvas就相当于画画时铺在上边的画板，我们把各类元素放在画布上后，Canvas要做的事情就是合并这些元素。

	合并的规则为，同一个Canvas里，相同层级的，相同材质球的元素进行合并，从而减少Drawcall。不过相同层级的概念并不是gameobject 上的节点层级，而是覆盖层级。Canvas说如果两个元素重叠，则可以认为它们是上下层关系，把所有重叠的层级数计算完毕后，第0层的所有元素统一合并，第1层的元素也统一合并，以此类推。

	Canvas上的参数 Render Mode 渲染模式比较重要，这里详细介绍下，你可以选择不以Camera为基准的Overlay模式，也可以选择Camera为基准的Screen Camera模式，也可以选择3D世界为基准的World Space模式。三者适合于三种不同的的使用场景各有不同。

	Overlay模式并不与空间上排序有任何关系，空间上的前后位置不再对元素起作用，它常用在纯UI的区域内，这种模式下Camera排序有别与其他模式，Sort order参数在排序时被着重使用到，Sort order参数的值越大，越靠前渲染。在这个模式下没有Camera的渲染机制因此很难加入普通的3D模型物体来增加效果。

	Screen Camera模式，相对比较通用一点，它依赖于Camera的平面透视，渲染时的布局依赖于它绑定的Camera。想让更多的非UGUI元素加入到UI中，Screen Camera模式更加具有优势。这种模式是实际项目中制作UI最常用的模式，不过UGUI底层有对排序做些规则，如对元素的z轴不为0的元素，会单独提取出来渲染，不参与合并。

	World Space模式，主要用于当UI物体放在3D世界中时用的，比如，一个大的场景中，需要将一张标志图放在一个石块头上，这时就需要World Space模式。它与 Screen Camera 的区别是，它常在世界空间中与普通3D物体一同展示，依赖于截锥体透视(Perspective)Camera。它的原理挺简单的，与普通物体一样当UI物体在这个Camera视野中时，就相当于渲染了一个普通的3D面片，只不过除了普通的渲染Canvas还对这些场景里的UI进行合并处理。
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;canvas-scaler&quot;&gt;Canvas Scaler&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	这是个缩放比例组件，用来指定画布中元素的比例大小。

	有简单指定比例大小的Constant Pixel Size模式，也有Scale With Screen Size以屏幕为基准的自动适配比例大小，或者Constant Physical Size以物理大小为基准的适配规则。

	在实际手游项目里，设备的屏幕分辨率变化比较大，通常使用以屏幕为基准的自动适配比例大小的Scale With Screen Size选项。
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;graphic-raycaster&quot;&gt;Graphic Raycaster&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	输入系统的图形碰撞测试组件，它并不会检测Canvas以外的内容，检测的都是画布下的元素。当图元素上存在有效的碰撞体时，Graphic Raycaster 组件会统一使用射线碰撞测试来检测碰撞的元素。

	我们也可以设置完全忽略输入的方式来彻底取消点击响应，也可以指定阻止对某些layers进行相应。
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;eventtrigger&quot;&gt;EventTrigger&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	输入事件触发器，与此脚本绑定的UI物体，都可以接受到输入事件。

	比如(鼠标，手指)按下，弹起，点击，开始拖动，拖动中，结束拖动，鼠标滚动事件等。

	它主要是起到点击响应作用，配合前面的 Graphic Raycaster 响应给输入事件系统。
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;imagerawimage&quot;&gt;Image，RawImage&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	这两个是UI里的主要部件，它们可以对图片进行展示，包括图片，图集。

	两者的区别是Image仅能展示图集中的图元但可以参与合并，而RawImage能展示单张图片但无法合并。通常我们会将小块的图片，打成图集来展示，这样更节省性能也更节省内存，这也是UGUI自动集成的功能，每个图片资源都有一个tag 标记，标记决定了哪些元素会合并到同一张图集内，如果没有tag标记，则默认不会合并图集它自己就是自己的图集。

	不使用图集而使用RawImage展示单张图片的时，通常都是由于图片尺寸太大导致合并图集效率太低，或者相同类型的图片数量太多，导致合并图集后的图集太大，而实际在画面上需要展示的这种类型的图片又很少，图集方式反而浪费大量内存空间，则使用RawImage逐一展示即可。
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;maskrectmask2d&quot;&gt;Mask，RectMask2D&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	遮挡组件，可以将其子节点下矩形区域外的内容剔除，是滚动窗口中最常用的组件。

	这两种方式的主要是在剔除的方法上有所区别，在实现效果上都是一样的，其中Mask 使用顶点重构的方式剔除矩形区域外的部分，而 RectMask2D 则采用 Shader 的剔除方式，每个元素都有自己的材质球实例和实例参数。

	Mask 和 RectMask2D 它俩具体的剔除算法和源代码分析我们将在后面的UGUI源码剖析章节讲解。
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;其他组件&quot;&gt;其他组件&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	其他大部分逻辑组件都是可以重写的，比如按钮组件Button，切换组件Toggle，滚动条组件ScrollBar，滑动组件Slider，下拉框组件DropDown，视图组件ScrollView，如果不想使用它们，觉得它们的功能不够用，我们是可以用Image，Mask等几个核心组件组合后重写的。

	在实际工作中，很多项目都会自定义属于自己的组件，为什么要自定义呢？很多时候项目里的需求更多样化，有自己的组件可以在特殊需求和特殊逻辑时，能够好不费劲的更改自定义的组件。所以大部分项目中，都会重写一些组件来用来给自己项目使用，也有一些人总结了这些组件的经验，写了些比较好用的组件开源在Github上。
&lt;/code&gt;&lt;/pre&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第四章，UI(一) - NGUI和UGUI比较</title>
   <link href="http://www.luzexi.com/2018/07/25/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-UI1"/>
   <updated>2018-07-25T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/07/25/Unity3D高级编程之进阶主程-UI1</id>
   <content type="html">&lt;p&gt;UI是游戏项目中重要的组成部分，面对一个从零开始的项目，首先要确立的就是选用哪个UI系统作为主框架。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;现在主流项目中基本上都是NGUI和UGUI，那么到底选哪个。我们先来做个比较。&lt;/p&gt;

&lt;h3 id=&quot;图集处理功能比较&quot;&gt;图集处理功能比较&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;	NGUI需要使用工具手动拼接图片成图集。

	UGUI开发期间可以直接使用图片作为元素，打包时会自动拼接成图集。

	图集的alpha拆分功能，NGUI可以通过增加工具类和shader来做，UGUI有自带的alpha拆分功能方便许多。
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;组件支持功能比较&quot;&gt;组件支持功能比较&lt;/h3&gt;

&lt;p&gt;NGUI组件比较多，比较常用的有&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	Localization System -- 多语言组件
	UIScrollView -- 滚动视图
	UIButton -- 按钮
	UIToggle -- 切换选择组件
	UIScrollBar -- 滚动条
	UIProcessBar -- 进度条
	UIPopupList --下拉列表菜单
	UIInput -- 输入框
	UIKeyBinding -- 快捷键绑定
	UIKeyNavigation -- 导航绑定
	UIGrid -- 排列
	UITable – 表格排列
	UIPlayAnimation -- UI动画
	UIAnchor -- 锚点
	UICamera -- UI摄像头
	UIDragXXX – 拖拽摄像头，元素，视图等
	UIFont – UI字体
	UILabel – 文字组件
	UIPanel – 绘制面板
	UIPlaySound – 声音播放
	UITexture – 贴图
	等等等等…还有很多
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;UGUI组件比较少&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	Text – 文字
	Image – 贴图(图集中的元素)
	RawImage – 贴图(单张图)
	Button – 按钮
	Toggle – 切换与选择
	Slider – 大小滑动块
	Scrollbar – 滑动条
	Dropdown – 下拉框
	Input Field – 输入框
	Canvas – 画布
	Panel – 一大块元素
	Scroll View – 滑动视图
	Mask – 遮挡块
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;可定制程度&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	NGUI有源码，可以随时随地修改满足特殊需求
	UGUI源码c#开源，可修改，但不是那么方便，需要重新打成dll后再替换原来的
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;输入事件处理&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	NGUI通过摄像头发出射线碰撞，接受输入事件，并通过碰撞顺序来处理层级。
	UGUI根据输入点的位置RaycastTarget，判断事件应该交给哪个UI元素。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;层级显示控制&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	NGUI依靠Panel depth、RenderQueue来控制层级。
	UGUI可以依靠距离摄像机的前后位置来控制层级，也可以用sort order排序设置前后顺序。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;字体制作&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	NGUI不支持dynamic font。
	UGUI支持dynamic font，可直接使用字体文件
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;社区完善程度&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	NGUI商业运营，氛围良好
	UGUI官方自运营，后台强大
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;性能&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	NGU据很多测试网站统计，性能尚可
	UGUI同样，据很多测试网站统计，性能良好
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;好了，以上列出的是从几个方面对NGUI和UGUI的比较。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;从图集工具来看，NGUI有更多的自主选择权，而UGUI更多的自动化的便利，&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;从组件支持度来看NGUI更适合快速原型，而UGUI更多的是需要自己编写适合自己的组件。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;从UI底层可定制度上看，两者都是可定制的，但NGUI更加方便。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;从输入事件处理上看，NGUI混合了物理系统，而UGUI有一套自己独立的事件系统。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;从层级显示上看，NGUI概念有点混淆，而UGUI层级概念清晰干净。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;从字体制作上看，NGUI麻烦了点，而UGUI更加方便。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;从社区完善上看，NGUI更加商业化，而UGUI有官方支持后台强大。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;从性能上比较看，NGUI臃肿了点但尚可，而UGUI更加良好。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;综合来看，NGUI和UGUI都各自有各自的特点，没有绝对好，也没有绝对的差，都各自有各自的特点，和擅长的领域。针对不同的人群和项目可以有不同的选择。&lt;/p&gt;

&lt;h3 id=&quot;如何选择适合你的gui&quot;&gt;如何选择适合你的GUI&lt;/h3&gt;

&lt;p&gt;适不适合，并不是绝对的，根据人和项目来分，上面分析过程中看，NGUI和UGUI都各自有自己的特点，对每个人每种情况来说都是不一样的。我的建议是选择你熟悉的，尝试你不熟悉的比较后再做决定。&lt;/p&gt;

&lt;p&gt;Unity3D 4.x的项目通常会选择NGUI，因为Unity3D 4.x对UGUI支持的并不好。而如今新项目都会选择使用最新版本的Unity3D，所以选择UGUI的会多一点。我们需要与时俱进跟随潮流，所以如果你没有尝试过UGUI，熟悉UGUI应该是迟早的事。铁了心要做一个决定是用NGUI还是用UGUI，我的建议是，新项目启用UGUI，老项目继续使用NGUI。&lt;/p&gt;

&lt;p&gt;但也不一定是一个套路用到底，比如有些人就是喜欢把控源码，希望在源码基础上进行更多的自定义的修改，然后完善成自己的系统框架，更好的服务于游戏逻辑，或者拥有更好的性能定制，那么最好的选择就是NGUI。不过UGUI也有源码开放，只是不能随意定制，只是在此基础上扩展和重载。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第二章，架构(三) - 架构的误区，如何做前端架构，以及如何架构Unity3D项目</title>
   <link href="http://www.luzexi.com/2018/07/24/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-%E6%9E%B6%E6%9E%843"/>
   <updated>2018-07-24T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/07/24/Unity3D高级编程之进阶主程-架构3</id>
   <content type="html">&lt;p&gt;前文我们对软件系统架构进行一个彻头彻尾的解释。并且对软件系统架构抽象的思维方式进行了一番详细介绍，包括了分层，分治，演化。这篇我们将来具体介绍下，架构中的误区，以及如何做前端架构，并且了解下如何架构Unity3D项目。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;h3 id=&quot;前端与后端架构之间的共性&quot;&gt;前端与后端架构之间的共性&lt;/h3&gt;

&lt;p&gt;前后端架构的目标都是，高性能、高可用、可扩展、安全、可容错。对于前端来说除了这些目标特性外，我们还需要加入更多的用户体验，包括视觉效果和操作灵敏度。&lt;/p&gt;

&lt;p&gt;作为前端工程师，用户体验是比较重要的，但这种体验涉及到很多方面，包括性能优化，视觉效果，以及操作上的人性化等，例如如何让游戏加载更快，如何制作更绚丽的特效，如何减少Drawcall，如何减少CPU的负载，如何最快的响应用户操作等。&lt;/p&gt;

&lt;p&gt;前端技术与后端技术，都是在同一个系统层面上建立起来的，都是建立在Linux，Windows，Android，IOS，操作系统之上的，两者最后要需要了解操作系统的接口以及底层运作原理。区别在于后端在操作系统上构建了一套服务端框架，而前端在操作系统之上构建了一个渲染引擎，两者都需要在这之上构建业务架构。当我们自己构建了或选择使用商业渲染引擎后，再在渲染引擎之上建立游戏应用的业务架构，因此我们其实有两套架构要学习，一套是渲染引擎架构，一套是游戏业务架构。&lt;/p&gt;

&lt;p&gt;对渲染引擎架构的探讨偏离了书本的初衷，留给作者在其他文章中再来详细讲解。在游戏架构中有很多需要我们搭建的框架，我们可以以模块形式来命名它们，包括网络框架，UI框架，数据框架，核心战斗框架，AI框架等我们将在下面的文章中讨论。&lt;/p&gt;

&lt;h3 id=&quot;培养架构设计思维&quot;&gt;培养架构设计思维&lt;/h3&gt;

&lt;p&gt;良好的架构设计思维的培养，离不开工作中大量高质量项目的实战锻炼，然后是平时的学习、思考和提炼总结。&lt;/p&gt;

&lt;p&gt;基本的架构设计思维，其实在我们大学计算机课程（比如数据结构和算法）中可以找到影子，大学里以学习理论知识为主，需要在具体的工作中实践他们。其实大学教育其实非常重要，基本的架构设计思维在那个时候就已经埋下种子，后面工程实践中会进一步消化和应用，随着经验的积累我们能够解决的问题的复杂性和规模逐渐变大，所用的方法就是抽象，包括分层、分治、演化。&lt;/p&gt;

&lt;p&gt;架构设计不是静态的，而是动态的。只有能够不断应对环境变化的系统，才是有生命力的系统。所以即使你掌握了抽象、分层和分治这三种基本思维，仍然需要演化式思维，在设计的同时，借助反馈和进化的力量推动架构的持续演进。&lt;/p&gt;

&lt;p&gt;架构师在关注技术，开发应用的同时，需要定期梳理自己的架构设计思维，积累时间长了，你看待世界事物的方式会发生根本性变化，你会发现我们生活其中的世界，其实也是在抽象、分层、分治和演化的基础上构建起来的。架构设计思维的形成，会对你的系统架构设计能力产生重大影响。可以说对抽象、分层、分治和演化掌握的深度和灵活应用的水平，直接决定架构师所能解决问题域的复杂性和规模大小，是区分普通应用型架构师和平台型/系统型架构师的一个分水岭。&lt;/p&gt;

&lt;h3 id=&quot;试着架构unity3d项目&quot;&gt;试着架构Unity3D项目&lt;/h3&gt;

&lt;p&gt;我们可以用以上的方法来试着架构Unity3D项目，我们用分层的思维方式，先确定架构的层级，如下图。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/2/unity-archiecture1.png&quot; alt=&quot;Unity3D架构1&quot; /&gt;&lt;/p&gt;

&lt;p&gt;把整个项目分成五大层级，网络层，数据层，资源层，核心逻辑框架层，UI层。&lt;/p&gt;

&lt;p&gt;这样一分清晰的知道了我们需要做哪几块大类的东西。但是这样拆分太笼统，特别是核心逻辑框架这块，完全是概括性的层级，无法表达具体的系统。所以我们再拆分层级。把太过于笼统的层级进行再分层。如下图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/2/unity-archiecture2.png&quot; alt=&quot;Unity3D架构2&quot; /&gt;&lt;/p&gt;

&lt;p&gt;经过再分层后，把核心逻辑框架分成了，工具编辑器，角色行为框架，AI框架，地图场景与寻路框架，Shader与特效，设备原始接口。这些子层都是在核心逻辑层中，他们有自己的框架，也可以互相调用，构成了核心逻辑部分，也就是核心玩法或者说核心战斗的主要部分。&lt;/p&gt;

&lt;p&gt;我们将资源管理层和数据管理层再进行了拆分，分成了Assetbundle资源管理和Prefab资源管理，以及内存数据管理和外部数据管理，这样更清晰的分工了各层的职能。其实还有很多其他的层级我们这里没有提到的，包括常用库，工具库，动画控制等，这里暂不一一提出来。&lt;/p&gt;

&lt;p&gt;在游戏项目中最常用的是，数据表，网络层，UI层，常用库，这几个模块。我们可以用这个层级的方式来试着搭建一个完整的项目，只是做抽象上的编写，就可以清晰的知道，这个项目需要哪些模块和层级了。&lt;/p&gt;

&lt;p&gt;比如如果项目单机的策略类游戏，可能就没有很多角色上的东西，而多了很多2D动画行为控制上的需求。这时我们就可以把层级划分下，把注意力重点放在，2D动画行为控制，UI框架，数据管理，资源管理，以及AI上。&lt;/p&gt;

&lt;p&gt;如果我们项目是3D人物角色为主的网络游戏，就有地形地图，角色行为控制，还需要一套角色技能特效动画编辑器。这时我们就需要把网络层这块好好决策下该用哪种框架，TCP-Socket？UDP？还是web形式的HTTP？！3DMMRPG的难度主要集中在了角色技能动画、AI、地图、物理模拟上。我们可以重点划分出来，找人专门做这块，把最难把控的放在最优先的位置去做，而后再对这些层级进行细致化的构建。&lt;/p&gt;

&lt;p&gt;对模块进行细致化构建时，我们可以用分治法方法去构建。如果某个要解决的内容已经确定了，而这个内容或问题的规模还是太大，无法直接的下手解决，那么我们用分治的方法，把一个问题分成几个小问题来做，把小问题再划分成更小的问题直到小到能直接解决，再依次对他们跟进处理。&lt;/p&gt;

&lt;p&gt;这里我们拿网络层来说，进行分而治之，如下图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/2/unity-archiecture3.png&quot; alt=&quot;Unity3D架构3&quot; /&gt;&lt;/p&gt;

&lt;p&gt;图中我们把网络层拆分成http，tcp-socket，udp这三种类型的形式，再对每个类型的具体接口进行了拆分，对于拆分出来的每个接口，如果还不能直接使用再进行细致的拆分，直到拆分到可以具体实施了为止。在上图中，我们以接口的形式进行拆分，先将接口拆分成，连接，断开连接，发送数据，收到数据，以及(断开、连接、终结)网络事件，然后再对每个接口进行拆分，把接口需要处理的问题拆分出来各个击破。&lt;/p&gt;

&lt;p&gt;除了我们举例的网络层，其他层级部分的框架也可以用同样的方式进行类似的分解，用分而治之的方法逐个击破每个模块。这里我们描述了各个模块的拆分原则:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;数据表 -- XLS导为二进制文件还是Json或其他格式，读取接口和解析接口的定义。

UI层 -- 使用NGUI还是UGUI，界面基类，界面管理，输入事件封装，自定义通用组件基类，自定义各类通用组件。

外部资源管理 -— 是否使用AssetBundle，AssetBundle资源分类，AssetBundle资源间的依赖关系，加载与释放AssetBundle的管理，AssetBundle加密。

AI层 -— 使用状态机还是行为树或者其他，状态机或行为树接口实现，AI可视化工具，AI扩展接口。

地形地图 -— 地图是2D还是3D，场景编辑器的结构，是否需要Mesh合并，场景内的大小物件区别对待，大地形在游戏里该怎么逐步显示，是否要划分区块。

寻路与网格 -— 使用A星还是跳点算法或者其他，使用网格栅格还是三角网格，长距离寻路的解决方案，地图数据管理。

常用库 -— 时间函数，数学函数，数字变量加密封装，坐标转换函数，Debug调试工具，各大逻辑系统通用工具等等。

角色行为控制 -— 人物移动处理方案，摄像机的碰撞检测，动画特效编辑器，技能编辑器，行为流的建立。

2D动画控制 -— 动画组件封装，2D动画的制作流程，2D图合并为图集。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;实际工作中，我们对层级和模块逐个攻破的同时，也进入架构演化模式。一开始的做的架构中某个部位的并不适合，或需要改善，在后面的工作中，修复和完善架构是演化的重要步骤。&lt;/p&gt;

&lt;p&gt;在不断编写完善架构的过程中原本抽象简单的架构，开始复杂化。虽然每个模块都在有条不紊的进行中，但也会不断冒出各种各样不适应或者不符合实际需求的问题出现，我们需要及时跟进演化内容。去除、重构或者改善，前面由于各种原因而导致的错误的理解。&lt;/p&gt;

&lt;p&gt;最后架构设计的文档要及时跟进完善，在抽象的过程中，我们需要整理和记录整个过程，以便为今后在完善时能够一下子翻阅到并记起当时在架构时所考虑的各方面问题的原因。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第二章，架构(二) - 软件系统架构思维方式</title>
   <link href="http://www.luzexi.com/2018/07/24/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-%E6%9E%B6%E6%9E%842"/>
   <updated>2018-07-24T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/07/24/Unity3D高级编程之进阶主程-架构2</id>
   <content type="html">&lt;p&gt;前面对软件系统架构进行了一个深刻的解释。对什么是软件系统架构，为什么需要软件系统架构，怎样才算是优秀软件系统架构做了详细的分析。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;这节我们来说说在构建软件架构的过程中，我们需要的几种思维方式。我们生活和学习中常常有思维方式的转换，构建软件架构时也同样需要不同的思维方式。&lt;/p&gt;

&lt;p&gt;对于构建一个软件架构来说，从思维方式上入手有很多值得我们学习的地方，这涉及到如何抽象构建架构的思维方式。这篇我们就来讲讲，软件系统架构思维方式有哪些。&lt;/p&gt;

&lt;p&gt;架构既承载了我们对这个项目的抽象思维构建，也同时帮助了我们理清业务体系的方向。如果要说软件研发、系统架构中最重要的能力是什么，我会毫不犹豫回答是抽象能力。&lt;/p&gt;

&lt;p&gt;在系统架构和设计中，抽象能力是个比较重要的能力，其实抽象能力在生活也很重要，只是在软件设计架构中尤其的重要。一个项目在最初的设计时是没有可见目标的，也就是说我们需要凭空创造出一个我们能看到或想象的到的构建目标，这个目标大概率指向软件形成的最终形态不会偏离很多。抽象能力在这个特殊时期发挥了重要作用，它可以帮助我们在没有形成任何可见可幻想的实际目标之前，描绘出一个大致的轮廓，以至于我们在实现架构途中能有个可见的标准和目标。因此实际工作中抽象能力的强弱，直接决定我们所能解决问题的复杂度和规模大小。&lt;/p&gt;

&lt;p&gt;软件系统架构设计和小朋友搭积木无本质差异，只是解决的问题域和规模不同罢了。架构师先要在大脑中形成抽象概念，然后是子模块分解，以及子模块之间的沟通方式，然后是依次实现子模块，最后将子模块拼装组合起来，形成最后系统。我们常说编程和架构设计就是搭积木，优秀的架构师受职业习惯影响，眼睛里看到的世界都是模块化拼装组合式的。&lt;/p&gt;

&lt;p&gt;抽象能力不仅对软件系统架构设计重要，对建筑、商业、管理等人类其它领域活动也同样非常重要。可以这样认为，我们生存的世界都是在抽象的基础上构建起来的，离开抽象人类将对事物的构建寸步难行。&lt;/p&gt;

&lt;p&gt;我在一篇名为《优秀架构师必须掌握的架构思维》的文章中看到关于抽象能力分析很好的分析，以下部分内容引申了他的文章。&lt;/p&gt;

&lt;h3 id=&quot;第一种分层思维&quot;&gt;第一种，分层思维&lt;/h3&gt;

&lt;p&gt;分层是我们应对和管理复杂性的基本思维武器.&lt;/p&gt;

&lt;p&gt;面对一个复杂的系统，我们一开始总是无从下手，就好比一下子在我们面前摆了有很多很多的问题，杂乱无章。这很大程度都会导致我们慌张，焦急，惶恐等心理波动。分层思维，就能很好的帮助我们抽象一个复杂的系统的架构层次，从而清晰的描述了我们有多少层面的事务需要我们解决，以及解决层级的先后次序。&lt;/p&gt;

&lt;p&gt;构建一套复杂系统，我们把整个系统划分成若干个层次，每一层专注解决某个领域的问题，并向上提供服务。这样的抽象做法，让复杂的事务变得更加清晰有序。有些层次并不一定是横向的，也可以是纵向的，纵向的层次贯穿其他横向层次，称为共享层。如下图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/2/layer-solution.png&quot; alt=&quot;分层思维&quot; /&gt;&lt;/p&gt;

&lt;p&gt;下面我们来介绍几个用分层思维作为抽象方法的架构案例：&lt;/p&gt;

&lt;p&gt;一个中小型的Spring Web应用程序，我们一般会设计成三层架构：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/2/spring-java-layer.png&quot; alt=&quot;spring分层&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Linux操作系统是经典的分层架构，如下图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/2/linux-layer.png&quot; alt=&quot;Linux系统分层&quot; /&gt;&lt;/p&gt;

&lt;p&gt;TCP/IP协议栈也是经典的分层架构，如下图：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/2/tcp-ip-protocol-layer.gif&quot; alt=&quot;TCP-IP分层&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果你关注人类文明演化史，你会发现今天的人类世界也是以分层方式一层层搭建和演化出来的。今天的互联网系统可以认为是现代文明的一个层次，其上是基于互联网的现代商业，其下是现代电子工业基础设施，诸如此类。&lt;/p&gt;

&lt;h3 id=&quot;第二种分治思维&quot;&gt;第二种，分治思维&lt;/h3&gt;

&lt;p&gt;分而治之也是应对和管理复杂性的一般性方法，下图展示一个分治的思维流程：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/2/divide.png&quot; alt=&quot;分治法&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这是我2015年在思考Unity3D手游项目开发整体流程时，用分治法抽象出来的对整个问题的分解。我首先把code编码作为主中心，再把除了code的以外的事拆分成打包发布，资源部署到外网与检测，版本控制，项目管理平台。再对拆分出来的大块问题，进行细化，分解到具体的某个小问题。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/uploads/2015/03/%E5%AE%A2%E6%88%B7%E7%AB%AF%E7%8E%AF%E5%A2%83%E5%B8%83%E5%B1%80.jpg&quot; alt=&quot;手游开发流程&quot; /&gt;&lt;/p&gt;

&lt;p&gt;对于一个无法一次解决的大问题，我们会先把大问题分解成若干个子问题，如果子问题还无法直接解决，则继续分解成子子问题，直到可以直接解决的程度，这个是分解(divide)的过程；然后将子子问题的解组合拼装成子问题的解，再将子问题的解组合拼装成原问题的解，这个是组合(combine)的过程。&lt;/p&gt;

&lt;p&gt;在生活中分治思维，解决大问题，复杂问题，是很好手段。特别是当遇到那些你从未处理过的问题时，或者特别复杂超出你能力范围的问题时，把它分解、拆分、解刨、撕裂。把大问题先分成几大块的问题，再从这几大块问题入手，对每个大块问题再分解，拆分成小块问题。倘若小块问题仍然无法进行，或者还是没有思路，再拆分，再解刨，再分解，直到分解到你能开始着手解决了为止。这样一步步，一点点，把小的问题解决了，就是把大块问题解决了。随着时间的推移，不断解决细分的小问题，大块问题被迎刃而解，最后大块问题解决完后，更大块问题迎刃而解。&lt;/p&gt;

&lt;h3 id=&quot;第三种演化思维&quot;&gt;第三种，演化思维&lt;/h3&gt;

&lt;p&gt;经常有人在讨论：架构是设计出来的？还是演化出来的？我个人基于多年的经验认为，架构既是设计出来的，同时也是演化出来的，对于互联网系统，基本上可以说是三分设计，七分演化，而且是在设计中演化，在演化中设计，一个不断迭代的过程。&lt;/p&gt;

&lt;p&gt;在互联网软件系统的整个生命周期过程中，前期的设计和开发大致只占三分，在后面的七分时间里，架构师需要根据用户的反馈对架构进行不断的调整。我认为架构师除了要利用自身的架构设计能力，同时也要学会借助用户反馈和进化的力量，推动架构的持续演进，这个就是演化式架构思维。&lt;/p&gt;

&lt;p&gt;当然一开始的架构设计非常重要，架构定系统基本就成型了，不容马虎。同时，优秀的架构师深知，能够不断应对环境变化的系统，才是有生命力的系统，架构的好坏，很大部分取决于它应对变化的灵活性。所以具有演化式思维的架构师，能够在一开始设计时就考虑到后续架构的演化特性，并且将灵活应对变化的能力作为架构设计的主要考量。&lt;/p&gt;

&lt;p&gt;从单块架构开始，随着架构师对业务域理解的不断深入，也随着业务和团队规模的不断扩大，渐进式地把单块架构拆分成微服务架构的思路，这就是演化式架构的思维。如果你观察现实世界中一些互联网公司（例如eBay，阿里，Netflix等等）的系统架构，大部分走得都是演化式架构的路线。&lt;/p&gt;

&lt;p&gt;下图是建筑的演化史，在每个阶段，你可以看到设计的影子，但如果时间线拉得足够长，演化的特性就出来了。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/2/building-archiecture.png&quot; alt=&quot;建筑演化史&quot; /&gt;&lt;/p&gt;

&lt;p&gt;总结下，我们上文中我们强调了抽象思维在架构设计中的重要性，以及抽象思维的几种用法，包括分层思维，分治思维，以及演化思维，他们帮助我们在抽象的架构设计中起到了很好的作用。&lt;/p&gt;

&lt;p&gt;参考文献：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	1.《优秀架构师必须掌握的架构思维》作者：杨波
&lt;/code&gt;&lt;/pre&gt;
</content>
 </entry>
 
 <entry>
   <title>Have fun in watch video</title>
   <link href="http://www.luzexi.com/2018/07/23/have-fun-in-watch-video"/>
   <updated>2018-07-23T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/07/23/have-fun-in-watch-video</id>
   <content type="html">&lt;p&gt;This is a funy video I would like to share with you. Learn english should be more funy than u think. I prefer to find out the funy way to keep, not only the boring thing as learn words or do homework on paper.&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;The way I find out this time is a short funy video. It’s just has 3 minutes long to watch, but makes me laugh out loudly.&lt;/p&gt;

&lt;p&gt;The story happend in a normal company, a person which in HR department is having a interview with a job seeker.&lt;/p&gt;

&lt;p&gt;The especial thing is they are using a new Lie Detector which will make noise when you lie to others.&lt;/p&gt;

&lt;p&gt;Let’s start:&lt;/p&gt;

&lt;iframe frameborder=&quot;0&quot; width=&quot;640&quot; height=&quot;498&quot; src=&quot;https://v.qq.com/iframe/player.html?vid=w0161zniawq&amp;amp;tiny=0&amp;amp;auto=0&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;p&gt;Hahahaha. Did you laugh out loudly like I do?&lt;/p&gt;

&lt;p&gt;故事大致是这样的：两个人面试时用了‘测谎仪3000’，求职者说我要插管什么的吗，面试官说以前要的，不过最新研制的这个机器不用了。然后他让求职者说几个明显的谎话。求职者说了几个觉得很好玩，面试官看他不爽，又让他说几个真实的，有意思的是求职者说自己有12英尺的大屌，测谎仪居然没报错，面试官怀疑是不是机器坏了，求职者说自己确实有12英尺的大屌。面试官兴奋异常，原来面试官是个同性恋，还一直在掩饰自己的性取向。但是求职者品德很坏，被开除很多次，也偷过很多钱。面试官很愤怒，最后问了他一个问题，把他给潜规则了。。。。。&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>白领投资探讨(三)</title>
   <link href="http://www.luzexi.com/2018/07/20/%E7%99%BD%E9%A2%86%E6%8A%95%E8%B5%84%E6%8E%A2%E8%AE%A83"/>
   <updated>2018-07-20T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/07/20/白领投资探讨3</id>
   <content type="html">&lt;p&gt;最近P2P雷爆很多，据统计平均每天有2家网贷平台倒闭和跑路，据说把所有倒闭跑路的网贷平台资金总量加起来有2万亿，倒霉的都是老百姓，很多家庭的财富顷刻间灰飞烟灭。我们今天就来说说，用钱赚钱这事。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;首先我并不认为他们那些把钱投向P2P的人值得同情，他们只想躺着赚钱，脑经连动都不想动，懒且贪。对于这些人，我认为教点财商税，交点智商税，交点学费是应该的。不给他们教训，他们是不会明白，这个世界从来都没有容易赚的钱。&lt;/p&gt;

&lt;p&gt;要达到财务自由，只有一条路：勤奋，并且努力学习，并且节俭。&lt;/p&gt;

&lt;p&gt;所有违背这条规则的路，最终是无法到达胜利的彼岸。如果彻底违背这条规则，那么将是另一面的情景，地狱情景。&lt;/p&gt;

&lt;p&gt;也就是说，在金融投资中，但凡你简单看两眼，或者看几个新闻，听别人说几句话，就把钱投进去的，都会被吞没掉，或者严重亏损。&lt;/p&gt;

&lt;p&gt;我可以100%肯定的跟你说，所有金融理财公司，包括最大的和最厉害的，全部看上的是你的本金，而不是你的本金带给他们的利润，而你看上的却是他们口头上承诺的利息。&lt;/p&gt;

&lt;p&gt;我明白，这个结论跟大多数人的观念是冲突的，他们会用比如“专业的事情交给专业的人去做”，来反驳我。虽然这个道理在很多其他领域上是正确的，但在金融投资中，却是错误的，至少错了一大半。&lt;/p&gt;

&lt;p&gt;在金融领域，专业人士不能100%保证给你赚钱，其实他们来操作和你自己来操作的赔率差不多。因为金融就是不确定的代名词，没有人能预测，也没有人能掌控，政府都没有这个能力，何况是小老百姓。&lt;/p&gt;

&lt;p&gt;金融的不确定性是人类造成的，人类的情绪就是金融波动的原因，情绪好时所有人都会往上冲，而情绪差时，所有人都往后跑形成挤兑和踩踏。&lt;/p&gt;

&lt;p&gt;这就是为什么温家宝说，信心比黄金还珍贵的原因。我们说的信心不仅仅只在金融领域，在生活，工作，学习中也是同样的道理。特别是人民大众的信心，是由一个个小老百姓构成的，不是说颠覆就颠覆，说雄起就雄起的，没有人有这么大的能耐，天皇老子也不行。&lt;/p&gt;

&lt;p&gt;看起来，像是赌博一样的金融投资，不确定性太大。那么什么才是确定的？唯有坚持不懈的努力和勤奋才是确定的。任何一个国家，公司，个人，能最最确定的一样事情就是，勤奋努力能带给他们好结果。&lt;/p&gt;

&lt;p&gt;过去30年的中国经济大发展带给人们太多的误区，导致大多数人都认为，今后的30年也会像前面那样，快速发展，包括房地产，企业，金融，投资等还会快速的增长。殊不知，我们已经到达了一个瓶颈，说瓶颈还是太容易了点，毕竟瓶颈看起来只要稍微努力下就能突破，应该把瓶颈换成‘天花板’，让我们有可能这辈子都无法颠覆的天花板。这个天花板就是美国。&lt;/p&gt;

&lt;p&gt;很多国人还没有清醒的意识到美国的强大，以及怎么个强大法。简单解释下：&lt;/p&gt;

&lt;p&gt;以下内容，可能有人认为，我忽视了中国的潜力和中国作为全球第二大经济国家的力量。我只是说说美国强大在什么地方，并没有说中国完全不可能超越，也说没有哪里中国不好。&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;全球都用美元来做交易货币。导致美国很容易就能控制全球的现金流，让钱回到国内就能回来，想放出去就放出去。如果他大量印钞，全球都在帮他分担通货膨胀的危机，也就是说他可以随时从全球国家的口袋中抽点利息税什么的。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;美国是全球最大(交易量最大，金融产品最丰富)的金融交易场所。只要在美国的交易所里公认某某资产贬值了，全球的市场的相关产品都跟着下跌。这就是领头羊的效应。也就是说美国主导了整个全球的金融产品价格。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;美国是全球资金力量最集中的地方。全球大部分有钱的，有名望的都扎根在美国或者有根据地在美国，形成聚拢效应，越有钱的人越想去美国。导致美国不缺少风险投资的资金，任何有希望成为未来风向标，或者新科技的公司，都被大量资金包围着。做公司和产品的人，会更关注产品质量和体验，而不是分出很大一部分精力来融资。&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;中国现在的‘天花板’就是这样一个美国。一个GDP比你大几乎一倍，人均GDP比你大10倍的美国。而从历史角度看，美国从前都忽视了中国的崛起，可能忽视不太准确，用睁一只眼闭一只眼来形容更加确切。而当前不一样了，现在的中国，可以威胁到美国了，虽然还不能构成巨大颠覆性的威胁，但跟从前的弱小比起来那是相当不一样的影响。&lt;/p&gt;

&lt;p&gt;美国完全有理由站起来，主动打压中国。面对美国这种主动性的打压，中国前面的道路更加艰难。但也不是没有翻盘的机会，我们只要秉持，勤奋努力，脚踏实地的准则，坚持韬光养晦的政策，多一点真诚，少一点虚假，翻盘也是早晚的事。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第二章，架构(一) - 架构的意义</title>
   <link href="http://www.luzexi.com/2018/07/18/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-%E6%9E%B6%E6%9E%841"/>
   <updated>2018-07-18T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/07/18/Unity3D高级编程之进阶主程-架构1</id>
   <content type="html">&lt;h6 id=&quot;什么是架构-架构每天都有人在耳边提起架构到底是什么却很少有人说的清楚&quot;&gt;什么是架构? 架构每天都有人在耳边提起。架构到底是什么，却很少有人说的清楚。&lt;/h6&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;网络上解释的，比如，软件架构是一个系统的草图，又比如，软件体系结构是构建计算机软件实践的基础，还有，软件系统架构是一系列相关的抽象模式，用于指导大型软件系统各个方面的设计。说的都对，但是阐述还是过于模糊，懂的人本来就懂不用看，而不懂的人看了还是一头雾水。&lt;/p&gt;

&lt;p&gt;‘架构’这个词太抽象，导致难以准确定义，而现在的大部分书本和文章中讨论的’架构‘都是服务器端的部署图，所以大部分人一提到架构就觉得是几台服务器放这里，放那里，用什么软件连接合作，用什么框架开发扩展等等。&lt;/p&gt;

&lt;p&gt;我希望能这通过这个章节引导人们将错误的观念纠正过来。实际上架构无处不在，它实质上是解决生活和工作中的问题的一种方案。除了自己着手寻找解决问题的方案外，其他方案，比如直接购买现成的，或者直接放弃，或者以外包以及部分外包的形式，以还有合作模式等来对解决问题，也是切实可行的，其实都是值得考虑的解决问题的方案。我们所求的不是最贵的也不是最高级的，而是最好用的。&lt;/p&gt;

&lt;p&gt;在软件系统架构中，架构承担了解决项目从研发到上线运营的方案。前端渲染引擎的选择是自己研发一款引擎，还是使用商业引擎？商业引擎使用Unity3D还是Unreal还是其他？以及怎么用，是和UGUI一起用还是和NGUI一起用？UI里的事件系统如何做统一的处理？AI行为算法是选择行为树还是状态机抑或事件型决策树？数据如何获取和存储？场景如何拆分？是否需要将资源分离出去？是长连接还是短连接？服务器端是用C++还是Java抑或Python？是全部使用关系型数据库还是加入Cache机制？网络协议是用 protocal buff 还是 json 还是 xml 或者使用完全自定义格式等等等。&lt;/p&gt;

&lt;p&gt;这些项目中的每个子系统的都有自己的决策方向，而子系统的决策方向，把它们合起来加入一定的关联性就构成了一个完整架构整体，即每个系统、模块、组件都是软件系统架构中的一部分。&lt;/p&gt;

&lt;p&gt;优秀的架构师，需要对每个子系统决策的方向要进行深思熟虑，不仅只针对单个系统的决策方向，还要结合其他系统以及整体系统需求的方向进行决策。&lt;/p&gt;

&lt;p&gt;在架构设计中，为了能够更好的整理，思考，描述，表达，于是就有了架构图这个东西。架构师把架构中抽象的系统、模块、组件画在图上用圆圈、方块和文字表示，让自己和大家能够更加系统的认识到架构的意图，规范，以及子系统的细节。&lt;/p&gt;

&lt;p&gt;一个完整架构图会有很多细节的子系统、或者说子模块架构图，比如UML对象关系图就是一种，它描述了数据类之间的关系，把系统中对象模块用画图的方式描述清楚了。又比如部署图也是其中的一种，它把需要多少种服务器，分别起到什么作用，相互之间的关系描述清楚了，还有时序图，把系统程序调用的次序与流程描述清楚了。这些子系统的架构图合起来构成了一个完整的项目的架构图，最后才有了总体的架构图。如果把子系统架构细节略去，在不关心细节的情况下，描述各系统的合作方式，展现给人的是整体的解决方案，从宏观的角度下看整个项目的布局，会让人一目了然。&lt;/p&gt;

&lt;p&gt;为了让更多人理解软件系统架构的，我想把软件系统架构比喻的更切入实际生活一点。&lt;/p&gt;

&lt;p&gt;软件系统架构可以理解为是软件程序的架子，和现实中的书架相比有异曲同工之妙，这个架子上有很多大大小小的格子，每个格子里都可以放固定种类的程序。架子有大有小，大的需要花费点去做，小的轻便快捷。&lt;/p&gt;

&lt;p&gt;架子的大小是由做设计的设计师决定的，设计师根据客户的需求设计大小，假如放置的空间大，且需要承载的东西多，那么就往空间大的方向设计，能容纳更多的东西，能放置各种不同类型的程序，反之则做小一点，又快，又轻。&lt;/p&gt;

&lt;p&gt;架子完成后要拿出去用，如果一有什么不同的情况就倒了或散架了就不算是个好的架子了，所以架子的好坏有几个方面的评估。&lt;/p&gt;

&lt;h6 id=&quot;一承载力&quot;&gt;一，承载力。&lt;/h6&gt;

&lt;p&gt;书架上能放多少东西，能放多重的东西是使用者(这里使用者可以为客户，玩家，或程序员)比较关注的点。&lt;/p&gt;

&lt;p&gt;从软件架构的程序意义来说，一个架构能承载多少个逻辑系统，代码复杂度扩展到100万行代码代码时是否依然能够有序规范，程序员彼此工作的模块相互依存度有多少，能够承载多少个程序员共同工作因为能工共同工作的架构加速了开发与迭代，这是对软件架构承载力的评定。&lt;/p&gt;

&lt;p&gt;从架构的结果上来看，对于服务器来说，当前架构能承受多少人同时访问，日均访问量能承载多少，是承载力的体现。而对于客户端来说，能显示多少UI元素，可渲染多少模型（包括同屏渲染和非同屏渲染），数据交互能达到多少量。&lt;/p&gt;

&lt;p&gt;访问量承载太低，访问量一上来就都卡在加载上，大家就不再有这个耐心来看你的产品，运营和宣传部门的导量效果就大打折扣。同样的，客户端渲染承载不了过多元素时，帧率过低，画面卡顿现象严重，产品就不会得到认同。&lt;/p&gt;

&lt;p&gt;承载力是重要因素，但并非是唯一关键因素，这个世界这个社会讲究的永远是综合因素，一个点的好坏并不能决定全盘的好坏，而往往木桶效应里最短的那块木板才是。&lt;/p&gt;

&lt;h6 id=&quot;二可扩展度&quot;&gt;二，可扩展度。&lt;/h6&gt;

&lt;p&gt;如果书架上只能放书，这个书架的用途就太单一了，花瓶不能放、箱子不能放、鞋子不能放、袋子不能放、衣服不能放，客户八成不买单。&lt;/p&gt;

&lt;p&gt;架子适应不同类型的需求，添加不同类型的系统，不同功能的子系统，是非常必要的。软件架构也是同样的，需要具有高的可扩展度。&lt;/p&gt;

&lt;p&gt;而且可扩展度的关键在于，在添加新的子系统后不能影响或者只能尽可能的少量影响其他子系统的运作。假设添加了子系统后，所有系统都得重写或者重构，那就是灾难，前面花去的时间和人力物力精力全部‘浪费’，这是我们不想看到的，因此可扩展度也是衡量好的架构的非常重要标准。&lt;/p&gt;

&lt;h6 id=&quot;三易用性&quot;&gt;三，易用性。&lt;/h6&gt;

&lt;p&gt;易用性是架构师最最容易忽视的一个点，有了完整的架构，但不好用，而架构师却还是一心在推动它使用，导致开发效率的下降是常有的。&lt;/p&gt;

&lt;p&gt;这就好比，书架上要取个东西，如果取个东西需要先输入密码，再打开门，再剥去袋子，拿出来，把袋子放进箱子，关上门，当放回书架上去时，则再来一遍以上这些所有步骤，实在是太繁琐，即使功能再多，承载力再好，使用者也无法承受这么繁琐的步骤，而且都是机械重复的，精力和注意力都损耗在了没有意义的地方。&lt;/p&gt;

&lt;p&gt;易用性决定了架构的整体开发效率，程序员容易上手，子系统容易对接，开发效率自然就高，各模块各部件的编写只需要花一点点精力来关注架构的融合，其他所有精力和注意力都可以全部集中在自己的框架结构上，才能让各系统各尽其职将效率发挥到极致。&lt;/p&gt;

&lt;h6 id=&quot;四可伸缩力&quot;&gt;四，可伸缩力。&lt;/h6&gt;

&lt;p&gt;还是用书架比喻，假设我现在没有这么多书和东西要放，房子也不够大，我的书架是否可以折叠缩小到我需要的大小，是可伸缩力的体现。&lt;/p&gt;

&lt;p&gt;如果我们制造的书架可随时放大或随时折叠缩小的那就太好。软件架构也是同样的，当需要的承载量没有这么大时，是否可以不使用不需要的功能，化繁为简，只使用需要的部分。&lt;/p&gt;

&lt;p&gt;例如从服务器端的角度来说，当需要急速导入大量用户时到做能承载几百万人同时在线，服务器可随时扩展到几百上千台服务器来提高承载量，当访问量骤减，或者平时访问量比较少的情况下，访问量甚至低到只有几十个人在访问时，服务器可缩减到就几台机子在运作，这样大大缩减了服务器费用的开销，可以根据需要而随时变更架构的承载力来节省成本。&lt;/p&gt;

&lt;p&gt;而从客户端的角度来说，伸缩力体现在是否能适应大型项目众多人协同开发复杂系统，既能适应大成本消耗下的大项目大作品，也能适应小项目1-3个人团队小而快速的开发环境，小成本小作品极速迭代。&lt;/p&gt;

&lt;p&gt;在实际项目中，有时可伸缩力看起来并不是关键的因素，很多人误认为伸缩能力是种程序员的负担，甚至有的项目某些时期根本不需要伸缩力，只需要适应当前的特定时间的需求就可以了。不得不强调可伸缩力在架构中的关键位置，它是深入理解、设计架构的关键因素，是做出优秀的完整的架构的重要因素。&lt;/p&gt;

&lt;h6 id=&quot;五容错力以及错误的感知力&quot;&gt;五，容错力以及错误的感知力&lt;/h6&gt;

&lt;p&gt;书架也会磕磕碰碰的时候，同样也会在某处做工不精时遇到使用后歪斜，如果我们保证不了完全没有问题，至少我们需要保证它不会因为一点点小小的毛病而彻底散架。&lt;/p&gt;

&lt;p&gt;软件架构也是同样的，软件中错误、异常、BUG常有，设备何时损坏我们无法预估。容灾力首先起到了不让产品彻底不能使用的作用，有备份方案自动启用，也同时要能够让我们及时得知到问题发生，以及问题的所在，通过EMAIL发送或者通过短信、电话方式通知维护者，并且记录并保存错误信息。&lt;/p&gt;

&lt;p&gt;从服务端的角度来说，容灾力包括，数据库容灾能力，应用服务器容灾力，缓存服务器容灾力，以及中心服务器容灾力，每个机子倒下了都需要通知相关中心服务器改变策略，或者监控服务器检测得知该服务器倒下了，更换成备用服务器或者直接更换链路。&lt;/p&gt;

&lt;p&gt;从客户端角度来说，容灾力包括当数据发生错误时，是否同样能够继续保持运行而不崩溃，当程序出错时，是否依然能够继续运行其他程序，而不闪退或崩溃甚至再次启动也不能使用的状况发生。所有出现的错误，都能及时的记录并发送到服务器后台存储成为错误日志，便于开发人员能都及时得到详细的错误信息，根据错误信息能够快速找出问题的所在。&lt;/p&gt;

&lt;h6 id=&quot;在架构中这五项能力缺一不可某项能力特别突出也不能决定整个架构的好坏综合因素才是哪一项比较弱则问题会不断向该方向聚集直到最终出现大的问题甚至崩溃我们需要一个牢固的多样化的好用的可伸缩的有韧性的书架这也是我们在构建架构时所追求的&quot;&gt;在架构中这五项能力缺一不可，某项能力特别突出也不能决定整个架构的好坏，综合因素才是。哪一项比较弱，则问题会不断向该方向聚集，直到最终出现大的问题，甚至崩溃。我们需要一个牢固的，多样化的，好用的，可伸缩的，有韧性的书架，这也是我们在构建架构时所追求的。&lt;/h6&gt;

&lt;h6 id=&quot;其实万物是相通的木桶原理在各个地方都适用木桶上仅有一条或几条板比较长没用其他板都短照样撑不住多少水说到实际工作生活中就比如老板ceo是有局限性的对公司的宏观架构多厉害多好多明智下面的高管不给力一切都是白费力气高管再给力小弟们不给力还是一塌糊涂一个点的好坏并不能决定整体的走向契合我们的架构理论最宏观的整体架构是由所有子系统的架构来支撑的整体架构虽然比其他子系统的架构都更加重要但再好的整体架构也并不能起到决定性作用引申到战争中也是一样立于众多溃败下的一两场漂亮的以少胜多的精彩战斗还是无法扭转全局因为一个好的将军需要众多好的将士支撑一个好的司令需要众多好的将领以及更多的好的士兵支撑综合因素决定了成败架构也同样如此如何让所有的因素都朝着好的方向发展是所有架构师最终都需要思考和解决的问题&quot;&gt;其实万物是相通的，木桶原理在各个地方都适用，木桶上仅有一条或几条板比较长没用，其他板都短照样撑不住多少水。说到实际工作生活中就比如，老板CEO是有局限性的，对公司的宏观架构多厉害多好多明智，下面的高管不给力，一切都是白费力气，高管再给力，小弟们不给力，还是一塌糊涂。一个点的好坏并不能决定整体的走向。契合我们的架构理论，最宏观的整体架构，是由所有子系统的架构来支撑的，整体架构虽然比其他子系统的架构都更加重要，但再好的整体架构也并不能起到决定性作用。引申到战争中，也是一样，立于众多溃败下的一两场漂亮的以少胜多的精彩战斗，还是无法扭转全局，因为一个好的将军需要众多好的将士支撑，一个好的司令需要众多好的将领以及更多的好的士兵支撑。综合因素决定了成败，架构也同样如此，如何让所有的因素都朝着好的方向发展是所有架构师最终都需要思考和解决的问题。&lt;/h6&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第一章，C#要点技术(二) - Dictionary 底层源码剖析</title>
   <link href="http://www.luzexi.com/2018/07/18/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-CSharp%E8%A6%81%E7%82%B9%E6%8A%80%E6%9C%AF2"/>
   <updated>2018-07-18T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/07/18/Unity3D高级编程之进阶主程-CSharp要点技术2</id>
   <content type="html">&lt;p&gt;前文剖析了 List 的源码，我们明白了 List 是用数组构建而成的，增加，减少，插入的操作，都在数组中进行。我们还分析了大部分 List 的接口，包括Add，Remove，Insert，IndexOf，Find，Sort，ToArray，等等。我们得出了一个结论，List 是一个兼容性比较好的组件，但 List 在效率方面并没有做优化，线程也并不安全，需要加锁机制来保证线程的安全性。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;这次我们来对常用的另一个组件 Dictionary 组件进行底层源码的分析，看看我们常用的字典容器是如何构造而成的，它的优缺点如何。&lt;/p&gt;

&lt;h3 id=&quot;dictionary-底层代码&quot;&gt;Dictionary 底层代码&lt;/h3&gt;

&lt;p&gt;我们知道 Dictionary 字典型数据结构，是以关键字Key 和 值Value 进行一一映射的。Key的类型并没有做任何的限制，可以是整数，也可以是的字符串，甚至可以是实例对象。关键字Key是如何映射到内存的呢？&lt;/p&gt;

&lt;p&gt;其实没有什么神秘的，这种映射关系可以用一个Hash函数来建立，Dictionary 也确实是这样做的。这个Hash函数也并非神秘，我们可以简单的认为它只是做了一个模(Mod余)的操作，Dictionary 将每个Key加入容器的元素都要进行一次Hash哈希的运算操作，从而找到自己的位置。&lt;/p&gt;

&lt;p&gt;Hash函数可以有很多种算法，最简单的可以认为是余操作，比如当Key为整数93时&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;hash_key = Key % 30 = 3
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;对于对象和字符串来说，虽然没有直接点数字做标准，但也能以实例ID为标准来做Hash操作。实际算法可能没有我举例子这么简单，我们将在下面的源码剖析中详细讲解。&lt;/p&gt;

&lt;p&gt;对于不同的关键字可能得到同一哈希地址，即&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;key1 != key2 =&amp;gt; F(key1)=F(fey2)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这种现象叫做Hash哈希冲突，在一般情况下，冲突只能尽可能的少，而不能完全避免。因为哈希函数是从关键字范围到索引范围的映射，通常关键字范围要远大于索引范围，它的元素包括多个可能的关键字。既然如此，如何处理冲突则是构造哈希表不可不解决的一个问题。&lt;/p&gt;

&lt;p&gt;在处理Hash哈希冲突的方法中通常有：开放定址法、再哈希法、链地址法、建立一个公共溢出区等。Dictionary使用的解决冲突方法是拉链法，又称链地址法。&lt;/p&gt;

&lt;p&gt;拉链法的原理：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;将所有关键字为同义词的结点链接在同一个单链表中。若选定的散列表长度为n，则可将散列表定义为一个由n个头指针组成的指针数 组T[0..n-1]。凡是散列地址为i的结点，均插入到以T[i]为头指针的单链表中。T中各分量的初值均应为空指针。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在哈希表上进行查找的过程，和，在哈希表构建的过程是基本一致的。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;给定Key值，根据造表时设定的哈希函数求得哈希地址，若表中此位置没有记录，则查找不成功；否则比较关键字，若何给定值相等，则查找成功；否则根据处理冲突的方法寻找“下一地址”，直到哈希表中某个位置为空或者表中所填记录的关键字等于给定值时为止。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们来看看更形象的结构图，如下：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/book/1/lalianfa.png&quot; alt=&quot;拉链法结构图&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如图所示，拉链法结构中，主要的宿主为数组指针，每个数组元素里存放着指向下一个节点的指针，如果没有元素在单元上，则为空指针。当多个元素都指向同一个单元格时，则以链表的形式依次存放并列的元素。&lt;/p&gt;

&lt;h6 id=&quot;在-dictionary-中究竟是如何实现的呢我们来剖析一下源码&quot;&gt;在 Dictionary 中究竟是如何实现的呢，我们来剖析一下源码。&lt;/h6&gt;

&lt;p&gt;首先我们来看看源码中对 Dictionary 的变量定义部分，如下:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
public class Dictionary&amp;lt;TKey,TValue&amp;gt;: IDictionary&amp;lt;TKey,TValue&amp;gt;, IDictionary, IReadOnlyDictionary&amp;lt;TKey, TValue&amp;gt;, ISerializable, IDeserializationCallback 
{
    
    private struct Entry {
        public int hashCode;    // Lower 31 bits of hash code, -1 if unused
        public int next;        // Index of next entry, -1 if last
        public TKey key;           // Key of entry
        public TValue value;         // Value of entry
    }

    private int[] buckets;
    private Entry[] entries;
    private int count;
    private int version;
    private int freeList;
    private int freeCount;
    private IEqualityComparer&amp;lt;TKey&amp;gt; comparer;
    private KeyCollection keys;
    private ValueCollection values;
    private Object _syncRoot;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;从继承的类和接口看，Dictionary 主要继承了 IDictionary 接口，和 ISerializable 接口。IDictionary 和 ISerializable 在使用过程中，其主要的接口为，Add, Remove, ContainsKey, Clear, TryGetValue, Keys, Values, 以及[]数组符号形式作为返回值的接口。也包括了常用库 Collection 中的接口，Count, Contains等。&lt;/p&gt;

&lt;p&gt;从 Dictionary 的定义变量中可以看出，Dictionary 是以数组为底层数据结构的类。当我们实例化 new Dictionary() 后，内部的数组是0个数组的状态。与 List 组件一样，Dictionary 也是需要扩容的，会随着元素数量的增加而不断扩容。具体我们来看看下面的接口源码剖析。&lt;/p&gt;

&lt;p&gt;下面的我们将围绕上述的接口进行解析 Dictionary 底层运作机制。&lt;/p&gt;

&lt;h6 id=&quot;了解add是最直接了解底层数据结构如何运作的途径我们来看下add接口的实现&quot;&gt;了解Add是最直接了解底层数据结构如何运作的途径，我们来看下Add接口的实现。&lt;/h6&gt;

&lt;p&gt;源代码如下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
public void Add(TKey key, TValue value)
{
    Insert(key, value, true);
}

private void Initialize(int capacity)
{
    int size = HashHelpers.GetPrime(capacity);
    buckets = new int[size];
    for (int i = 0; i &amp;lt; buckets.Length; i++) buckets[i] = -1;
    entries = new Entry[size];
    freeList = -1;
}

private void Insert(TKey key, TValue value, bool add)
{
    if( key == null ) {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
    }

    if (buckets == null) Initialize(0);
    int hashCode = comparer.GetHashCode(key) &amp;amp; 0x7FFFFFFF;
    int targetBucket = hashCode % buckets.Length;

#if FEATURE_RANDOMIZED_STRING_HASHING
    int collisionCount = 0;
#endif

    for (int i = buckets[targetBucket]; i &amp;gt;= 0; i = entries[i].next) {
        if (entries[i].hashCode == hashCode &amp;amp;&amp;amp; comparer.Equals(entries[i].key, key)) {
            if (add) { 
                ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AddingDuplicate);
            }
            entries[i].value = value;
            version++;
            return;
        } 

#if FEATURE_RANDOMIZED_STRING_HASHING
        collisionCount++;
#endif
    }
    int index;
    if (freeCount &amp;gt; 0) {
        index = freeList;
        freeList = entries[index].next;
        freeCount--;
    }
    else {
        if (count == entries.Length)
        {
            Resize();
            targetBucket = hashCode % buckets.Length;
        }
        index = count;
        count++;
    }

    entries[index].hashCode = hashCode;
    entries[index].next = buckets[targetBucket];
    entries[index].key = key;
    entries[index].value = value;
    buckets[targetBucket] = index;
    version++;

#if FEATURE_RANDOMIZED_STRING_HASHING

#if FEATURE_CORECLR
    // In case we hit the collision threshold we'll need to switch to the comparer which is using randomized string hashing
    // in this case will be EqualityComparer&amp;lt;string&amp;gt;.Default.
    // Note, randomized string hashing is turned on by default on coreclr so EqualityComparer&amp;lt;string&amp;gt;.Default will 
    // be using randomized string hashing

    if (collisionCount &amp;gt; HashHelpers.HashCollisionThreshold &amp;amp;&amp;amp; comparer == NonRandomizedStringEqualityComparer.Default) 
    {
        comparer = (IEqualityComparer&amp;lt;TKey&amp;gt;) EqualityComparer&amp;lt;string&amp;gt;.Default;
        Resize(entries.Length, true);
    }
#else
    if(collisionCount &amp;gt; HashHelpers.HashCollisionThreshold &amp;amp;&amp;amp; HashHelpers.IsWellKnownEqualityComparer(comparer)) 
    {
        comparer = (IEqualityComparer&amp;lt;TKey&amp;gt;) HashHelpers.GetRandomizedEqualityComparer(comparer);
        Resize(entries.Length, true);
    }
#endif // FEATURE_CORECLR

#endif

}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;展示的代码稍稍多了点，我们摘出其中的要点，通过要点来了解重点，再通过重点了解全局。&lt;/p&gt;

&lt;p&gt;其实 Add 接口就是 Insert 的代理，因为它 Insert 一句话，那么 Inset 里做了什么呢？&lt;/p&gt;

&lt;p&gt;首先在加入数据前需要对数据结构进行构造。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;if (buckets == null) Initialize(0);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;其实在 Dictionary 构建时如果没有指定任何数量 buckets 就有可能是空的，所以需要对buckets进行初始化，Initialize(0)，说明构建的数量级最少。&lt;/p&gt;

&lt;p&gt;不过奥妙就在 Initialize 函数里，如果传入的参数不是0，而是5、10、25、或其他更大的数量的话，那么构造多大的数据结构才合适呢？&lt;/p&gt;

&lt;p&gt;在 Initialize 函数中，给了我们答案，看下面这行:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;int size = HashHelpers.GetPrime(capacity);
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;它们有专门的方法来计算到底该使用多大的数组，我们查出源码 HashHelpers 中，primes数值是这样定义的:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
 public static readonly int[] primes = {
        3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919,
        1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591,
        17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437,
        187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263,
        1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369};

public static int GetPrime(int min) 
{
    if (min &amp;lt; 0)
        throw new ArgumentException(Environment.GetResourceString(&quot;Arg_HTCapacityOverflow&quot;));
    Contract.EndContractBlock();

    for (int i = 0; i &amp;lt; primes.Length; i++) 
    {
        int prime = primes[i];
        if (prime &amp;gt;= min) return prime;
    }

    //outside of our predefined table. 
    //compute the hard way. 
    for (int i = (min | 1); i &amp;lt; Int32.MaxValue;i+=2) 
    {
        if (IsPrime(i) &amp;amp;&amp;amp; ((i - 1) % Hashtable.HashPrime != 0))
            return i;
    }
    return min;
}

// Returns size of hashtable to grow to.
public static int ExpandPrime(int oldSize)
{
    int newSize = 2 * oldSize;

    // Allow the hashtables to grow to maximum possible size (~2G elements) before encoutering capacity overflow.
    // Note that this check works even when _items.Length overflowed thanks to the (uint) cast
    if ((uint)newSize &amp;gt; MaxPrimeArrayLength &amp;amp;&amp;amp; MaxPrimeArrayLength &amp;gt; oldSize)
    {
        Contract.Assert( MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), &quot;Invalid MaxPrimeArrayLength&quot;);
        return MaxPrimeArrayLength;
    }

    return GetPrime(newSize);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述代码为 HashHelpers 部分的源码，其中 GetPrime 会返回一个需要的 size 最小的数值，从 GetPrime 函数的代码中，我们可以知道这个 size 是由数组 primes 里的值与当前需要的数量大小有关，当需要的数量小于 primes 某个单元格的数字时返回该数字，而 ExpandPrime 则更加简单粗暴，直接返回原来size的2倍作为扩展数量。&lt;/p&gt;

&lt;p&gt;从Prime的定义看的出，首次定义size为3，每次扩大2倍，也就是，3-&amp;gt;7-&amp;gt;17-&amp;gt;37-&amp;gt;…. 底层数据结构的大小是按照这个数值顺序来扩展的，除非你在创建 Dictionary 时，先定义了他的初始大小，指定的初始大小也会先被 GetPrime 计算该分配的数量最终得到应该分配的数组大小。这和 List 组件的分配方式一模一样。&lt;/p&gt;

&lt;p&gt;我们继续看初始化后的内容，对关键字 Key 做Hash哈希操作从而获得地址索引:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    int hashCode = comparer.GetHashCode(key) &amp;amp; 0x7FFFFFFF;
    int targetBucket = hashCode % buckets.Length;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;当调用函数获得Hash哈希值后，还需要对哈希地址做余操作，以确定地址落在 Dictionary 数组长度范围内不会溢出。&lt;/p&gt;

&lt;p&gt;紧接着对指定数组单元格内的链表元素做遍历操作，找出空出来的位置将值填入。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;for (int i = buckets[targetBucket]; i &amp;gt;= 0; i = entries[i].next) {
    if (entries[i].hashCode == hashCode &amp;amp;&amp;amp; comparer.Equals(entries[i].key, key)) {
        if (add) { 
            ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AddingDuplicate);
        }
        entries[i].value = value;
        version++;
        return;
    } 

#if FEATURE_RANDOMIZED_STRING_HASHING
    collisionCount++;
#endif
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这一步就是前面我们所说的拉链法的链表推入动作。当获得Hash值的数组索引后，我们知道了该将数据存放在哪个数组位置上，如果该位置已经有元素被推入，则需要将其推入到链表的尾部。从for循环开始，检查是否到达链表的末尾，最后将数据放入尾部，并结束函数。&lt;/p&gt;

&lt;p&gt;如果数组的空间不够了怎么办？源码中体现了这一点:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;int index;
if (freeCount &amp;gt; 0) {
    index = freeList;
    freeList = entries[index].next;
    freeCount--;
}
else {
    if (count == entries.Length)
    {
        Resize();
        targetBucket = hashCode % buckets.Length;
    }
    index = count;
    count++;
}

entries[index].hashCode = hashCode;
entries[index].next = buckets[targetBucket];
entries[index].key = key;
entries[index].value = value;
buckets[targetBucket] = index;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;当被用来记录剩余单元格数量的变量 freeCount 等于0时，则进行扩容，扩容后的大小就是我们前面提到的 调用 ExpandPrime 后的数量，即通常情况下为原来的2倍，再根据这个空间大小数字调用 GetPrime 来得到真正的新数组的大小。&lt;/p&gt;

&lt;h6 id=&quot;了解了add接口我们来看看remove部分&quot;&gt;了解了Add接口，我们来看看Remove部分。&lt;/h6&gt;

&lt;p&gt;删除的过程和插入的过程比较相似，因为要查找到Key元素所在位置，所以再次将Key值做哈希操作也是难免的，然后类似沿着拉链法的模式寻找与关键字匹配的元素。&lt;/p&gt;

&lt;h6 id=&quot;remove-用关键字删除元素的接口源码&quot;&gt;Remove 用关键字删除元素的接口源码：&lt;/h6&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
public bool Remove(TKey key)
{
    if(key == null) {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
    }

    if (buckets != null) {
        int hashCode = comparer.GetHashCode(key) &amp;amp; 0x7FFFFFFF;
        int bucket = hashCode % buckets.Length;
        int last = -1;
        for (int i = buckets[bucket]; i &amp;gt;= 0; last = i, i = entries[i].next) {
            if (entries[i].hashCode == hashCode &amp;amp;&amp;amp; comparer.Equals(entries[i].key, key)) {
                if (last &amp;lt; 0) {
                    buckets[bucket] = entries[i].next;
                }
                else {
                    entries[last].next = entries[i].next;
                }
                entries[i].hashCode = -1;
                entries[i].next = freeList;
                entries[i].key = default(TKey);
                entries[i].value = default(TValue);
                freeList = i;
                freeCount++;
                version++;
                return true;
            }
        }
    }
    return false;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们注意到 Remove 接口相对 Add 接口简单的多，同样用哈希函数 comparer.GetHashCode 再除余后得到范围内的地址索引，再做余操作确定地址落在数组范围内，从哈希索引地址开始，查找冲突的元素的Key是否与需要移除的Key值相同，相同则进行移除操作并退出。&lt;/p&gt;

&lt;p&gt;注意源码中，Remove 的移除操作并没有对内存进行删减，而只是将其单元格置空，这是位了减少了内存的频繁操作。&lt;/p&gt;

&lt;h6 id=&quot;我们继续剖析另一个重要的接口-containskey-检测是否包含关键字的接口源码如下&quot;&gt;我们继续剖析另一个重要的接口 ContainsKey 检测是否包含关键字的接口。源码如下：&lt;/h6&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
public bool ContainsKey(TKey key)
{
    return FindEntry(key) &amp;gt;= 0;
}

private int FindEntry(TKey key)
{
    if( key == null) {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
    }

    if (buckets != null) {
        int hashCode = comparer.GetHashCode(key) &amp;amp; 0x7FFFFFFF;
        for (int i = buckets[hashCode % buckets.Length]; i &amp;gt;= 0; i = entries[i].next) {
            if (entries[i].hashCode == hashCode &amp;amp;&amp;amp; comparer.Equals(entries[i].key, key)) return i;
        }
    }
    return -1;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;从源码中看到 ContainsKey 是一个查找Key位置的过程。它调用了 FindEntry 函数，FindEntry 查找Key值位置的方法跟我们前面提到的相同。从用Key值得到的哈希值地址开始查找，查看所有冲突链表中，是否有与Key值相同的值，找到即刻返回该索引地址。&lt;/p&gt;

&lt;h6 id=&quot;有了前面对几个核心接口理解的基础其他接口相对比较就简单多了我们快速的看过去&quot;&gt;有了前面对几个核心接口理解的基础，其他接口相对比较就简单多了，我们快速的看过去。&lt;/h6&gt;

&lt;h6 id=&quot;trygetvalue-尝试获取值的接口&quot;&gt;TryGetValue 尝试获取值的接口:&lt;/h6&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
public bool TryGetValue(TKey key, out TValue value)
{
    int i = FindEntry(key);
    if (i &amp;gt;= 0) {
        value = entries[i].value;
        return true;
    }
    value = default(TValue);
    return false;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;与 ContainsKey 同样，他调用的也是FindEntry的接口，来获取Key对应的Value值。&lt;/p&gt;

&lt;h6 id=&quot;对操作符的重定义源码&quot;&gt;对[]操作符的重定义，源码:&lt;/h6&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
public TValue this[TKey key] {
    get {
        int i = FindEntry(key);
        if (i &amp;gt;= 0) return entries[i].value;
        ThrowHelper.ThrowKeyNotFoundException();
        return default(TValue);
    }
    set {
        Insert(key, value, false);
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在重新定义[]符号的代码中，获取元素时也同样使用 FindEntry 函数，而 Set 设置元素时则使用与 Add 调用相同的 Insert函数，它们都是同一套方法，即哈希拉链冲突解决方案。&lt;/p&gt;

&lt;h6 id=&quot;从源码剖析来看哈希冲突的拉链法贯穿了整个底层数据结构因此哈希函数是关键了哈希函数的好坏直接决定了效率高低&quot;&gt;从源码剖析来看，哈希冲突的拉链法贯穿了整个底层数据结构。因此哈希函数是关键了，哈希函数的好坏直接决定了效率高低。&lt;/h6&gt;

&lt;p&gt;既然这么重要，我们来看看哈希函数的创建过程，比较函数的创建的源码：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
private static EqualityComparer&amp;lt;T&amp;gt; CreateComparer()
{
    Contract.Ensures(Contract.Result&amp;lt;EqualityComparer&amp;lt;T&amp;gt;&amp;gt;() != null);

    RuntimeType t = (RuntimeType)typeof(T);
    // Specialize type byte for performance reasons
    if (t == typeof(byte)) {
        return (EqualityComparer&amp;lt;T&amp;gt;)(object)(new ByteEqualityComparer());
    }
    // If T implements IEquatable&amp;lt;T&amp;gt; return a GenericEqualityComparer&amp;lt;T&amp;gt;
    if (typeof(IEquatable&amp;lt;T&amp;gt;).IsAssignableFrom(t)) {
        return (EqualityComparer&amp;lt;T&amp;gt;)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(GenericEqualityComparer&amp;lt;int&amp;gt;), t);
    }
    // If T is a Nullable&amp;lt;U&amp;gt; where U implements IEquatable&amp;lt;U&amp;gt; return a NullableEqualityComparer&amp;lt;U&amp;gt;
    if (t.IsGenericType &amp;amp;&amp;amp; t.GetGenericTypeDefinition() == typeof(Nullable&amp;lt;&amp;gt;)) {
        RuntimeType u = (RuntimeType)t.GetGenericArguments()[0];
        if (typeof(IEquatable&amp;lt;&amp;gt;).MakeGenericType(u).IsAssignableFrom(u)) {
            return (EqualityComparer&amp;lt;T&amp;gt;)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(NullableEqualityComparer&amp;lt;int&amp;gt;), u);
        }
    }
    
    // See the METHOD__JIT_HELPERS__UNSAFE_ENUM_CAST and METHOD__JIT_HELPERS__UNSAFE_ENUM_CAST_LONG cases in getILIntrinsicImplementation
    if (t.IsEnum) {
        TypeCode underlyingTypeCode = Type.GetTypeCode(Enum.GetUnderlyingType(t));

        // Depending on the enum type, we need to special case the comparers so that we avoid boxing
        // Note: We have different comparers for Short and SByte because for those types we need to make sure we call GetHashCode on the actual underlying type as the 
        // implementation of GetHashCode is more complex than for the other types.
        switch (underlyingTypeCode) {
            case TypeCode.Int16: // short
                return (EqualityComparer&amp;lt;T&amp;gt;)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(ShortEnumEqualityComparer&amp;lt;short&amp;gt;), t);
            case TypeCode.SByte:
                return (EqualityComparer&amp;lt;T&amp;gt;)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(SByteEnumEqualityComparer&amp;lt;sbyte&amp;gt;), t);
            case TypeCode.Int32:
            case TypeCode.UInt32:
            case TypeCode.Byte:
            case TypeCode.UInt16: //ushort
                return (EqualityComparer&amp;lt;T&amp;gt;)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(EnumEqualityComparer&amp;lt;int&amp;gt;), t);
            case TypeCode.Int64:
            case TypeCode.UInt64:
                return (EqualityComparer&amp;lt;T&amp;gt;)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(LongEnumEqualityComparer&amp;lt;long&amp;gt;), t);
        }
    }
    // Otherwise return an ObjectEqualityComparer&amp;lt;T&amp;gt;
    return new ObjectEqualityComparer&amp;lt;T&amp;gt;();
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们看到源码中，对数字，byte，有‘比较’接口(IEquatable&amp;lt;T&amp;gt;)，和没有‘比较’接口，四种方式进行了区分对待。&lt;/p&gt;

&lt;p&gt;对于像数字和byte类的，比较容易比较，所以它们都是一类，且是有相应固定的比较函数的。而有‘比较’接口(IEquatable&amp;lt;T&amp;gt;)的实体，则直接使用GenericEqualityComparer&amp;lt;T&amp;gt;来获得哈希函数。最后那些没有‘比较’接口(IEquatable&lt;T&gt;)的实体，如果继承了 Nullable\&amp;lt;U\&amp;gt; 接口，则使用一个叫 NullableEqualityComparer 的比较函数来代替。如果什么都不是，就只能使用 ObjectEqualityComparer\&amp;lt;T\&amp;gt; 默认的对象比较方式来做比较了。&lt;/T&gt;&lt;/p&gt;

&lt;p&gt;在C#里所有类都继承了 Object 类，所以即使没有特别的重写 Equals 函数，都会使用 Object 类的 Equals 函数:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
public virtual bool Equals(Object obj)
{
    return RuntimeHelpers.Equals(this, obj);
}

[System.Security.SecuritySafeCritical]  // auto-generated
[ResourceExposure(ResourceScope.None)]
[MethodImplAttribute(MethodImplOptions.InternalCall)]
public new static extern bool Equals(Object o1, Object o2);

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;而这个 Equals 两个对象的比较，是以内存地址为基准的。&lt;/p&gt;

&lt;h6 id=&quot;dictionary-同list一样并不是线程安全的组件官方源码中进行了这样的解释&quot;&gt;Dictionary 同List一样并不是线程安全的组件，官方源码中进行了这样的解释。&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;    ** Hashtable has multiple reader/single writer (MR/SW) thread safety built into 
    ** certain methods and properties, whereas Dictionary doesn't. If you're 
    ** converting framework code that formerly used Hashtable to Dictionary, it's
    ** important to consider whether callers may have taken a dependence on MR/SW
    ** thread safety. If a reader writer lock is available, then that may be used
    ** with a Dictionary to get the same thread safety guarantee. 
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Hashtable在多线程读写中是线程安全的，而 Dictionary 不是。如果要在多个线程中共享Dictionaray的读写操作，就要自己写lock以保证线程安全。&lt;/p&gt;

&lt;h3 id=&quot;到这里我们已经全面了解了-dictionary-的内部构造和运作机制他是由数组构成并且由哈希函数完成地址构建由拉链法冲突解决方式来解决冲突&quot;&gt;到这里我们已经全面了解了 Dictionary 的内部构造和运作机制。他是由数组构成，并且由哈希函数完成地址构建，由拉链法冲突解决方式来解决冲突。&lt;/h3&gt;

&lt;h3 id=&quot;从效率上看同list一样最好在-实例化对象时即-new-时尽量确定大致数量会更加高效另外用数值方式做key比用类实例方式作为key值更加高效率&quot;&gt;从效率上看，同List一样最好在 实例化对象时，即 new 时尽量确定大致数量会更加高效，另外用数值方式做Key比用类实例方式作为Key值更加高效率。&lt;/h3&gt;

&lt;h3 id=&quot;从内存操作上看大小以3-7-17-37-的速度每次增加2倍多的顺序进行删除时并不缩减内存&quot;&gt;从内存操作上看，大小以3-&amp;gt;7-&amp;gt;17-&amp;gt;37-&amp;gt;….的速度，每次增加2倍多的顺序进行，删除时，并不缩减内存。&lt;/h3&gt;

&lt;h3 id=&quot;如果想在多线程中共享-dictionary-则需要进行我们自己进行lock操作&quot;&gt;如果想在多线程中，共享 Dictionary 则需要进行我们自己进行lock操作。&lt;/h3&gt;

&lt;p&gt;&lt;a href=&quot;https://referencesource.microsoft.com/#mscorlib/system/collections/generic/dictionary.cs&quot;&gt;Dictionary源码&lt;/a&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第三章，数据表(三) - 多语言的实现</title>
   <link href="http://www.luzexi.com/2018/07/17/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-%E6%95%B0%E6%8D%AE%E8%A1%A83"/>
   <updated>2018-07-17T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/07/17/Unity3D高级编程之进阶主程-数据表3</id>
   <content type="html">&lt;p&gt;前文介绍了关于数据表的几种形式，以及如何让数据表运用更加简单高效，这篇我们来讲讲多语言在数据表中的实现方式。&lt;/p&gt;

&lt;p&gt;游戏项目中文字显示本身就是件比较头疼的事，再加上多语言，更多的问题将待需解决。很多时候项目起初，文本是写进代码里的，但是当项目中后期，文字又需要由策划来修改和添加，这个导致了大量的程序员的重复工作。所以文字的数据还是放在表里比较好些，就让该考虑文字内容的人去考虑文字内容吧，我们需要把这块工作分离出来，完全交给独立的人处理。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;为了实现这个独立模块，我们需要多数据配置表的导出做些规则的设计。&lt;/p&gt;

&lt;p&gt;我们来分析下，一般文字放在Excel表里一般都会以Key-Value形式存放，就比如，Key为”RoleName”,Value为”角色名字”，或者Key为1101,Value为”好友分享”等。&lt;/p&gt;

&lt;p&gt;这种Key-Value形式，一般会以Int-String形式存在，或者string-string形式存在。&lt;/p&gt;

&lt;p&gt;我们先来讨论下这两种形式。&lt;/p&gt;

&lt;p&gt;你从数据表里获取文字的方式，你是喜欢以整数为键值还是字符串形式呢？&lt;/p&gt;

&lt;p&gt;用整数形式获取就会像这样的样式存在&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;string content = GetTextString(12)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这种形式，看起来不是很美观，对于其他程序员来说，或者我们过了几周再回头来看时，我们怎么知道12代表什么？只能猜，这个12可能是某个字符串。随着代码的增多文字量的增多，对应数字Key也增多，我们很难识别这句话是代表什么，调试起来会很麻烦。一个项目一般会有10-30万行代码，到处都是这种形式的字符串获取方式，任何人看起来都会崩溃。维护性太差，校验检查难度太大，效率太低。&lt;/p&gt;

&lt;p&gt;如果用字符串形式获取&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	string content = GetTextString(“FightWin”)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这种形式，会好些吗？看起来似乎好了些，至少我知道了我获取的大概是什么内容的字符串。不过任然有问题，你用一个字符串去获取另一个字符串，那岂不是双份内存，GC垃圾回收的消耗也会同时增加。原本只需要存储一个字符串就够了，现在要存两个，就因为用了键值字符串去获取内容字符串。&lt;/p&gt;

&lt;p&gt;当文字内容很多时，我们可能需要用很长的字符串去获取另一个很长的内容，比如用”BattleSceneFightAllianceWin” 去取”联盟战胜利了”，这种形式的字符串换字符串，导致文字数据表变得很大，内存占用量也加大了很多，因为你要另外存一份常量的字符串。&lt;/p&gt;

&lt;p&gt;那么我们来想个更好的方法吧，我们既要用简洁的数字去代表文字，又要让键值看起来形象。怎么办？&lt;/p&gt;

&lt;p&gt;我们策略是生成一个类，用变量的形式去记录文字的ID，在文字表生成数据表，同时生成数据定义类，使用变量去代表数字。我们依然在表里填字符串对应字符串，比如上面提到的”BattleSceneFightAllianceWin” 对应”联盟战胜利了”，在导出xls数据文件时，生成一个类文件，专门把Key值按次序写进类中当变量。如下&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c#&quot; data-lang=&quot;c#&quot;&gt;&lt;span class=&quot;n&quot;&gt;Class&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TextKey&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BattleSceneFightAllianceWin&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;BattleSceneFightAllianceLose&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;再把“联盟战胜利了”这种文本数据按次序，依次写入数据文件。这样就可以一一对应了。也就是，第一个变量对应第一个文字，第二个变量对应第二个文字。获取文本的方式改为了&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;string content = GetTextString(TextKey.BattleSceneFightAllianceWin)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这时文本数据的排列是如下&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;联盟战胜利了
联盟战失败了
…
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;而程序变量生成后为如下：&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-c#&quot; data-lang=&quot;c#&quot;&gt;&lt;span class=&quot;n&quot;&gt;Class&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TextKey&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;BattleSceneFightAllianceWin&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
	&lt;span class=&quot;n&quot;&gt;BattleSceneFightAllianceLose&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;文字与变量的数字依次对应，既解决了用数字做Key不够形象的问题，又解决了字符串做Key太多冗余的问题。&lt;/p&gt;

&lt;p&gt;那么多语言部分怎么处理？&lt;/p&gt;

&lt;p&gt;简单的处理方式就是做多个表，每个表一个语言。获取方式可以根据不同语言来获取，如下&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;string content = GetTextString(”win”,Language.Chinese)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;不过每次增加删除都要一一对应，否则一个语言没有改就会报错，调试起来非常麻烦。&lt;/p&gt;

&lt;p&gt;优化下合并数据表，把一个表里的一个Key对应多个语言的文字内容写在一个表里面。如下&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;键值  中文  英文  日文   韩文
Win   赢了  Win  勝った 이기다
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这样就有了规则，即第2列是中文，第3列是英文，第4列是日文，第5列是韩文，依次类推。&lt;/p&gt;

&lt;p&gt;接口没变，任然是&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;string content = GetTextString(”win”,Language.Chinese)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;但是文字表在编辑时就更加的形象，方便，快捷了。&lt;/p&gt;

&lt;p&gt;做到这里多语言部分就完美了。策划设计人员和运营人员只要关心文字语言表里内容和键值是否正确就可以了，其他都可以完全交给程序员处理了。&lt;/p&gt;

&lt;p&gt;关于分散读取，和集中读取，以及预读取部分，我们也在这里做个讲解。&lt;/p&gt;

&lt;p&gt;假如把所有表都集中起来成一个表，那么游戏在加载数据表就需要一次集中使用CPU去处理，导致游戏有时会卡顿现象，不合理。我们需要让游戏表现的尽可能的顺畅。&lt;/p&gt;

&lt;p&gt;所以分散读取比较可取，各个表数据都自己管自己读取吧，这样就CPU就分散开来了，不会一下子对CPU的需求量很大。而且在读取数据表时，要按需读取，而不是一开始就初始化，这样的话跟集中在一起就没有区别了。&lt;/p&gt;

&lt;p&gt;至于预读取，其实和提前集中读取没有区别，关键是如何利用空挡时间进行预读取。比如在Loading等待时读取一部分数据，这样在等待时也不会浪费CPU。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第三章，数据表(二) - 数据表的制作方式</title>
   <link href="http://www.luzexi.com/2018/07/17/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-%E6%95%B0%E6%8D%AE%E8%A1%A82"/>
   <updated>2018-07-17T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/07/17/Unity3D高级编程之进阶主程-数据表2</id>
   <content type="html">&lt;h1 id=&quot;第三章数据表二&quot;&gt;第三章，数据表(二)&lt;/h1&gt;

&lt;h3 id=&quot;本篇来介绍下数据表的几种制作数据的方式&quot;&gt;本篇来介绍下数据表的几种制作数据的方式。&lt;/h3&gt;

&lt;p&gt;Excel是大部分数值策划选择用的填数工具，因为Excel是天生为数据处理而生，而Excel转为什么格式就需要选择了。&lt;/p&gt;

&lt;p&gt;最简单的就是直接将Excel里的数据复制黏贴到文本文件作为游戏数据。这种简单快捷的方式任何人都能做，但问题是当需要多个Excel转化为文件数据时，我们就遇到麻烦了，每次都要手动复制粘贴一遍所有的Excel数据文件到指定的文本文件中，这种人工手动操作的事，常常会引起不少麻烦，当我们手动导入我们常还要想想，是不是复制粘贴错了，有可能是真的操作错了或者只是自己健忘。在最初的几年的程序开发历程中，为了让Demo加快进度，也这么干过，总是会需要我们查好几次莫名奇妙的数据表错误问题，其实都是因为人脑的极限，人脑对重复的工作有自己的极限，到达了这种枯燥的极限，我们就会增大犯错的概率和次数，于是时常会因为重复劳动太多次而出错。&lt;/p&gt;

&lt;p&gt;有比较简单的直接Excel手动另存为导出CSV，就有了CSV的一个规范。
这样既有了规范，又不怕复制粘贴错误。不过不要高兴太早，这样依然会有很多问题，比如当我们导出多个文件时，我们枯燥乏味的重复劳动还是很多，出错的概率依然很高。&lt;/p&gt;

&lt;p&gt;于是为了避免出错概率和次数，自动化和流水线是成为了进阶的方式，将所有需要人工操作的流程，全部写入程序，让程序来帮助我们完成我们想要完成工作。&lt;/p&gt;

&lt;p&gt;主流的制作自动化程序方式有很多，例如使用Shell或Bat(window批处理)设计自动化流程操作，在Mac或Windows下执行我们编写的批处理文件就能自动一步步地执行我们规则的操作步骤。&lt;/p&gt;

&lt;p&gt;也有自己通过特定语言写自动化程序的，比如C#从Excel中读取数据后写入特定文件，会使用.Net库，或者其他第三方库来取得Excel里的数据，再将数据以自己希望的格式输出到文件中。&lt;/p&gt;

&lt;p&gt;很多同学还使用Jenkins来强化自动化流水线。Jenkins可以认为是一个电脑中待命的程序，它有自己的本地站点，可以通过网页的形式，添加我们需要执行的操作或程序命令，还可以设置运行的时间和次数，每次运行结束都会有失败和成功的信息显示，还会有很多错误的日志记录在里面。&lt;/p&gt;

&lt;p&gt;Jenkins是很多公司的自动化流水线必备工具，比如打包，转换数据表，同步上传，自动化检测，自动运行等功能被广大的高级程序员所喜爱。&lt;/p&gt;

&lt;p&gt;当然Jenkins也不是万能的，并不是说一定要使用它，我们也可以有自己的流水线制作途径。Jenkins只是多了一个可视化的Web页面，它同样需要借助特定的语言，比如Shell或C#或Python或Ruby等来编写我们需要的操作过程，甚至这些语言的组合起来的操作流程也是很常见的。如果这些你都不太熟悉，还可以使用Unity3D的菜单栏编辑功能，实现点击菜单栏按钮后执行一套相关程序，也是种不错的选择。&lt;/p&gt;

&lt;p&gt;自动化流水线的最终目标是让人力成本降到最低，节省所有能节省的精力消耗，把所有人员的注意力都集中到核心问题上去，比如框架，核心战斗，变化多端的渲染方式等。&lt;/p&gt;

&lt;h3 id=&quot;让数据使用起来更加方便&quot;&gt;让数据使用起来更加方便&lt;/h3&gt;

&lt;p&gt;用什么形式的文件作为数据表并不最最重要的，CSV也照样能把游戏跑得很好，因为这些技术并不能决定游戏的性能会有大幅的偏差，只要我们喜欢，什么形式都可以。很多时候，我们在选定数据存储规则时，大都选择的是自己喜欢的方式去规划，并且符合团队做事风格，因为这能给我们带来很大的工作的效率，沟通的效率，也加快了开发速度，让团队不用这么痛苦并且浪费时间去适应新的规则。&lt;/p&gt;

&lt;p&gt;数据表的关键作用是连接游戏策划设计师与其他部门的桥梁，所以我们在指定数据导入导出规则的时候需要考虑设计师们体验的因素。如何让策划在配置数据表的时候能够有更好的体验就成了关键，好用是关键。&lt;/p&gt;

&lt;p&gt;为什么要好用呢？只有好用才能提高效率，同事们都用着舒心顺畅，无意中降低了工作中的精神损耗，减少了精力损耗，打通了沟通交流上的问题，从而提高了效率。&lt;/p&gt;

&lt;p&gt;前面提到的自动化就是提高效率的一种方法，还有比如一键转化XXX，这种形式的也是提高效率的好办法。&lt;/p&gt;

&lt;p&gt;一键转化Excel到其他格式是一个比较人性化的工具，不需要人工手动去转化，通过工具就能搞定，只要数值策划按照你们双方约定的规则就行。这能大大提高数值策划与程序的协调性，一个系统，一个模块，需要什么数值，什么类型的数值，数据表建立的流程，在你们约定的填表规则上，建立，读取，转化，变得轻而易举。这种在规则下，大家都遵守同一规则，减少了沟通时间以及沟通的障碍，彼此能默契的合作，是多么高效和舒心。&lt;/p&gt;

&lt;p&gt;不过只有自动化，或者只有一键XXXX的功能，还是不够。这些只是工具，我们要的是团队间的，特别是部门间的，不同工种间的配合，能顺畅无障碍。&lt;/p&gt;

&lt;p&gt;如果只是单个表有了自动化，策划设计人员可以自由的将Excel数据转化成能让程序员读取的数据格式，但是策划设计人员一直在对数据进行变动，特别是对字段的类型，字段的名字，一直在变。今天这个字段定义为id，明天这个字段成了是time了。或者插入了一个新的字段，删掉了旧的字段，或者新增了一个数据表，或者删掉了一个旧的数据表，等等。这让程序员很头疼，每次更改都需要及时得到通知程序员，即使及时得到通知，也会遇到不少的麻烦。&lt;/p&gt;

&lt;p&gt;因此我们要再深入加强这种规则的好用性，将单个表，边为多个表的自定义配置，将所有策划内容的数据表和导出规则都由策划来指定填写。&lt;/p&gt;

&lt;p&gt;以前每次策划设计人员增加Excel表时，都需要更程序员打招呼说，你帮我把这个Excel表加入到自动化和一键XXX的功能里去。程序员就需要腾出时间和精力来为策划设计人员服务。我们可以把这块工作移交出去，策划设计人员能够可以自定义导出哪个Excel文件，以及Excel文件里的哪个工作簿。这样策划可以自主选择和自主增加表的导出内容。&lt;/p&gt;

&lt;p&gt;我们来看怎么做？一个可行的办法是，在程序命令中预留几个参数，这个参数是指向某个需要导出的文件的，以及需要导出sheet。那么在命令行里，执行这个程序并且后面跟上参与就能导出数据。&lt;/p&gt;

&lt;p&gt;但是批处理写命令行也好，shell写命令行也好，毕竟还是程序级别的。策划并不能很好的控制对表的导出内容的增加和修改。
比如我需要新增一个表，或者修改某个表的文件名，或者修改导出某个表文件里的sheet，这项工作还是需要修改命令行的，或者说修改批处理文件，或shell文件的。策划并不会改，或者说并且这种命令形式并不直观。&lt;/p&gt;

&lt;p&gt;再次加入规则，让自动化和一键XXX更加人性化。我们可以增加一个Excel表，表里面填有具体要导出哪些Excel文件里的哪个sheet，这些sheet的数据导出后的文件名是什么，以及生成文件后，文件应该转移到哪个文件夹中去。这样策划就可以自行定制，我们需要用哪些Excel里的哪些sheet，可以自行增删改，可以完全自给自足了。策划设计人员完全能够主导所有数据的导出工作和转移工作都了。&lt;/p&gt;

&lt;p&gt;还不够。虽然这样方便了策划设计人员，但程序员的麻烦还没解决，如果设计人员改了字段名字，插入了新的字段，删除了字段，又没有及时通知程序员，或者说忘记了自己做过什么怎么办，那岂不是要全盘彻查了？不可以，彻查这样的效率太低，不可以让这种事情发生。如果能在生成数据时自动检查与程序的对应关系就好了。&lt;/p&gt;

&lt;p&gt;我们再加一个规则，让字段名字与程序对应的规则。用程序生成一群变量定义与每个数据表字段名对应，将每个要导出的sheet里的头行的列名作为变量名字写入程序变量定义中，以方便程序在读取数据表时，列名与数据表对齐，无形中校验做好了。&lt;/p&gt;

&lt;p&gt;举个例子，在role.xls文件中，role这个sheet(工作簿)中的第一列字段为ID，第二列字段为Name，第三列字段为Age，那么程序变量自动生成后就成了以下这种格式：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
public Class ExcelDefine
{
	public const int role_role_ID = 1;
	public const int role_role_Name = 2;
	public const int role_role_Age = 3;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;当策划设计人员更改字段后，新增字段，删除字段后，会如何呢，比如第一列ID更改为Identifier,第二列插入School，第三列Age删除后，生成了如下的代码：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
public Class ExcelDefine
{
	public const int role_role_Identifier = 1;
	public const int role_role_School = 2;
	public const int role_role_Name = 3;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;如此一来，在转换数据表后，原来在程序中使用的ExcelDefine.role_role_ID，ExcelDefine.role_role_Age，将失效报错，而 ExcelDefine.role_role_Name则自动转入读取第三列无需修改。&lt;/p&gt;

&lt;p&gt;现在程序员不再需要知道策划设计人员修了数据是第几列，策划设计人员忘记了修改哪里也无妨，因为在编译时就会报出相关的错误，提示程序员们，表中的哪些字段进行了修改，你需要向策划设计人员询问具体意向。&lt;/p&gt;

&lt;p&gt;到这里，我们有了自动化和一键转化XXX的工具，省去了不少人力，并且加入了规则，让策划设计人员完全可以自己控制Excel数据表的操作，又加入了检查校验和修复的功能，让程序员在数据表衔接部分也得到了很好的检查和校验作用。这个方案可以供大家参考，许多大项目大公司都采用这样的方式，安全又稳定。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Challenge your fearing</title>
   <link href="http://www.luzexi.com/2018/07/11/challenge-your-fearing"/>
   <updated>2018-07-11T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/07/11/challenge-your-fearing</id>
   <content type="html">&lt;p&gt;#Challenge your fearing&lt;/p&gt;

&lt;p&gt;Recently I’m thinking about the challenge. Why we stop challenging? Why pick up again after we drop it? What it the best way to keep moving?&lt;/p&gt;

&lt;p&gt;We challenge a lot in live. Pick one example in my life.&lt;/p&gt;

&lt;p&gt;My swimming experience.&lt;/p&gt;

&lt;p&gt;In my swimming experience fearing is the most important thing I have to overcome. I’m not the smart one in people, my swimming experience begin at 10 years old, until now I just know how to swim in free style.&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;Fearing stay in the whole swimming experience. At the beginning of swim, I scare gone down in the water, I cant breathe air as normally as on lands. It makes me feel that I will die. I escape like a army deserter.&lt;/p&gt;

&lt;p&gt;The fearing makes me stop learning swim. A long time after, one day in summer, my fearing about swim fade away, but still dont like to swim, until one day someone invite me to swim together. I lie to them that I can swim, it makes me feel good. Because some of them never tried swim, they even never went to swim pool before. I feel good, at least i went to swim pool, and tried.&lt;/p&gt;

&lt;p&gt;I’m in my a little self-confidence, try to focous on learning, but still scare a lot. I scare after my energy expend I will die. There’s too much fearing in my head, makes me drop again. ‘I can not learn swim’ I told to myself in sad.&lt;/p&gt;

&lt;p&gt;a long time after again, fearing fade away again. One day in summer again, in one friend’s party we talk about the sport, I support swimming is the best one in sport. After that I think must keep the truth in life, I went to swim pool, start learning again.&lt;/p&gt;

&lt;p&gt;This time, I keep do swimming sport for a long days, because no longer scare breathe air, and no longer scare expend energy. I put my focous on swimming skill, the skill how to breathe enough air and how to save energy. I do a greate job on swim now, can swim a lot without fearing.&lt;/p&gt;

&lt;p&gt;I know one example is not enough to prove anything, but most of things has the same root. Fearing is always the most important thing we have to beat. If you can beat fearing in anything, u can win the world.&lt;/p&gt;

&lt;p&gt;Why we stop? Most of the reason is fearing, or you can say the unknow future, its the same as fearing. We scare that we can’t learn or finish, we thought its too hard for us.&lt;/p&gt;

&lt;p&gt;Why pick up again? The fearing fade away, or maybe we forget the feelling of fear, or maybe someone push us to do. Many unknow reasons or things will push us to pick up again after fearing away. But no scare anymore is necessary.&lt;/p&gt;

&lt;p&gt;What’s the best way to keep moving? Try your best to find the way you think is fun for you to overcome the fearing. Fearing is the biggest boss we must beat, and you must clear that it can’t be beated in a short time, it will be continues for a long long time in your head, longer than you think. So find out the fun thing for you, it will makes you more intereted in keep moving.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>白领投资探讨(二)</title>
   <link href="http://www.luzexi.com/2018/07/10/%E7%99%BD%E9%A2%86%E6%8A%95%E8%B5%84%E6%8E%A2%E8%AE%A82"/>
   <updated>2018-07-10T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/07/10/白领投资探讨2</id>
   <content type="html">&lt;p&gt;#白领投资探讨(二)&lt;/p&gt;

&lt;p&gt;说说股市，&lt;/p&gt;

&lt;p&gt;中国股市跌了大半年了，整体所有股票平均都有30%以上的跌幅，50%-70%的跌幅是正常现象。&lt;/p&gt;

&lt;p&gt;原因很多，首先最大原因是中国任然没有走出困境，过去过于追求快速的发展遇到了瓶颈，限制了现在的发展。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;现在在抓紧转型中，痛苦是无法避免的。一旦转型成功，前途不可估量，那意味着中国将站上世界顶峰，上证破万点不是梦。但是否会成功，需要花多少时间成功，这是个主要问题。&lt;/p&gt;

&lt;p&gt;时间能改变一切，时间成本是最大的。&lt;/p&gt;

&lt;p&gt;另外一个原因是美国发起的贸易战。其实贸易战是常事，只是在中国最最艰难的时候发起，确实是一种打击你，压垮你，鞭挞你的感觉。美国毕竟是世界经济的领袖，全球霸主，对于向他发起挑战的国家，进行打击打压，也是可以理解的。就看我们如何应对，如何防御了。&lt;/p&gt;

&lt;p&gt;A股2700点反弹到2800点这个位置多空博弈最激烈，买入看涨的和反弹止损的对抗赛。&lt;/p&gt;

&lt;p&gt;熊市没有底，因为是各板块是轮番下跌的，板块之间有联动会被拖累，所以底部深的很，即使政府站出来背书也没用，人心没有这么容易稳.&lt;/p&gt;

&lt;p&gt;前几天政府官员就站出来背书了，说明金融风险加大很多了。系统性风险的关键，并不是上证2xxx点或者1xxx点的问题，而是大众情绪的问题。防止踩踏和挤兑才是关键，即使经济出现问题，也不能让民众慌乱，因为情绪一旦失控，整个国家就完蛋了。&lt;/p&gt;

&lt;p&gt;贸易战肯定是持久战。现在中国想借助贸易战转型，第一坚持不放水，第二防止大规模踩踏为转型争取时间。&lt;/p&gt;

&lt;h3 id=&quot;这个位置不要做任何买卖无意义的区间等一方彻底胜利或者对抗平息后再做决定&quot;&gt;这个位置不要做任何买卖，无意义的区间，等一方彻底胜利或者对抗平息后再做决定。&lt;/h3&gt;

&lt;h3 id=&quot;投资是寂寞的我现在才明白为什么大佬说要像投资实业那样投资股票&quot;&gt;投资是寂寞的。我现在才明白，为什么大佬说，要像投资实业那样投资股票。&lt;/h3&gt;

&lt;h3 id=&quot;和权利的游戏一样如果你不用尽全力对待通常很快就死了&quot;&gt;和权利的游戏一样。如果你不用尽全力对待，通常很快就死了。&lt;/h3&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第三章，数据表(一) - 数据表的种类</title>
   <link href="http://www.luzexi.com/2018/07/09/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-%E6%95%B0%E6%8D%AE%E8%A1%A81"/>
   <updated>2018-07-09T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/07/09/Unity3D高级编程之进阶主程-数据表1</id>
   <content type="html">&lt;p&gt;在游戏项目中缺少不了数据表，数据决定了游戏的整个进程，因此怎么用数据表配置数，配置数据时是否方便成了关键的问题。&lt;/p&gt;

&lt;p&gt;那么如何来理解数据表的存在呢？数据表完全可以认为是一个本地的数据库，只不过这个数据库里的数据是不可被修改的，是只读的。可以这么说，在实际项目的开发中，它们大部分从Excel里生成出来，再导入到游戏中去的，也有其他的各种方式，比如使用比较原始的方法直接写在代码里，下面的内容中我们来讲讲数据的存放种类。&lt;/p&gt;

&lt;p&gt;数据表的在项目中的作用是什么？数据表是连接美术，设计策划，和程序的桥梁。艺术家们用它来配置效果，设计师用它来调整游戏的数值平衡，程序员们用它来判断逻辑。所以数据表的意义非常大，是连接各个环节的桥梁。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;h3 id=&quot;大部分数据都是在excel里填写生成的&quot;&gt;大部分数据都是在Excel里填写生成的&lt;/h3&gt;

&lt;p&gt;自从微软创建出神级的Excel后，全世界都爱上了这款软件，无论自用还是商用，制图还是分析都离不开它了。&lt;/p&gt;

&lt;p&gt;居我的了解，基本上所有的公司都在用excel。而且，只要跟数据，数字，接触的职业，都会大量使用excel来做数据分析，数据制表等工作。&lt;/p&gt;

&lt;p&gt;它能给你所有你想处理数据的功能，而且快捷，方便，易于保存，上手快，方便传播等等等，说不完的好品质。&lt;/p&gt;

&lt;p&gt;所以呢，游戏行业里数值策划更是喜欢这个工具了，我们的游戏数据跟excel分不开，我们需要用它的来加快效率，特别是数据开发，数值平衡的工作。&lt;/p&gt;

&lt;p&gt;现在你应该明白了，我为何如此夸大的它的优秀，因为他确实在很大程度上加快了项目的进度。&lt;/p&gt;

&lt;p&gt;下面说所的数据表，基本上都是意味着从Excel文件中导出来的数据的集合。&lt;/p&gt;

&lt;h3 id=&quot;最原始的数据方式代码数据&quot;&gt;最原始的数据方式，代码数据&lt;/h3&gt;

&lt;p&gt;最最原始的数据，是写在程序里写着的，这种一般都是临时级别的数据，在更改，增加，删除时增加了大量的程序员的工作量。&lt;/p&gt;

&lt;p&gt;然而让策划人员去自行更改代码里的数据，不但增加了修改程序的风险，也让数值策划人员批量修改数值的难度增加好几倍。&lt;/p&gt;

&lt;p&gt;所以这种放在代码里的数据，基本都只存在于Demo阶段，或者mini游戏中，因为数据量小，更改的次数少，不会特别去在意数值的平衡性。&lt;/p&gt;

&lt;p&gt;数据放在代码里的原因就只有一个，快，制作快，使用快，效率快。不需要建立与其他部门的桥梁，只要程序员自己动手就能搞定。不需要像Excel那样，需要建立先Excel表，制定规则，再转化数据，再加载，再解析等步骤后才能使用。代码里的数据，程序员们直接就能用。&lt;/p&gt;

&lt;p&gt;因为只有程序员知道是什么，为什么这么写，也只有程序员能看明白，所以当数据使用量逐渐增大，或者使用规则逐渐复杂，或者数据的修改次数逐渐增加，就会渐渐觉得对于小改动的花费的精力太大，效率也大幅降低。&lt;/p&gt;

&lt;h3 id=&quot;txt文本数据&quot;&gt;txt文本数据&lt;/h3&gt;

&lt;p&gt;文本是一种常用的数据表形式，例如用.Json，Xml，Csv为扩展名的文件，里面全是字符串形式的文本，包括数字在内也都以字符串的形式存在，在当程序读取这些字符串内容后将它们转化为相应的数据类型，整数，浮点数，文本，数组。于是这些字符串以怎样的规则存储在文件中是一个比较需要考究的东西了，因为我们还要解析它们。&lt;/p&gt;

&lt;p&gt;文本读取规则有很多标准形式的，包括Json，Xml，CSV等，这些都是常用的文本读取规则，在很多情况下用文本读取字符串形式的数据然后按规则来转化成相应的数据和数据组是极佳的选择。它的优点是肉眼能很直观的看到数据，并且也容易查找问题，并能立即直接对文本进行修改，无需其他工具。&lt;/p&gt;

&lt;p&gt;像文本这样的数据存储方式，如果为了方便快捷，把数据直接用逗号隔开，或者用空格隔开，或者用特别的符号比如’;’分号等形式隔开，就可以作为一个数据的规则格式，无需其他复杂的协议商定。&lt;/p&gt;

&lt;p&gt;还记得我们主要的数据开发工具是Excel么，为了能从Excel里更容易的导出数据，我们会选择一些更加简单实用的导出方式。比如直接从Excel里复制粘贴数据到txt文件，这是最直接的手动导出方式。&lt;/p&gt;

&lt;p&gt;也有用Excel直接保存CSV格式的，它是以’,’逗号开的格式的文本文件。&lt;/p&gt;

&lt;p&gt;这些都是比较容易的方式，如果要做到不手动，而是用程序工具转化，那就写个程序读取Excel文件内容，程序看以直接导出相应的格式的文本文件。&lt;/p&gt;

&lt;p&gt;写一个程序工具来导出Excel的数据，会有很多格式可以考虑，比如我们前面提到的，Json格式，Xml格式，以及自定义格式，每次导出时都会将数据在内存中以一定规则排列好，再导出到文本文件。&lt;/p&gt;

&lt;p&gt;通常使用过工具导出Excel数据的同学，以后都会非常喜欢使用使用程序工具导出数据，因为这样会更高效。当他们已经熟悉了这套流程和方法，在制定项目模块规划时，就很习惯性的把程序自动导出数据的模块规划进项目里，对他们来说，这是顺手拈来的事情，而且这事很容易做到，一劳永逸。&lt;/p&gt;

&lt;p&gt;这种程序化代替人工操作的事，我们通常称为自动化或流水线。其实自动化是最终的目标，我们希望任何工作都可以自动化来代替，从而减少人工手动操作导致的失误，同时也减少了工作量，减少了同学们不必要的精力消耗。&lt;/p&gt;

&lt;h3 id=&quot;比特流数据&quot;&gt;比特流数据&lt;/h3&gt;

&lt;p&gt;数据比特流是一种稍微底层点的数据表现形式，他是将数据转化为二进制形式存放在文件里，然后程序通过读取二进制文件，按一定的规则将其转化为所需要的数据。它比起文本形式的数据文件，比特流数据文件特点是，占用的空间更加小，读取速度更快，但缺点也同时存在，通用性差，无法直观和任意的修改。&lt;/p&gt;

&lt;p&gt;为什么会更加小，二进制比特流在存储数字时，会直接用二进制形式存储，比如，txt文本中23345是“23345”这个字符串，占用了5个字符，每个字符2个字节，就用了10个字节，而二进制比特流则在存储时可以直接使用二个字节(short)存储’23345‘这个数字，所以数据比特流形式的数据存储文件更加小。一个以文本形式的txt文件来建立的10MB的数据文件，转化为2进制格式后，可以压缩到几百KB甚至几十KB。&lt;/p&gt;

&lt;p&gt;一个10MB的文件在读取的时候是很消耗CPU的，假如项目中有几个甚至几十个这样的数据文件，在游戏进行中的卡顿是难免的。这么大的数据文件光读取整个内容就已经很消耗CPU资源了，更别说，还需要在读取文本数据后进行解析。庞大的文本解析工作，让成千上万个字符串转化为数字或者浮点数，会消耗比较多的CPU计算量。&lt;/p&gt;

&lt;p&gt;其实比特流数据和数据网络传输时使用的协议是一个道理，有人使用了json格式的数据协议来传输网络数据，所以当数据大时，json字符串占用的数据量也非常大，启用压缩算法也不能解决根本问题。因此很多人转而使用数据比特流形式的数据协议来传输网络数据，以减少网络数据占用量，即使在网络不稳定的情况下，因为体量比较少，能够准确送达的概率也大了很多，从而网络性能也提高了很多。关于网络协议具体内容，会在以后的章节中一一介绍。&lt;/p&gt;

&lt;p&gt;以比特流形式作为协议的标准很多。比如最近比较流行的，Google protobuf，还有MessagePack。&lt;/p&gt;

&lt;p&gt;这里简单介绍下Google protobuf：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	protobuf是一个开源项目，而且是后台很硬的开源项目。网上现有的大部分（至少80%）开源项目，要么是某人单干、要么是几个闲杂人等合伙搞。而protobuf则不然，它是Google公司开发的，并且在Google内部久经考验的一个数据协议。
	
	那这个听起来牛X的东西到底有啥用处呢？简单地说，这个东西干的事儿其实和XML差不多，也就是把某种数据结构的信息，以某种格式保存起来。主要用于数据存储、传输协议格式等场合。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;protobuf有什么特色呢？我们将这些数据协议内容放在网络层的章节里讲。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>《Unity3D高级编程之进阶主程》第一章，C#要点技术(一) - List 底层源码剖析</title>
   <link href="http://www.luzexi.com/2018/07/06/Unity3D%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B%E4%B9%8B%E8%BF%9B%E9%98%B6%E4%B8%BB%E7%A8%8B-CSharp%E8%A6%81%E7%82%B9%E6%8A%80%E6%9C%AF1"/>
   <updated>2018-07-06T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/07/06/Unity3D高级编程之进阶主程-CSharp要点技术1</id>
   <content type="html">&lt;p&gt;很多老鸟看到C#基础总想跳过，因为看了太多次，次次都一样，基础里无非是几个语法，或者由继承展开的特性，再加上一些高级特有的属性，看多了确实有点枯燥。但我还是要强调一下，基础的重要性，没有扎实的基础，所有写的程序就会随着软件规模的扩大，使用规模的扩大，或者使用途径的扩大而遇到越来越多的问题，这些程序最后大部分都会被遗弃，或者重新开始。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;也是因为基础的重要性之高，使得很多资深的程序员从这里出发，最终又回到了这里，他们一遍又一遍的看着这部分内容，希望从中得到新的启发或者纠正自己以前的错误观念。&lt;/p&gt;

&lt;p&gt;我不仅仅想把基础的东西告知给大家，但又不想让大家觉得开枯燥，所以我想写些不一样的东西。我认为能看这本书的，基本上都能做到基础的语法部分已经滚瓜烂熟，所以我们在基础的语法之上讲些进阶的内容会来得更有趣些，比如算法设计，比如常用组件的底层代码分析，比如设计模式，比如动态库(so文件和dll文件)等等。&lt;/p&gt;

&lt;p&gt;首先我们来讲解下我们在日常的工作中常常会用到的C#组件底层原理，从这一章的知识中，我们可以充分的了解到我们日常编程代码中，这些组件在底层是如何运作的，当我们再次编写代码时，能有意识的理解背后的执行步骤，从而能更好的提升代码的质量。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;1。 ## 常用组件底层代码解析&lt;/p&gt;

&lt;h3 id=&quot;list-底层代码剖析&quot;&gt;List 底层代码剖析&lt;/h3&gt;

&lt;p&gt;List是一个C#中最常见的可伸缩数组组件，我们常常用它来替代数组，因为它是可伸缩的，所以我们在写的时候不用手动去分配数组的大小。甚至有时我们也会拿它当链表使用。那么到底它的底层是怎么编写的呢，每次增加和减少以及赋值，内部是怎么执行和运作的呢？我们接下来就来详细的讲解。&lt;/p&gt;

&lt;p&gt;我们首先来看看List的构造部分，源码如下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
public class List&amp;lt;T&amp;gt; : IList&amp;lt;T&amp;gt;, System.Collections.IList, IReadOnlyList&amp;lt;T&amp;gt;
{
    private const int _defaultCapacity = 4;

    private T[] _items;
    private int _size;
    private int _version;
    private Object _syncRoot;
    
    static readonly T[]  _emptyArray = new T[0];        
        
    // Constructs a List. The list is initially empty and has a capacity
    // of zero. Upon adding the first element to the list the capacity is
    // increased to 16, and then increased in multiples of two as required.
    public List() {
        _items = _emptyArray;
    }

    // Constructs a List with a given initial capacity. The list is
    // initially empty, but will have room for the given number of elements
    // before any reallocations are required.
    // 
    public List(int capacity) {
        if (capacity &amp;lt; 0) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.capacity, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
        Contract.EndContractBlock();

        if (capacity == 0)
            _items = _emptyArray;
        else
            _items = new T[capacity];
    }

    //...
    //其他内容
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;从源码中可以知道，List 继承于IList，IReadOnlyList，IList是提供了主要的接口，IReadOnlyList提供了迭代接口。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://referencesource.microsoft.com/#mscorlib/system/collections/ilist.cs,5d74f6adfeaf6c7d&quot;&gt;IList源码&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://referencesource.microsoft.com/#mscorlib/system/collections/generic/ireadonlylist.cs,b040fb780bdd59f4&quot;&gt;IReadOnlyList源码&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;看构造部分，我们明确了，List内部是用数组实现的，而不是链表，并且当没有给予指定容量时，初始的容量为0。&lt;/p&gt;

&lt;p&gt;也就是说，我们可以大概率推测List组件在Add,Remove两个函数调用时都采用的是“从原数组拷贝生成到新数组”的方式工作的。&lt;/p&gt;

&lt;p&gt;下面我们来看下，我们的猜测是否正确。&lt;/p&gt;

&lt;h6 id=&quot;add接口源码&quot;&gt;Add接口源码：&lt;/h6&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
// Adds the given object to the end of this list. The size of the list is
// increased by one. If required, the capacity of the list is doubled
// before adding the new element.
//
public void Add(T item) {
    if (_size == _items.Length) EnsureCapacity(_size + 1);
    _items[_size++] = item;
    _version++;
}

// Ensures that the capacity of this list is at least the given minimum
// value. If the currect capacity of the list is less than min, the
// capacity is increased to twice the current capacity or to min,
// whichever is larger.
private void EnsureCapacity(int min) {
    if (_items.Length &amp;lt; min) {
        int newCapacity = _items.Length == 0? _defaultCapacity : _items.Length * 2;
        // Allow the list to grow to maximum possible capacity (~2G elements) before encountering overflow.
        // Note that this check works even when _items.Length overflowed thanks to the (uint) cast
        if ((uint)newCapacity &amp;gt; Array.MaxArrayLength) newCapacity = Array.MaxArrayLength;
        if (newCapacity &amp;lt; min) newCapacity = min;
        Capacity = newCapacity;
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;上述List源代码中的Add函数，每次增加一个元素的数据，Add接口都会首先检查的是容量还够不够，如果不够则用 EnsureCapacity 来增加容量。&lt;/p&gt;

&lt;p&gt;在 EnsureCapacity 中，有这样一行代码：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;    int newCapacity = _items.Length == 0? _defaultCapacity : _items.Length * 2;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;每次容量不够的时候，整个数组的容量都会扩充一倍，_defaultCapacity 是容量的默认值为4。因此整个扩充的路线为4，8，16，32，64，128，256，512，1024…依次类推。&lt;/p&gt;

&lt;p&gt;List使用数组形式作为底层数据结构，好处是使用索引方式提取元素很快，但在扩容的时候就会很糟糕，每次new数组都会造成内存垃圾，这给垃圾回收GC带来了很多负担。&lt;/p&gt;

&lt;p&gt;这里按2指数扩容的方式，可以为GC减轻负担，但是如果当数组连续被替换掉也还是会造成GC的不小负担，特别是代码中List频繁使用的Add时。另外，如果数量不得当也会浪费大量内存空间，比如当元素数量为 520 时，List 就会扩容到1024个元素，如果不使用剩余的504个空间单位，就造成了大部分的内存空间的浪费。具体该怎么做才是最佳的策略，我们将在后面的文章中讨论。&lt;/p&gt;

&lt;p&gt;我们再来看看Remove接口部分的源码：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
// Removes the element at the given index. The size of the list is
// decreased by one.
// 
public bool Remove(T item) {
    int index = IndexOf(item);
    if (index &amp;gt;= 0) {
        RemoveAt(index);
        return true;
    }

    return false;
}

// Returns the index of the first occurrence of a given value in a range of
// this list. The list is searched forwards from beginning to end.
// The elements of the list are compared to the given value using the
// Object.Equals method.
// 
// This method uses the Array.IndexOf method to perform the
// search.
// 
public int IndexOf(T item) {
    Contract.Ensures(Contract.Result&amp;lt;int&amp;gt;() &amp;gt;= -1);
    Contract.Ensures(Contract.Result&amp;lt;int&amp;gt;() &amp;lt; Count);
    return Array.IndexOf(_items, item, 0, _size);
}

// Removes the element at the given index. The size of the list is
// decreased by one.
// 
public void RemoveAt(int index) {
    if ((uint)index &amp;gt;= (uint)_size) {
        ThrowHelper.ThrowArgumentOutOfRangeException();
    }
    Contract.EndContractBlock();
    _size--;
    if (index &amp;lt; _size) {
        Array.Copy(_items, index + 1, _items, index, _size - index);
    }
    _items[_size] = default(T);
    _version++;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Remove接口中包含了 IndexOf 和 RemoveAt，其中用 IndexOf 函数是位了找到元素的索引位置，用 RemoveAt 可以删除指定位置的元素。&lt;/p&gt;

&lt;p&gt;从源码中我们可以看到，元素删除的原理其实就是用 Array.Copy 对数组进行覆盖。IndexOf 启用的是 Array.IndexOf 接口来查找元素的索引位置，这个接口本身内部实现是就是按索引顺序从0到n对每个位置的比较，复杂度为O(n)。&lt;/p&gt;

&lt;p&gt;先补急着总结，我们再看来 Insert 接口源码。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
// Inserts an element into this list at a given index. The size of the list
// is increased by one. If required, the capacity of the list is doubled
// before inserting the new element.
// 
public void Insert(int index, T item) {
    // Note that insertions at the end are legal.
    if ((uint) index &amp;gt; (uint)_size) {
        ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_ListInsert);
    }
    Contract.EndContractBlock();
    if (_size == _items.Length) EnsureCapacity(_size + 1);
    if (index &amp;lt; _size) {
        Array.Copy(_items, index, _items, index + 1, _size - index);
    }
    _items[index] = item;
    _size++;            
    _version++;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;与Add接口一样，先检查容量是否足够，不足则扩容。从源码中获悉，Insert插入元素时，使用的用拷贝数组的形式，将数组里的指定元素后面的元素向后移动一个位置。&lt;/p&gt;

&lt;p&gt;看到这里，可以我们明白了List的Add，Insert，IndexOf，Remove接口都是没有做过任何形式的优化，都使用的是顺序迭代的方式，如果过于频繁使用的话，会导致效率降低，也会造成不少内存的冗余，使得垃圾回收(GC)时承担了更多的压力。&lt;/p&gt;

&lt;p&gt;其他相关接口比如 AddRange，RemoveRange的原理和Add与Remove一样，区别只是多了几个元素，把单个元素变成了以容器为单位的形式进行操作。都是先检查容量是否合适，不合适则扩容，或者当Remove时先得到索引位置再进行整体的覆盖掉后面的的元素，容器本身大小不会变化，只是做了重复覆盖的操作。&lt;/p&gt;

&lt;h6 id=&quot;其他接口也同样基于数组并使用了类似的方式来对数据做操作我们可以来快速的看看其他常用接口的源码是如何实现的&quot;&gt;其他接口也同样基于数组，并使用了类似的方式来对数据做操作，我们可以来快速的看看其他常用接口的源码是如何实现的。&lt;/h6&gt;

&lt;p&gt;比如 []的实现，&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
// Sets or Gets the element at the given index.
// 
public T this[int index] {
    get {
        // Following trick can reduce the range check by one
        if ((uint) index &amp;gt;= (uint)_size) {
            ThrowHelper.ThrowArgumentOutOfRangeException();
        }
        Contract.EndContractBlock();
        return _items[index]; 
    }

    set {
        if ((uint) index &amp;gt;= (uint)_size) {
            ThrowHelper.ThrowArgumentOutOfRangeException();
        }
        Contract.EndContractBlock();
        _items[index] = value;
        _version++;
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;[]的实现，直接使用了数组的索引方式获取元素。&lt;/p&gt;

&lt;h6 id=&quot;再比如-clear-清除接口&quot;&gt;再比如 Clear 清除接口&lt;/h6&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
// Clears the contents of List.
public void Clear() {
    if (_size &amp;gt; 0)
    {
        Array.Clear(_items, 0, _size); // Don't need to doc this but we clear the elements so that the gc can reclaim the references.
        _size = 0;
    }
    _version++;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Clear接口在调用时并不会删除数组，而只是将数组中的元素清零，并设置 _size 为 0 而已，用于虚拟地表明当前容量为0。&lt;/p&gt;

&lt;h6 id=&quot;再比如-contains-接口用于确实某元素是否存在于list中&quot;&gt;再比如 Contains 接口，用于确实某元素是否存在于List中&lt;/h6&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
// Contains returns true if the specified element is in the List.
// It does a linear, O(n) search.  Equality is determined by calling
// item.Equals().
//
public bool Contains(T item) {
    if ((Object) item == null) {
        for(int i=0; i&amp;lt;_size; i++)
            if ((Object) _items[i] == null)
                return true;
        return false;
    }
    else {
        EqualityComparer&amp;lt;T&amp;gt; c = EqualityComparer&amp;lt;T&amp;gt;.Default;
        for(int i=0; i&amp;lt;_size; i++) {
            if (c.Equals(_items[i], item)) return true;
        }
        return false;
    }
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;从源代码中我们可以看到，Contains 接口使用的是线性查找方式比较元素，对数组进行迭代，比较每个元素与参数的实例是否一致，如果一致则返回true，全部比较结束还没有找到，则认为查找失败。&lt;/p&gt;

&lt;h6 id=&quot;再比如-toarray-转化数组接口&quot;&gt;再比如 ToArray 转化数组接口&lt;/h6&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
// ToArray returns a new Object array containing the contents of the List.
// This requires copying the List, which is an O(n) operation.
public T[] ToArray() {
    Contract.Ensures(Contract.Result&amp;lt;T[]&amp;gt;() != null);
    Contract.Ensures(Contract.Result&amp;lt;T[]&amp;gt;().Length == Count);

    T[] array = new T[_size];
    Array.Copy(_items, 0, array, 0, _size);
    return array;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;ToArray接口中，重新new了一个指定大小的数组，再将本身数组上的内容考别到新数组上，再返回出来。&lt;/p&gt;

&lt;h6 id=&quot;再比如-find-查找接口&quot;&gt;再比如 Find 查找接口&lt;/h6&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
public T Find(Predicate&amp;lt;T&amp;gt; match) {
    if( match == null) {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);
    }
    Contract.EndContractBlock();

    for(int i = 0 ; i &amp;lt; _size; i++) {
        if(match(_items[i])) {
            return _items[i];
        }
    }
    return default(T);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Find接口使用的同样是线性查找，对每个元素都进行了比较，复杂度为O(n)。&lt;/p&gt;

&lt;h6 id=&quot;再比如-enumerator-枚举迭代部分的细节&quot;&gt;再比如 Enumerator 枚举迭代部分的细节&lt;/h6&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
// Returns an enumerator for this list with the given
// permission for removal of elements. If modifications made to the list 
// while an enumeration is in progress, the MoveNext and 
// GetObject methods of the enumerator will throw an exception.
//
public Enumerator GetEnumerator() {
    return new Enumerator(this);
}

/// &amp;lt;internalonly/&amp;gt;
IEnumerator&amp;lt;T&amp;gt; IEnumerable&amp;lt;T&amp;gt;.GetEnumerator() {
    return new Enumerator(this);
}

System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
    return new Enumerator(this);
}

[Serializable]
public struct Enumerator : IEnumerator&amp;lt;T&amp;gt;, System.Collections.IEnumerator
{
    private List&amp;lt;T&amp;gt; list;
    private int index;
    private int version;
    private T current;

    internal Enumerator(List&amp;lt;T&amp;gt; list) {
        this.list = list;
        index = 0;
        version = list._version;
        current = default(T);
    }

    public void Dispose() {
    }

    public bool MoveNext() {

        List&amp;lt;T&amp;gt; localList = list;

        if (version == localList._version &amp;amp;&amp;amp; ((uint)index &amp;lt; (uint)localList._size)) 
        {                                                     
            current = localList._items[index];                    
            index++;
            return true;
        }
        return MoveNextRare();
    }

    private bool MoveNextRare()
    {                
        if (version != list._version) {
            ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
        }

        index = list._size + 1;
        current = default(T);
        return false;                
    }

    public T Current {
        get {
            return current;
        }
    }

    Object System.Collections.IEnumerator.Current {
        get {
            if( index == 0 || index == list._size + 1) {
                 ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);
            }
            return Current;
        }
    }

    void System.Collections.IEnumerator.Reset() {
        if (version != list._version) {
            ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
        }
        
        index = 0;
        current = default(T);
    }

}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;其中我们需要注意 Enumerator 这个结构，每次获取迭代器时，Enumerator 每次都是被new出来，如果大量使用迭代器的话，比如foreach就会造成大量的垃圾对象，这也是为什么我们常常告诫程序员们，尽量不要用foreach，因为 List 的 foreach 会增加有新的 Enumerator 实例，最后由GC垃圾回收掉。&lt;/p&gt;

&lt;h6 id=&quot;最后我们来看看-sort-排序接口&quot;&gt;最后我们来看看 Sort 排序接口&lt;/h6&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
// Sorts the elements in a section of this list. The sort compares the
// elements to each other using the given IComparer interface. If
// comparer is null, the elements are compared to each other using
// the IComparable interface, which in that case must be implemented by all
// elements of the list.
// 
// This method uses the Array.Sort method to sort the elements.
// 
public void Sort(int index, int count, IComparer&amp;lt;T&amp;gt; comparer) {
    if (index &amp;lt; 0) {
        ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
    }
    
    if (count &amp;lt; 0) {
        ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
    }
        
    if (_size - index &amp;lt; count)
        ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
    Contract.EndContractBlock();

    Array.Sort&amp;lt;T&amp;gt;(_items, index, count, comparer);
    _version++;
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;它使用了 Array.Sort接口进行排序，其中Array.Sort的源码我们也把它找出来。以下为 Array.Sort 的使用的算法源码：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;
internal static void DepthLimitedQuickSort(T[] keys, int left, int right, IComparer&amp;lt;T&amp;gt; comparer, int depthLimit)
{
    do
    {
        if (depthLimit == 0)
        {
            Heapsort(keys, left, right, comparer);
            return;
        }

        int i = left;
        int j = right;

        // pre-sort the low, middle (pivot), and high values in place.
        // this improves performance in the face of already sorted data, or 
        // data that is made up of multiple sorted runs appended together.
        int middle = i + ((j - i) &amp;gt;&amp;gt; 1);
        SwapIfGreater(keys, comparer, i, middle);  // swap the low with the mid point
        SwapIfGreater(keys, comparer, i, j);   // swap the low with the high
        SwapIfGreater(keys, comparer, middle, j); // swap the middle with the high

        T x = keys[middle];
        do
        {
            while (comparer.Compare(keys[i], x) &amp;lt; 0) i++;
            while (comparer.Compare(x, keys[j]) &amp;lt; 0) j--;
            Contract.Assert(i &amp;gt;= left &amp;amp;&amp;amp; j &amp;lt;= right, &quot;(i&amp;gt;=left &amp;amp;&amp;amp; j&amp;lt;=right)  Sort failed - Is your IComparer bogus?&quot;);
            if (i &amp;gt; j) break;
            if (i &amp;lt; j)
            {
                T key = keys[i];
                keys[i] = keys[j];
                keys[j] = key;
            }
            i++;
            j--;
        } while (i &amp;lt;= j);

        // The next iteration of the while loop is to &quot;recursively&quot; sort the larger half of the array and the
        // following calls recrusively sort the smaller half.  So we subtrack one from depthLimit here so
        // both sorts see the new value.
        depthLimit--;

        if (j - left &amp;lt;= right - i)
        {
            if (left &amp;lt; j) DepthLimitedQuickSort(keys, left, j, comparer, depthLimit);
            left = i;
        }
        else
        {
            if (i &amp;lt; right) DepthLimitedQuickSort(keys, i, right, comparer, depthLimit);
            right = j;
        }
    } while (left &amp;lt; right);
}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Array.Sort 使用的是快速排序方式进行排序，从而我们明白了 List 的 Sort 排序的效率为O(nlogn)。&lt;/p&gt;

&lt;h6 id=&quot;我们把大部分的接口都列了出来差不多把所有的源码都分析了一遍我们可以看到-list-的效率并不高只是通用性强而已大部分的算法都使用的是线性复杂度的算法这种线性算法当遇到规模比较大的计算量级时就会导致cpu的大量损耗&quot;&gt;我们把大部分的接口都列了出来，差不多把所有的源码都分析了一遍，我们可以看到 List 的效率并不高，只是通用性强而已，大部分的算法都使用的是线性复杂度的算法，这种线性算法当遇到规模比较大的计算量级时就会导致CPU的大量损耗。&lt;/h6&gt;

&lt;h6 id=&quot;我们可以自己改进它比如不再使用有线性算法的接口自己重写一套但凡要优化list-中的线性算法的地方都使用我们自己制作的工具类&quot;&gt;我们可以自己改进它，比如不再使用有线性算法的接口，自己重写一套，但凡要优化List 中的线性算法的地方都使用，我们自己制作的工具类。&lt;/h6&gt;

&lt;h6 id=&quot;list的内存分配方式也极为不合理当list里的元素不断增加时会多次重新new数组导致原来的数组被抛弃最后当gc被调用时造成回收的压力&quot;&gt;List的内存分配方式也极为不合理，当List里的元素不断增加时，会多次重新new数组，导致原来的数组被抛弃，最后当GC被调用时造成回收的压力。&lt;/h6&gt;

&lt;h6 id=&quot;我们可以提前告知-list-对象最多会有多少元素在里面这样的话-list-就不会因为空间不够而抛弃原有的数组去重新申请数组了&quot;&gt;我们可以提前告知 List 对象最多会有多少元素在里面，这样的话 List 就不会因为空间不够而抛弃原有的数组，去重新申请数组了。&lt;/h6&gt;

&lt;p&gt;&lt;a href=&quot;https://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs&quot;&gt;List源码&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;另外我们也可以从源码上看得出代码是线程不安全的它并没有对多线程下做任何锁或其他同步操作并发情况下无法判断-_size-的执行顺序因此当我们在多线程间使用-list-时加上安全机制&quot;&gt;另外我们也可以从源码上看得出，代码是线程不安全的，它并没有对多线程下做任何锁或其他同步操作。并发情况下，无法判断 _size++ 的执行顺序，因此当我们在多线程间使用 List 时加上安全机制。&lt;/h3&gt;

&lt;h3 id=&quot;最后list-并不是高效的组件真实情况是他比数组的效率还要差的多他只是个兼容性比较强得组件而已好用但效率差&quot;&gt;最后List 并不是高效的组件，真实情况是，他比数组的效率还要差的多，他只是个兼容性比较强得组件而已，好用，但效率差。&lt;/h3&gt;
</content>
 </entry>
 
 <entry>
   <title>白领投资探讨(一)</title>
   <link href="http://www.luzexi.com/2018/07/05/%E7%99%BD%E9%A2%86%E6%8A%95%E8%B5%84%E6%8E%A2%E8%AE%A8(%E4%B8%80)"/>
   <updated>2018-07-05T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/07/05/白领投资探讨(一)</id>
   <content type="html">&lt;p&gt;我在金融领域上学习了有一段时间了，大概花了快4年的时间，特别是前面几年在微博上自言自语了很长时间，就是为了让自己独立思考，自己能够好好静下心来学习，不用受到外界的干扰。&lt;/p&gt;

&lt;p&gt;从最初的做股票，到做国内期货，然后又从国内期货退出来，研究程序自动化交易，包括K线知识和交易知识等，再到研究外汇期货与外汇商品，再放下转为研究宏观经济，再转入研究全球经济系统，再重头回到股票上。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;从那时起决定对股票进行深入探索，从短线玩玩，到研究短线启动点，买点，卖点，短线爆发线索，再到发现短线不是长久之计从而放弃短线，到研究当时很流行的”价值投资”，然后在股灾时摒弃”价值投资”的偏见，再到只研究自己感兴趣的行业，最后才到现在的，深入调查研究公司运营层面和管理层面的问题，以投资实业的而心态投资股票，关注行业动态，秉持低吸高抛的理念，坚持慢入法则，及无限现金流打法。&lt;/p&gt;

&lt;p&gt;一口气用简单的文字说了这么多看似很简单的经历，其实中间的过程经历非常丰富，而且自己也交了很多很多学费，这个学费交的我心疼，但我觉得很值得，它让我的世界观端正了很多，也让我的眼界扩大了很多。&lt;/p&gt;

&lt;p&gt;我花去了大量的时间和精力来学习这些我从来没有接触过的知识和理念。整个过程让我受益匪浅，最重要的是让我大开眼界，完全颠覆了我前20年的思维逻辑，此后在面对很多生活中的事情的时候自己能够很快看透事情的本质，抓住重点。&lt;/p&gt;

&lt;p&gt;投资是项一生都要学习的知识，我们所在的这个世界本身就是由金融构成的，而金融则是由交易构成，而交易则由投资促成。无论什么时候都别忘了学习投资，也包括如何投资自己。&lt;/p&gt;

&lt;p&gt;不避讳的说，我学金融投资的目的就是为了赚钱，说有其他学术目的都是虚的。对什么感兴趣就要抓紧时间去学习，这也是我的学习法则，我对如何赚钱，以及赚钱的哲学有着深厚浓烈的兴趣。我在前面文章有写自己关于兴趣的原则 &lt;a href=&quot;http://luzexi.com/life/2018/07/04/Always-challenge-miracle.html&quot;&gt;《Always challenge miracle》&lt;/a&gt; 欢迎探讨。&lt;/p&gt;

&lt;p&gt;本想探讨下，我在金融投资领域获得的经验和知识，但最近又对卖产品这事有了浓厚的兴趣。所以想趁热打铁，学习下如何卖产品。&lt;/p&gt;

&lt;p&gt;前面聊的有点多，自己写文章并没有什么章法，想到哪就说到哪，随心而动罢了。’用心写’我认为就是最好的最感动的文章，不管章法只要用心，就能得到读者同样的感触。主题正式进入。&lt;/p&gt;

&lt;p&gt;最近在想一个问题，人们是如何把产品卖出去赚钱的，重要的是如何做到卖出去很多产品。所以查阅了一些资料，来跟大家分享下。我这里探讨的都是我的假设，我并没有实际的运作过任何商品营销的事，就当做意淫好了。&lt;/p&gt;

&lt;p&gt;我假设我现在想批发来一些商品，然后卖出去赚钱。首先，怎么进货？从哪里进货？其次，怎么让这些商品最快速度的卖出去赚取利润？&lt;/p&gt;

&lt;h3 id=&quot;关于进货&quot;&gt;关于进货&lt;/h3&gt;

&lt;p&gt;第一。想到的是从批发市场进货。那里进货有个好处就是可以亲眼亲手感受到商品的品质好与坏，能直接第一时间把控商品的品质。批发市场进货也有坏处，价格落差太大，要留精力砍价，而且在批发市场里找适合自己品质的商品需要花的精力需要很多，不是一两天就能搞定的。可能起码要去个5，6次才开始熟悉。&lt;/p&gt;

&lt;p&gt;第二。我想到了网上进货，比如阿里巴巴网站，他时供应全球的电子商品批发市场，可以在里面找到很轻松的找到你想要的产品，而且价格都是明码标价的。找货和价格更容易谈了，但试错成本却高了很多，因为我们看不到摸不到感受不到商品的真实品质，所以在试错上成本会增加很多。&lt;/p&gt;

&lt;p&gt;第三。一次进货就有量起步，大概看了下，一种商品进货的起步资金需求在3000-10000不等。如果是5种商品以上的话，在库存压货上的资金就会有很大的压力。于是我想到了二级代理，让别人去压货，就相当于把被人当做仓库，提供了货源的资金，自己专注于做商品营销策划。这样我大大降低了，进货的风险和成本，坏处就是自己的利润少了，对商品的改进的主动能力少了，把控商品更加困难。&lt;/p&gt;

&lt;p&gt;第四。如果我又想控制商品质量和价格，又想少承担风险呢？怎么办？招募合伙人，分摊资金风险。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;h3 id=&quot;关于营销&quot;&gt;关于营销&lt;/h3&gt;

&lt;p&gt;从商品在卖出去到卖完的过程中，本身就需要一个自我完善的过程，其实就是从无到有过程。&lt;/p&gt;

&lt;p&gt;所以对营销方法的思考中，我认定一个原则，不断优化完善现有的方法和方案，才是最终成功卖完的关键。&lt;/p&gt;

&lt;p&gt;整个销售过程，我把它拆分成了几个部分，我们来从宏观的看下，我们到底需要面对哪些问题，并且有哪些手段和方法或者说途径来处理，其实就是我们有哪些工具可以运用。&lt;/p&gt;

&lt;p&gt;下面是我对整个销售过程，各个环节可以用到的方法和途径的一些总结。&lt;/p&gt;

&lt;h6 id=&quot;商品展示&quot;&gt;商品展示&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	宣传图片
	模特照片
	功能介绍
	材质介绍
	特性介绍
	视频介绍
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;文案&quot;&gt;文案&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	故事文案
	促销文案
	段子文案
	热门事件文案
	干货文案
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;营销途径&quot;&gt;营销途径&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	朋友圈传播营销 -- 促销转发点赞砍价的传播方式
	策划讨论营销 -- Q群和微信群策划一场讨论而造成的营销
	建立自己的客户群营销 —- 维护型营销方式
	微博营销 —- 吸粉吸眼球营销
	博客营销 —- 干货吸粉营销
	公众号营销 —- 自己建立公众号和借助别人的公众号
	视频营销 —- 视频中加入广告
	引流营销 —- 把不喜欢此商品的客户引向另一个商品
	直播营销 —- 真人直播介绍产品
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;策划案&quot;&gt;策划案&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	销售策划案
	促销策划案
	营销策划案
	功能开发策划案
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;广告策划&quot;&gt;广告策划&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	图文广告
	视频广告
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;广告投放途径&quot;&gt;广告投放途径&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	淘宝搜索广告
	百度搜索广告
	广告牌投放广告
	热门公众号广告投放
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;其实营销方案和途径还有很多种，包括百科营销，线下活动营销，展会营销等。不过那些我都认为是属于效果比较远，对于资金量大的企业来说是需要的，但对于资金量小的小企业甚至个体户来说事承担不起的成本。&lt;/p&gt;

&lt;p&gt;打造产品的时候，我们的目标是爆款，目标要高，这样才有更多的idea，更多的激情，更多的动力。&lt;/p&gt;

&lt;p&gt;爆款如何打造？其实网上干货很多。&lt;/p&gt;

&lt;p&gt;大致从三个方面入手。&lt;/p&gt;

&lt;h3 id=&quot;1产品质量与品质&quot;&gt;1。产品质量与品质。&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;	产品关有两个属性：第一是从科学角度出发，能够被相关领域的专家认可；第二是从消费角度出发，能够被消费者认可。

	要亲自使用，并且让团队的人一起使用，最好是天天用。亲自感受产品的好与坏，同时改善产品品质和使用体验。

	只有高于同行业品质的产品才能有机会成为爆款。一个烂产品是永远不可能成为爆款的。

	只有更好的产品更符合市场、消费者需求才能获得更好的进步。再加上价格因素，性价比也要高于行业中的竞品。

	高品质，高性价比的产品，在主推过程中，才能发挥最佳的效果。
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;2营销策划方案&quot;&gt;2。营销策划方案。&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;	产品对象选好了，就要对这个产品有针对性的进行策划宣传。

	首先，要有一套科学的营销方案。

	只有营销方案恰当，才能够为销售做好准备；如果营销方案不恰当，这个产品最终也只能是失败的。

	宣传、终端销售、消费者工作等，解决这些问题不要交给那些不了解产品的咨询公司，唯一办法就是靠自己摸索。

	有了想法后，先找一个小的市场试水，后面再不断地完善。

	比如经过研究后认为，朋友圈软文营销是最好的突破口，就从软文开始着手，一点点完善。

	倘若觉得软文还不够，再加入其他的营销途径比如客户群营销，一点点把原来简单的想法，变得更大，变得更系统，更庞大，更丰富，逐步吸收更多流量，更多关注。

	有一个好的产品，而且这个产品有知名度、有好口碑，是远远不够的，让消费者买得到才是最终目的。所以接下来我们做了营销手册，把营销标准化，将市场分类，比如A是淘宝店，B是微信商铺，C是网站电子商务。对这三类市场做细致的营销案拆分。

	比如淘宝使用直通车广告方式吸收流量，优化关键词等手段完善点击率与付费转化率，也就是砸钱换流量和关注度。

	而微信商品则采用软文营销和群聊营销手段，把客户的粘在公众号或群里面，也就是增加人力成本来换取流量和关注度。

	对于电子商务网站，则采用引流方式，把干货，故事，段子的软文放入，论坛，贴吧，微博，视频中去，将目标群体引导进网站。
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;3团队配合与执行力&quot;&gt;3。团队配合与执行力。&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;	如果仅仅做一个局部市场的话，老板亲自坐镇，问题不大。但如果目标是更大的市场甚至是全国市场，团队的作用就至关重要了,这就是产品的第三关——团队关。

	市场过大，每一个小的终端是否按照之前制定的标准严格执行，光靠老板一个人是无法全面监管的，所以团队的执行力很关键。

	如果前两关产品关和策划关都过了，而且我们都努力做到了一流，团队关过不了，比如广告费只谈到了30%，那么这个产品的运营成本会居高不下，也无法盈利。

	因此，究竟能不能让你的团队按照既定的标准执行，也是产品能否成功盈利的关键所在。

	这一点麦当劳就做得很好，总部搞研发，制定标准和流程；分部只需要按部就班的执行就可以了，这对于以全国为市场的行业来说是一个非常重要的方法。千万不能让终端人员自己发挥聪明才智，搞策划方案，最后弄得乌烟瘴气一团糟；一定要总部亲力亲为做市场调查，然后效果评估，最后制定出一个标准，再在全国铺开来做。
&lt;/code&gt;&lt;/pre&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;产品的品质是关键中的关键，好的品质才能衬托出大的潜力，加上精准的营销，让广告效应和传播效应深入人们的心里面，当人们有此类产品需求时，第一个想到的就是我们这个产品。而团队的配合和执行力，使得营销方案再锦上添花，爆款一触即发。所谓爆款都是天时地利人和的一种机遇，但没有品质，营销，团队三个因素，再好的机遇都会瞬间溜走。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Always challenge miracle</title>
   <link href="http://www.luzexi.com/2018/07/04/Always-challenge-miracle"/>
   <updated>2018-07-04T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/07/04/Always-challenge-miracle</id>
   <content type="html">&lt;p&gt;Recently I’m thinking of the meaning in my life. Whatever I lose or get something that was not the end of the life, we always have another objective to get or maybe restart training or studying and try to get again and again until win.&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;So I’m thinking that why me always like to chanllenge the work or the job or the task. Because i have much fun when i’m working , sometimes geting something or get some new skills or learned some knowledge. The root is I’m insteresting in doing some special thing which I really like.&lt;/p&gt;

&lt;p&gt;Sometimes I was tired, I dont know why, but I was really tired to do the special thing which interest before. But I’m still keep moving to do that, it makes the job much more lower effective, and my brain becomes more and more slow to run. The feeling of failed and sad coming, the situation becomes much more bad now.&lt;/p&gt;

&lt;p&gt;I know you have to win or get the special thing, its your purpose and you must insist.But is it the best way to insist doing the only thing you want? I think no, you can keep learning but not the only thing in your life.&lt;/p&gt;

&lt;p&gt;So I would like to say that, we must keep moving but not only in one thing. The most important thing is we are keeping have fun in studying or learning something. If you are tired, pls just put it down or drop it directly, then finding out the thing which you have fun.&lt;/p&gt;

&lt;p&gt;Do not spend all your time on training or studying - this way you will probably become very exhausted and unwilling to compete more. Whatever you do - have fun.&lt;/p&gt;

&lt;p&gt;Once you find programming is no fun anymore – drop it. Play soccer, find a girlfriend or boyfriend, study something not related to programming, just live a life.&lt;/p&gt;

&lt;p&gt;work are only work, job are only job, programming are only programming, and nothing more. Don’t let them become your life - for your life is much more interesting and colorful.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>股市底部探索逻辑分析</title>
   <link href="http://www.luzexi.com/2018/07/02/%E8%82%A1%E5%B8%82%E5%BA%95%E9%83%A8%E6%8E%A2%E7%B4%A2%E9%80%BB%E8%BE%91%E5%88%86%E6%9E%90"/>
   <updated>2018-07-02T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/07/02/股市底部探索逻辑分析</id>
   <content type="html">&lt;p&gt;前面探讨了 &lt;a href=&quot;http://www.luzexi.com/%E9%87%91%E8%9E%8D/%E9%87%91%E8%9E%8D%E6%8A%95%E8%B5%84/2018/06/22/%E8%82%A1%E5%B8%82%E7%9A%84%E5%BA%95%E9%83%A8%E7%A9%B6%E7%AB%9F%E6%98%AF%E6%80%8E%E6%A0%B7%E5%BD%A2%E6%88%90%E7%9A%84.html&quot;&gt;《股市的底部究竟是怎样形成的》&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;分析了股市是由人性决定的，是由人创造的，所以会因为人而辉煌，并且也会因为人而毁坏。&lt;/p&gt;

&lt;p&gt;面对不确定的持续性的下跌，我们应该如何面对的呢？&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;首先，我们是避不开下跌的，所以的上升都会引起同样的下跌。只要你在股市里，就必须面对下跌。&lt;/p&gt;

&lt;p&gt;其次，下跌也分好几种。有从高位摔下来的，有从中继平台继续下探的，有再创历史新低的。三种情况都不一样，高位摔下来肯定不能玩，中继平台下跌的可以看看，新低的要研究其基本面。&lt;/p&gt;

&lt;p&gt;那么要怎么样判断股票在下跌过程中时可以买入的呢？&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;一。基本面扎实。 是最最重要的一点，也是所有买入的必要条件。所谓买好股做好人就是这个道理。只有买好的公司的股票才真正赚钱。投资股票要像投资实业那样脚踏实地才能真正赚到钱。否则所谓的炒作，最终都会将自己带入坟墓。&lt;/p&gt;

&lt;p&gt;那么什么叫基本面扎实？&lt;/p&gt;

&lt;p&gt;首先，营收要稳定增长，毛利率要稳定，&lt;/p&gt;

&lt;p&gt;其次，债务不能太多否则营收利润都会被债务吃掉，&lt;/p&gt;

&lt;p&gt;然后，管理层人员，特别是CEO要有能力，善于学习，积极学习，深耕本行业。而不是为了抄高股价而来。&lt;/p&gt;

&lt;p&gt;最后，研发能力强，研发经费占比要大，这能说明公司比较重视科研，那些以运营为目的公司是走不长的。&lt;/p&gt;

&lt;p&gt;另外市值大小其实并不能说明其爆发力和持续能力的多少，小企业也同样能爆发大力量，大企业也会因为不善的经营管理策略而衰败。&lt;/p&gt;

&lt;p&gt;二。行业未来前途光明。如果行业未来的不确定性过大，就无法支撑当前的基本面的持续增长。也将导致股票价格的不确定性波动。一个在未来无法确定的东西，那跟赌博是没有区别的。所以我们一定要选，行业在未来的潜力大却持续性很好的来做。&lt;/p&gt;

&lt;p&gt;三。被低估。如果不是被低估的股票，是能难有好的收益的，即使他是高成长高发展的股票，也有到头的时候，如果不是被低估，注定要做很大的过山车。&lt;/p&gt;

&lt;p&gt;其实在牛市里，我们很难找到被低估的股票，基本上所有的股票都是被高估的，因为在牛市里大家的风险意识下降，只要有点前景的股票都会被买到很高的价格。&lt;/p&gt;

&lt;p&gt;而在牛熊中间，有很多诱骗人的价格，因为是从高处下来的股价，所以会认为捡到便宜货，而其实并不便宜，因为价格是相对的，看见过高的，就认为现在时便宜的。殊不知，在未来的熊市中，会有比这低一倍的价格出现。&lt;/p&gt;

&lt;p&gt;真正便宜的价格是在熊市中出现的。为什么这么说呢？因为只有在熊市中，人们的风险意识才是最高的，这个时候，很多利好甚至重大利好消息，都会被忽略，导致价格与实际基本面完全背离。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;在熊市中，有两种风险，一种是底部还有底，二是破产。熊市中的风险就是人们的恐慌，所以底部下还有底部，因为人们不相信一切未来，只在乎当下的亏损。而破产很大一部分原因就是因为大家的谨慎导致资金面紧缺，最后可能由于债务或者资金链导致无法继续经营下去而破产。只有那种基本面扎实的，一心扑在行业里深耕拓展的企业才能最终活下来。也只有脚踏实地的企业，能在熊市中崛起。&lt;/p&gt;

&lt;p&gt;从另外一个角度看，熊市是一个很好的布局机会。因为只有熊市才会让好企业的价格完全低于它企业本身的价值，在未来爆发的过程中，能得到很好的投资收益。&lt;/p&gt;

&lt;p&gt;记住，价格不会因为有人买而见底，只会因为没人卖而见底。同样，价格也不会因为有人卖而见顶，只会因为没人买而见顶。股市，房市，都是一样的道理，因为交易是由人性构成的。&lt;/p&gt;

&lt;p&gt;在持续下跌的熊市中，我们需要很好的耐心，来等待底部的形成，也需要很好的资金控制，逐步买入好的企业的股票。这也是我提倡的，无限现金流打法，按月持续买入底部好企业，并深入研究企业的运营状况，以及行业的发展方向。&lt;/p&gt;

&lt;p&gt;这市场有不少靠嘴巴吃饭的分析师几乎都是一个套路，市场刚刚下跌或没下跌时会反复说“A股没有大幅下跌的基础，长期慢牛上涨将是股市波动主基调”。后来市场大幅下跌了，跌得惨不忍睹，他们就开始换口吻说“股市大幅下跌已严重背离经济基本面”。其实，股市的涨跌一定有其内在原因。再说，股市就算是经济的晴雨表，也是未来经济基本面的晴雨表吧？既然是未来，是不是具备太多的不确定性因素呢？&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Vi和Vim常用命令集</title>
   <link href="http://www.luzexi.com/2018/06/30/Vi%E5%92%8CVim%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4%E9%9B%86"/>
   <updated>2018-06-30T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/06/30/Vi和Vim常用命令集</id>
   <content type="html">&lt;p&gt;vi,vim命令是种神奇的存在，他似乎提高了编辑的效率但门槛又变的太高导致不能流行在程序员中。看起来有些复杂的vi命令编辑，在一些人眼里感觉很厉害，其实都是通过成千上百次的练习掌握的技巧。本文我就来试图分解这些命令块，让大家在练习时，能更加有针对性更加有序有逻辑。&lt;/p&gt;

&lt;h2 id=&quot;入门命令之简单编辑&quot;&gt;入门命令之简单编辑&lt;/h2&gt;

&lt;h5 id=&quot;1光标移动&quot;&gt;1.光标移动&lt;/h5&gt;

&lt;pre&gt;&lt;code&gt;	←↓↑→				h,j,k,l
	到下一行的第一个字符	+
	到上一行的第一个字符	-
	到单词的结尾			e或E(忽略符号，只识别空格，回车)
	往后一个单词			w或W(跟符号和空格和回车挂钩)
	往前一个单词			b或B(跟符号和空格和回车挂钩)
	到一行的结尾			$
	到一行的开头			0(零)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;h5 id=&quot;2文本编辑操作&quot;&gt;2.文本编辑操作&lt;/h5&gt;

&lt;pre&gt;&lt;code&gt;	在光标位置插入文本			i
	在一行的开头插入文本		I(大写I)
	在光标所在位置附加文本		a
	在一行的最后附加文本		A(大写A)
	在光标下一行打开新行		o
	在光标上一行打开新行		O(大写O)
	删除一行并替换文本			S(大写S)
	用新文本覆盖现有的文本		R(大写R)
	合并当前这一行与下一行		J(大写J)
	切换当前字符的大小写		~
	重复上一个动作			.
	撤销上一个动作			u
	将一整行恢复到原来的		U(大写U)
&lt;/code&gt;&lt;/pre&gt;

&lt;h5 id=&quot;3对缓冲区的操作&quot;&gt;3.对缓冲区的操作&lt;/h5&gt;

&lt;pre&gt;&lt;code&gt;	粘贴内容					p或P
	保存文件					w
	保存并退出				wq
	强制退出不保存			q!
&lt;/code&gt;&lt;/pre&gt;

&lt;h5 id=&quot;4组合命令&quot;&gt;4.组合命令&lt;/h5&gt;

&lt;pre&gt;&lt;code&gt;	更改一个单词		cw
	删除一个单词		dw
	赋值一个单词		yw
	更改一整行		cc
	删除一整行		dd
	复制一整行		yy
	更改到行尾		c$或C
	删除到行尾		d$或D
	复制到行尾		y$
	更改到行头		c0
	删除到行头		d0
	复制到行头		y0
	更改一个字		I
	删除一个字		x或X
	复制一个字		y1或yh
	更改5个字			5s
	删除5个字			5x
	复制5个字			5yl
&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id=&quot;高级进阶之光标快速移动&quot;&gt;高级进阶之光标快速移动&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;h5 id=&quot;快速翻屏&quot;&gt;快速翻屏&lt;/h5&gt;

    &lt;pre&gt;&lt;code&gt; 往后滚动一整屏		ctrl + f
 往前滚动一整屏		ctrl + b
 往后半屏				ctrl + d
 往前半屏				ctrl + u
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;h5 id=&quot;同屏移动&quot;&gt;同屏移动&lt;/h5&gt;

    &lt;pre&gt;&lt;code&gt; 移动到屏幕顶端		H
 移动到屏幕中间		M
 移动到屏幕底部		L
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;h5 id=&quot;段落移动&quot;&gt;段落移动&lt;/h5&gt;

    &lt;pre&gt;&lt;code&gt; 移动到当前段落开头		{
 移动到下一段开头		}
 移动到这一节开头		[[
 移动到下一节开头		]]
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;h5 id=&quot;搜索模式移动&quot;&gt;搜索模式移动&lt;/h5&gt;

    &lt;pre&gt;&lt;code&gt; 往后搜索某个字符串		/搜索的字符串
 往前搜索某个字符串		?搜索的字符串
 往同一个方向重复搜索	n
 往反方向重复搜索		N
 往后重复搜索			/
 往前重复搜索			?
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;h5 id=&quot;直接跳转到某个位置&quot;&gt;直接跳转到某个位置&lt;/h5&gt;

    &lt;pre&gt;&lt;code&gt; 跳转至第n行			nG
 跳转至文件末尾		``
 显示当前的行号		ctrl + g
 光标往后n行			nj
 光标往前n行			nk
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;h5 id=&quot;做标记只在一个文件内有效&quot;&gt;做标记(只在一个文件内有效)&lt;/h5&gt;

    &lt;pre&gt;&lt;code&gt; 将当前位置标记成x字符		mx
 将光标移动到第一个标记x		'x(单引号)
 将光标移动到以x标记的字符	`x(反引号)
 回到上一个标记位置			``(两个反引号)
 回到上一个标记开头			''(两个单引号)
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;h2 id=&quot;高级进阶之搜索与替换&quot;&gt;高级进阶之搜索与替换&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;h5 id=&quot;搜索与替换的基本命令&quot;&gt;搜索与替换的基本命令&lt;/h5&gt;

    &lt;pre&gt;&lt;code&gt; s/old/new		搜索替换当前行，第一个找到的
 s/old/new/g 	搜索替换当前行，所有找到的
 g/old/new/g 	搜索替换当前文件中，所有找到的内容
 g/old/new/gc	搜索替换当前文件中，所有找到的内容，并且做yes or no 的替换确认

 g/pattern/s/old/new/gc 先搜索文件中所有含有pattern的行，在这些行中找到的所有old内容，并且做yes or no 的替换确认
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;h5 id=&quot;搜索替换匹配规则&quot;&gt;搜索替换匹配规则&lt;/h5&gt;

    &lt;pre&gt;&lt;code&gt; .(点号) 表示任意一个字符
 * 表示任意字符
 ^ 是正则表达式的开头
 $ 正则表达式的结尾
 \ 特殊字符
 [] 匹配出方括号里的任何一个字符。
 \&amp;lt; 以某字符开头
 \&amp;gt; 以某字符结尾
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;h5 id=&quot;查找替换多个文件内容&quot;&gt;查找替换多个文件内容&lt;/h5&gt;

    &lt;pre&gt;&lt;code&gt; 多文件查找内容

 使用grep
	
 直接在vim中输入:grep abc * 这是直接调用unix下的grep命令 
	
 使用vimgrep 

 基本用法就是
 :vimgrep /匹配模式/[g][j] 要搜索的文件/范围 
 :vim[grep][!] /{pattern}/[g][j] {file} ...
 g 和 j 是两个可选的标志位，g表示是否把每一行的多个匹配结果都加入。j表示是否搜索完后定位到第一个匹配位置。
 要搜索的文件 可以是具体的文件路径，也可以是带通配符的路径比如 *.as **/*.as ，**表示递归所有子目录。 要搜索的文件和或搜索范围都可 以写多个，用空格分开。

 例子：

 :vimgrep /\&amp;lt;flash\&amp;gt;/ **/*.as 搜索当前目录以及所有子目录内as文件中的 &quot;flash&quot;
 :vimgrep /an error/ *.c 就是在所有的.c文件中搜索an error。
 :vimgrep/an error/* 意思是查找当前目录下的文件中的an error，不包括子目录

 定位 

 输入上述的命令后，可以像输入:make命令，那样定位匹配到的文件位置 
 :cnext (:cn)           下一个匹配位置
 :cprevious (:cp)     上一个匹配位置
 :cwindow (:cw)     quickfix窗口，可以选择匹配的文件位置
 :cl(:clist)                查看所有匹配的位置

 多文件替换(arg) 
 a、加入要处理的文件  :args *.txt
 b、输入对上述文件的动作  :argdo %s/hate/love/gc | update  （这里将hate替换成love，update表示要写入到文件中，否则只作替换而不写入）
&lt;/code&gt;&lt;/pre&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;高级进阶之强化功能&quot;&gt;高级进阶之强化功能&lt;/h2&gt;

&lt;h5 id=&quot;1分割多窗口&quot;&gt;1.分割多窗口&lt;/h5&gt;

&lt;pre&gt;&lt;code&gt;直接打开多窗口

	$ vim -o file1 file2 使用shell命令开启多窗口

分割窗口

	:[n]split [++opt] [+cmd] [file] 横向分割窗口

	: 前面必须以冒号开始命令

	n 为新窗口中显示的行数

	opt 为传递给新窗口的选项信息(必须前置两个+号)

	cmd 为传入新窗口中执行的命令(必须前置一个+号)

	file 指定打开的文件地址

	例如： :15split ++fileformat-unix ./site/testfile.txt
	新窗口有15行高，确保是unix文件格式，路径为./site/testfile.txt

	另外还有，vsplit, new, vnew都是同理分割窗口，split和new只有细微差别。
	:[n]vsplit [++opt] [+cmd] [file] 是垂直分割
	:[n]new [++opt] [+cmd] [file] 是横向分割，并执行自动命令
	:[n]vnew [++opt] [+cmd] [file] 为垂直分割，并执行自动命令

	此外，还有sview filename 和 sfind filename作为辅助作用也很重要。
	sview 以只读形式打分割一个窗口打开文件
	sfind 为在path中寻找filename，如果未找到则不会分割窗口

	vi命令
	split		ctrl + w then s
	vsplit		ctrl + w then v
	new			ctrl + w then n

窗口游走

	光标游走到下边窗口 ctrl + w then j 或 ↓(down)
	光标游走到上边窗口 ctrl + w then k 或 ↑(up)
	光标游走到左边窗口 ctrl + w then h 或 ←(left)
	光标游走到右边窗口 ctrl + w then l 或 →(right)

	另外还有一些不太实用的游走命令比如左上，右上，左下，右下游走等这里不进行细致的说明了，用到的实在是很少。

窗口移动

	移动本窗口到最顶端		ctrl + w then K(大写)
	移动本窗口到最底端		ctrl + w then J(大写)
	移动本窗口到最左端		ctrl + w then H(大写)
	移动本窗口到最右端		ctrl + w then L(大写)
	移动本窗口向右或向下轮换		ctrl + w then r(小写)
	移动本窗口向左或向上轮换		ctrl + w then R(大写)
	移动本窗口与下一个窗口交换		ctrl + w then x

	上下左右的窗口移动都是不太实用的操作，很少会有。本来窗口移动的频率就小，基本都是顶底左右，其他操作基本可以忽略。

窗口大小调整

	窗口高度减少一行	ctrl + w then -
	窗口高度增加一行	ctrl + w then +
	窗口宽度减少一行	ctrl + w then &amp;gt;
	窗口宽度增加一行	ctrl + w then &amp;lt;
&lt;/code&gt;&lt;/pre&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;h5 id=&quot;2标签页多窗口&quot;&gt;2.标签页多窗口&lt;/h5&gt;

&lt;pre&gt;&lt;code&gt;	:tabe &amp;lt;文件名&amp;gt;  在新标签页中打开指定的文件。
	:tabnew &amp;lt;文件名&amp;gt;  在新标签页中编辑新的文件。
	:tab split  在新标签页中，打开当前缓冲区中的文件。
	:tabf 或 :tabfind  允许你在当前目录搜索文件，并在新标签页中打开。比如:tabf img.*。此命令只能打开一个文件.

	Vim默认最多只能打开10个标签页。你可以用set tabpagemax=15改变这个限制。

	列示标签页
	:tabs  显示已打开标签页的列表，并用“&amp;gt;”标识出当前页面，用“+”标识出已更改的页面。

	关闭标签页
	:tabc  关闭当前标签页。
	:tabo  关闭所有的标签页。
	:tabo :tabonly 关闭所有其他标签页

	切换标签
	:tabn 或 gt  移动到下一个标签页。
	:tabp 或 gT  移动到上一个标签页。
	:tabfirst 或 :tabfir 或 :tabr  移动到第一个标签页。
	:tablast 或 :tabl  移动到最后一个标签页。

	移动标签页
	:tabm [次序]  移动当前文件的标签页次序。比如:tabm 1将把当前标签页移动到第2的位置。如:tabm不指定参数将被移动到最后。

	配置标签页
	:set showtabline=[1,2,3]  标签页在窗口上方显示标签栏。=0完全不显示标签栏，=1只有用户新建时才显示，=2总是显示标签栏。

	多标签页命令
	:tabdo &amp;lt;命令&amp;gt;  同时在多个标签页中执行命令。比如:tabdo %s/food/drink/g 命令把当前多个标签页文件中的“food”都替换成“drink”。
&lt;/code&gt;&lt;/pre&gt;

&lt;h5 id=&quot;3批量缩进&quot;&gt;3.批量缩进&lt;/h5&gt;

&lt;pre&gt;&lt;code&gt;	方法一

	按v进入visual状态，选择多行，然后用&amp;gt;缩进或&amp;lt;缩出 

	方法二

	&amp;gt;为缩进，&amp;lt;为缩出，前面加n，表示当前行起n行同时缩进或缩出，5&amp;gt; then 回车 或 5&amp;lt; then 回车
&lt;/code&gt;&lt;/pre&gt;

</content>
 </entry>
 
 <entry>
   <title>Markdown(MD)的语法说明</title>
   <link href="http://www.luzexi.com/2018/06/27/Markdown-MD-%E7%9A%84%E8%AF%AD%E6%B3%95%E8%AF%B4%E6%98%8E"/>
   <updated>2018-06-27T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/06/27/Markdown-MD-的语法说明</id>
   <content type="html">&lt;p&gt;针对中文,演示Markdown的各种语法&lt;/p&gt;

&lt;p&gt;标题大小用多个#号表示大小，#越多字体越小&lt;/p&gt;

&lt;p&gt;#
#这是 H1&lt;/p&gt;

&lt;p&gt;##
##这是 H2&lt;/p&gt;

&lt;p&gt;###&lt;/p&gt;
&lt;h3 id=&quot;这是-h3&quot;&gt;这是 H3&lt;/h3&gt;

&lt;p&gt;####&lt;/p&gt;
&lt;h4 id=&quot;这是-h4&quot;&gt;这是 h4&lt;/h4&gt;

&lt;p&gt;#####&lt;/p&gt;
&lt;h5 id=&quot;这是-h5&quot;&gt;这是 h5&lt;/h5&gt;

&lt;p&gt;######&lt;/p&gt;
&lt;h6 id=&quot;这是-h6&quot;&gt;这是 h6&lt;/h6&gt;

&lt;p&gt;????&lt;/p&gt;

&lt;h3 id=&quot;注意下面所有语法的提示我都先用小标题提醒了&quot;&gt;注意!!!下面所有语法的提示我都先用小标题提醒了!!!&lt;/h3&gt;

&lt;h3 id=&quot;单行文本框&quot;&gt;单行文本框&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;这是一个单行的文本框,只要1个Tab再输入文字即可
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;多行文本框&quot;&gt;多行文本框&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;	这是一个有多行的文本框
	你可以写入代码等,每行文字只要输入2个Tab再输入文字即可
	这里你可以输入一段代码
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;比如我们可以在多行文本框里输入一段代码来一个java版本的helloworld吧&quot;&gt;比如我们可以在多行文本框里输入一段代码,来一个Java版本的HelloWorld吧&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;    public class HelloWorld {
      /**
      * @param args
	    */
	    public static void main(String[] args) {
		    System.out.println(&quot;HelloWorld!&quot;);

	    }

    }
&lt;/code&gt;&lt;/pre&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;链接&quot;&gt;链接&lt;/h3&gt;

&lt;p&gt;链接内容定义的形式为：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;方括号（前面可以选择性地加上至多三个空格来缩进），里面输入链接文字
接着一个冒号加空格或直接括号
接着链接的网址
选择性地接着 title 内容，可以用单引号、双引号或是括弧包着
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;例如:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;	[点击这里你可以链接到www.baidu.com](http://www.baidu.com)&amp;lt;br /&amp;gt;
	[点击这里我你可以链接到我的博客](http://luzexi.com)&amp;lt;br /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;效果:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.baidu.com&quot;&gt;点击这里你可以链接到www.baidu.com&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://luzexi.com&quot;&gt;点击这里我你可以链接到我的博客&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;显示图片&quot;&gt;显示图片&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;	[]前面加!就代表图片了，其他和普通的连接差不多
	![icon](http://luzexi.com/public/apple-touch-icon-144-precomposed.png &quot;icon&quot;)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;效果:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://luzexi.com/public/apple-touch-icon-144-precomposed.png&quot; alt=&quot;icon&quot; title=&quot;icon&quot; /&gt;&lt;/p&gt;

&lt;p&gt;###想点击某个图片进入一个网页,比如我想点击blog的icorn然后再进入www.luzexi.com
		[]中加入图片显示，进行嵌套操作
		&lt;a href=&quot;http://www.luzexi.com/&quot;&gt;&lt;img src=&quot;http://luzexi.com/public/favicon.ico&quot; alt=&quot;image&quot; title=&quot;blog&quot; /&gt;&lt;/a&gt;
		这个可以分解拆分为[&lt;img src=&quot;http://luzexi.com/public/favicon.ico&quot; alt=&quot;image&quot; title=&quot;blog&quot; /&gt;] 和 (http://www.luzexi.com/) 两部分&lt;/p&gt;

&lt;p&gt;效果:&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.luzexi.com/&quot;&gt;&lt;img src=&quot;http://luzexi.com/public/favicon.ico&quot; alt=&quot;image&quot; title=&quot;blog&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;文字被些字符包围&quot;&gt;文字被些字符包围&lt;/h3&gt;

&lt;p&gt;&amp;gt; 文字被些字符包围&lt;/p&gt;

&lt;p&gt;&amp;gt; 只要再文字前面加上&amp;gt;空格即可&lt;/p&gt;

&lt;p&gt;&amp;gt; 如果你要换行的话,新起一行,输入&amp;gt;空格即可,后面不接文字&lt;/p&gt;

&lt;p&gt;&amp;gt; 但&amp;gt; 只能放在行首才有效&lt;/p&gt;

&lt;p&gt;效果:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;文字被些字符包围&lt;/p&gt;

  &lt;p&gt;只要再文字前面加上&amp;gt;空格即可&lt;/p&gt;

  &lt;p&gt;如果你要换行的话,新起一行,输入&amp;gt;空格即可,后面不接文字
但&amp;gt; 只能放在行首才有效&lt;/p&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;h3 id=&quot;文字被些字符包围多重包围&quot;&gt;文字被些字符包围,多重包围&lt;/h3&gt;

&lt;p&gt;&amp;gt; 文字被些字符包围开始&lt;/p&gt;

&lt;p&gt;&amp;gt; &amp;gt; 只要再文字前面加上&amp;gt;空格即可&lt;/p&gt;

&lt;p&gt;&amp;gt;  &amp;gt; &amp;gt; 如果你要换行的话,新起一行,输入&amp;gt;空格即可,后面不接文字&lt;/p&gt;

&lt;p&gt;&amp;gt; &amp;gt; &amp;gt; &amp;gt; 但&amp;gt; 只能放在行首才有效&lt;/p&gt;

&lt;p&gt;效果:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;文字被些字符包围开始&lt;/p&gt;

  &lt;blockquote&gt;
    &lt;p&gt;只要再文字前面加上&amp;gt;空格即可&lt;/p&gt;
  &lt;/blockquote&gt;

  &lt;blockquote&gt;
    &lt;blockquote&gt;
      &lt;p&gt;如果你要换行的话,新起一行,输入&amp;gt;空格即可,后面不接文字&lt;/p&gt;
    &lt;/blockquote&gt;
  &lt;/blockquote&gt;

  &lt;blockquote&gt;
    &lt;blockquote&gt;
      &lt;blockquote&gt;
        &lt;p&gt;但&amp;gt; 只能放在行首才有效&lt;/p&gt;
      &lt;/blockquote&gt;
    &lt;/blockquote&gt;
  &lt;/blockquote&gt;
&lt;/blockquote&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;特殊字符处理&quot;&gt;特殊字符处理&lt;/h3&gt;
&lt;p&gt;有一些特殊字符如&amp;lt;,#等,只要在特殊字符前面加上转义字符\即可&lt;br /&gt;
你想换行的话其实可以直接用html标签&amp;lt;br /&amp;gt;&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;列表&quot;&gt;列表&lt;/h3&gt;

&lt;h5 id=&quot;无序列表使用星号加号或是减号作为列表标记&quot;&gt;无序列表使用星号、加号或是减号作为列表标记：&lt;/h5&gt;

&lt;pre&gt;&lt;code&gt;*   Jesse
*   Sharon
*   Anne
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;效果:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Jesse&lt;/li&gt;
  &lt;li&gt;Sharon&lt;/li&gt;
  &lt;li&gt;Anne&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;等同于：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;+   Jesse
+   Sharon
+   Anne
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;效果:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Jesse&lt;/li&gt;
  &lt;li&gt;Sharon&lt;/li&gt;
  &lt;li&gt;Anne&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;等同于：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;-   Jesse
-   Sharon
-   Anne
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;效果:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Jesse&lt;/li&gt;
  &lt;li&gt;Sharon&lt;/li&gt;
  &lt;li&gt;Anne&lt;/li&gt;
&lt;/ul&gt;

&lt;hr /&gt;

&lt;p&gt;#####有序列表则使用数字接着一个英文句点&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;1.  Jesse
2.  Sharon
3.  Anne
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;效果:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Jesse&lt;/li&gt;
  &lt;li&gt;Sharon&lt;/li&gt;
  &lt;li&gt;Anne&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;等同于:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;1. Jesse
1. Sharon
1. Anne
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;效果:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Jesse&lt;/li&gt;
  &lt;li&gt;Sharon&lt;/li&gt;
  &lt;li&gt;Anne&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;等同于:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;4. Jesse
2. Sharon
8. Anne
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;效果:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Jesse&lt;/li&gt;
  &lt;li&gt;Sharon&lt;/li&gt;
  &lt;li&gt;Anne&lt;/li&gt;
&lt;/ol&gt;

&lt;hr /&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;h3 id=&quot;分割线&quot;&gt;分割线&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;	* * *

	***

	*****

	- - -

	---------------------------------------
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;效果:&lt;/p&gt;

&lt;p&gt;test1&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;test2
***
test3&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;testend&lt;/p&gt;

&lt;hr /&gt;

&lt;h3 id=&quot;强调&quot;&gt;强调&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;	*single asterisks*

	_single underscores_

	**double asterisks**

	__double underscores__
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;效果:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;single asterisks&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;single underscores&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;double asterisks&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;double underscores&lt;/strong&gt;&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>JaveScript的奇葩写法规则</title>
   <link href="http://www.luzexi.com/2018/06/26/JaveScript%E7%9A%84%E5%A5%87%E8%91%A9%E5%86%99%E6%B3%95%E8%A7%84%E5%88%99"/>
   <updated>2018-06-26T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/06/26/JaveScript的奇葩写法规则</id>
   <content type="html">&lt;h3 id=&quot;--箭头函数&quot;&gt;① =&amp;gt; 箭头函数&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;	两个箭头同时使用 arg1 =&amp;gt; arg2 =&amp;gt;{return xxx;}
	可以理解为
	function( arg1 )
	{
		return function(arg2)
		{
			return xxx;
		}
	}
	也就是可以是 (arg1,arg2)=&amp;gt;{return xxx;}的简写
	也就是function( arg1, arg2 ){ return xxx; }的意思

===
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;--剩余数据&quot;&gt;② … 剩余数据&lt;/h3&gt;

&lt;h6 id=&quot;数组中的剩余数据&quot;&gt;数组中的剩余数据&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	[a, b, ...rest] = [10, 20, 30, 40, 50];
	console.log(a); // 10
	console.log(b); // 20
	console.log(rest); // [30, 40, 50]
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;对象中的剩余数据&quot;&gt;对象中的剩余数据&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	({a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40});
	console.log(a); // 10
	console.log(b); // 20
	console.log(rest); // {c: 30, d: 40}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;--数组分配赋值用法&quot;&gt;③ [] 数组分配赋值用法&lt;/h3&gt;

&lt;h6 id=&quot;数组分配1&quot;&gt;数组分配1&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	var x = [1, 2, 3, 4, 5];
	var [y, z] = x;
	console.log(y); // 1
	console.log(z); // 2
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;数组分配2&quot;&gt;数组分配2&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	var foo = ['one', 'two', 'three'];
	var [one, two, three] = foo;
	console.log(one); // &quot;one&quot;
	console.log(two); // &quot;two&quot;
	console.log(three); // &quot;three&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;数组赋值默认值用法&quot;&gt;数组赋值默认值用法&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	var a, b;
	[a=5, b=7] = [1];
	console.log(a); // 1
	console.log(b); // 7
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;用数组做变量置换&quot;&gt;用数组做变量置换&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	var a = 1;
	var b = 3;
	[a, b] = [b, a];
	console.log(a); // 3
	console.log(b); // 1
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;省略赋值用法&quot;&gt;省略赋值用法&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	var [a, ...b] = [1, 2, 3];
	console.log(a); // 1
	console.log(b); // [2, 3]
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;--对象分配用法&quot;&gt;④ {} 对象分配用法&lt;/h3&gt;

&lt;h6 id=&quot;对象分配赋值&quot;&gt;对象分配赋值&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	var o = {p: 42, q: true};
	var {p, q} = o;
	console.log(p); // 42
	console.log(q); // true
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;默认值用法&quot;&gt;默认值用法&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	var {a = 10, b = 5} = {a: 3};
	console.log(a); // 3
	console.log(b); // 5
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;指定key赋值默认值&quot;&gt;指定key赋值+默认值&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	var {a:aa = 10, b:bb = 5} = {a: 3};
	console.log(aa); // 3
	console.log(bb); // 5
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;指定属性key解析赋值&quot;&gt;指定属性key解析赋值&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	let key = 'z';
	let {[key]: foo} = {z: 'bar'};
	console.log(foo); // &quot;bar&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;对象中剩余赋值法&quot;&gt;对象中剩余赋值法&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	let {a, b, ...rest} = {a: 10, b: 20, c: 30, d: 40}
	a; // 10 
	b; // 20 
	rest; // { c: 30, d: 40 }
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;弱变量定义与赋值&quot;&gt;弱变量定义与赋值&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	const foo = { 'fizz-buzz': true };
	const { 'fizz-buzz': fizzBuzz } = foo;
	console.log(fizzBuzz); // &quot;true&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;h3 id=&quot;-objectassign-合并对象&quot;&gt;⑤ Object.assign() 合并对象&lt;/h3&gt;

&lt;h6 id=&quot;将源对象-source-的所有可枚举属性复制到目标对象-target-&quot;&gt;将源对象（ source ）的所有可枚举属性，复制到目标对象（ target ）&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	var target = { a: 1 };
	var source1 = { b: 2 };
	var source2 = { c: 3 };
	Object.assign(target, source1, source2);
	target // {a:1, b:2, c:3}
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;如果目标对象与源对象有同名属性或多个源对象有同名属性则后面的属性会覆盖前面的属性&quot;&gt;如果目标对象与源对象有同名属性，或多个源对象有同名属性，则后面的属性会覆盖前面的属性&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	var target = { a: 1, b: 1 };
	var source1 = { b: 2, c: 2 };
	var source2 = { c: 3 };
	Object.assign(target, source1, source2);
	target // {a:1, b:2, c:3}
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;objectassign方法实行的是浅拷贝而不是深拷贝也就是说如果源对象某个属性的值是对象那么目标对象拷贝得到的是这个对象的引用&quot;&gt;Object.assign方法实行的是浅拷贝，而不是深拷贝。也就是说，如果源对象某个属性的值是对象，那么目标对象拷贝得到的是这个对象的引用。&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	var obj1 = {a: {b: 1}};
	var obj2 = Object.assign({}, obj1);
	obj1.a.b = 2;
	obj2.a.b // 2
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;对于这种嵌套的对象一旦遇到同名属性objectassign的处理方法是替换而不是添加&quot;&gt;对于这种嵌套的对象，一旦遇到同名属性，Object.assign的处理方法是替换，而不是添加。&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	var target = { a: { b: 'c', d: 'e' } }
	var source = { a: { b: 'hello' } }
	Object.assign(target, source)
	// { a: { b: 'hello' } }
&lt;/code&gt;&lt;/pre&gt;

&lt;h6 id=&quot;objectassign-常见用途&quot;&gt;Object.assign 常见用途&lt;/h6&gt;

&lt;pre&gt;&lt;code&gt;	为对象添加属性
	class Point {
		constructor(x, y) {
			Object.assign(this, {x, y});
		}
	}

	为对象添加方法
	Object.assign(SomeClass.prototype, {
		someMethod(arg1, arg2) {
		···
		},
		anotherMethod() {
		···
		}
	});
	//  等同于下面的写法
	SomeClass.prototype.someMethod = function (arg1, arg2) {
	···
	};
	SomeClass.prototype.anotherMethod = function () {
	···
	};

	克隆对象
	function clone(origin) {
		return Object.assign({}, origin);
	}

	合并多个对象
	const merge =(target, ...sources) =&amp;gt; Object.assign(target, ...sources);

	为属性指定默认值
	const DEFAULTS = {
		logLevel: 0,
		outputFormat: 'html'
	};
	function processContent(options) {
		let options = Object.assign({}, DEFAULTS, options);
	}
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;-function-函数function后面跟括号&quot;&gt;⑥ (function(){})(); 函数function后面跟()括号&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;	举个例子:

	(function(p1,p2){alert(p1+p2);})(1,2);

	实际就相当与

	function test(p1,p2){
	   alert(p1+p2);
	};
	test(1,2);

	这种写法可以看做是私有的内部类,一般出于加载时就需要立即执行的代码可以这样来些,第2个就是避免与其它的名称相冲突.

	匿名方法的好处,上面也有提到.

	1.其它外部调用不到,相对安全.

	2.可用于onload事件保证不与其冲突.

	3.可看做线程安全.
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;-function-函数function前面加感叹号&quot;&gt;⑦ !function(){}(); 函数function前面加!感叹号&lt;/h3&gt;

&lt;pre&gt;&lt;code&gt;	如果我们尝试为一个“定义函数”末尾加上()，解析器是无法理解的。

	function msg(){
	  alert('message');
	}();//解析器是无法理解的

	原来，使用括号包裹定义函数体，解析器将会以函数表达式的方式去调用定义函数。也就是说，任何能将函数变成一个函数表达式的作法，都可以使解析器正确的调用定义函数。而 ! 就是其中一个，而 + - || 都有这样的功能。

	其实就是为了能省略一个字符……

	// 这么写会报错，因为这是一个函数定义：
	function(){}()

	// 常见的（多了一对括号），调用匿名函数：
	(function() {})()

	// 但在前面加上一个布尔运算符（只多了一个感叹号），就是表达式了，将执行后面的代码，也就合法实现调用
	!function() {}()
&lt;/code&gt;&lt;/pre&gt;

</content>
 </entry>
 
 <entry>
   <title>股市的底部究竟是怎样形成的</title>
   <link href="http://www.luzexi.com/2018/06/22/%E8%82%A1%E5%B8%82%E7%9A%84%E5%BA%95%E9%83%A8%E7%A9%B6%E7%AB%9F%E6%98%AF%E6%80%8E%E6%A0%B7%E5%BD%A2%E6%88%90%E7%9A%84"/>
   <updated>2018-06-22T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/06/22/股市的底部究竟是怎样形成的</id>
   <content type="html">&lt;p&gt;股灾再一次来临，对于大部分投资者来说都是痛苦的。中国股市这两年在金融领域受挫很大，股，债，汇都面临巨大的压力，爆发出无数的问题。大部分投资者都付出了沉痛的代价，30%的损失已经算是少的了，多的可以达到70-80%甚至90%以上的损失。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;不过无论环境有多差，投机的活跃资金一直都会尝试新的突破和抄底。但个人意志和能力拗不过大的趋势，中国股市也在持续滑向风雨缥缈，活跃资金的一次次的抄底带来的一次次的失望，导致抄底资金也陷入了困境。这让所有的人都陷入了失望，失望我认为还太轻，绝望又太重，差不多是在失望和绝望的中间状态。&lt;/p&gt;

&lt;p&gt;有话说的好，要在别人恐惧的时候我贪婪，别人贪婪的时候我恐惧。这句话虽然没什么大问题，而且算是极其有道理的，但细化的内容大家都没有认识到，一味得执行别人恐惧我贪婪，别人贪婪我恐惧，就容易导致巨大的亏损。细化内容比如你如何判断大家是最后一次恐惧，而不会继续恐惧，又比如如何判断别人是最后一次绝望的，而不会有更绝望的事情发生，反过来也是一样，如何判断大家是贪婪的，并且不会有更贪婪的事情发生。除了上面说的几条外，还需要自身做到一些非常人的境界，比如自身必须不受恐惧影响，不害怕不犹豫的坚定买入，又比如买入时能否做到冷静克制而不贪婪，不可以一次性提升大量仓位而是在2个月或者半年内慢慢提升仓位，又比如当买入时再次遇到恐惧甚至踩踏，是否能够理性分析临危不乱。&lt;/p&gt;

&lt;p&gt;首先必须坚定的事实是，金融是动态的，并且反射到我们生活中来说，世界也是动态的。事件，事情，经济，人心，都是在动态的发生着变化，谁都无法准确预料明天，或者下一秒会发生什么事情。今天的底，有可能是明天的中继平台，今天的涨，也有可能是只是反弹后继续下跌的一个陷阱。但是为什么，从现在看历史，历史是却是清晰的，为什么？！因为金融是动态的，世界是动态的，人心是动态的，他随时随地的在变化，没有人能准确预料到下一秒会是什么。但是，这里重点强调，一旦发生就固定在了历史中了。这就是为什么，我们回顾看历史都是固定清晰的，而当前的状况或者说当前的情况，都是模糊不清，难以琢磨的。&lt;/p&gt;

&lt;p&gt;如果要刨根问底说，为什么就无法准确预料下一秒下一天下个月发生的事呢，答案就是人本身。这个世界是由人创造的，金融也是一样是由人创造出来，它受到人心的支配，人心是怎么样的，它就是怎么样的，人心是飘忽不定的，它也就是飘忽不定的。我们生活在人类创造的世界里，受到人群的影响，而人本身思维就是个无法琢磨的无法确定的存在，无论你多少厉害，都无法确定的知道人本身是如何想的，即使你知道人是如何想的，也并不可能知道他会如何做，因为世界是动态的，人与人之间的影响也是动态的，所有这一切你看到的都是动态的。就是由于人类本身是无法琢磨的，导致了无法准确的预测人类的行为，进而导致无法准确的预测下一秒下一天下个月发生的事。&lt;/p&gt;

&lt;p&gt;无法准确预测是件很恐怖的事，因为未知，或者说对未知的恐惧。庆幸的是，无法准确预测，却可以从一个比较长的时间中猜测到大概率会发生什么。我们可以根据大概率发生的事件而提前布局，比如中国股市大概率在未来10-20年内会全面开放给全球，全球投资者涌入的同时会带来一波巨大的牛市，冲破6000点不是梦。当然，这10-20年内中间的过程究竟会发生什么，谁都无法知道。就目前来看，是不会太顺利的。毕竟不经历风雨怎能见彩虹。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;从上面的分析来看，股市是不确定的，无法准确的预测，甚至有时模糊的预测都是错误的，只可以做一个5年或者10年或者20年后大概率发生的事，时间越久概率越大。人性恐惧导致了底部还有底。&lt;/p&gt;

&lt;p&gt;面对下跌的熊市，那么最终股市的底部是如何形成的呢，我们来推演下沙盘。这可以解决我们的心理的疑惑，知道未来会发生的大概的模糊的画面，好让自己有个心理准备，并且做出相应的对策。&lt;/p&gt;

&lt;p&gt;1.下跌。有人开始卖出，↓&lt;/p&gt;

&lt;p&gt;2.持续下跌。越来越多人不相信会涨，↓↓&lt;/p&gt;

&lt;p&gt;3.巨幅下跌。大家都认为要跌了，↓↓↓&lt;/p&gt;

&lt;p&gt;4.买卖平衡横盘走势。大家认为跌的有点多，→→→&lt;/p&gt;

&lt;p&gt;5.反弹。一部分人认为跌多了会涨回来点，↑&lt;/p&gt;

&lt;p&gt;6.反弹结束买卖平衡横盘。有人不相信会涨回去，→&lt;/p&gt;

&lt;p&gt;7.继续下跌。越来越多人不相信会涨回去，↓&lt;/p&gt;

&lt;p&gt;8.大幅下跌。越来越多人认为利空消息还会继续，↓↓&lt;/p&gt;

&lt;p&gt;9.巨幅下跌，跌破前期支撑。所有人都一致认为利空消息太大，↓↓↓&lt;/p&gt;

&lt;p&gt;10.继续下跌。利空消息让所有人恐惧，↓&lt;/p&gt;

&lt;p&gt;11.深跌后大量股票跌幅达到30-50%，开始横盘。人们开始反思是不是跌太多了。→&lt;/p&gt;

&lt;p&gt;12.反弹开启。越来越多人相信跌太多了。↑&lt;/p&gt;

&lt;p&gt;13.买卖平衡。有人相信跌太多，有人求早点跑。→&lt;/p&gt;

&lt;p&gt;14.又开始跌。抢跑的人又开始抛售。↓&lt;/p&gt;

&lt;p&gt;15.继续跌破前期低点。所有人都陷入绝望。↓↓&lt;/p&gt;

&lt;p&gt;16.横盘或持续阴跌。人们看不到希望，认为没有希望。→↓→↓&lt;/p&gt;

&lt;p&gt;17.大部分股票跌入安全区，但任然在跌。恐惧的氛围一直没有散开。→→↓→→↓&lt;/p&gt;

&lt;p&gt;18.开始有人相信买入安全区的股票是有价值的，但股市并没有买账，因为相信的人太少。→→→→↓→→→→&lt;/p&gt;

&lt;p&gt;19.又一些人加入相信安全区的行列，股市开始不再阴跌而是横盘或在小范围上下震荡。→→→→&lt;/p&gt;

&lt;p&gt;20.越来越多人认为现在时安全区，持续买入的力量越来越大。股市从小幅震荡变为了大幅度震荡。→→↑→→↑&lt;/p&gt;

&lt;p&gt;21.这种震荡并没有使得股市形成向上的趋势，任然在某个位置，上上下下的来回震荡。→↑→↓&lt;/p&gt;

&lt;p&gt;22.有一部分人看不到希望，开始退场。又开始持续阴跌。↓&lt;/p&gt;

&lt;p&gt;23.越来越多的人跟随看不到希望的人退出场外。又开始大幅下跌。↓↓&lt;/p&gt;

&lt;p&gt;24.可能又再一次跌破了前期的低点，或者接近前期的低点。所有人都绝望。↓↓&lt;/p&gt;

&lt;p&gt;25.利好消息终于来临。但没有人相信。股市继续横盘。→→→→&lt;/p&gt;

&lt;p&gt;26.小幅上涨，开始有人相信利好消息将带来股市上涨。→→↑→→↑&lt;/p&gt;

&lt;p&gt;27.大幅上涨，越来越多人相信并且买入。→↑→↑&lt;/p&gt;

&lt;p&gt;28.反复在利空和利好消息的情绪变化中思考，总体上是震荡向上的，局部是横盘震荡格局。→↑→↓&lt;/p&gt;

&lt;p&gt;29.底部形成。&lt;/p&gt;

&lt;p&gt;底部的形成是个艰难的，并且时间很长的过程，大概需要花起码半年的时间。在风雨缥缈中构建，在悄无声息中成长。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Back to blog</title>
   <link href="http://www.luzexi.com/2018/06/22/Back-to-Blog"/>
   <updated>2018-06-22T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2018/06/22/Back-to-Blog</id>
   <content type="html">&lt;p&gt;It’s a long time before back to blog. Before, I went to Weibo to study something about finance. It’s good for me in the future. So now I have much knowledge and experience in finance invest. I’m glad that I make the real world more close to me.&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;And also, through studying finance I completely know technology is very much importanter than i thought before. Technology makes me have good work or job and also good life. Good life is the most important thing, and good life is depends on money and work sometime must be chanllenge. Money is not the most important thing but work and knowledge is, if you have knowledge and keep works you will be talent which must be popular round you.&lt;/p&gt;

&lt;p&gt;I stay at Weibo for a long time and learn much. But why I’m back, first reason is its too much information in Weibo both good and bad that makes me mix. Second one is more and more uesless information on Weibo makes me too tired to skip them. So i said to myself that i must think in individual style, only thinking individual style makes me more and more powerful. If I just follow some guy or some information I will be completely failed in my life. The only way to beat otherones else is keep leaning and thinking in individual style.&lt;/p&gt;

&lt;p&gt;Now I’m back. It’s a completely new start for my technology and my life. I will record all my mind, idea, experience or life in the blog. I wont be shy if my words were error in your view, but I would like to see that you can comment in the end of the page.&lt;/p&gt;

&lt;p&gt;‘Keep moving, never stop” is my motto.&lt;/p&gt;

&lt;p&gt;Thanks for your reading.&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Swimming excercise</title>
   <link href="http://www.luzexi.com/2016/04/22/Swimclub"/>
   <updated>2016-04-22T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2016/04/22/Swimclub</id>
   <content type="html">&lt;p&gt;#Swimming excercise&lt;/p&gt;

&lt;p&gt;Hi guys, recently i swim a lot, it make feel good. These years i work so hard that make my body worse and worse, so i decide to do some excercise to keep health. i think swim is good maybe the one of the best sport for health.&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;Actually my first swimming in shanghai make me terrible. I didnot do any excersice for a long time before that, And i event didnot spread myself before jump in the water. I just went in the swimming pool and spend all of energy to swim one side to the other side very fast, therefor i feel lots tried and dizzy. And then you know what, im fucking vomit, damn it.&lt;/p&gt;

&lt;p&gt;Since that i didnt go to swim a long time, maybe 3 month i dont remember. But i still try to do some excersice to keep health. i try to go to gym and buy 5 years membership. i know its waste my money but i bought :( 2 month ago i decide to come back to swim again. This time i try swimming carefully and do spread myself before swim and swim slowly at the beginning. Its feels good, and i think its make me realize that swimming is really good for health and easy to have in my life. So i start to swim every week and recommend others to swim.&lt;/p&gt;

&lt;p&gt;Actually these years i touch many new stuff in my life. i tried understanding foreign culture and learning foreign language and travelling lots of foreign countries and do swimming sport. All these stuffs are out of work, but i think its good for my life. I learn much things which i never touch any or maybe dont like it any. I think its great, i like to challenge.&lt;/p&gt;

&lt;p&gt;Well, i really like swimming but u know sometimes people is just like to do it in a short time, but i think i will insist swimming in a long time, maybe 3 years maybe 10 years. i dont know, i hope i can insist much more times.&lt;/p&gt;

&lt;p&gt;Now i’m going to build a swim club to collect people who love to swim or would like to swim. Its will be much fun if a group people go together and do the same thing. People know that swim is good for their health, but still dont want to play. These’s many reason i heard what they dont want to go. A lot of people dont know how to swim and some of them afraid water, And others maybe too lazy or too busy to do.&lt;/p&gt;

&lt;p&gt;If you would like to swim pls join us. Whatever, i will never give up persuading people to swim.
Well i create a swim club in meetup , if you would like to join us ,pls open it and join.
http://www.meetup.com/Shanghai-Swimming-Meetup/&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Gitlab removal</title>
   <link href="http://www.luzexi.com/2016/04/19/Gitlab-removal"/>
   <updated>2016-04-19T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2016/04/19/Gitlab-removal</id>
   <content type="html">&lt;p&gt;#Gitlab removal&lt;/p&gt;

&lt;p&gt;Hi guys, long time no see. these day i have much work and much things to learn so i didnot write any thing in the blog, sorry about that. its really busy in my probject. u know people always have their own reason not to do something lol. one day i saw my server which i bought in aliyun is almost time over, so i think about whether i should change the region of the server to hongkong. finally i make a decision to move my blog, gitlab and some other things to hongkong server.&lt;/p&gt;

&lt;p&gt;at the begion of the preparation, i try to buy a hongkong server for a month to test the network and server’s perform. actually it is really good. so im start to move blog, gitlab and svn etc. to my new server.&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;#Prolbem&lt;/p&gt;

&lt;p&gt;1.the first problem i have is which folder or data i have to move.&lt;/p&gt;

&lt;p&gt;i move the whole git folder to new server include gitlab, gitlab-satellites, gitlab-shell, https-ca, repositories.&lt;/p&gt;

&lt;p&gt;2.the second problem i have is mysql data.&lt;/p&gt;

&lt;p&gt;i have lots of data in gitlab and the gitlab save the data in mysql, so i have to move them as the same.&lt;/p&gt;

&lt;p&gt;actually its easy to move mysql. i just install mysql in new server and dump the data of mysql then import them into new server.&lt;/p&gt;

&lt;p&gt;ok, all of the data is already be move to new server.&lt;/p&gt;

&lt;p&gt;3.the hardest things i have is install ruby, lib of mysql and some of the dependent stuff.&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;its really hard for me to install lib of mysql for ruby because i dont know which source of apt-get i should use. i installed
5.5.47 mysql version but it tells me the lib of mysql is 5.5.37 and it is the newest version in source of apt-get which the installed is failed. i change some of them but still cant be install. i even download the mysql of source and make them and try to pick the lib to install but failed. finally i choose the nature source of aliyun to install the lib of mysql, its success. i think its my bad, because i dont believe the nature source of aliyun so i change it.&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;4.After i finished all, i open gitlab site and it works. Then pull one of the repositories and change some file and push them. It said something wrong in the gitlab so i cant push the commit. i check search the problem and find i have to reline the update file in every repositories’s hook. So i do it, but it still socks. A little git time later i find that redis is not able to visit, so i add the user which git use into redis group. So the redis’s problem is solved but still cant push. I feel something wrong in satellites but i actually dont how to fix. So i remove all of the gitlab except repositories and install gitlab again, then import the respositories by using:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;cd /home/git/gitlab/
sudo -u git -H bundle exec rake gitlab:import:repos RAILS_ENV=production
sudo -u git -H bundle exec rake gitlab:satellites:create RAILS_ENV=production
sudo chmod -R ug+rwX,o-rwx /home/git/repositories/
sudo chmod -R ug-s /home/git/repositories/
find /home/git/repositories/ -type d -print0 | sudo xargs -0 chmod g+s
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;5.now i finished setup the lib of all i need. and then i have to set the config to let the gitlab run.&lt;/p&gt;

&lt;p&gt;i set the gitlab config, gitlab-shell config and the nginx vhost, its much easy because i set them before in the old server.&lt;/p&gt;

&lt;p&gt;ok i should check all of the setting by using: sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production&lt;/p&gt;

&lt;h1 id=&quot;all-of-thing-is-ok-now-so-lets-run-the-gitlab&quot;&gt;all of thing is ok now, so lets run the gitlab.&lt;/h1&gt;

</content>
 </entry>
 
 <entry>
   <title>译安卓应用架构体系</title>
   <link href="http://www.luzexi.com/2016/01/15/%E8%AF%91%E5%AE%89%E5%8D%93%E5%BA%94%E7%94%A8%E6%9E%B6%E6%9E%84%E4%BD%93%E7%B3%BB"/>
   <updated>2016-01-15T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2016/01/15/译安卓应用架构体系</id>
   <content type="html">&lt;p&gt;#安卓应用架构体系&lt;/p&gt;

&lt;p&gt;我们经历了从标准的Activity加AsyncTask架构到当前流行的用RxJava驱动的MVP模式为基础的架构体系。&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;alignnone size-full wp-image-355&quot; src=&quot;/assets/译安卓应用架构体系/1.png&quot; alt=&quot;48391BD3-BEAC-4E45-B6C4-17AA7B5B0428&quot; /&gt;
不同部分的软件模块应该是独立的，但放在一起工作时却想是个烂醉的机器–Chester Alvarez拍的照片&lt;/p&gt;

&lt;p&gt;安卓开发生态步伐很快，每个星期都有新的工具被创建，新的库被更新，新的博客文章发出来，新的话题被讨论。如果你去度假了一个月，你回来的时候就会有新版本的支持库或者新的服务等你去处理。
我在ribot团队做了3年的安卓应用。在此期间，我们使用的安卓应用技术和架构已经连续演变了很多次。这篇文章将带你经历讲述我们是如何在架构变化下学习，出错并且找到原因的。&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;p&gt;###旧时代&lt;/p&gt;

&lt;p&gt;回到2012年我们底层习惯使用基础的结构。我们不使用任何的网络库但AsyncTask任然是我们的朋友。下面的图差不多展示这个架构是个怎样子的。&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;alignnone size-full wp-image-355&quot; src=&quot;/assets/译安卓应用架构体系/2.png&quot; alt=&quot;48391BD3-BEAC-4E45-B6C4-17AA7B5B0428&quot; /&gt;
初始的架构&lt;/p&gt;

&lt;p&gt;程序使用两层结构：数据层负责使用REST API检索和保存数据并持久化数据存储。视图层负责调用句柄和在UI上显示数据。
APIProvider 提供方法使得Activity和Fragment容易用REST API相互通信。这些方法使用URLConnection和AsyncTask执行网络调用在分开的线程里并经由回调返回结果给Activity。
同样的，CacheProvider包含了些方法用SharedPreference或者SQLite数据库，检索和存储数据。它也通过回调把结果返回给Activity。&lt;/p&gt;

&lt;p&gt;###问题集&lt;/p&gt;

&lt;p&gt;这个途径最主要的问题是视图层负责的东西太多。想象一个的常用的方案，一个应用必须去加载一串博客文章，用SQLite数据库缓存他们并最终在ListView上展示他们。Activity会做以下的事情。&lt;/p&gt;

&lt;p&gt;1.在APIProvider里调用loadPosts方法(回调)。&lt;/p&gt;

&lt;p&gt;2.等待APIProvider回调成功然后在CacheProvider里调用savePosts(回调)。&lt;/p&gt;

&lt;p&gt;3.等待CacheProvider回调成功然后在ListView上展示文章。&lt;/p&gt;

&lt;p&gt;4.分别处理从APIProvider和CacheProvider这两个可能的错误回调。&lt;/p&gt;

&lt;p&gt;这是个非常简单的例子。在实际的案例方案里REST API可能不会返回视图需要的数据。因为Activity将需要以某种方法在展示前转换或过滤数据。另一个常用的案例是当loadPosts()方法带上一个需要从其他地方获取的参数时，比如SDK服务提供的一个邮件地址。SDK貌似会以异步回调方式返回邮件地址，这意味着我们现在有3层嵌套回调。如果我们继续增加复杂度，众所周知这个途径的结果是将会变成回调的地狱。&lt;/p&gt;

&lt;p&gt;###小结&lt;/p&gt;

&lt;p&gt;1.Activity和Fragment会变得很大并且难以维护。&lt;/p&gt;

&lt;p&gt;2.太多的嵌套回调意味着程序是丑陋的并且难以理解，所以在更换和增加新的功能时会痛苦。&lt;/p&gt;

&lt;p&gt;3.单元测试变得有挑战性或者不太可能了，因为太多逻辑放在Activity和Fragment里，这样会导致单元测试会很费劲。&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;alignnone size-full wp-image-355&quot; src=&quot;/assets/译安卓应用架构体系/3.png&quot; alt=&quot;48391BD3-BEAC-4E45-B6C4-17AA7B5B0428&quot; /&gt;
一个由RxJave驱动的新架构&lt;/p&gt;

&lt;p&gt;我们用前面的方法大概2年时间。在此期间，我们做了一些改进，稍微减轻前面描述的问题。
一直到2014年我们开始看RxJava。在尝试一些试验工程后，我们认识到它能最终解决嵌套回调的问题。如果你不熟悉反应式系统编你可以看这个介绍(https://gist.github.com/staltz/868e7e9bc2a7b8c1f754) 。简而言之，RxJava能让你通过异步流管理数据并且给你很多操作，你可以应用到数据流里去转换，过滤或者合并数据。
考虑到前些年痛苦的经验，我们开始考虑在新的app里采用怎样的架构。然后我们开始这样的做了。&lt;/p&gt;

&lt;p&gt;###RxJava驱动的架构体系&lt;/p&gt;

&lt;p&gt;与第一个方法相似，这个架构也能被拆分成数据和视图层。数据层包括DataManager和一些工具。视图层由Android框架组件的形式例如Fragment，Activity，ViewGroup等。
工具类(图形的第三列)是非常特殊的责任，用简洁方式实现了他们。例如，大多数项目有访问REST API的工具，读取数据库数据或者与第三方SDK通信。不同的应用有不同数量的工具但大多数工具为如下：&lt;/p&gt;

&lt;p&gt;1.PreferencesHelper:用SharePreferences读取和保存数据&lt;/p&gt;

&lt;p&gt;2.DatabaseHelper：处理存取SQLite数据库&lt;/p&gt;

&lt;p&gt;3.Retrofit(https://github.com/square/retrofit) 服务：执行调用REST API。我们已经开始使用Retrofit代替Volley因为它提供对RxJava的支持。它也非常好用。&lt;/p&gt;

&lt;p&gt;工具类里面的大多数公有方法会返回RxJava的Observable。
DataManager是架构的大脑。它广泛使用RxJava操作去合并过滤转换数据并用工具类检索。DataManager的目的是去大量减少Activity和Fragment处理准备展示数据的工作，不再常需要任何转换。&lt;/p&gt;

&lt;p&gt;下面条目展示的是DataManager方法是怎样的。样例方法是如下工作的：
1.调用Retrofit服务从REST API加载一串博客文章。&lt;/p&gt;

&lt;p&gt;2.使用DatabaseHelper保存文章到本地数据库以达到缓存的目的。&lt;/p&gt;

&lt;p&gt;3.过滤今天写的博客文章因为这是视图层想要展示的东西。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;public Observable&amp;lt;Post&amp;gt; loadTodayPosts() {
        return mRetrofitService.loadPosts()
                .concatMap(new Func1&amp;lt;List&amp;lt;Post&amp;gt;, Observable&amp;lt;Post&amp;gt;&amp;gt;() {
                    @Override
                    public Observable&amp;lt;Post&amp;gt; call(List&amp;lt;Post&amp;gt; apiPosts) {
                        return mDatabaseHelper.savePosts(apiPosts);
                    }
                })
                .filter(new Func1&amp;lt;Post, Boolean&amp;gt;() {
                    @Override
                    public Boolean call(Post post) {
                        return isToday(post.date);
                    }
                });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;视图中的组件像Activity或Fragment会简单调用这个方法并订阅返回的Observable。一旦订阅结束，用Observable得到的不同的文章能直接加入到适配器去展示在RecyclerView或者类似地方。&lt;/p&gt;

&lt;p&gt;文章最后一个元素是事件总线。事件总线允许我们广播那些发生在数据层上的事件，所以在视图层里的多种组件可以订阅这些事件。例如，在DataManager里的asignOut()方法可以推出一个事件当Observable完成时，然后在这个事件里被订阅的多种Activity就可以更换他们的UI来展示一个不在线的信号。&lt;/p&gt;

&lt;p&gt;###为什么这个方法更好&lt;/p&gt;

&lt;p&gt;1.RxJava 的Observable和操作者去除了嵌套回调的需求。&lt;/p&gt;

&lt;p&gt;2.DataManager接管了以前视图部分的工作。因此它使得Activity和Fragment更加轻便。&lt;/p&gt;

&lt;p&gt;3.把Activity和Fragment的代码迁移到DataManager和工具类意味着单元测试变得更加容易。&lt;/p&gt;

&lt;p&gt;4.整理拆分责任并让DataManager作为与数据层通信的唯一途径，这样使得这个架构测试友好。工具类和DataManager可以被容易模仿。&lt;/p&gt;

&lt;p&gt;###我们仍然存在什么问题？&lt;/p&gt;

&lt;p&gt;1.对于大型和复杂的项目来说DataManager会变得太臃肿并且难以维护。&lt;/p&gt;

&lt;p&gt;2.虽然视图层组件像Activity和Fragment变得更加轻便了，但他们仍然需要去处理相当部分围绕管理RxJava的订阅逻辑和错误分析等。&lt;/p&gt;

&lt;p&gt;###整合MVP(模型-视图-发言者)&lt;/p&gt;

&lt;p&gt;在过去几年，一些架构模式像MVP或者MVVM在安卓社区曾获得流行。在一个样例工程和文章上探索这些模式后，我们发现MVP能带来很好的价值来改进我们现存的方法。因为我们现在架构被分成2个层级(视图和数据)，加上MVP后显得自然。我们仅仅需要加入一个新的发言者层级并把视图层的代码迁移到发言者层上就可以。&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;alignnone size-full wp-image-355&quot; src=&quot;/assets/译安卓应用架构体系/4.png&quot; alt=&quot;48391BD3-BEAC-4E45-B6C4-17AA7B5B0428&quot; /&gt;
MVP为基础的架构&lt;/p&gt;

&lt;p&gt;数据层保留下来但现在叫模型层了为了与模式的名字更加匹配。&lt;/p&gt;

&lt;p&gt;发言者是负责从模型层加载数据然后调用视图层的指定方法当结果准备好的时候。他们用DataManger订阅了Observable的返回。因为他们要去处理一些事情像调度程序和订阅一样。另外，他们可能分析错误代码或者应用特殊的操作在数据流上如果需要的话。例如，如果我们需要过滤一些数据并且这个过滤器不想被在其他地方重复使用，这样在发言者里实现比在DataManager里实现更有意义。&lt;/p&gt;

&lt;p&gt;下面你可以看到一个公有的方法在发言者里是怎样的。这个代码用thedataManager.loadTodayPosts() 方法订阅了Observable返回，它被定义在前面提到的部分。&lt;/p&gt;

&lt;p&gt;MvpView是视图组件而发言者是协助的。通常MVP视图是Activity，Fragment或者ViewGroup的实例。
如以前的架构，视图层包含标准框架组件例如ViewGroup，Fragment或Activity。主要的区别是这些组件不直接订阅Observable。他们用实现MvpView接口替代并提供一串简洁的方法例如showError()或者showProgressIndicator()。这视图的组件同样负责处理用户交互例如点击事件和行为通过在发言者里调用指定的方法。例如，我们有一个按钮加载一串文章，我们的Activity会从onClick监听者中调用presenter.loadTodayPosts()。&lt;/p&gt;

&lt;p&gt;如果你想了解一整个MVP为基础架构的工作样例，你可以渐出我们的Android Boilerplatex(https://github.com/ribot/android-boilerplate)项目在Github上，你也可以在ribot的指南(https://github.com/ribot/android-guidelines/blob/master/architecture_guidelines/android_architecture.md)上了解更多。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;###为什么这个方法更好？&lt;/p&gt;

&lt;p&gt;1.Activity和Fragment变得很轻便。他们的责任只有装配和更新UI并且处理用户事件。因此他们变得很容易去维护。&lt;/p&gt;

&lt;p&gt;2.现在我们能容易的通过模仿视图层为发言者写单元测试。之前，这个程序是视图层的一部分所以我们不能对它单元测试。整个架构变得非常测试友好。&lt;/p&gt;

&lt;p&gt;3.如果DataManager开始变得臃肿，我们能通过迁移代码到发言者上来减缓这个问题。&lt;/p&gt;

&lt;p&gt;###我们任然存在的问题是什么？&lt;/p&gt;

&lt;p&gt;当有一个单例DataManager仍然有一个问题当底层变得很大很复杂的时候。我们还没有达到这个真正问题点，但我们意识到他可能会发生。&lt;/p&gt;

&lt;p&gt;重点提下这个不是完美的架构。实际上，认为世界上有一个唯一并且完美的架构能永远解决你所有的问题是很幼稚天真的。安卓社区会持续快速发展，我们需要保持探索，阅读和尝试，这样我们可以找到一个更好的方式去继续构建优秀的安卓应用。
希望你喜欢这篇文章并且能对你有用。如果这真是这样的话，不要忘了点击推荐按钮。也欢迎能听到你自己对这些方法的想法。&lt;/p&gt;

&lt;p&gt;翻译自:&lt;a href=&quot;https://medium.com/ribot-labs/android-application-architecture-8b6e34acda65#.1qvkq2xaa&quot;&gt;https://medium.com/ribot-labs/android-application-architecture-8b6e34acda65#.1qvkq2xaa&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;原文作者: Ivan&lt;/p&gt;

&lt;p&gt;作者授权邮件：
&lt;img class=&quot;alignnone size-full wp-image-355&quot; src=&quot;/assets/译安卓应用架构体系/5.png&quot; alt=&quot;48391BD3-BEAC-4E45-B6C4-17AA7B5B0428&quot; /&gt;&lt;/p&gt;

&lt;p&gt;申明此篇文章为本人参加GAD翻译比赛文章&lt;/p&gt;

&lt;p&gt;转载请注明出处: http://www.luzexi.com&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>译Unity3d 综合性能窍门</title>
   <link href="http://www.luzexi.com/2016/01/08/%E8%AF%91Unity3d%E7%BB%BC%E5%90%88%E6%80%A7%E8%83%BD%E7%AA%8D%E9%97%A8"/>
   <updated>2016-01-08T00:00:00+08:00</updated>
   <id>http://www.luzexi.com/2016/01/08/译Unity3d综合性能窍门</id>
   <content type="html">&lt;p&gt;很久没写博文，抱歉让大家失望了，最近忙于学习新的东西，也有懒惰的成分。现在开始我打算用英文来写我的博文，不为了什么，只是自己的英文太差，想练习练习。最近开始翻译一些自己觉得好的文章，其实这篇文章，很早就有人翻译了，因为比较好，我又为自己翻译了一遍。&lt;/p&gt;

&lt;p&gt;Hi everyone, long time no see. these days i’m busy to learn something else not about technology but life, maybe it’s just a excuse that i didnt write anymore :) from now on i’d like to write in english, just practice my poor english :( recently i maybe translate some article which is usefully for you guys maybe translated before by other people.&lt;/p&gt;

&lt;p&gt;###下面的内容并不一定很详细，但能够引导unity3d开发者如何制作性能流畅的游戏应用
	内容：
	1.官方提示文档
	2.性能优化概述
	3.模型网格
	4.灯光
	5.贴图
	6.音频
	7.物理碰撞
	8.Shader
	9.脚本
===&lt;/p&gt;

&lt;p&gt;###官方提示文档&lt;/p&gt;

&lt;p&gt;图形性能优(http://docs.unity3d.com/Documentation/Manual/OptimizingGraphicsPerformance.html)
如何减少包大小(http://docs.unity3d.com/Documentation/Manual/ReducingFilesize.html)
角色动画(技巧比较零散) ( http://unity3d.com/Documentation/Manual/Character-Animation.html)&lt;/p&gt;

&lt;p&gt;###优化技巧概述&lt;/p&gt;

&lt;p&gt;分析第一步，不要试图花时间去优化一些模糊不清的程序或者降低图片的大小除非你确实知道他们是瓶颈。首要的是去一直分析你的游戏找到瓶颈在哪里。Apple的Shark是一个分析OpenGL基础应用不错的工具。
分析第二步，不要忘了在优化后对游戏再分析一次以便查看他们是否有效，同时你也有可能会发现另一些瓶颈。&lt;/p&gt;

&lt;p&gt;开发工作第一 – 性能优化第二。尽可能花时间使你的游戏更加平滑顺畅。能够使得更改和更新游戏变得更快也将让减轻以后的性能转变。 在观察屏中测试场景，他将告诉你性能是被在场景中的物体拖慢速度还是被绑定在物体上的脚本拖慢速度。如果是观察屏中迟钝缓慢，你可能需要优化一下模型或者贴图，如果不是，瓶颈可能在程序中或者物理碰撞上。关闭个别的游戏物体，在编辑器里，试图关掉一些个别的物体，这样通常能排查到拖慢游戏的物体。&lt;/p&gt;

&lt;p&gt;###模型网格&lt;/p&gt;

&lt;p&gt;尽可能的将邻近的模型合并为单个模型单个材质球。例如，如果你的场景里的桌子上堆叠有很多个物体，合并这些物体将会很有意义(有可能会需要将一些贴图合并一张大的贴图图集)。减少Unity渲染的物体的数量能显著促进性能。
一个材质球一个模型，每个材质球都会被视为分开的模型渲染。
使用极致低模的模型(500个多边形以下)会使得性能增加。大多数的显卡都有转换和照明功能，这意味他们每秒都处理一些奇怪的多边形。加之通常会提交一个网格让显卡渲染，所以太过于减少模型的多边形可能使你的游戏模型看起来像块状。
开始吧，用大约1500-2000的三角形做角色，这个数字可以变化大些，但是作为一个首发的美术人员应该在一个细节层级上对质量和性能有一个比较好的妥协。注意，如果你有模型使用四边形，(四边形)Unity将会把每个四边形都转换成2个三角形再导入。&lt;/p&gt;

&lt;p&gt;###光照&lt;/p&gt;

&lt;p&gt;每个像素光渲染都会生效另外的渲染管道。像素光会使你的游戏看起来更好但不要太过于热衷于他们。然而，使用Quality Manager去调整像素光的渲染在每个质量等级上是一个很好的方式，这在你发布的游戏里提供了性能与质量的平衡性。
 聚光灯比点光源和方向光更加费性能。光照一个场景最好的方式是先确定你想要的效果，然后去看所有的灯光中哪个是重要的哪个可以削减掉使得场景效果与你想要的相似。
点光源和聚光灯值影响在他们范围内的模型网格。如果模型在点光源和聚光灯范围以外，光的影响将被削弱，模型将不会被灯光影响从而节省性能消耗。这个方式可以在理论上解释拥有很多小的点光源却任然拥有好的性能表现，因为他们只影响一小部分的物体。记住，一个模型最多只能被8个光源所影响。&lt;/p&gt;

&lt;p&gt;###贴图&lt;/p&gt;

&lt;p&gt;在看起来可以接受的情况下尽量缩小贴图的大小。如果你的显卡没有拥有足够的内存来存放这些贴图时，他们将被放置在系统内存里，当他们需要被渲染时再被上传。这在新的电脑上没什么问题因为他们有很多可以使用的空间。但如果你执着于完全能在低端显存设备上运行你的游戏的话，不需要在图片工具上改变贴图的大小，你可以使用Unity导入图片并对其设置大小。
不要使用低品质的图片文件，试图使用jpeg的低品质文件或者低色彩png或者gif文件也不会降低游戏中的大小。Unity会在打包发布时自动压缩所有图片，所以请保持原始的高品质贴图文件。这是由于多种压缩和解压缩对品质最小化变得很轻便。&lt;/p&gt;

&lt;p&gt;###音频&lt;/p&gt;

&lt;p&gt;使用.ogg对音频压缩，其他的音频格式在发布打包时将被作为非压缩的PCM音频格式存储在包内。
对小的音效使用非压缩音频，Unity(从1.6开始)导入时会解压缩所有ogg文件。它让短音效播放时使用非压缩的wav或者aiff文件，这样可以不消耗CPU在解压音频文件上。例如那些急速开枪、脚步等类似的声音。&lt;/p&gt;

&lt;p&gt;###物理&lt;/p&gt;

&lt;p&gt;每个rigidbody都消耗计算，所以越少越理想。已有的Rigidbody物体同样最好关闭起来当他们的旋转速度和移动速度到减少到一定程度的时候。当这个发生的时候，大量计算被显著去除并保持比较低的量，直到他们受到手动受力或者碰撞其他的碰撞体如果存在的话。
复杂的碰撞比普通的消耗更多的计算量，大量堆叠在一起的球形rigidbody碰撞体在地形上会比相隔较远的消耗更多的复杂计算过程。&lt;/p&gt;

&lt;p&gt;###着色器&lt;/p&gt;

&lt;p&gt;很多复杂的着色器比简单的可能消耗性能。VertexLit Diffuse 着色器应该是带贴图和光照最快的着色器了。然而，如果没有像素灯光在场景里或者所有像素光都被Quality Manager关掉了，那么大多数着色器将回退到更加简单的顶点渲染版本。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;###脚本&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;1.你是否使用了好的算法？选择一个好的算法对工作收益来说比其他的调整会有更好的优化效果如果可能的话。
注意最好的算法不总是那个最低平均复杂度的算法。对于小数据量来说，通常使用一个简单的低速算法比智能却带高初始化的算法要来的好。(例如可以用hash表或二叉树作为以名字存取的大型数据，但你可以使用一个简单链表和线性算法如果你存取小型数据的话。虽然dotnet的哈希表类在这种情况下已经选择了最佳的方式根据你的数据量大小。)

2.FixedUpdate方法里尽可能保持少的逻辑。这些逻辑可以被调用大约50-100次每秒在每个有效脚本的每个物体里，所以他们是优化的重要目标。如果某些逻辑确实需要在渲染更新后执行再把这些代码放进update方法里去。
尽可能把一些物体上的脚本关掉当不再需要他们的时候。如果你的游戏有一个大型的场景，里面的怪物在几公里远的地方，你可以关掉他的AI脚本直到摄像头靠近他们时再开启。这里有个好方法去开关它们，就是使用gameObject.SetActiveRecursively(false)并且设置球形和方形碰撞体为触发器(trigger)。

3.小心空的Update方法。当使用资源菜单创建新的脚本时他们包含了空的Update方法。去掉它如果你不需要它的话，因为它会带来一些(少量的)性能消耗。这个性能消耗点应用所有在MonoBehaviour脚本里的重载方法，以Update和FixedUpdate为主要的目标。

4.关于一个GameObject中最合逻辑的组件，某人可以理论上这样写: someGameObject.transform.gameObject.rigidbody.transform.gameObject.rigidbody.transform，但这个有大部分都是不需要的。如果你需要去处理一个物体的Transform，可以映射它到你的脚本里的开头部分。

5.协程是你的好朋友。协程只有很少的开销并更适合被选择，而一个update方法在他们不需要的时候也总是被调用。例如，你有一个脚本去实现一个命令触发的渐进渐出的灯光效果，你可以用协程去实现渐进渐出替换Update。这样做在大部分时候当灯光不进行渐进渐出时，脚本是最低的性能消耗。如果渐进渐出在Update方法里实现，你将低效的轮询去查看是否渐进渐出结束了。

6.在没有必要的情况下不要用方法去搜索物体，这包括方法GameObject.FindByTag() 和 GameObject.GetComponent()，与所有便利的属性(transform,light,等)一样，这些方法可以被优化的运行起来地尽可能的快，但他们仍然必须去通过搜索关联的物体去找到你想要的那个。最重要的事是避免调用搜索方法在Update和FixedUpdate方法里，于之替换的是调用某方法一次并存储它在你的类成员变量里，然后在你下次需要它时从这个成员变量里取得。

7.不要使用SendMessage(或类似的方法)当你不是必要的时候，SendMessage比直接调用方法慢至少100倍，并且这个倍数还会增加当很多脚本和方法在物体上生效时。如果你能得到你的脚本你最好尽早去找，同样的直接调用这个方法。

8.JavaScripts(和Boos)的duck类型会消耗已少量的计算量。在性能零界区域和在使用javascript时，请直接尝试声明你使用的变量类型。(尽管这个通常计算机会自动识别那个有效的你所指定的类型，所以你的程序可以多样化)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;翻译自：http://wiki.unity3d.com/index.php?title=General_Performance_Tips&lt;/p&gt;

&lt;p&gt;申明此篇文章为本人参加GAD翻译比赛文章&lt;/p&gt;

&lt;p&gt;转载请注明出处: http://www.luzexi.com&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Unity3D之slua集成第三方库</title>
   <link href="http://www.luzexi.com/2015/09/26/Unity3D%E4%B9%8Bslua%E9%9B%86%E6%88%90%E7%AC%AC%E4%B8%89%E6%96%B9%E5%BA%93"/>
   <updated>2015-09-26T14:46:26+08:00</updated>
   <id>http://www.luzexi.com/2015/09/26/Unity3D之slua集成第三方库</id>
   <content type="html">&lt;p&gt;Unity3D中使用lua最近越来越火，我比较中意slua的思路与代码质量。因为先前的项目对slua做了几个第三方库的封装，所有在空出来的时间就对slua做了fork加入了一些大家都比较常用的第三方库。&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/luzexi/slua-3rd-lib&quot;&gt;源码&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这面是源码的地址，如果喜欢可以star或watch，我会一直更新以方便大家。&lt;/p&gt;

&lt;p&gt;至今集成的第三方库罗列一下:&lt;/p&gt;

&lt;p&gt;1.pbc (&lt;a href=&quot;https://github.com/cloudwu/pbc&quot;&gt;https://github.com/cloudwu/pbc&lt;/a&gt;) 是云风用c写的google protocol buffers。&lt;/p&gt;

&lt;p&gt;2.lpeg (&lt;a href=&quot;http://www.inf.puc-rio.br/~roberto/lpeg/lpeg.html&quot;&gt;http://www.inf.puc-rio.br/~roberto/lpeg/lpeg.html&lt;/a&gt;) 是正则表达式的解析与匹配库.&lt;/p&gt;

&lt;p&gt;3.lua-cjson (&lt;a href=&quot;http://www.kyne.com.au/~mark/software/lua-cjson.php&quot;&gt;http://www.kyne.com.au/~mark/software/lua-cjson.php&lt;/a&gt;) 是一个支持json的库，他的效率非常惊人。&lt;/p&gt;

&lt;p&gt;4.lua-socket (&lt;a href=&quot;http://w3.impa.br/~diego/software/luasocket/home.html&quot;&gt;http://w3.impa.br/~diego/software/luasocket/home.html&lt;/a&gt;) 是一个用c写的socket的库，里面封装了TCP和UDP。提供了网络连接与传输的API，你可以用它在lua里实现网络层。&lt;/p&gt;

&lt;p&gt;5.sproto (&lt;a href=&quot;https://github.com/cloudwu/sproto&quot;&gt;https://github.com/cloudwu/sproto&lt;/a&gt;) 这也是云风写的一个关于网络协议，他类似与google protocol buffer，不同的是他在google protocol buffer基础上对协议的格式和内容做了修改，使得解析与构造的效率非常高。&lt;/p&gt;

&lt;p&gt;6.sqlite (&lt;a href=&quot;https://github.com/LuaDist/lsqlite3&quot;&gt;https://github.com/LuaDist/lsqlite3&lt;/a&gt;) 是一个以文件形式存在的轻量级数据库，他被很多软件用来做本地数据存储。他的api轻便好用，广受程序员欢迎。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;源码中已将所有第三方库构建进slua里，并完成了android(x86,armv7),ios,mac,windows(x86,x64)这几个平台的测试。&lt;/p&gt;

&lt;p&gt;如果你需要加入自己的第三方库或者说你希望去除一些你用不到的第三方库，你可以在源码的build里修改我写的自动build的批处理程序。&lt;/p&gt;

&lt;p&gt;1.make_ios.sh 构建ios平台的批处理程序。需要在mac下运行。&lt;/p&gt;

&lt;p&gt;2.make_osx.sh 构建osx平台的批处理程序。需要在mac下运行。&lt;/p&gt;

&lt;p&gt;3.make_android.sh 构建android平台的批处理程序。需要在mac或linux下运行。&lt;/p&gt;

&lt;p&gt;4.make-windows-32.cmd 构建windows x86 dll。需要在windows下运行。&lt;/p&gt;

&lt;p&gt;5.make-windows-64.cmd 构建windows x64 dll。需要在windows下运行。&lt;/p&gt;

&lt;p&gt;这里说明一些构建时需要注意的事情。ios构建时需要注意脚本里的xcode地址。如果你不是xcode7.0。可能需要设置一下脚本里的路径。android的构建脚本也是一样，需要配置你机子上正确的ndk路径。windows下编译dll需要用的MingGw，我在源码中用git的submodule的方式安了一个，你需要运行一下submodule的更新命令git submodule update –init。MingGw很大300mb，你要做好心理准备，或者你直接去这个地址下载一个&lt;a href=&quot;https://github.com/luzexi/MinGW&quot;&gt;https://github.com/luzexi/MinGW&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;转载注明出处 http://www.luzexi.com&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Unity3D-重新编译Mono加密DLL</title>
   <link href="http://www.luzexi.com/2015/04/11/Unity3D-%E9%87%8D%E6%96%B0%E7%BC%96%E8%AF%91Mono%E5%8A%A0%E5%AF%86DLL"/>
   <updated>2015-04-11T18:58:36+08:00</updated>
   <id>http://www.luzexi.com/2015/04/11/Unity3D-重新编译Mono加密DLL</id>
   <content type="html">&lt;p&gt;Unity3D-重新编译Mono加密DLL。安卓应用总是让人头疼，游戏遭到破解与反编译是研发的人最不愿意看到的。自己的辛苦劳动成果被人随意窃取与利用，对这些咬牙切齿的痛恨。所以我们需要加强自身的反破解技术力量。不过这世上没有破解不了的东西，道高一尺魔高一丈，我们做的只是让破解更加困难而已。让那些破解的人付出点代价才能得到他们想要的，如果他们觉得代价太高，看不清前面的道路，他们就有可能放弃，然后我们的目的达到了。&lt;/p&gt;

&lt;p&gt;游戏本身加密方式有很多，对apk加壳，防止apk二次打包等。对这些android的加密与破解技术看过比较好的文章参考：&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://blog.csdn.net/column/details/security-android.html&quot;&gt;《Android安全及病毒分析》&lt;/a&gt; ，其中&lt;a href=&quot;http://blog.csdn.net/androidsecurity/article/details/8809542&quot;&gt;《Android APK加壳技术方案【2】》&lt;/a&gt; 最为经典。而本篇文章我们主要来说说针对Unity3D的加密。&lt;/p&gt;

&lt;p&gt;闲扯就到这里，我们开始说正事：&lt;/p&gt;

&lt;p&gt;Unity3D所有客户端的代码都会以dll文件形式存下来，当游戏应用被开启时c#vm(也就是mono的虚拟机)会去加载所有dll，从而开始运行真正的程序画面了。而破解的很大一部分都是通过解压apk后拿到主逻辑dll，对dll进行反编译，然后修改后重新编译，再放入apk重新签名打包。所以我们需要针对dll进行加密，以防止他们反编译dll。&lt;/p&gt;

&lt;p&gt;加密一个dll文件非常容易，无论你用什么算法都行，但是在哪解密呢？答案是libmono.so。libmono.so是mono的核心程序，它承载了加载解析dll和虚拟机运行的功能。所以说libmono.so是关键，我们需要修改mono内核程序并重新编译它。&lt;/p&gt;

&lt;p&gt;下面将开始mono的编译过程，别看步骤写得简单明了，其实我花了起码一个多星期的思考，尝试，失败，再思考，再尝试，再失败…..总结其中原因一方面也是自己的愚钝的资质，另一方面是unity mono和mono并不一样，unity mono缺少编译文档并且还混合着原mono的编译文档，导致误判了很多：&lt;/p&gt;

&lt;p&gt;1.首先不要认为unity mono 与 原生态mono一样。可以编译mono就可以同样步骤编译unity mono。我在这里尝试了很久，使用configure进行编译，尝试使用不同的编译参数，进行编译，最后发现unity mono使用的是ndk-9下的linux-4.8编译器，所有参数都是根据这个编译器所设定的。&lt;/p&gt;

&lt;p&gt;2.unity mono 地址：&lt;a href=&quot;https://github.com/Unity-Technologies/mono&quot;&gt;https://github.com/Unity-Technologies/mono&lt;/a&gt; 你需要从这里下载unity mono。&lt;/p&gt;

&lt;p&gt;3.mono需要autoconf automake libtool pkg-config这些工具。你最好还是去下载安装了。你可以用brew安装。brew install autoconf automake libtool pkg-config。&lt;/p&gt;

&lt;p&gt;4.我一开始使用mac x86_64进行编译，折腾了很久然后建了个linux-x86_64虚拟机来编译，然后又折腾了很久，又建了个linux-i386来重新编译mono，因为我一直认为交叉编译需要加些不同的编译参数和变量。在linux-i386首次编译成功后又开始转化到mac上，进行交叉编译也一样成功，最后发现其实是我没找对路子。这路子就是unity 的mono-build-tool：&lt;a href=&quot;https://github.com/Unity-Technologies/monobuildtools&quot;&gt;https://github.com/Unity-Technologies/monobuildtools&lt;/a&gt; 它已经在unity mono的项目里了，在mono的external/buildscripts下。&lt;/p&gt;

&lt;p&gt;5.buildscripts下的build_runtime_android.sh是编译安卓平台的关键。它是unity制作的一个自动编译 mono 流程的脚本。你需要将这个脚本copy到mono根目录下再执行。&lt;/p&gt;

&lt;p&gt;6.脚本里写些内容，如果你懒得看，我帮你稍微解释下。它会去检查你当前的ANDROID_NDK_ROOT环境变量是否是指向ndk-9，所以你需要去下ndk-9版本，放到机子上，然后编辑环境变量ANDROID_NDK_ROOT指向它，如果你没有它会通过perl模块lwp-download去下载ndk-9，但是你必须要要有这个perl模块才行，我劝你还是老老实实自己去下吧。ndk版本下载地址参考这里：&lt;a href=&quot;/前端技术/2015/04/06/Android-SDK-NDK-Studio-下载列表和构建说明.html&quot;&gt;《android-sdk-ndk-studio-下载列表和构建说明》&lt;/a&gt;。如果是linux下编译环境变量设定参考这里：&lt;a href=&quot;/后端技术/2015/04/07/linux环境变量简介.html&quot;&gt;《linux环境变量简介》&lt;/a&gt;。然后呢，它会用git去clone一个编译时用到的库，这个也是unity自己改编过的一个库，地址为：&lt;a href=&quot;https://github.com/Unity-Technologies/krait-signal-handler&quot;&gt;https://github.com/Unity-Technologies/krait-signal-handler&lt;/a&gt; ，这个库有个坑说下：perl脚本build.pl头部有个命令是#!/usr/bin/env perl -w，这个在部分机子上并不兼容，如果你有错误停在这里这个文件上，你可以将env去除再尝试手动perl build.pl 运行构建一遍没问题再重新编译，原因参考：&lt;a href=&quot;http://abloz.com/2011/01/13/why-use-usr-bin-env.html&quot;&gt;http://abloz.com/2011/01/13/why-use-usr-bin-env.html&lt;/a&gt; 。最后就先make clean &amp;amp;&amp;amp; make distclean 清除前面编译的内容，然后进行预编译configure，参数都在脚本里设置好了，你不需要关心了。预编译后就开始make编译了。&lt;/p&gt;

&lt;p&gt;7.执行build_runtime_android.sh后terminal基本都是刷屏的节奏。刷刷刷的编译输出，你根本来不及看清到底做到哪了做了些什么内容。而config.log这个文件记录所有的编译输出，包括哪行错误了，哪行通过了。调试基本也考这个log文件，如果关键部位错误它会停止，然后你就可以针对性的查了。这里提醒一点，编译时它很多地方都是在检测编译器是否正常，因为它要确认编译器对错误的编译内容是否能够检测到，所以很多错误内容只是测试内容-你需要省略掉。&lt;/p&gt;

&lt;p&gt;8.如果编译成功，那就恭喜你了。windows下我没有测试过，有可能会增加不少坑，我建议还是用linux或者mac编译吧，因为我搜集资料的时候不少人对windows下编译mono都抱怨不少。那么我们开始迈入下一个坑吧:)&lt;/p&gt;

&lt;p&gt;下面介绍加解密DLL部分：&lt;/p&gt;

&lt;p&gt;加密算法自己选我不多说了，但我这里要引用一篇同样介绍mono的dll加密的文章，我觉得也写得满不错的，但是文章描述不够详尽。我这篇文章弥补了他的不足，将细节补充得更加细致。你大可以两篇文章加起来参考。&lt;a href=&quot;http://www.unitymanual.com/home.php?mod=space&amp;amp;uid=7672&amp;amp;do=blog&amp;amp;id=1440&quot;&gt;http://www.unitymanual.com/home.php?mod=space&amp;amp;uid=7672&amp;amp;do=blog&amp;amp;id=1440&lt;/a&gt; 不知道地址是不是原作者的，如果不是我再更换吧。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;1.首先找到dll解密入口。mono下/mono/metadata/image.c里mono_image_open_from_data_with_name是关键方法，参数中的data是dll传入的数据。你要做的就是将它解密后传给datac，这个方法程序你必须看下，因为你要了解下解密程序放在哪才合适。&lt;/p&gt;

&lt;p&gt;2.大部分dll都会通过mono_image_open_from_data_with_name这个方法进行加载，但不是所有dll，例如mscorlib.dll和System.Core.dll就不会，可能还有其他dll，我并不确定还有哪些。所以你还是得辨别下哪些dll会通过这个方法，这样你才能确定哪个dll可以加密。如何判断data属于哪个dll呢，参数name就是data的路径名，name打印出来后就像:/data/app/com.xx.xx.apk/assets/bin/Data/Managed/xxx.dll 这样。&lt;/p&gt;

&lt;p&gt;3.打印调试。你可以使用g_message例如：g_warning(“dll name: %s \n”, name); 其他的打印调试你可以查看源码中的它写的代码。很容易找到，查关键字LOG吧。&lt;/p&gt;

&lt;p&gt;4.改完后重新编译mono，找到libmono.so(find . -name libmono.so)，完成编译后libmono.so的平台有好几个，你可以根据自己的平台来选。有人拷贝这些mono重新编译过的文件去覆盖了unity编辑器的原来mono文件，这样也可行。但我选择在打包android时再从外部复制libmono.so，这样就可以绕过编译器重新编译后无法读取无加密dll的麻烦，可以少做一层无意义的编辑器状态下的加解密工作。&lt;/p&gt;

&lt;p&gt;5.mono解密部分就到这里了。其他部分的关键就是你的加解密程序了，是否能够加密和解密都是ok的并且都是不改变size。你需要的参数有data和data_len，mono_image_open_from_data_with_name方法里面都有。&lt;/p&gt;

&lt;p&gt;6.为了安全起见我使用c来编写加密程序，因为我认为c#和c的编译器对于变量内存的存储机制不一样，怕引起不必要的麻烦。
这里要非常感谢一个人，全程都在提供帮助：炽乐@宗树&lt;/p&gt;

&lt;p&gt;转载请注明出处：http://www.luzexi.com&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>linux环境变量简介</title>
   <link href="http://www.luzexi.com/2015/04/07/linux%E7%8E%AF%E5%A2%83%E5%8F%98%E9%87%8F%E7%AE%80%E4%BB%8B"/>
   <updated>2015-04-07T10:42:29+08:00</updated>
   <id>http://www.luzexi.com/2015/04/07/linux环境变量简介</id>
   <content type="html">&lt;p&gt;linux环境变量简介。这个话题已经很老了,我只是重新温习一遍旧的知识而已。顺便熟悉下资料整理工具OmntOutliner。我们在使用android-sdk，android-ndk,jdk等众多软件时都会用到linux环境变量的配置。所以我觉得重点介绍下还是很有必要的。
因为我使用的是iframe标签，所以有可能有部分浏览器并不支持。
如果你查看时有问题可以直接点击:&lt;a href=&quot;/static-page/linux-env.html/index.html&quot;&gt;linux环境变量简介&lt;/a&gt;&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;&amp;lt;iframe src=”/static-page/linux-env.html/index.html” width=”100%” , height=”1000”&amp;gt;
&amp;lt;/iframe&amp;gt;&lt;/p&gt;

&lt;p&gt;转载注明出处 http://www.luzexi.com&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Unity3D-游戏画面高低品质切换</title>
   <link href="http://www.luzexi.com/2015/04/01/Unity3D-%E6%B8%B8%E6%88%8F%E7%94%BB%E9%9D%A2%E9%AB%98%E4%BD%8E%E5%93%81%E8%B4%A8%E5%88%87%E6%8D%A2"/>
   <updated>2015-04-01T00:36:06+08:00</updated>
   <id>http://www.luzexi.com/2015/04/01/Unity3D-游戏画面高低品质切换</id>
   <content type="html">&lt;p&gt;Unity3D-游戏画面高低品质切换。最近想办法让游戏在高画质和低画质之间切换，在判定游戏帧数高低后，可以实时切换游戏品质让游戏更加流畅。这能给客户端在渠道发行后提高些许留存率。
ngui和ugui切换方式有所不同，一个基于atlas一种基于Image，一种是之前的ngui的atlas，一种是Unity3D4.6.1后的sprite 2D(ugui)。&lt;/p&gt;

&lt;p&gt;两种方式都基于两套图和两套prefab。共同特点就是在开发期间prefab，用脚本工具去生成相应的sd prefab。细节如下：&lt;/p&gt;

&lt;p&gt;1.一套ui图分两套图，一套高清一套低清，ui prefab一套为指向高清图，一套指向低清图。&lt;/p&gt;

&lt;p&gt;2.基于两套图和两套prefab。开发期间，在修改其中一个prefab时，做个工具脚本，自动复制这个prefab到sd文件夹下，并将prefab里的所有图替换为sd。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;3.程序在选择sd还是hd时，只要关注prefab名就可以了。prefab名可以后缀不一样，load时可以区分开来。&lt;/p&gt;

&lt;p&gt;4.最后做到极致时，可以加强工具脚本，一键生成所有sd的prefab。
ngui部分的prefab，编写脚本使用atlas指向切换。ugui部分的prefab，编写脚本使用更换Image。&lt;/p&gt;

&lt;p&gt;最后总结：两套图一高一低，需要维护两套，使用脚本工具根据hd的prefab生成sd的prefab。低清图在压缩时，如果使用unity3d自带的压缩机制太过于粗糙的话，可以美术手动压缩。&lt;/p&gt;

&lt;p&gt;特别感谢一起讨论的童鞋，结论是通过大家的智慧结合：完美@yang，巨人@tangram，炽乐@宗树。&lt;/p&gt;

&lt;p&gt;转载请注明出处：http://www.luzexi.com&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Unity3DLua-将xls转化为lua</title>
   <link href="http://www.luzexi.com/2015/03/26/Unity3DLua-%E5%B0%86xls%E8%BD%AC%E5%8C%96%E4%B8%BAlua"/>
   <updated>2015-03-26T13:29:02+08:00</updated>
   <id>http://www.luzexi.com/2015/03/26/Unity3DLua-将xls转化为lua</id>
   <content type="html">&lt;p&gt;Unity3DLua-将xls转化为lua。使用lua写unity3d项目，由于加载数据一直觉得不方便，于是写个脚本将xls数据文件转化为lua文件，这样lua逻辑就可以直接读取数据。
为什么要这样做呢？&lt;/p&gt;

&lt;p&gt;首先转化为lua后就不再需要解析csv,json等数据了，可以直接使用。&lt;/p&gt;

&lt;p&gt;其次，数据转乘lua后，在同步lua脚本时，可以一并同步数据。在同步环节省去了同步数据的麻烦。&lt;/p&gt;

&lt;p&gt;再者，使用xls2lua脚本转化为lua数据文件，可以达到自动化校验的效果，省去一部分人为操作失误。&lt;/p&gt;

&lt;p&gt;最后，我观察到很多游戏都使用这种方式，其中比较有名的是 《刀塔传奇》。其实这种方式早就很普遍了，只不过我沿着别人的足迹而已走到了这了里而已。&lt;/p&gt;

&lt;p&gt;项目已经放在了github上，如果需要可以去拿。如果喜欢可以star(收藏)下。&lt;/p&gt;

&lt;p&gt;Github address : &lt;a href=&quot;https://github.com/luzexi/xls2lua&quot;&gt;https://github.com/luzexi/xls2lua&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;以下是github的readme部分的文字，描述有英文和中文两个版本。你可以忽略英文部分。&lt;/p&gt;

&lt;h3 id=&quot;excute-example-举例执行命令&quot;&gt;Excute Example (举例执行命令)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;python ./xls2lua.py example_building.xls ./data/
&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;notice注意点&quot;&gt;NOTICE:(注意点)&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;The sheet name must start with &quot;output_&quot; , the lua file name will be the name behind &quot;output_&quot;.
The **first row** must be **title**. 
The **second row** must be **type** 
The **type must be i , f , s , b , ai , af , as , ab.
i mean int , f mean float , s mean string , b mean bool , ai mean array int , af mean array float , as mean array string , ab mean array bool.
The **first column** must be int , so the type in first column must be i.
The string type with char **&quot;** or **'** will be replace by \&quot; or \' 
The empty col will be a default value like 0 or &quot;&quot; or false or {} 
(sheet名以&quot;output_&quot;开头的才会被识别转换，否则将被忽略) 
(第1行必须是关键字名) 
(第2行必须为类型) 
(类型有：i,f,s,b,ai,af,as,ab这几种) 
(i表示int，f表示float,s表示string,b表示bool,ai表示int数组,af表示float数组,as表示string数组,ab表示bool数组) 
(第1列必须为int类型的唯一关键字) 
(string类型中&quot;和'会自动用\&quot;和\'替代)
(空列将会被默认值代替，例如:0,&quot;&quot;,false,{})
&lt;/code&gt;&lt;/pre&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;h3 id=&quot;lua-script-生成后的lua文件示例&quot;&gt;Lua script (生成后的Lua文件示例)&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&quot;language-lua&quot;&gt;-- this file is generated by program!
-- don't change it manaully.
-- source file: example_building.xls
-- created at: Thu Mar 26 02:53:52 2015

local data = {}

data[1] = { id = 1,  name = &quot;house&quot;,  use_money = 1000,  use_food = 2.33,  is_init = true,  defense = 100,  aadd = {1,2,3},  aadddss = {1.23,2,3.23},  ddff = {&quot;sdf&quot;,&quot;23e&quot;,&quot;s&quot;},  ffdd = {true,false,true}}
data[2] = { id = 2,  name = &quot;house2&quot;,  use_money = 123,  use_food = 336.2,  is_init = true,  defense = 0,  aadd = {1,2,3},  aadddss = {1,2.3445,3},  ddff = {&quot;你好&quot;,&quot;你在哪&quot;},  ffdd = {true,false}}
data[3] = { id = 3,  name = &quot;&quot;,  use_money = 456,  use_food = 222.33665,  is_init = false,  defense = 130,  aadd = {3,2,5},  aadddss = {3,2,2.5},  ddff = {&quot;我在这里啊&quot;,&quot;你在那&quot;,&quot;呢&quot;},  ffdd = {false,true}}
data[4] = { id = 4,  name = &quot;farm&quot;,  use_money = 100,  use_food = 220.0,  is_init = false,  defense = 200,  aadd = {2,3},  aadddss = {200.3,3,234.23},  ddff = {&quot;df&quot;,&quot;ssd&quot;,&quot;dd&quot;,&quot;dd&quot;},  ffdd = {}}
data[5] = { id = 5,  name = &quot;house5&quot;,  use_money = 0,  use_food = 22.1,  is_init = false,  defense = 234,  aadd = {3,6,6,7},  aadddss = {3,6.3,6,7},  ddff = {&quot;ss&quot;,&quot;d&quot;,&quot;d&quot;,&quot;d&quot;},  ffdd = {true,true}}
data[6] = { id = 6,  name = &quot;horse3&quot;,  use_money = 200,  use_food = 0,  is_init = false,  defense = 333,  aadd = {},  aadddss = {},  ddff = {&quot;2e&quot;,&quot;w&quot;,&quot;e&quot;,&quot;we&quot;},  ffdd = {false,false,false,false}}

return data

&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id=&quot;how-to-use-lua-with-data-如何使用生成的lua数据&quot;&gt;How to use lua with data. (如何使用生成的lua数据)&lt;/h3&gt;

&lt;pre&gt;&lt;code class=&quot;language-lua&quot;&gt;local building = require &quot;building&quot;

print(building[1].name)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The console will print “house”&lt;/p&gt;

&lt;p&gt;转载注明出处 http://www.luzexi.com&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Unity3D项目环境全部署</title>
   <link href="http://www.luzexi.com/2015/03/06/Unity3D%E9%A1%B9%E7%9B%AE%E7%8E%AF%E5%A2%83%E5%85%A8%E9%83%A8%E7%BD%B2"/>
   <updated>2015-03-06T18:18:08+08:00</updated>
   <id>http://www.luzexi.com/2015/03/06/Unity3D项目环境全部署</id>
   <content type="html">&lt;p&gt;Unity3D项目环境全部署。这次我想总结下项目环境部署。可别小看了这个开发环境部署，这将直接导致项目的进程速度，以及成员们在这个开发行程中的疲劳积累速度。优秀的部署能省去成员们不必要的心理负担，卸下包袱专心去做自己的功能块。差劲的部署，会加重成员们的心理负担，并且有可能引起不必要的开发冲突。&lt;/p&gt;

&lt;p&gt;首先我来罗列下Unity3D开发手游需要经历哪些节点。&lt;/p&gt;

&lt;p&gt;1.Unity3D内编码。里面的内容自然主要是架构与编码，本篇不对其细说。&lt;/p&gt;

&lt;p&gt;2.IOS,Android  SDK接口对接。不同的平台，不同的sdk对接。&lt;/p&gt;

&lt;p&gt;3.项目进度监督与管理，所有策划案与修改案都会被记录在项目进度表中，每周都需要一个报告来支撑项目进度。项目的进度跟踪是对项目进度把控的最好体现。&lt;/p&gt;

&lt;p&gt;4.测试部门的bug提交，与用户体验建议&amp;amp;修改。这将极大的完善游戏体验，让bug与差体验都尽可能得消失殆尽。&lt;/p&gt;

&lt;p&gt;5.wiki面板。这将项目中的重大事件与注意事项进行记录，共所有人查阅，以方便在开发中查阅前面所经历的问题，缩短询问时间。&lt;/p&gt;

&lt;p&gt;6.策划案数据自动生成。策划案的数据在开发过程中随时都会进行改变，一键生成数据并同步到程序，反应到客户端是加快项目速度的好方法，也同时消除了成员们手动操作的心理负担。&lt;/p&gt;

&lt;p&gt;7.一键生成需要动态加载的资源与资源版本号，有些还需要对动态资源进行zip压缩打包。很多游戏都会在游戏的开头，或游戏中进行加载资源，开发中这些资源都会随时随地的被成员们改变，一键生成将加快项目进度，消除成员们手动操作的心理负担。&lt;/p&gt;

&lt;p&gt;8.打包成不同平台的包。ios-&amp;gt;ipa , android-&amp;gt;apk (android这里还有不同的游戏平台游戏包，一个游戏至少会有20个平台需要接，每个平台1个包)，winphone也一样。&lt;/p&gt;

&lt;p&gt;9.将包上传至内部服务器供，测试部门以及项目成员们测试&amp;amp;预览。ios可以用testFlight，也可以用协议在网页上自行下载安装。android和winphone不用说可以放内部网页直接下载安装。&lt;/p&gt;

&lt;p&gt;以上是所有开发需要的步骤，包括了 主逻辑开发，平台对接，项目管理，测试管理，备忘录，数据同步，资源打包，客户端打包，以及客户端同步。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;这些都只是称述而已，那么如何将这些部分连起来呢。看下图：
&lt;img class=&quot;alignnone size-full wp-image-501&quot; src=&quot;/assets/uploads/2015/03/客户端环境布局.jpg&quot; alt=&quot;客户端环境布局&quot; width=&quot;711&quot; height=&quot;486&quot; /&gt;能看明白吗，容我解释下(其实上面也有一部分称述)：&lt;/p&gt;

&lt;p&gt;点1和2=&amp;gt;使用git版本控制，建立主分支，各功能块拆分成不同分支并行开发，在主分支上建立ios,android,winphone平台分支，这里只写平台代码逻辑代码从主分支获取。&lt;/p&gt;

&lt;p&gt;点3和4和5=&amp;gt;使用redmine(当然也可以用别的管理)，建立wiki写备忘录，提供测试与体验提交问题，并且使用office软件与email编写进度报表共成员们查阅。&lt;/p&gt;

&lt;p&gt;点6=&amp;gt;用python(也可以ruby,php…等等自己选)解析xls生成数据并上传。&lt;/p&gt;

&lt;p&gt;点7=&amp;gt;这里可以用python语言也可用jenkins来执行资源打包程序，并上传。&lt;/p&gt;

&lt;p&gt;点8=&amp;gt;使用jenkins打包，建立&amp;amp;配置打包流程，随时随地可以一键打包，使得打包快速、准确、稳定。&lt;/p&gt;

&lt;p&gt;点9=&amp;gt;ios可以用testFlight，也可以用协议在网页上自行下载安装。android和winphone不用说可以放内部网页直接下载安装。&lt;/p&gt;

&lt;p&gt;总结：尽量将各部门的合作用自动化的形式融合在一起，维护好自动化程序与合作流程，让各个成员们都只关注自己的部分。最终目的就是加快开发速度，减少成员疲劳度积累(避免是不可能的)。&lt;/p&gt;

&lt;p&gt;另外题外话—-附上web服务器的项目布局，见下图。&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;alignnone size-full wp-image-510&quot; src=&quot;/assets/uploads/2015/03/服务端开发环境布局.jpg&quot; alt=&quot;服务端开发环境布局&quot; width=&quot;686&quot; height=&quot;468&quot; /&gt;&lt;/p&gt;

&lt;p&gt;转载请注明出处：http://www.luzexi.com&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Unity3D上海CTO&amp;Leader讨论会</title>
   <link href="http://www.luzexi.com/2015/02/11/Unity3D%E4%B8%8A%E6%B5%B7CTO&Leader%E8%AE%A8%E8%AE%BA%E4%BC%9A"/>
   <updated>2015-02-11T20:15:24+08:00</updated>
   <id>http://www.luzexi.com/2015/02/11/Unity3D上海CTO&Leader讨论会</id>
   <content type="html">&lt;p&gt;2015-02-11 今天去了趟unity上海分部的CTO&amp;amp;Leader讨论会。记录下会议内容很多无聊的东西我都略过不写了，报告下我们比较关心的，或者将来会遇到的困难。&lt;/p&gt;

&lt;p&gt;1.现在unity3d 4.6.2 打包64位的ios app 不靠谱，bug有400多个，不建议升级。4月1号前说是会出个稳定版，但讨论中透露，稳定版中也会由许多未知因素。IL2P打成c++的方式有众多问题和困难。&lt;/p&gt;

&lt;p&gt;2.他们说了个热更新的解决方案，但只限安卓。原理就是把更新apk里的dll部分。然后我说了关于使用lua更新机制，虽然现在在u3d里仍不是非常成熟，但困难是永远都有的，各位可以值得一试，不要等到别人用熟了你才开始，那已经慢了好大一步了。&lt;/p&gt;

&lt;p&gt;3.问了关于内存释放的问题，他们阐述说resources.unloadunusedassets并不是根据引用计数来释放内存，而是根据世界树中的实例检测，而System.GC.Collect()是根据引用计数销毁的，所以可以选择两个一起使用，也就是我们现在的方式。unity3d内存销毁，似乎有时会隔一个场景，所以在主场景和战斗场景切换时中间隔个空场景是有必要的。&lt;/p&gt;

&lt;p&gt;4.关于内存，他们在阐述他们提供的一个性能测试服务中，提到由于ngui底层是mesh重构，而每次重构都会积累内存消耗，所以ngui的内存问题是比较严重。&lt;/p&gt;

&lt;p&gt;5.关于混淆，他们只有一般的混淆方案。网上可以找到。没什么特别的自己方案。混淆的注意点是，混淆时安卓和ios等平台对接的接口不要进行混淆，在平台调用时会找不到相应接口。unity技术团队说他们可以提供混淆服务，但我觉得并不困难。&lt;/p&gt;

&lt;p&gt;6.unity5 将与unity4完全不同，互不兼容，也就是像cocosx 2与cocosx 3一样，拆分两个版本维护。IL2P将会逐渐替代mono的打包方式。mono也很难升级，因为高版本的授权费用很高，一般的用户承受不了。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;7.以下是一些unity5比较有用的改善。没兴趣关注的就不提了。&lt;/p&gt;

&lt;p&gt;*场景中的shader可以自动合并成一个shader，减低drawcall。
*assetbundle打包可以进行增量更新，无变化的assetbundle会被自动识别，加快了批量打assebbundle的速度。
*更多得采用多线程处理，加快unity应用的速度。
*支持webGL平台的开发。在支持webGL浏览器里不需要unity插件。
*unity5升级为64位，编辑器可使用更多内存。&lt;/p&gt;

&lt;p&gt;8.最后他们提供说一个真机性能测试的服务，可以提供60页的性能测试报表。性能卡点自己找起来比较准确点，就看你愿不愿意去找了。&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;alignnone size-full wp-image-487&quot; src=&quot;/assets/uploads/2015/02/IMG_0972.jpg&quot; alt=&quot;IMG_0972&quot; width=&quot;1763&quot; height=&quot;885&quot; /&gt;&lt;/p&gt;

&lt;p&gt;转载注明出处 http://www.luzexi.com&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Lua使用实记</title>
   <link href="http://www.luzexi.com/2015/01/21/Lua%E4%BD%BF%E7%94%A8%E5%AE%9E%E8%AE%B0"/>
   <updated>2015-01-21T20:14:59+08:00</updated>
   <id>http://www.luzexi.com/2015/01/21/Lua使用实记</id>
   <content type="html">&lt;p&gt;c调用lua堆栈常用操作——-&lt;/p&gt;

&lt;p&gt;===================初级========================&lt;/p&gt;

&lt;p&gt;void lua_gettop() : 用于返回栈中元素的个数，同时也是栈顶元素的索引，因为栈底是1，所以栈中有多少个元素，栈顶索引就是多少。&lt;/p&gt;

&lt;p&gt;void lua_settop(int index) : 设置栈顶，也是设置栈的大小，多的去除，少的填nil。– #define lua_pop(L,n) lua_settop(L,-(n)-1)&lt;/p&gt;

&lt;p&gt;void lua_pushvalue(int index) : 拷贝索引index元素并压入栈。&lt;/p&gt;

&lt;p&gt;void lua_remove(int index) : 删除索引index。&lt;/p&gt;

&lt;p&gt;void lua_replace(int index) : 弹出栈顶元素，并将其替换到索引index元素。&lt;/p&gt;

&lt;p&gt;void lua_getglobal(const char *name) : 把全局变量name压入栈顶。&lt;/p&gt;

&lt;p&gt;void lua_pop(int n) : 推出栈顶(移除)n个元素。&lt;/p&gt;

&lt;p&gt;void lua_insert(int index) : 弹出栈顶，并将其插入索引index中。&lt;/p&gt;

&lt;p&gt;void lua_remove(int index) : 移除索引index的元素。&lt;/p&gt;

&lt;p&gt;lua_is***(int index) 检查变量是不是某个类型，index指示变量的顺序，栈顶为-1。&lt;/p&gt;

&lt;p&gt;lua_to***(int index) 获取栈中的变量，然后转换为某个指定的类型，并返回。&lt;/p&gt;

&lt;p&gt;lua_push***() 压入某类型元素。&lt;/p&gt;

&lt;p&gt;int lua_type(int index) : 获得索引index的值的类型。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;===================中级========================&lt;/p&gt;

&lt;p&gt;void lua_call(int nargs, int nresults) : 调用方法，其中nargs为参数数量，nresults为结果数量。栈中必须保持  ….. func , arg1 , arg2 ,art3 (&lt;em&gt;) ，&lt;/em&gt;表示栈顶，调用后func,arg都会消失，只留下结果，如果调用没有问题的话。&lt;/p&gt;

&lt;p&gt;int lua_pcall(int nargs, int nresults, int errfunc) :  功能pcall与call一样，区别在最后多了个自定义错误处理，当调用出错后，会调用其栈中索引方法。pcall返回0为无错误，其他则表示调用有错。如果errfunc为0，则报错调用原始方法。&lt;/p&gt;

&lt;p&gt;void lua_createtable(int narr, int nrec) : 创建一个narr行nrec列的table，将其压入栈。&lt;/p&gt;

&lt;p&gt;void lua_newtable() : 创建一个空表，将其压入栈。与lua_createtable(0,0)一个意思。&lt;/p&gt;

&lt;p&gt;void lua_getfield(int index, const char *k) :  t[k]，获取表(索引为t的表)中的k值，并压入栈。这个会调用被修改的__index的方法，如果被修改过的话。&lt;/p&gt;

&lt;p&gt;void lua_setfield(int index, const char *k) : 设置t[k] = v，t为索引index的表，k为参数k值，v为栈顶元素，并弹出栈顶元素。此调用会触发修改过的newindex方法。&lt;/p&gt;

&lt;p&gt;void lua_getglobal(const char *name) : 获取全局变量name，并压入栈顶。&lt;/p&gt;

&lt;p&gt;void lua_setglobal(const char *name) : 弹出栈顶，并将其设置为全局变量name的值。&lt;/p&gt;

&lt;p&gt;void lua_gettable(int index) : 弹出栈顶，并压入t[k]值，t为索引index的表，k为栈顶值。此方法将调用被修改的__index的方法，如果被修改过的话。&lt;/p&gt;

&lt;p&gt;void lua_settable(int index) : 设置表t[k]=v，t为索引index的表，v是栈顶元素，k为栈顶下面一个元素。例如 lua_settable(-3)  —- table , “key” , “value” (&lt;em&gt;) ，&lt;/em&gt;表示栈顶。并弹出v(栈顶)和k(栈顶下面)元素，此调用会触发修改过的newindex方法。&lt;/p&gt;

&lt;p&gt;void lua_rawget(int index) : 与gettable一样，但调用的索引方法是原始的。&lt;/p&gt;

&lt;p&gt;void lua_rawset(int index) :  与settable一样，但调用的索引方法是原始的。&lt;/p&gt;

&lt;p&gt;void lua_rawgeti(int index, int n) : 压入t[n]值，t为索引index的表，n为参数n，调用的索引方法是原始的。&lt;/p&gt;

&lt;p&gt;void lua_rawseti(int index, int n) : 设置t[n]=v，t为索引index的表，v为栈顶值，n为参数n，并弹出栈顶元素。调用的索引方法为原始的。&lt;/p&gt;

&lt;p&gt;int lua_getmetatable(int index) : 压入索引index的值的metatable，如果索引index的值有metatable则返回非0，否则返回0并不压入任何元素。&lt;/p&gt;

&lt;p&gt;int lua_setmetatable(int index) : 弹出栈顶，并将其设置为索引index值的metatable。&lt;/p&gt;

&lt;p&gt;void lua_register(const char *name, lua_CFunction f) :  向lua注册名字为name的f方法。这个方法相当于 #define lua_register(L,n,f) (lua_pushcfunction(L, f), lua_setglobal(L, n))&lt;/p&gt;

&lt;p&gt;转载请注明出处：http://www.luzexi.com&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Unity3D手机中的网页调用与回调</title>
   <link href="http://www.luzexi.com/2014/11/23/Unity3D%E6%89%8B%E6%9C%BA%E4%B8%AD%E7%9A%84%E7%BD%91%E9%A1%B5%E8%B0%83%E7%94%A8%E4%B8%8E%E5%9B%9E%E8%B0%83"/>
   <updated>2014-11-23T23:04:46+08:00</updated>
   <id>http://www.luzexi.com/2014/11/23/Unity3D手机中的网页调用与回调</id>
   <content type="html">&lt;p&gt;Unity3D手机中的网页调用与回调，其实就是我们口中常说的Webview，是手机项目里调用网页，来代替游戏画面展示的一种方法。因为其展示的是网页，所以展示画面是相对动态的，可以由服务器来控制。但是我们不只需要展示画面，还需要当点击网页按钮时产生回调以使U3D执行指定程序。&lt;/p&gt;

&lt;p&gt;其实日本在webview这个模块上做的很不错，我也是借用日本Gree公司在github上公开的webview程序来作为底层代码的，只是我稍微改进了下，以适合自己的程序习惯和项目习惯。下面我主要介绍下这个webview的运作机制。&lt;/p&gt;

&lt;p&gt;一、调用Webview打开网页。
我在源码基础上加了个单例接口，使得接口调用更加简单。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;初始化接口：WebViewObject.sInstance.Init((msg)=&amp;gt;{
Debug.Log(string.Format(&quot;CallFromJS[{0}]&quot;, msg));
}); 初始化传入的是点击回调的消息接口。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;设置窗体大小：WebViewObject.sInstance.SetMargins(5, 5, 5, 40); 窗体大小定义的十左边，上边，右边，下边的间隔像素。&lt;/p&gt;

&lt;p&gt;展示OR关闭网页：WebViewObject.sInstance.SetVisibility(true);&lt;/p&gt;

&lt;p&gt;通过地址加载某网页：WebViewObject.sInstance.LoadURL(path);&lt;/p&gt;

&lt;p&gt;二、Webview在不同平台的插件载体和编译这些载体的方法。&lt;/p&gt;

&lt;p&gt;这里主要是Android、IPHONE和MAC平台—因为它只做了这3个平台，所以Windows平台的同学就不能再编辑器里看到了，调试Webview时就用MAC吧。Android使用jar做插件体，iphone用.mm文件做插件体，mac用bundle做插件载体。&lt;/p&gt;

&lt;p&gt;iphone是用.mm文件，所以不用编译载体，只有Android和Mac需要。在platform_src/Android和platfor_src/iOS下分别有install.ssh文件可以执行，用来编译各自的载体。所以你只要执行就可以了。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;三、Webview网页按钮回调。&lt;/p&gt;

&lt;p&gt;Webview网页按钮回调的功能对我们来说非常有用，所以这里细致讲下它是如何辨认并回调的。
所有的网页里的链接都可以想象成按钮，无论是图片超链接还是普通的链接地址，更或是form形式的submit提交按钮，都是以网页形式访问网页地址。当访问网页地址时，native程序中拦截了网页地址，并查看网页地址是否以’unity:’打头，如果是以’unity:’打头的认为是调用unity3d的按钮，将此地址直接发送(SendMessage)给U3D的Webview程序，让其判断根据接收到得地址判断需要调用哪段程序。比如，地址为unity:auth，U3D程序在init时传入的接口接收到的为auth的字符串。所以如果你想让网页链接调用U3D程序，你就把链接写成unity:开头的超链接。&lt;/p&gt;

&lt;p&gt;最后，我们既然有了手机中得网页调用与回调，我们就可以充分运用其在游戏里的功能了，最最基础的就是公告了，其次就是攻略网页，还有游戏功能的说明和调用，甚至有时可以直接代替游戏画面，直接替换画面的灵活性就非常强大了。&lt;/p&gt;

&lt;p&gt;现在，我连Webview的测试案例都写好，测试案例的项目在git clone 后切记使用 git submodule update –init 更新submodule。：）Good luck!&lt;/p&gt;

&lt;p&gt;注：有人反馈显示网页后，点击奔溃的问题，这是U3D的Editor本身与MAC兼容的问题，U3D编辑器的调用MAC程序奔溃比较频繁，所以细致的测试还得在真机上进行比较合适。&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;alignnone size-full wp-image-450&quot; src=&quot;/assets/uploads/2014/11/img.png&quot; alt=&quot;img&quot; width=&quot;1408&quot; height=&quot;960&quot; /&gt;&lt;/p&gt;

&lt;p&gt;源码地址：&lt;a href=&quot;https://github.com/luzexi/Unity3DWebView&quot;&gt;Unity3DWebview&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;测试案例地址：&lt;a href=&quot;https://github.com/luzexi/Unity3DWebView-Test&quot;&gt;Unity3dWebviewTest&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;转载请注明出处：http://www.luzexi.com&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Unity3D人物行为状态机和指令状态机</title>
   <link href="http://www.luzexi.com/2014/10/25/Unity3D%E4%BA%BA%E7%89%A9%E8%A1%8C%E4%B8%BA%E7%8A%B6%E6%80%81%E6%9C%BA%E5%92%8C%E6%8C%87%E4%BB%A4%E7%8A%B6%E6%80%81%E6%9C%BA"/>
   <updated>2014-10-25T15:06:18+08:00</updated>
   <id>http://www.luzexi.com/2014/10/25/Unity3D人物行为状态机和指令状态机</id>
   <content type="html">&lt;p&gt;Unity3D人物行为状态机和指令状态机。状态机在各类游戏中都应用的比较平凡，不要以为只有角色扮演类游戏才用到状态机，但凡有物体有动作切换或者重复的指令切换都是状态机用到的地方。其实状态机到处都是，只有你想不到没有有他触及不到的地方，当然我这里主要讲有限状态机。这里省略一万字的状态机基础知识以及我的唠叨。&lt;/p&gt;

&lt;p&gt;状态机在游戏项目中的主要运用点：&lt;/p&gt;

&lt;p&gt;1.人物动作。一般的RPG人物最为明显，每个人物都有4-5个基本动作，比如 空闲动作，行走，攻击，受伤，施法等。把每个动作看成一个状态，就有了基本的状态，把每个状态用参数串联起来就算是状态机了。&lt;/p&gt;

&lt;p&gt;2.AI。这个在游戏AI里表现得最为明显，用状态机写AI是最快并且最有效的方式了。每个AI也都有几个基本状态，比如 追击状态，逃跑状态，疯狂状态，巡逻状态。有了这几个状态，就可以用参数串联起来成为状态机了。&lt;/p&gt;

&lt;p&gt;3.人物指令。这个可能很多没有写过状态机的童鞋比较难懂，他跟AI状态有点像，是为了辅助人物动作状态的。当我们把人物动作状态机定义完毕时，发现只有人物动作状态，如何去扩展除了动作以外的东西呢，比如移动，缩放。人物指令状态机就是做这事的，他相当于在动作状态机上套了又一层状态机。比如人物行走指令里，调用了动作行走状态，让人物一直保持行走动作，然后自己只要去处理人物位移就可以了，这样既有了动作也有了位移，整套移动指令就完整了。又比如攻击指令状态，发出指令后，攻击指令状态先调用行走动作状态并移动到目标点，再调用攻击动作状态，再处理攻击受伤逻辑。表现就是，一个人走到目标点攻击了另一个人。中间还可以加很多逻辑，比如判断是否处于封印状态，如果处在封印状态就不能攻击。&lt;/p&gt;

&lt;p&gt;状态机极大的简化了对人物行为变化的处理方式，也更加容易进行扩展。Unity3D里有一个动作的状态功能Animator，他的图形界面很清晰得解释了状态机的原理。但我自己并不喜欢使用，因为我认为这个Animator限制了程序员的思维，虽然达到了可视化状态机的目的但扩展性差，所以我一直不推崇使用，不过这只是个人看法。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;&lt;img class=&quot;alignnone size-full wp-image-433&quot; src=&quot;/assets/uploads/2014/10/10039783.jpg&quot; alt=&quot;10039783&quot; width=&quot;550&quot; height=&quot;240&quot; /&gt;&lt;/p&gt;

&lt;p&gt;运用状态机时需要注意的几个问题:&lt;/p&gt;

&lt;p&gt;1.传统网游里人物状态不太多，最多也就10个极限了，但是现在已经不是从前了，特别是单机游戏里，动不动就2，3十个状态，这个时候状态与状态之间切换逻辑就成了一个麻烦事。我们可以增加事件处理句柄来整理状态切换事件，其他方法也有很多种，运用时最好围绕让程序员容易找，容易改来，不要为了发挥设计模式而抛弃了代码清晰度。&lt;/p&gt;

&lt;p&gt;2.输入参数。当前游戏项目中，输入参数有很多种。一种输入参数已经满足不了状态机在游戏中得运用了。而且不同的状态对于输入的参数也不同，所以我们可以面向对象方式对状态机输入进行扩做。我们定义一个Input类，里面什么都没有，每个状态或者某几个状态另开出一个类xxInput继承Input，增加里面的参数，再在输入参数时，对参数进行辨别，如果是xxInput时就对相应状态进行判定和处理。&lt;/p&gt;

&lt;p&gt;总结：状态机没有固定框架，还得要大家自己亲自试验，最好能借鉴别人的项目或者在自己的项目里试着写入状态机。我能帮大家的也就分析解释下状态机在游戏里的运用情况，最后还得靠自己琢磨出来。&lt;/p&gt;

&lt;p&gt;转载请注明出处：http://www.luzexi.com&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Unity3D行为树系统编程实现</title>
   <link href="http://www.luzexi.com/2014/10/24/Unity3D%E8%A1%8C%E4%B8%BA%E6%A0%91%E7%B3%BB%E7%BB%9F%E7%BC%96%E7%A8%8B%E5%AE%9E%E7%8E%B0"/>
   <updated>2014-10-24T11:16:11+08:00</updated>
   <id>http://www.luzexi.com/2014/10/24/Unity3D行为树系统编程实现</id>
   <content type="html">&lt;p&gt;Unity3D行为树系统编程实现。行为树在AI领域使用十分广泛，行为树的架构非常通用和便捷，在扩展性方面表现得更是非常好。我这里只讲关于如何实现行为树，跳过了基础的行为树概念。关于行为树的介绍和行为树概念请看&lt;a href=&quot;/游戏通用模块/2013/01/26/使用行为树(Behavior-Tree)实现游戏AI.html&quot;&gt;《使用行为树behavior-tree实现游戏ai》&lt;/a&gt;这是业界一个前辈写的文章我只是代为转载。&lt;/p&gt;

&lt;p&gt;很久没有写文章了，如果文笔弱请多见谅，随着工作的繁忙，写博客也是件奢侈的事情。在整理完代码后累得跟死猪一样，时间并不充裕，经历也有限，请各位期待更新的朋友多多见谅。行为树一直是我希望完善的一个系统框架，本身在2011年时就用在3DRPG里，但那是并不完善，只是个初级的产品。这次我彻彻底底把他完善成一个固定框架，可以直接拿来扩展使用。原本也这个行程也只是记在笔记本上，但最近有个英国的出版社发MAILL给我说有篇UNITY3D AI的书他们正在出版，要我做一下审阅和评论，于是看了几天审阅了段时间，里面大部分内容都是讲U3D里一个行为树插件RAIN如何使用，一项不喜欢使用插件的我看得我很不爽，因为文章大部分内容都是叙述使用过程没什么可看的。一念之下，一口气直接把行为树系统框架和行为树编辑器写了一遍。审阅和评论的事情没弄完，自己的行为树系统倒是写完了，罪过罪过。—–废话完毕。&lt;/p&gt;

&lt;p&gt;我先把源码地址贴上，让大家可以边对着代码，边看我的文章。这样可以像下酒菜一样，边喝着啤(wen)酒(zhang)，边吃着花(dai)生(ma)。
源码放在github上: &lt;a href=&quot;https://github.com/luzexi/Unity3DAIBehaviorTree&quot;&gt;https://github.com/luzexi/Unity3DAIBehaviorTree&lt;/a&gt; 如果你喜欢可以star或者follow下。&lt;/p&gt;

&lt;p&gt;行为树系统实现：&lt;/p&gt;

&lt;p&gt;1.我把行为树以BNode为基类节点。&lt;/p&gt;

&lt;p&gt;2.BNode的一层子类为BNodeAction(行动基类)，BNodeComposite(执行顺序基类)，BNodeCondition(条件基类)，BNodeDecorator(修饰基类)。&lt;/p&gt;

&lt;p&gt;3.BNode基类节点的基本方法：OnEnter–进入事件,OnExist–退出事件,Excute–执行事件。子类可以通过重写这三个方法以达到实现自我功能。&lt;/p&gt;

&lt;p&gt;4.节点的执行顺序是需要提前考虑的，我写了几个，比如顺序，选择，并行，随机，迭代。具体点：顺序–子节点中顺序执行直到结束如果遇到失败的节点就返回失败否则成功，选择–子节点中顺序执行直到遇到返回成功的节点，并行–顺序执行节点无论失败成功直到结束返回成功，随机–随机选择一个节点执行返回该节点的结果，迭代–顺序执行直到并连续成功N次就返回成功否则失败。&lt;/p&gt;

&lt;p&gt;5.输入参数BInput是行为树系统的必要类实例，因为在运行行为树时，每个节点的执行对象需要依赖输入参数，每个项目的输入参数都不一样，所以你可以继承BInput扩展成你希望的输入参数，比如：TestInput里加入了hp,mp两个元素用来保存AI的对象状态，你也可以加入更多更复杂的元素，比如角色信息类，伙伴信息类等。&lt;/p&gt;

&lt;p&gt;6.为了让行为树运用到项目里去，我们可以对BNodeAction，BNodeCondition，BNodeDecorator，进行扩展。继承书写的规则：继承子类必须在Game.AIBehaviorTree命名空间里，构造函数里最好对m_strName进行赋值，这样就能在行为树编辑器里看到你的扩展类和扩展命名，每个扩展的节点，如果有需要对变量进行编辑的，变量必须是public的，而且类型只限于int,float,bool,string四种。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;7.行为树编辑器是行为树系统可用性的关键。我把行为树用目录结构的方式来实现，这样对于节点的增加，删除，切换，拖拽，体验都非常便捷。对于每个节点的编辑状态在选择该节点后会在编辑器的右下脚现实。编辑器还承载了保存，加载，以及对行为树的增加和删除，重命名的功能。如下图：&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;alignnone size-full wp-image-429&quot; src=&quot;/assets/uploads/2014/10/img.png&quot; alt=&quot;img&quot; width=&quot;985&quot; height=&quot;492&quot; /&gt;&lt;/p&gt;

&lt;p&gt;8.行为树数据存储。我用json格式来存储行为树，先前我用bytes来存，但发现因为扩展子类的变量经常性变化，所以用bytes来存常常会导致已编辑完成的数据无法重新编辑。所以我用了c#反射进行类与json变量的配对。&lt;/p&gt;

&lt;p&gt;9.最后请大家注意，行为树不只是用来做AI的，比如开宝箱，掉落，物品使用都可以用行为树来做。&lt;/p&gt;

&lt;p&gt;10.文档化数据，行为树有了编辑器似乎抛弃了xls,但我还是认为xls对于策划人员的数据编辑工作还是主流的，所以我接下来扩展的方向就是行为树编辑器xls化。&lt;/p&gt;

&lt;p&gt;转载请注明出处: http://www.luzexi.com&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>html5游戏开发---cocos2dx-js</title>
   <link href="http://www.luzexi.com/2014/09/28/html5%E6%B8%B8%E6%88%8F%E5%BC%80%E5%8F%91-cocos2dx-js"/>
   <updated>2014-09-28T12:05:23+08:00</updated>
   <id>http://www.luzexi.com/2014/09/28/html5游戏开发---cocos2dx-js</id>
   <content type="html">&lt;p&gt;html5游戏开发—cocos2dx-js。在神经猫发热后，我开始关注html5游戏。所以前段时间做了2个html5小游戏当试水，用的引擎是cocos2dx-js。在这里我想做回忆下，html5的制作过程和需要注意的地方，也阐述下当下html5游戏渠道的问题。&lt;/p&gt;

&lt;p&gt;我先接触了egret引擎，它是国内html5优秀的游戏引擎，采用typescripts做主语言。在我用了几天发现，它的主语言并非js所以html5底层的东西会被下意识的屏蔽掉，何不用js的引擎来写html5呢，即能快速开发又能随时接触html5底层。所以我使用了cocos2dx-js。&lt;/p&gt;

&lt;p&gt;关于cocos2dx-js的教程就不在这里写了，接口跟cocos2dx-c++的差不多。&lt;/p&gt;

&lt;p&gt;js写html5需要注意的几点：&lt;/p&gt;

&lt;p&gt;1.html5游戏最需要快速加载，js代码不能分成很多个一个个加载，减慢了加载速度也容易流失用户。所以在写玩html5时需要对所有js代码进行压缩合并。google的closure compiler解决了这个问题，cocos2dx-js也将这个功能融入进里面。下面是步骤：&lt;/p&gt;

&lt;p&gt;在cocos2dx-js-v3.0里使用cocos compile -p web -m release来对html5的js代码进行压缩。&lt;/p&gt;

&lt;p&gt;在cocos2dx-html5(与cocos2dx-js-v3.0是两个版本，cocos2dx-js-v3.0比较好用)里打包html5需要用到ant和build.xml。打包主要内容为将所有js文件打包成同一个文件，目的是减少加载文件数量，加快加载速度。打包命令：cd到项目文件夹下。敲入:ant或者ant -buildfile build.xml。build.xml中囊括了要打包的js文件。其中指定了js打包编译工具complie.jar是google closure compiler专门压缩js文件大小工具。&lt;/p&gt;

&lt;p&gt;关于微信分享:&lt;/p&gt;

&lt;p&gt;微信api已经在微信的浏览器里已经注入了它的api。&lt;/p&gt;

&lt;p&gt;微信api的机制其实就是事件机制，比如当你点击分享时，调用某个你制定的方法显示你想显示的文字和图片。&lt;/p&gt;

&lt;p&gt;微信api你可以参考 &lt;a href=&quot;https://github.com/zxlie/WeixinApi&quot;&gt;https://github.com/zxlie/WeixinApi&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;主要用到的有分享朋友圈，分享给朋友，分享后回调。你可以首先看这几个重点。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;关于html5游戏渠道:&lt;/p&gt;

&lt;p&gt;1.现在html5游戏的渠道很少，而且现在html5游戏基本呈现在微信浏览器中，所以微信的公众平台是渠道之一，而且似乎占比比较大。&lt;/p&gt;

&lt;p&gt;2.在国外html5游戏在电视上的占比也开始大了，我不知道国内的情况，未来国内是否有可能在电视上呈现html5游戏。&lt;/p&gt;

&lt;p&gt;3.html5游戏的app形式，这个还是很有潜力的，因为渠道太少倒逼html5游戏发展成轻型app。也就是说html5的app就只是个连接地址，但在手机上呈现的是一个图标，打开后以浏览器形式访问。&lt;/p&gt;

&lt;p&gt;最后大家可以看看这个小游戏，请使用手机二维码扫描。&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;alignnone size-full wp-image-413&quot; src=&quot;/assets/uploads/2014/09/qrcode.png&quot; alt=&quot;qrcode&quot; width=&quot;280&quot; height=&quot;280&quot; /&gt;&lt;/p&gt;

&lt;p&gt;源码地址：&lt;a href=&quot;https://github.com/hangzhou-JIMI/MemoryFruits&quot;&gt;https://github.com/hangzhou-JIMI/MemoryFruits&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;转载请注明出处：http://www.luzexi.com&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Git命令使用集合</title>
   <link href="http://www.luzexi.com/2014/09/12/Git%E5%91%BD%E4%BB%A4%E4%BD%BF%E7%94%A8%E9%9B%86%E5%90%88"/>
   <updated>2014-09-12T18:54:22+08:00</updated>
   <id>http://www.luzexi.com/2014/09/12/Git命令使用集合</id>
   <content type="html">&lt;p&gt;下面是我最近一直在使用的git命令。不知道能否为你提供点帮助。当然，我在github上做了很多好东西，全是开源的供你慢慢享用，如果觉得不错可以star(收藏),fllow(订阅)一下。等我空点了再把项目技术分享给你们哦。&lt;a href=&quot;https://github.com/luzexi&quot;&gt;https://github.com/luzexi&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;===&lt;/p&gt;

&lt;h4 id=&quot;clone&quot;&gt;clone:&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;	git clone path dir 或者 git clone path 名字可以省略默认使用该项目名
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;branch&quot;&gt;branch:&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;	git branch -r 查看所有分支

	git branch 查看当前获取的分支信息，在没有获取分支信息前，只会显示在哪个提交点。
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;checkout&quot;&gt;checkout:&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;	git checkout branch_name 切换某个分支

	git checkout -b branch_name 以当前分支未基础建立新分支并且切换过去

	git checkout -f 强制替换与分支不一样的文件

	git checkout path 撤销某个未提交文件恢复原状

	git checkout . 撤销所有未提交文件恢复原状

	git checkout --ours path  冲突中以当前分支未基准

	git checkout --theirs path 冲突中以对方分支未基准
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;add&quot;&gt;add:&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;	git add path 告诉git这个文件或文件夹加入到提交队列

	git add . 告诉git目录下文件和文件夹都加入到提交队列
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;rm&quot;&gt;rm:&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;	git rm path 告诉git这个文件或文件夹需要被删除

	git rm . 告诉git当前目录下所有文件和文件夹都需要被删除

	git rm `git status | grep delete | awk '{print $2}'` 删除所有被删除的文件
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;commit&quot;&gt;commit:&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;	git commit -m &quot;write something&quot; 提交当前被git add 或者 git rm 的内容

	git commit -a -m &quot;write something&quot; 提交所有被git跟踪的文件，-a表示所有
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;push&quot;&gt;push:&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;	git push origin branch_name 推送branch_name分支

	git push origin branch_name1:branch_name2 把当branch_name1支推送到新建一个分支branch_name2中去

	git push 推送所有本地修改过的分支到服务器
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;pull&quot;&gt;pull:&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;	git pull 下载当前分支内容

	git pull server branch_name 下载server服务器上的 branch_name分支，如果有冲突将自动合并
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;show&quot;&gt;show:&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;	git show commit_id 查看某次提交修改的内容
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;whatchanged&quot;&gt;whatchanged:&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;	git whatchanged filepath  查看某个文件的修改记录
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;clean&quot;&gt;clean:&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;	git clean -f 清除所有未被git跟踪的文件

	git clean -df 清除所有未被git跟踪的文件和文件夹
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;reset&quot;&gt;reset:&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;	git reset --hard commitID 回撤到某个提交点,文件也一同回到那个状态

	git reset --soft commitID  回撤到某个提交点，但文件仍然保持原来被修改后的状态。
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;fetch&quot;&gt;fetch:&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;	git fetch 这个我只知道抓取当前项目的所有信息主要实分支信息和提交记录，并不下载内容
&lt;/code&gt;&lt;/pre&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;h4 id=&quot;merge&quot;&gt;merge:&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;	git merge server branch_name 合并server项目上branch_name分支

	git merge branch_name 合并默认origin项目上的branch_name分支
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;diff&quot;&gt;diff:&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;	git diff file_name 比较文件与前一个版本的不同之处
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;remote&quot;&gt;remote:&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;	git remote add server_name git_path 将git_path以server_name命名，方便后面操作
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;submodule&quot;&gt;submodule:&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;	git submodule add git_address dir_name  加入其他模块仓库

	git submodule init  初始化模块仓库

	git submodule update  更新模块仓库
&lt;/code&gt;&lt;/pre&gt;

&lt;h4 id=&quot;log&quot;&gt;log:&lt;/h4&gt;

&lt;pre&gt;&lt;code&gt;	-p：按补丁显示每个更新间的差异

	--stat：显示每次更新的修改文件的统计信息

	--shortstat：只显示--stat中最后的行数添加修改删除统计

	--name-only：尽在已修改的提交信息后显示文件清单

	--name-status：显示新增、修改和删除的文件清单

	--abbrev-commit：仅显示SHA-1的前几个字符，而非所有的40个字符

	--relative-date：使用较短的相对时间显示（例如：&quot;two weeks ago&quot;）

	--graph：显示ASCII图形表示的分支合并历史

	--pretty：使用其他格式显示历史提交信息
&lt;/code&gt;&lt;/pre&gt;

</content>
 </entry>
 
 <entry>
   <title>Unity3D之对pomelo框架网络层改装</title>
   <link href="http://www.luzexi.com/2014/06/25/Unity3D%E4%B9%8B%E5%AF%B9pomelo%E6%A1%86%E6%9E%B6%E7%BD%91%E7%BB%9C%E5%B1%82%E6%94%B9%E8%A3%85"/>
   <updated>2014-06-25T14:27:25+08:00</updated>
   <id>http://www.luzexi.com/2014/06/25/Unity3D之对pomelo框架网络层改装</id>
   <content type="html">&lt;p&gt;最近了解了下网易的开源服务器框架pomelo，github地址：&lt;a href=&quot;https://github.com/NetEase/pomelo&quot;&gt;https://github.com/NetEase/pomelo&lt;/a&gt;发现其中它封装的u3d的网络层部分有线程安全问题，几乎不能直接u3d项目，所以对其进行了2次封装，让他可以真正用于u3d项目。&lt;/p&gt;

&lt;p&gt;封装后的源码也同样放在我的github上：&lt;a href=&quot;https://github.com/luzexi&quot;&gt;https://github.com/luzexi&lt;/a&gt; 供大家参考。&lt;/p&gt;

&lt;p&gt;下面写下pomelo的u3d网络层源码问题和我对源码的封装过程：
pomelo网络通信方式分:原始socket和websocket两种，这两种方式由项目需要而选择其中一种来使用。pomelo种对u3d网络通信封装的最主要问题是线程中逻辑句柄的调用，因为u3d对它本身主线程意外的线程调用本身api是非常排斥，当你在其他线程中运行u3d的api会直接被cut掉，所以我们需要将其调用游戏逻辑句柄的那部分放到u3d主线程中去。&lt;/p&gt;

&lt;p&gt;如何将线程通信后的游戏句柄调用部分放到u3d主线程中去：&lt;/p&gt;

&lt;p&gt;1.网络通信是由数据包为单位来驱动游戏逻辑句柄的，所以只要加入队列概念，在收到数据包时由原来的直接调用句柄，改为先推入到队列中，等待处理。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;#if LUZEXI
        private System.Object m_cLock = new System.Object();    //the lock object
        private const int PROCESS_NUM = 5;  //the process handle num per fps
        private Queue&amp;lt;byte[]&amp;gt; m_seqReceiveMsg = new Queue&amp;lt;byte[]&amp;gt;();    //the message queue
        private TranspotUpdate m_cUpdater = null;   //The Updater of the message queue
#endif
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;2.在u3d主逻辑中增加一个专门处理网络通信逻辑句柄的类。这个类在通信开始时就加入到u3d主逻辑中去，当通信结束(无论是正常还是异常结束)时在u3d主逻辑中销毁。在主逻辑中不断的或者说每帧都去查看网络通信层队列中有没有等待处理的句柄，有就取出来处理。这样就由等待队列串联起来了几个线程的共同合作。ps:对队列做防死锁操作&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;#if LUZEXI
        internal void Update()
        {
            for( int i = 0 ; i&amp;lt;PROCESS_NUM &amp;amp;&amp;amp; i&amp;lt; this.m_seqReceiveMsg.Count; i++)
            {
                lock(this.m_cLock)
                {
                    byte[] data = this.m_seqReceiveMsg.Dequeue();
                    this.messageProcesser.Invoke(data);
                }
            }
        }
#endif
&lt;/code&gt;&lt;/pre&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;3.拆分网络通信事件。将网络通信事件拆分为OnConnect , OnError , OnDisconnect , 这3个事件是最常见的通信事件。这3个事件也必须在主线程中调用，但这3个事件又不是以网络数据包为基础，所以需要在专门处理通信逻辑的类中加入3种状态start , error , disconnect.当网络通信层出现这三种事件时，将状态切过去（ps:而不是直接调用句柄）,然后再由专门处理通信逻辑的类在下一帧去调用对应的事件句柄。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;using UnityEngine;
using System;
using System.Collections.Generic;
using SimpleJson;

//  TranspotUpdate.cs
//  Author: Lu Zexi
//  2014-6-20
namespace Pomelo.DotNetClient
{
    /// &amp;lt;summary&amp;gt;
    /// Transpot updater.
    /// &amp;lt;/summary&amp;gt;
    public class TranspotUpdate : MonoBehaviour
    {
        private enum STATE
        {
            NONE = 0,
            START = 1,
            RUNING = 2,
            CLOSE = 3,
            DESTORY = 4,
        }
        private STATE m_eStat = STATE.NONE; //the state of the transpotUpdate
        private Action m_cUpdate;   //update action
        private List&amp;lt;Action&amp;lt;JsonObject&amp;gt;&amp;gt; m_cOnDisconnect;   //on the disconnect

        /// &amp;lt;summary&amp;gt;
        /// Init this instance.
        /// &amp;lt;/summary&amp;gt;
        internal static TranspotUpdate Init()
        {
            GameObject obj = new GameObject(&amp;lt;span class=&quot;string&quot;&amp;gt;&quot;Socket&quot;);
            TranspotUpdate trans = obj.AddComponent&amp;lt;TranspotUpdate&amp;gt;();
            return trans;
        }

        /// &amp;lt;summary&amp;gt;
        /// set the update of the process message action
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name=&quot;update&quot;&amp;gt;Update.&amp;lt;/param&amp;gt;
        internal void SetUpdate( Action update )
        {
            this.m_cUpdate = update;
        }

        /// &amp;lt;summary&amp;gt;
        /// set the disconnect evet.
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name=&quot;ondisconnect&quot;&amp;gt;Ondisconnect.&amp;lt;/param&amp;gt;
        internal void SetOndisconnect( List&amp;lt;Action&amp;lt;JsonObject&amp;gt;&amp;gt; ondisconnect )
        {
            this.m_cOnDisconnect = ondisconnect;
        }

        /// &amp;lt;summary&amp;gt;
        /// close the updater
        /// &amp;lt;/summary&amp;gt;
        internal void Close()
        {
            this.m_eStat = STATE.CLOSE;
        }

        /// &amp;lt;summary&amp;gt;
        /// Start this update.
        /// &amp;lt;/summary&amp;gt;
        internal void _Start()
        {
            this.m_eStat = STATE.START;
        }

        /// &amp;lt;summary&amp;gt;
        /// destory the gameobject.
        /// &amp;lt;/summary&amp;gt;
        internal void _Destory()
        {
            if(this.m_eStat != STATE.CLOSE)
                this.m_eStat = STATE.DESTORY;
        }

        /// &amp;lt;summary&amp;gt;
        /// Fixeds the update.
        /// &amp;lt;/summary&amp;gt;
        void FixedUpdate()
        {
            switch(this.m_eStat)
            {
            case STATE.START:
            case STATE.RUNING:
                if(this.m_cUpdate != null )
                {
                    this.m_cUpdate();
                }
                break;
            case STATE.CLOSE:
                if(this.m_cOnDisconnect != null )
                {
                    foreach(Action&amp;lt;JsonObject&amp;gt; action in this.m_cOnDisconnect)
                        action.Invoke(null);
                }
                this.m_cOnDisconnect = null;
                this.m_eStat = STATE.DESTORY;
                break;
            case STATE.DESTORY:
                this.m_eStat = STATE.NONE;
                GameObject.Destroy(gameObject);
                break;
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;转载请注明出处：http://www.luzexi.com&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Unity3D之如何将包大小减少到极致</title>
   <link href="http://www.luzexi.com/2014/06/06/Unity3D%E4%B9%8B%E5%A6%82%E4%BD%95%E5%B0%86%E5%8C%85%E5%A4%A7%E5%B0%8F%E5%87%8F%E5%B0%91%E5%88%B0%E6%9E%81%E8%87%B4"/>
   <updated>2014-06-06T18:38:46+08:00</updated>
   <id>http://www.luzexi.com/2014/06/06/Unity3D之如何将包大小减少到极致</id>
   <content type="html">&lt;p&gt;Unity3D之如何将包大小减少到极致,图片是游戏app里最最占空间的资源，所以请各位还没有理解u3d对图片文件存储方式理解的请看&lt;a href=&quot;/unity3d/前端技术/2014/05/21/Unity3D-Texture图片空间和内存占用分析.html&quot;&gt;《unity3d-texture图片空间和内存占用分析》&lt;/a&gt;。因为u3d对资源的压缩并不阐述的十分详细，所以很多项目在遇到包大小瓶颈时非常头疼。我也不是神仙，也同样痛苦过，但经历几个项目的折腾，最终能梳理出一套能将u3d包大小减少到合理范围的方法。&lt;/p&gt;

&lt;p&gt;首先来展示下，官方如何说的:（&lt;a href=&quot;http://docs.unity3d.com/Manual/ReducingFilesize.html&quot;&gt;http://docs.unity3d.com/Manual/ReducingFilesize.html&lt;/a&gt; 官方手册）&lt;/p&gt;

&lt;p&gt;1.替换jpg，使用psd，减少重复资源&lt;/p&gt;

&lt;p&gt;2.剔除不必要的资源&lt;/p&gt;

&lt;p&gt;3.打包时查看log纪录，由此判断需要减少的文件类型&lt;/p&gt;

&lt;p&gt;4.优化，压缩图片，减少图片大小&lt;/p&gt;

&lt;p&gt;5.优化，压缩网格和动画，减少文件大小&lt;/p&gt;

&lt;p&gt;6.剔除system.dll和system.xml.dll ,尽量不要依赖他们，或用其他组件来代替。&lt;/p&gt;

&lt;p&gt;这些官方解释对我们帮助甚少。&lt;/p&gt;

&lt;p&gt;为了将包大小减少到极致，我把包分成三段：1.首包(里面包含了最最必要的资源)。2.首次进入包加载(加载游戏运行必要的资源)。3.游戏运行中资源加载(按每个游戏不同各自定义，以场景和单位个体为主要，在画面进入时加载资源，加载结束后再运行并显示)。&lt;/p&gt;

&lt;p&gt;这个说的好笼统，来说的细点：&lt;/p&gt;

&lt;p&gt;大部分网络游戏都可以把资源分成包内资源和首次进游戏加载的资源。这样就解决了一部分资源放里面的问题。&lt;/p&gt;

&lt;p&gt;不是所有游戏都适合游戏进行中加载网络资源的，况且在中国网络环境这么糟糕的情况下。敢在游戏中加载网络资源的也就那些重度手游，而且是大厂的重度了。所以我们还需要想办法把前两种方法里的资源压缩到最小。&lt;/p&gt;

&lt;p&gt;因为首包内大部分资源都是ui资源，以及一些必要的场景资源。所以我们最主要是提取这些资源到外面并放服务器上去。&lt;/p&gt;

&lt;p&gt;一般包内资源操作都是阻塞式的，为了让游戏的架构没有太大的变化，我们还是使用阻塞加载方式，但方式稍微换一下，本来使用Resources.Load的接口，改用AssetBundle.CreateFromFile + AssetBundle.Load。把包内绝大部分ui以及其他资源打成一个assetbundle文件，并且使用的是非压缩模式。&lt;/p&gt;

&lt;p&gt;因为非压缩模式使得文件更大，我们还需要一步操作，就是将文件压缩成zip文件后上传服务器。&lt;/p&gt;

&lt;p&gt;我们将包外的资源压缩成zip文件。这样在加载网络资源时就能省去很多网络流量。下载下来后，对zip文件进行解压，后再对zip文件删除，减少空间占用。&lt;/p&gt;

&lt;p&gt;这样做以后是不是差不多所有资源都在包外了。而且基本上包内就只有程序和游戏首画面的资源了。30mb内应该也可以轻松达到。&lt;/p&gt;

&lt;p&gt;至于如何使用AssetBundle的这些接口，我对Assetbundle api做了一个封装。所有assetbundle的api都封装在里面。你自可以尽情使用。请看:&lt;a href=&quot;https://github.com/luzexi/Unity3DGameResource&quot;&gt;Unity3DGameResource&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;关于u3d的AssetBundle资源加载与打包封装，请查看&lt;a href=&quot;/unity3d/游戏通用模块/前端技术/2014/04/16/Unity3D之AssetBundle资源加载封装.html&quot;&gt;《unity3d之assetbundle资源加载封装》&lt;/a&gt;这篇文章。&lt;/p&gt;

&lt;p&gt;下面说说关于动态资源加载和资源导入内存的所有方式，这是优化资源的必要条件，只有你知道了所有的加载和导入途径你才能对项目资源优化进行全面的分析。知道了有哪几条路你才能选择一条最好的。如下：&lt;/p&gt;

&lt;p&gt;利用动态资源加载有几种方式：&lt;/p&gt;

&lt;p&gt;1.将资源放入assetstream文件夹随打包一起打入文件。&lt;/p&gt;

&lt;p&gt;2.将部分资源放在服务器，游戏前或游戏中进行加载，并保存到本地。&lt;/p&gt;

&lt;p&gt;3.将资源压缩成多个zip文件放在服务器，客户端下载这些zip文件进行解压。&lt;/p&gt;

&lt;p&gt;其实www.LoadFromCacheOrDownload加载机制也可以算一种，但似乎u3d4.5.2开始已经摒弃这个接口，大部分人也不太建议使用这个接口，似乎是因为这个界面消耗内存太厉害。其实具体什么原因我也不太清楚。个人认为这个接口太过死板，操作余地太小，受限太大。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;资源导入内存的方式有几种：&lt;/p&gt;

&lt;p&gt;1.从Resources文件夹中导入。这是阻塞加载最常见的方式，接口Resources.Load();&lt;/p&gt;

&lt;p&gt;2.从某地址加载资源www。这是异步加载最常见方式，接口new www(path);&lt;/p&gt;

&lt;p&gt;3.直接从文件或者bytes[]生成AssetBundl，接口：AssetBundle.CreateFromFile阻塞方法, AssetBundle.CreateFromMemory异步方法，AssetBundle.CreateFromMemoryImmediate阻塞方法&lt;/p&gt;

&lt;p&gt;4.使用File类加载文件，File.ReadAllBytes，File.ReadAllText。&lt;/p&gt;

&lt;p&gt;有了资源加载和内存导入后，还需要配备资源验证方式，这样整个项目动态加载体制才完善。资源版本更新方式可以使用json格式并以文件形式查看。验证方式的理解，还需要大家使用在项目里才能体会到真正的用途，才能将原理牢记于心。&lt;/p&gt;

&lt;p&gt;验证资源版本有几种：&lt;/p&gt;

&lt;p&gt;1.资源整体验证。步骤：1.客户端向服务器发送资源版本。2.服务器匹配最近版本号，若版本号不同，比较两个版本号之间的内容差异，发送差异资源地址。3.客户端删除差异内容中需要删除的文件资源，加载需要下载的文件资源并保存文件。最后保存版本号。&lt;/p&gt;

&lt;p&gt;2.单个资源文件的时间戳验证。步骤：1.客户端向服务器发送资源版本号。2.服务器匹配最新版本号，如果不同就重新发送一份资源版本列表，里面包含了资源文件的时间戳。3.保存版本列表。4.在客户端需要加载该资源时比较时间戳是否一致，如果不一样或这不存在就加载资源文件并保存固定时间戳。&lt;/p&gt;

&lt;p&gt;3.单个资源文件md5验证。步骤：1.客户端向服务器发送资源版本号。2.服务器匹配最新版本号，如果不同重新发送一分资源版本列表，里面包含了资源文件的md5码。3.客户端比较新旧资源版本列表中的，删除需要删除的和更新的资源。4.在客户端需要加载该资源时查看文件是否存在，如果存在就说明资源是最新的。如果不存在则需要实时下载。&lt;/p&gt;

&lt;p&gt;对于资源版本验证与更新我写了一个关于资源更新的解决方案&lt;a href=&quot;https://github.com/luzexi/version_update&quot;&gt;https://github.com/luzexi/version_update&lt;/a&gt; 是一个用php写的脚本，将资源进行记录，每次生成版本，对比资源是否有被修改，或删除，或增加，有兴趣可以看看。&lt;/p&gt;

&lt;p&gt;转发请注明出处:http://www.luzexi.com&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Unity3D--Texture图片空间和内存占用分析</title>
   <link href="http://www.luzexi.com/2014/05/21/Unity3D-Texture%E5%9B%BE%E7%89%87%E7%A9%BA%E9%97%B4%E5%92%8C%E5%86%85%E5%AD%98%E5%8D%A0%E7%94%A8%E5%88%86%E6%9E%90"/>
   <updated>2014-05-21T11:05:21+08:00</updated>
   <id>http://www.luzexi.com/2014/05/21/Unity3D-Texture图片空间和内存占用分析</id>
   <content type="html">&lt;p&gt;Texture图片空间和内存占用分析。由于U3D并没有很好的诠释对于图片的处理方式，所以很多人一直对于图集的大小和内存的占用情况都不了解。在此对于U3D的图片问题做一个实际数据的分析。此前的项目都会存在这样或者那样的打包后包大小与内存占用情况的问题，所以这次所以彻彻底底得分析下U3D对于Texture的处理方式。程序里的内存优化请参考&lt;a href=&quot;/unity3d/游戏架构/前端技术/2014/02/22/Unity3d优化之路.html&quot;&gt;《unity3d优化之路》&lt;/a&gt;。减少U3D包大小请参考&lt;a href=&quot;/unity3d/游戏架构/前端技术/2014/06/06/Unity3D之如何将包大小减少到极致.html&quot;&gt;《unity3d之如何将包大小减少到极致》&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;我打包多种类型的项目，空项目和10张放在Resources文件夹中的图为比较案例。以下是比较数据。&lt;/p&gt;

&lt;p&gt;IPHONE：&lt;/p&gt;

&lt;p&gt;1.空项目—-空间占用量42.3MB—-IPA大小10MB&lt;/p&gt;

&lt;p&gt;2.10张1200*520无压缩Texure 单张图占用量2.8MB—-空间占用量70.2MB—-IPA大小22.9MB&lt;/p&gt;

&lt;p&gt;3.10张1200&lt;em&gt;520压缩成1024&lt;/em&gt;1024PVRTC4 单张图占用量0.5MB—-空间占用量47.3MB—-IPA大小13.2MB&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;10张1024*1024无压缩Texture 单张图占用量4MB—-空间占用量82.3MB—-IPA大小14.6MB&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;5.10张1024*1024压缩为PVRTC4格式 单张图占用量0.5MB—-空间占用量47.3MB—-IPA大小11.6MB&lt;/p&gt;

&lt;p&gt;宗上数据总结：&lt;/p&gt;

&lt;p&gt;一、2的N次方大小的图片会得到引擎更大的支持，包括压缩比率，内存消耗，打包压缩大小，而且支持的力度非常大。&lt;/p&gt;

&lt;p&gt;二、减小图片的占用大小和内存方式有:图片大小变化(Maxsize),色彩位数变化(16位，32位)，压缩(PVRC)。&lt;/p&gt;

&lt;p&gt;三、U3D对于图片的格式是自己生成的，而并不是你给他什么格式，他就用什么格式，一张1024*1024图在无压缩格式下，它会被U3D以无压缩文件形式存放，也就是说U3D里的Texture Preview里显示的占用大小**MB不只是内存占用大小，还是空间占用大小。如下图所示：
&lt;img class=&quot;alignnone size-full wp-image-310&quot; src=&quot;/assets/uploads/2014/05/QQ截图20140521102030.png&quot; alt=&quot;QQ截图20140521102030&quot; width=&quot;626&quot; height=&quot;726&quot; /&gt;&lt;/p&gt;

&lt;p&gt;U3D的内部机制为自动生成图片类型来替换我们给的图片，在图片的压缩方式上需要进行谨慎的选择。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;压缩格式在U3D的&lt;a href=&quot;http://docs.unity3d.com/Documentation/Components/class-Texture2D.html&quot;&gt;Component Reference&lt;/a&gt;里有介绍我就不再详细介绍，只介绍几个重点的:&lt;/p&gt;

&lt;p&gt;RGBA32格式为无压缩最保真格式，但也是最浪费内存和空间的格式。Automatic Turecolor和它一个意思。&lt;/p&gt;

&lt;p&gt;RGBA16格式为无压缩16位格式，比32位节省一半的空间和内存。Automatic 16bits和它一个意思。&lt;/p&gt;

&lt;p&gt;RGBA Compressed PVRTC 4bits格式为PVRTC图片格式，它相当于把图片更改了压缩方式新生成了一个图片来替换原来的我们给的图片格式(比如我们给的是PNG格式)。&lt;/p&gt;

&lt;p&gt;注意：U3D所有图片的压缩格式都会以另一种方式来存储，不会以你给的方式来存储，只有你指定了某种格式，它才会转成你要的格式。而且压缩格式在Android里并不一定有效，因为Android的机型多，GPU的渲染方式也不一样，有的是Nvidia，有的是PowerVR，最最好的在安卓机子上启用RGBA16方式，因为这个是适应所有机型的，并且比32位占用量少一半，但也需要因项目而异，只是推荐使用的格式，可以多用。&lt;/p&gt;

&lt;p&gt;转载请注明出自：http://www.luzexi.com&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>WEB游戏服务器架构(HTTP)</title>
   <link href="http://www.luzexi.com/2014/04/18/WEB%E6%B8%B8%E6%88%8F%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%9E%B6%E6%9E%84(HTTP)"/>
   <updated>2014-04-18T11:13:33+08:00</updated>
   <id>http://www.luzexi.com/2014/04/18/WEB游戏服务器架构(HTTP)</id>
   <content type="html">&lt;p&gt;WEB游戏服务器架构(HTTP)。在如今游戏中，越来越多使用WEB架构方式来做游戏，《临兵斗者三国志》也是其中一个。我使用了PHP来做WEB服务器主逻辑语言，原因为他的开发速度快，并且容易招人容易上手。
接下来主要来介绍下WEB服务器游戏架构的方式。(本文以PHP+MYSQL+MEMCACHE+NGINX为例)&lt;/p&gt;

&lt;p&gt;PHP可以选用任何你熟悉的框架，能站在别人肩膀上的就站上去吧，自己写时间过长。我们选用的是Yii框架，特点不多没有刻意去挑选。Memcache主要存储数据表的内存，因为PHP没有内存可控制，所以结合Memchache是最好的选择，也可以用Redis，功能上都一样，不过Redis有排序而已。用NGINX主要用来做WEB端的负载平衡，也可以在测试时做IP过滤，以及维护时的IP封锁。&lt;/p&gt;

&lt;p&gt;WEB架构图：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;----------------------------------------------------------
GAME_DB1(Mysql-master&amp;amp;slave)---|
GAME_DB2(Mysql-master&amp;amp;slave)   |  
GAME_DB3(Mysql-master&amp;amp;slave)   |---------------|
GAME_DB4(Mysql-master&amp;amp;slave)---|               |
                                               |
Memcache1(master&amp;amp;slave)                        |
Memcache2(master&amp;amp;slave)                        |
Memcache3(master&amp;amp;slave)                        |
Memcache4(master&amp;amp;slvae)                        |
/       \                                      |
GameServerPHP1                                 |
GameServerPHP2---------------------------------|
GameServerPHP3  
|     /  
Nginx  
------------------------------------------  
GB_DB(Mysql)  
|  
GM_ADMIN_PHP+(memcache)  
------------------------------------------
&lt;/code&gt;&lt;/pre&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;描述：&lt;/p&gt;

&lt;p&gt;Ngix起负载均衡作用。&lt;/p&gt;

&lt;p&gt;Memcache起数据缓冲作用，当PHP从Memcache中读取数据发现Memcache没有，就从MYSQL里读并且存到Memcahe，查看Memcache玩家数据是否大于某个数(主要是限制内存，不能让Memcache内存无限增加最后导致奔溃)，当PHP需要更新和插入时，PHP先插入或更新到MYSQL，然后再插入或更新到Memcache，使得两边数据能够同步，抑或只插入或更新到MYSQL，在查询时再进行Memcache加载也可以。
Mysql和Memcache设置为多台，主要是用于当数据过大时的对数据库做的数据散列处理，就是将某块功能数据单独放在某个数据库上，抑或将主键以某种方式分段拆分，例如按每100万主键拆分Mysql,0-100万为1台Mysql,101-200万为另一台Mysql以此类推。&lt;/p&gt;

&lt;p&gt;关于PHP框架的改造，我们需要遵循框架的接口，这样我们才能对底层的框架进行改造并且不影响上层的逻辑。例如上面提到的当查询时，先对Memcache进行操作，查看是否有数据，如没有再对Mysql操作，这种写法的基本条件就是接口必须使用框架固有的，而不是直接写sql语句来执行，但凡只用使用sql语句的都需要被改成框架接口，并且尽量不使用合并操作和查询嵌套。&lt;/p&gt;

&lt;p&gt;关于数据统计，由于WEB没有逻辑线程，所以我使用Linux的任务或Windows的计划代替，每天执行一次某PHP脚本，每周执行一次某PHP脚本，以达到统计数据的要求。
这个框架是一般游戏服务器的架构，在即时上线时，因为前期人数并不多，服务器的台数可以自由调整，随着人数上线的增多可以动态得进行扩充GameServerPhp服务器的数量，Memcache服务器的数量，最后实在抗不住，也可以增加DB的数量。&lt;/p&gt;

&lt;p&gt;转载请注明出处：http://www.luzexi.com&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Unity3D之AssetBundle资源加载封装</title>
   <link href="http://www.luzexi.com/2014/04/16/Unity3D%E4%B9%8BAssetBundle%E8%B5%84%E6%BA%90%E5%8A%A0%E8%BD%BD%E5%B0%81%E8%A3%85"/>
   <updated>2014-04-16T21:07:53+08:00</updated>
   <id>http://www.luzexi.com/2014/04/16/Unity3D之AssetBundle资源加载封装</id>
   <content type="html">&lt;p&gt;Unity3D之AssetBundle资源加载封装。在《临兵斗者三国志》中我使用了U3D的AssetBundle资源动态加载机制，原因是某些画面资源太多，IO阻塞过慢会造成游戏奔溃。在开发过程中，遇到点问题:&lt;/p&gt;

&lt;p&gt;1.当资源更改变化时，如何能快速得反应到开发中。&lt;/p&gt;

&lt;p&gt;解决方案:&lt;/p&gt;

&lt;p&gt;我使用宏定义UNITY_EDITOR来判断是否是开发编辑状态。当处于开发编辑状态时，自动读取指定目录下U3D本身资源，而不使用AssetBundle。这样就达到了当prefb变化时能快速反应到开发编辑中。而当不是处于编辑状态时，则正常使用异步加载读取AssetBundle。这个方式唯一的毛病就是，必须让所有U3D程序员都非常清除明白，如果写错，编辑模式下会没问题，发布后会出问题，所以需要检查。&lt;/p&gt;

&lt;p&gt;2.当不同资源之间有重复的资源时如何将AssetBundle空间占有量最小化。&lt;/p&gt;

&lt;p&gt;解决方案:&lt;/p&gt;

&lt;p&gt;GUI资源之间有特别多的重复的问题，挑出几个重复得特别厉害的，比如ICON图集，公用图集。在打包期间把他们设为共享资源，并在加载时首先加载共享资源，这样既节省了AssetBundle空间占有量，也节省了内存。这个方式的毛病是当你将资源更改要打包某个资源时，需要将所有与共享有关的资源重新打包一遍。&lt;/p&gt;

&lt;p&gt;3.如何应对自动释放资源问题。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;解决方案:&lt;/p&gt;

&lt;p&gt;在游戏中有指定资源释放和自动释放所有AssetBundle资源以销毁内存(这里不是指销毁U3D内存，而是AssetBundle内存，U3D内存管理分图片内存，AssetBundle内存，编译程序)。销毁指定资源就按正常来没有争议。销毁所有资源就要有点措施了，因为有些资源是不能被销毁的，因为它们是共享资源，需要全程跟着游戏走，所以当自动销毁所有资源时，将共享资源排除在外。并且在销毁后调用Resources.UnloadUnusedAssets();和GC.Collect();&lt;/p&gt;

&lt;p&gt;4.打包AssetBundle方式。&lt;/p&gt;

&lt;p&gt;解决方案:&lt;/p&gt;

&lt;p&gt;打包AssetBundle方式有几种:1.单资源打包，也就是说一个.prefb或Texture打一个包。2.多个资源打包，将某些资源都打成一个AssetBundle，节省了几个资源包之间的共享资源也减小了多个AssetBundle引起的空间扩大问题。但并不是说所有项目都是多个资源打成一个AssetBundle是好的。《临兵斗者三国志》就是一大部分使用单一打包，而共享资源使用多个资源打成一个AssetBundle的方式。&lt;/p&gt;

&lt;p&gt;最后奉上本人对AssetBundle封装的源码。&lt;a href=&quot;https://github.com/luzexi/Unity3DGameResource&quot;&gt;https://github.com/luzexi/Unity3DGameResource&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;你也可以去我的&lt;a href=&quot;https://github.com/luzexi&quot;&gt;github&lt;/a&gt;上查看找我做的一些源码插件，如果喜欢的话可以star或者fllow。&lt;/p&gt;

&lt;p&gt;转发请注明出自：http://www.luzexi.com&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Unity3d优化之路</title>
   <link href="http://www.luzexi.com/2014/02/22/Unity3d%E4%BC%98%E5%8C%96%E4%B9%8B%E8%B7%AF"/>
   <updated>2014-02-22T19:43:01+08:00</updated>
   <id>http://www.luzexi.com/2014/02/22/Unity3d优化之路</id>
   <content type="html">&lt;p&gt;U3D的架构部分已经讲了很多了，这里我想讲讲对于U3D优化的亲身体验。从渲染优化，包体大小，再到内存，深入分析与解释如何进行游戏优化。&lt;/p&gt;

&lt;p&gt;优化之路分三块：&lt;/p&gt;

&lt;p&gt;一.渲染级别。&lt;/p&gt;

&lt;p&gt;GUI部分：我使用的是NGUI，它对动态移动、旋转、缩放GUI支持的是比较差的，所以我尽量不要把过多的移动旋转缩放的部分写在GUI中，但很多情况下是避免不了的，比如：大量的伤害数字，物品掉落，图标的移动和旋转等，为了不让GUI去控制这些渲染物体，一小部分我使用3D面片代替，而大部分使用程序去生成面片渲染脱离了GUI的控制。另外在那些静态的GUI中，我使用了静态物体优化的属性，加上排除不必要的GUI设置，使得GUI部分效率足够高。&lt;/p&gt;

&lt;p&gt;3D部分：&lt;/p&gt;

&lt;p&gt;1.特效是对画面效果最最有影响的部分，尽量少使用粒子或者将粒子的数量减少到最小。&lt;/p&gt;

&lt;p&gt;2.尽量减少灯光的使用，而使用烘培后的图来代替。但烘培贴图用多了内存就爆，所以还是要谨慎。&lt;/p&gt;

&lt;p&gt;3.使用静态批处理，将那些不动的物体设置为静态，以降低CPU。但unity说静态批处理会增加更多内存。这里也需要权衡。&lt;/p&gt;

&lt;p&gt;4.尽可能的将多个物体使用同一个材质球。这样可以有效降低drawcall。虽然在实际操作中比较困难，但还是需要做的。&lt;/p&gt;

&lt;p&gt;5.使用动态批处理。将多个不动的，使用相同材质球的物体合并mesh。这个更加有效得降低drawcall。&lt;/p&gt;

&lt;p&gt;6.尽量多的关闭的阴影渲染。这个会降低不少drawcall。减少对阴影的计算。&lt;/p&gt;

&lt;p&gt;2D部分：&lt;/p&gt;

&lt;p&gt;1.将同一页面或者同一类型的ui图合并成一个图集，减少渲染时的drawcall。&lt;/p&gt;

&lt;p&gt;2.将需要alpha和不需要alpha的图拆分开来做图集，不需要alpha的的图集可以大幅度的做压缩处理，因为这些图像压缩后看起来不会太糟糕。而有alpha的图集压缩会使得图像差别比较大。这样可以减少内存占用。&lt;/p&gt;

&lt;p&gt;3.在以上基础上，尽量将图集做压缩处理。只有那些需要高精度的图才不做压缩。这样做可以大幅度减少内存占用。&lt;/p&gt;

&lt;p&gt;二.减少占用空间大小。&lt;/p&gt;

&lt;p&gt;你可以参考这篇文章 &lt;a href=&quot;/unity3d/游戏架构/前端技术/2014/06/06/Unity3D之如何将包大小减少到极致.html&quot;&gt;《Unity3D之如何将包大小减少到极致》&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;1.我使用的是动态资源的加载方式WWW方式。所以在导出资源时我使用BuildAssetBundleOptions.DisableWriteTypeTree 的打包方式，减少包的大小。&lt;/p&gt;

&lt;p&gt;2.对每张Texture，都设置对图片进行压缩。&lt;/p&gt;

&lt;p&gt;3.让美术减少模型面片数，并在fbx模型设置中，设置对模型进行压缩&lt;/p&gt;

&lt;p&gt;4.让美术减少动画帧数，并在动画设置中，设置对动画进行压缩&lt;/p&gt;

&lt;p&gt;三.内存&lt;/p&gt;

&lt;p&gt;1.最基本的就是对需要的资源进行加载，使用完毕后，释放。&lt;/p&gt;

&lt;p&gt;2.GUI部分在GUIManager管理类中增加 对GUI进行定时检查的部分，对不展示的GUI资源进行释放。对于一些ui出现时的释放资源卡顿的问题，可以在ui出现后1秒再释放资源。其他策略可以自己想下，有效就好。&lt;/p&gt;

&lt;p&gt;3.因为有些时候一次性加载的资源过多，内存一下子会膨胀，IO过慢导致奔溃，所以我选择资源异步加载。使得加载这么多资源不那么可怕，并且平滑。&lt;/p&gt;

&lt;p&gt;4.内存释放：这里有个重点，我重点测试了一下内存释放的忽视点。在我们利用ngui,或者2dtoolkit进行gui编程时，在释放其gui节点时通常会忘记将gui引用置为null，这就导致了内存泄漏。一些不再使用的贴图或者实例数据仍然继续存留在内存中。对于是否有需要将这些gui变量置为null的说法，我还做了一个实验。&lt;/p&gt;

&lt;p&gt;首先将屏幕置空，没有任何多余内存，再将gui展示，接着隐藏gui销毁gameobject，先将所有引用变量置为null，然后再展示，再销毁，这次销毁，不将变量置为null。得出得内存结论就是，不将gui组件变量置为null的，贴图内存还会滞留。如下图：&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;alignnone size-full wp-image-355&quot; src=&quot;/assets/uploads/2014/02/48391BD3-BEAC-4E45-B6C4-17AA7B5B0428.png&quot; alt=&quot;48391BD3-BEAC-4E45-B6C4-17AA7B5B0428&quot; width=&quot;568&quot; height=&quot;211&quot; /&gt;&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;测试代码如下：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;using UnityEngine;
using System.Collections;

public class testgui
{
    private tk2dSprite tex;
    private GameObject root;

    private tk2dSprite tex1;
    private GameObject root1;

    private tk2dSprite tex2;
    private GameObject root2;

    public void show()
    {
        root =  GameObject.Instantiate( Resources.Load(&amp;lt;span class=&quot;string&quot;&amp;gt;&quot;GameObject&quot;) ) as GameObject;
        root.transform.parent = GameObject.Find(&amp;lt;span class=&quot;string&quot;&amp;gt;&quot;ROOT/MIDDLE_CENTER&quot;).transform;
        tex = root.GetComponent&amp;lt;tk2dSprite&amp;gt;();

        root1 =  GameObject.Instantiate( Resources.Load(&amp;lt;span class=&quot;string&quot;&amp;gt;&quot;GameObject1&quot;) ) as GameObject;
        root1.transform.parent = GameObject.Find(&amp;lt;span class=&quot;string&quot;&amp;gt;&quot;ROOT/MIDDLE_CENTER&quot;).transform;
        tex1 = root1.GetComponent&amp;lt;tk2dSprite&amp;gt;();

        root2 =  GameObject.Instantiate( Resources.Load(&amp;lt;span class=&quot;string&quot;&amp;gt;&quot;GameObject2&quot;) ) as GameObject;
        root2.transform.parent = GameObject.Find(&amp;lt;span class=&quot;string&quot;&amp;gt;&quot;ROOT/MIDDLE_CENTER&quot;).transform;
        tex2 = root2.GetComponent&amp;lt;tk2dSprite&amp;gt;();
    }

    public void hiden1()
    {
        GameObject.Destroy(root);
        GameObject.Destroy(root1);
        GameObject.Destroy(root2);
        Resources.UnloadUnusedAssets();
    }

    public void hiden()
    {
        GameObject.Destroy(root);
        GameObject.Destroy(root1);
        GameObject.Destroy(root2);
        root = null;
        root1 = null;
        root2 = null;
        tex = null;
        tex1 = null;
        tex2 = null;
        Resources.UnloadUnusedAssets();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;请看红框内绿色曲线，第一次是置空的情况，内存立刻销毁，而第二次是不置空的情况，内存仍然驻留。结论很明显了，所以，在你编程时，将引用置空是件非常重要的事，这会直接影响到你的内存使用量。这也事c#垃圾回收机制引起的，当实例没有引用数量时内存才会被回收，并且彻底销毁。&lt;/p&gt;

&lt;p&gt;笔者最后提醒优化无极限，其实都在细节中，能省一点CPU是一点，能省一点内存是一点。你不打败99.5%的其他人，你就没有机会功成名就。&lt;/p&gt;

&lt;p&gt;转载请注明出处:http://www.luzexi.com&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Unity3D-HTTP网络层封装</title>
   <link href="http://www.luzexi.com/2014/02/15/Unity3D-HTTP%E7%BD%91%E7%BB%9C%E5%B1%82%E5%B0%81%E8%A3%85"/>
   <updated>2014-02-15T15:59:57+08:00</updated>
   <id>http://www.luzexi.com/2014/02/15/Unity3D-HTTP网络层封装</id>
   <content type="html">&lt;p&gt;Unity3D-HTTP网络层封装。短连接的C#封装在这里做些分享。我把网络层封装成DLL在项目中使用，所以在设计时要将接口封装的很好。 我又对HTTP部分进行了改造不再使用DDL封装了（所以去除文章部分暂时用横线做标记），所有源码都以git的submodule形式作为项目的模块。源码会再文章的最后给出来。这次改装主要是针对程序员是否能快速理解，快速上手的方面来进行的。随着对HTTP网络层不断的理解加深，我将给出更加全面的HTTP源码模块，我还对本HTTP模块写了测试案例，你们可以看测试案例，进行使用。谢谢各位的关注，再次感谢不断得支持。&lt;/p&gt;

&lt;p&gt;一.首先说下网络层需要的一些接口，在游戏里需要用到的网络层都具有大部分的共同点，无论是TCP长连接还是HTTP短连接有部分区别，HTTP的网络事件要相对少一点。&lt;/p&gt;

&lt;p&gt;HTTP接口基本为：数据发送Send接口，网络错误事件，数据包回调执行句柄。HTTP网络层的数据包预发送接口被我去除，因为我回想，这个功能完全可以用模块外部的程序代替，而且可以针对每个界面来写，这样更加清晰，而且主要是这个功能障碍了程序员对程序的理解。&lt;/p&gt;

&lt;p&gt;二.说明下网络层中几个类的用途和作用。 HTTPSession类，是整个网络层的主类，承载了发送，接受，事件相应的事务。&lt;/p&gt;

&lt;p&gt;HTTPPacketRequest类，网络请求数据包基础类。每个请求数据包的最基础的数据，包含一个m_strAction地址变量，这个是因为每个HTTP请求的地址不同而设的变量，每个请求地址前缀都相同比如：http://luzexi.com/ ，而后缀有可能变化，比如author/regist/。所以这个变量要在你创建的时候在构建函数里进行设置。&lt;/p&gt;

&lt;p&gt;HTTPPacketAck类，网络回调数据包基础类。每个回调数据包的基础数据，每个数据包都含有几个基础的变量，我现在写的是code错误码 和 desc错误描述，你也可以改成其他你想要的，如果你理解整这段程序。总之，继承这个回调数据类的类中可以放置本次回调的数据，有数据回调时，系统将自动解析成这个类。&lt;/p&gt;

&lt;p&gt;HTTPSession类，会话类是网络层的主类，有发送接口和网络主地址，还有一个报错接口。是整个HTTP网络层的接入口。&lt;/p&gt;

&lt;p&gt;HttpDummySession类，我还做了虚拟会话，主要是给HTTP和单机切换用，有些游戏想做网络和单机一起兼顾，可以用HTTP虚拟类来实现，不需要改原来写好的HTTP请求，只要你对加个请求句柄处理就可以了。不过一般人不会用到。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;三.网络层最重要的是能够快速，方便使用，能够适应变化多端的需求改变。&lt;/p&gt;

&lt;p&gt;每个请求类（HTTPPacketRequest子类）和回调类（HTTPPacketAck子类）都是自动生成参数和自动映射回调类实例的，但千万记住，回调过来的数据和HTTPPacketAck子类里的变量名必须一致。&lt;/p&gt;

&lt;p&gt;四.关于HTTP的Head数据部分&lt;/p&gt;

&lt;p&gt;HEAD部分我已经写好了，但是一个统一的HEAD变量，在session类中你可以找到。因为head部分都是统一的参数变量，动的比较少，所以才这么做。在游戏项目里，可以用HEAD部分的数据也可以不用，因为完全可以用数据包的形式代替。&lt;/p&gt;

&lt;p&gt;最后奉上源码：&lt;a href=&quot;https://github.com/luzexi/Unity3DNetwork-http&quot;&gt;Unity3D-HTTP&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;测试案例：&lt;a href=&quot;https://github.com/luzexi/Unity3DNetwork-http-Test&quot;&gt;Unity3D-HTTP-Test&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;转载请注明出处:http://www.luzexi.com&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Unity3D AssetBundle 资源加载注意事项</title>
   <link href="http://www.luzexi.com/2014/02/15/Unity3D-AssetBundle-%E8%B5%84%E6%BA%90%E5%8A%A0%E8%BD%BD%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9"/>
   <updated>2014-02-15T14:47:57+08:00</updated>
   <id>http://www.luzexi.com/2014/02/15/Unity3D AssetBundle 资源加载注意事项</id>
   <content type="html">&lt;p&gt;Unity3D AssetBundle 资源加载注意事项.U3d资源打包以及加载出过很多问题，在这里把所有该注意的东西列出来分享下。&lt;/p&gt;

&lt;p&gt;1.由于Unity3D的自身的问题，在资源打包时会遗漏一些引用的资源，导致在游戏加载 AssetBundle 时没有问题，但在把资源实例化时出现资源找不到，或者资源缺少引用资源等问题。&lt;/p&gt;

&lt;p&gt;解决办法： 在打包获取资源时，分3个步骤做：1.创建空的临时的预置体Prefab 2.把要打包的资源实例化进场景 3.把实例化后的物体装进刚创建的空的预置体Prefab 4.最后再用该预置体打包成AssetBundle（注意多资源打包最好用BuildAssetBundleExplicitAssetNames接口，原先使用BuildAssetBundle出现过很多问题）&lt;/p&gt;

&lt;p&gt;2.WWW资源url加载AssetBundle时会遇到版本资源更新的问题。U3D自4.0后就不再有删除某缓存资源的接口，所以资源更新时遇到麻烦。&lt;/p&gt;

&lt;p&gt;解决办法：&lt;/p&gt;

&lt;p&gt;&lt;del&gt;1.客户端记录由服务器发过来的MD5码，倘若与本地存储的MD5码不同则增加自动增加一个版本号。这个办法方便但会积累很多的旧的无用的资源占用手机，导致用户有可能因为游戏占用太多硬盘资源而删除游戏。 2.客户端记录由服务器发过来的CRC校验码，用CRC校验码来判定当前资源是否需要重新加载，调用接口为static function LoadFromCacheOrDownload(url: string, version: int, crc: uint = 0): WWW; 这个办法可以解决旧资源删除问题，但最主要的问题是资源在打包后由于U3D的资源CRC校验码并不是普通的校验码，所以只能根据他的接口来判定，4.2以及4.2以前都没有获取CRC校验码的接口，所以每次打包资源后，都需要校对校验码。如何校对CRC校验码请自己去搜索资料。&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;2015-09-28: 我这里做一下修正，其实这个问题就是assetbundle版本更新解决方案。答案可以在这里找到&lt;a href=&quot;/unity3d/游戏架构/前端技术/2014/06/06/Unity3D之如何将包大小减少到极致.html&quot;&gt;Unity3D之如何将包大小减少到极致&lt;/a&gt;&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;3.如何知道哪些资源包需要加载以及加载地址和版本号。&lt;/p&gt;

&lt;p&gt;&lt;del&gt;解决办法： 1.使用XML，将所有需要加载的资源信息写入XML文件。每次游戏开始，先读取该文件。 2.由服务器的逻辑服务器提供需要加载的资源信息。&lt;/del&gt;&lt;/p&gt;

&lt;p&gt;在那1年后的我写了一个关于资源更新的解决方案&lt;a href=&quot;https://github.com/luzexi/version_update&quot;&gt;https://github.com/luzexi/version_update&lt;/a&gt; 是一个用php写的脚本，将资源进行记录，每次生成版本，对比资源是否有被修改，或删除，或增加，有兴趣可以看看。&lt;/p&gt;

&lt;p&gt;转载请注明出处：http://www.luzexi.com&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>游戏物品系统通用架构</title>
   <link href="http://www.luzexi.com/2013/12/31/%E6%B8%B8%E6%88%8F%E7%89%A9%E5%93%81%E7%B3%BB%E7%BB%9F%E9%80%9A%E7%94%A8%E6%9E%B6%E6%9E%84"/>
   <updated>2013-12-31T23:24:47+08:00</updated>
   <id>http://www.luzexi.com/2013/12/31/游戏物品系统通用架构</id>
   <content type="html">&lt;p&gt;游戏物品系统通用架构，物品系统是游戏中最最常用的功能之一，面对各种纷繁复杂的游戏物品系统，在具体项目中如何顺手拈来、驾驭自如？请容我一一到来。在内容前我有必要说明几点，所有内容都是基于我最大的架构思想之下，其中 客户端U3D架构思想的文章地址为 &lt;a href=&quot;/unity3d/游戏架构/前端技术//2013/10/29/Unity3D游戏架构.html&quot;&gt;Unity3D游戏架构&lt;/a&gt; ，以及服务器架构：&lt;a href=&quot;游戏服务端/游戏架构/后端技术/2013/08/24/王途霸业-战争策略游戏的服务器架构设计.html&quot;&gt;《王图霸业》战争策略游戏服务器架构&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt;直入主题&lt;/p&gt;

&lt;p&gt;首先，我们先归纳一下物品的基本属性。
    物品基本属性：名字，描述，数量，最大可叠加数，图标
    装备物品扩展属性：装备位置，攻击力，防御力，暴击率，魔力，血量，等等
    消耗物品扩展属性：BUFF增加，HP增加，最大HP增加，等等&lt;/p&gt;

&lt;p&gt;在游戏项目中，把这些物品的扩展属性，单个提取出来组合成各种游戏中需要的物品，如何做到？&lt;/p&gt;

&lt;p&gt;1.把所有属性都写进物品数据配置表中？数据表过大，导致最终项目人员更改数据缓慢，错误不断，甚至连写此系统的程序员自己也吃不消。&lt;/p&gt;

&lt;p&gt;2.把每个物品都按单各类进行特殊处理？假设物品有100种，那程序员就得写100各类实例，每增加一个写一个，浪费人力浪费时间。&lt;/p&gt;

&lt;p&gt;3.以物品基类的继承的形式，将各种物品分类处理，归类，派生？繁琐，分类时间过长，效率过低，程序员最终会为物品该归到哪一类和策划人员而打起来。&lt;/p&gt;

&lt;p&gt;如何才能做到物品系统适应不同的游戏需要，又能使得物品配置方便扩展性强？在这里我引入一个Action概念，Action以动作的概念来理解。当物品使用时，就调用不同的Action来达到不同的效果，例如：加血100加魔法200的物品，点击后，该物品执行加血100的Action实例，接着执行加魔200的实例，完毕后我们就能看到我们需要的效果了。所以物品只需要对应N各Action就可以完全做到你想做的，甚至可以做到你想不到的。&lt;/p&gt;

&lt;p&gt;我们能发现，其实物品的很多功能是可以用不同单个功能组合起来的，比如加血,比如加魔，比如加BUFF状态，把这些相同功能的Action都做成一个，然后把不同的Action做成一个集合是多么完美的事，既达到了数据持续化存储和更新能力，又可使得物品功能完全由策划人员自行配置，发挥他们的无限想象力。&lt;/p&gt;

&lt;p&gt;现在来看看实际中Action是如何编写的。把加血动作写成 类ActionAddHp，类中参数一个:HP，把加魔法动作写成 类ActionAddMP，类中参数一个：MP，这两个Action从数据表中读取相应的参数数据，变成众多Action实例里的一个，由ActionManager管理存储。到此回头看看，所有继承自Action类的扩展类拥有几个共同的属性：ID（唯一标识，用来区分所有Action），RunType（用来区分不同类型的Action），Read接口（用来读取数据），Excute接口（用来执行Action）。我在此之上加上一个Action类型，把所有Action拆分为条件类Action和执行类Action，条件类Action主要用来判断该物品的使用条件是否满足，执行类Action主要用来执行该动作的效果。因此Action基类就成了这样:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  

//  CAction.cs  
//  Author: Lu Zexi  
//  2013-12-24    
  
//base action  
public abstract class CAction : IAction  
{  
    private int m_iID;  //id  
  
    // id (0-99999) , actionType(0-99) , runType(0-99)  
    public CAction(int id, int actionType, int runType)  
    {  
        this.m_iID = actionType*10000000 + runType*100000 + id;  
    }  
  
    public abstract ActionError Excute(ActionInput input);  
  
    //roll back action  
    public virtual ActionError RollBack(ActionInput input)  
    {  
        return ActionError.NoError;  
    }  
  
    //get id  
    public int ID  
    {  
        get  
        {  
            return this.m_iID;  
        }  
    }  
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;有人会问，那如何让物品对应好几个Action呢？我再引入一个Action事件类，把它定义为类EventAction，他里面存储着16个Action ID，前8个ActionID是条件型动作，后8个ActionID为执行型动作。物品的数据中就有一个EventAction的ID，由这个EventAction来判断该执行那些Action，是否满足物品执行条件。当EventAction执行时，首先执行前8个Action，看看是否满足物品执行条件，如果不满足，则不执行后8个效果，如果满足再去执行后8个Action效果。因此EventAction也有很多各实例，由ID来区分不同的EventAction，由EventActionManager来管理所有事件Action。EventAction类结构：&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
  
  
//  EventAction.cs  
//  Author: Lu zexi  
//  2013-12-24  
  
  
  
/// &amp;lt;summary&amp;gt;  
/// Action event  
/// &amp;lt;/summary&amp;gt;  
public class EventAction  
{  
    private int m_iID; //ID  
    private int[] m_vecActions; //Action ID list  
    private int[] m_vecReqActions; //Require Action ID list  
  
    public EventAction()  
    {  
        this.m_vecActions = new int[8];  
        this.m_vecReqActions = new int[8];  
    }  
  
    // id (0-99999) , actionType(0-99) , runType(0-99)  
    public EventAction( int id , int eventType , int runType)  
    {  
        this.m_iID = eventType * 10000000 + runType*100000 + id;  
    }  
  
    //get id  
    public int ID  
    {  
        get  
        {  
            return this.m_iID;  
        }  
    }  
  
    //set action  
    public void SetAction( int[] act )  
    {  
        this.m_vecActions = act;  
    }  
  
    //set require  
    public void SetRequire( int[] req )  
    {  
        this.m_vecReqActions = req;  
    }  
  
    //excute action  
    public ActionError Excute( ActionInput input  )  
    {  
        for (int i = 0; i &amp;lt; this.m_vecActions.Length; i++)  
        {  
            CAction action = ActionManager.sInstance.GetAction(this.m_vecActions[i]);  
            if (action != null)  
            {  
                ActionError error = action.Excute(input);  
                if ( error != null &amp;amp;&amp;amp; error.code != ACTION_ERROR_CODE.NONE)  
                    return error;  
            }  
        }  
        return ActionError.NoError;  
    }  
  
    //action require  
    public ActionError Require( ActionInput input)  
    {  
        for (int i = 0; i &amp;lt; this.m_vecReqActions.Length; i++)  
        {  
            CAction action = ActionManager.sInstance.GetAction(this.m_vecReqActions[i]);  
            if (action != null)  
            {  
                ActionError error = action.Excute(input);  
                if ( error != null &amp;amp;&amp;amp; error.code != ACTION_ERROR_CODE.NONE)  
                    return error;  
            }  
        }  
        return ActionError.NoError;  
    }  
  
    //action roll back  
    public ActionError RollBack(ActionInput input)  
    {  
        for (int i = 0; i &amp;lt; this.m_vecActions.Length; i++)  
        {  
            CAction action = ActionManager.sInstance.GetAction(this.m_vecActions[i]);  
            if (action != null)  
            {  
                ActionError error = action.RollBack(input);  
                if (error != null &amp;amp;&amp;amp; error.code != ACTION_ERROR_CODE.NONE)  
                    return error;  
            }  
        }  
        return ActionError.NoError;  
    }  
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;以上描述解决了物品中消耗品的问题，物品素材本身就是没有功能的消耗品，在这里我就不介绍了。而下面，我们来看看如何解决物品装备。
装备物品在消耗品上多了几样关于英雄角色的属性，这些属性有的明，有的暗，那我们就把那些明的属性提取出来，例如加增攻击力，增加防御力。而那些暗的属性，再拆分两种，一种是直接写进明的配置表中而不显示在界面上，另一个是直接写进动作Action里，当需要时执行相应EventAction实例。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;最后，我们看看整个物品系统数据配置和系统源码。
物品类：名字，物品类型(消耗品，装备，素材)，描述，数量，最大叠加数量，使用等级,EventActionID,装备位置,攻击力，防御力，HP，MP
Action表: ID , 描述 , ActionType , RunType , 参数1，参数2...
EventAction表: ID,描述,
ReqActionID1,ReqActionID2,...ReqActionID8,
ActionID9,ActionID10,...ActionID16.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;源码：&lt;a href=&quot;https://github.com/luzexi/Unity3DItemSystem&quot;&gt;https://github.com/luzexi/Unity3DItemSystem&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;测试案例：&lt;a href=&quot;https://github.com/luzexi/Unity3DItemSystem-Test&quot;&gt;https://github.com/luzexi/Unity3DItemSystem-Test&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;转载请注明出处：http://www.luzexi.com&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Unity3D-深入剖析NGUI的游戏UI架构</title>
   <link href="http://www.luzexi.com/2013/12/14/Unity3D-%E6%B7%B1%E5%85%A5%E5%89%96%E6%9E%90NGUI%E7%9A%84%E6%B8%B8%E6%88%8FUI%E6%9E%B6%E6%9E%84"/>
   <updated>2013-12-14T13:00:54+08:00</updated>
   <id>http://www.luzexi.com/2013/12/14/Unity3D-深入剖析NGUI的游戏UI架构</id>
   <content type="html">&lt;p&gt;Unity3D-NGUI分析，使用NGUI做UI需要注意的几个要点在此我想罗列一下，对我在U3D上做UI的一些总结，最后解剖一下NGUI的源代码，它是如果架构和运作的。&lt;/p&gt;

&lt;p&gt;在此前我介绍了自己项目的架构方式，所以在NGUI的利用上也是同样的做法，UI逻辑的程序不被绑定在物体上。那么如何做到GUI输入消息的传递呢，答案是：我封装了一个关于NGUI输入消息的类，由于NGUI的输入消息传递方式是U3D中的SendMessage方式，所以在每个需要接入输入的物体上动态的绑定该封装脚本。在这个消息封装类中，加入消息传递的委托方法后，所有关于该物体的输入消息将通过封装类直接传递到方法上，再通过消息类型的识别就可以脱离传统脚本绑定的束缚了。&lt;/p&gt;

&lt;p&gt;在用NGUI制作UI时需要注意的几点：&lt;/p&gt;

&lt;p&gt;1.每个GUI以1各UIPanel为标准，过多的UIPanel首先会导致DrawCall的增多，其次是导致UI逻辑的混乱。&lt;/p&gt;

&lt;p&gt;2.UITexture不能使用的过于平凡，因为每个UITexture都会增加1各DrawCall，所以一般会作为背景图出现在UI上，小背景，大背景都可以。&lt;/p&gt;

&lt;p&gt;3.图集不宜过大，过大的图集，不要把很多个GUI都放在一个图集里，在UI显示时加载资源IO速度会非常慢。我尝试了各种方式来管理图集，例如每个GUI一个图集，大雨300*100宽度的图不做图集，抑或一个系统模块2个图集，甚至我有尝试过以整个游戏为单位划分公共图集，按钮图集，头像图集，问题图集，但这种方式最终以图集过大IO过慢而放弃，这些图集的管理方式都是应项目而适应的，并没有固定的方式，最主要是你怎么理解程序读取资源时的IO操作时间。&lt;/p&gt;

&lt;p&gt;4.在开发中，尽量用Free分辨率来测试项目的适配效果，不要到上线才发现适配问题。&lt;/p&gt;

&lt;p&gt;适配源码：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;float defaultWHRate = 800f / 480f;
float ScreenWHRate = (float)Screen.width / (float)Screen.height;
bool isUseHResize = defaultWHRate &amp;gt;= ScreenWHRate ? false : true;
UIRoot root = GameObject.Find(&quot;ROOT&quot;).GetComponent&amp;lt;UIRoot&amp;gt;();
if (!isUseHResize)
{
    float curScreenH = (float)Screen.width / defaultWHRate;
    float Hrate = curScreenH / Screen.height;
    root.manualHeight =(int)(480f / Hrate);
}
else
{
    root.manualHeight = 480;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;5.拆分以及固定各个锚点，上，左上，右上，中，左中，右中，下，左下，右下&lt;/p&gt;

&lt;p&gt;6.拆分GUI层级，层级越高，显示越靠前。层级的正确拆分能有效管理GUI的显示方式。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;/// &amp;lt;summary&amp;gt;
/// GUI层级
/// &amp;lt;/summary&amp;gt;
public enum GUILAYER
{
    GUI_BACKGROUND = 0, //背景层
    GUI_MENU,           //菜单层0
    GUI_MENU1,           //菜单层1
    GUI_PANEL,          //面板层
    GUI_PANEL1,         //面板1层
    GUI_PANEL2,         //面板2层
    GUI_PANEL3,         //面板3层
    GUI_FULL,           //满屏层
    GUI_MESSAGE,        //消息层
    GUI_MESSAGE1,        //消息层
    GUI_GUIDE,           //引导层
    GUI_LOADING,        //加载层
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;8.要充分的管理GUI，不然过多的GUI会导致内存加速增长，而每次都销毁不用的GUI则会让IO过于频繁降低运行速度。我的方法是找到两者间的中间态，给予隐藏的GUI一个缓冲带,当每次某各GUI进行隐藏时判断是否有需要销毁的GUI。或者也可以这么做，每时每刻去监控隐藏的GUI，哪些GUI内存时间驻留过长就销毁。关于内存优化问题，可以参考&lt;a href=&quot;/unity3d/前端技术/2014/05/21/Unity3D-Texture图片空间和内存占用分析.html&quot;&gt;《unity3d-texture图片空间和内存占用分析》&lt;/a&gt;和 &lt;a href=&quot;/unity3d/游戏架构/前端技术/2014/02/22/Unity3d优化之路.html&quot;&gt;《unity3d优化之路》&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;9.另外关于图标，像头像，物品，数量过多的，可以用打成几个图集，按一定规则进行排列，减小文件大小减少一次性读取的IO时间。&lt;/p&gt;

&lt;p&gt;10.尽量减少不必要的UI更改，NGUI一旦有UI进行更改，它就得重新绘制MESH和贴图，比起cocos2d耗得CPU大的多。&lt;/p&gt;

&lt;p&gt;11.如果可以不用动态字体就不要用动态字体，因为动态字体每次都会做IO操作读取相应的图片，这个是NGUI一个问题，费cpu，费内存。&lt;/p&gt;

&lt;p&gt;12.设置脚本执行次序，在U3D的Project setting-&amp;gt;Script Execution Order 中。由于NGUI以UIPanel为主要渲染入口，所以，所有关于游戏渲染处理的程序最好放在渲染之后，也就是UIPanel之后。UIPanel以LateUpdate为接口入口，所以关于渲染方面的程序还得斟酌是否方在LateUpdate里。&lt;/p&gt;

&lt;p&gt;13.NGUI对于动态的移动旋转等的UI操作支持性很差，当有这种操作过多的时候，会使得屏幕很卡。解决办法就是，自己用程序生成面片，面片的渲染不再受到NGUI的控制。&lt;/p&gt;

&lt;p&gt;以上是我能想起来的注意点，若有没想起来的，在以后的时间想到的也将补充进去。口无遮拦的说了这么多，不剖析一下源码怎么说的过去，之前对NGUI输入消息进行了封装，对2D动画序列帧进行了封装，却一直没能完整剖析它的底层源码，着实遗憾。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;NGUI中UIPanel是渲染的关键，他承载了在他下面的子物体的所有渲染工作，每个渲染元素都是由UIWidget继承而来，每个UI物体的渲染都是由面片、材质球、UV点组成，每个种材质由一个UIDrawCall完成渲染工作，UIDrawCall中自己创建Mesh和MeshRender来进行统一的渲染工作。这些都是对NGUI底层的简单的介绍，下面将进行更加细致的分析。&lt;/p&gt;

&lt;p&gt;首先我们来看UIWidget这个组件基类，从它拥有的类内部变量就能知道它承担得怎样的责任:&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;// Cached and saved values
[HideInInspector][SerializeField] protected Material mMat;//材质
[HideInInspector][SerializeField] protected Texture mTex;//贴图
[HideInInspector][SerializeField] Color mColor = Color.white;//颜色
[HideInInspector][SerializeField] Pivot mPivot = Pivot.Center;//对齐位置
[HideInInspector][SerializeField] int mDepth = 0;//深度
protected Transform mTrans;//坐标转换
protected UIPanel mPanel;//相应的UIPanel

protected bool mChanged = true;//是否更改
protected bool mPlayMode = true;//模式

Vector3 mDiffPos;//位置差异
Quaternion mDiffRot;//旋转差异
Vector3 mDiffScale;//缩放差异
int mVisibleFlag = -1;//可见标志

// Widget's generated geometry
UIGeometry mGeom = new UIGeometry();//多变形实例
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;UIWidget承担了存储显示内容，颜色调配，显示深度，显示位置，显示大小，显示角度，显示的多边形形状，归属哪个UIPanel。这就是UIWidget所要承担的内容，在UIWidget的所有子类中都具有以上相同的属性和任务。UIWidget和UIPanel的关系非常密切，因为UIPanel承担了UIWidget的所有渲染工作，而UIWidget只是承担了存储需要渲染数据。所以，在UIWidget在更换贴图，材质球，甚至更换UIPanel父节点时它会及时通知UIPanel说：”我更变配置了，你得重新获取我的渲染数据”。&lt;/p&gt;

&lt;p&gt;UIWidget中最重要的虚方法为 virtual public void OnFill(BetterList&lt;Vector3&gt; verts, BetterList&lt;Vector2&gt; uvs, BetterList&lt;Color32&gt; cols) { } 它是区分子类的显示内容的重要方法。它的工作就是填写如何显示，显示什么。&lt;/Color32&gt;&lt;/Vector2&gt;&lt;/Vector3&gt;&lt;/p&gt;

&lt;p&gt;UIWidget中在使用OnFill方法的重要的方法是 更新渲染多边型方法：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;public bool UpdateGeometry (ref Matrix4x4 worldToPanel, bool parentMoved, bool generateNormals)
{
  if (material == null) return false;

  if (OnUpdate() || mChanged)
  {
    mChanged = false;
    mGeom.Clear();
    OnFill(mGeom.verts, mGeom.uvs, mGeom.cols);

    if (mGeom.hasVertices)
    {
      Vector3 offset = pivotOffset;
      Vector2 scale = relativeSize;
      offset.x *= scale.x;
      offset.y *= scale.y;

      mGeom.ApplyOffset(offset);
      mGeom.ApplyTransform(worldToPanel * cachedTransform.localToWorldMatrix, generateNormals);
    }
    return true;
  }
  else if (mGeom.hasVertices &amp;amp;&amp;amp; parentMoved)
  {
    mGeom.ApplyTransform(worldToPanel * cachedTransform.localToWorldMatrix, generateNormals);
  }
  return false;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;它的作用就是，当需要重新组织多边型展示内容时，进行多边型的重新规划。&lt;/p&gt;

&lt;p&gt;接着，我们来看看UINode，这个类很容易被人忽视，而他的作用也很重要。它是在UIPanel被告知有新的UIWidget显示元素时被创建的，它的创建主要是为了监视被创建的UIWidget的位置，旋转，大小是否被更改，若被更改，将由UIPanel进行重新的渲染工作。
HasChanged这是UINode唯一重要的方法之一，它的作用就是被UIPanel用来监视每个元素是否改变了进而进行重新渲染。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;public bool HasChanged ()
{
#if UNITY_3 || UNITY_4_0
  bool isActive = NGUITools.GetActive(mGo) &amp;amp;&amp;amp; (widget == null || (widget.enabled &amp;amp;&amp;amp; widget.isVisible));

  if (lastActive != isActive || (isActive &amp;amp;&amp;amp;
  (lastPos != trans.localPosition ||
  lastRot != trans.localRotation ||
  lastScale != trans.localScale)))
  {
    lastActive = isActive;
    lastPos = trans.localPosition;
    lastRot = trans.localRotation;
    lastScale = trans.localScale;
    return true;
  }
#else
  if (widget != null &amp;amp;&amp;amp; widget.finalAlpha != mLastAlpha)
  {
    mLastAlpha = widget.finalAlpha;
    trans.hasChanged = false;
    return true;
  }
  else if (trans.hasChanged)
  {
    trans.hasChanged = false;
    return true;
  }
#endif
  return false;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;接着，来看UIDrawCall，它是被NGUI隐藏起来的类。他的内部变量来看看：&lt;/p&gt;

&lt;p&gt;Transform        mTrans;            //坐标转换类&lt;/p&gt;

&lt;p&gt;Material        mSharedMat;        // 渲染材质&lt;/p&gt;

&lt;p&gt;Mesh            mMesh0;            //首个MESH&lt;/p&gt;

&lt;p&gt;Mesh            mMesh1;            //用于更换的Mesh&lt;/p&gt;

&lt;p&gt;MeshFilter        mFilter;        //绘制的MeshFilter&lt;/p&gt;

&lt;p&gt;MeshRenderer    mRen;            //渲染MeshRender组件&lt;/p&gt;

&lt;p&gt;Clipping        mClipping;        //裁剪类型&lt;/p&gt;

&lt;p&gt;Vector4            mClipRange;        //裁剪范围&lt;/p&gt;

&lt;p&gt;Vector2            mClipSoft;        //裁剪缓冲方位&lt;/p&gt;

&lt;p&gt;Material        mMat;            //实例化材质&lt;/p&gt;

&lt;p&gt;int[]            mIndices;        //做为Mesh三角型索引点&lt;/p&gt;

&lt;p&gt;由这些内部变量可知，UIDrawCall是负责NGUI的最重要的渲染类。他制造Mesh制造Material，设置裁剪范围，为NGUI提供渲染底层。
他最重要的方法是：&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;public void Set (BetterList&amp;lt;Vector3&amp;gt; verts, BetterList&amp;lt;Vector3&amp;gt; norms, BetterList&amp;lt;Vector4&amp;gt; tans, BetterList&amp;lt;Vector2&amp;gt; uvs, BetterList&amp;lt;Color32&amp;gt; cols)
{
  int count = verts.size;

  // Safety check to ensure we get valid values
  if (count &amp;gt; 0 &amp;amp;&amp;amp; (count == uvs.size &amp;amp;&amp;amp; count == cols.size) &amp;amp;&amp;amp; (count % 4) == 0)
  {
    // Cache all components
    if (mFilter == null) mFilter = gameObject.GetComponent&amp;lt;MeshFilter&amp;gt;();
    if (mFilter == null) mFilter = gameObject.AddComponent&amp;lt;MeshFilter&amp;gt;();
    if (mRen == null) mRen = gameObject.GetComponent&amp;lt;MeshRenderer&amp;gt;();

    if (mRen == null)
    {
      mRen = gameObject.AddComponent&amp;lt;MeshRenderer&amp;gt;();
      #if UNITY_EDITOR
      mRen.enabled = isActive;
      #endif
      UpdateMaterials();
    }
    else if (mMat != null &amp;amp;&amp;amp; mMat.mainTexture != mSharedMat.mainTexture)
    {
      UpdateMaterials();
    }

    if (verts.size &amp;lt; 65000)
    {
      int indexCount = (count &amp;gt;&amp;gt; 1) * 3;
      bool rebuildIndices = (mIndices == null || mIndices.Length != indexCount);

      // Populate the index buffer
      if (rebuildIndices)
      {
        // It takes 6 indices to draw a quad of 4 vertices
        mIndices = new int[indexCount];
        int index = 0;

        for (int i = 0; i &amp;lt; count; i += 4)
        {
          mIndices[index++] = i;
          mIndices[index++] = i + 1;
          mIndices[index++] = i + 2;

          mIndices[index++] = i + 2;
          mIndices[index++] = i + 3;
          mIndices[index++] = i;
        }
      }

      // Set the mesh values
      Mesh mesh = GetMesh(ref rebuildIndices, verts.size);
      mesh.vertices = verts.ToArray();
      if (norms != null) mesh.normals = norms.ToArray();
      if (tans != null) mesh.tangents = tans.ToArray();
      mesh.uv = uvs.ToArray();
      mesh.colors32 = cols.ToArray();
      if (rebuildIndices) mesh.triangles = mIndices;
      mesh.RecalculateBounds();
      mFilter.mesh = mesh;
    }
    else
    {
      if (mFilter.mesh != null) mFilter.mesh.Clear();
      Debug.LogError(&quot;Too many vertices on one panel: &quot; + verts.size);
    }
  }
  else
  {
    if (mFilter.mesh != null) mFilter.mesh.Clear();
    Debug.LogError(&quot;UIWidgets must fill the buffer with 4 vertices per quad. Found &quot; + count);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;在这个方法里，它制造Mesh,MeshFilter,MeshRender,Materials。&lt;/p&gt;

&lt;p&gt;最后，我们来说说最重要的UI渲染入口UIPanel。
    UIPanel的渲染步骤：
    1.当有任何形式的UI组件启动渲染时加入UIPanel的渲染队列，当有新的渲染组件需要有新的UIDrawCall时，进行生成新的UIDrawCall.
    2.对所有UIPanel的渲染队列进行检查，是否队列中渲染组件需要重新渲染，包括位移，缩放，更改图片，启用，关闭.
    3.获取渲染组件对应的UIDrawCall，更新Mesh,贴图,UV，位置，大小
    4.对需要更新的UIDrawCall进行重新渲染
    5.最后标记已经渲染的渲染组件，告诉他们已经渲染，为下次判断更新做好准备。删除不再需要渲染的UIDrawCall，销毁渲染冗余。
    注意：所有的渲染都是在LateUpdate下进行，也就是它是进行的延迟渲染。&lt;/p&gt;

&lt;p&gt;接口源码：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;void LateUpdate ()
{
  // Only the very first panel should be doing the update logic
  if (list[0] != this) return;

  // Update all panels
  for (int i = 0; i &amp;lt; list.size; ++i)
  {
  UIPanel panel = list[i];
  panel.mUpdateTime = RealTime.time;
  panel.UpdateTransformMatrix();
  panel.UpdateLayers();
  panel.UpdateWidgets();
}

// Fill the draw calls for all of the changed materials
if (mFullRebuild)
{
  UIWidget.list.Sort(UIWidget.CompareFunc);
  Fill();
}
else
{
  for (int i = 0; i &amp;lt; UIDrawCall.list.size; )
  {
    UIDrawCall dc = UIDrawCall.list[i];

    if (dc.isDirty)
    {
      if (!Fill(dc))
      {
        DestroyDrawCall(dc, i);
        continue;
      }
    }
    ++i;
  }
}

  // Update the clipping rects
  for (int i = 0; i &amp;lt; list.size; ++i)
  {
    UIPanel panel = list[i];
    panel.UpdateDrawcalls();
  }
  mFullRebuild = false;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Fill()接口源码：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;/// &amp;lt;summary&amp;gt;
/// Fill the geometry fully, processing all widgets and re-creating all draw calls.
/// &amp;lt;/summary&amp;gt;
static void Fill ()
{
  for (int i = UIDrawCall.list.size; i &amp;gt; 0; )
  DestroyDrawCall(UIDrawCall.list[--i], i);

  int index = 0;
  UIPanel pan = null;
  Material mat = null;
  UIDrawCall dc = null;

  for (int i = 0; i &amp;lt; UIWidget.list.size; )
  {
    UIWidget w = UIWidget.list[i];

    if (w == null)
    {
      UIWidget.list.RemoveAt(i);
      continue;
    }

    if (w.isVisible &amp;amp;&amp;amp; w.hasVertices)
    {
      if (pan != w.panel || mat != w.material)
      {
        if (pan != null &amp;amp;&amp;amp; mat != null &amp;amp;&amp;amp; mVerts.size != 0)
        {
          pan.SubmitDrawCall(dc);
          dc = null;
        }

        pan = w.panel;
        mat = w.material;
      }

      if (pan != null &amp;amp;&amp;amp; mat != null)
      {
        if (dc == null) dc = pan.GetDrawCall(index++, mat);
        w.drawCall = dc;
        if (pan.generateNormals) w.WriteToBuffers(mVerts, mUvs, mCols, mNorms, mTans);
        else w.WriteToBuffers(mVerts, mUvs, mCols, null, null);
      }
    }
    else w.drawCall = null;
    ++i;
  }

  if (mVerts.size != 0)
  pan.SubmitDrawCall(dc);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;转载请注明出处：http://www.luzexi.com&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>Unity3D游戏架构</title>
   <link href="http://www.luzexi.com/2013/10/29/Unity3D%E6%B8%B8%E6%88%8F%E6%9E%B6%E6%9E%84"/>
   <updated>2013-10-29T15:32:51+08:00</updated>
   <id>http://www.luzexi.com/2013/10/29/Unity3D游戏架构</id>
   <content type="html">&lt;p&gt;Unity3D虽然是一款3D的引擎，但由于一部分开发者提供了GUI的插件，所以它也是可以用来开发2D游戏的，但我还需要改造一下。《王途霸业》就是完完全全在U3D上架构起来的2D游戏，在去年年底前完成这款游戏的架构底层后，开发速度直线上升，这也同样证明了我的这套架构的可行性。下面我将介绍如何用U3D和GUI插件架构一套2D游戏。&lt;/p&gt;

&lt;p&gt;《王途霸业》U3D项目中有个最大的特点就是不再有纷繁复杂的脚本绑定，整个游戏逻辑不再依赖众多的 MonoBehaviour ，所有GUI不存在脚本绑定一说。我这样做最主要的原因是要将美术，策划，程序的工作完全拆分开来，美术看不到程序，程序不需要管美术，策划更是不需要去顾及那些纷繁复杂的美术和程序专业技术。这样做最直接提升了游戏项目开发效率，用一句话代表这种架构意图：各顾各的，却能成事。&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;文章停在这里，我一时不知道如何表达这种架构，因为我想说的东西太多，介于我的书面表达能力，能清晰的表达清楚这种架构不太可能。那我就讲最最最重要的部分，我的心理想法和架构思路。
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;一.&lt;strong&gt;架构必须符合安全性&lt;/strong&gt;。关于项目安全性，很多人第一时间都会跟我说’在项目还没成型前，安全性并不重要’。确实当项目没有可看到的东西的情况下，这些所谓的资源都是没有价值的。但当项目看到有价值的时候，你再去做安全性还来得及吗，我能非常确认的说，那时你根本来不及做安全性的问题，即使你确实非常想推动这事，也会极其缓慢，而且在所有人眼里吃力不讨好，因为程序架构本身就决定了安全性的问题。C++项目里这问题并不十分严重，因为他不属于脚本语言，而U3D不同的是它全篇都是脚本。&lt;/p&gt;

&lt;p&gt;二.&lt;strong&gt;分离美术与程序的工作&lt;/strong&gt;。本架构的第一思想就是分离部门之间的工作。美术和程序之间有不可分割的交点，但项目必须保证程序不用去做美术的工作，不用去担心美术的问题，美术也不需要获得程序的工作内容，更不需要美术去学习程序。这最大限度保证了部门之间合作效率，也是我一直提倡的架构意图：各顾各的，却能成事。&lt;/p&gt;

&lt;p&gt;三.&lt;strong&gt;消除程序员代码上的交点&lt;/strong&gt;。架构本身就是解决程序员与程序员之间的问题。”为什么要把解决程序员与程序员的事做为重点问题?”：游戏项目的进度，技术起了主导作用，程序员的工作效率直接决定了整个项目的速度，所以如何让程序员保持良好的效率也就成为了项目程序架构需要解决和考虑的范畴之一了。程序员之间在编写代码的时候，都会有这样那样的交点，有些交点是不应该被其他人修改的或删除的，这些错综复杂的交点会使得整个项目随着时间的推进越来越混乱的局面，致使项目后期进度缓慢效率低下，优秀的程序架构本身就提前考虑了交点问题，将交点以拆分，消除，或合并成组件等形式融入到整体设计当中。如果一个人就能搞定整套程序那也用不着这么多架构思想了，怎么写都是一个人在工作，他可以用无数种方式来编写自己的程序。但事实并非如此，毕竟人脑是有极限的，再牛的人也抗不住错综复杂的逻辑关系和单调的重复劳动。一个优秀的架构能够支撑整个程序团队快速高效并且持续地往前推进，每个人都心情舒畅，工作愉快，富有激情，何乐而不为呢。&lt;/p&gt;

&lt;p&gt;四.&lt;strong&gt;分层结构设计全面可扩展&lt;/strong&gt;。架构最重要的部分之一就是能够适应变化不断的项目需求，优秀的架构能够尽量减少当需求变化而引起的程序上的修改或删除而引起的整体架构更改。项目的需求变化并不是由我们可以控制，在不断变化的情况下，甚至是当程序员已经适应当前架构的情况下，架构这块基石仍是稳如泰山。我在架构设计时最看重分层设计，每一层都有统一的意图，且只可被上一层调用，必要时以动态库形式存在。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;架构思想就谈到这里，接下来讲如何将这些架构思想融入到实际的Uniyt3D项目当中去。&lt;/p&gt;

&lt;p&gt;一.&lt;strong&gt;SVN与AssetServer结合控制版本&lt;/strong&gt;。这是分离美术与程序的第一步，也是保证安全性的重要手段。SVN更适合做代码版本控制，而AssetServer更适合做U3D的资源版本控制，两者相结合能够让项目管理更加出彩。&lt;/p&gt;

&lt;p&gt;二.&lt;strong&gt;整个架构只有一个入口点&lt;/strong&gt;。《王途霸业》中用只有GameClient类继承了MonoBehaviour，它也不再被其他类继承。它就是唯一一个入口点。各大系统，各大模块的初始化，逻辑更新，销毁，调用，都需要它的直接或者间接的调用。这就是为什么其他需要用到MonoBehaviour特性的程序不需要继承MonoBehaviour的原因。这是分离美术与程序的第二步，程序员不需要再去管理纷繁复杂的物体绑定脚本。&lt;/p&gt;

&lt;p&gt;三.&lt;strong&gt;动态资源生成与管理&lt;/strong&gt;。每个显示实例都由各系统独立承担生成，更新，销毁的管理，例如《王途霸业》中的GUI系统，每个GUI在展示时才进行资源生成，在隐藏时判断资源是否需要销毁，这不仅解决的动态资源控制问题也解决了内存控制问题。每个程序员只需要关注自己正在编写的GUI界面，而不需要估计其他GUI的资源问题。这是消除程序员交点的一个方式，也是分离美术与程序的工作。&lt;/p&gt;

&lt;p&gt;四.&lt;strong&gt;独立模块以DLL形式存在&lt;/strong&gt;。《王途霸业》中以DLL形式存在的独立模块有，2D动画系统模块、人物运动控制模块、基础模块、网络通信模块，GUI通用层模块。将《王途霸业》游戏分割为，网络层，基础层，专业领域层，数据层，安卓第三方SDK接口层，渲染层，工具层。渲染层又分为GUI层，动画层。做这些的层级的划分主要是为了能够架构成MVC模式。以MVC形式呈现的分层架构是一种效率比较高的架构，我在架构时尽量向此种架构靠拢，所以在项目后期确保了项目的安全性，也保证了程序部工作效率，架构越是到后期，发挥的作用越大。正所谓”人不可貌相，架构不可斗量”，架构的威力就是体现在更加宏观的层面上。&lt;/p&gt;

&lt;p&gt;五.&lt;strong&gt;尽量编写方便的工具&lt;/strong&gt;。游戏架构不仅需要游戏主逻辑本身的规范，还需要这些周边工具的支撑，有了这些工具能让项目进度快速提升，加上优秀的架构思想，项目完工指日可待。&lt;/p&gt;

&lt;p&gt;文笔过烂，持续更新中—-往各位见谅。&lt;/p&gt;

&lt;p&gt;&lt;img alt=&quot;2013051412202849588237.gif&quot; class=&quot;alignnone size-full wp-image-48&quot; height=&quot;480&quot; src=&quot;/assets/uploads/2013/08/2013051412202849588237.gif.jpg&quot; width=&quot;800&quot; /&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>游戏服务端优化</title>
   <link href="http://www.luzexi.com/2013/10/22/%E6%B8%B8%E6%88%8F%E6%9C%8D%E5%8A%A1%E7%AB%AF%E4%BC%98%E5%8C%96"/>
   <updated>2013-10-22T13:42:14+08:00</updated>
   <id>http://www.luzexi.com/2013/10/22/游戏服务端优化</id>
   <content type="html">&lt;p&gt;完成《王途霸业》后，慢慢地开始整理客户端，服务端的架构思路和优化思路。虽然1年来项目工作非常累，但我还是不想放弃这个整理资料，梳理知识的绝佳机会。废话不多说，直接开始游戏服务端优化思路。&lt;/p&gt;

&lt;p&gt;1.整体架构决定承载量。想要完全突破游戏用户承载量，你必须尽快即使调整自己服务器的架构，这个非常不容易，特别是在项目开发后期，在过多的代码积累的情况下，你很难对当前的架构进行调整。基本上，当时的情况都是，不动则已，一动一发不可收拾。架构调整主要是为了分担服务器的 游戏逻辑处理，数据存储，第三方接口处理。&lt;/p&gt;

&lt;p&gt;2.用户集中短时间使用的优化。比如帐号登录，帐号修改，角色获取，角色选择，角色创建等，在一个相对比较短的时间里完成的东西，又有可能会借助第三放接口的，需要进行统一管理和拆分，在用户量扩大时可以使用扩充服务器来对承载量扩充。一句话来表明：对于这种时间较短，用户会集中冲击承载量的，把它做成能扩充服务器就可以达到效果的架构，这种架构只要花钱就能解决。但凡能走上只要花钱就能解决承载量的架构，一定没有问题。&lt;/p&gt;

&lt;p&gt;3.大量用户集中使用同一功能的优化。《王途霸业》的挂机系统就遇到这样的情况，大部分玩家都会使用这个功能，而且这是机器与机器的交互，大部分玩家都会在长时间里使用这个功能。当大量用户时，这个挂机战斗功能，对数据存储服务器的SQL语句执行冲击很大，类似的情况还有很多。像这样的功能呢，就需要我们进一步优化，不但是优化逻辑处理，还要优化数据存储方式，不可以加大了逻辑服务器的承载，却忽视了数据服务器的承载。优化方法很多，比如当前功能不存储数据，只在玩家下线时操作数据存储更新，还有数据差异化存储，当数据有变化才进行储存更新，去除了很多不必要的存储SQL执行时间。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;4.HTTP与TCP结合的优化。《王途霸业》一开始尝试过只使用TCP长连接的架构方式，但最终以不方便，不好用的试用结果落败。TCP与WEB的HTTP结合的方式优化架构是个非常好的方式。首先WEB的HTTP本身就是个大众化稳定的架构体系，它在帐号管理，数据记录，需求变化的适应力等方面都有非常好的表现。其实就是人们口中所有的短连接和长连接的结合。《王途霸业》使用HTTP方式做了帐号认证系统是为了所有服务器的帐号获取方式，又用HTTP方式做了服务器列表，全服公告，网关轮询等做为中心服务器，这个中心服务器还配备了后台管理界面，让运营人员非常舒服，最后还用HTTP方式为《王途霸业》的数据统计做了一个统计管理的WEB后台管理，让市场人员能清晰得看到整个游戏数据的起伏变化情况。&lt;/p&gt;

&lt;p&gt;5.循环驱动更改为事件驱动。很多时候我们服务器里会有一些需要用逻辑帧循环来做的事情，《王途霸业》中就有很多，比如建筑时间完成判断，常胜峰战斗奖励判断，玩家当前资源量计算，盘丝洞挂机计算，这些程序逻辑倘若都用服务器的主逻辑帧来做，那将是个非常损耗CPU的事情，并且它还会影响到DB存储服务器的效率。这时候我们就要好好利用事件驱动，在玩家下线时，上线时，玩家获取信息时，更或者在玩家退出某个场景时，进行操作。这样不仅省去了主逻辑帧的循环CPU损耗，更让DB服务器有了更多的力量去处理更重要的事，大大提高了整个游戏服务器的承载量。&lt;/p&gt;

&lt;p&gt;PS:一点点小分享，大神切莫嘲笑。&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Unity3D架构设计NavMesh寻路</title>
   <link href="http://www.luzexi.com/2013/10/06/Unity3D%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1NavMesh%E5%AF%BB%E8%B7%AF"/>
   <updated>2013-10-06T18:04:28+08:00</updated>
   <id>http://www.luzexi.com/2013/10/06/Unity3D架构设计NavMesh寻路</id>
   <content type="html">&lt;p&gt;国庆闲来没事把NavMesh巩固一下。以Unity3D引擎为例写一个底层c# NavMesh寻路。因为Unity3D中本身自带的NavMesh寻路不能很好的融入到游戏项目当中，所以重写一个NavMesh寻路是个必经之路。NavMesh在很多游戏中应用广泛，不同种类的框架下NavMesh寻路发挥的淋漓尽致。与传统的A星寻路相比，NavMesh不仅减少了内存空间占有量，加快了寻路速度，还可以加入寻路角色的宽高限制，以及动态物体寻路等功能，基本上适应了大部分项目变化多端的需求。&lt;/p&gt;

&lt;p&gt;我把写NavMesh的过程分成好几个部分，一一进行描述：&lt;/p&gt;

&lt;p&gt;一.首先要理解NavMesh核心算法。NavMesh的核心算法就是用三角形代替传统寻路的方格，用计算拐点优化寻路路径来代替合并路径直线。
如下图1NavMesh寻路:
&lt;img class=&quot;alignnone wp-image-91 size-full&quot; src=&quot;/assets/uploads/2013/10/2303121.jpg&quot; alt=&quot;&quot; width=&quot;479&quot; height=&quot;312&quot; /&gt;&lt;/p&gt;

&lt;p&gt;以及如下图2传统的方格寻路:
&lt;img class=&quot;alignnone wp-image-94 size-full&quot; src=&quot;/assets/uploads/2013/10/20131006173445.jpg&quot; alt=&quot;&quot; width=&quot;621&quot; height=&quot;568&quot; /&gt;&lt;/p&gt;

&lt;p&gt;看到两者的差别了吧，NavMesh已三角形为寻路块，而传统以方格为寻路块。其实两者都使用A*寻路，但就是其网格生成不一样，导致当有大范围寻路时，其效率和要求也不一样。&lt;/p&gt;

&lt;p&gt;二.NavMesh寻路中的路径优化之拐点计算。其实NavMesh中比较常用的是&lt;strong&gt;光照射线法，&lt;/strong&gt;但这里不做详细介绍，光照射浅法详细内容地址:&lt;a href=&quot;http://www.cnblogs.com/neoragex2002/archive/2007/09/09/887556.html&quot;&gt;http://www.cnblogs.com/neoragex2002/archive/2007/09/09/887556.html&lt;/a&gt;
拐点计算优化路径就是到达目的地需要经过的一堆三角形中计算出最简洁的移动方式。其核心算法就是从当前点到另一个三角形中的点之间的线段，与这条线段相交的线段全部是路径所穿越的线段，就是拐点，把所有的拐点找出来，并得到一条最长的拐点，那个拐点就是最佳的拐点位置。&lt;/p&gt;

&lt;p&gt;三.NavMesh类设计详解(这里只设计2D的寻路，对于3D方向的寻路，其实是可以2D寻路代替的)：&lt;/p&gt;

&lt;p&gt;1.所有类都在同一的命名空间NavMesh内 namespace NavMesh
Triangle 三角形基础类
NavTriangle 寻路三角形类 (继承Triangle)
Line2D 线段类
Polygon 多边形类
Seeker 寻路主算法类&lt;/p&gt;

&lt;p&gt;—————————————– 让大家久等了 ————————————&lt;/p&gt;

&lt;p&gt;在寻路前，我们需要建立MESH三角形网格，这是NAV_MESH的重点之一。&lt;/p&gt;

&lt;p&gt;1.首先我们先要画出一个范围来确定我们的可行走范围。&lt;/p&gt;

&lt;p&gt;2.再在可行走范围中去添加不可行走的范围。&lt;/p&gt;

&lt;p&gt;3.我们用多个多边形Polygon代替以上的范围，也就是说，一个大的可行走Polygon内包含了若干个小的不可行走的Polygon。&lt;/p&gt;

&lt;p&gt;这是生成MESH前我们需要知道的生成范围，然后再由这些多边形的各个顶点来生成三角形网格,三角形网格的生成算法如下：&lt;/p&gt;

&lt;p&gt;Step 1 :  用可行走Polygon的任意一条边作为起点，将其推入堆栈列表。到Step2.&lt;/p&gt;

&lt;p&gt;Step 2:  从堆栈中推出一条边，在所有三角形中计算出边的DT点，构成约束Delaunay三角形，到Step3。如果没有DT点就重复做Step2，直到堆栈为空就结束整个程序。&lt;/p&gt;

&lt;p&gt;Step 3:  将所构成的三角形，另两边做如下处理：检查堆栈中是否已存在，如果存在就删除该边，如果不存在就加入到堆栈中。&lt;/p&gt;

&lt;p&gt;生成mesh后如图：（绿色的为多边形边框，蓝色的为寻路路径，红色的为编辑器选中的多边形）
&lt;img class=&quot;alignnone size-full wp-image-376&quot; src=&quot;/assets/uploads/2013/10/OD04RIKAWEMUTM_3MSBQ.jpg&quot; alt=&quot;OD04[RIKAWEMUTM_3MS}B$Q&quot; width=&quot;475&quot; height=&quot;312&quot; /&gt;&lt;/p&gt;

&lt;p&gt;核心源码为：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;        /// &amp;lt;summary&amp;gt;
        /// 创建导航网格
        /// &amp;lt;/summary&amp;gt;
        /// 所有阻挡区域&amp;lt;/param&amp;gt;
        /// 输出的导航网格&amp;lt;/param&amp;gt;
        /// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;
        public NavResCode CreateNavMesh(List&amp;lt;Polygon&amp;gt; polyAll , ref int id , int groupid , ref List&amp;lt;Triangle&amp;gt; triAll)
        {
            triAll.Clear();
            List&amp;lt;Line2D&amp;gt; allLines = new List&amp;lt;Line2D&amp;gt;(); //线段堆栈

            //Step1 保存顶点和边
            NavResCode initRes = InitData(polyAll);
            if (initRes != NavResCode.Success)
                return initRes;

            int lastNeighborId = -1;
            Triangle lastTri = null;

            //Step2.遍历边界边作为起点
            {
				Line2D sEdge = startEdge;
                allLines.Add(sEdge);
                Line2D edge = null;

                do
                {
                    //Step3.选出计算出边的DT点，构成约束Delaunay三角形
                    edge = allLines[allLines.Count - 1];
                    allLines.Remove(edge);

                    Vector2 dtPoint;
                    bool isFindDt = FindDT(edge, out dtPoint);
                    if (!isFindDt)
                        continue;
                    Line2D lAD = new Line2D(edge.GetStartPoint(), dtPoint);
                    Line2D lDB = new Line2D(dtPoint, edge.GetEndPoint());

                    //创建三角形
					Triangle delaunayTri = new Triangle(edge.GetStartPoint(), edge.GetEndPoint(), dtPoint, id++ , groupid);
                    // 保存邻居节点
                    // if (lastNeighborId != -1)
                    // {
                    // delaunayTri.SetNeighbor(lastNeighborId);
                    // if(lastTri != null)
                    // lastTri.SetNeighbor(delaunayTri.ID);
                    // }
                    //save result triangle
                    triAll.Add(delaunayTri);

                    // 保存上一次的id和三角形
                    lastNeighborId = delaunayTri.GetID();
                    lastTri = delaunayTri;

                    int lineIndex;
                    //Step4.检测刚创建的的线段ad,db；如果如果它们不是约束边
                    //并且在线段堆栈中，则将其删除，如果不在其中，那么将其放入
                    if (!Line2D.CheckLineIn(allEdges, lAD, out lineIndex))
                    {
                        if (!Line2D.CheckLineIn(allLines, lAD, out lineIndex))
                            allLines.Add(lAD);
                        else
                            allLines.RemoveAt(lineIndex);
                    }

                    if (!Line2D.CheckLineIn(allEdges, lDB, out lineIndex))
                    {
                        if (!Line2D.CheckLineIn(allLines, lDB, out lineIndex))
                            allLines.Add(lDB);
                        else
                            allLines.RemoveAt(lineIndex);
                    }

                    //Step5.如果堆栈不为空，则转到第Step3.否则结束循环
                } while (allLines.Count &amp;gt; 0);
            }

            // 计算邻接边和每边中点距离
            for (int i = 0; i &amp;lt; triAll.Count; i++)
            {
                Triangle tri = triAll[i];
                //// 计算每个三角形每边中点距离
                //tri.calcWallDistance();

                // 计算邻居边
                for (int j = 0; j &amp;lt; triAll.Count; j++)
                {
                    Triangle triNext = triAll[j];
                    if (tri.GetID() == triNext.GetID())
                        continue;

                    int result = tri.isNeighbor(triNext);
                    if (result != -1)
                    {
                        tri.SetNeighbor(result , triNext.GetID() );
                    }
                }
            }

            return NavResCode.Success;
        }
&lt;/code&gt;&lt;/pre&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;这里对如何计算DT点进行一个说明：&lt;/p&gt;

&lt;p&gt;Step1. 构造三角形的外接圆，以及外接圆的包围盒&lt;/p&gt;

&lt;p&gt;Step2. 依次访问网格包围盒内的每个网格单元：&lt;/p&gt;

&lt;p&gt;若某个网格单元中存在可见点 p, 并且 ∠p1pp2 &amp;gt; ∠p1p3p2，则令 p3=p，转Step1；&lt;/p&gt;

&lt;p&gt;否则，转Step3.&lt;/p&gt;

&lt;p&gt;Step3. 若当前网格包围盒内所有网格单元都已被处理完,也即C（p1，p2，p3）内无可见点，则 p3 为的 p1p2 的 DT 点.&lt;/p&gt;

&lt;p&gt;核心源码为：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;/// &amp;lt;summary&amp;gt;
/// 找到指定边的约束边DT
/// &amp;lt;/summary&amp;gt;
/// &amp;lt;param name=&quot;line&quot;&amp;gt;&amp;lt;/param&amp;gt;
/// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;
private bool FindDT(Line2D line, out Vector2 dtPoint)
{
    dtPoint = new Vector2();
    if (line == null)
        return false;

    Vector2 ptA = line.GetStartPoint();
    Vector2 ptB = line.GetEndPoint();

    List&amp;lt;Vector2&amp;gt; visiblePnts = new List&amp;lt;Vector2&amp;gt;();
    foreach (Vector2 point in allPoints)
    {
        if (IsPointVisibleOfLine(line, point))
            visiblePnts.Add(point);
    }

    if (visiblePnts.Count == 0)
        return false;

    bool bContinue = false;
    dtPoint = visiblePnts[0];

    do
    {
        bContinue = false;
        //Step1.构造三角形的外接圆，以及外接圆的包围盒
        Circle circle = NMath.CreateCircle(ptA, ptB, dtPoint);
        Rect boundBox = NMath.GetCircleBoundBox(circle);

        //Step2. 依次访问网格包围盒内的每个网格单元：
        //若某个网格单元中存在可见点 p, 并且 &amp;amp;ang;p1pp2 &amp;gt; &amp;amp;ang;p1p3p2，则令 p3=p，转Step1；
        //否则，转Step3.
        float angOld = (float)Math.Abs(NMath.LineRadian(ptA, dtPoint, ptB));
        foreach (Vector2 pnt in visiblePnts)
        {
            if (pnt == ptA || pnt == ptB || pnt == dtPoint)
                continue;
            if (!boundBox.Contains(pnt))
                continue;

            float angNew = (float)Math.Abs(NMath.LineRadian(ptA, pnt, ptB));
            if (angNew &amp;gt; angOld)
            {
                dtPoint = pnt;
                bContinue = true;
                break;
            }
        }

        //false 转Step3
    } while (bContinue);

    //Step3. 若当前网格包围盒内所有网格单元都已被处理完，
    // 也即C（p1，p2，p3）内无可见点，则 p3 为的 p1p2 的 DT 点
    return true;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;为了让各位能更容易读懂此文，此文仍会继续补充。&lt;/p&gt;

&lt;p&gt;现在我将所有源码都存放在了Github上，请各位跟随我到Github去取源码：&lt;a href=&quot;https://github.com/luzexi/Unity3DNavMesh&quot;&gt;https://github.com/luzexi/Unity3DNavMesh&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;转载请注明出处：http://www.luzexi.com&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>赌博游戏程序设计中如何运用数学概率论</title>
   <link href="http://www.luzexi.com/2013/09/04/%E8%B5%8C%E5%8D%9A%E6%B8%B8%E6%88%8F%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1%E4%B8%AD%E5%A6%82%E4%BD%95%E8%BF%90%E7%94%A8%E6%95%B0%E5%AD%A6%E6%A6%82%E7%8E%87%E8%AE%BA"/>
   <updated>2013-09-04T21:07:10+08:00</updated>
   <id>http://www.luzexi.com/2013/09/04/赌博游戏程序设计中如何运用数学概率论</id>
   <content type="html">&lt;p&gt;我们在《王途霸业》中加入了一个非常有特色的系统，叫做”通吃谷”，它是一个类似于街机中的水果机那样的游戏系统。每盘玩家可以押注8个元素中任意个数，按开始后，轮盘开始转动，一旦停下来，停在某个元素上，该元素就算中奖，奖励的倍数由所在的元素显示的倍数决定。这种类似的押宝游戏我一直想做，但一直没有机会，所以这个押宝系统一直对我笼罩着一层神秘感。随着研究的深入，这层神秘感渐渐消除。&lt;/p&gt;

&lt;p&gt;首先说说，如何将概率的准确性达到最高。内容在最下面的转载内容部分。请细细品尝。&lt;/p&gt;

&lt;p&gt;再着，先把大部分人认定的那些误区给去除，水果机并不是由算法支撑核心的，那是数学概率论的一部分。每个元素都有一个概率，准确定义这个概率，再加上一定的算法技巧，就会有一个可玩性，体验好，庄家必赢的赌博系统。&lt;/p&gt;

&lt;p&gt;首先给8个元素定义一个赔率，5倍，10倍，15倍，20倍，30倍，40倍，50倍，100倍。漫无目的的调整中奖概率是件很痛苦的事情，那我就用一个方法去定义，提取这些倍率的公倍数，在将他们各自的倍率去除以这个公倍数，得到一个基础的中奖概率。这样就得到一个总和加起来并不是百分之一百的概率体系。为什么要是总和不是100%的概率体系呢？我把剩余的未中奖的概率设定为吞吃比率，也就是在这些中奖概率后，有一个一定吞吃所有的概率，当这个吞吃概率被命中时，我们就需要用一定的算法来获得一个不一定不让玩家赢的中奖位置，这个中奖位置也可能是玩家选择的位置，也可以是玩家没有选择的赔率，为了让游戏体验更加的有趣，这个算法非常重要。&lt;/p&gt;

&lt;p&gt;其实整个游戏所用到的知识非常简单，而如何让玩家能有一个非常良好的体验，是从吞吃算法和概率调整上下手。我并没有去细致的调整概率，而是做了一个整体概率调整的方案，也就是一个浮点数调整所有概率，当浮点数为1时，是基础的概率，浮点数越大，各元素的中奖概率越大，玩家中奖机会也越大，再加上吞吃算法，整个游戏体验将会是非常切合人的本性。这样，既实现了游戏了娱乐性，也实现了庄家必赢的局面，最重要的是，整个游戏调整起来非常便捷，无需了解很多知识，这也是对项目灵活性的一个很大帮助。&lt;/p&gt;

&lt;p&gt;以下内容转载网络，由于页面不复存在，所以只能贴出来：
计算机随机模拟方法，是一种基于”随机数”的计算方法。这一方法源于美国在第二次世界大战中研制原子弹的”曼哈顿计划”。该计划的主持人之一、数学家冯诺伊曼用驰名世界的赌城-摩纳哥的Monte Carlo-来命名这种方法，为它蒙上了一层神秘色彩。Monte Carlo方法的基本思想很早以前就被人们所发现和利用。早在17世纪，人们就知道用事件发生的”频率”来决定事件的”概率”。19世纪人们用投针试验的方法来决定圆周率pi。本世纪40年代电子计算机的出现，特别是近年来高速电子计算机的出现，使得用数学方法在计算机上大量、快速地模拟这样的试验成为可能。考虑平面上的一个边长为1的正方形及其内部的一个形状不规则的”图形”，如何求出这个”图形”的面积呢？Monte Carlo方法是这样一种”随机化”的方法：向该正方形”随机地”投掷N个点,其中有M个点落于”图形”内，则该”图形”的面积近似为M/N。可用民意测验来作一个不严格的比喻。民意测验的人不是征询每一个登记选民的意见，而是通过对选民进行小规模的抽样调查来确定可能的优胜者。其基本思想是一样的。科技计算中的问题比这要复杂得多。比如金融衍生产品（期权、期货、掉期等）的定价及交易风险估算，问题的维数（即变量的个数）可能高达数百甚至数千。对这类问题，难度随维数的增加呈指数增长，这就是所谓的”维数的灾难”(Course Dimensionality)，传统的数值方法难以对付（即使使用速度最快的计算机）。Monte Carlo方法能很好地用来对付维数的灾难，因为该方法的计算复杂性不再依赖于维数。以前那些本来是无法计算的问题现在也能够计算量。为提高方法的效率，科学家们提出了许多所谓的”方差缩减”技巧。另一类形式与Monte Carlo方法相似，但理论基础不同的方法-“拟蒙特卡罗方法”(Quasi-Monte Carlo方法)-近年来也获得迅速发展。我国数学家华罗庚、王元提出的”华-王”方法即是其中的一例。这种方法的基本思想是”用确定性的超均匀分布序列(数学上称为Low Discrepancy Sequences)
代替Monte Carlo方法中的随机数序列。对某些问题该方法的实际速度一般可比Monte Carlo方法提出高数百倍，并可计算精确度。&lt;/p&gt;

&lt;p&gt;高精度概率事件，程序实现如下：&lt;/p&gt;

&lt;p&gt;设P(i)，其中i=1..n，为n个个体被选择的概率，在轮盘上表示为所占扇区的面积百分比，这里显然sum(P)=1。select用来保存n次选择的结果。&lt;/p&gt;

&lt;p&gt;1） 第一种实现办法：可以想象一个转动的轮盘，注意这里轮盘最多只转一圈。每次转轮盘前，把色子随机放到轮盘外缘的某处，即色子不随轮盘转动，以一个随机数sel代表它所处的位置。轮盘转动后，色子所指示的轮盘扇区号不断变化，轮盘停止时色子所指示的轮盘上扇区号，即为本次轮盘赌所选中的个体号。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;for i = 1:n   %第i次掷色子
    sel = rand; %产生一个0、1之间的随机数，代表色子在轮盘外缘所指示的位置
    sumPs = 0;   %轮盘初始转动的位置，从0变化到1
    j = 1;   %轮盘初始指示的位置
    while sumPs&amp;lt;sel   %终止条件为轮盘转动的位置超过色子位置
        sumPs = sumPs + P(j)   %轮盘转动
        j = j + 1；   %轮盘指示位置
    end
    select(i) = j-1;   %轮盘停止时色子停留位置所指示的个体
end   %循环终了，会对轮盘上由P所划分出来的n个区间产生n次随机选择，扇区越大，该扇区被选中的几率也越大
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;还需要注意的是：上面的程序中，我们当然可以把n改成2&lt;em&gt;n或者10&lt;/em&gt;n，产生的结果都是’个体概率所表示扇区越大，该个体被选中的几率也越大’，并且随着实验次数的增大，这一结果越精确。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;2）这种方法可以想象成往划分好扇区的轮盘里扔色子，事先生成一组满足均匀分布的随机数，代表n次掷色子或者n个色子一起扔，轮盘不动，色子所在区域为选择结果。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;r = rand(1,n)   %预先产生n个色子的位置，注意这里r服从0、1之间均匀分布
for i = 1:n   %第i次轮盘赌
    select(i) = n;   %本次轮盘赌的结果初始化为n
    for j = 1:n   %轮盘开始转动
        if r(j) &amp;lt;=P(i)   %若色子停在轮盘第j扇区
        select(i) = j; %则第i次轮盘赌的结果为j
        break;   %第i次轮盘赌结束
    end %~第i次轮盘赌结束
end %~第i次轮盘赌结束
end %n次轮盘赌结束
&lt;/code&gt;&lt;/pre&gt;

&lt;pre&gt;&lt;code class=&quot;language-c#&quot;&gt;%%%%%%%%%%%%%下面为完整的matlab程序实现%%%%%%%%%%%%%%%
function Select=Roulette(P,num)
按轮盘赌策略选择下一点,返回num次轮盘赌结果
第一种轮盘赌方法,精度很低,
 m = length(P);
 Select = zeros(1,num);
 for i=1:num
     Select(i) = m;% 初始化为最后一个
     for j=1:m %:按概率选择
       if P(j)&amp;gt;rand()
          Select(i)=j;
          break;
       end
     end
 end
第二种轮盘赌方法,精度较高
m = length(P);
Select = zeros(1,num);
r = rand(1,num);
for i=1:num
    sumP = 0;
    j = ceil(m*rand); %产生1~m之间的随机整数
    while sumP &amp;lt; r(i)
        sumP = sumP + P(mod(j-1,m)+1);
        j = j+1;
    end
Select(i) = mod(j-2,m)+1;
end
 本程序中轮盘赌方法的准确程度可由如下程序验证
 P=rand(10,1);
 P=P./sum(P);
 Select=Roulette(P,1e6);
 for i=1:10
     Ps(i)=(sum(Select==i)/1e6);
 end
最后验证该轮盘赌方法准确程度
比较P和Ps差异大小，例
&lt;/code&gt;&lt;/pre&gt;
</content>
 </entry>
 
 <entry>
   <title>《王途霸业》战争策略游戏的服务器架构设计</title>
   <link href="http://www.luzexi.com/2013/08/24/%E7%8E%8B%E9%80%94%E9%9C%B8%E4%B8%9A-%E6%88%98%E4%BA%89%E7%AD%96%E7%95%A5%E6%B8%B8%E6%88%8F%E7%9A%84%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1"/>
   <updated>2013-08-24T15:54:22+08:00</updated>
   <id>http://www.luzexi.com/2013/08/24/《王途霸业》战争策略游戏的服务器架构设计</id>
   <content type="html">&lt;p&gt;王途霸业的服务器架构设计从2012年11月份开始历时10个月从6月份的V1.0.0第一次于玩家见面，到现在V1.1.5版本，这10个月里《王途霸业》经历了很多次架构的调整，客户端和服务端都已经有成型的架构体系，特别是服务器的架构，似乎从一个无知的少年经过1年的时间成长为了一个成熟的白领，可以正式抗起整个家庭的重担。&lt;/p&gt;

&lt;p&gt;服务端：&lt;/p&gt;

&lt;p&gt;服务端整个架构由4个部分组成,分别是：数据缓冲服务器，逻辑服务器，认证服务器，中心服务器&lt;/p&gt;

&lt;p&gt;数据缓冲服务器：对MYSQL数据库从内存上的数据存储优化，将更大程度上优化数据读取和存储效率。&lt;/p&gt;

&lt;p&gt;逻辑服务器：不言而明，这是《王途霸业》的核心服务器，对《王途霸业》的游戏正常运行起到了关键性的作用&lt;/p&gt;

&lt;p&gt;认证服务器：《王途霸业》是一款面向多平台，多渠道的游戏。她的帐号系统面向《王途霸业》中的所有服务器。也就是说，你只要注册一个帐号，就可以登录任何一个服务器进行游戏。&lt;/p&gt;

&lt;p&gt;中心服务器：主要负责对《王途霸业》的服务器地址管理，版本更新和公告内容。她是整个游戏的入口点，就像个看门人，对所有进入游戏的手机终端，检查他们的版本是否最新，并给予当前的全服公告和现有服务器的状态。&lt;/p&gt;

&lt;p&gt;《王途霸业》中缓冲服务器有，DB缓冲服务器，日志缓冲服务器&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;这两个服务器分别分则对游戏数据存储和游戏日志数据存储。&lt;/p&gt;

&lt;p&gt;《王途霸业》中逻辑服务器有，GAME游戏服务器，LOGIN登录服务器&lt;/p&gt;

&lt;p&gt;游戏服务器对负责游戏的逻辑，而LOGIN服务器负责对帐号验证分流一部分HTTP访问造成的延迟。&lt;/p&gt;

&lt;p&gt;《王途霸业》中认证服务器主要负责对所有平台的帐号进行验证，并提供帐号所对应的ID，这里启用HTTP连接，短连接的好处就是不必担心个服务器之间的长连接中断问题，并且能够承载更多的用户，统一所有服务器的帐号。&lt;/p&gt;

&lt;p&gt;《王途霸业》中的中心服务器是HTTP方式，也是因为这样能更好的承载客户端入口点，快速让客户端获得最新的更新内容和最新的服务器列表状态。将入口功能集成于一身。&lt;/p&gt;

&lt;p&gt;&lt;img alt=&quot;ddd&quot; class=&quot;alignnone size-full wp-image-56&quot; height=&quot;761&quot; src=&quot;/assets/uploads/2013/08/ddd.png&quot; width=&quot;1035&quot; /&gt;&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>使用行为树(Behavior Tree)实现游戏AI</title>
   <link href="http://www.luzexi.com/2013/01/26/%E4%BD%BF%E7%94%A8%E8%A1%8C%E4%B8%BA%E6%A0%91(Behavior-Tree)%E5%AE%9E%E7%8E%B0%E6%B8%B8%E6%88%8FAI"/>
   <updated>2013-01-26T19:09:33+08:00</updated>
   <id>http://www.luzexi.com/2013/01/26/使用行为树(Behavior Tree)实现游戏AI</id>
   <content type="html">&lt;p&gt;使用行为树(Behavior Tree)实现游戏AI
by AKara 2010-12-09 @ http://blog.csdn.net/akara @ akaras@163.com
=============================================================
谈到游戏AI，很明显智能体拥有的知识条目越多，便显得更智能，但维护庞大数量的知识条目是个噩梦：使用有限状态机(FSM)，分层有限状态机(HFSM)，
决策树(Decision Tree)来实现游戏AI总有那么些不顺意。试试Next-Gen AI的行为树(Behavior Tree)吧。虽说Next-Gen AI，但距其原型提出已有约10年时间，而微软Halo系列估计已用了超过8年了，Spore和一些著名游戏也早已使用行为树作为它们的AI结构。如从未接触，那wikipedia(http://en.wikipedia.org/wiki/Behavior_Trees) 绝对是入门好资料。&lt;/p&gt;

&lt;p&gt;先贴本文最具价值图(配色可花了不少时间)&lt;/p&gt;

&lt;p&gt;&lt;img class=&quot;alignnone size-full wp-image-20&quot; src=&quot;/assets/uploads/2013/01/213345_1292157838h3CY.jpg&quot; alt=&quot;213345_1292157838h3CY&quot; width=&quot;444&quot; height=&quot;768&quot; /&gt;&lt;/p&gt;

&lt;p&gt;为显美观：BT被横放，Node层次被刻意减少，Dec被刻意安插，Cond被刻意捏造。&lt;/p&gt;

&lt;p&gt;PS：其实真正的高效的Node Group剔除应多加一层Sequence Node。&lt;/p&gt;

&lt;p&gt;行为树(Behavior Tree)具有如下的特性：&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;它只有4大类型的Node：&lt;/li&gt;
  &lt;li&gt;Composite Node&lt;/li&gt;
  &lt;li&gt;Decorator Node&lt;/li&gt;
  &lt;li&gt;Condition Node&lt;/li&gt;
  &lt;li&gt;Action Node&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;任何Node被执行后，必须向其Parent Node报告执行结果：成功 / 失败.&lt;/p&gt;

&lt;p&gt;这简单的成功 / 失败汇报原则被很巧妙地用于控制整棵树的决策方向。&lt;/p&gt;

&lt;p&gt;先看Composite Node，其实它按复合性质还可以细分为3种：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Selector Node&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当执行本类型Node时，它将从begin到end迭代执行自己的Child Node：&lt;/p&gt;

&lt;p&gt;如遇到一个Child Node执行后返回True，那停止迭代，&lt;/p&gt;

&lt;p&gt;本Node向自己的Parent Node也返回True；否则所有Child Node都返回False，&lt;/p&gt;

&lt;p&gt;那本Node向自己的Parent Node返回False。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Sequence Node&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;当执行本类型Node时，它将从begin到end迭代执行自己的Child Node：&lt;/p&gt;

&lt;p&gt;如遇到一个Child Node执行后返回False，那停止迭代，&lt;/p&gt;

&lt;p&gt;本Node向自己的Parent Node也返回False；否则所有Child Node都返回True，&lt;/p&gt;

&lt;p&gt;那本Node向自己的Parent Node返回True。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Parallel Node&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;并发执行它的所有Child Node。&lt;/p&gt;

&lt;p&gt;而向Parent Node返回的值和Parallel Node所采取的具体策略相关：&lt;/p&gt;

&lt;p&gt;Parallel Selector Node: 一False则返回False，全True才返回True。&lt;/p&gt;

&lt;p&gt;Parallel Sequence Node: 一True则返回True，全False才返回False。&lt;/p&gt;

&lt;p&gt;Parallel Hybird Node: 指定数量的Child Node返回True或False后才决定结果。&lt;/p&gt;

&lt;p&gt;Parallel Node提供了并发，提高性能。&lt;/p&gt;

&lt;p&gt;不需要像Selector/Sequence那样预判哪个Child Node应摆前，哪个应摆后，&lt;/p&gt;

&lt;p&gt;常见情况是：&lt;/p&gt;

&lt;p&gt;(1)用于并行多棵Action子树。&lt;/p&gt;

&lt;p&gt;(2)在Parallel Node下挂一棵子树，并挂上多个Condition Node，&lt;/p&gt;

&lt;p&gt;以提供实时性和性能。&lt;/p&gt;

&lt;p&gt;Parallel Node增加性能和方便性的同时，也增加实现和维护复杂度。&lt;/p&gt;

&lt;p&gt;PS：上面的Selector/Sequence准确来说是Liner Selector/Liner Sequence。&lt;/p&gt;

&lt;p&gt;AI术语中称为strictly-order：按既定先后顺序迭代。&lt;/p&gt;

&lt;p&gt;Selector和Sequence可以进一步提供非线性迭代的加权随机变种。&lt;/p&gt;

&lt;p&gt;Weight Random Selector提供每次执行不同的First True Child Node的可能。&lt;/p&gt;

&lt;p&gt;Weight Random Sequence则提供每次不同的迭代顺序。&lt;/p&gt;

&lt;p&gt;AI术语中称为partial-order，能使AI避免总出现可预期的结果。&lt;/p&gt;

&lt;p&gt;=============================================================&lt;/p&gt;

&lt;p&gt;再看Decorator Node，它的功能正如它的字面意思：它将它的Child Node执行&lt;/p&gt;

&lt;p&gt;后返回的结果值做额外处理后，再返回给它的Parent Node。很有些AOP的味道。&lt;/p&gt;

&lt;p&gt;比如Decorator Not/Decorator FailUtil/Decorator Counter/Decorator Time…&lt;/p&gt;

&lt;p&gt;更geek的有Decorator Log/Decorator Ani/Decorator Nothing…&lt;/p&gt;

&lt;p&gt;====================================================================&lt;/p&gt;

&lt;p&gt;然后是很直白的Condition Node，它仅当满足Condition时返回True。&lt;/p&gt;

&lt;p&gt;====================================================================&lt;/p&gt;

&lt;p&gt;最后看Action Node，它完成具体的一次(或一个step)的行为，视需求返回值。&lt;/p&gt;

&lt;p&gt;而当行为需要分step/Node间进行时，可引入Blackboard进行简单数据交互。&lt;/p&gt;

&lt;p&gt;====================================================================&lt;/p&gt;

&lt;p&gt;整棵行为树中，只有Condition Node和Action Node才能成为Leaf Node，而也&lt;/p&gt;

&lt;p&gt;只有Leaf Node才是需要特别定制的Node；Composite Node和Decorator Node均&lt;/p&gt;

&lt;p&gt;用于控制行为树中的决策走向。(所以有些资料中也统称Condition Node和Action&lt;/p&gt;

&lt;p&gt;Node为Behavior Node，而Composite Node和Decorator Node为Decider Node。)&lt;/p&gt;

&lt;p&gt;更强大的是可以加入Stimulus和Impulse，通过Precondition来判断masks开关。&lt;/p&gt;

&lt;p&gt;通过上述的各种Nodes几乎可以实现所有的决策控制：if, while, and, or,&lt;/p&gt;

&lt;p&gt;not, counter, time, random, weight random, util…&lt;/p&gt;

&lt;p&gt;====================================================================&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;总的来说，行为树具有如下几种优点，确实是实现AI框架的利器，甚至是一种&lt;/p&gt;

&lt;p&gt;通用的可维护的复杂流程管理利器：&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;静态性&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;越复杂的功能越需要简单的基础，否则最后连自己都玩不过来。&lt;/p&gt;

&lt;p&gt;静态是使用行为树需要非常着重的一个要点：即使系统需要某些”动态”性。&lt;/p&gt;

&lt;p&gt;其实诸如Stimulus这类动态安插的Node看似强大，&lt;/p&gt;

&lt;p&gt;但却破坏了本来易于理解的静态性，弊大于利。&lt;/p&gt;

&lt;p&gt;Halo3相对于Halo2对BT AI的一个改进就是去除Stimulus的动态性。&lt;/p&gt;

&lt;p&gt;取而代之的做法是使用Behavior Masks，Encounter Attitude，Inhibitions。&lt;/p&gt;

&lt;p&gt;原则就是保持全部Node静态，只是根据事件和环境来检查是否启用Node。&lt;/p&gt;

&lt;p&gt;静态性直接带来的好处就是整棵树的规划无需再运行时动态调整，为很多优化&lt;/p&gt;

&lt;p&gt;和预编辑都带来方便。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;直观性&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;行为树可以方便地把复杂的AI知识条目组织得非常直观。&lt;/p&gt;

&lt;p&gt;默认的Composite Node的从begin往end的Child Node迭代方式就像是处理一个&lt;/p&gt;

&lt;p&gt;预设优先策略队列，也非常符合人类的正常思考模式：先最优再次优。&lt;/p&gt;

&lt;p&gt;行为树编辑器对优秀的程序员来说也是唾手可得。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;复用性&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;各种Node，包括Leaf Node，可复用性都极高。&lt;/p&gt;

&lt;p&gt;实现NPC AI的个性区别甚至可以通过在一棵共用的行为树上不同的位置来&lt;/p&gt;

&lt;p&gt;安插Impulse来达到目的。&lt;/p&gt;

&lt;p&gt;当然，当NPC需要一个完全不同的大脑，比如70级大BOSS，&lt;/p&gt;

&lt;p&gt;与其绞尽脑汁在一棵公用BT安插Impulse，不如重头设计一棵专属BT。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;扩展性&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;虽然上述Node之间的组合和搭配使用几乎覆盖所有AI需求。&lt;/p&gt;

&lt;p&gt;但也可以容易地为项目量身定做新的Composite Node或Decorator Node。&lt;/p&gt;

&lt;p&gt;还可以积累一个项目相关的Node Lib，长远来说非常有价值。&lt;/p&gt;
</content>
 </entry>
 
 <entry>
   <title>游戏服务端架构</title>
   <link href="http://www.luzexi.com/2013/01/26/%E6%B8%B8%E6%88%8F%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%9E%B6%E6%9E%84"/>
   <updated>2013-01-26T18:47:21+08:00</updated>
   <id>http://www.luzexi.com/2013/01/26/游戏服务端架构</id>
   <content type="html">&lt;p&gt;本文引用自网络.这里讨论的游戏服务器架构大概是目前国内乃至世界上的网游通用的一种架构了：&lt;/p&gt;

&lt;p&gt;有段时间没有研究技术了，这次正好看到了新版的mangos，较之以前我看的版本有了比较大的完善，于是再次浏览了下他的代码，也借此机会整理下我在游戏服务器开发方面的一些心得，与大家探讨。
另外由于为避免与公司引起一些不必要的纠纷，我所描述的全都是通过google能够找到的资料，所以也可以认为我下面的内容都是网上所找资料的整理合 集。在平时的开发中我也搜索过相关的中文网页，很少有讲游戏服务器相关技术的，大家的讨论主要还是集中在3D相关技术，所以也希望我将开始的这几篇文章能 够起到抛砖引玉的作用，潜水的兄弟们也都上来透透气。&lt;/p&gt;

&lt;p&gt;要描述一项技术或是一个行业，一般都会从其最古老的历史开始说起，我本也想按着这个套路走，无奈本人乃一八零后小辈，没有经历过那些苦涩的却令人羡慕的 单机游戏开发，也没有响当当的拿的出手的优秀作品，所以也就只能就我所了解的一些技术做些简单的描述。一来算是敦促自己对知识做个梳理，二来与大家探讨的 过程也能够找到我之前学习的不足和理解上的错误，最后呢，有可能的话也跟业内的同行们混个脸熟，哪天要是想换个工作了也好有个人帮忙介绍下。最后的理由有 些俗了。&lt;/p&gt;

&lt;p&gt;关于游戏开发，正如云风在其blog上 所说，游戏项目始终只是个小工程，另外开发时间还是个很重要的问题，所以软件工程的思想及方法在大部分的游戏公司中并不怎么受欢迎。当然这也只是从我个人 一些肤浅的了解所得，可能不够充分。从游戏开发的程序团队的人员构成上也可看出来，基本只能算作是小开发团队。有些工作室性质的开发团队，那就更简单了。&lt;/p&gt;

&lt;p&gt;我所了解的早些的开发团队，其成员间没有什么严格的分工，大家凭兴趣自由选择一些模块来负责，完成了再去负责另一模块，有其他同事的工作需要接手或协助 的也会立即转入。所以游戏开发人员基本都是多面手，从网络到数据库，从游戏逻辑到图形图象，每一项都有所了解，并能实际应用。或者说都具有非常强的学习能 力，在接手一项新的任务后能在很短的时间内对该领域的技术迅速掌握并消化，而且还能现炒现卖。当然，这也与早期2D游戏的技术要求相对比较简单，游戏逻辑 也没有现在这般复杂有关。而更重要的可能是，都是被逼出来的吧！:)&lt;/p&gt;

&lt;p&gt;好了，闲话少说，下一篇，也就是第一篇了，主题为，服务器结构探讨。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;服务器结构探讨 – 最简单的结构&lt;/p&gt;

&lt;p&gt;所谓服务器结构，也就是如何将服务器各部分合理地安排，以实现最初的功能需求。所以，结构本无所谓正确与错误；当然，优秀的结构更有助于系统的搭建，对系统的可扩展性及可维护性也有更大的帮助。&lt;/p&gt;

&lt;p&gt;好的结构不是一蹴而就的，而且每个设计者心中的那把尺都不相同，所以这个优秀结构的定义也就没有定论。在这里，我们不打算对现有游戏结构做评价，而是试着从头开始搭建一个我们需要的MMOG结构。&lt;/p&gt;

&lt;p&gt;对于一个最简单的游戏服务器来说，它只需要能够接受来自客户端的连接请求，然后处理客户端在游戏世界中的移动及交互，也即游戏逻辑处理即可。如果我们把这两项功能集成到一个服务进程中，则最终的结构很简单：&lt;/p&gt;

&lt;p&gt;client —– server&lt;/p&gt;

&lt;p&gt;嗯，太简单了点，这样也敢叫服务器结构？好吧，现在我们来往里面稍稍加点东西，让它看起来更像是服务器结构一些。&lt;/p&gt;

&lt;p&gt;一般来说，我们在接入游戏服务器的时候都会要提供一个帐号和密码，验证通过后才能进入。关于为什么要提供用户名和密码才能进入的问题我们这里不打算做过 多讨论，云风曾对此也提出过类似的疑问，并给出了只用一个标识串就能进入的设想，有兴趣的可以去看看他们的讨论。但不管是采用何种方式进入，照目前看来我 们的服务器起码得提供一个帐号验证的功能。&lt;/p&gt;

&lt;p&gt;我们 把观察点先集中在一个大区内。在大多数情况下，一个大区内都会有多组游戏服，也就是多个游戏世界可供选择。简单点来实现，我们完全可以抛弃这个大区的概 念，认为一个大区也就是放在同一个机房的多台服务器组，各服务器组间没有什么关系。这样，我们可为每组服务器单独配备一台登录服。最后的结构图应该像这 样：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;loginServer   gameServer
|           /
|         /
client
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;该结构下的玩家操作流程为，先选择大区，再选择大区下的某台服务器，即某个游戏世界，点击进入后开始帐号验证过程，验证成功则进入了该游戏世界。但是，如果玩家想要切换游戏世界，他只能先退出当前游戏世界，然后进入新的游戏世界重新进行帐号验证。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;早期的游戏大都采用的是这种结构，有些游戏在实现时采用了一些技术手段使得在切换游戏服时不需要再次验证帐号，但整体结构还是未做改变。&lt;/p&gt;

&lt;p&gt;该结构存在一个服务器资源配置的问题。因为登录服处理的逻辑相对来说比较简单，就是将玩家提交的帐号和密码送到数据库进行验证，和生成会话密钥发送给游 戏服和客户端，操作完成后连接就会立即断开，而且玩家在以后的游戏过程中不会再与登录服打任何交道。这样处理短连接的过程使得系统在大多数情况下都是比较 空闲的，但是在某些时候，由于请求比较密集，比如开新服的时候，登录服的负载又会比较大，甚至会处理不过来。&lt;/p&gt;

&lt;p&gt;另外在实际的游戏运营中，有些游戏世界很火爆，而有些游戏世界却非常冷清，甚至没有多少人玩的情况也是很常见的。所以，我们能否更合理地配置登录服资源，使得整个大区内的登录服可以共享就成了下一步改进的目标。&lt;/p&gt;

&lt;p&gt;服务器结构探讨 – 登录服的负载均衡&lt;/p&gt;

&lt;p&gt;回想一下我们在玩wow时的操作流程：运行wow.exe进入游戏后，首先就会要求我们输入用户名和密码进行验证，验证成功后才会出来游戏世界列表，之后是排队进入游戏世界，开始游戏…&lt;/p&gt;

&lt;p&gt;可以看到跟前面的描述有个很明显的不同，那就是要先验证帐号再选择游戏世界。这种结构也就使得登录服不是固定配备给个游戏世界，而是全区共有的。&lt;/p&gt;

&lt;p&gt;我们可以试着从实际需求的角度来考虑一下这个问题。正如我们之前所描述过的那样，登录服在大多数情况下都是比较空闲的，也许我们的一个拥有20个游戏世 界的大区仅仅使用10台或更少的登录服即可满足需求。而当在开新区的时候，或许要配备40台登录服才能应付那如潮水般涌入的玩家登录请求。所以，登录服在 设计上应该能满足这种动态增删的需求，我们可以在任何时候为大区增加或减少登录服的部署。&lt;/p&gt;

&lt;p&gt;当然，在这里也不会存在要求添加太多登录服的情况。还是拿开新区的情况来说，即使新增加登录服满足了玩家登录的请求，游戏世界服的承载能力依然有限，玩家一样只能在排队系统中等待，或者是进入到游戏世界中导致大家都卡。&lt;/p&gt;

&lt;p&gt;另外，当我们在增加或移除登录服的时候不应该需要对游戏世界服有所改动，也不会要求重启世界服，当然也不应该要求客户端有什么更新或者修改，一切都是在背后自动完成。&lt;/p&gt;

&lt;p&gt;最后，有关数据持久化的问题也在这里考虑一下。一般来说，使用现有的商业数据库系统比自己手工技术先进要明智得多。我们需要持久化的数据有玩家的帐号及密码，玩家创建的角色相关信息，另外还有一些游戏世界全局共有数据也需要持久化。&lt;/p&gt;

&lt;p&gt;好了，需求已经提出来了，现在来考虑如何将其实现。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;对于负载均衡来说，已有了成熟的解决方案。一般最常用，也最简单部署的应该是基于DNS的负载均衡系统了，其通过在DNS中为一个域名配置多个IP地址 来实现。最新的DNS服务已实现了根据服务器系统状态来实现的动态负载均衡，也就是实现了真正意义上的负载均衡，这样也就有效地解决了当某台登录服当机 后，DNS服务器不能立即做出反应的问题。当然，如果找不到这样的解决方案，自己从头打造一个也并不难。而且，通过DNS来实现的负载均衡已经包含了所做 的修改对登录服及客户端的透明。&lt;/p&gt;

&lt;p&gt;而对于数据库的应用，在这种结构下，登录服及游戏世界服都会需要连接数据库。从数据库服务器的部署上来说，可以将帐号和角色数据都放在一个中心数据库中，也可分为两个不同的库分别来处理，基到从物理上分到两台不同的服务器上去也行。&lt;/p&gt;

&lt;p&gt;但是对于不同的游戏世界来说，其角色及游戏内数据都是互相独立的，所以一般情况下也就为每个游戏世界单独配备一台数据库服务器，以减轻数据库的压力。所 以，整体的服务器结构应该是一个大区有一台帐号数据库服务器，所有的登录服都连接到这里。而每个游戏世界都有自己的游戏数据库服务器，只允许本游戏世界内 的服务器连接。&lt;/p&gt;

&lt;p&gt;最后，我们的服务器结构就像这样：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;大区服务器
/   　 |       
/       |       　
登录服1   登录服2   世界服1   世界服2
     　   |       　 |     　 |
   　   |       　 |         |
帐号数据库         DBS     DBS
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这里既然讨论到了大区及帐号数据库，所以顺带也说一下关于激活大区的概念。wow中一共有八个大区，我们想要进入某个大区游戏之前，必须到官网上激活这个区，这是为什么呢？&lt;/p&gt;

&lt;p&gt;一般来说，在各个大区帐号数据库之上还有一个总的帐号数据库，我们可以称它为中心数据库。比如我们在官网上注册了一个帐号，这时帐号数据是只保存在中心 数据库上的。而当我们要到一区去创建角色开始游戏的时候，在一区的帐号数据库中并没有我们的帐号数据，所以，我们必须先到官网上做一次激活操作。这个激活 的过程也就是从中心库上把我们的帐号数据拷贝到所要到的大区帐号数据库中。&lt;/p&gt;

&lt;p&gt;服务器结构探讨 – 简单的世界服实现&lt;/p&gt;

&lt;p&gt;讨论了这么久我们一直都还没有进入游戏世界服务器内部，现在就让我们来窥探一下里面的结构吧。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;对于现在大多数MMORPG来说，游戏服务器要处理的基本逻辑有移动、聊天、技能、物品、任务和生物等，另外还有地图管理与消息广播来对其他高级功能做支撑。如纵队、好友、公会、战场和副本等，这些都是通过基本逻辑功能组合或扩展而成。&lt;/p&gt;

&lt;p&gt;在所有这些基础逻辑中，与我们要讨论的服务器结构关系最紧密的当属地图管理方式。决定了地图的管理方式也就决定了我们的服务器结构，我们仍然先从最简单的实现方式开始说起。&lt;/p&gt;

&lt;p&gt;回想一下我们曾战斗过无数个夜晚的暗黑破坏神，整个暗黑的世界被分为了若干个独立的小地图，当我们在地图间穿越时，一般都要经过一个叫做传送门的装置。 世界中有些地图间虽然在地理上是直接相连的，但我们发现其游戏内部的逻辑却是完全隔离的。可以这样认为，一块地图就是一个独立的数据处理单元。&lt;/p&gt;

&lt;p&gt;既然如此，我们就把每块地图都当作是一台独立的服务器，他提供了在这块地图上游戏时的所有逻辑功能，至于内部结构如何划分我们暂不理会，先把他当作一个黑盒子吧。&lt;/p&gt;

&lt;p&gt;当两个人合作做一件事时，我们可以以对等的关系相互协商着来做，而且一般也都不会有什么问题。当人数增加到三个时，我们对等的合作关系可能会有些复杂， 因为我们每个人都同时要与另两个人合作协商。正如俗语所说的那样，三个和尚可能会碰到没水喝的情况。当人数继续增加，情况就变得不那么简单了，我们得需要 一个管理者来对我们的工作进行分工、协调。游戏的地图服务器之间也是这么回事。&lt;/p&gt;

&lt;p&gt;一般来说，我们的游戏世界不可能会只有一块或者两块小地图，那顺理成章的，也就需要一个地图管理者。先称它为游戏世界的中心服务器吧，毕竟是管理者嘛，大家都以它为中心。&lt;/p&gt;

&lt;p&gt;中心服务器主要维护一张地图ID到地图服务器地址的映射表。当我们要进入某张地图时，会从中心服上取得该地图的IP和port告诉客户端，客户端主动去 连接，这样进入他想要去的游戏地图。在整个游戏过程中，客户端始终只会与一台地图服务器保持连接，当要切换地图的时候，在获取到新地图的地址后，会先与当 前地图断开连接，再进入新的地图，这样保证玩家数据在服务器上只有一份。&lt;/p&gt;

&lt;p&gt;我们来看看结构图是怎样的：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;中心服务器
/                
/                  
登录服     地图1     地图2   地图n
         |         /       /
       |         /       /
客户端
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;很简单，不是吗。但是简单并不表示功能上会有什么损失，简单也更不能表示游戏不能赚钱。早期不少游戏也确实采用的就是这种简单结构。&lt;/p&gt;

&lt;p&gt;服务器结构探讨 – 继续世界服&lt;/p&gt;

&lt;p&gt;都已经看出来了，这种每切换一次地图就要重新连接服务器的方式实在是不够优雅，而且在实际游戏运营中也发现，地图切换导致的卡号，复制装备等问题非常多，这里完全就是一个事故多发地段，如何避免这种频繁的连接操作呢？&lt;/p&gt;

&lt;p&gt;最直接的方法就是把那个图倒转过来就行了。客户端只需要连接到中心服上，所有到地图服务器的数据都由中心服来转发。很完美的解决方案，不是吗？&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;这种结构在实际的部署中也遇到了一些挑战。对于一般的MMORPG服务器来说，单台服务器的承载量平均在2000左右，如果你的服务器很不幸地只能带 1000人，没关系，不少游戏都是如此；如果你的服务器上跑了3000多玩家依然比较流畅，那你可以自豪地告诉你的策划，多设计些大量消耗服务器资源的玩 法吧，比如大型国战、公会战争等。&lt;/p&gt;

&lt;p&gt;2000人，似乎我们的策划朋友们不大愿意接受这个数字。我们将地图服务器分开来原来也是想将负载分开，以多带些客户端，现在要所有的连接都从中心服上转发，那连接数又遇到单台服务器的可最大承载量的瓶颈了。&lt;/p&gt;

&lt;p&gt;这里有必要再解释下这个数字。我知道，有人一定会说，才带2000人，那是你水平不行，我随便写个TCP服务器都可带个五六千连接。问题恰恰在于你是随 便写的，而MMORPG的服务器是复杂设计的。如果一个演示socket API用的echo服务器就能满足MMOG服务器的需求，那写服务器该是件多么惬意的事啊。&lt;/p&gt;

&lt;p&gt;但我们所遇到的事实是，服务器收到一个移动包后，要向周围所有人广播，而不是echo服务器那样简单的回应；服务器在收到一个连接断开通知时要向很多人 通知玩家退出事件，并将该玩家的资料写入数据库，而不是echo服务器那样什么都不需要做；服务器在收到一个物品使用请求包后要做一系列的逻辑判断以检查 玩家有没有作弊；服务器上还启动着很多定时器用来更新游戏世界的各种状态……&lt;/p&gt;

&lt;p&gt;其实这么一比较，我们也看出资源消耗的所在了：服务器上大量的复杂的逻辑处理。再回过头来看看我们想要实现的结构，我们既想要有一个唯一的入口，使得客户端不用频繁改变连接，又希望这个唯一入口的负载不会太大，以致于接受不了多少连接。&lt;/p&gt;

&lt;p&gt;仔细看一看这个需求，我们想要的仅仅只是一台管理连接的服务器，并不打算让他承担太多的游戏逻辑。既然如此，那五六千个连接也还有满足我们的要求。至少 在现在来说，一个游戏世界内，也就是一组服务器内同时有五六千个在线的玩家还是件让人很兴奋的事。事实上，在大多数游戏的大部分时间里，这个数字也是很让 人眼红的。&lt;/p&gt;

&lt;p&gt;什么？你说梦幻、魔兽还有史先生的那个什么征途远不止这么点人了！噢，我说的是大多数，是大多数，不包括那些明星。你知道大陆现在有多少游戏在运营吗？或许你又该说，我们不该在一开始就把自己的目标定的太低！好吧，我们还是先不谈这个。&lt;/p&gt;

&lt;p&gt;继续我们的结构讨论。一般来说，我们把这台负责连接管理的服务器称为网关服务器，因为内部的数据都要通过这个网关才能出去，不过从这台服务器提供的功能来看，称其为反向代理服务器可能更合适。我们也不在这个名字上纠缠了，就按大家通用的叫法，还是称他为网关服务器吧。&lt;/p&gt;

&lt;p&gt;网关之后的结构我们依然可以采用之前描述的方案，只是，似乎并没有必要为每一个地图都开一个独立的监听端口了。我们可以试着对地图进行一些划分，由一个 Master Server来管理一些更小的Zone Server，玩家通过网关连接到Master Server上，而实际与地图有关的逻辑是分派给更小的Zone Server去处理。&lt;/p&gt;

&lt;p&gt;最后的结构看起来大概是这样的：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Zone Server         Zone Server
             /
           /
Master Server           Master Server
/                          /
/                          /
Gateway Server                        /
|                               /
|                             /
|               Center Server
|
|
Client
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;服务器结构探讨 – 最终的结构&lt;/p&gt;

&lt;p&gt;如果我们就此打住，可能马上就会有人要嗤之以鼻了，就这点古董级的技术也敢出来现。好吧，我们还是把之前留下的问题拿出来解决掉吧。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;一般来说，当某一部分能力达不到我们的要求时，最简单的解决方法就是在此多投入一点资源。既然想要更多的连接数，那就再加一台网关服务器吧。新增加了网 关服后需要在大区服上做相应的支持，或者再简单点，有一台主要的网关服，当其负载较高时，主动将新到达的连接重定向到其他网关服上。&lt;/p&gt;

&lt;p&gt;而对于游戏服来说，有一台还是多台网关服是没有什么区别的。每个代表客户端玩家的对象内部都保留一个代表其连接的对象，消息广播时要求每个玩家对象使用 自己的连接对象发送数据即可，至于连接是在什么地方，那是完全透明的。当然，这只是一种简单的实现，也是普通使用的一种方案，如果后期想对消息广播做一些 优化的话，那可能才需要多考虑一下。&lt;/p&gt;

&lt;p&gt;既然说到了优化，我们也稍稍考虑一下现在结构下可能采用的优化方案。&lt;/p&gt;

&lt;p&gt;首先是当前的Zone Server要做的事情太多了，以至于他都处理不了多少连接。这其中最消耗系统资源的当属生物的AI处理了，尤其是那些复杂的寻路算法，所以我们可以考虑把这部分AI逻辑独立出来，由一台单独的AI服务器来承担。&lt;/p&gt;

&lt;p&gt;然后，我们可以试着把一些与地图数据无关的公共逻辑放到Master Server上去实现，这样Zone Server上只保留了与地图数据紧密相关的逻辑，如生物管理，玩家移动和状态更新等。&lt;/p&gt;

&lt;p&gt;还有聊天处理逻辑，这部分与游戏逻辑没有任何关联，我们也完全可以将其独立出来，放到一台单独的聊天服务器上去实现。&lt;/p&gt;

&lt;p&gt;最后是数据库了，为了减轻数据库的压力，提高数据请求的响应速度，我们可以在数据库之前建立一个数据库缓存服务器，将一些常用数据缓存在此，服务器与数据库的通信都要通过这台服务器进行代理。缓存的数据会定时的写入到后台数据库中。&lt;/p&gt;

&lt;p&gt;好了，做完这些优化我们的服务器结构大体也就定的差不多了，暂且也不再继续深入，更细化的内容等到各个部分实现的时候再探讨。&lt;/p&gt;

&lt;p&gt;好比我们去看一场晚会，舞台上演员们按着预定的节目单有序地上演着，但这就是整场晚会的全部吗？显然不止，在幕后还有太多太多的人在忙碌着，甚至在晚会前和晚会后都有。我们的游戏服务器也如此。&lt;/p&gt;

&lt;p&gt;在之前描述的部分就如同舞台上的演员，是我们能直接看到的，幕后的工作人员我们也来认识一下。&lt;/p&gt;

&lt;p&gt;现实中有警察来维护秩序，游戏中也如此，这就是我们常说的GM。GM可以采用跟普通玩家一样的拉入方式来进入游戏，当然权限会比普通玩家高一些，也可以提供一台GM服务器专门用来处理GM命令，这样可以有更高的安全性，GM服一般接在中心服务器上。&lt;/p&gt;

&lt;p&gt;在以时间收费的游戏中，我们还需要一台计费的服务器，这台服务器一般接在网关服务器上，注册玩家登录和退出事件以记录玩家的游戏时间。&lt;/p&gt;

&lt;p&gt;任何为用户提供服务的地方都会有日志记录，游戏服务器当然也不例外。从记录玩家登录的时间，地址，机器信息到游戏过程中的每一项操作都可以作为日志记录下来，以备查错及数据挖掘用。至于搜集玩家机器资料所涉及到的法律问题不是我们该考虑的。&lt;/p&gt;

&lt;p&gt;差不多就这么多了吧，接下来我们会按照这个大致的结构来详细讨论各部分的实现。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;服务器结构探讨 – 一点杂谈&lt;/p&gt;

&lt;p&gt;再强调一下，服务器结构本无所谓好坏，只有是否适合自己。我们在前面探讨了一些在现在的游戏中见到过的结构，并尽我所知地分析了各自存在的一些问题和可以做的一些改进，希望其中没有谬误，如果能给大家也带来些启发那自然更好。&lt;/p&gt;

&lt;p&gt;突然发现自己一旦罗嗦起来还真是没完没了。接下来先说说我在开发中遇到过的一些困惑和一基础问题探讨吧，这些问题可能有人与我一样，也曾遇到过，或者正在被困扰中，而所要探讨的这些基础问题向来也是争论比较多的，我们也不评价其中的好与坏，只做简单的描述。&lt;/p&gt;

&lt;p&gt;首先是服务器操作系统，linux与windows之争随处可见，其实在大多数情况下这不是我们所能决定的，似乎各大公司也基本都有了自己的传统，如网易的freebsd，腾讯的linux等。如果真有权利去选择的话，选自己最熟悉的吧。&lt;/p&gt;

&lt;p&gt;决定了OS也就基本上确定了网络IO模型，windows上的IOCP和linux下的epool，或者直接使用现有的网络框架，如ACE和asio等，其他还有些商业的网络库在国内的使用好像没有见到，不符合中国国情嘛。:)&lt;/p&gt;

&lt;p&gt;然后是网络协议的选择，以前的选择大多倾向于UDP，为了可靠传输一般自己都会在上面实现一层封装，而现在更普通的是直接采用本身就很可靠的TCP，或 者TCP与UDP的混用。早期选择UDP的主要原因还是带宽限制，现在宽带普通的情况下TCP比UDP多出来的一点点开销与开发的便利性相比已经不算什么 了。当然，如果已有了成熟的可靠UDP库，那也可以继续使用着。&lt;/p&gt;

&lt;p&gt;还有消息包格式的定义，这个曾在云风的blog上展开过激烈的争论。消息包格式定义包括三段，包长、消息码和包体，争论的焦点在于应该是消息码在前还是包长在前，我们也把这个当作是信仰问题吧，有兴趣的去云风的blog上看看，论论。&lt;/p&gt;

&lt;p&gt;另外早期有些游戏的包格式定义是以特殊字符作分隔的，这样一个好处是其中某个包出现错误后我们的游戏还能继续。但实际上，我觉得这是完全没有必要的，真 要出现这样的错误，直接断开这个客户端的连接可能更安全。而且，以特殊字符做分隔的消息包定义还加大了一点点网络数据量。&lt;/p&gt;

&lt;p&gt;最后是一个纯技术问题，有关socket连接数的最大限制。开始学习网络编程的时候我犯过这样的错误，以为port的定义为unsigned short，所以想当然的认为服务器的最大连接数为65535，这会是一个硬性的限制。而实际上，一个socket描述符在windows上的定义是 unsigned int，因此要有限制那也是四十多亿，放心好了。&lt;/p&gt;

&lt;p&gt;在服务器上port是监听用的，想象这样一种情况，web server在80端口上监听，当一个连接到来时，系统会为这个连接分配一个socket句柄，同时与其在80端口上进行通讯；当另一个连接到来时，服务 器仍然在80端口与之通信，只是分配的socket句柄不一样。这个socket句柄才是描述每个连接的唯一标识。按windows网络编程第二版上的说 法，这个上限值配置影响。&lt;/p&gt;

&lt;p&gt;好了，废话说完了，下一篇，我们开始进入登录服的设计吧。&lt;/p&gt;

&lt;p&gt;登录服的设计 – 功能需求&lt;/p&gt;

&lt;p&gt;正如我们在前面曾讨论过的，登录服要实现的功能相当简单，就是帐号验证。为了便于描述，我们暂不引入那些讨论过的优化手段，先以最简单的方式实现，另外也将基本以mangos的代码作为参考来进行描述。&lt;/p&gt;

&lt;p&gt;想象一下帐号验证的实现方法，最容易的那就是把用户输入的明文用帐号和密码直接发给登录服，服务器根据帐号从数据库中取出密码，与用户输入的密码相比较。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;这个方法存在的安全隐患实在太大，明文的密码传输太容易被截获了。那我们试着在传输之前先加一下密，为了服务器能进行密码比较，我们应该采用一个可逆的 加密算法，在服务器端把这个加密后的字串还原为原始的明文密码，然后与数据库密码进行比较。既然是一个可逆的过程，那外挂制作者总有办法知道我们的加密过 程，所以，这个方法仍不够安全。&lt;/p&gt;

&lt;p&gt;哦，如果我们只 是希望密码不可能被还原出来，那还不容易吗，使用一个不可逆的散列算法就行了。用户在登录时发送给服务器的是明文的帐号和经散列后的不可逆密码串，服务器 取出密码后也用同样的算法进行散列后再进行比较。比如，我们就用使用最广泛的md5算法吧。噢，不要管那个王小云的什么论文，如果我真有那么好的运气，早 中500w了，还用在这考虑该死的服务器设计吗？&lt;/p&gt;

&lt;p&gt;似乎是一个很完美的方案，外挂制作者再也偷不到我们的密码了。慢着，外挂偷密码的目的是什么？是为了能用我们的帐号进游戏！如果我们总是用一种固定的算法来对密码做散列，那外挂只需要记住这个散列后的字串就行了，用这个做密码就可以成功登录。&lt;/p&gt;

&lt;p&gt;嗯，这个问题好解决，我们不要用固定的算法进行散列就是了。只是，问题在于服务器与客户端采用的散列算法得出的字串必须是相同的，或者是可验证其是否匹 配的。很幸运的是，伟大的数学字们早就为我们准备好了很多优秀的这类算法，而且经理论和实践都证明他们也确实是足够安全的。&lt;/p&gt;

&lt;p&gt;这其中之一是一个叫做SRP的算法，全称叫做Secure Remote Password，即安全远程密码。wow使用的是第6版，也就是SRP6算法。有关其中的数学证明，如果有人能向我解释清楚，并能让我真正弄明白的话， 我将非常感激。不过其代码实现步骤倒是并不复杂，mangos中的代码也还算清晰，我们也不再赘述。&lt;/p&gt;

&lt;p&gt;登录服除了帐号验证外还得提供另一项功能，就是在玩家的帐号验证成功后返回给他一个服务器列表让他去选择。这个列表的状态要定时刷新，可能有新的游戏世 界开放了，也可能有些游戏世界非常不幸地停止运转了，这些状态的变化都要尽可能及时地让玩家知道。不管发生了什么事，用户都有权利知道，特别是对于付过费 的用户来说，我们不该藏着掖着，不是吗？&lt;/p&gt;

&lt;p&gt;这个游戏世界列表的功能将由大区服来提供，具体的结构我们在之前也描述过，这里暂不做讨论。登录服将从大区服上获取到的游戏世界列表发给已验证通过的客户端即可。好了，登录服要实现的功能就这些，很简单，是吧。&lt;/p&gt;

&lt;p&gt;确实是太简单了，不过简单的结构正好更适合我们来看一看游戏服务器内部的模块结构，以及一些服务器共有组件的实现方法。这就留作下一篇吧。&lt;/p&gt;

&lt;p&gt;服务器公共组件实现 – mangos的游戏主循环&lt;/p&gt;

&lt;p&gt;当阅读一项工程的源码时，我们大概会选择从main函数开始，而当开始一项新的工程时，第一个写下的函数大多也是main。那我们就先来看看，游戏服务器代码实现中，main函数都做了些什么。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;由于我在读技术文章时最不喜看到的就是大段大段的代码，特别是那些直接Ctrl+C再Ctrl+V后未做任何修改的代码，用句时髦的话说，一点技术含量 都没有！所以在我们今后所要讨论的内容中，尽量会避免出现直接的代码，在有些地方确实需要代码来表述时，也将会选择使用伪码。&lt;/p&gt;

&lt;p&gt;先从mangos的登录服代码开始。mangos的登录服是一个单线程的结构，虽然在数据库连接中可以开启一个独立的线程，但这个线程也只是对无返回结果的执行类SQL做缓冲，而对需要有返回结果的查询类SQL还是在主逻辑线程中阻塞调用的。&lt;/p&gt;

&lt;p&gt;登录服中唯一的这一个线程，也就是主循环线程对监听的socket做select操作，为每个连接进来的客户端读取其上的数据并立即进行处理，直到服务器收到SIGABRT或SIGBREAK信号时结束。&lt;/p&gt;

&lt;p&gt;所以，mangos登录服主循环的逻辑，也包括后面游戏服的逻辑，主循环的关键代码其实是在SocketHandler中，也就是那个Select函数 中。检查所有的连接，对新到来的连接调用OnAccept方法，有数据到来的连接则调用OnRead方法，然后socket处理器自己定义对接收到的数据 如何处理。&lt;/p&gt;

&lt;p&gt;很简单的结构，也比较容易理解。&lt;/p&gt;

&lt;p&gt;只是，在对性能要求比较高的服务器上，select一般不会是最好的选择。如果我们使用windows平台，那IOCP将是首选；如果是 linux，epool将是不二选择。我们也不打算讨论基于IOCP或是基于epool的服务器实现，如果仅仅只是要实现服务器功能，很简单的几个API 调用即可，而且网上已有很多好的教程；如果是要做一个成熟的网络服务器产品，不是我几篇简单的技术介绍文章所能达到。&lt;/p&gt;

&lt;p&gt;另外，在服务器实现上，网络IO与逻辑处理一般会放在不同的线程中，以免耗时较长的IO过程阻塞住了需要立即反应的游戏逻辑。&lt;/p&gt;

&lt;p&gt;数据库的处理也类似，会使用异步的方式，也是避免耗时的查询过程将游戏服务器主循环阻塞住。想象一下，因某个玩家上线而发起的一次数据库查询操作导致服务器内所有在线玩家都卡住不动将是多么恐怖的一件事！&lt;/p&gt;

&lt;p&gt;另外还有一些如事件、脚本、消息队列、状态机、日志和异常处理等公共组件，我们也会在接下来的时间里进行探讨。&lt;/p&gt;

&lt;p&gt;服务器公共组件实现 – 继续来说主循环&lt;/p&gt;

&lt;p&gt;前面我们只简单了解了下mangos登录服的程序结构，也发现了一些不足之处，现在我们就来看看如何提供一个更好的方案。&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;正如我们曾讨论过的，为了游戏主逻辑循环的流畅运行，所有比较耗时的IO操作都会分享到单独的线程中去做，如网络IO，数据库IO和日志IO等。当然，也有把这些分享到单独的进程中去做的。&lt;/p&gt;

&lt;p&gt;另外对于大多数服务器程序来说，在运行时都是作为精灵进程或服务进程的，所以我们并不需要服务器能够处理控制台用户输入，我们所要处理的数据来源都来自网络。&lt;/p&gt;

&lt;p&gt;这样，主逻辑循环所要做的就是不停要取消息包来处理，当然这些消息包不仅有来自客户端的玩家操作数据包，也有来自GM服务器的管理命令，还包括来自数据库查询线程的返回结果消息包。这个循环将一直持续，直到收到一个通知服务器关闭的消息包。&lt;/p&gt;

&lt;p&gt;主逻辑循环的结构还是很简单的，复杂的部分都在如何处理这些消息包的逻辑上。我们可以用一段简单的伪码来描述这个循环过程：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;while (Message* msg = getMessage())
{
if (msg为服务器关闭消息)
break;
处理msg消息;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这里就有一个问题需要探讨了，在getMessage()的时候，我们应该去哪里取消息？前面我们考虑过，至少会有三个消息来源，而我们还讨论过，这些消息源的IO操作都是在独立的线程中进行的，我们这里的主线程不应该直接去那几处消息源进行阻塞式的IO操作。&lt;/p&gt;

&lt;p&gt;很简单，让那些独立的IO线程在接收完数据后自己送过来就是了。好比是，我这里提供了一个仓库，有很多的供货商，他们有货要给我的时候只需要交到仓库， 然后我再到仓库去取就是了，这个仓库也就是消息队列。消息队列是一个普通的队列实现，当然必须要提供多线程互斥访问的安全性支持，其基本的接口定义大概类 似这样：&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;IMessageQueue
{
void putMessage(Message*);
Message* getMessage();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;网络IO，数据库IO线程把整理好的消息包都加入到主逻辑循环线程的这个消息队列中便返回。有关消息队列的实现和线程间消息的传递在ACE中有比较完全的代码实现及描述，还有一些使用示例，是个很好的参考。&lt;/p&gt;

&lt;p&gt;这样的话，我们的主循环就很清晰了，从主线程的消息队列中取消息，处理消息，再取下一条消息……&lt;/p&gt;

&lt;script type=&quot;text/javascript&quot;&gt;
	if (/(iPhone|iPad|iPod|iOS|Android)/i.test(navigator.userAgent))
    { //移动端
        /*mobile-20:5*/
    	var cpro_id = &quot;u3493987&quot;;
		document.write(&quot;\&lt;script type=\&quot;text\/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/cm.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
    }
    else
    {
         /*700*200信息流*/
        var cpro_id = &quot;u3469788&quot;;
        document.write(&quot;\&lt;script type=\&quot;text/javascript\&quot; src=\&quot;//cpro.baidustatic.com/cpro/ui/c.js\&quot;\&gt;\&lt;\/script\&gt;&quot;);
	}
&lt;/script&gt;

&lt;p&gt;服务器公共组件实现 – 消息队列&lt;/p&gt;

&lt;p&gt;既然说到了消息队列，那我们继续来稍微多聊一点吧。&lt;/p&gt;

&lt;p&gt;我们所能想到的最简单的消息队列可能就是使用stl的list来实现了，即消息队列内部维护一个list和一个互斥锁，putMessage时将 message加入到队列尾，getMessage时从队列头取一个message返回，同时在getMessage和putMessage之前都要求先 获取锁资源。&lt;/p&gt;

&lt;p&gt;实现虽然简单，但功能是绝对满足需求的，只是性能上可能稍稍有些不尽如人意。其最大的问题在频繁的锁竞争上。&lt;/p&gt;

&lt;p&gt;对于如何减少锁竞争次数的优化方案，Ghost Cheng提出了一种。提供一个队列容器，里面有多个队列，每个队列都可固定存放一定数量的消息。网络IO线程要给逻辑线程投递消息时，会从队列容器中取 一个空队列来使用，直到将该队列填满后再放回容器中换另一个空队列。而逻辑线程取消息时是从队列容器中取一个有消息的队列来读取，处理完后清空队列再放回 到容器中。&lt;/p&gt;

&lt;p&gt;这样便使得只有在对队列容器进行操作时才需要加锁，而IO线程和逻辑线程在操作自己当前使用的队列时都不需要加锁，所以锁竞争的机会大大减少了。&lt;/p&gt;

&lt;p&gt;这里为每个队列设了个最大消息数，看来好像是打算只有当IO线程写满队列时才会将其放回到容器中换另一个队列。那这样有时也会出现IO线程未写满一个队 列，而逻辑线程又没有数据可处理的情况，特别是当数据量很少时可能会很容易出现。Ghost Cheng在他的描述中没有讲到如何解决这种问题，但我们可以先来看看另一个方案。&lt;/p&gt;

&lt;p&gt;这个方案与上一个方案基本类似，只是不再提供队列容器，因为在这个方案中只使用了两个队列，arthur在他的一封邮件中描述了这个方案的实现及部分代 码。两个队列，一个给逻辑线程读，一个给IO线程用来写，当逻辑线程读完队列后会将自己的队列与IO线程的队列相调换。所以，这种方案下加锁的次数会比较 多一些，IO线程每次写队列时都要加锁，逻辑线程在调换队列时也需要加锁，但逻辑线程在读队列时是不需要加锁的。&lt;/p&gt;

&lt;p&gt;虽然看起来锁的调用次数是比前一种方案要多很多，但实际上大部分锁调用都是不会引起阻塞的，只有在逻辑线程调换队列的那一瞬间可能会使得某个线程阻塞一下。另外对于锁调用过程本身来说，其开销是完全可以忽略的，我们所不能忍受的仅仅是因为锁调用而引起的阻塞而已。&lt;/p&gt;

&lt;p&gt;两种方案都是很优秀的优化方案，但也都是有其适用范围的。Ghost Cheng的方案因为提供了多个队列，可以使得多个IO线程可以总工程师的，互不干扰的使用自己的队列，只是还有一个遗留问题我们还不了解其解决方法。 arthur的方案很好的解决了上一个方案遗留的问题，但因为只有一个写队列，所以当想要提供多个IO线程时，线程间互斥地写入数据可能会增大竞争的机 会，当然，如果只有一个IO线程那将是非常完美的&lt;/p&gt;

</content>
 </entry>
 

</feed>
