t1k:cocos:playable:unity-particle
| Field | Value |
|---|---|
| Module | playable |
| Version | 0.15.0 |
| Effort | high |
| Tools | — |
Keywords: ads, billboard, bubble, cocos, convert, curverange, emitter, gradientrange, mat, particle, particlesystem, playable, port, prefab, ripped, shapemodule, sizeovertime, unity, vfx
How to invoke
Section titled “How to invoke”/t1k:cocos:playable:unity-particleUnity ParticleSystem → Cocos 3.8.7 ParticleSystem
Section titled “Unity ParticleSystem → Cocos 3.8.7 ParticleSystem”Port a Unity ParticleSystem prefab (e.g. from a ripped/exported project) into a Cocos Creator 3.8.7 cc.ParticleSystem prefab. Cocos has no editor importer for Unity particles, so the prefab is hand-authored as JSON by cloning the serialization shape of a known-good Cocos particle prefab and rewriting the module values from the Unity source.
Inputs — what to provide when you invoke this skill
Section titled “Inputs — what to provide when you invoke this skill”Provide at least the absolute path to the Unity ParticleSystem prefab. Everything else (its material .mat + texture) is resolved from that file.
| Input | Required | Default | Example |
|---|---|---|---|
Unity .prefab absolute path | ✅ yes | — | C:\Users\me\Downloads\Rip\ExportedProject\Assets\GameObject\BubbleInPipe.prefab |
Cocos output folder (where to write the new .prefab) | optional | assets/game-assets/vfx/Prefabs/3D/ | assets/game-assets/vfx/Prefabs/3D/ |
| Output prefab name | optional | same as the Unity prefab | BubbleInPipe |
| Blend override | optional | infer from Unity .mat (transparent → alpha-blend, else additive) | alpha-blend | additive |
| Import the source texture? | optional | yes | yes | reuse <existing texture uuid> |
Copy-paste invocation:
Convert the Unity VFX at
C:\…\BubbleInPipe.prefabinto a Cocos particle prefab underassets/game-assets/vfx/Prefabs/3D/, nameBubbleInPipe, alpha-blend.
⛔ If the absolute .prefab path is missing, STOP and ask for it before doing anything else — never guess a path. If the output folder is omitted, use the default above and say so.
When to use
Section titled “When to use”- A Unity
.prefabwhose GameObject has aParticleSystem(!u!198) +ParticleSystemRenderer(!u!199). - You need it as a Cocos
.prefabreferencing a Cocoscc.Material+cc.Texture2D.
Not for: Unity VFX Graph / Shuriken sub-emitter trees with mesh particles (only the common billboard case is covered here — flag the gaps).
Workflow
Section titled “Workflow”- Read the Unity source — the
.prefab(ParticleSystem+ParticleSystemRendererblocks) and the material it references (m_Materials[0]guid →.mat) to get the texture guid and blend (_Blend/_SrcBlend/_DstBlend/ render queue). - Find a Cocos skeleton — locate an existing project particle prefab with a single
cc.ParticleSystemto clone the exact__id__graph (grep'"__type__": "cc.ParticleSystem"'and pick the file with one match). Never author the id-graph from memory — clone it. - Map the values — use the tables below to translate every module. Keep the skeleton’s
__id__layout identical; only change scalar values, curve keys, enum ints, and material/texture uuids. - Author
.prefab+.meta— write the JSON, generate a fresh v4 uuid for the.meta(importer: "prefab",syncNodeName). - Import texture + material — copy the Unity texture PNG into the project, hand-write its image
.meta(clone a sibling PNG.meta, new uuid, same6c48atexture sub-id), and make a builtin-particle material (see Material section). - Validate —
node -e "JSON.parse(fs.readFileSync(...))"on every file; grep the new uuids project-wide to confirm zero collisions; confirm the prefab no longer references the skeleton’s original material/texture uuids.
Cannot visually verify without the editor — say so. The editor reimports the new assets on focus; the prefab renders only after import.
Parameter mapping (Unity → Cocos)
Section titled “Parameter mapping (Unity → Cocos)”Emitter / main module
Section titled “Emitter / main module”Unity (ParticleSystem) | Cocos (cc.ParticleSystem) |
|---|---|
lengthInSec | duration |
looping | loop |
playOnAwake | playOnAwake |
maxNumParticles | _capacity |
moveWithTransform 1 / scalingMode Local | _simulationSpace 1, scaleSpace 1 (Local) |
InitialModule.startLifetime | startLifetime (CurveRange) |
startSpeed | startSpeed (CurveRange) |
startSize | startSize/startSizeX (CurveRange) |
startColor | startColor (cc.GradientRange) |
gravityModifier | gravityModifier |
EmissionModule.rateOverTime | rateOverTime |
EmissionModule.m_Bursts[].countCurve | bursts[].count (cc.Burst, _time, _repeatCount) |
Unity minMaxState → Cocos CurveRange.mode
Section titled “Unity minMaxState → Cocos CurveRange.mode”Unity minMaxState | meaning | Cocos CurveRange |
|---|---|---|
| 0 | constant — value is scalar | mode: 0, constant: <scalar> |
| 1 | curve | mode: 1, spline → cc.RealCurve |
| 2 | two curves | mode: 2 (twoCurves) |
| 3 | random between two constants — minScalar..scalar | mode: 3, constantMin, constantMax |
⚠️ Unity-export quirk: a burst countCurve with minMaxState: 0 sometimes serializes scalar: 0 while minScalar holds the real authored value (e.g. 30). Trust the non-zero minScalar (and confirm with the user) rather than emitting a 0-count burst.
Shape module
Section titled “Shape module”Unity ShapeModule.type ints — Cocos ShapeModule.shapeType: 0 Box · 1 Circle · 2 Cone · 3 Sphere · 4 Hemisphere. Cocos has no Edge/Mesh shape.
- Unity Edge (
type: 12) → Cocos Box (shapeType 0,emitFrom 3= Volume) with_scalecollapsed on two axes to a thin line (e.g. a vertical column ={x:0.4, y:<len>, z:0.4}).radius(edge length) → the long-axis_scale. The shape’s_scaleIS applied at runtime (ShapeModule.onInit→constructMat), so the box bounds emission correctly. emitFromenum (ParticleEmitLocation):0 Base(Circle/Cone only) ·1 Edge·2 Shell·3 Volume. For a Box use3(Volume) to fill the box. ⚠️0(Base) is INVALID for Box: the engine only rewrites Base→Volume inside theshapeTypesetter, which does NOT run when a hand-authored prefab is deserialized — so a hand-written"emitFrom": 0on a Box makesboxEmitfall through to itsdefaultbranch, leaving the particle position uninitialized → particles spawn at garbage/stale coordinates (looks “scattered / infinite / outside the box”). Always emit3.- Carry
randomDirectionAmount,randomPositionAmount,_position,_rotation.
Size-over-life, color-over-life
Section titled “Size-over-life, color-over-life”- Unity
SizeModulecurve →cc.SizeOvertimeModuleenabled,size=CurveRange mode 1→cc.RealCurve(_times/_valuesofcc.RealKeyframeValue). A growing 0.34→1 Unity curve becomes two keyframes value 0.34 → 1. - Unity
ColorModule(disabled by default) → optionalcc.ColorOvertimeModule. Adding a gentle alpha fade in/out (cc.GradientalphaKeys 0→255→255→0) is a sane polish when Unity hard-cuts particles — flag it as a deviation.
Renderer
Section titled “Renderer”Unity ParticleSystemRenderer.m_RenderMode | Cocos cc.ParticleSystemRenderer._renderMode |
|---|---|
| 0 Billboard | 0 Billboard |
| 1 Stretched | 1 StretchedBillboard |
| 3 HorizontalBillboard | 2 |
| 4 VerticalBillboard | 3 |
| 5 Mesh | 4 (needs _mesh) |
Set _cpuMaterial and _materials[0] to the Cocos material; set _mainTexture to the texture’s <uuid>@6c48a sub-asset; _alignSpace: 2.
Unity ParticleSystemRenderer.sortingOrder / sortingFudge has no direct Cocos particle-renderer field to map. Reproduce the intended draw order manually through the Cocos node hierarchy: put the competing emitters on sibling nodes under an empty container, then order those siblings from back to front. If Unity’s highest-sorted emitter is also the parent, move that emitter onto a new child node so it can be placed after the other emitters; leaving the particle component on the parent cannot express the same sibling ordering.
Material & texture import (critical)
Section titled “Material & texture import (critical)”The particle material must use the builtin-particle effect — reuse the effect uuid an existing project particle material already references (it is a builtin, not a local .effect file). The blend is chosen by _techIdx:
_techIdx | Blend | Use for |
|---|---|---|
| 0 | Additive | glows, sparks, light orbs, neon |
| 1 | Alpha-blend | translucent/soft particles — smoke, glass bubbles, dust |
Match the Unity material: transparent render queue + alpha _Blend → techIdx 1; additive → techIdx 0. Clone a sibling .mtl of the right techIdx, swap _props[0].mainTexture to the imported texture’s @6c48a uuid, set tintColor, and write a .meta (importer: "material", new uuid).
The builtin-particle tinted fragment shader multiplies 2.0 * vertexColor * tintColor * texture. Therefore the neutral/no-op material tint is 0.5 grey: { r: 128, g: 128, b: 128, a: 128 } in 0–255 color values (equivalently 0.5, 0.5, 0.5, 0.5), not white. A white {255,255,255,255} tint makes the 2.0 factor double both RGB and alpha, which can turn soft/translucent particles bright, opaque, and hard-edged.
Texture import: copy the PNG, clone a sibling image .meta, give it a fresh top-level uuid and the same 6c48a texture sub-id (<uuid>@6c48a is what the renderer references), keep redirect → <uuid>@6c48a.
Cocos cc.ParticleSystem id-graph (skeleton)
Section titled “Cocos cc.ParticleSystem id-graph (skeleton)”A single-PS prefab is a flat array of objects referenced by __id__. Canonical layout (clone, don’t memorize):
0 cc.Prefab 1 cc.Node 2 cc.ParticleSystem 3 cc.CompPrefabInfo4 startColor GradientRange 5..7 startSize{,Y,Z} CurveRange8 startSpeed 9..11 startRotation{X,Y,Z} 12 startDelay 13 startLifetime14 gravityModifier 15 rateOverTime 16 rateOverDistance 17 cc.Burst 18 burst count CurveRange19 ColorOvertimeModule 20 GradientRange 21 cc.Gradient 22..25 cc.AlphaKey26 ShapeModule 27 arcSpeed 28 SizeOvertimeModule 29 size CurveRange 30 cc.RealCurve 31..33 x/y/z34 VelocityOvertimeModule 35..38 · 39 ForceOvertimeModule 40..42 · 43 LimitVelocityOvertimeModule 44..4748 RotationOvertimeModule 49..51 · 52 TextureAnimationModule 53 CurveRange 54 RealCurve 55 CurveRange56 NoiseModule · 57 TrailModule 58..61 · 62 cc.ParticleSystemRenderer · 63 cc.PrefabInfoEach tunable lives in its own cc.CurveRange object (mode 0 constant / mode 3 min-max / mode 1 spline). Disabled modules keep _enable: false with zeroed CurveRanges — leave them intact from the skeleton.
See references/conversion-checklist.md for the step-by-step file-by-file checklist.
Gotchas
Section titled “Gotchas”- Clone the id-graph; never hand-number it. A wrong
__id__reference silently corrupts the prefab. - Box
emitFromMUST be3(Volume), never0(Base). Base is auto-corrected to Volume only in the editorshapeTypesetter, not on prefab load — a hand-authoredemitFrom: 0on a Box leaves emit positions uninitialized → particles scatter outside the box (the “spawns everywhere / infinite” bug). Same trap: Sphere/Hemisphere need3(Volume), not0/1. _mainTextureneeds the@6c48asub-uuid, not the bare image uuid.- techIdx is the #1 fidelity bug: additive vs alpha-blend completely changes the look; a glass bubble on additive becomes a glowing ring.
- Unity particle sorting is a hierarchy conversion step, not a field mapping. Make competing emitters siblings and order them back-to-front; move a highest-sorted parent emitter onto its own last child when necessary.
- uuid uniqueness: grep every generated uuid across
assets/before finishing. - Tiny Unity sizes (0.1–0.15) stay faithful but may look invisible at the project’s scene scale — keep the values and tell the user to scale the node, don’t silently inflate.
startSpeed 0means particles don’t move (grow/fade in place) — preserve it; don’t “helpfully” add velocity unless asked.- Run from the parent project root, not inside a submodule.