CullingService V2 - Custom Client Sided Culling/Streaming Module

Thank you

so besides me not setting the autostart to true did I do anything else wrong?

Is it possible to create your own custom regions, and dictate what models are culled based on the player being within them, as opposed to using the built-in regions?

It’s not possible to change the regions (culling service creates those automatically), but you retain full control over which models will be culled in and which are not.

When I install the plugin, it doesn’t appear in the toolbar.

Hi @https_KingPie, is there a how-to video on how to install CullingService V2? If there is, please link it here.

Many thanks! A video will help us a lot with the setup. :smiley: :+1:

Do you mind making a youtube video, I got stuck on step 6…
So confused where I should put the localscript, and how can I call CullingService if its not a global variable?

Please make a video tutorial.

I’ve been having an issue with this, the assets load perfectly fine on some phones and some pcs but for others they don’t. How could i troubleshoot this?

Are you players getting an errors or warnings in their output? Feel free to hit me up in private messages or via the community Discord as well.

Omg this is so useful! I’m working on a game where the playerbase is 90% mobile so this will definitely improve the performance.

I just need quick question answered though. Where do I put or create the Short, Medium, Long folders? I tried putting them in ModelStorage with the models inside and the same thing for AnchorPoints. I’m not a scripter so sorry if it’s a dumb question.

They go inside the models themselves.

image

For a good reference, check out: CullingService Testing - Roblox which is uncopylocked and shows the whole set-up. You can customize the ranges that models appear within CullingService > Settings

1 Like

is this still useful now a days ? I plan to make a building rendering system and I am planning to make one like yours that can help with the task I have. however mine will allow me to use some other systems I’ve already made. but is it worth it still or would other stuff made by roblox studios be a better option like streaming enabled.

1 Like

Hey there and great question, this is something that I’ve been meaning to address for a while, especially with occlusion culling on the roadmap. Bear with the long answer.

TL;DR: It depends on what you value; below it is broken into key points.

Control ‘what’ is streamed in and out

CullingService (CS) was sparked because in 2021 StreamingEnabled was really restrictive. Everything was streamed in/out or nothing was. The main point of CS was control of what streams in and what does not. StreamingEnabled has this to a degree with ModelStreamingMode. If that is all you are concerned about, then StreamingEnabled may be the better choice.

Control ‘how’ streaming works

CS does offer model streaming ranges, which is another way of approach level of detail. Roblox does not natively support this through either StreamingEnabled or the LevelOfDetail property in models. This is because CS allows you to specify at which ranges certain instances within a model will appear on the client, so you can precisely the visuals of your game versus working around auto handling. If that is a concern for you, consider CS.

CS also offers signals to know when things are culled in/out (to include whole models or parts of models culled in/out at certain ranges). That’s a major plus if the alternative is connecting to workspace.DescendantAdded or workspace.ChildAdded. If that is an advantage, consider CS.

Control how streaming ‘looks’

CS offers aesthetic controls for models and it is very easy to create your own. What this means is that you can build streaming into your game as an aesthetic. For example, you could run through the a low poly cartoon style forest and see trees springing up around you. That’s a cool aesthetic that’s very easy to accomplish with CS (and included as an animation preset) and basically turns an optimization technique into a feature of your game’s identity. You can’t do that with StreamingEnabled - stuff will just blink in/out. If that is an advantage, consider CS.

Support for motion and other possible edge cases

None of my experiences have really worked with motion + StreamingEnabled too much, so I’m not sure how much of an issue this still is (I believe it used to be one). With CS, you can stream in/out moving parts without desync across clients or issues with Roblox physics. For example, you could build a busy city street with car-to-car moving/dynamic traffic and comfortably stream everything without issue. That might not be an issue any longer with StreamingEnabled, but having a streaming solution that you can write to and change does let you handle weird edge cases that emerge with how Roblox features interact with games. If that is an advantage, CS might be a good choice.

Overall Performance

Neither has been benchmarked against each other and to be honest, I’m not sure a great way to do it. StreamingEnabled will benefit from being built into Roblox’s engine, so it can make use of things that CS simply can’t. With that said, CS is scrappy in the sense that what I’ve been able to optimize, I have. For example, if you had a forest of 10,000 duplicate trees - CS would only save one of the trees in ReplicatedStorage and simply clone it in as necessary. StreamingEnabled might save 10,000 trees internally. That’s not fear mongering or an indirect push to CS, we just don’t have visibility on how StreamingEnabled really handles that stuff on the backend, unless an engineer on the team for that feature can comment. CS might have the edge there, it might not. Similarly, I’m not sure how the two systems compare in a forest of 10,000 unique trees. StreamingEnabled might win out, it might not. CS also doesn’t cull in those instances on the server - only the client. That probably complicates a thorough benchmark/comparison, because that significantly reduces the load on the server’s end, especially for things concerning physics (for those who haven’t used CS, the perspective from the server could potentially be an empty baseplate with players running around, whereas for the players they would have a whole world culled in/out around them)

However, I can say that both solutions will help your game. It’s probably just a factor of how much and how your project is set up that might specifically inform those results.


Hope that helps in breaking down (what I see as) some of the key factors in deciding what to go with. If I can clarify anything, let me know.

That’s okay :slight_smile: And also, thanks for making the module a lot of way its structured is how I’m making my own just slighting different tho due to it having a slightly different task. Also one thing I did notice is that a we could make a steaming enabled like system for objects that are only seen on the client, and it should work the same way as Streaming Enabled by Roblox, the only difference is no signal firing. ( the server sends signals to the clients telling them to load/unload that stuff ) . If you look bellow all its apparently doing to unload them is setting there parent to nil. and then some other stuff that make sures the server knows that players object is streamed out.

Quote
“When an instance streams out, it is parented to nil so that any existing Luau state will reconnect if the instance streams back in. As a result, removal signals such as ChildRemoved or DescendantRemoving fire on its parent or ancestor, but the instance itself is not destroyed in the same sense as an Instance:Destroy() call.” Signals related to destruction do not fire on stream out.

So if Streaming Enabled sets the stuff to nil and doesn’t do something else with them other then tell the server. A client-sided Streaming Enabled for client sided objects only that sets them to nil to stream them out , I believe would work the same way as a roblox one and there wouldn’t even be any network data signals being fired its not even on the server.

But the only reason my system should work like this is because these objects don’t get affected by the server. For other games that require the object to be interacted with by many people and move, the thing I’ll be working on would NOT work. But for client-only objects, like the ones I’m working with, the way your system loads/unloads—and me adding a few stuff to allow it to work for my things I believe would work well. Lastly this system could work with streaming enabled while streaming enabled handles the server sided objects this handles client sided.

Also I have a question about your system the only problem I don’t know (and haven’t tested) is: Does the client still get network data increases if that object is streamed out by your system and the server starts doing stuff with the part?
( also sorry if spelling is bad I’m trying to type this on mobile )

1 Like

This is what I’m actually planning to use to my advantage the fact that objects could literally only be on the client and not be made on the server. And this could also allow us to use this way along with streaming enabled.

1 Like

Also I noticed that quote didn’t show up here you can read it better here if you haven’t already :+1:

1 Like

The way CS works is that models are only streamed in on the client. When the game initially starts, they don’t exist on the server at all - only anchor points which indicate where the models should be streamed too. Based on proximity to anchor points, the models are streamed in.

That is why, in the Example Place, the workspace looks like:
image

but when players join and run around, they start to see houses appear (the distance ranges are set to make the streaming very obvious, versus how a real game would tailor the distances to be more subtle)

Looping back to your question, there ends up never being cases where the server is doing something with a part that the client can’t see (unless it’s a moving part, which is a slightly different case). However, if that was important, it wouldn’t be hard to accomplish this with minimal networking. For example, if I wanted to change the color of a house on the server and have that replicate to all players (I’d envision something based on attributes of anchor points and slight modification of the signal methods to also include the anchor point - that way models could be updated based on anchor point characteristics and keep things in sync with the server).


Touching quickly on how streaming sets the parent to nil - thanks for that, I wasn’t sure how it literally got handled so that’s great. To address it quickly since you are making something similar, that approach would not work with something like CS - models should be destroyed. This is because models are cloned and streamed in and then destroyed when streamed out. Each time it streams in, it’s a new model. There’s some accidental pros to this - for example, if someone used a btools exploit to destroy a segment of a wall, walked away causing it to cull back, and then walked back within culling range, then a new wall would be generated, since it’s cloned each time in. Setting to nil would cause lots of lingering in memory, since StreamingEnabled essentially streams ‘the’ model in/out, whereas CS does not. Neither is right or wrong, it’s just a difference in approach and there’s pros/limitations to both.

very very well done I like it even more your system. now here’s another new question if your system destroys objects and clones then back in what happens when its doing this to around 50 objects at a time or its doing many of them at a time wouldn’t that be really bad and cause strain on the client because you are making 50 objects have to be cloned and then rendered back in? setting something to nil would allow for faster loading I believe and less issues of having to clone many objects in if you are dealing with games with many many objects secondly, then again by setting it to nil the instance is still in the game leading to memory usage still, its just not rendered in. but ya I understand I guess there’s just trade offs at points if you do it one way compared to another way. Also like the quote

said here. wouldn’t yours be disconnecting those object signals leading to destroy events going off child removed events going of and even descendant removing events going off if people have them connected plus variables having those instances would then be nil as the instance no longer exists, just from destroying the instance. This also means we can’t actually store data on the object itself that changes over time like attributes unless we put it in a parent folder or unless your system remembers to add it back if not we then have to make another one that does it, when the new object gets added that seems like more of a hassle and possible performance issues just by doing that if many objects have information on them.

1 Like

Great question. When I’ve set up games using CS, I’ve opted to take small things (like flowers, ivy, small rocks) in a close area and treat them as one entity. Breaking down those models to their functional level, which is decor, can point you in the direction of simply treating them as one single object, especially if they are close together (this is basically the idea for range folders, just within a single model). This way, when a player enters the range where the developer once these entities to be spawned in and visible, only one object is culled in/out as opposed to how ever many smaller models might collectively compose the decor (e.g., 50). At heart, you can avoid this dilemma through smart set-up.

This may be true, but load times are a micro-optimization comparisons are a micro-optimization. Since memory usage is remaining the same, its a moot point to really compare which loads in faster, especially because good culling set-up either happens without the player even noticing (i.e., cull things in within a 250 radius around the character, but tailor atmosphere + lighting and employ max zoom restrictions so the player maintains the illusion that the map is always spawned in). Alternatively, if culling is meant to be seen and contributes to a game’s aesthetic, then load times still don’t really matter - the player’s experience isn’t really affected if something could spawn in 0.1 seconds earlier or later (arbitrary number)

Correct, that definitely happens. All objects being culled in/out of the map are parented to a folder called CulledObjects. Importantly, that allows for some selective listening for any Child/Descendant Added/Removed events (CDAR events, for short) (I don’t think it’s fantastic practice to connect CDAR events to the workspace as a whole, because it’s going to be firing very frequently, regardless of CS). Keeping everything being culled in/out in a single folder allows you to set up events looking for CDAR events and exclude that folder by looking running :IsDescendantOf or only running CDAR events on other folders/models that aren’t affected by CS object streaming.

It is true that variables set to something that is culled in/out will be nil if that object is culled out. However, that would also be true in some circumstances with StreamingEnabled if that instance has not been initially streamed in for the client. Regardless, work with any kind of streaming necessitates that the code needs to be flexible in case the object does or does not exist (whether physically or actually in the workspace) when code runs.

Personally, I like to make most instances that touch my code not stream in/out at all, to avoid weird edge cases. CS helps me do that (and new ModelStreamingBehavior properties for models help with that too), and I think that’s especially important in cases where you want to update the state of the object itself.

kk welp it was nice chatting with you and thanks for the ideas and chat, ill be working on my system now and ill be testing the 2 ways, yours and my own with the nil way I wanna see which would be the best for a game that has thousands of things needing to be streamed in and out. what makes a game. trial and error !

2 Likes

I’m back guess im not gonna be working on that system lol. just got fired from a company that stole all my work without paying me for it claiming they did hahahhaa. but other then that, I was able to make a small version of the system and here are the results, that system you made I made a version that sets stuff to nil. so the difference is not really noticeable however one will cause more memory usage the nil one but is a lot faster loading in and out buildings. but destroying system you have is more performant wise when it comes to memory use however rendering its not as fast.

1 Like