t1k:cocos:playable:particle-ui
| Field | Value |
|---|---|
| Module | playable |
| Version | 0.15.0 |
| Effort | medium |
| Tools | — |
Keywords: 2d, 3d to ui, ads, canvas, cocos, invisible, node scale, particle, particlesystem, playable, render particle in ui, scale particle, tiny, too small, ui, uimeshrenderer, uitransform, vfx
How to invoke
Section titled “How to invoke”/t1k:cocos:playable:particle-uiCocos 3D ParticleSystem → 2D / UI
Section titled “Cocos 3D ParticleSystem → 2D / UI”Make a 3D cc.ParticleSystem prefab render correctly (and at a usable size) inside a Canvas / UI tree. Two independent steps, both applied by scripts/particle-3d-to-ui.cjs (pure file-based, no editor/MCP needed):
- UI components — a 3D particle will NOT draw under a Canvas until its node has a
cc.UIMeshRenderer. Addcc.UITransform+cc.UIMeshRendererto every particle node; addcc.UITransformto the root node. - Scale by data, never by node.scale — UI/Canvas space is pixel-scaled (huge vs. 3D metres), so a ported effect looks microscopic. Multiply the spatial params by a factor. Do NOT set
node.setScale()/_lscale— Cocos scales the simulation oddly and the particles smear/clip (this is the whole reason the skill exists).
# add UI comps AND scale x60 in one pass (recommended first run)node scripts/particle-3d-to-ui.cjs --ui --scale 60 assets/.../Hit_VFX.prefab assets/.../Death_VFX.prefab# tune only (already UI-ready): multiply another x3, or shrink /3 with --scale 0.3333node scripts/particle-3d-to-ui.cjs --scale 3 assets/.../Hit_VFX.prefab--ui is idempotent (never double-adds components). --scale compounds on each run.
How big? — the scale factor
Section titled “How big? — the scale factor”A 3D effect authored in world-units (Unity/Cocos sizes ~0.1–8) must become tens of pixels in a 1080×1920 UI. Empirically:
| Design resolution | Good starting factor | Notes |
|---|---|---|
| 1080×1920 (portrait playable) | ×50–60 | ×60 was the sweet spot in production (CarBattle VFX). |
| 720×1280 | ×30–40 | scale roughly with design width |
Tuning method: run --ui --scale 20 first, view in-editor, then step by ×3 / ÷3 (--scale 3, --scale 0.3333) until the effect fills the intended UI area. 3–4 steps converges. Judge by eye against the UI, not the 3D scene.
What gets scaled (and what must NOT)
Section titled “What gets scaled (and what must NOT)”Scaled ×N (length / velocity / acceleration): startSize(+X/Y/Z), startSpeed, gravityModifier, shape radius/length/position, velocity / force / limit-velocity modules, child-node local positions. rateOverDistance is divided by N (particles-per-distance → keep trail density).
Left unchanged (time / angle / color / count): lifetime, rotation, start color + alpha, burst counts, rateOverTime, the size-over-life curve (it is a normalized 0–1 multiplier — magnitude already lives in startSize), texture-sheet, renderer lengthScale/velocityScale.
Verify after running
Section titled “Verify after running”node -e 'const p=JSON.parse(require("fs").readFileSync(process.argv[1])); const psN=new Set(p.filter(o=>o.__type__=="cc.ParticleSystem").map(o=>o.node.__id__)); for(const n of psN){const c=(p[n]._components||[]).map(x=>p[x.__id__].__type__); if(c.filter(t=>t=="cc.UITransform").length!=1||c.filter(t=>t=="cc.UIMeshRenderer").length!=1)throw"bad "+n;} const ids=p.filter(o=>o.__type__=="cc.CompPrefabInfo").map(o=>o.fileId); if(new Set(ids).size!=ids.length)throw"dup fileId"; console.log("OK");' your.prefabConfirm: each PS node has exactly one cc.UITransform + one cc.UIMeshRenderer; root has cc.UITransform; every cc.CompPrefabInfo.fileId is unique; JSON still parses. Then open the prefab under a Canvas in the editor to reimport.
Gotchas
Section titled “Gotchas”cc.UIMeshRendereronly works under a Canvas. The particle node must be in the UI/2D tree; dropped in a 3D scene node it does nothing.- Shared-CurveRange double-scale trap.
startSizeandstartSizeXusually reference the SAMECurveRangeobject ({"__id__":6}). Scaling by field name hits it twice → ×N². The script dedups by__id__; any hand-rolled scaler MUST too (symptom: a ×20 request producing ×400 sizes). - Never use
node.scaleto resize a Cocos particle — the emitter shape and stretch billboards distort; always scale the params. This is the skill’s core lesson. --scalecompounds. Two--scale 3runs = ×9. To undo an overshoot, run the inverse (--scale 0.3333), don’t git-revert-then-redo unless you want the exact prior state.- Component insertion is append-only. New
cc.UITransform/cc.UIMeshRenderer/cc.CompPrefabInfoobjects are pushed to the array tail and their{__id__}back-refs registered innode._components— never renumber existing__id__s (breaks every reference in the prefab). emitFrom/ mesh render modes are untouched — this skill is about UI-readiness + size, not fidelity. For 3D-mesh particles or blend/texture fidelity seet1k:cocos:playable:unity-particle.