# Particle System Egret v.s Cocos Creator ## 重點先說 Creator 3D 目前只有 unity-like 的獨特粒子特效, 和 2d 泛用的粒子特效規格完全不同. 以下以未來 cc3d & 2d 合併的前提下, 研究 egret 與 cc2d 的粒子特效規格轉換的可行性. ## cocos 官方推薦編輯器之一 - [Particle2dx For EffectHub](http://www.effecthub.com/particle2dx) - design particle for Cocos2dx CoronaSDK - plist 內可含 Base64 的圖片資訊 - Particle2dx 無法表演旋轉的屬性 ## 差異 - 副檔名: egret `.json`, cc `.plist`. - 坐標系: egret `左手`, cc `右手`. 因此在2D世界中, Y軸要反轉. 影響屬性為: - gravityy - 顏色: egret `0-255`, cc `0.0-1.0`. - 影響屬性為: - finishColorBlue - finishColorVarianceBlue - finishColorGreen - finishColorRed - finishColorVarianceRed - finishColorVarianceGreen - startColorBlue - startColorVarianceBlue - startColorGreen - startColorVarianceGreen - startColorRed - startColorVarianceRed - 但 alpha 兩邊都是 `0.0-1.0`. - 時長: egret `毫秒(ms)`, cc `秒(s)`. - 影響屬性為: - duration - particleLifespan - particleLifespanVariance - 混和類型: egret `係數類型字串`, cc `係數(0.0-1.0)` - `Egret Feather` 可吃該格式, 但無法從編輯介面調整. - 影響屬性為: - blendFuncDestination - blendFuncSource - egret 預設值為 `src: one, dst: oneMinusSourceAlpha` 對應 cc 數值為 `src: 1, dst: 771` - particle2dx 會直接使用 cc2d 的功能設定數值, [Cocos2d-html5-v2.2.3.min.js](https://github.com/mash76/particle2dx/blob/master/Cocos2d-html5-v2.2.3.min.js) 相關的參數: ```javascript= cc.BLEND_SRC = cc.OPTIMIZE_BLEND_FUNC_FOR_PREMULTIPLIED_ALPHA ? 1 : 770; cc.BLEND_DST = 771; gl.ONE = 1; gl.ZERO = 0; gl.SRC_ALPHA = 770; gl.ONE_MINUS_SRC_ALPHA = 771; gl.ONE_MINUS_DST_COLOR = 775; ``` - 大多屬性都是一對一的, 除了: - `sourcePositionx` & `sourcePositiony` 來自 `emitter {x,y}` - `sourcePositionVariancex` & `sourcePositionVariancey` 來自 `emitterVariance {x,y}` - `gravityx` & `gravityy` 來自 `gravity {x,y}` - `minRadiusVariance` 理論上 cc 也吃, 但[編輯器模板](https://github.com/mash76/particle2dx/blob/master/particle/template.plist)沒有此屬性. - `plist` 可以將圖片直接以 Base64 的形式儲存在 `textureImageData`, egret 沒有相應的欄位. ## 轉換器本體 ### e2c v2.0 - 仿造 particle2dx 替換 template 特殊字串的做法. (原本為逐一添加) - 以 cc 所需參數為主導的方式 mapping.(前一版以 egret 屬性為主) - 搭配 `eval` & json data 簡化複雜度 ```javascript= // 直接複製到 chome console 即可執行 // v2.0 主要參考 mash76/particle2dx 採用模板替換關鍵字的作法 // template base on https://github.com/mash76/particle2dx/blob/master/particle/template.plist, 增加 minRadiusVariance & rotationEndVariance. // <?xml version="1.0" encoding="UTF-8"?> // <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> // <plist version="1.0"> // <dict> // <key>angle</key> // <real>__angle__</real> // <key>angleVariance</key> // <real>__angleVar__</real> // <key>blendFuncDestination</key> // <integer>__blendFuncDestination__</integer> // <key>blendFuncSource</key> // <integer>__blendFuncSource__</integer> // <key>duration</key> // <real>__duration__</real> // <key>emitterType</key> // <real>__emitterMode__</real> // <key>finishColorAlpha</key> // <real>__end_a__</real> // <key>finishColorBlue</key> // <real>__end_b__</real> // <key>finishColorGreen</key> // <real>__end_g__</real> // <key>finishColorRed</key> // <real>__end_r__</real> // <key>finishColorVarianceAlpha</key> // <real>__end_a_var__</real> // <key>finishColorVarianceBlue</key> // <real>__end_b_var__</real> // <key>finishColorVarianceGreen</key> // <real>__end_g_var__</real> // <key>finishColorVarianceRed</key> // <real>__end_r_var__</real> // <key>finishParticleSize</key> // <real>__endSize__</real> // <key>finishParticleSizeVariance</key> // <real>__endSizeVar__</real> // <key>gravityx</key> // <real>__grav_x__</real> // <key>gravityy</key> // <real>__grav_y__</real> // <key>maxParticles</key> // <real>__maxParticles__</real> // <key>maxRadius</key> // <real>__maxRadius__</real> // <key>maxRadiusVariance</key> // <real>__maxRadiusVar__</real> // <key>minRadius</key> // <real>__minRadius__</real> // <key>minRadiusVariance</key> // <real>__minRadiusVar__</real> // <key>particleLifespan</key> // <real>__life__</real> // <key>particleLifespanVariance</key> // <real>__lifeVar__</real> // <key>radialAccelVariance</key> // <real>__rad_accel_var__</real> // <key>radialAcceleration</key> // <real>__rad_accel__</real> // <key>rotatePerSecond</key> // <real>__rotatePerSecond__</real> // <key>rotatePerSecondVariance</key> // <real>__rotatePerSecondVar__</real> // <key>rotationEnd</key> // <real>__endSpin__</real> // <key>rotationEndVariance</key> // <real>__endSpinVar__</real> // <key>rotationStart</key> // <real>__startSpin__</real> // <key>rotationStartVariance</key> // <real>__startSpinVar__</real> // <key>sourcePositionVariancex</key> // <real>__posx_var__</real> // <key>sourcePositionVariancey</key> // <real>__posy_var__</real> // <key>sourcePositionx</key> // <real>__posx__</real> // <key>sourcePositiony</key> // <real>__posy__</real> // <key>speed</key> // <real>__speed__</real> // <key>speedVariance</key> // <real>__speedVar__</real> // <key>startColorAlpha</key> // <real>__start_a__</real> // <key>startColorBlue</key> // <real>__start_b__</real> // <key>startColorGreen</key> // <real>__start_g__</real> // <key>startColorRed</key> // <real>__start_r__</real> // <key>startColorVarianceAlpha</key> // <real>__start_a_var__</real> // <key>startColorVarianceBlue</key> // <real>__start_b_var__</real> // <key>startColorVarianceGreen</key> // <real>__start_g_var__</real> // <key>startColorVarianceRed</key> // <real>__start_r_var__</real> // <key>startParticleSize</key> // <real>__startSize__</real> // <key>startParticleSizeVariance</key> // <real>__startSizeVar__</real> // <key>tangentialAccelVariance</key> // <real>__tan_accel_var__</real> // <key>tangentialAcceleration</key> // <real>__tan_accel__</real> // <key>textureFileName</key> // <string>__textureFileName__.png</string> // <key>textureImageData</key> // <string>__png_base64__</string> // </dict> // </plist> var e2c = { mappingData: { "angle": { "path": "emitAngle", "replacement": "__angle__" }, "angleVariance": { "path": "emitAngleVariance", "replacement": "__angleVar__" }, "blendFuncDestination": { "path": "blendFactorDestination", "replacement": "__blendFuncDestination__" }, "blendFuncSource": { "path": "blendFactorSource", "replacement": "__blendFuncSource__" }, "duration": { "path": "duration", "replacement": "__duration__" }, "emitterType": { "path": "emitterType", "replacement": "__emitterMode__" }, "finishColorAlpha": { "path": "endAlpha", "replacement": "__end_a__" }, "finishColorBlue": { "path": "endBlue", "replacement": "__end_b__" }, "finishColorGreen": { "path": "endGreen", "replacement": "__end_g__" }, "finishColorRed": { "path": "endRed", "replacement": "__end_r__" }, "finishColorVarianceAlpha": { "path": "endAlphaVariance", "replacement": "__end_a_var__" }, "finishColorVarianceBlue": { "path": "endBlueVariance", "replacement": "__end_b_var__" }, "finishColorVarianceGreen": { "path": "engGreenVariance", "replacement": "__end_g_var__" }, "finishColorVarianceRed": { "path": "endRedVariance", "replacement": "__end_r_var__" }, "finishParticleSize": { "path": "endSize", "replacement": "__endSize__" }, "finishParticleSizeVariance": { "path": "endSizeVariance", "replacement": "__endSizeVar__" }, "gravityx": { "path": "gravity.x", "replacement": "__grav_x__" }, "gravityy": { "path": "gravity.y", "replacement": "__grav_y__" }, "maxParticles": { "path": "maxParticles", "replacement": "__maxParticles__" }, "maxRadius": { "path": "maxRadius", "replacement": "__maxRadius__" }, "maxRadiusVariance": { "path": "maxRadiusVariance", "replacement": "__maxRadiusVar__" }, "minRadius": { "path": "minRadius", "replacement": "__minRadius__" }, "minRadiusVariance": { "path": "minRadiusVariance", "replacement": "__minRadiusVar__" }, "particleLifespan": { "path": "lifespan", "replacement": "__life__" }, "particleLifespanVariance": { "path": "lifespanVariance", "replacement": "__lifeVar__" }, "radialAccelVariance": { "path": "radialAccelerationVariance", "replacement": "__rad_accel_var__" }, "radialAcceleration": { "path": "radialAcceleration", "replacement": "__rad_accel__" }, "rotatePerSecond": { "path": "rotatePerSecond", "replacement": "__rotatePerSecond__" }, "rotatePerSecondVariance": { "path": "rotatePerSecondVariance", "replacement": "__rotatePerSecondVar__" }, "rotationEnd": { "path": "endRotation", "replacement": "__endSpin__" }, "rotationEndVariance": { "path": "endRotationVariance", "replacement": "__endSpinVar__" }, "rotationStart": { "path": "startRotation", "replacement": "__startSpin__" }, "rotationStartVariance": { "path": "startRotationVariance", "replacement": "__startSpinVar__" }, "sourcePositionVariancex": { "path": "emitterVariance.x", "replacement": "__posx_var__" }, "sourcePositionVariancey": { "path": "emitterVariance.y", "replacement": "__posy_var__" }, "sourcePositionx": { "path": "emitter.x", "replacement": "__posx__" }, "sourcePositiony": { "path": "emitter.y", "replacement": "__posy__" }, "speed": { "path": "speed", "replacement": "__speed__" }, "speedVariance": { "path": "speedVariance", "replacement": "__speedVar__" }, "startColorAlpha": { "path": "startAlpha", "replacement": "__start_a__" }, "startColorBlue": { "path": "startBlue", "replacement": "__start_b__" }, "startColorGreen": { "path": "startGreen", "replacement": "__start_g__" }, "startColorRed": { "path": "startRed", "replacement": "__start_r__" }, "startColorVarianceAlpha": { "path": "startAlphaVariance", "replacement": "__start_a_var__" }, "startColorVarianceBlue": { "path": "startBlueVariance", "replacement": "__start_b_var__" }, "startColorVarianceGreen": { "path": "startGreenVariance", "replacement": "__start_g_var__" }, "startColorVarianceRed": { "path": "startRedVariance", "replacement": "__start_r_var__" }, "startParticleSize": { "path": "startSize", "replacement": "__startSize__" }, "startParticleSizeVariance": { "path": "startSizeVariance", "replacement": "__startSizeVar__" }, "tangentialAccelVariance": { "path": "tangentialAccelerationVariance", "replacement": "__tan_accel_var__" }, "tangentialAcceleration": { "path": "tangentialAcceleration", "replacement": "__tan_accel__" }, "textureFileName": { "path": "texture", "replacement": "__textureFileName__" } }, getValue(mappedKey, value) { switch (mappedKey) { case "duration": if (value != -1) value /= 1000; break; case "particleLifespan": case "particleLifespanVariance": value /= 1000; break; case "finishColorBlue": case "finishColorVarianceBlue": case "finishColorGreen": case "finishColorRed": case "finishColorVarianceRed": case "finishColorVarianceGreen": case "startColorBlue": case "startColorVarianceBlue": case "startColorGreen": case "startColorVarianceGreen": case "startColorRed": case "startColorVarianceRed": // 之後要轉換可在這裡 switch value /= 255; break; case "blendFuncDestination": // 之後要轉換可在這裡 switch value = 711; break; case "blendFuncSource": value = 1; break; } return value; }, phase(src) { let result = '<?xml version="1.0" encoding="UTF-8"?>\r\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\r\n<plist version="1.0">\r\n<dict>\r\n\t<key>angle</key>\r\n\t<real>__angle__</real>\r\n\t<key>angleVariance</key>\r\n\t<real>__angleVar__</real>\r\n\t<key>blendFuncDestination</key>\r\n\t<integer>__blendFuncDestination__</integer>\r\n\t<key>blendFuncSource</key>\r\n\t<integer>__blendFuncSource__</integer>\r\n\t<key>duration</key>\r\n\t<real>__duration__</real>\r\n\t<key>emitterType</key>\r\n\t<real>__emitterMode__</real>\r\n\t<key>finishColorAlpha</key>\r\n\t<real>__end_a__</real>\r\n\t<key>finishColorBlue</key>\r\n\t<real>__end_b__</real>\r\n\t<key>finishColorGreen</key>\r\n\t<real>__end_g__</real>\r\n\t<key>finishColorRed</key>\r\n\t<real>__end_r__</real>\r\n\t<key>finishColorVarianceAlpha</key>\r\n\t<real>__end_a_var__</real>\r\n\t<key>finishColorVarianceBlue</key>\r\n\t<real>__end_b_var__</real>\r\n\t<key>finishColorVarianceGreen</key>\r\n\t<real>__end_g_var__</real>\r\n\t<key>finishColorVarianceRed</key>\r\n\t<real>__end_r_var__</real>\r\n\t<key>finishParticleSize</key>\r\n\t<real>__endSize__</real>\r\n\t<key>finishParticleSizeVariance</key>\r\n\t<real>__endSizeVar__</real>\r\n\t<key>gravityx</key>\r\n\t<real>__grav_x__</real>\r\n\t<key>gravityy</key>\r\n\t<real>__grav_y__</real>\r\n\t<key>maxParticles</key>\r\n\t<real>__maxParticles__</real>\r\n\t<key>maxRadius</key>\r\n\t<real>__maxRadius__</real>\r\n\t<key>maxRadiusVariance</key>\r\n\t<real>__maxRadiusVar__</real>\r\n\t<key>minRadius</key>\r\n\t<real>__minRadius__</real>\r\n\t<key>minRadiusVariance</key>\r\n\t<real>__minRadiusVar__</real>\r\n\t<key>particleLifespan</key>\r\n\t<real>__life__</real>\r\n\t<key>particleLifespanVariance</key>\r\n\t<real>__lifeVar__</real>\r\n\t<key>radialAccelVariance</key>\r\n\t<real>__rad_accel_var__</real>\r\n\t<key>radialAcceleration</key>\r\n\t<real>__rad_accel__</real>\r\n\t<key>rotatePerSecond</key>\r\n\t<real>__rotatePerSecond__</real>\r\n\t<key>rotatePerSecondVariance</key>\r\n\t<real>__rotatePerSecondVar__</real>\r\n\t<key>rotationEnd</key>\r\n\t<real>__endSpin__</real>\r\n\t<key>rotationEndVariance</key>\r\n\t<real>__endSpinVar__</real>\r\n\t<key>rotationStart</key>\r\n\t<real>__startSpin__</real>\r\n\t<key>rotationStartVariance</key>\r\n\t<real>__startSpinVar__</real>\r\n\t<key>sourcePositionVariancex</key>\r\n\t<real>__posx_var__</real>\r\n\t<key>sourcePositionVariancey</key>\r\n\t<real>__posy_var__</real>\r\n\t<key>sourcePositionx</key>\r\n\t<real>__posx__</real>\r\n\t<key>sourcePositiony</key>\r\n\t<real>__posy__</real>\r\n\t<key>speed</key>\r\n\t<real>__speed__</real>\r\n\t<key>speedVariance</key>\r\n\t<real>__speedVar__</real>\r\n\t<key>startColorAlpha</key>\r\n\t<real>__start_a__</real>\r\n\t<key>startColorBlue</key>\r\n\t<real>__start_b__</real>\r\n\t<key>startColorGreen</key>\r\n\t<real>__start_g__</real>\r\n\t<key>startColorRed</key>\r\n\t<real>__start_r__</real>\r\n\t<key>startColorVarianceAlpha</key>\r\n\t<real>__start_a_var__</real>\r\n\t<key>startColorVarianceBlue</key>\r\n\t<real>__start_b_var__</real>\r\n\t<key>startColorVarianceGreen</key>\r\n\t<real>__start_g_var__</real>\r\n\t<key>startColorVarianceRed</key>\r\n\t<real>__start_r_var__</real>\r\n\t<key>startParticleSize</key>\r\n\t<real>__startSize__</real>\r\n\t<key>startParticleSizeVariance</key>\r\n\t<real>__startSizeVar__</real>\r\n\t<key>tangentialAccelVariance</key>\r\n\t<real>__tan_accel_var__</real>\r\n\t<key>tangentialAcceleration</key>\r\n\t<real>__tan_accel__</real>\r\n\t<key>textureFileName</key>\r\n\t<string>__textureFileName__.png</string>\r\n\t<key>textureImageData</key>\r\n\t<string>__png_base64__</string>\r\n</dict>\r\n</plist>'; for (const key in this.mappingData) { const data = this.mappingData[key]; const value = this.getValue(key, eval(`src.${data.path}`)); if (value != null) { console.log(`${data.replacement}: ${value}`); result = result.replace(data.replacement, value); } } result = result.replace("__png_base64__", ""); // 如果想要 image in plist 可以塞進這欄位 console.log(result); } }; ``` --- ### 使用範例 ```javascript= e2c.phase( { "texture": "ttt.png", "gravity": { "x": 0, "y": 500 }, "radialAcceleration": 38.85, "maxParticles": 30, "radialAccelerationVariance": 0, "emitterType": 0, "engGreenVariance": 0, "emitterVariance": { "x": 100, "y": 50 }, "endAlpha": 1, "tangentialAccelerationVariance": 0, "startAlpha": 0.8901960784313725, "duration": -1, "startBlueVariance": 0, "maxRadiusVariance": 30, "lifespan": 2230, "blendFactorDestination": "oneMinusSourceAlpha", "minRadius": 20, "lifespanVariance": 700, "minRadiusVariance": 10, "startSize": 30, "blendFactorSource": "one", "rotatePerSecond": 30, "startSizeVariance": 10, "rotatePerSecondVariance": 10, "startBlue": 255, "startRedVariance": 0, "emitter": { "x": -12, "y": 13 }, "endSizeVariance": 5, "tangentialAcceleration": 0, "emitAngle": 260, "endSize": 20, "emitAngleVariance": 360, "startAlphaVariance": 0, "startRotation": 0, "startGreenVariance": 0, "startRotationVariance": 45, "endRedVariance": 0, "endAlphaVariance": 0, "endRotation": 263.58, "speed": 150, "endRotationVariance": 292.07, "maxRadius": 100, "endRed": 255, "startRed": 255, "endBlue": 255, "speedVariance": 0, "endBlueVariance": 0, "startGreen": 255, "endGreen": 255, } ) ``` ###### tags: `Egret` `cocos creator` `particle system` `plist`