Not in this system, this is abandoned and I’m working on a recode, which will have better documentation, an example place and videos, and also more optimization
I have a few quality of life suggestions for the new version that yield greater control over particles. I’ve read through the documentation, however I believe these features are absent if I’m not mistaken.
In the documentation there’s no mention of using attachments as adornees being supported. Hopefully that ends up being supported, attachments are used heavily in animating vfx rather than baseparts.
I don’t recall seeing a method to lock particles cframe to their adornee’s cframe, similar to regular particles have a LockedToPart property. LockedToAdornee would be great.
An Emit() method would provide better quantity control, there are many instances were I would need to guarantee only a single particle appears. I don’t think there’s a way to do that as of right now. I could imagine if you wanted to make an electrical explosion for example, you’d want 10 sparks to appear at once rather than using rate, vice versa if you wanted 10 sparks to appear at the same time every few seconds I currently don’t think thats possible without hardcoding it. Perhaps there could be more controls over the Rate property, like an EmitMultiplier which allows both Rate and :Emit() to yield a multiplied amount of particles, ex. Emit(1) but 10 sparks appear, Rate = 1 but 10 sparks appear at once every emission.
It would be neat if the plugin had a way of decoding the properties into string value instances so that rather than writing all of the properties into a new table you could create an emitter by specifying a “preset” name, and it would search for the string value with that name and if one is found decodes it into your properties table. With the addition of a plugin, I believe the new module could be used to make vfx like this with little to no coding involved. I can only imagine being able to create 3d vfx with no code. This honestly seems like the next big thing in terms of development tools. Very exciting!
The LockedToAdornee part is a valid point, since using parts as anchors for VFX is pretty inefficient compared to attachments. The Emit() method is also pretty much a requirement if you want any real control over bursts. The preset idea sounds like it might add some unnecessary overhead, but it could work if the plugin handles the heavy lifting.
Perhaps there’s other ways to do it, but at least in moon animator it stores your projects via string values that it encodes. I feel as if that could also be used to prevent having to write in all of your properties manually whenever you want to use something you made with a plugin.
That makes sense for a plugin workflow. Using string values to store presets is a decent way to handle data without bloating the actual scripts.
Attachments are supported, anything that a CFrame can come from, so an attachment, attachment in attachment, part, model primary part, etc
I used to have a LockedToPart property on the documentation but it was extremely buggy, it broke collisions, acceleration, and inherit velocity, so I removed it, but the new version will absolutely have it.
There is actually an Emit function, but it’s named “EmitParticle” which I will agree is a little weird on my part, you can use it like this:
local VortexFX = require(game.ReplicatedStorage.VortexFX)
local Emitter = VortexFX.new(script.Parent)
Emitter:Create({
EmitterID = "Spark",
-- Special properties
ParticleFolder = script.Parent.Parent.Particles,
MaximumParticleCount = 60,
-- Normal properties
Transparency = 0,
Speed = 10,
EmissionDirection = Enum.NormalId.Top,
Lifetime = 3,
Rate = 0,
Acceleration = Vector3.new(0, -9.81, 0),
SpreadAngle = Vector3.new(70, 70, 70)
})
Emitter:EmitParticle()
One of my other resources I’m still working on called BetterBezier has this exact feature, where you can take an entire Bezier curve, parts, properties, all of it, and encode it into a stringvalue that you can share or load, so you bet it’ll be in Vortex3D
One goal I have for Vortex3D is to make it so you can take your knowledge from normal particles and apply them into 3D particles, without needing to hunt for anything, so like EmitParticle being just Emit, LockedToAdornee like you said being just LockedToPart, etc.
The naming for EmitParticle is definitely a bit clunky. Renaming it to just Emit would make it much more intuitive for anyone used to the standard API.
I’m assuming the EmitParticle method takes a number? I was also curious, I never thought it would be possible to create 3d particles- so I’m wondering if 3d trails and 3d beams could eventually be added? Its difficult to ensure a particle is made so that each particle touches the previous one’s bounds so there’s no visual gaps. Trails would fix that so the adornee can move at high velocities and produce a trail with no gaps. Likewise, beams are somewhat possible but currently you can’t lock the particles to a moving path, this could even use your bezier resource to make non-linear beams.
I don’t want to suggest things abruptly assuming you’re still upholding your leave of Roblox- as you mentioned on your profile. I only ask this because if the module isn’t planned to have such features added I’d love to fork a version and add support for those things- or to start writing my own resources.
Your continuation of this module has peaked my curiosity greatly, If there’s any way to contribute to this resource to help further its development in any way, I’d love to help!
Examples of vfx only partially possible currently that I think could benefit for trails/beams with the flipbook feature:
Technically it is fully possible to have 3D trails in the idea that you have, I’ve done it, and this system can do it, it’s done with the Reference Object and the Trajectory vertices
What the trajectory vertices do is it has each particle move towards a point in 3D space (attachment, part, cframe, or vector), and in an order, so if you add a bunch of attachments to a character, you could technically have particles move as an outline around the character, it’s not instant of course, since it’s velocity based, but you can have it move very fast with no smoothing so that it’s mostly instant.
I’m still open to hearing suggestions since my leave of Roblox is definitely still happening, it has changed a bit, I’m not exactly leaving as what I meant when I made the comment, but new games that I haven’t already started on won’t be made on Roblox, I’m in the process of already making one game called Inbetween on Roblox since I started it a few months ago, I plan to finish that but that’ll likely be the last full game based project that I have on Roblox
Reference Objects are basically the key to having full customizability, it let’s you use meshes, unions, models (yes, models can be emitted with this module), trails under parts, beams, etc.
See these:
As an update on the progress of Vortex3D, you can see these:
Both of these are emitters without any properties and fully driven by default
The left emitter is the one made by Vortex3D
The right emitter is the one made by VortexFX
They are identical, which is the goal because:
The left emitter at the time of taking this screenshot was running at 0.8% script activity
The right emitter however, was running at 6.4% script activity, that is a 87.5% performance increase
Now obviously it’s not done, it only has really basic properties like rate, color, transparency, lifetime, etc, and nothing special quite yet, but it’s a start and I’m working on it nearly every day. I’ve learned a LOT since when I made VortexFX
What kind of techniques did you learn for performance? I’m curious to know this since I made a voxel destruction resource a couple years back that could stand a rewrite using such techniques. It uses a high part count but greedy meshes it to attempt to keep it as few parts as possible- I never used bulk to move and instead opted for relying on part cache (which should really be moved to object cache for the move to method). I never got around to continuing it- I had hoped to add destructible wedge parts since their shape can be voxelized- the math involving their shape stumped me though. Never figured out how to detect colliding assemblies performantly either, like detecting groups of touching parts. Any ideas?
well, for your case, don’t make voxel destruction a physics-part problem first, make it a data/remesh problem first, then only create parts/colliders where absolutely needed
roblox can handle a decent number of parts but destruction systems die when every tiny voxel becomes a real live part with physics, touch, collision, replication, rendering, etc.
so what I would do:
1: use greedy meshing like you do now
but change your method/process a little bit, do something like:
1: Convert hit into voxel space damage
2: Update voxel occupancy data
3: Greedy mesh only the affected chunk
4: Then spawn/update the fewest possible visual pieces
from what you said, you’re doing something probably like:
1: subdivide into tons of parts
2: try to optimize the mess afterwards
2: use chunks
don’t remesh an entire destructible object every time, split the object into chunks, like 16x16x16 voxels per chunk and 32x32x32 max if the voxel size is larger
3: Use smaller chunk-local arrays
just use smaller arrays and split a huge temporary table up, then piece it back together where you need to, ignoring anything else you don’t need
4: Use flood fill per chunk/nearby chunks
this can fix hard connection checks, pretty simple
5: Spread big chunk writes and stuff across multiple frames
this alone can decrease hefty lag spikes by a lot since you’re writing across multiple frames to even out the load, most good computers won’t experience this, but it’d be more of an optimization for mobile users or people with low-end CPUs
6: Keep voxels as DATA, not instances
This should probably be #1 but I only thought of it now, but this is a big
Don’t do 1 voxel = 1 part, that’s really bad, instead do something like 1 voxel = 1 bit
You can do singular bits with booleans, or a small number in a single flat array
For denser data, you should be using table.create instead of just writing straight into {}, luau recommends doing this, to preallocate array-like tables when you know a maximum size of data, luau also recommends avoiding temporary allocations in tight loops because of high allocation rates, those add GC work (go to How we make Luau fast | Luau)
so, avoid nested tables like this:
voxels[x][y][z]
instead do:
voxels[index]
7: use BulkMoveTo, obviously
not much I need to say about this
8: ObjectCache, not PartCache
I actually was using PartCache for VortexFX for a while before I realized just how much better ObjectCache was, it alone increased performance, obviously not up to my standard now, but it did increase it, a lot.
9: Use spatial query
you can detect what gets hit by stuff like an explosion, sword, wtv, but you should be using workspace:GetPartBoundsInBox() or workspace:GetPartBoundsInRadius()
but make sure to use OverlapParams
so basically:
what destructible objects are near this explosion: GetPartBoundsInRadius
what objects are in this sword/projectile hitbox: GetPartBoundsInBox
i need exact volume overlap with exact part shape: GetPartsInPart
don’t use .Touched unless you really need to, it’s not efficient, even if you use :Once
DON’T use GetTouchingParts for the main stuff, it has annoying caveats and the api reference says if a part has cancollide as false, then it returns empty unless there is a touch interest instance, and adjacent but not intersecting parts are not considered touching
for voxel data, use grid adjacency, something like:
for each solid voxel, check +X, -X, +Y, -Y, +Z, -Z neighbors, then flood fill connected groups
or for merged boxes, you can use AABB:
- two boxes are considered connected/touching if:
- their faces touch within an epsilon
- their ranges overlap on the other two axes
or:
- A maxX == B minX
- A Y range overlaps B Y range
- A Z range overlaps B Z range
this alone is much better than calling GetTouchingParts on every single part
10: ArePartsTouchingOthers can answer “if anything is touching”, not “what group is this”
roblox has workspace:ArePartsTouchingOthers(partlist, overlapIgnored), which returns true if at least one part in the list is touching another part within a small distance threshold
this is useful for “does this group touch anything” but it’s NOT useful enough for “build all connected islands of rubble”
for islands like that, use your own graph/flood fill
as for the wedge voxelization:
you are getting stuck on wedge math, the trick is to just convert the voxel sample point into the wedge’s local space, then test against the wedge’s half-space
a wedge is basically just box bounds + one diagonal clipping plane
so the test is something like:
local point = wedge.CFrame:PointToObjectSpace(worldPoint)
-- inside box, use an abs function:
abs(x) <= size.X / 2
abs(y) <= size.Y / 2
abs(z) <= size.Z / 2
-- inside the slope, the point is below/above the wedge plane
since roblox’s exact wedge orientation is mind numbingly annoying sometimes, the best way to do this is:
- define the actual wedge plane once
- visual-test it in studio manually
- if it’s flipped, flip the sign or the axis
- then reuse that one function forever
for voxelization, don’t test only the voxel center, that creates holes near the slope, use either:
center + 8 corners
or for a test:
if any corner is inside wedge then fill the voxel
but what u probably want is
if 4+ sample points are inside then fill the voxel
as for the physics, don’t simulate every fragment
for the destruction, use 3 tiers:
- static remeshed chunks for main destroyed wall/floor shape and is anchored
- important debris as big pieces players can move and interact with and are unanchored
- cosmetic debris as dust/small chunks and are client only or non-collide
most fragments should not be real physics assemblies
if you do use unanchored parts and pieces, keep the count low and remove them quickly
so you can:
- spawn 8-30 real debris chunks
- everything else is visual only
- return to the cache after 5-15 seconds
network ownership could probably help distribute physics load to clients, but it has security issues because client-owned physics can be manipulated, so don’t use client authority for destruction results, but network ownership can improve server performance by splitting physics computations across clients
for any visual debris that are anchored, ALWAYS have these properties for them:
part.CanCollide = false
part.CanTouch = false
part.CanQuery = false
part.Massless = true
part.EnableFluidForces = false
-- and if you split the visual instances into models, you can use the new SLIM LOD that roblox made:
part.Parent.LevelOfDetail = Enum.ModelLevelOfDetail.SLIM
parallel luau usage
good parallel luau usage can help massively, but ONLY for data work, not instance mutation, roblox won’t event let you change instances on a worker thread
so good parallel work is something like:
- voxel damage masks
- greedy meshing calculation
- connectivity flood fill
- AABB adjacency graph
- chunk rebuild planning
but what you don’t want to do:
- creating parts
- parenting instances
- setting CFrames directly from parallel code
- changing properties
messages between actors are copied across VM boundaries, so sending giant tables every frame is also bad
so what I would do:
Main Thread
- collect hit request
Worker Actor
- compute dirty voxel cells
- compute greedy boxes
- return compact result
Main Thread:
- update pooled parts
- BulkMoveTo result parts
keep the data passed to workers compact, send the chunk id, hit position, radius, seed, etc. but not giant voxel tables every time
editable mesh / meshpart route
this is probably worth considering if the destruction is mostly visual
the editable mesh can modify the geometry during runtime, but it’s slow, memory expensive, and have really strict client side memory budgets, docs also recommend recalculating collision geometry at the end of a conceptual edit rather than after every individual geometry change
so the best use is something like:
- EditableMesh = visual surface
- few invisible parts = collision
and not using an editable mesh with live collision rebuilt every tiny edit
for voxel destruction, i’d probably do:
Server
- authoritative voxel state + simple collider parts
Client
- nicer visuals / debris / mesh effects
don’t use CSG
the geometry service has subtract async, union async, fragment async, etc. but those functions yield and marked unsafe, fragment async also returns meshparts from a voronoi-style decomposition
that’s good and useful for special destruction, but i definitely would not build a high frequency voxel destruction system around runtime CSG, it’s way too unpredictable for rapid hits
so just use voxel data + greedy remesh
if I were you, I’d focus on these for the rewrite:
- Voxel data first
- Chunked dirty rebuilds
- greedy mesh output
- objectCache/BulkMoveTo for pooeld visuals
- spatial queries only for broadphase
- Custom flood fill for connected assemblies
- Very limited actual physics debris
sorry for the entire essay lol
Hi,
Relating to your comment at the top ’ rewrite called Vortex3D, ’ … are you still working on this ?
EDIT:
umm ok… from your comment at the very top it seemed you were implying to wait for Vortex3D.. which I do not see a link to, to check out..
Bro, I’m actively giving updates to it. Just… scroll up and read.
Hi, Roblox released a Beta for Scene Analysis, and I tried testing it on this module with a really quick default setup.
emitter:Create({
ReferenceObject = ReplicatedStorage.Assets.ParticleMeshes.IceShard,
})
emitter:Start()
task.wait(15)
emitter:Kill()
Unless the Analysis was wrong, or I misunderstood some part of the code, I managed to spot two memory leaks.
Both issues were under [CastVisuals].
Leak 1: CastOriginPart and the rest of the Adornments aren’t properly removed in their :Destroy() function. They are unparented and stack up in memory.
Leak 2: When WorldRoot is defaulted to Workspace, the AncestryChanged Connection never gets disconnected due to Workspace never being removed, so the connection stacks in memory.
My solution was very simple, I just changed the end of the :Destroy() function to this.
self._worldRootConn:Disconnect()
self.ActiveVisuals = {}
table.clear(self)
The self._worldRootConn is put before the AncestryChanged connection in the constructor.
The simple table clear managed to remove the loose instances from memory.
Sick, obviously I’m not going to actually update this system with that fix because I’d rather spend time working on the recode, but this is good for a lot of people to know about, the scene analysis is definitely gonna make the recode better, by a LOT.
I’d love to work on the plugin for the new one, if it would help. I was messing around with making a test plugin for the current version using this plugin ui library (has some easy to fix errors, its not kept up with it seems). I don’t plan on finishing it, it helped me think of a nice layout though.
Please disregard this if you intend on soloing it.
I was thinking that there would need to be some sort of a collection, where you select (with filtering) your created selections via a dropdown. Collections only serve as a container so that you can work on a group of emitters- assuming that most vfx would require more than one emitter. This way you can switch between your vfx visibility without having to manually set each emitter to visible.
When creating a new emitter you’re asked to set the name and adornee.
From there you’d gain a list of all the emitters in the collection, each has an option to toggle its visibility individually with a checkbox. A separate ‘Properties’ window opens to modify the emitter properties.
A separate ‘Emit’ window opens with an emit button and an emit amount.
Without knowing what method you want for creating a pointer to the module (I assumed a tag), and if the module will have a built in way to store plugin emitters (I assumed string value instances under the module) or visualize them, I can’t really do much front-end. I think json encoding them into strings would probably be the safest bet like moon2? Then just passing a string to the create method rather than a table of properties. That’s how I would do it at least, not sure if that’s a good approach.
Just some ideas I wanted to throw out : )
Curious to know if there’s anything I can do to help out, I really like this resource; I see it as a much better tool for 3d effects in moon2 than mesh emission plugins that make you write code into every animation event- the goal is to prevent that if possible.
Oh dude I’d love it if you’d want to do the plugin, I will absolutely accept any help I can get, if you wanna talk about this more you can DM me on discord @distinguished_duccer
I think a plugin for Vortex3D would be a lot easier to make than VortexFX since I’ve actually got read and write properties, like attribute based emitters and the likeness working, where you can change them at run-time and have them update, right now it’s fully possible to make codeless emitters
JSON encoding with Base64 is next on my list before I work on porting all of the VortexFX properties to Vortex3D (like instability, trajectory, sound, etc), I’m working on the backend and making it so when I do work on porting, it’ll be a walk in the park.





