I’ve just finished designing and testing a little module I’m calling “PartCache”.
This module is used to make the illusion of quickly creating and destroying parts for stuff like laser beams and such. This module uses CFrame to its advantage to work, since creating parts with Instance.new() can get very laggy.
Here’s a stress test. I have 64 sentries here each shooting up to 10 bullets per second. It’s completely lagless!
While this has a number of other performance implications (e.g. moving parts far away does remove them from their current cluster which means they need to be re-added when being moved back), it’s orders of magnitude better than creating a whole new instance only to delete it right after, so the benefits outweigh the caveats quite extremely.
Let me know what you think, and please don’t hesitate to post your creations using this module in the replies below!
Click here to see the API / How to use this module.
API
PCache PartCacheModule.new(BasePart template, int precreatedParts = 5)
Creates a new part cache (“PCache”) using the specified template part, creating precreatedParts parts to grab from the cache. This will error if template is nil or precreatedParts < 0.
BasePart PCache:GetPart()
Gets a part from the cache that isn’t currently in use. If there are no more parts in the cache, it will send a warning to the output and add a new part to the cache.
void PCache:ReturnPart(BasePart part)
Returns a part to the cache after you’re done using it. Automatically CFrames it to a location far away. This will throw an error if the part passed in to the function was not part of the cache’s in-use list (either by passing in a part that has already been returned to the cache or a part that was never a member of the cache in the first place).
Sets the parent of all cached parts to the specified instance.
Errors if the parent is not an Instance, errors if the parent is nil, errors if the cached parts will not be a descendant of Workspace.
void PCache:Dispose()
Deletes all cached parts regardless of if they are in use or available, then removes the cache object itself. Attempting to call any methods or functions of the cache will throw an error.
I’ve noticed that reparenting is slower, yes. As for why, I’m not entirely sure, since moving it far away also pulls it from the render queue due to being out of range.
It would be nice if you could give us some concrete statistics (ie percentages in script performance tab) on how much more efficient this is compared to reparenting / repeated instancing.
I don’t know if you meant to say that to me, but yeah, I’ll check as soon as possible.
Edit: I’m not sure of a good way to actually benchmark it. My benchmark tests seem to indicated the opposite of what zeuxcg said, so I probably implemented it wrong.
I hope I don’t get blasted into low Earth orbit for bumping, but your module came in really handy when I was optimizing my arrow tracers. Now instead of rendering everything on the server, I just send some data off to the clients that use this module to render the tracers and I am in love with how it performs!
I did notice your API reference doesn’t accurately reflect the module. The latest version of your module creates PartCaches via .new() but your API reference in the OP claims it’s done with :CreateNewCache()
With that aside, you saved me a boatload of work and testing. Thanks for the module!
Read the docs – The DestroyPart method returns a part to the cache so that it can be used later. The Destroy method deletes the cache itself and deletes all of the parts that are within it so that it can no longer be used.
If you see it as a good idea, I could rename the method.
Edit: I may do that anyway actually, since I’m sure other users may have stumbled upon the same confusion.
Edit 2: The proper method is now PCache:ReturnPart – DestroyPart will still work for backwards compatibility.
Sorry for the bump, but I have a question as to how I would actually use this module. I’m using a custom implementation of this module for an ambient particle system, but I’m not sure how I would actually return a part to the open list.
100 parts are used every second/few seconds (depends on the algorithm) like this:
local part = Pool:GetUnusedPart()
part.CFrame = CFrame.new(pos.X, pos.Y, pos.Z)
part.ParticleEmitter.Acceleration = Vector3.new(rand:NextInteger(-5, 5), rand:NextInteger(-5, 5), rand:NextInteger(-5, 5))
part.ParticleEmitter:Emit(1)
I assume to put the part back into the open list, I would just use a coroutine:
But I worry that creating a coroutine for every part could cause lag, defeating the entire purpose of such a module. Is this true? Is there some other way that people should add parts back to the unused list? Thanks for the help.
Correct me if I’m wrong, but BulkMoveTo doesn’t replicate the movements to clients for optimization?
If so, then it’s not a good solution as the parts will still be visible after being moved to the Cache.
You can specify a CFrame property change (basically replicated to client) I believe but its hard to know since there’s not much API information on it but I think I am correct, as you can specify something like that within a parameter.
It depends on your implementation. In your specific case, I’d advise against using parts if your goal is to have single particles emit at random locations. A better choice might (I say might because I don’t actually know what you’re trying to do) be to parent an Attachment to terrain. CFrame the attachment, emit the particle, delay, repeat. You could have a single attachment + emitter and just randomly emit around your area. That, or better yet, just make a big part and use Roblox’s random placement (but I assume you’re avoiding that for a reason).
To answer your question, I’d advise having it all in one single function, wait included. That, or use spawn instead of coroutine.wrap as spawn ties into Roblox’s scheduler (same system as wait for the most part)
It could be done relatively easily, the issue is that there’s no real purpose for it unless you’re building close to the preset position, which would cause other far more severe problems for your game