# 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`