Skip to content

t1k:cocos:playable:unity-particle

FieldValue
Moduleplayable
Version0.15.0
Efforthigh
Tools

Keywords: ads, billboard, bubble, cocos, convert, curverange, emitter, gradientrange, mat, particle, particlesystem, playable, port, prefab, ripped, shapemodule, sizeovertime, unity, vfx

/t1k:cocos:playable:unity-particle

Unity 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.

InputRequiredDefaultExample
Unity .prefab absolute path✅ yesC:\Users\me\Downloads\Rip\ExportedProject\Assets\GameObject\BubbleInPipe.prefab
Cocos output folder (where to write the new .prefab)optionalassets/game-assets/vfx/Prefabs/3D/assets/game-assets/vfx/Prefabs/3D/
Output prefab nameoptionalsame as the Unity prefabBubbleInPipe
Blend overrideoptionalinfer from Unity .mat (transparent → alpha-blend, else additive)alpha-blend | additive
Import the source texture?optionalyesyes | reuse <existing texture uuid>

Copy-paste invocation:

Convert the Unity VFX at C:\…\BubbleInPipe.prefab into a Cocos particle prefab under assets/game-assets/vfx/Prefabs/3D/, name BubbleInPipe, 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.

  • A Unity .prefab whose GameObject has a ParticleSystem (!u!198) + ParticleSystemRenderer (!u!199).
  • You need it as a Cocos .prefab referencing a Cocos cc.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).

  1. Read the Unity source — the .prefab (ParticleSystem + ParticleSystemRenderer blocks) and the material it references (m_Materials[0] guid → .mat) to get the texture guid and blend (_Blend / _SrcBlend / _DstBlend / render queue).
  2. Find a Cocos skeleton — locate an existing project particle prefab with a single cc.ParticleSystem to 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.
  3. 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.
  4. Author .prefab + .meta — write the JSON, generate a fresh v4 uuid for the .meta (importer: "prefab", syncNodeName).
  5. Import texture + material — copy the Unity texture PNG into the project, hand-write its image .meta (clone a sibling PNG .meta, new uuid, same 6c48a texture sub-id), and make a builtin-particle material (see Material section).
  6. Validatenode -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.

Unity (ParticleSystem)Cocos (cc.ParticleSystem)
lengthInSecduration
loopingloop
playOnAwakeplayOnAwake
maxNumParticles_capacity
moveWithTransform 1 / scalingMode Local_simulationSpace 1, scaleSpace 1 (Local)
InitialModule.startLifetimestartLifetime (CurveRange)
startSpeedstartSpeed (CurveRange)
startSizestartSize/startSizeX (CurveRange)
startColorstartColor (cc.GradientRange)
gravityModifiergravityModifier
EmissionModule.rateOverTimerateOverTime
EmissionModule.m_Bursts[].countCurvebursts[].count (cc.Burst, _time, _repeatCount)

Unity minMaxState → Cocos CurveRange.mode

Section titled “Unity minMaxState → Cocos CurveRange.mode”
Unity minMaxStatemeaningCocos CurveRange
0constant — value is scalarmode: 0, constant: <scalar>
1curvemode: 1, splinecc.RealCurve
2two curvesmode: 2 (twoCurves)
3random between two constants — minScalar..scalarmode: 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.

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 _scale collapsed 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 _scale IS applied at runtime (ShapeModule.onInitconstructMat), so the box bounds emission correctly.
  • emitFrom enum (ParticleEmitLocation): 0 Base (Circle/Cone only) · 1 Edge · 2 Shell · 3 Volume. For a Box use 3 (Volume) to fill the box. ⚠️ 0 (Base) is INVALID for Box: the engine only rewrites Base→Volume inside the shapeType setter, which does NOT run when a hand-authored prefab is deserialized — so a hand-written "emitFrom": 0 on a Box makes boxEmit fall through to its default branch, leaving the particle position uninitialized → particles spawn at garbage/stale coordinates (looks “scattered / infinite / outside the box”). Always emit 3.
  • Carry randomDirectionAmount, randomPositionAmount, _position, _rotation.
  • Unity SizeModule curve → cc.SizeOvertimeModule enabled, size = CurveRange mode 1cc.RealCurve (_times/_values of cc.RealKeyframeValue). A growing 0.34→1 Unity curve becomes two keyframes value 0.34 → 1.
  • Unity ColorModule (disabled by default) → optional cc.ColorOvertimeModule. Adding a gentle alpha fade in/out (cc.Gradient alphaKeys 0→255→255→0) is a sane polish when Unity hard-cuts particles — flag it as a deviation.
Unity ParticleSystemRenderer.m_RenderModeCocos cc.ParticleSystemRenderer._renderMode
0 Billboard0 Billboard
1 Stretched1 StretchedBillboard
3 HorizontalBillboard2
4 VerticalBillboard3
5 Mesh4 (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.

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:

_techIdxBlendUse for
0Additiveglows, sparks, light orbs, neon
1Alpha-blendtranslucent/soft particles — smoke, glass bubbles, dust

Match the Unity material: transparent render queue + alpha _BlendtechIdx 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.CompPrefabInfo
4 startColor GradientRange 5..7 startSize{,Y,Z} CurveRange
8 startSpeed 9..11 startRotation{X,Y,Z} 12 startDelay 13 startLifetime
14 gravityModifier 15 rateOverTime 16 rateOverDistance 17 cc.Burst 18 burst count CurveRange
19 ColorOvertimeModule 20 GradientRange 21 cc.Gradient 22..25 cc.AlphaKey
26 ShapeModule 27 arcSpeed 28 SizeOvertimeModule 29 size CurveRange 30 cc.RealCurve 31..33 x/y/z
34 VelocityOvertimeModule 35..38 · 39 ForceOvertimeModule 40..42 · 43 LimitVelocityOvertimeModule 44..47
48 RotationOvertimeModule 49..51 · 52 TextureAnimationModule 53 CurveRange 54 RealCurve 55 CurveRange
56 NoiseModule · 57 TrailModule 58..61 · 62 cc.ParticleSystemRenderer · 63 cc.PrefabInfo

Each 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.

  • Clone the id-graph; never hand-number it. A wrong __id__ reference silently corrupts the prefab.
  • Box emitFrom MUST be 3 (Volume), never 0 (Base). Base is auto-corrected to Volume only in the editor shapeType setter, not on prefab load — a hand-authored emitFrom: 0 on a Box leaves emit positions uninitialized → particles scatter outside the box (the “spawns everywhere / infinite” bug). Same trap: Sphere/Hemisphere need 3 (Volume), not 0/1.
  • _mainTexture needs the @6c48a sub-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 0 means 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.