PlayCanvas技术文档
Contents
- 1. 1. 快速入门
- 2. 2. 优化
- 3. 3. Q&A
- 3.1. 3.1. PlayCanvas里面的1Size在3DS Max里面是多少米?
- 3.2. 3.2. 如何实现碰撞检测?
- 3.3. 3.3. 如何在游戏中使用贴图和特殊字体?
- 3.4. 3.4. 游戏在安卓版微信上声音无法播放?
- 3.5. 3.5. 我的代码能同步到Github上吗?
- 3.6. 3.6. 如何把游戏部署到我们自己服务器?
- 3.7. 3.7. 如何自定义loading界面?
- 3.8. 3.8. 如何使用HTML和CSS ?
- 3.9. 3.9. 替换资源的技巧?
- 3.10. 3.10. 如何把一个带Rigidbody的实体的惯性去掉?
- 3.11. 3.11. 把transparent canvas选择打开后,带来的坑
- 3.12. 3.12. 背景的处理?
- 3.13. 3.13. 如何给字体加动效?
- 3.14. 3.14. 粒子效果不正常?
- 3.15. 3.15. 替换材质球?
- 3.16. 3.16. 替换模型?
- 3.17. 3.17. 改变材质球参数?
- 3.18. 3.18. 改变带rigidbody的实体角度或者位置?
- 3.19. 3.19. Teleport父元素,子元素rigidbody失效?
#PlayCanvas技术文档(持续更新)
@(PlayCanvas Note)[HTML5, canvas]
1. 快速入门
- 强烈建议先看快速入门视频,再去看官方的Manual !
1.1. YouTube上PlayCanvas的快速入门视频
- Getting Started with PlayCanvas - YouTube
- PlayCanvas Tutorial 1 - Getting started with PlayCanvas - YouTube
这系列视频是歪果仁录制的,对于英语听力差的小伙伴会有点小压力 - 待补充…
1.2. PlayCanvas官方的Manual
- PlayCanvas Manual包括:
- User Manual
- Tutorials
- Video
- API Reference
- 官方建议直接先看User Manual,不过个人认为先去Video看看。它的视频都放在YouTube,没有翻墙的小伙伴就不好意思了。在YouTube,有各种PlayCanvas的介绍视频,也包括上面两个系列的快速入门视频。先不说收获,你会被那非常炫的开发方式感动的。
- 视频看完后,强烈建议把User Manual过一遍,再把Tutorials过一遍。而且以后,也会经常访问这三者:User Manual、Tutorials、API Reference。其中,Tutorials中有一个Keepy Up游戏的简单教程,值得反复推敲。
1.3. PlayCanvas的论坛Forum
- 有什么问题,都可以在这个Forum上发布问题(Of course, in English),一不小心,PlayCanvas的CTO就回复你了。帅吧!所以,有问题尽管去问吧!
2. 优化
- 你的PlayCanvas Apps通常会运行在各种各样的设备上,从 高性能的PC端 到 低性能的智能手机端。为了能够保证在各类设备上,都让你的Apps有最佳的表现,这里提供一些优化的指导。同时,PlayCanvas也提供了工具去帮助你找到并消除这些性能的瓶颈。
2.1. 最优化指导
2.1.1. JavaScript
- 用
new
去实例化一个JavaScript对象(尤其是vectors, matrices 和 quaternions)是一种动态分配,这个代价比较高。因此,应该尽可能地预先在script的initialize
函数实例化,并在update
函数中重用它们。这样,也可以让JS的垃圾回收机制省点心。2.1.2. Graphics
- As the value for texture anisotropy increases, visual improve but performance decreases. Be careful to balance visuals against performance.
- Look for opportunities to pack multiple textures into single images. For example, a greyscale opacity map can be stored in the alpha channel of a diffuse map. Or a greyscale gloss map can be stored in the alpha channel of a specular map. This results in lower VRAM usage.(应该是尽可能用合图的意思)
- 每个场景中动态灯光的数量一定要心中有数,尽可能让它们的数量减少到最低。
- 从始至终都不会动的模型,给它的static属性打上勾吧。
- 打开动态灯光的阴影投射是很耗性能的,尤其是点光的阴影投射。 For each point light that casts shadow, the scene must be rendered 6 times into a shadow map.
- 在 PlayCanvas中,一个多边形实体就会调用一个
draw
方法 (a command to draw an individual graphical primitive)。每一次调用draw
方法,都需要分配CPU资源给WebGL。因此,可取之法是保证draw
方法调用的次数尽可能低,尤其是在智能手机等移动设备上。你会看到一堆的draw
方法在你为一个特殊的模型添加材质并在检查窗(Inspector)观察它的时候而被调用多次。 100-200个draw
方法在低端的移动设备被调用是个非常难实现的目标。在某种情况下,高端的桌面设备每一帧的draw
方法调用次数甚至可以达到数以千计,同时保持60的FPS。 - 为你的Project打开
Use Device Pixel Ratio
(使用设备的像素比) 的时候应该注意。打开这个会让你的PlayCanvas app使用设备的分辨率,但是这会导致更多的像素点被填充,这将明显降低游戏的帧率。 - 在一个场景中,尽量把混合多边形实体(
blended mesh instances
)数量降到最低。Blended meshes are deferred until all opaque mesh instances have been dispatched and are then submitted in back to front camera depth order. This results in pixels being filled multiple times and can result in a lot of render state changes since blended meshes cannot be sorted by material. - Try to keep the number of 着色器(
shaders
) generated by your app as low as possible. Shaders have to be compiled and linked on demand and this operation is expensive, causing delay in app startup and glitches in framerate. If material A has an emissive map but material B doesn’t, two shaders will be generated. If you set a black emissive map on material B, the materials can share the same shader. Reducing the number of materials in your scene should also reduce the number of generated shaders. Post effects
can be expensive so think carefully before you enable them. They can cost a lot in terms of pixel fill.- Only enable 截头锥体(
frustum culling
) on a camera component if, on balance, it is likely to save more performance than it costs to calculate visiblity. 如果被渲染的场景中,所有的多边形实体(mesh instances)都可是看得见的,那一定要把这个勾去掉。(说白了就是看得见的那些会被渲染,但是还要经过计算才能知道哪些是看得见的,所以如果所有的多边形实体都可以看见,就没有必要把这个打开了。) - Enabling backface culling on a material will be cheaper than disabling it. Generally speaking, backface culling reduces the number of pixels that the GPU has to fill. This is the default setting for newly created materials.
2.1.3. Physics
- 碰撞多边形网格(Collision meshes)不用像被渲染的那个模型那么精细,推荐用尽可能低分辨率的多边形网格作为碰撞检测的判断,甚至可以直接用Box。
- 在一个场景中,使动态刚体(rigid body)的数量尽可能地保持最少,特别是在手机上。
2.2. 性能分析器
- PlayCanvas Profiler
2.3. 性能最佳实践
- 能不用3D建模就不用3D建模,用贴图模拟3D模型,所谓裸眼3D的效果。
- 建模要尽可能简单,不能有太多
Mesh Instances
,Mesh Instance
最好只有一个。用PlayCanvas的Profiler
查看时,Mesh Instances
尽可能不超过50,Draw Calls
尽可能不起过50 。一个比较复杂的模型,只有一个Mesh Instance
,如图: - 不要使用动态打光,如果要用,尽可能少,强烈建议关闭灯光的阴影效果,用半透明贴图来代替。如图:
- 有些地方可以用
LightMap
,即光照贴图。 - 重复利用的对象应该使用对象池,可以用数组来作为最直接的对象池。这样可以避免新建对象时内存的分配和销毁对象时内存的回收,让垃圾回收机制省点心。所谓对象池:对象池就存放需要被反复调用资源的一个空间,比如游戏中要常被大量复制的对象,子弹,敌人,以及任何重复出现的对象。
- 地面可以是一个大的
Plane
,在Diffuse
上贴图,在Offet&Tiling
中调节,可以达到瓦片地图的效果。如下图: - 抗锯齿功能:
USE DEVICE PIXEL RATIO
,如果把它勾上,它的值是window.devicePixelRatio
,如果不选,它的值是1. 苹果的window.devicePixelRatio
的值是2,即我们平时说的两倍屏。为了好的体验,可以强制苹果使用devicePixelRatio
,而安卓机提供开关让其是否用设备的屏幕比例。在苹果上不可以用这个动态开关,否则当从勾选到勾选的时候,会导致只显示1/4屏幕的问题。这是PlayCanvas的Bug。
3. Q&A
- 这是开发时遇到过的一些问题和解决方法。
3.1. PlayCanvas里面的1Size在3DS Max里面是多少米?
- 在PlayCanvas中,想要把一个物体进行位移,只要
this.entity.translate(1, 0, 0);
就可以在X轴上平移1了。可是问题在于,这个1代表多少米呢?事实上,这个1代表是 1 size或者 1 unit,那 1 unit 又在哪里设置呢? - 请参考:1 size equals how many centimeters?
- 此坑后来官方也填了,见:PlayCanvas Units
3.2. 如何实现碰撞检测?
- 先看看官方文档中关于碰撞检测的资料:Collision。这里有比较关键的一段话:
- 如何实现,请看:collision-and-triggers。这里要注意的地方是:
- 可以看看这个表:CollisionComponent
3.3. 如何在游戏中使用贴图和特殊字体?
- 在PlayCanvas Document的首页中间,有一个Engine Users。有2D Images和text,一个是贴图,一个是字体。如果需要用到贴图或者特殊字体,就要把
sprite.js
或者font.js
上传到PlayCanvas的Project中。 - 字体:
- 其中sprite比较好处理,特殊字体比较难处理一点(暂时是这样)。因为PlayCanvas要一个由特殊字体组成的PNG和一个JSON文件。这里推荐一个在线编辑字体的网站:littera,但是这个网站导出字体的时候,只能导出PNG和fnt。注意:用littera导出字体的时候,选择Text(.fnt)。
- 因此,我们要把fnt格式转为json格式,就要用到这里面text提供的教程:
- 转化成json格式之后,还没完!这个json文件还有个错误:需要给某个key加上双引号。具体是哪个key,把文件拖到sublime里面一看就知道了(前提是sublime装了JSON Prettify插件)。
- 的确很烦,如果各位撸友有什么更方便的方法,分享一下呗。官方提供的两个编辑字体的软件,一个是太旧了(也只有window版的),另一个要收费。所以我选择的是littera。额…这里没有打广告。效果如图:
- 贴图:
- 贴图的话就简单多了,如图:
- 把图片拖进去,调整位置即可。效果是这样的:
- 可以看到,上面的按钮是可以被点击的,我们要做点击反馈,如点下去的时候缩小等等。因此,这里不推荐直接使用
sprite.js
提供的点击事件。每次点击,判断点击的位置和目标按钮的位置即可。代码如图: 3.4. 游戏在安卓版微信上声音无法播放?
- Entity的
Sound
中,有一个Positional
的选项,把勾勾去掉即可。详情请看下面链接: - Sound not play on WhChat(Android)
3.5. 我的代码能同步到Github上吗?
- 现在PlayCanvas用的是
Script2.0
,不可以同步到Github
了。 - 详情:Where’s the code tab in dashboard ?
3.6. 如何把游戏部署到我们自己服务器?
- 首先你得是个Personal Plan以上的高级帐户,才有Web Download的权限。
- 有这个权限后,其实很简单,只要在发布那里点击`Web Download即可。下载下来是一个zip文件,解压后push到服务器,Done !
- 但是有个问题,服务器的带宽是有限的,因此我们要把资源放到CDN上,只留
index.html
在服务器上就好了。这样又导致WebGL的一个跨域资源的问题,下面链接是解决方案,也会提及把资源和index.html
分离后,需要在index.html
里面个性什么。 - 详情:Cross origin error when self hosting。
- 这里有个很牛逼的提示:下载的时候是可以选择是否混淆代码的。如图:
3.7. 如何自定义loading界面?
- 首先你得是个Organization Plan高级帐户,才可以修改loading界面。
- 这是官方的教程:Loading Screen。
- 这里有个坑,就是当Fork一个Project的时候,Fork出来的那个项目的loadingScreen的脚本会自动取消关联。所以在
Editor
的设置的LoadingScreen
中,要重新选择一下LoadingScreen
的脚本。3.8. 如何使用HTML和CSS ?
- 直接上官方教程:html、css。
- 很简单,看了就懂了。
3.9. 替换资源的技巧?
- PlayCanvas上传资源很方便,直接拖到资源面板就行了。
- 有时候,美术可能会让你换一下贴图啊字体啊这类的。但是这个时候,你的贴图可能已经应该在几个
sprite.js
上了,这样改起来就不方便了。 - 于是,上传资源的时候,把资源的名字改成被替换的资源的名字就可以了,PlayCanvas会自动更新。
3.10. 如何把一个带Rigidbody的实体的惯性去掉?
3.11. 把transparent canvas选择打开后,带来的坑
- 首先说说
transparent canvas
的好处。打开之后,可以用DOM
来做背景,非常方便和简单。 - 但是,
transparent canvas
打开之后,当粒子后面(z轴)没有实体挡住它的时候,canvas
的transparent
会把粒子搞成白色。 - 粒子变白的情况也可能发生在用
sprite.js
生成的贴图上。 - 总的来说,
canvas
的透明通道可能和其他东西的透明通道有冲突吧。 - 因此,是不能把
transparent canvas
打开的方法简单地做一个背景的。3.12. 背景的处理?
- 游戏的背景用一个
Plane
实体来处理,把这个Plane
作为Camera
的一个子对象。然后把这个Plane
的大小,角度等调到合适的值即可。如图: 3.13. 如何给字体加动效?
- PlayCanvas暂时还没有提交比较好的
Tween
控制脚本,所以现在所以的动效都要自己去实现。 - 现在改变字体的大小只能用
font.js
中的maxResHeight
这个属性。听PlayCanvas的技术人员说,font.js2.0
已经在路上了。 - 目前,我们的动画只能自己在
update
函数中写。代码如下,详细的代码在萌宠快跑的InGame.js中。123456789101112131415161718192021222324252627// update code called every frameInGame.prototype.update = function(dt) {this.addScoreTween();this.countDownTween();};//放大缩小分数字样的动画InGame.prototype.addScoreTween = function() {if(this.scoreAdded) {if(this.becomeBigger) {this.scoreEntity.script.fontRenderer.maxResHeight -= this.scaleStep;this.scoreEntity.script.fontRenderer.y += this.moveStep;} else {this.scoreEntity.script.fontRenderer.maxResHeight += this.scaleStep;this.scoreEntity.script.fontRenderer.y -= this.moveStep;}this.count ++;if(this.count === (this.scaleTimes >> 1)) {this.becomeBigger = false;}if(this.count === this.scaleTimes) {this.becomeBigger = true;this.count = 0;this.scoreAdded = false;}}};
3.14. 粒子效果不正常?
- 这个粒子我觉得嘛,挺多的问题。不是特别完美。
- 上面也说了,
transparent canvas
会导致粒子变白,说白了也是透明通道。 - 所以,感觉这个粒子和有透明通道东西有会有冲突。
- 萌宠快跑的路块,本来是有
alpha
透明通道,于是导致了粒子有时候不显示,坑得一匹。 - Forum详情:http://forum.playcanvas.com/t/particle-not-play-again-sometimes/2588/6
- Github Issue: https://github.com/playcanvas/engine/issues/759
3.15. 替换材质球?
- 有时候我们要给实体换套皮肤什么的,这个时候就要换材质球了。
- 那我们知道,每个实体都可以有很多
Component
,材质球是在Model
这个Component
上。可以通过以下代码找到:this.entity.model.meshInstances
,这是一个数组,存的都是材质球。12var material = this.app.assets.findByTag('CatMaterial')[0];this.entity.model.meshInstances[0] = material;
3.16. 替换模型?
- 闲话休提,直接上代码。12var model = this.app.assets.findByTag('CatModel')[0];this.entity.model.asset = model;
3.17. 改变材质球参数?
首先找到这个实体的材质球。有个坑的地方,不能够用一个变量来指向这个材质球。演示个错识误的做法:
123var material = this.entity.model.meshInstances[0].material;material.opacity = 0.5;material.update();上面的代码这样是没有效的,这个时候
material
并没有指向材质球,而是复制了一份。所以正确的做法是:12this.entity.model.meshInstances[0].material.opacity = 0.5;this.entity.model.meshInstances[0].material.update();这里有个关于性能的问题得提一下,就是使用
this.entity.model.meshInstances[0].material.update();
的时候要注意:所以这里有些非官方的做法,这个就不用update了。
1this.entity.model.meshInstances[0].setParameter('material_opacity', 1);关于这个
material_opacity
是怎么来的,可以看这个:http://forum.playcanvas.com/t/i-have-some-problem-when-i-change-the-material/2412/53.18. 改变带rigidbody的实体角度或者位置?
- 当我们设置一个实体的位置、角度时,我们很自然地想到用
setPosition
、setEulerAngles
。 - 很不幸的是,如果用这两个方法给带有
rigidbody
的实体设置是无效的。 - 带有
rigidbody
的实体,去改变他的位置只能用teleport
。详情:http://developer.playcanvas.com/en/api/pc.RigidBodyComponent.html#teleport - 那角度呢?还是用teleport,暂时还没有能单独设置带rigidbody的实体的角度的方法。但是,已经在路上了。详情:https://github.com/playcanvas/engine/issues/782
3.19. Teleport父元素,子元素rigidbody失效?
- 其实并不是失效。
- 情况是这样的:Children’s Rigidbody Invalid when Teleport Parent’s Rigidbody