白鹭引擎
整体架构
- display 封装好的显示对象及容器
- events 事件分发模块
- filters 各种渲染效果
- geom 几何对象定义
- i18n 国际化
- localStorage 本地存储
- media 声音
加载流程
渲染流程
事件机制
更新机制
- html 文件有可能被缓存,不应在内部写过多文件
- 每次请求manifest.json获取代码文件列表,文件列表可更新。
- 文件根据内容计算MD5, 并用作文件名,每次文件内容修改会生成不一样的文件名,可避免被缓存。
- 图集文件与对应的png文件使用同名放在同一目录下,并修改sheet加载器,使能正确加载
性能优化
加载优化
- 代码压缩
- gzip
- 图片压缩
- 配置文件打包,加速下载
渲染优化
- 纹理集
- 图集纹理连续使用
- 文字渲染加速
- 少用 alpha 混合
- 少用 描边
- 少用 mask
- 少用 filter
- 使用 cacheAsBitmap 减少重绘次数
内存管理
- 对象池
- 无用的内容,及时指向空,让它能被自动回收
- 资源自动释放
- 显存优化: 安全内存大概150M左右,2048*2048的图片占用16M内存
- 纹理等比压缩
CPU运算优化
- 及时删除事件侦听
- 显式禁用触摸交互
- 合理使用DispatchEvents
- Tween 动画
- 统一的计时器
- 使用 ejs 的ui模式
- 分帧计算
- 处理网络协议的时候,当处理一条协议之后达到帧间隔上限,则留下一帧再处理,可减少卡顿感
- 分帧加载
- 不可见的节点及时从舞台移除或者设置visible= false
感受优化
- 预加载
- 原生html首屏loading
- 地图分步加载
防作弊优化
- HTTPS
- 二进制流传输
- 协议文件加密
- 内存数据加密
- 防控制台输入
兼容性问题
debug
- 使用 chrome://inspect
- Fildder
- Weinre
- 修改Host
- 修改WiFi设置,代理设为电脑
- console.log
- adb
- Chrome Dev Tools: profile, timeline, performance
- window.onerror 收集错误日志
- 观察FPS面板的各项指标
为什么选择egret
- 工具流完善
- 性能满足需求
- 文档比较全
- 论坛活跃
webGL 与 Canvas
- 性能,webGL 超过Canvas 数倍。
- webGL 理论可支持2D 和 3D, 但Canvas 只支持2D
- 普及率,Canvas100%支持, webGL支持率也很高
EnterFrame 事件 与 timer 有什么不一样, 各有什么特点?
资源加载流程是怎样的?有哪些类型的资源? 怎么缓存? 怎么释放?
WebSocket 怎么兼容低版本浏览器?
内存泄漏都有哪些情况? 怎么去排查和修正?
怎么解决跨域问题?
白鹭音频有哪些兼容性问题? 怎么解决?
Cocos
引擎架构
渲染流程
内存管理
事件机制
更新机制
定制修改
- 修改FileUtils.getPathForFilename 接口,处理资源路径混淆
- 修改FileUtils.getData 接口, 对资源做解密处理
- 新增保底纹理,以防资源丢失导致的闪退
性能优化
从一个精灵添加到舞台到被绘制到屏幕上, 中间经历了什么?
框架
框架的作用是什么?
框架包含哪些部分?
UI
图形渲染
shaders
有哪些图片格式?它们各有什么特点?
有哪些像素格式? 各占多少内存?
性能
游戏内存占用有多少?
游戏资源大小有多少?
主城地图最多能多少人同屏?
数据结构
数组
- 连续的存储结构,数据密度高, 随机访问, 快速遍历, 增删消耗较大
链表
- 通过next指针来连接, 增删容易,无法随机访问
hash 表
- 内部数据结构是数组。
- 根据键值计算索引存储数据, 需要考虑冲突的处理情况。
- O(1)的查找效率,空间消耗比较大
堆栈
先进后出队列
先进先出二叉堆
分为大根堆,小根堆, 能快速找到一组数据中的最大/小值。二叉找树
- 用于维护易查找的数据
- 每个结点最多只有两个子节点
- 如果有左子树,则左子树的所有结点值均小于根结点的值
- 如果有右子树,则右子树的所有结点值均大于根结点的值
- 没有键值相等的值
参考
二叉平衡树(AVL)
- 相对于二叉查找树,限制了同一结点的两颗子树高度差最大为1,使得查找效率相对稳定
- 插入或删除可导致树不平衡,共有四种不同的失衡情况,可以通过左旋右旋子树来重新保持平衡。
参考
跳表
算法
帧同步
统一随机种子,或者使用固定的随机池;
- 服务器端向各客户端同步随机种子
- 服务器端以每秒固定帧率向前端发送帧步进消息。
- 客户端操作的时候,随时将操作同步到服务器端,服务器端收集操作,并在帧消息中同步转发操作。
- 客户端收到帧消息,按照消息中的操作,计算逻辑。
- 客户端渲染帧根据逻辑帧的计算结果,做插值移动。
- 如果客户端迟迟没有收到帧消息, 则逻辑帧不运算, 渲染帧继续播动画,不走路。
A*寻路
- 启发式寻路算法
- 从起点开始,遍历周边可达点,然后计算每个格子的分值(消耗 + 预期离终点距离),
- 从所有候选点中,取出分值最低的, 然后重复上述循环, 直到到达终点.
- 缺点: 起终点不可达的时候, 可能需要遍历整个地图也找不到。
- 方法: 可以考虑从起终点同时开始往对方查找,可以及时种植。
记录失败情况,尽快返回。路径平滑
障碍点突围
CRC 与 Md5
设计一个f(x), 使得 f(f(x)) = 1/x;
如何判断一个字符串是否是另一个字符串旋转串?
网络模块
长连接短连接
短连接是每次连接发送厚接收完数据就马上断开连接
长连接是发起连接之后,一直保持连接,中途可以随时发送数据。
tcp
粘包半包分包
因为tcp只有流的概念, 没有数据包的概念。
半包是指接收方一次没有接收到一个完整的包, 只接受到一部分
粘包是指发送方发送的若干个包数据在接收方接收时连在一起。
如何保证可靠性呢?
三次握手的过程是怎样的?
- 第一次,C2S, SYN=1,seq=x; C进入SYN_SENT状态.
S收到C发来的建立连接请求后, - 第二次,S2C, SYN=1,ACK=1, seq=y, ack=x+1; S进入SYN_RECV状态.
C收到S发过来的确认报文之后, - 第三次,C2S, ACK=1,seq=x+1, ack=y+1; C 进入ESTABLISHED,该报文可以携带数据。
S收到C的确认报文之后,也进入ESTABLISHED状态。
- 为什么要三次握手呢?
为了防止已失效的请求连接报文又被传到服务器而建立的连接。
下面是如果没有三次握手的情况:
C请求,丢失,C再请求,建立连接,发送数据,关闭连接, S收到了C第一次丢失的连接请求,错误的以为要建立连接,然后就处于一直等待状态,白白浪费资源。断开连接的过程是怎样的?
四次挥手
- 第一次,C2S, FIN=1, C 进入FIN_WAIT_1 状态
S收到C结束报文之后 - 第二次,S2C, ACK=1, S 进入CLOSE_WAIT状态
C收到S的确认报文之后,进入FIN_WAI_2状态
S如果有数据要发送,继续发送数据, 直到数据发送完毕 - 第三次,S2C,FIN=1,S进入LAST_ACK状态,不再发送数据
C收到S的结束报文之后 - 第四次,C2S,ACK=1,进入TIME_WAIT状态,
S收到ACK之后,断开连接,
C等待2MSL之后,关闭连接。
socket
websocket
其他
对公司有什么要求
对团队有什么样的要求
unity
C++
new/delete 和 malloc/free 之间有什么联系和区别?
有哪些内存分配方式? 以及他们之间的区别?
进程与线程有什么区别?
进程之间如何实现数据交互?
线程之间如何实现数据交互?
单例模式在多线程环境下可能会有什么问题?如何避免?
都有哪些标准特性用于支持多线程?
内存对齐
C
lua
如何实现OOP?
如何防止外部给table添加属性?
C++ 如何与lua 交互
tolua++ 内部实现机制? 是怎么将C++对象封装成userdata传到lua 中的?
TS 相对于js, 有什么优缺点?
OS
Android
在安卓打包过程中,都有哪些阶段? 各个阶段都生成了什么文件?
安卓包重签名?
如何避免安卓包被重新签名?
设计模式
23种设计模式
创建型
- Factory Method (工厂方法)
- Abstract Factory (抽象工厂)
- Builder (建造者)
- Prototype (原型)
- Singleton (单例)
结构型
- Adapter Class/Object (适配器)
- Bridge (桥接)
- Composite (组合)
- Decorator (装饰)
- Facade (外观)
- Flyweight (亨元)
- Proxy (代理)
行为型
- Interpreter (解释器)
- Template Method (模板方法)
- Chain of Responsibility (责任链)
- Command (命令)
- Iterator (迭代器)
- Mediator (中介者)
- Mementor (备忘录)
- Observer (观察者)
- State (状态)
- Strategy (策略)
- Visitor (访问者)
编程原则
良好的额编程原则
java 设计模式
良好的编程原则与良好的设计工程原则密切相关。本文总结的这些设计原则,帮助开发者更有效率的编写代码,并帮助成为一名优秀的程序员。 避免重复原则(DRY – Don’t repeat yourself)
编程的最基本原则是避免重复。在程序代码中总会有很多结构体,如循环、函数、类等等。一旦你重复某个语句或概念,就会很容易形成一个抽象体。抽象原则(Abstraction Principle )
与DRY原则相关。要记住,程序代码中每一个重要的功能,只能出现在源代码的一个位置。简单原则(Keep It Simple and Stupid )
简单是软件设计的目标,简单的代码占用时间少,漏洞少,并且易于修改。避免创建你不要的代码 Avoid Creating a YAGNI (You aren’t going to need it)
除非你需要它,否则别创建新功能。尽可能做可运行的最简单的事(Do the simplest thing that could possibly work)
尽可能做可运行的最简单的事。在编程中,一定要保持简单原则。作为一名程序员不断的反思“如何在工作中做到简化呢?”这将有助于在设计中保持简单的路径。别让我思考(Don’t make me think )
这是Steve Krug一本书的标题,同时也和编程有关。所编写的代码一定要易于读易于理解,这样别人才会欣赏,也能够给你提出合理化的建议。相反,若是繁杂难解的程序,其他人总是会避而远之的。开闭原则(Open/Closed Principle)
一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
一个软件实体应该通过扩展来实现变化,而不是通过修改已有代码来实现变化。代码维护(Write Code for the Maintainer)
一个优秀的代码,应当使本人或是他人在将来都能够对它继续编写或维护。代码维护时,或许本人会比较容易,但对他人却比较麻烦。因此你写的代码要尽可能保证他人能够容易维护。用书中原话说“如果一个维护者不再继续维护你的代码,很可能他就有想杀了你的冲动。最小惊讶原则(Principle of least astonishment)
最小惊讶原则通常是在用户界面方面引用,但同样适用于编写的代码。代码应该尽可能减少让读者惊喜。也就是说,你编写的代码只需按照项目的要求来编写。其他华丽的功能就不必了,以免弄巧成拙。单一责任原则(Single Responsibility Principle)
应该有且仅有一个原因引起类的变更。低耦合原则(Minimize Coupling)
代码的任何一个部分应该减少对其他区域代码的依赖关系。尽量不要使用共享参数。低耦合往往是完美结构系统和优秀设计的标志。最大限度凝聚原则(Maximize Cohesion)
相似的功能代码应尽量放在一个部分。隐藏实现细节(Hide Implementation Details)
隐藏实现细节原则,当其他功能部分发生变化时,能够尽可能降低对其他组件的影响。迪米特法则又叫作最少知识原则(Law of Demeter)
- 只和朋友交流
朋友类的定义是这样的:出现在成员变量、方法的输入输出参数中的类称为成员朋友类,而出现在方法体内部的类不属于朋友类。 - 暴露尽量少的接口
避免过早优化(Avoid Premature Optimization)
除非你的代码运行的比你想像中的要慢,否则别去优化。假如你真的想优化,就必须先想好如何用数据证明,它的速度变快了。过早的优化是一切罪恶的根源”——Donald Knuth
代码重用原则(Code Reuse is Good)
重用代码能提高代码的可读性,缩短开发时间。关注点分离(Separation of Concerns)
不同领域的功能,应该由不同的代码和最小重迭的模块组成。拥抱改变(Embrace Change)
这是Kent Beck一本书的标题,同时也被认为是极限编程和敏捷方法的宗旨。
许多其他原则都是基于这个概念的,即你应该积极面对变化。事实上,一些较老的编程原则如最小化耦合原则都是为了使代码能够容易变化。无论你是否是个极限编程者,基于这个原则去编写代码会让你的工作变得更有意义。接口隔离原则(ISP)
接口隔离原则要求接口尽量小
接口要高内聚,高内聚就是要提高接口、类、模块的处理能力,减少对外的交互。依赖倒置原则(DIP)
- 高层模块不应该依赖底层模块,两者都应该依赖其抽象;
- 抽象不应该依赖细节;
- 细节应该依赖抽象。
- 里氏替换原则(LSP)
所有引用基类的地方必须能透明地使用其子类的对象。
数据库范式
Normal Form (NF)
范式的好处, 消除异常(插入,删除,更新),减少冗余数据。
1NF
列的原子性,即每一列都不能再拆分。
EX: 一个人有工作电话和家庭电话两个属性,那么数据库设计时, 就不应该只有一个电话列。
2NF
基于1NF,表必须有主键,且非主键列必须完全依赖所有主键,而不能只依赖部分主键列。
EX: 订单id和商品id 作主键的表,如果商品价格只依赖于商品id, 即所有同一商品id,价钱必定相同, 那么商品价格就不应该在这个表中。因为它只依赖部分主键列。
3NF
基于2NF,非主键列必须直接依赖主键列,不能传递依赖。
EX: 订单id 作主键的表, 商品id依赖订单id, 商品价格依赖订单id. 商品价格不应该在这个表中, 存在传递依赖。
BCNF
基于3NF, 消除主键列对于部分主键列的依赖。